From 25fe93231fc7390836878602329e564acca96930 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: Mon, 8 Dec 2025 15:48:46 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20?= =?UTF-8?q?=D0=B7=D0=BE=D0=BD=D1=8B=20=D0=B4=D0=BB=D1=8F=20=D1=81=D0=BF?= =?UTF-8?q?=D1=83=D1=82=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dbapp/mainapp/forms.py | 6 +++ .../0020_satellite_location_place.py | 18 +++++++++ dbapp/mainapp/models.py | 24 ++++++------ .../templates/mainapp/satellite_form.html | 21 +++++++++- .../templates/mainapp/satellite_list.html | 25 +++++++++++- .../mainapp/templates/mainapp/statistics.html | 12 ++++++ dbapp/mainapp/views/satellite.py | 13 +++++++ dbapp/mainapp/views/statistics.py | 38 ++++++++++++------- 8 files changed, 128 insertions(+), 29 deletions(-) create mode 100644 dbapp/mainapp/migrations/0020_satellite_location_place.py diff --git a/dbapp/mainapp/forms.py b/dbapp/mainapp/forms.py index 06e8219..7a5a5af 100644 --- a/dbapp/mainapp/forms.py +++ b/dbapp/mainapp/forms.py @@ -811,6 +811,7 @@ class SatelliteForm(forms.ModelForm): fields = [ 'name', 'alternative_name', + 'location_place', 'norad', 'international_code', 'band', @@ -829,6 +830,9 @@ class SatelliteForm(forms.ModelForm): 'class': 'form-control', 'placeholder': 'Введите альтернативное название (необязательно)' }), + 'location_place': forms.Select(attrs={ + 'class': 'form-select' + }), 'norad': forms.NumberInput(attrs={ 'class': 'form-control', 'placeholder': 'Введите NORAD ID' @@ -863,6 +867,7 @@ class SatelliteForm(forms.ModelForm): labels = { 'name': 'Название спутника', 'alternative_name': 'Альтернативное название', + 'location_place': 'Комплекс', 'norad': 'NORAD ID', 'international_code': 'Международный код', 'band': 'Диапазоны работы', @@ -874,6 +879,7 @@ class SatelliteForm(forms.ModelForm): help_texts = { 'name': 'Уникальное название спутника', 'alternative_name': 'Альтернативное название спутника (например, на другом языке)', + 'location_place': 'К какому комплексу принадлежит спутник', 'norad': 'Идентификатор NORAD для отслеживания спутника', 'international_code': 'Международный идентификатор спутника (например, 2011-074A)', 'band': 'Выберите диапазоны работы спутника (удерживайте Ctrl для множественного выбора)', diff --git a/dbapp/mainapp/migrations/0020_satellite_location_place.py b/dbapp/mainapp/migrations/0020_satellite_location_place.py new file mode 100644 index 0000000..6c31dad --- /dev/null +++ b/dbapp/mainapp/migrations/0020_satellite_location_place.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.7 on 2025-12-08 12:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mainapp', '0019_add_coords_to_source_request'), + ] + + operations = [ + migrations.AddField( + model_name='satellite', + name='location_place', + field=models.CharField(choices=[('kr', 'КР'), ('dv', 'ДВ')], default='kr', help_text='К какому комплексу принадлежит спутник', max_length=30, null=True, verbose_name='Комплекс'), + ), + ] diff --git a/dbapp/mainapp/models.py b/dbapp/mainapp/models.py index fb2deea..5af1b37 100644 --- a/dbapp/mainapp/models.py +++ b/dbapp/mainapp/models.py @@ -307,10 +307,10 @@ class Satellite(models.Model): Представляет спутник связи с его основными характеристиками. """ - # PLACES = [ - # ("kr", "КР"), - # ("dv", "ДВ") - # ] + PLACES = [ + ("kr", "КР"), + ("dv", "ДВ") + ] # Основные поля name = models.CharField( max_length=100, @@ -327,14 +327,14 @@ class Satellite(models.Model): db_index=True, help_text="Альтернативное название спутника", ) - # location_place = models.CharField( - # max_length=30, - # choices=PLACES, - # null=True, - # default="kr", - # verbose_name="Комплекс", - # help_text="К какому комплексу принадлежит спутник", - # ) + location_place = models.CharField( + max_length=30, + choices=PLACES, + null=True, + default="kr", + verbose_name="Комплекс", + help_text="К какому комплексу принадлежит спутник", + ) norad = models.IntegerField( blank=True, null=True, diff --git a/dbapp/mainapp/templates/mainapp/satellite_form.html b/dbapp/mainapp/templates/mainapp/satellite_form.html index 3cee18e..90fc7e4 100644 --- a/dbapp/mainapp/templates/mainapp/satellite_form.html +++ b/dbapp/mainapp/templates/mainapp/satellite_form.html @@ -69,7 +69,7 @@ {% csrf_token %}
-
+
-
+
+ +
+
+ + {{ form.location_place }} + {% if form.location_place.errors %} +
+ {{ form.location_place.errors.0 }} +
+ {% endif %} + {% if form.location_place.help_text %} +
{{ form.location_place.help_text }}
+ {% endif %} +
+
diff --git a/dbapp/mainapp/templates/mainapp/satellite_list.html b/dbapp/mainapp/templates/mainapp/satellite_list.html index 29f29d2..d148f43 100644 --- a/dbapp/mainapp/templates/mainapp/satellite_list.html +++ b/dbapp/mainapp/templates/mainapp/satellite_list.html @@ -95,6 +95,18 @@
+ +
+ + +
+ + +
+ + +
+
diff --git a/dbapp/mainapp/views/satellite.py b/dbapp/mainapp/views/satellite.py index 9e9cd26..83a2b67 100644 --- a/dbapp/mainapp/views/satellite.py +++ b/dbapp/mainapp/views/satellite.py @@ -32,6 +32,7 @@ class SatelliteListView(LoginRequiredMixin, View): # Get filter parameters search_query = request.GET.get("search", "").strip() selected_bands = request.GET.getlist("band_id") + selected_location_places = request.GET.getlist("location_place") norad_min = request.GET.get("norad_min", "").strip() norad_max = request.GET.get("norad_max", "").strip() undersat_point_min = request.GET.get("undersat_point_min", "").strip() @@ -58,6 +59,10 @@ class SatelliteListView(LoginRequiredMixin, View): if selected_bands: satellites = satellites.filter(band__id__in=selected_bands).distinct() + # Filter by location_place + if selected_location_places: + satellites = satellites.filter(location_place__in=selected_location_places) + # Filter by NORAD ID if norad_min: try: @@ -154,6 +159,8 @@ class SatelliteListView(LoginRequiredMixin, View): "-updated_at": "-updated_at", "transponder_count": "transponder_count", "-transponder_count": "-transponder_count", + "location_place": "location_place", + "-location_place": "-location_place", } if sort_param in valid_sort_fields: @@ -169,10 +176,14 @@ class SatelliteListView(LoginRequiredMixin, View): # Get band names band_names = [band.name for band in satellite.band.all()] + # Get location_place display value + location_place_display = dict(Satellite.PLACES).get(satellite.location_place, "-") if satellite.location_place else "-" + processed_satellites.append({ 'id': satellite.id, 'name': satellite.name or "-", 'alternative_name': satellite.alternative_name or "-", + 'location_place': location_place_display, 'norad': satellite.norad if satellite.norad else "-", 'international_code': satellite.international_code or "-", 'bands': ", ".join(band_names) if band_names else "-", @@ -200,6 +211,8 @@ class SatelliteListView(LoginRequiredMixin, View): int(x) if isinstance(x, str) else x for x in selected_bands if (isinstance(x, int) or (isinstance(x, str) and x.isdigit())) ], + 'location_places': Satellite.PLACES, + 'selected_location_places': selected_location_places, 'norad_min': norad_min, 'norad_max': norad_max, 'undersat_point_min': undersat_point_min, diff --git a/dbapp/mainapp/views/statistics.py b/dbapp/mainapp/views/statistics.py index ec35a57..f7ec294 100644 --- a/dbapp/mainapp/views/statistics.py +++ b/dbapp/mainapp/views/statistics.py @@ -62,7 +62,11 @@ class StatisticsView(TemplateView): satellite_ids = self.request.GET.getlist('satellite_id') return [int(sid) for sid in satellite_ids if sid.isdigit()] - def get_base_queryset(self, date_from, date_to, satellite_ids): + def get_selected_location_places(self): + """Получает выбранные комплексы из параметров запроса.""" + return self.request.GET.getlist('location_place') + + def get_base_queryset(self, date_from, date_to, satellite_ids, location_places=None): """Возвращает базовый queryset ObjItem с фильтрами.""" qs = ObjItem.objects.filter( geo_obj__isnull=False, @@ -75,12 +79,14 @@ class StatisticsView(TemplateView): qs = qs.filter(geo_obj__timestamp__date__lte=date_to) if satellite_ids: qs = qs.filter(parameter_obj__id_satellite__id__in=satellite_ids) + if location_places: + qs = qs.filter(parameter_obj__id_satellite__location_place__in=location_places) return qs - def get_statistics(self, date_from, date_to, satellite_ids): + def get_statistics(self, date_from, date_to, satellite_ids, location_places=None): """Вычисляет основную статистику.""" - base_qs = self.get_base_queryset(date_from, date_to, satellite_ids) + base_qs = self.get_base_queryset(date_from, date_to, satellite_ids, location_places) # Общее количество точек total_points = base_qs.count() @@ -89,13 +95,13 @@ class StatisticsView(TemplateView): total_sources = base_qs.filter(source__isnull=False).values('source').distinct().count() # Новые излучения - объекты, у которых имя появилось впервые в выбранном периоде - new_emissions_data = self._calculate_new_emissions(date_from, date_to, satellite_ids) + new_emissions_data = self._calculate_new_emissions(date_from, date_to, satellite_ids, location_places) # Статистика по спутникам - satellite_stats = self._get_satellite_statistics(date_from, date_to, satellite_ids) + satellite_stats = self._get_satellite_statistics(date_from, date_to, satellite_ids, location_places) # Данные для графика по дням - daily_data = self._get_daily_statistics(date_from, date_to, satellite_ids) + daily_data = self._get_daily_statistics(date_from, date_to, satellite_ids, location_places) return { 'total_points': total_points, @@ -106,7 +112,7 @@ class StatisticsView(TemplateView): 'daily_data': daily_data, } - def _calculate_new_emissions(self, date_from, date_to, satellite_ids): + def _calculate_new_emissions(self, date_from, date_to, satellite_ids, location_places=None): """ Вычисляет новые излучения - уникальные имена объектов, которые появились впервые в выбранном периоде. @@ -129,7 +135,7 @@ class StatisticsView(TemplateView): ) # Базовый queryset для выбранного периода - period_qs = self.get_base_queryset(date_from, date_to, satellite_ids).filter( + period_qs = self.get_base_queryset(date_from, date_to, satellite_ids, location_places).filter( name__isnull=False ).exclude(name='') @@ -173,9 +179,9 @@ class StatisticsView(TemplateView): return {'count': len(new_names), 'objects': new_objects} - def _get_satellite_statistics(self, date_from, date_to, satellite_ids): + def _get_satellite_statistics(self, date_from, date_to, satellite_ids, location_places=None): """Получает статистику по каждому спутнику.""" - base_qs = self.get_base_queryset(date_from, date_to, satellite_ids) + base_qs = self.get_base_queryset(date_from, date_to, satellite_ids, location_places) # Группируем по спутникам stats = base_qs.filter( @@ -190,9 +196,9 @@ class StatisticsView(TemplateView): return list(stats) - def _get_daily_statistics(self, date_from, date_to, satellite_ids): + def _get_daily_statistics(self, date_from, date_to, satellite_ids, location_places=None): """Получает статистику по дням для графика.""" - base_qs = self.get_base_queryset(date_from, date_to, satellite_ids) + base_qs = self.get_base_queryset(date_from, date_to, satellite_ids, location_places) daily = base_qs.annotate( date=TruncDate('geo_obj__timestamp') @@ -208,6 +214,7 @@ class StatisticsView(TemplateView): date_from, date_to, preset = self.get_date_range() satellite_ids = self.get_selected_satellites() + location_places = self.get_selected_location_places() # Получаем только спутники, у которых есть точки ГЛ satellites_with_points = ObjItem.objects.filter( @@ -221,7 +228,7 @@ class StatisticsView(TemplateView): ).order_by('name') # Получаем статистику - stats = self.get_statistics(date_from, date_to, satellite_ids) + stats = self.get_statistics(date_from, date_to, satellite_ids, location_places) # Сериализуем данные для JavaScript daily_data_json = json.dumps([ @@ -238,6 +245,8 @@ class StatisticsView(TemplateView): context.update({ 'satellites': satellites, 'selected_satellites': satellite_ids, + 'location_places': Satellite.PLACES, + 'selected_location_places': location_places, 'date_from': date_from.isoformat() if date_from else '', 'date_to': date_to.isoformat() if date_to else '', 'preset': preset or '', @@ -259,7 +268,8 @@ class StatisticsAPIView(StatisticsView): def get(self, request, *args, **kwargs): date_from, date_to, preset = self.get_date_range() satellite_ids = self.get_selected_satellites() - stats = self.get_statistics(date_from, date_to, satellite_ids) + location_places = self.get_selected_location_places() + stats = self.get_statistics(date_from, date_to, satellite_ids, location_places) # Преобразуем даты в строки для JSON daily_data = []