""" 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, }) # Get marks for the source marks_data = [] for mark in source.marks.all().order_by('-timestamp'): mark_timestamp = '-' if mark.timestamp: local_time = timezone.localtime(mark.timestamp) mark_timestamp = local_time.strftime("%d.%m.%Y %H:%M") marks_data.append({ 'id': mark.id, 'mark': mark.mark, 'timestamp': mark_timestamp, 'created_by': str(mark.created_by) if mark.created_by else '-', }) 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 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)