Добавил отметки с источниками
This commit is contained in:
301
dbapp/mainapp/views/source_marks.py
Normal file
301
dbapp/mainapp/views/source_marks.py
Normal file
@@ -0,0 +1,301 @@
|
||||
"""
|
||||
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')})",
|
||||
})
|
||||
Reference in New Issue
Block a user