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

363 lines
16 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')
has_date_filter = bool(date_from or date_to)
objitem_count = form.cleaned_data.get('objitem_count')
sources_with_date_info = []
for source in sources:
source_data = {
'source': source,
'objitems_data': []
}
for objitem in source.source_objitems.all():
objitem_matches_date = True
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 has_date_filter:
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
elif has_date_filter:
# Если фильтр по дате задан, но у точки нет даты - не подходит
objitem_matches_date = False
# Добавляем только точки, подходящие по дате (или все, если фильтр не задан)
if not has_date_filter or objitem_matches_date:
source_data['objitems_data'].append({
'objitem': objitem,
'matches_date': objitem_matches_date,
'geo_date': geo_date
})
# ЭТАП 2: Проверяем количество отфильтрованных точек
filtered_count = len(source_data['objitems_data'])
# Применяем фильтр по количеству точек (если задан)
include_source = True
if objitem_count:
if objitem_count == '1':
include_source = (filtered_count == 1)
elif objitem_count == '2+':
include_source = (filtered_count >= 2)
if source_data['objitems_data'] and include_source:
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'):
queryset = queryset.filter(
source_objitems__parameter_obj__id_satellite__band__in=filters['band']
).distinct()
# Фильтр по поляризации
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)
# Диапазон дат ГЛ (самая ранняя - самая поздняя)
geo_dates = []
for objitem in objitems_list:
if hasattr(objitem, 'geo_obj') and objitem.geo_obj and objitem.geo_obj.timestamp:
geo_dates.append(objitem.geo_obj.timestamp.date())
date_range_str = '-'
if geo_dates:
min_date = min(geo_dates)
max_date = max(geo_dates)
# Форматируем даты в формате d.m.Y
min_date_str = min_date.strftime('%d.%m.%Y')
max_date_str = max_date.strftime('%d.%m.%Y')
if min_date == max_date:
# Если даты совпадают, показываем только одну
date_range_str = min_date_str
else:
# Иначе показываем диапазон
date_range_str = f"{min_date_str}-{max_date_str}"
# Записываем строку
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=date_range_str)
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")}.xlsx"'
return response