Переделал усреднение. Вариант 1
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
Points averaging view for satellite data grouping by day/night intervals.
|
Points averaging view for satellite data grouping by day/night intervals.
|
||||||
|
Groups points by Source, then by time intervals within each Source.
|
||||||
"""
|
"""
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
@@ -8,7 +9,7 @@ from django.shortcuts import render
|
|||||||
from django.views import View
|
from django.views import View
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from ..models import ObjItem, Satellite
|
from ..models import ObjItem, Satellite, Source
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
calculate_mean_coords,
|
calculate_mean_coords,
|
||||||
calculate_distance_wgs84,
|
calculate_distance_wgs84,
|
||||||
@@ -29,8 +30,9 @@ class PointsAveragingView(LoginRequiredMixin, View):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
# Get satellites that have points with geo data
|
# Get satellites that have sources with points with geo data
|
||||||
satellites = Satellite.objects.filter(
|
satellites = Satellite.objects.filter(
|
||||||
|
parameters__objitem__source__isnull=False,
|
||||||
parameters__objitem__geo_obj__coords__isnull=False
|
parameters__objitem__geo_obj__coords__isnull=False
|
||||||
).distinct().order_by('name')
|
).distinct().order_by('name')
|
||||||
|
|
||||||
@@ -44,13 +46,14 @@ class PointsAveragingView(LoginRequiredMixin, View):
|
|||||||
|
|
||||||
class PointsAveragingAPIView(LoginRequiredMixin, View):
|
class PointsAveragingAPIView(LoginRequiredMixin, View):
|
||||||
"""
|
"""
|
||||||
API endpoint for grouping and averaging points by day/night intervals.
|
API endpoint for grouping and averaging points by Source and day/night intervals.
|
||||||
|
|
||||||
Groups points into:
|
Groups points into:
|
||||||
- Day: 08:00 - 19:00
|
- Day: 08:00 - 19:00
|
||||||
- Night: 19:00 - 08:00 (next day)
|
- Night: 19:00 - 08:00 (next day)
|
||||||
|
- Weekend: Friday 19:00 - Monday 08:00
|
||||||
|
|
||||||
For each group, calculates average coordinates and checks for outliers (>56 km).
|
For each group within each Source, calculates average coordinates and checks for outliers (>56 km).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
@@ -76,9 +79,50 @@ class PointsAveragingAPIView(LoginRequiredMixin, View):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
return JsonResponse({'error': 'Неверный формат даты'}, status=400)
|
return JsonResponse({'error': 'Неверный формат даты'}, status=400)
|
||||||
|
|
||||||
# Get all points for the satellite in the date range
|
# Get all Sources for the satellite that have points in the date range
|
||||||
objitems = ObjItem.objects.filter(
|
sources = Source.objects.filter(
|
||||||
parameter_obj__id_satellite=satellite,
|
source_objitems__parameter_obj__id_satellite=satellite,
|
||||||
|
source_objitems__geo_obj__coords__isnull=False,
|
||||||
|
source_objitems__geo_obj__timestamp__gte=date_from_obj,
|
||||||
|
source_objitems__geo_obj__timestamp__lt=date_to_obj,
|
||||||
|
).distinct().prefetch_related(
|
||||||
|
'source_objitems',
|
||||||
|
'source_objitems__geo_obj',
|
||||||
|
'source_objitems__geo_obj__mirrors',
|
||||||
|
'source_objitems__parameter_obj',
|
||||||
|
'source_objitems__parameter_obj__polarization',
|
||||||
|
'source_objitems__parameter_obj__modulation',
|
||||||
|
'source_objitems__parameter_obj__standard',
|
||||||
|
)
|
||||||
|
|
||||||
|
if not sources.exists():
|
||||||
|
return JsonResponse({'error': 'Источники не найдены в указанном диапазоне'}, status=404)
|
||||||
|
|
||||||
|
# Process each source
|
||||||
|
result_sources = []
|
||||||
|
for source in sources:
|
||||||
|
source_data = self._process_source(source, date_from_obj, date_to_obj)
|
||||||
|
if source_data['groups']: # Only add if has groups with points
|
||||||
|
result_sources.append(source_data)
|
||||||
|
|
||||||
|
if not result_sources:
|
||||||
|
return JsonResponse({'error': 'Точки не найдены в указанном диапазоне'}, status=404)
|
||||||
|
|
||||||
|
return JsonResponse({
|
||||||
|
'success': True,
|
||||||
|
'satellite': satellite.name,
|
||||||
|
'date_from': date_from,
|
||||||
|
'date_to': date_to,
|
||||||
|
'sources': result_sources,
|
||||||
|
'total_sources': len(result_sources),
|
||||||
|
})
|
||||||
|
|
||||||
|
def _process_source(self, source, date_from_obj, date_to_obj):
|
||||||
|
"""
|
||||||
|
Process a single Source: get its points and group them by time intervals.
|
||||||
|
"""
|
||||||
|
# Get all points for this source in the date range
|
||||||
|
objitems = source.source_objitems.filter(
|
||||||
geo_obj__coords__isnull=False,
|
geo_obj__coords__isnull=False,
|
||||||
geo_obj__timestamp__gte=date_from_obj,
|
geo_obj__timestamp__gte=date_from_obj,
|
||||||
geo_obj__timestamp__lt=date_to_obj,
|
geo_obj__timestamp__lt=date_to_obj,
|
||||||
@@ -89,16 +133,12 @@ class PointsAveragingAPIView(LoginRequiredMixin, View):
|
|||||||
'parameter_obj__modulation',
|
'parameter_obj__modulation',
|
||||||
'parameter_obj__standard',
|
'parameter_obj__standard',
|
||||||
'geo_obj',
|
'geo_obj',
|
||||||
'source',
|
|
||||||
).prefetch_related(
|
).prefetch_related(
|
||||||
'geo_obj__mirrors'
|
'geo_obj__mirrors'
|
||||||
).order_by('geo_obj__timestamp')
|
).order_by('geo_obj__timestamp')
|
||||||
|
|
||||||
if not objitems.exists():
|
# Group points by day/night intervals
|
||||||
return JsonResponse({'error': 'Точки не найдены в указанном диапазоне'}, status=404)
|
groups = self._group_points_by_intervals(list(objitems))
|
||||||
|
|
||||||
# Group points by source name and day/night intervals
|
|
||||||
groups = self._group_points_by_intervals(objitems)
|
|
||||||
|
|
||||||
# Process each group: calculate average and check for outliers
|
# Process each group: calculate average and check for outliers
|
||||||
result_groups = []
|
result_groups = []
|
||||||
@@ -106,21 +146,27 @@ class PointsAveragingAPIView(LoginRequiredMixin, View):
|
|||||||
group_result = self._process_group(group_key, points)
|
group_result = self._process_group(group_key, points)
|
||||||
result_groups.append(group_result)
|
result_groups.append(group_result)
|
||||||
|
|
||||||
return JsonResponse({
|
# Get source name from first point or use ID
|
||||||
'success': True,
|
source_name = f"Источник #{source.id}"
|
||||||
'satellite': satellite.name,
|
if objitems.exists():
|
||||||
'date_from': date_from,
|
first_point = objitems.first()
|
||||||
'date_to': date_to,
|
if first_point.name:
|
||||||
|
source_name = first_point.name
|
||||||
|
|
||||||
|
return {
|
||||||
|
'source_id': source.id,
|
||||||
|
'source_name': source_name,
|
||||||
|
'total_points': sum(len(g['points']) for g in result_groups),
|
||||||
'groups': result_groups,
|
'groups': result_groups,
|
||||||
'total_groups': len(result_groups),
|
}
|
||||||
})
|
|
||||||
|
|
||||||
def _group_points_by_intervals(self, objitems):
|
def _group_points_by_intervals(self, objitems):
|
||||||
"""
|
"""
|
||||||
Group points by source name and day/night intervals.
|
Group points by day/night intervals.
|
||||||
|
|
||||||
Day: 08:00 - 19:00
|
Day: 08:00 - 19:00
|
||||||
Night: 19:00 - 08:00 (next day)
|
Night: 19:00 - 08:00 (next day)
|
||||||
|
Weekend: Friday 19:00 - Monday 08:00
|
||||||
"""
|
"""
|
||||||
groups = {}
|
groups = {}
|
||||||
|
|
||||||
@@ -129,19 +175,14 @@ class PointsAveragingAPIView(LoginRequiredMixin, View):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
timestamp = timezone.localtime(objitem.geo_obj.timestamp)
|
timestamp = timezone.localtime(objitem.geo_obj.timestamp)
|
||||||
# timestamp = objitem.geo_obj.timestamp
|
|
||||||
source_name = objitem.name or f"Объект #{objitem.id}"
|
|
||||||
|
|
||||||
# Determine interval
|
# Determine interval
|
||||||
interval_key = self._get_interval_key(timestamp)
|
interval_key = self._get_interval_key(timestamp)
|
||||||
|
|
||||||
# Create group key: (source_name, interval_key)
|
if interval_key not in groups:
|
||||||
group_key = (source_name, interval_key)
|
groups[interval_key] = []
|
||||||
|
|
||||||
if group_key not in groups:
|
groups[interval_key].append(objitem)
|
||||||
groups[group_key] = []
|
|
||||||
|
|
||||||
groups[group_key].append(objitem)
|
|
||||||
|
|
||||||
return groups
|
return groups
|
||||||
|
|
||||||
@@ -208,7 +249,7 @@ class PointsAveragingAPIView(LoginRequiredMixin, View):
|
|||||||
return date - timedelta(days=3)
|
return date - timedelta(days=3)
|
||||||
return date
|
return date
|
||||||
|
|
||||||
def _process_group(self, group_key, points):
|
def _process_group(self, interval_key, points):
|
||||||
"""
|
"""
|
||||||
Process a group of points: calculate average and check for outliers.
|
Process a group of points: calculate average and check for outliers.
|
||||||
|
|
||||||
@@ -218,8 +259,6 @@ class PointsAveragingAPIView(LoginRequiredMixin, View):
|
|||||||
3. Iteratively add points within 56 km of current average
|
3. Iteratively add points within 56 km of current average
|
||||||
4. Points not within 56 km of final average are outliers
|
4. Points not within 56 km of final average are outliers
|
||||||
"""
|
"""
|
||||||
source_name, interval_key = group_key
|
|
||||||
|
|
||||||
# Parse interval info
|
# Parse interval info
|
||||||
date_str, interval_type = interval_key.rsplit('_', 1)
|
date_str, interval_type = interval_key.rsplit('_', 1)
|
||||||
interval_date = datetime.strptime(date_str, '%Y-%m-%d').date()
|
interval_date = datetime.strptime(date_str, '%Y-%m-%d').date()
|
||||||
@@ -322,7 +361,7 @@ class PointsAveragingAPIView(LoginRequiredMixin, View):
|
|||||||
# Calculate median time from valid points using timestamp_objects array
|
# Calculate median time from valid points using timestamp_objects array
|
||||||
valid_timestamps = []
|
valid_timestamps = []
|
||||||
for i in valid_indices:
|
for i in valid_indices:
|
||||||
if timestamp_objects[i]:
|
if i < len(timestamp_objects) and timestamp_objects[i]:
|
||||||
valid_timestamps.append(timestamp_objects[i])
|
valid_timestamps.append(timestamp_objects[i])
|
||||||
|
|
||||||
median_time_str = '-'
|
median_time_str = '-'
|
||||||
@@ -344,7 +383,6 @@ class PointsAveragingAPIView(LoginRequiredMixin, View):
|
|||||||
median_time_str = timezone.localtime(median_datetime).strftime("%d.%m.%Y %H:%M")
|
median_time_str = timezone.localtime(median_datetime).strftime("%d.%m.%Y %H:%M")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'source_name': source_name,
|
|
||||||
'interval_key': interval_key,
|
'interval_key': interval_key,
|
||||||
'interval_label': interval_label,
|
'interval_label': interval_label,
|
||||||
'total_points': len(points_data),
|
'total_points': len(points_data),
|
||||||
|
|||||||
Reference in New Issue
Block a user