763 lines
32 KiB
Python
763 lines
32 KiB
Python
"""
|
||
API endpoints for AJAX requests and data retrieval.
|
||
"""
|
||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||
from django.http import JsonResponse
|
||
from django.utils import timezone
|
||
from django.views import View
|
||
|
||
from ..models import ObjItem
|
||
from ..utils import format_coordinate, format_coords_display, format_frequency, format_symbol_rate
|
||
|
||
|
||
class GetLocationsView(LoginRequiredMixin, View):
|
||
"""API endpoint for getting locations by satellite ID in GeoJSON format."""
|
||
|
||
def get(self, request, sat_id):
|
||
locations = (
|
||
ObjItem.objects.filter(parameter_obj__id_satellite=sat_id)
|
||
.select_related(
|
||
"geo_obj",
|
||
"parameter_obj",
|
||
"parameter_obj__polarization",
|
||
)
|
||
)
|
||
|
||
if not locations.exists():
|
||
return JsonResponse({"error": "Объектов не найдено"}, status=404)
|
||
|
||
features = []
|
||
for loc in locations:
|
||
if not hasattr(loc, "geo_obj") or not loc.geo_obj or not loc.geo_obj.coords:
|
||
continue
|
||
|
||
param = getattr(loc, 'parameter_obj', None)
|
||
if not param:
|
||
continue
|
||
|
||
features.append(
|
||
{
|
||
"type": "Feature",
|
||
"geometry": {
|
||
"type": "Point",
|
||
"coordinates": [loc.geo_obj.coords[0], loc.geo_obj.coords[1]],
|
||
},
|
||
"properties": {
|
||
"pol": param.polarization.name if param.polarization else "-",
|
||
"freq": param.frequency * 1000000 if param.frequency else 0,
|
||
"name": loc.name or "-",
|
||
"id": loc.geo_obj.id,
|
||
},
|
||
}
|
||
)
|
||
|
||
return JsonResponse({"type": "FeatureCollection", "features": features})
|
||
|
||
|
||
class LyngsatDataAPIView(LoginRequiredMixin, View):
|
||
"""API endpoint for getting LyngSat source data."""
|
||
|
||
def get(self, request, lyngsat_id):
|
||
from lyngsatapp.models import LyngSat
|
||
|
||
try:
|
||
lyngsat = LyngSat.objects.select_related(
|
||
'id_satellite',
|
||
'polarization',
|
||
'modulation',
|
||
'standard'
|
||
).get(id=lyngsat_id)
|
||
|
||
# Format date with local timezone
|
||
last_update_str = '-'
|
||
if lyngsat.last_update:
|
||
local_time = timezone.localtime(lyngsat.last_update)
|
||
last_update_str = local_time.strftime("%d.%m.%Y")
|
||
|
||
data = {
|
||
'id': lyngsat.id,
|
||
'satellite': lyngsat.id_satellite.name if lyngsat.id_satellite else '-',
|
||
'frequency': format_frequency(lyngsat.frequency),
|
||
'polarization': lyngsat.polarization.name if lyngsat.polarization else '-',
|
||
'modulation': lyngsat.modulation.name if lyngsat.modulation else '-',
|
||
'standard': lyngsat.standard.name if lyngsat.standard else '-',
|
||
'sym_velocity': format_symbol_rate(lyngsat.sym_velocity),
|
||
'fec': lyngsat.fec or '-',
|
||
'channel_info': lyngsat.channel_info or '-',
|
||
'last_update': last_update_str,
|
||
'url': lyngsat.url or None,
|
||
}
|
||
|
||
return JsonResponse(data)
|
||
except LyngSat.DoesNotExist:
|
||
return JsonResponse({'error': 'Источник LyngSat не найден'}, status=404)
|
||
except Exception as e:
|
||
return JsonResponse({'error': str(e)}, status=500)
|
||
|
||
|
||
class SigmaParameterDataAPIView(LoginRequiredMixin, View):
|
||
"""API endpoint for getting SigmaParameter data."""
|
||
|
||
def get(self, request, parameter_id):
|
||
from ..models import Parameter
|
||
|
||
try:
|
||
parameter = Parameter.objects.select_related(
|
||
'id_satellite',
|
||
'polarization',
|
||
'modulation',
|
||
'standard'
|
||
).prefetch_related(
|
||
'sigma_parameter__mark',
|
||
'sigma_parameter__id_satellite',
|
||
'sigma_parameter__polarization',
|
||
'sigma_parameter__modulation',
|
||
'sigma_parameter__standard'
|
||
).get(id=parameter_id)
|
||
|
||
# Get all related SigmaParameter
|
||
sigma_params = parameter.sigma_parameter.all()
|
||
|
||
sigma_data = []
|
||
for sigma in sigma_params:
|
||
# Get marks
|
||
marks = []
|
||
for mark in sigma.mark.all().order_by('-timestamp'):
|
||
mark_str = '+' if mark.mark else '-'
|
||
date_str = '-'
|
||
if mark.timestamp:
|
||
local_time = timezone.localtime(mark.timestamp)
|
||
date_str = local_time.strftime("%d.%m.%Y %H:%M")
|
||
marks.append({
|
||
'mark': mark_str,
|
||
'date': date_str
|
||
})
|
||
|
||
# Format start and end dates
|
||
datetime_begin_str = '-'
|
||
if sigma.datetime_begin:
|
||
local_time = timezone.localtime(sigma.datetime_begin)
|
||
datetime_begin_str = local_time.strftime("%d.%m.%Y %H:%M")
|
||
|
||
datetime_end_str = '-'
|
||
if sigma.datetime_end:
|
||
local_time = timezone.localtime(sigma.datetime_end)
|
||
datetime_end_str = local_time.strftime("%d.%m.%Y %H:%M")
|
||
|
||
sigma_data.append({
|
||
'id': sigma.id,
|
||
'satellite': sigma.id_satellite.name if sigma.id_satellite else '-',
|
||
'frequency': format_frequency(sigma.frequency),
|
||
'transfer_frequency': format_frequency(sigma.transfer_frequency),
|
||
'freq_range': format_frequency(sigma.freq_range),
|
||
'polarization': sigma.polarization.name if sigma.polarization else '-',
|
||
'modulation': sigma.modulation.name if sigma.modulation else '-',
|
||
'standard': sigma.standard.name if sigma.standard else '-',
|
||
'bod_velocity': format_symbol_rate(sigma.bod_velocity),
|
||
'snr': f"{sigma.snr:.1f}" if sigma.snr is not None else '-',
|
||
'power': f"{sigma.power:.1f}" if sigma.power is not None else '-',
|
||
'status': sigma.status or '-',
|
||
'packets': 'Да' if sigma.packets else 'Нет' if sigma.packets is not None else '-',
|
||
'datetime_begin': datetime_begin_str,
|
||
'datetime_end': datetime_end_str,
|
||
'marks': marks
|
||
})
|
||
|
||
return JsonResponse({
|
||
'parameter_id': parameter.id,
|
||
'sigma_parameters': sigma_data
|
||
})
|
||
except Parameter.DoesNotExist:
|
||
return JsonResponse({'error': 'Parameter не найден'}, status=404)
|
||
except Exception as e:
|
||
return JsonResponse({'error': str(e)}, status=500)
|
||
|
||
|
||
class SourceObjItemsAPIView(LoginRequiredMixin, View):
|
||
"""API endpoint for getting ObjItems related to a Source."""
|
||
|
||
def get(self, request, source_id):
|
||
from datetime import datetime, timedelta
|
||
from ..models import Source
|
||
|
||
try:
|
||
# Get filter parameters from query string
|
||
geo_date_from = request.GET.get("geo_date_from", "").strip()
|
||
geo_date_to = request.GET.get("geo_date_to", "").strip()
|
||
|
||
# Load Source with prefetch_related for ObjItem
|
||
source = Source.objects.prefetch_related(
|
||
'source_objitems',
|
||
'source_objitems__parameter_obj',
|
||
'source_objitems__parameter_obj__id_satellite',
|
||
'source_objitems__parameter_obj__polarization',
|
||
'source_objitems__parameter_obj__modulation',
|
||
'source_objitems__parameter_obj__standard',
|
||
'source_objitems__geo_obj',
|
||
'source_objitems__geo_obj__mirrors',
|
||
'source_objitems__lyngsat_source',
|
||
'source_objitems__transponder',
|
||
'source_objitems__created_by__user',
|
||
'source_objitems__updated_by__user',
|
||
# 'marks',
|
||
# 'marks__created_by__user'
|
||
).get(id=source_id)
|
||
|
||
# Get all related ObjItems, sorted by created_at
|
||
objitems = source.source_objitems.all()
|
||
|
||
# Apply Geo timestamp filter if provided
|
||
if geo_date_from:
|
||
try:
|
||
geo_date_from_obj = datetime.strptime(geo_date_from, "%Y-%m-%d")
|
||
objitems = objitems.filter(
|
||
geo_obj__isnull=False,
|
||
geo_obj__timestamp__gte=geo_date_from_obj
|
||
)
|
||
except (ValueError, TypeError):
|
||
pass
|
||
|
||
if geo_date_to:
|
||
try:
|
||
geo_date_to_obj = datetime.strptime(geo_date_to, "%Y-%m-%d")
|
||
# Add one day to include entire end date
|
||
geo_date_to_obj = geo_date_to_obj + timedelta(days=1)
|
||
objitems = objitems.filter(
|
||
geo_obj__isnull=False,
|
||
geo_obj__timestamp__lt=geo_date_to_obj
|
||
)
|
||
except (ValueError, TypeError):
|
||
pass
|
||
|
||
objitems = objitems.order_by('created_at')
|
||
|
||
objitems_data = []
|
||
for objitem in objitems:
|
||
# Get parameter data
|
||
param = getattr(objitem, 'parameter_obj', None)
|
||
satellite_name = '-'
|
||
satellite_id = None
|
||
frequency = '-'
|
||
freq_range = '-'
|
||
polarization = '-'
|
||
bod_velocity = '-'
|
||
modulation = '-'
|
||
standard = '-'
|
||
snr = '-'
|
||
parameter_id = None
|
||
|
||
if param:
|
||
parameter_id = param.id
|
||
if hasattr(param, 'id_satellite') and param.id_satellite:
|
||
satellite_name = param.id_satellite.name
|
||
satellite_id = param.id_satellite.id
|
||
frequency = format_frequency(param.frequency)
|
||
freq_range = format_frequency(param.freq_range)
|
||
if hasattr(param, 'polarization') and param.polarization:
|
||
polarization = param.polarization.name
|
||
bod_velocity = format_symbol_rate(param.bod_velocity)
|
||
if hasattr(param, 'modulation') and param.modulation:
|
||
modulation = param.modulation.name
|
||
if hasattr(param, 'standard') and param.standard:
|
||
standard = param.standard.name
|
||
snr = f"{param.snr:.0f}" if param.snr is not None else '-'
|
||
|
||
# Get geo data
|
||
geo_timestamp = '-'
|
||
geo_location = '-'
|
||
geo_coords = '-'
|
||
|
||
if hasattr(objitem, 'geo_obj') and objitem.geo_obj:
|
||
if objitem.geo_obj.timestamp:
|
||
local_time = timezone.localtime(objitem.geo_obj.timestamp)
|
||
geo_timestamp = local_time.strftime("%d.%m.%Y %H:%M")
|
||
|
||
geo_location = objitem.geo_obj.location or '-'
|
||
|
||
if objitem.geo_obj.coords:
|
||
geo_coords = format_coords_display(objitem.geo_obj.coords)
|
||
|
||
# Get created/updated info
|
||
created_at = '-'
|
||
if objitem.created_at:
|
||
local_time = timezone.localtime(objitem.created_at)
|
||
created_at = local_time.strftime("%d.%m.%Y %H:%M")
|
||
|
||
updated_at = '-'
|
||
if objitem.updated_at:
|
||
local_time = timezone.localtime(objitem.updated_at)
|
||
updated_at = local_time.strftime("%d.%m.%Y %H:%M")
|
||
|
||
created_by = str(objitem.created_by) if objitem.created_by else '-'
|
||
updated_by = str(objitem.updated_by) if objitem.updated_by else '-'
|
||
|
||
# Check for LyngSat
|
||
has_lyngsat = hasattr(objitem, 'lyngsat_source') and objitem.lyngsat_source is not None
|
||
lyngsat_id = objitem.lyngsat_source.id if has_lyngsat else None
|
||
|
||
# Check for Transponder
|
||
has_transponder = hasattr(objitem, 'transponder') and objitem.transponder is not None
|
||
transponder_id = objitem.transponder.id if has_transponder else None
|
||
transponder_info = '-'
|
||
if has_transponder:
|
||
try:
|
||
downlink = objitem.transponder.downlink if objitem.transponder.downlink else '-'
|
||
freq_range_t = objitem.transponder.frequency_range if objitem.transponder.frequency_range else '-'
|
||
transponder_info = f"{downlink}:{freq_range_t}"
|
||
except Exception:
|
||
transponder_info = '-'
|
||
|
||
# Check for Sigma
|
||
has_sigma = False
|
||
sigma_info = '-'
|
||
if param and hasattr(param, 'sigma_parameter'):
|
||
sigma_count = param.sigma_parameter.count()
|
||
if sigma_count > 0:
|
||
has_sigma = True
|
||
sigma_info = f"{sigma_count}"
|
||
|
||
# Get comment, is_average, and mirrors from geo_obj
|
||
comment = '-'
|
||
is_average = '-'
|
||
mirrors = '-'
|
||
if hasattr(objitem, 'geo_obj') and objitem.geo_obj:
|
||
comment = objitem.geo_obj.comment or '-'
|
||
is_average = 'Да' if objitem.geo_obj.is_average else 'Нет'
|
||
# Get mirrors list
|
||
mirrors_list = list(objitem.geo_obj.mirrors.values_list('name', flat=True))
|
||
mirrors = ', '.join(mirrors_list) if mirrors_list else '-'
|
||
|
||
objitems_data.append({
|
||
'id': objitem.id,
|
||
'name': objitem.name or '-',
|
||
'satellite_name': satellite_name,
|
||
'satellite_id': satellite_id,
|
||
'frequency': frequency,
|
||
'freq_range': freq_range,
|
||
'polarization': polarization,
|
||
'bod_velocity': bod_velocity,
|
||
'modulation': modulation,
|
||
'standard': standard,
|
||
'snr': snr,
|
||
'geo_timestamp': geo_timestamp,
|
||
'geo_location': geo_location,
|
||
'geo_coords': geo_coords,
|
||
'created_at': created_at,
|
||
'updated_at': updated_at,
|
||
'created_by': created_by,
|
||
'updated_by': updated_by,
|
||
'comment': comment,
|
||
'is_average': is_average,
|
||
'has_lyngsat': has_lyngsat,
|
||
'lyngsat_id': lyngsat_id,
|
||
'has_transponder': has_transponder,
|
||
'transponder_id': transponder_id,
|
||
'transponder_info': transponder_info,
|
||
'has_sigma': has_sigma,
|
||
'sigma_info': sigma_info,
|
||
'parameter_id': parameter_id,
|
||
'mirrors': mirrors,
|
||
})
|
||
|
||
# Отметки теперь привязаны к TechAnalyze, а не к Source
|
||
# marks_data оставляем пустым для обратной совместимости
|
||
marks_data = []
|
||
|
||
return JsonResponse({
|
||
'source_id': source_id,
|
||
'objitems': objitems_data,
|
||
'marks': marks_data
|
||
})
|
||
except Source.DoesNotExist:
|
||
return JsonResponse({'error': 'Источник не найден'}, status=404)
|
||
except Exception as e:
|
||
return JsonResponse({'error': str(e)}, status=500)
|
||
|
||
|
||
class LyngsatTaskStatusAPIView(LoginRequiredMixin, View):
|
||
"""API endpoint for getting Celery task status."""
|
||
|
||
def get(self, request, task_id):
|
||
from celery.result import AsyncResult
|
||
from django.core.cache import cache
|
||
|
||
task = AsyncResult(task_id)
|
||
|
||
response_data = {
|
||
'task_id': task_id,
|
||
'state': task.state,
|
||
'result': None,
|
||
'error': None
|
||
}
|
||
|
||
if task.state == 'PENDING':
|
||
response_data['status'] = 'Задача в очереди...'
|
||
elif task.state == 'PROGRESS':
|
||
response_data['status'] = task.info.get('status', '')
|
||
response_data['current'] = task.info.get('current', 0)
|
||
response_data['total'] = task.info.get('total', 1)
|
||
response_data['percent'] = int((task.info.get('current', 0) / task.info.get('total', 1)) * 100)
|
||
elif task.state == 'SUCCESS':
|
||
# Get result from cache
|
||
result = cache.get(f'lyngsat_task_{task_id}')
|
||
if result:
|
||
response_data['result'] = result
|
||
response_data['status'] = 'Задача завершена успешно'
|
||
else:
|
||
response_data['result'] = task.result
|
||
response_data['status'] = 'Задача завершена'
|
||
elif task.state == 'FAILURE':
|
||
response_data['status'] = 'Ошибка при выполнении задачи'
|
||
response_data['error'] = str(task.info)
|
||
else:
|
||
response_data['status'] = task.state
|
||
|
||
return JsonResponse(response_data)
|
||
|
||
|
||
class TransponderDataAPIView(LoginRequiredMixin, View):
|
||
"""API endpoint for getting Transponder data."""
|
||
|
||
def get(self, request, transponder_id):
|
||
from mapsapp.models import Transponders
|
||
|
||
try:
|
||
transponder = Transponders.objects.select_related(
|
||
'sat_id',
|
||
'polarization',
|
||
'created_by__user'
|
||
).get(id=transponder_id)
|
||
|
||
# Format created_at date
|
||
created_at_str = '-'
|
||
if transponder.created_at:
|
||
local_time = timezone.localtime(transponder.created_at)
|
||
created_at_str = local_time.strftime("%d.%m.%Y %H:%M")
|
||
|
||
# Get created_by username
|
||
created_by_str = '-'
|
||
if transponder.created_by:
|
||
created_by_str = str(transponder.created_by)
|
||
|
||
data = {
|
||
'id': transponder.id,
|
||
'name': transponder.name or '-',
|
||
'satellite': transponder.sat_id.name if transponder.sat_id else '-',
|
||
'downlink': format_frequency(transponder.downlink),
|
||
'uplink': format_frequency(transponder.uplink) if transponder.uplink else None,
|
||
'frequency_range': format_frequency(transponder.frequency_range),
|
||
'polarization': transponder.polarization.name if transponder.polarization else '-',
|
||
'zone_name': transponder.zone_name or '-',
|
||
'transfer': format_frequency(transponder.transfer) if transponder.transfer else None,
|
||
'snr': f"{transponder.snr:.1f}" if transponder.snr is not None else None,
|
||
'created_at': created_at_str,
|
||
'created_by': created_by_str,
|
||
}
|
||
|
||
return JsonResponse(data)
|
||
except Transponders.DoesNotExist:
|
||
return JsonResponse({'error': 'Транспондер не найден'}, status=404)
|
||
except Exception as e:
|
||
return JsonResponse({'error': str(e)}, status=500)
|
||
|
||
|
||
class GeoPointsAPIView(LoginRequiredMixin, View):
|
||
"""API endpoint for getting all geo points for polygon filter visualization."""
|
||
|
||
def get(self, request):
|
||
from ..models import Geo
|
||
|
||
try:
|
||
# Limit to reasonable number of points to avoid performance issues
|
||
limit = int(request.GET.get('limit', 10000))
|
||
limit = min(limit, 50000) # Max 50k points
|
||
|
||
# Get all Geo objects with coordinates
|
||
geo_objs = Geo.objects.filter(
|
||
coords__isnull=False
|
||
).select_related(
|
||
'objitem',
|
||
'objitem__source'
|
||
)[:limit]
|
||
|
||
points = []
|
||
for geo_obj in geo_objs:
|
||
if not geo_obj.coords:
|
||
continue
|
||
|
||
# Get source_id if available
|
||
source_id = None
|
||
if hasattr(geo_obj, 'objitem') and geo_obj.objitem:
|
||
if hasattr(geo_obj.objitem, 'source') and geo_obj.objitem.source:
|
||
source_id = geo_obj.objitem.source.id
|
||
|
||
points.append({
|
||
'id': geo_obj.id,
|
||
'lat': geo_obj.coords.y,
|
||
'lng': geo_obj.coords.x,
|
||
'source_id': source_id or '-'
|
||
})
|
||
|
||
return JsonResponse({
|
||
'points': points,
|
||
'total': len(points),
|
||
'limited': len(points) >= limit
|
||
})
|
||
except Exception as e:
|
||
return JsonResponse({'error': str(e)}, status=500)
|
||
|
||
|
||
class SatelliteDataAPIView(LoginRequiredMixin, View):
|
||
"""API endpoint for getting Satellite data."""
|
||
|
||
def get(self, request, satellite_id):
|
||
from ..models import Satellite
|
||
|
||
try:
|
||
satellite = Satellite.objects.prefetch_related(
|
||
'band',
|
||
'created_by__user',
|
||
'updated_by__user'
|
||
).get(id=satellite_id)
|
||
|
||
# Format dates
|
||
created_at_str = '-'
|
||
if satellite.created_at:
|
||
local_time = timezone.localtime(satellite.created_at)
|
||
created_at_str = local_time.strftime("%d.%m.%Y %H:%M")
|
||
|
||
updated_at_str = '-'
|
||
if satellite.updated_at:
|
||
local_time = timezone.localtime(satellite.updated_at)
|
||
updated_at_str = local_time.strftime("%d.%m.%Y %H:%M")
|
||
|
||
launch_date_str = '-'
|
||
if satellite.launch_date:
|
||
launch_date_str = satellite.launch_date.strftime("%d.%m.%Y")
|
||
|
||
# Get bands
|
||
bands_list = list(satellite.band.values_list('name', flat=True))
|
||
bands_str = ', '.join(bands_list) if bands_list else '-'
|
||
|
||
data = {
|
||
'id': satellite.id,
|
||
'name': satellite.name,
|
||
'norad': satellite.norad if satellite.norad else '-',
|
||
'undersat_point': f"{satellite.undersat_point}°" if satellite.undersat_point is not None else '-',
|
||
'bands': bands_str,
|
||
'launch_date': launch_date_str,
|
||
'url': satellite.url or None,
|
||
'comment': satellite.comment or '-',
|
||
'created_at': created_at_str,
|
||
'created_by': str(satellite.created_by) if satellite.created_by else '-',
|
||
'updated_at': updated_at_str,
|
||
'updated_by': str(satellite.updated_by) if satellite.updated_by else '-',
|
||
}
|
||
|
||
return JsonResponse(data)
|
||
except Satellite.DoesNotExist:
|
||
return JsonResponse({'error': 'Спутник не найден'}, status=404)
|
||
except Exception as e:
|
||
return JsonResponse({'error': str(e)}, status=500)
|
||
|
||
|
||
|
||
class SatelliteDataAPIView(LoginRequiredMixin, View):
|
||
"""API endpoint for getting Satellite data."""
|
||
|
||
def get(self, request, satellite_id):
|
||
from ..models import Satellite
|
||
|
||
try:
|
||
satellite = Satellite.objects.prefetch_related('band').get(id=satellite_id)
|
||
|
||
# Format launch_date
|
||
launch_date_str = '-'
|
||
if satellite.launch_date:
|
||
launch_date_str = satellite.launch_date.strftime("%d.%m.%Y")
|
||
|
||
# Format created_at and updated_at
|
||
created_at_str = '-'
|
||
if satellite.created_at:
|
||
local_time = timezone.localtime(satellite.created_at)
|
||
created_at_str = local_time.strftime("%d.%m.%Y %H:%M")
|
||
|
||
updated_at_str = '-'
|
||
if satellite.updated_at:
|
||
local_time = timezone.localtime(satellite.updated_at)
|
||
updated_at_str = local_time.strftime("%d.%m.%Y %H:%M")
|
||
|
||
# Get band names
|
||
bands = list(satellite.band.values_list('name', flat=True))
|
||
bands_str = ', '.join(bands) if bands else '-'
|
||
|
||
# Get location place display
|
||
location_place_display = '-'
|
||
if satellite.location_place:
|
||
location_place_choices = dict(Satellite.PLACES)
|
||
location_place_display = location_place_choices.get(satellite.location_place, satellite.location_place)
|
||
|
||
data = {
|
||
'id': satellite.id,
|
||
'name': satellite.name,
|
||
'alternative_name': satellite.alternative_name or '-',
|
||
'norad': satellite.norad if satellite.norad else None,
|
||
'international_code': satellite.international_code or '-',
|
||
'location_place': location_place_display,
|
||
'bands': bands_str,
|
||
'undersat_point': satellite.undersat_point if satellite.undersat_point is not None else None,
|
||
'url': satellite.url or None,
|
||
'comment': satellite.comment or '-',
|
||
'launch_date': launch_date_str,
|
||
'created_at': created_at_str,
|
||
'updated_at': updated_at_str,
|
||
'created_by': str(satellite.created_by) if satellite.created_by else '-',
|
||
'updated_by': str(satellite.updated_by) if satellite.updated_by else '-',
|
||
}
|
||
|
||
return JsonResponse(data)
|
||
except Satellite.DoesNotExist:
|
||
return JsonResponse({'error': 'Спутник не найден'}, status=404)
|
||
except Exception as e:
|
||
return JsonResponse({'error': str(e)}, status=500)
|
||
|
||
|
||
class MultiSourcesPlaybackDataAPIView(LoginRequiredMixin, View):
|
||
"""API endpoint for getting playback data for multiple sources."""
|
||
|
||
def get(self, request):
|
||
from ..models import Source
|
||
|
||
ids = request.GET.get('ids', '')
|
||
if not ids:
|
||
return JsonResponse({'error': 'Не указаны ID источников'}, status=400)
|
||
|
||
try:
|
||
id_list = [int(x) for x in ids.split(',') if x.isdigit()]
|
||
if not id_list:
|
||
return JsonResponse({'error': 'Некорректные ID источников'}, status=400)
|
||
|
||
sources = Source.objects.filter(id__in=id_list).prefetch_related(
|
||
'source_objitems',
|
||
'source_objitems__parameter_obj',
|
||
'source_objitems__geo_obj',
|
||
)
|
||
|
||
# Collect data for each source
|
||
sources_data = []
|
||
global_min_time = None
|
||
global_max_time = None
|
||
|
||
# Define colors for different sources
|
||
colors = ['red', 'blue', 'green', 'purple', 'orange', 'cyan', 'magenta', 'yellow', 'lime', 'pink']
|
||
|
||
for idx, source in enumerate(sources):
|
||
# Get all ObjItems with geo data and timestamp
|
||
objitems = source.source_objitems.filter(
|
||
geo_obj__isnull=False,
|
||
geo_obj__coords__isnull=False,
|
||
geo_obj__timestamp__isnull=False
|
||
).select_related('geo_obj', 'parameter_obj').order_by('geo_obj__timestamp')
|
||
|
||
points = []
|
||
for objitem in objitems:
|
||
geo = objitem.geo_obj
|
||
param = getattr(objitem, 'parameter_obj', None)
|
||
|
||
timestamp = geo.timestamp
|
||
|
||
# Update global min/max time
|
||
if global_min_time is None or timestamp < global_min_time:
|
||
global_min_time = timestamp
|
||
if global_max_time is None or timestamp > global_max_time:
|
||
global_max_time = timestamp
|
||
|
||
freq_str = '-'
|
||
if param and param.frequency:
|
||
freq_str = f"{param.frequency} МГц"
|
||
|
||
points.append({
|
||
'lat': geo.coords.y,
|
||
'lng': geo.coords.x,
|
||
'timestamp': timestamp.isoformat(),
|
||
'timestamp_ms': int(timestamp.timestamp() * 1000),
|
||
'name': objitem.name or f'Точка #{objitem.id}',
|
||
'frequency': freq_str,
|
||
'location': geo.location or '-',
|
||
})
|
||
|
||
if points:
|
||
# Get source name from first objitem or use ID
|
||
source_name = f"Объект #{source.id}"
|
||
if source.source_objitems.exists():
|
||
first_objitem = source.source_objitems.first()
|
||
if first_objitem and first_objitem.name:
|
||
# Extract base name (without frequency info)
|
||
source_name = first_objitem.name.split(' ')[0] if first_objitem.name else source_name
|
||
|
||
sources_data.append({
|
||
'source_id': source.id,
|
||
'source_name': source_name,
|
||
'color': colors[idx % len(colors)],
|
||
'points': points,
|
||
'points_count': len(points),
|
||
})
|
||
|
||
# Format global time range
|
||
time_range = None
|
||
if global_min_time and global_max_time:
|
||
time_range = {
|
||
'min': global_min_time.isoformat(),
|
||
'max': global_max_time.isoformat(),
|
||
'min_ms': int(global_min_time.timestamp() * 1000),
|
||
'max_ms': int(global_max_time.timestamp() * 1000),
|
||
}
|
||
|
||
return JsonResponse({
|
||
'sources': sources_data,
|
||
'time_range': time_range,
|
||
'total_sources': len(sources_data),
|
||
})
|
||
except Exception as e:
|
||
return JsonResponse({'error': str(e)}, status=500)
|
||
|
||
|
||
|
||
class SatelliteTranspondersAPIView(LoginRequiredMixin, View):
|
||
"""API endpoint for getting transponders for a satellite."""
|
||
|
||
def get(self, request, satellite_id):
|
||
from mapsapp.models import Transponders
|
||
|
||
try:
|
||
transponders = Transponders.objects.filter(
|
||
sat_id=satellite_id
|
||
).select_related('polarization').order_by('downlink')
|
||
|
||
if not transponders.exists():
|
||
return JsonResponse({
|
||
'satellite_id': satellite_id,
|
||
'transponders': [],
|
||
'count': 0
|
||
})
|
||
|
||
transponders_data = []
|
||
for t in transponders:
|
||
transponders_data.append({
|
||
'id': t.id,
|
||
'name': t.name or '-',
|
||
'downlink': float(t.downlink) if t.downlink else 0,
|
||
'uplink': float(t.uplink) if t.uplink else None,
|
||
'frequency_range': float(t.frequency_range) if t.frequency_range else 0,
|
||
'polarization': t.polarization.name if t.polarization else '-',
|
||
'zone_name': t.zone_name or '-',
|
||
})
|
||
|
||
return JsonResponse({
|
||
'satellite_id': satellite_id,
|
||
'transponders': transponders_data,
|
||
'count': len(transponders_data)
|
||
})
|
||
except Exception as e:
|
||
return JsonResponse({'error': str(e)}, status=500)
|