Files
dbstorage/dbapp/mainapp/views/kubsat.py

331 lines
15 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Представления для страницы Кубсат с фильтрацией и экспортом в Excel
"""
from datetime import datetime
from io import BytesIO
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.gis.geos import Point
from django.db.models import Count, Q
from django.http import HttpResponse
from django.views.generic import FormView
from openpyxl import Workbook
from openpyxl.styles import Font, Alignment
from mainapp.forms import KubsatFilterForm
from mainapp.models import Source, ObjItem
from mainapp.utils import calculate_mean_coords
class KubsatView(LoginRequiredMixin, FormView):
"""Страница Кубсат с фильтрами и таблицей источников"""
template_name = 'mainapp/kubsat.html'
form_class = KubsatFilterForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['full_width_page'] = True
# Если форма была отправлена, применяем фильтры
if self.request.GET:
form = self.form_class(self.request.GET)
if form.is_valid():
sources = self.apply_filters(form.cleaned_data)
# Определяем, какие источники подходят по дате
date_from = form.cleaned_data.get('date_from')
date_to = form.cleaned_data.get('date_to')
# Добавляем информацию о соответствии дате для каждого источника
sources_with_date_info = []
for source in sources:
source_data = {
'source': source,
'matches_date': False,
'objitems_data': []
}
# Проверяем каждый ObjItem
for objitem in source.source_objitems.all():
objitem_matches_date = False
geo_date = None
if hasattr(objitem, 'geo_obj') and objitem.geo_obj and objitem.geo_obj.timestamp:
geo_date = objitem.geo_obj.timestamp.date()
# Проверяем попадание в диапазон дат
if date_from and date_to:
objitem_matches_date = date_from <= geo_date <= date_to
elif date_from:
objitem_matches_date = geo_date >= date_from
elif date_to:
objitem_matches_date = geo_date <= date_to
else:
objitem_matches_date = True # Нет фильтра по дате
source_data['objitems_data'].append({
'objitem': objitem,
'matches_date': objitem_matches_date,
'geo_date': geo_date
})
# Если хотя бы одна точка подходит по дате, весь источник подходит
if objitem_matches_date:
source_data['matches_date'] = True
sources_with_date_info.append(source_data)
context['sources_with_date_info'] = sources_with_date_info
context['form'] = form
return context
def apply_filters(self, filters):
"""Применяет фильтры к queryset Source"""
queryset = Source.objects.select_related('info').prefetch_related(
'source_objitems__parameter_obj__id_satellite',
'source_objitems__parameter_obj__polarization',
'source_objitems__parameter_obj__modulation',
'source_objitems__transponder__sat_id'
).annotate(objitem_count=Count('source_objitems'))
# Фильтр по спутникам
if filters.get('satellites'):
queryset = queryset.filter(
source_objitems__parameter_obj__id_satellite__in=filters['satellites']
).distinct()
# Фильтр по полосе спутника (пока не реализован полностью)
if filters.get('band'):
pass # TODO: реализовать фильтр по band
# Фильтр по поляризации
if filters.get('polarization'):
queryset = queryset.filter(
source_objitems__parameter_obj__polarization__in=filters['polarization']
).distinct()
# Фильтр по центральной частоте
if filters.get('frequency_min'):
queryset = queryset.filter(
source_objitems__parameter_obj__frequency__gte=filters['frequency_min']
)
if filters.get('frequency_max'):
queryset = queryset.filter(
source_objitems__parameter_obj__frequency__lte=filters['frequency_max']
)
# Фильтр по полосе частот
if filters.get('freq_range_min'):
queryset = queryset.filter(
source_objitems__parameter_obj__freq_range__gte=filters['freq_range_min']
)
if filters.get('freq_range_max'):
queryset = queryset.filter(
source_objitems__parameter_obj__freq_range__lte=filters['freq_range_max']
)
# Фильтр по модуляции
if filters.get('modulation'):
queryset = queryset.filter(
source_objitems__parameter_obj__modulation__in=filters['modulation']
).distinct()
# Фильтр по типу объекта
if filters.get('object_type'):
queryset = queryset.filter(info__in=filters['object_type'])
# Фильтр по количеству ObjItem
objitem_count = filters.get('objitem_count')
if objitem_count == '1':
queryset = queryset.filter(objitem_count=1)
elif objitem_count == '2+':
queryset = queryset.filter(objitem_count__gte=2)
# Фиктивные фильтры (пока не применяются)
# has_plans, success_1, success_2, date_from, date_to
return queryset.distinct()
class KubsatExportView(LoginRequiredMixin, FormView):
"""Экспорт отфильтрованных данных в Excel"""
form_class = KubsatFilterForm
def post(self, request, *args, **kwargs):
# Получаем список ID точек (ObjItem) из POST
objitem_ids = request.POST.getlist('objitem_ids')
if not objitem_ids:
return HttpResponse("Нет данных для экспорта", status=400)
# Получаем ObjItem с их источниками
objitems = ObjItem.objects.filter(id__in=objitem_ids).select_related(
'source',
'source__info',
'parameter_obj__id_satellite',
'parameter_obj__polarization',
'transponder__sat_id',
'geo_obj'
).prefetch_related('geo_obj__mirrors')
# Группируем ObjItem по Source для расчета инкрементального среднего
sources_objitems = {}
for objitem in objitems:
if objitem.source:
if objitem.source.id not in sources_objitems:
sources_objitems[objitem.source.id] = {
'source': objitem.source,
'objitems': []
}
sources_objitems[objitem.source.id]['objitems'].append(objitem)
# Создаем Excel файл
wb = Workbook()
ws = wb.active
ws.title = "Кубсат"
# Заголовки
headers = [
'Дата',
'Широта, град',
'Долгота, град',
'Высота, м',
'Местоположение',
'ИСЗ',
'Прямой канал, МГц',
'Обратный канал, МГц',
'Перенос',
'Получено координат, раз',
'Дата',
'Зеркала',
'СКО, км',
'Примечание',
'Оператор'
]
# Стиль заголовков
for col_num, header in enumerate(headers, 1):
cell = ws.cell(row=1, column=col_num, value=header)
cell.font = Font(bold=True)
cell.alignment = Alignment(horizontal='center', vertical='center')
# Заполняем данные
current_date = datetime.now().strftime('%d.%m.%Y')
operator_name = f"{request.user.first_name} {request.user.last_name}" if request.user.first_name else request.user.username
row_num = 2
for source_id, data in sources_objitems.items():
source = data['source']
objitems_list = data['objitems']
# Рассчитываем инкрементальное среднее координат из оставшихся точек
average_coords = None
for objitem in objitems_list:
if hasattr(objitem, 'geo_obj') and objitem.geo_obj and objitem.geo_obj.coords:
coord = (objitem.geo_obj.coords.x, objitem.geo_obj.coords.y)
if average_coords is None:
# Первая точка
average_coords = coord
else:
# Инкрементальное усреднение
average_coords, _ = calculate_mean_coords(average_coords, coord)
# Если нет координат из geo_obj, берем из source
if average_coords is None:
coords = source.coords_kupsat or source.coords_average or source.coords_valid or source.coords_reference
if coords:
average_coords = (coords.x, coords.y)
latitude = average_coords[1] if average_coords else ''
longitude = average_coords[0] if average_coords else ''
# Получаем местоположение из первого ObjItem с geo_obj
location = ''
for objitem in objitems_list:
if hasattr(objitem, 'geo_obj') and objitem.geo_obj and objitem.geo_obj.location:
location = objitem.geo_obj.location
break
# Получаем данные спутника и частоты
satellite_info = ''
reverse_channel = ''
direct_channel = ''
transfer = ''
for objitem in objitems_list:
if hasattr(objitem, 'parameter_obj') and objitem.parameter_obj:
param = objitem.parameter_obj
if param.id_satellite:
sat_name = param.id_satellite.name
norad = f"({param.id_satellite.norad})" if param.id_satellite.norad else ""
satellite_info = f"{sat_name} {norad}"
if param.frequency:
reverse_channel = param.frequency
if objitem.transponder and objitem.transponder.transfer:
transfer = objitem.transponder.transfer
if param.frequency:
direct_channel = param.frequency + objitem.transponder.transfer
break
objitem_count = len(objitems_list)
# Зеркала
mirrors = []
for objitem in objitems_list:
if hasattr(objitem, 'geo_obj') and objitem.geo_obj:
for mirror in objitem.geo_obj.mirrors.all():
if mirror.name not in mirrors:
mirrors.append(mirror.name)
mirrors_str = '\n'.join(mirrors)
# Записываем строку
ws.cell(row=row_num, column=1, value=current_date)
ws.cell(row=row_num, column=2, value=latitude)
ws.cell(row=row_num, column=3, value=longitude)
ws.cell(row=row_num, column=4, value=0) # Высота всегда 0
ws.cell(row=row_num, column=5, value=location)
ws.cell(row=row_num, column=6, value=satellite_info)
ws.cell(row=row_num, column=7, value=direct_channel)
ws.cell(row=row_num, column=8, value=reverse_channel)
ws.cell(row=row_num, column=9, value=transfer)
ws.cell(row=row_num, column=10, value=objitem_count)
ws.cell(row=row_num, column=11, value='-') # Дата (пока не заполняется)
ws.cell(row=row_num, column=12, value=mirrors_str)
ws.cell(row=row_num, column=13, value='') # СКО не заполняется
ws.cell(row=row_num, column=14, value='') # Примечание не заполняется
ws.cell(row=row_num, column=15, value=operator_name)
row_num += 1
# Автоширина колонок
for column in ws.columns:
max_length = 0
column_letter = column[0].column_letter
for cell in column:
try:
if len(str(cell.value)) > max_length:
max_length = len(str(cell.value))
except:
pass
adjusted_width = min(max_length + 2, 50)
ws.column_dimensions[column_letter].width = adjusted_width
# Сохраняем в BytesIO
output = BytesIO()
wb.save(output)
output.seek(0)
# Возвращаем файл
response = HttpResponse(
output.read(),
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
)
response['Content-Disposition'] = f'attachment; filename="kubsat_{datetime.now().strftime("%Y%m%d_%H%M%S")}.xlsx"'
return response