Поправил отображение отметок
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -68,6 +68,7 @@ class SignalMarksHistoryAPIView(LoginRequiredMixin, View):
|
|||||||
"""
|
"""
|
||||||
API для получения истории отметок с фиксированными 15 колонками.
|
API для получения истории отметок с фиксированными 15 колонками.
|
||||||
Делит выбранный временной диапазон на 15 равных периодов.
|
Делит выбранный временной диапазон на 15 равных периодов.
|
||||||
|
Поддерживает два режима: группировка по диапазонам и все отметки за 90 дней.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
NUM_COLUMNS = 15 # Фиксированное количество колонок
|
NUM_COLUMNS = 15 # Фиксированное количество колонок
|
||||||
@@ -81,6 +82,18 @@ class SignalMarksHistoryAPIView(LoginRequiredMixin, View):
|
|||||||
date_to = request.GET.get('date_to')
|
date_to = request.GET.get('date_to')
|
||||||
page = int(request.GET.get('page', 1))
|
page = int(request.GET.get('page', 1))
|
||||||
size = int(request.GET.get('size', 50))
|
size = int(request.GET.get('size', 50))
|
||||||
|
view_mode = request.GET.get('view_mode', 'grouped') # 'grouped' или 'all_marks'
|
||||||
|
|
||||||
|
# Фильтры
|
||||||
|
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')
|
||||||
|
search = request.GET.get('search', '').strip()
|
||||||
|
|
||||||
if not satellite_id:
|
if not satellite_id:
|
||||||
return JsonResponse({'error': 'Не выбран спутник'}, status=400)
|
return JsonResponse({'error': 'Не выбран спутник'}, status=400)
|
||||||
@@ -92,11 +105,56 @@ class SignalMarksHistoryAPIView(LoginRequiredMixin, View):
|
|||||||
'polarization', 'modulation', 'standard'
|
'polarization', 'modulation', 'standard'
|
||||||
).order_by('frequency', 'name')
|
).order_by('frequency', 'name')
|
||||||
|
|
||||||
|
# Применяем фильтры к теханализам
|
||||||
|
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)
|
||||||
|
)
|
||||||
|
|
||||||
# Базовый фильтр отметок по спутнику
|
# Базовый фильтр отметок по спутнику
|
||||||
marks_base_qs = ObjectMark.objects.filter(
|
marks_base_qs = ObjectMark.objects.filter(
|
||||||
tech_analyze__satellite_id=satellite_id
|
tech_analyze__satellite_id=satellite_id
|
||||||
).select_related('created_by__user', 'tech_analyze')
|
).select_related('created_by__user', 'tech_analyze')
|
||||||
|
|
||||||
|
# Режим "все отметки за 90 дней"
|
||||||
|
if view_mode == 'all_marks':
|
||||||
|
return self._get_all_marks_mode(request, tech_analyzes, marks_base_qs, size, page)
|
||||||
|
|
||||||
|
# Режим группировки по диапазонам (по умолчанию)
|
||||||
# Определяем диапазон дат
|
# Определяем диапазон дат
|
||||||
parsed_date_from = None
|
parsed_date_from = None
|
||||||
parsed_date_to = None
|
parsed_date_to = None
|
||||||
@@ -127,6 +185,7 @@ class SignalMarksHistoryAPIView(LoginRequiredMixin, View):
|
|||||||
'last_page': 1,
|
'last_page': 1,
|
||||||
'total': 0,
|
'total': 0,
|
||||||
'message': 'Нет отметок в выбранном диапазоне',
|
'message': 'Нет отметок в выбранном диапазоне',
|
||||||
|
'view_mode': view_mode,
|
||||||
})
|
})
|
||||||
|
|
||||||
# Используем указанные даты или данные из БД
|
# Используем указанные даты или данные из БД
|
||||||
@@ -143,9 +202,9 @@ class SignalMarksHistoryAPIView(LoginRequiredMixin, View):
|
|||||||
total_duration = end_dt - start_dt
|
total_duration = end_dt - start_dt
|
||||||
period_duration = total_duration / self.NUM_COLUMNS
|
period_duration = total_duration / self.NUM_COLUMNS
|
||||||
|
|
||||||
# Генерируем границы периодов
|
# Генерируем границы периодов (от новых к старым)
|
||||||
periods = []
|
periods = []
|
||||||
for i in range(self.NUM_COLUMNS):
|
for i in range(self.NUM_COLUMNS - 1, -1, -1):
|
||||||
period_start = start_dt + (period_duration * i)
|
period_start = start_dt + (period_duration * i)
|
||||||
period_end = start_dt + (period_duration * (i + 1))
|
period_end = start_dt + (period_duration * (i + 1))
|
||||||
periods.append({
|
periods.append({
|
||||||
@@ -172,6 +231,11 @@ class SignalMarksHistoryAPIView(LoginRequiredMixin, View):
|
|||||||
row = {
|
row = {
|
||||||
'id': ta.id,
|
'id': ta.id,
|
||||||
'name': ta.name,
|
'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,
|
||||||
'marks': [],
|
'marks': [],
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,6 +268,129 @@ class SignalMarksHistoryAPIView(LoginRequiredMixin, View):
|
|||||||
'data': data,
|
'data': data,
|
||||||
'last_page': num_pages,
|
'last_page': num_pages,
|
||||||
'total': total_count,
|
'total': total_count,
|
||||||
|
'view_mode': view_mode,
|
||||||
|
})
|
||||||
|
|
||||||
|
def _get_all_marks_mode(self, request, tech_analyzes, marks_base_qs, size, page):
|
||||||
|
"""
|
||||||
|
Режим отображения отметок за последние 90 дней.
|
||||||
|
Группировка по дням (1 колонка = 1 день) для оптимизации.
|
||||||
|
Максимум 90 колонок.
|
||||||
|
"""
|
||||||
|
from datetime import datetime, date
|
||||||
|
|
||||||
|
# Фильтруем отметки за последние 90 дней
|
||||||
|
date_90_days_ago = timezone.now() - timedelta(days=90)
|
||||||
|
marks_base_qs = marks_base_qs.filter(timestamp__gte=date_90_days_ago)
|
||||||
|
|
||||||
|
# Получаем диапазон дат с отметками
|
||||||
|
date_range = marks_base_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:
|
||||||
|
return JsonResponse({
|
||||||
|
'periods': [],
|
||||||
|
'data': [],
|
||||||
|
'last_page': 1,
|
||||||
|
'total': 0,
|
||||||
|
'message': 'Нет отметок за последние 90 дней',
|
||||||
|
'view_mode': 'all_marks',
|
||||||
|
})
|
||||||
|
|
||||||
|
# Генерируем список дней от 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'),
|
||||||
|
})
|
||||||
|
|
||||||
|
# Пагинация теханализов
|
||||||
|
if size == 0:
|
||||||
|
page_obj = tech_analyzes
|
||||||
|
num_pages = 1
|
||||||
|
total_count = tech_analyzes.count()
|
||||||
|
else:
|
||||||
|
paginator = Paginator(tech_analyzes, size)
|
||||||
|
page_obj = paginator.get_page(page)
|
||||||
|
num_pages = paginator.num_pages
|
||||||
|
total_count = paginator.count
|
||||||
|
|
||||||
|
# Получаем ID теханализов на текущей странице
|
||||||
|
ta_ids = [ta.id for ta in page_obj]
|
||||||
|
|
||||||
|
# Загружаем все отметки для этих теханализов одним запросом
|
||||||
|
all_marks = list(marks_base_qs.filter(
|
||||||
|
tech_analyze_id__in=ta_ids
|
||||||
|
).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 page_obj:
|
||||||
|
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,
|
||||||
|
'marks': [],
|
||||||
|
}
|
||||||
|
|
||||||
|
# Для каждого дня собираем все отметки
|
||||||
|
for period in periods:
|
||||||
|
key = (ta.id, period['date'])
|
||||||
|
day_marks = marks_dict.get(key, [])
|
||||||
|
|
||||||
|
if day_marks:
|
||||||
|
# Формируем список всех отметок за день
|
||||||
|
marks_list = []
|
||||||
|
for mark in day_marks:
|
||||||
|
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,
|
||||||
|
'last_page': num_pages,
|
||||||
|
'total': total_count,
|
||||||
|
'view_mode': 'all_marks',
|
||||||
|
'date_range': f"Последние 90 дней (с {date_90_days_ago.strftime('%d.%m.%Y')})",
|
||||||
})
|
})
|
||||||
|
|
||||||
def _format_period_label(self, start, end, total_duration):
|
def _format_period_label(self, start, end, total_duration):
|
||||||
|
|||||||
Reference in New Issue
Block a user