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 @@