From 908e11879d65c7e9ea5e06eb9b008717c6b15538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=BE=D1=88=D0=BA=D0=B8=D0=BD=20=D0=A1=D0=B5=D1=80?= =?UTF-8?q?=D0=B3=D0=B5=D0=B9?= Date: Thu, 27 Nov 2025 17:36:23 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=BE=D0=BF=D1=80=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=BE=D0=B1=D1=89=D1=83=D1=8E=20=D0=BA=D0=B0=D1=80?= =?UTF-8?q?=D1=82=D1=83=20=D1=81=20footprinta=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dbapp/mapsapp/templates/mapsapp/map2d.html | 773 +++++++++++++-------- dbapp/mapsapp/utils.py | 2 +- dbapp/mapsapp/views.py | 338 ++++----- 3 files changed, 666 insertions(+), 447 deletions(-) diff --git a/dbapp/mapsapp/templates/mapsapp/map2d.html b/dbapp/mapsapp/templates/mapsapp/map2d.html index 1e7e29d..84c56ca 100644 --- a/dbapp/mapsapp/templates/mapsapp/map2d.html +++ b/dbapp/mapsapp/templates/mapsapp/map2d.html @@ -17,6 +17,8 @@ транспондеров + +
{% endblock content %} -{% block extra_css %} - -{% endblock extra_css %} - {% block extra_js %} -{% endblock extra_js %} +{% endblock extra_js %} \ No newline at end of file diff --git a/dbapp/mapsapp/utils.py b/dbapp/mapsapp/utils.py index 69de76e..f41f3ed 100644 --- a/dbapp/mapsapp/utils.py +++ b/dbapp/mapsapp/utils.py @@ -22,7 +22,7 @@ def search_satellite_on_page(data: dict, satellite_name: str): def get_footprint_data(position: str = 62) -> dict: """Возвращает словарь с данным по footprint для спутников на выбранной долготе""" - response = requests.get(f"https://www.satbeams.com/footprints?position={position}", verify=False) + response = requests.get(f"https://www.satbeams.com/footprints?position={position}", verify=True) response.raise_for_status() match = re.search(r'var data = ({.*?});', response.text, re.DOTALL) if match: diff --git a/dbapp/mapsapp/views.py b/dbapp/mapsapp/views.py index 6d12a87..59ab3b7 100644 --- a/dbapp/mapsapp/views.py +++ b/dbapp/mapsapp/views.py @@ -1,169 +1,169 @@ -# 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__objitem__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__objitem__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) +# 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__objitem__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__objitem__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)