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

302 lines
12 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.

"""
Views для отображения отметок сигналов по выбранным источникам.
Сопоставляет теханализы с первой точкой источника по имени.
"""
import json
from datetime import timedelta
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Max, Min, Q
from django.http import JsonResponse
from django.shortcuts import render
from django.utils import timezone
from django.views import View
from mainapp.models import (
Source,
ObjItem,
TechAnalyze,
ObjectMark,
Polarization,
Modulation,
)
class SourceMarksView(LoginRequiredMixin, View):
"""
Страница отображения отметок сигналов для выбранных источников.
Сопоставляет теханализы с первой точкой источника по имени.
"""
def get(self, request):
# Получаем IDs источников из параметра
ids_param = request.GET.get('ids', '')
source_ids = [int(id_str) for id_str in ids_param.split(',') if id_str.strip().isdigit()]
# Справочники для фильтров
polarizations = Polarization.objects.all().order_by('name')
modulations = Modulation.objects.all().order_by('name')
context = {
'source_ids': source_ids,
'source_ids_json': json.dumps(source_ids),
'full_width_page': True,
'polarizations': polarizations,
'modulations': modulations,
}
return render(request, 'mainapp/source_marks.html', context)
class SourceMarksAPIView(LoginRequiredMixin, View):
"""
API для получения данных отметок по выбранным источникам.
Сопоставляет теханализы с первой точкой источника по имени.
"""
def get(self, request):
from datetime import datetime
# Получаем параметры
ids_param = request.GET.get('ids', '')
source_ids = [int(id_str) for id_str in ids_param.split(',') if id_str.strip().isdigit()]
size = int(request.GET.get('size', 0))
search = request.GET.get('search', '').strip()
# Фильтры
polarization_ids = request.GET.getlist('polarization_id')
modulation_ids = request.GET.getlist('modulation_id')
freq_min = request.GET.get('freq_min')
freq_max = request.GET.get('freq_max')
freq_range_min = request.GET.get('freq_range_min')
freq_range_max = request.GET.get('freq_range_max')
bod_velocity_min = request.GET.get('bod_velocity_min')
bod_velocity_max = request.GET.get('bod_velocity_max')
if not source_ids:
return JsonResponse({
'error': 'Не выбраны источники',
'periods': [],
'data': [],
}, status=400)
# Получаем источники с их первыми точками
sources = Source.objects.filter(id__in=source_ids).prefetch_related('source_objitems')
if not sources.exists():
return JsonResponse({
'message': 'Источники не найдены',
'periods': [],
'data': [],
})
# Собираем имена первых точек для каждого источника
source_first_objitem_names = {}
source_coords = {}
for source in sources:
# Получаем первую точку источника (по ID - порядок добавления)
first_objitem = source.source_objitems.order_by('id').first()
if first_objitem and first_objitem.name:
source_first_objitem_names[source.id] = first_objitem.name
# Сохраняем усреднённые координаты
if source.coords_average:
source_coords[source.id] = f"{source.coords_average.y:.6f}, {source.coords_average.x:.6f}"
else:
source_coords[source.id] = "-"
if not source_first_objitem_names:
return JsonResponse({
'message': 'У выбранных источников нет точек с именами',
'periods': [],
'data': [],
})
# Получаем имена для поиска теханализов
objitem_names = list(source_first_objitem_names.values())
# Ищем теханализы по именам
tech_analyzes = TechAnalyze.objects.filter(
name__in=objitem_names
).select_related(
'satellite', 'polarization', 'modulation', 'standard'
).order_by('frequency', 'name')
if not tech_analyzes.exists():
return JsonResponse({
'message': 'Не найдены теханализы, соответствующие именам точек выбранных источников',
'periods': [],
'data': [],
})
# Применяем фильтры к теханализам
if polarization_ids:
tech_analyzes = tech_analyzes.filter(polarization_id__in=polarization_ids)
if modulation_ids:
tech_analyzes = tech_analyzes.filter(modulation_id__in=modulation_ids)
if freq_min:
try:
tech_analyzes = tech_analyzes.filter(frequency__gte=float(freq_min))
except ValueError:
pass
if freq_max:
try:
tech_analyzes = tech_analyzes.filter(frequency__lte=float(freq_max))
except ValueError:
pass
if freq_range_min:
try:
tech_analyzes = tech_analyzes.filter(freq_range__gte=float(freq_range_min))
except ValueError:
pass
if freq_range_max:
try:
tech_analyzes = tech_analyzes.filter(freq_range__lte=float(freq_range_max))
except ValueError:
pass
if bod_velocity_min:
try:
tech_analyzes = tech_analyzes.filter(bod_velocity__gte=float(bod_velocity_min))
except ValueError:
pass
if bod_velocity_max:
try:
tech_analyzes = tech_analyzes.filter(bod_velocity__lte=float(bod_velocity_max))
except ValueError:
pass
if search:
tech_analyzes = tech_analyzes.filter(
Q(name__icontains=search) | Q(id__icontains=search)
)
# Создаём маппинг имя теханализа -> source_id для координат
name_to_source_id = {name: sid for sid, name in source_first_objitem_names.items()}
# Получаем ID теханализов
ta_ids = list(tech_analyzes.values_list('id', flat=True))
# Фильтруем отметки за последние 90 дней
date_90_days_ago = timezone.now() - timedelta(days=90)
marks_qs = ObjectMark.objects.filter(
tech_analyze_id__in=ta_ids,
timestamp__gte=date_90_days_ago
).select_related('created_by__user', 'tech_analyze')
# Получаем диапазон дат с отметками
date_range = marks_qs.aggregate(
min_date=Min('timestamp'),
max_date=Max('timestamp')
)
min_date = date_range['min_date']
max_date = date_range['max_date']
if not min_date or not max_date:
# Нет отметок, но есть теханализы - показываем пустую таблицу
data = []
for ta in tech_analyzes:
source_id = name_to_source_id.get(ta.name)
coords = source_coords.get(source_id, '-') if source_id else '-'
data.append({
'id': ta.id,
'name': ta.name,
'frequency': float(ta.frequency) if ta.frequency else 0,
'freq_range': float(ta.freq_range) if ta.freq_range else 0,
'polarization': ta.polarization.name if ta.polarization else '-',
'modulation': ta.modulation.name if ta.modulation else '-',
'bod_velocity': float(ta.bod_velocity) if ta.bod_velocity else 0,
'coords_average': coords,
'satellite': ta.satellite.name if ta.satellite else '-',
'marks': [],
})
return JsonResponse({
'periods': [],
'data': data,
'message': 'Нет отметок за последние 90 дней',
})
# Генерируем список дней от max_date до min_date (от новых к старым)
days = []
current_date = max_date.date()
end_date = min_date.date()
while current_date >= end_date:
days.append(current_date)
current_date -= timedelta(days=1)
# Формируем заголовки колонок (дни)
periods = []
for day in days:
periods.append({
'date': day,
'label': day.strftime('%d.%m'),
})
# Загружаем все отметки
all_marks = list(marks_qs.order_by('-timestamp'))
# Создаём словарь: {(tech_analyze_id, date): список всех отметок за день}
marks_dict = {}
for mark in all_marks:
mark_date = timezone.localtime(mark.timestamp).date()
key = (mark.tech_analyze_id, mark_date)
if key not in marks_dict:
marks_dict[key] = []
marks_dict[key].append(mark)
# Формируем данные
data = []
for ta in tech_analyzes:
source_id = name_to_source_id.get(ta.name)
coords = source_coords.get(source_id, '-') if source_id else '-'
row = {
'id': ta.id,
'name': ta.name,
'frequency': float(ta.frequency) if ta.frequency else 0,
'freq_range': float(ta.freq_range) if ta.freq_range else 0,
'polarization': ta.polarization.name if ta.polarization else '-',
'modulation': ta.modulation.name if ta.modulation else '-',
'bod_velocity': float(ta.bod_velocity) if ta.bod_velocity else 0,
'coords_average': coords,
'satellite': ta.satellite.name if ta.satellite else '-',
'marks': [],
}
# Для каждого дня собираем все отметки
for period in periods:
key = (ta.id, period['date'])
day_marks = marks_dict.get(key, [])
if day_marks:
# Сортируем по времени (от раннего к позднему)
day_marks_sorted = sorted(day_marks, key=lambda m: m.timestamp)
marks_list = []
for mark in day_marks_sorted:
local_time = timezone.localtime(mark.timestamp)
marks_list.append({
'mark': mark.mark,
'user': str(mark.created_by) if mark.created_by else '-',
'time': local_time.strftime('%H:%M'),
})
row['marks'].append({
'count': len(marks_list),
'items': marks_list,
})
else:
row['marks'].append(None)
data.append(row)
return JsonResponse({
'periods': [p['label'] for p in periods],
'data': data,
'total': len(data),
'date_range': f"Последние 90 дней (с {date_90_days_ago.strftime('%d.%m.%Y')})",
})