From 8994a0e50023281dda1cb85d285b311802fa570c 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: Sun, 16 Nov 2025 23:32:29 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20=D0=B8?= =?UTF-8?q?=20=D1=83=D0=BB=D1=83=D1=87=D1=88=D0=B5=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?=D0=B2=D0=B8=D0=B7=D1=83=D0=B0=D0=BB=D0=B0.=20=D0=94=D0=BE?= =?UTF-8?q?=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20=D1=84=D1=83=D0=BD=D0=BA=D1=86?= =?UTF-8?q?=D0=B8=D0=BE=D0=BD=D0=B0=D0=BB=20=D0=BE=D1=82=D0=BC=D0=B5=D1=82?= =?UTF-8?q?=D0=BE=D0=BA.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dbapp/mainapp/admin.py | 19 + ...5_alter_sigmaparmark_options_objectmark.py | 33 + .../0006_change_objectmark_to_source.py | 27 + .../migrations/0007_make_source_required.py | 19 + dbapp/mainapp/models.py | 72 +- .../templates/mainapp/components/_navbar.html | 10 +- .../mainapp/components/_objitems_table.html | 126 +++ .../components/_selected_items_offcanvas.html | 2 +- .../components/_sigma_parameter_modal.html | 4 +- .../mainapp/components/_sources_table.html | 122 +++ dbapp/mainapp/templates/mainapp/home.html | 823 ++++++++++-------- dbapp/mainapp/templates/mainapp/home_old.html | 450 ++++++++++ .../templates/mainapp/object_marks.html | 383 ++++++++ .../templates/mainapp/objitem_list.html | 12 +- .../mainapp/source_bulk_delete_confirm.html | 2 +- .../mainapp/source_confirm_delete.html | 8 +- .../templates/mainapp/source_form.html | 6 +- .../templates/mainapp/source_list.html | 104 ++- .../mainapp/templates/mainapp/source_map.html | 2 +- .../mainapp/source_with_points_map.html | 14 +- .../transponder_bulk_delete_confirm.html | 2 +- .../templates/mainapp/transponder_list.html | 2 +- dbapp/mainapp/urls.py | 8 +- dbapp/mainapp/views/README.md | 75 -- dbapp/mainapp/views/__init__.py | 3 +- dbapp/mainapp/views/api.py | 22 +- dbapp/mainapp/views/base.py | 481 ++++++++++ dbapp/mainapp/views/marks.py | 142 +++ dbapp/mainapp/views/source.py | 14 +- .../migrations/0002_alter_transponders_snr.py | 18 + 30 files changed, 2495 insertions(+), 510 deletions(-) create mode 100644 dbapp/mainapp/migrations/0005_alter_sigmaparmark_options_objectmark.py create mode 100644 dbapp/mainapp/migrations/0006_change_objectmark_to_source.py create mode 100644 dbapp/mainapp/migrations/0007_make_source_required.py create mode 100644 dbapp/mainapp/templates/mainapp/components/_objitems_table.html create mode 100644 dbapp/mainapp/templates/mainapp/components/_sources_table.html create mode 100644 dbapp/mainapp/templates/mainapp/home_old.html create mode 100644 dbapp/mainapp/templates/mainapp/object_marks.html delete mode 100644 dbapp/mainapp/views/README.md create mode 100644 dbapp/mainapp/views/marks.py create mode 100644 dbapp/mapsapp/migrations/0002_alter_transponders_snr.py diff --git a/dbapp/mainapp/admin.py b/dbapp/mainapp/admin.py index 5071fe2..0aaa2c8 100644 --- a/dbapp/mainapp/admin.py +++ b/dbapp/mainapp/admin.py @@ -24,6 +24,7 @@ from .models import ( Modulation, Standard, SigmaParMark, + ObjectMark, SigmaParameter, Parameter, Satellite, @@ -339,6 +340,23 @@ class ParameterInline(admin.StackedInline): # ============================================================================ +@admin.register(ObjectMark) +class ObjectMarkAdmin(BaseAdmin): + """Админ-панель для модели ObjectMark.""" + + list_display = ("source", "mark", "timestamp", "created_by") + list_select_related = ("source", "created_by__user") + search_fields = ("source__id",) + ordering = ("-timestamp",) + list_filter = ( + "mark", + ("timestamp", DateRangeQuickSelectListFilterBuilder()), + ("source", MultiSelectRelatedDropdownFilter), + ) + readonly_fields = ("timestamp", "created_by") + autocomplete_fields = ("source",) + + @admin.register(SigmaParMark) class SigmaParMarkAdmin(BaseAdmin): """Админ-панель для модели SigmaParMark.""" @@ -1023,6 +1041,7 @@ class SourceAdmin(ImportExportActionModelAdmin, LeafletGeoAdmin, BaseAdmin): ("created_at", DateRangeQuickSelectListFilterBuilder()), ("updated_at", DateRangeQuickSelectListFilterBuilder()), ) + search_fields = ("id",) ordering = ("-created_at",) readonly_fields = ("created_at", "created_by", "updated_at", "updated_by") inlines = [ObjItemInline] diff --git a/dbapp/mainapp/migrations/0005_alter_sigmaparmark_options_objectmark.py b/dbapp/mainapp/migrations/0005_alter_sigmaparmark_options_objectmark.py new file mode 100644 index 0000000..9337106 --- /dev/null +++ b/dbapp/mainapp/migrations/0005_alter_sigmaparmark_options_objectmark.py @@ -0,0 +1,33 @@ +# Generated by Django 5.2.7 on 2025-11-16 10:01 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mainapp', '0004_change_geo_mirrors_to_satellites'), + ] + + operations = [ + migrations.AlterModelOptions( + name='sigmaparmark', + options={'ordering': ['-timestamp'], 'verbose_name': 'Отметка сигнала', 'verbose_name_plural': 'Отметки сигналов'}, + ), + migrations.CreateModel( + name='ObjectMark', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('mark', models.BooleanField(blank=True, help_text='True - объект обнаружен, False - объект отсутствует', null=True, verbose_name='Наличие объекта')), + ('timestamp', models.DateTimeField(auto_now_add=True, db_index=True, help_text='Время фиксации отметки', verbose_name='Время')), + ('created_by', models.ForeignKey(blank=True, help_text='Пользователь, создавший отметку', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='marks_created', to='mainapp.customuser', verbose_name='Создан пользователем')), + ('objitem', models.ForeignKey(help_text='Связанный объект', on_delete=django.db.models.deletion.CASCADE, related_name='marks', to='mainapp.objitem', verbose_name='Объект')), + ], + options={ + 'verbose_name': 'Отметка объекта', + 'verbose_name_plural': 'Отметки объектов', + 'ordering': ['-timestamp'], + }, + ), + ] diff --git a/dbapp/mainapp/migrations/0006_change_objectmark_to_source.py b/dbapp/mainapp/migrations/0006_change_objectmark_to_source.py new file mode 100644 index 0000000..7b52b4a --- /dev/null +++ b/dbapp/mainapp/migrations/0006_change_objectmark_to_source.py @@ -0,0 +1,27 @@ +# Generated by Django 5.2.7 on 2025-11-16 15:25 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mainapp', '0005_alter_sigmaparmark_options_objectmark'), + ] + + operations = [ + migrations.AlterModelOptions( + name='objectmark', + options={'ordering': ['-timestamp'], 'verbose_name': 'Отметка источника', 'verbose_name_plural': 'Отметки источников'}, + ), + migrations.RemoveField( + model_name='objectmark', + name='objitem', + ), + migrations.AddField( + model_name='objectmark', + name='source', + field=models.ForeignKey(help_text='Связанный источник', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='marks', to='mainapp.source', verbose_name='Источник'), + ), + ] diff --git a/dbapp/mainapp/migrations/0007_make_source_required.py b/dbapp/mainapp/migrations/0007_make_source_required.py new file mode 100644 index 0000000..3e7ecab --- /dev/null +++ b/dbapp/mainapp/migrations/0007_make_source_required.py @@ -0,0 +1,19 @@ +# Generated by Django 5.2.7 on 2025-11-16 15:26 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mainapp', '0006_change_objectmark_to_source'), + ] + + operations = [ + migrations.AlterField( + model_name='objectmark', + name='source', + field=models.ForeignKey(help_text='Связанный источник', on_delete=django.db.models.deletion.CASCADE, related_name='marks', to='mainapp.source', verbose_name='Источник'), + ), + ] diff --git a/dbapp/mainapp/models.py b/dbapp/mainapp/models.py index d583ebb..46c5235 100644 --- a/dbapp/mainapp/models.py +++ b/dbapp/mainapp/models.py @@ -68,9 +68,75 @@ class CustomUser(models.Model): ordering = ["user__username"] +class ObjectMark(models.Model): + """ + Модель отметки о наличии объекта. + + Используется для фиксации моментов времени когда объект был обнаружен или отсутствовал. + """ + + # Основные поля + mark = models.BooleanField( + null=True, + blank=True, + verbose_name="Наличие объекта", + help_text="True - объект обнаружен, False - объект отсутствует", + ) + timestamp = models.DateTimeField( + auto_now_add=True, + verbose_name="Время", + db_index=True, + help_text="Время фиксации отметки", + ) + source = models.ForeignKey( + 'Source', + on_delete=models.CASCADE, + related_name="marks", + verbose_name="Источник", + help_text="Связанный источник", + ) + created_by = models.ForeignKey( + CustomUser, + on_delete=models.SET_NULL, + related_name="marks_created", + null=True, + blank=True, + verbose_name="Создан пользователем", + help_text="Пользователь, создавший отметку", + ) + + def can_edit(self): + """Проверка возможности редактирования отметки (в течение 5 минут)""" + from datetime import timedelta + if not self.timestamp: + return False + time_diff = timezone.now() - self.timestamp + return time_diff < timedelta(minutes=5) + + def can_add_new_mark_for_object(self): + """Проверка возможности добавления новой отметки для объекта (прошло 5 минут с последней)""" + from datetime import timedelta + if not self.timestamp: + return True + time_diff = timezone.now() - self.timestamp + return time_diff >= timedelta(minutes=5) + + def __str__(self): + if self.timestamp: + timestamp = self.timestamp.strftime("%d.%m.%Y %H:%M") + return f"+ {timestamp}" if self.mark else f"- {timestamp}" + return "Отметка без времени" + + class Meta: + verbose_name = "Отметка источника" + verbose_name_plural = "Отметки источников" + ordering = ["-timestamp"] + + +# Для обратной совместимости с SigmaParameter class SigmaParMark(models.Model): """ - Модель отметки о наличии сигнала. + Модель отметки о наличии сигнала (для Sigma). Используется для фиксации моментов времени когда сигнал был обнаружен или потерян. """ @@ -97,8 +163,8 @@ class SigmaParMark(models.Model): return "Отметка без времени" class Meta: - verbose_name = "Отметка" - verbose_name_plural = "Отметки" + verbose_name = "Отметка сигнала" + verbose_name_plural = "Отметки сигналов" ordering = ["-timestamp"] diff --git a/dbapp/mainapp/templates/mainapp/components/_navbar.html b/dbapp/mainapp/templates/mainapp/components/_navbar.html index 7c19f8c..9040d1b 100644 --- a/dbapp/mainapp/templates/mainapp/components/_navbar.html +++ b/dbapp/mainapp/templates/mainapp/components/_navbar.html @@ -13,11 +13,14 @@