148 lines
6.1 KiB
Python
148 lines
6.1 KiB
Python
# Standard library imports
|
||
from typing import Any, Dict
|
||
|
||
# Django imports
|
||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||
from django.http import HttpResponse, HttpResponseNotFound, JsonResponse
|
||
from django.utils.decorators import method_decorator
|
||
from django.views import View
|
||
from django.views.decorators.cache import cache_page
|
||
from django.views.decorators.http import require_GET
|
||
from django.views.generic import TemplateView
|
||
|
||
# Third-party imports
|
||
import requests
|
||
|
||
# Local imports
|
||
from mainapp.models import Satellite
|
||
from .models import Transponders
|
||
from .utils import get_band_names
|
||
|
||
|
||
class CesiumMapView(LoginRequiredMixin, TemplateView):
|
||
"""
|
||
Представление для отображения 3D карты с использованием Cesium.
|
||
|
||
Отображает спутники и их зоны покрытия на интерактивной 3D карте.
|
||
"""
|
||
template_name = 'mapsapp/map3d.html'
|
||
|
||
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
|
||
context = super().get_context_data(**kwargs)
|
||
# Оптимизированный запрос - загружаем только необходимые поля
|
||
context['sats'] = Satellite.objects.filter(
|
||
parameters__objitems__isnull=False
|
||
).distinct().only('id', 'name').order_by('name')
|
||
return context
|
||
|
||
class GetFootprintsView(LoginRequiredMixin, View):
|
||
"""
|
||
API для получения зон покрытия (footprints) спутника.
|
||
|
||
Возвращает список названий зон покрытия для указанного спутника.
|
||
"""
|
||
def get(self, request, sat_id):
|
||
try:
|
||
# Оптимизированный запрос - загружаем только поле name
|
||
sat_name = Satellite.objects.only('name').get(id=sat_id).name
|
||
footprint_names = get_band_names(sat_name)
|
||
|
||
return JsonResponse(footprint_names, safe=False)
|
||
except Satellite.DoesNotExist:
|
||
return JsonResponse({"error": "Спутник не найден"}, status=404)
|
||
except Exception as e:
|
||
return JsonResponse({"error": str(e)}, status=500)
|
||
|
||
|
||
class TileProxyView(View):
|
||
"""
|
||
Прокси для загрузки тайлов карты покрытия спутников.
|
||
|
||
Кэширует тайлы на 7 дней для улучшения производительности.
|
||
"""
|
||
# Константы
|
||
TILE_BASE_URL = "https://static.satbeams.com/tiles"
|
||
CACHE_DURATION = 60 * 60 * 24 * 7 # 7 дней
|
||
REQUEST_TIMEOUT = 10 # секунд
|
||
|
||
@method_decorator(require_GET)
|
||
@method_decorator(cache_page(CACHE_DURATION))
|
||
def dispatch(self, *args, **kwargs):
|
||
return super().dispatch(*args, **kwargs)
|
||
|
||
def get(self, request, footprint_name, z, x, y):
|
||
# Валидация имени footprint
|
||
if not footprint_name.replace('-', '').replace('_', '').isalnum():
|
||
return HttpResponse("Invalid footprint name", status=400)
|
||
|
||
url = f"{self.TILE_BASE_URL}/{footprint_name}/{z}/{x}/{y}.png"
|
||
|
||
try:
|
||
resp = requests.get(url, timeout=self.REQUEST_TIMEOUT)
|
||
if resp.status_code == 200:
|
||
response = HttpResponse(resp.content, content_type='image/png')
|
||
response["Access-Control-Allow-Origin"] = "*"
|
||
response["Cache-Control"] = f"public, max-age={self.CACHE_DURATION}"
|
||
return response
|
||
else:
|
||
return HttpResponseNotFound("Tile not found")
|
||
except requests.Timeout:
|
||
return HttpResponse("Request timeout", status=504)
|
||
except requests.RequestException as e:
|
||
return HttpResponse(f"Proxy error: {e}", status=500)
|
||
|
||
class LeafletMapView(LoginRequiredMixin, TemplateView):
|
||
"""
|
||
Представление для отображения 2D карты с использованием Leaflet.
|
||
|
||
Отображает спутники и транспондеры на интерактивной 2D карте.
|
||
"""
|
||
template_name = 'mapsapp/map2d.html'
|
||
|
||
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
|
||
context = super().get_context_data(**kwargs)
|
||
# Оптимизированные запросы - загружаем только необходимые поля
|
||
context['sats'] = Satellite.objects.filter(
|
||
parameters__objitems__isnull=False
|
||
).distinct().only('id', 'name').order_by('name')
|
||
|
||
context['trans'] = Transponders.objects.select_related(
|
||
'sat_id', 'polarization'
|
||
).only(
|
||
'id', 'name', 'sat_id__name', 'polarization__name',
|
||
'downlink', 'frequency_range', 'zone_name'
|
||
)
|
||
return context
|
||
|
||
|
||
class GetTransponderOnSatIdView(LoginRequiredMixin, View):
|
||
"""
|
||
API для получения транспондеров спутника.
|
||
|
||
Возвращает список транспондеров для указанного спутника с оптимизированными запросами.
|
||
"""
|
||
def get(self, request, sat_id):
|
||
# Оптимизированный запрос с select_related и only
|
||
trans = Transponders.objects.filter(
|
||
sat_id=sat_id
|
||
).select_related('polarization').only(
|
||
'name', 'downlink', 'frequency_range',
|
||
'zone_name', 'polarization__name'
|
||
)
|
||
|
||
if not trans.exists():
|
||
return JsonResponse({'error': 'Объектов не найдено'}, status=404)
|
||
|
||
# Используем list comprehension для лучшей производительности
|
||
output = [
|
||
{
|
||
"name": tran.name,
|
||
"frequency": tran.downlink,
|
||
"frequency_range": tran.frequency_range,
|
||
"zone_name": tran.zone_name,
|
||
"polarization": tran.polarization.name if tran.polarization else "-"
|
||
}
|
||
for tran in trans
|
||
]
|
||
|
||
return JsonResponse(output, safe=False) |