diff --git a/dbapp/dbapp/settings/base.py b/dbapp/dbapp/settings/base.py index 8ecf2ff..8f486b6 100644 --- a/dbapp/dbapp/settings/base.py +++ b/dbapp/dbapp/settings/base.py @@ -150,6 +150,11 @@ USE_I18N = True USE_TZ = True +# Authentication settings +LOGIN_URL = 'login' +LOGIN_REDIRECT_URL = 'home' +LOGOUT_REDIRECT_URL = 'home' + # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/5.2/howto/static-files/ diff --git a/dbapp/dbapp/urls.py b/dbapp/dbapp/urls.py index c494521..2b68550 100644 --- a/dbapp/dbapp/urls.py +++ b/dbapp/dbapp/urls.py @@ -17,6 +17,7 @@ Including another URLconf from django.contrib import admin from django.urls import path, include from mainapp import views +from django.contrib.auth import views as auth_views from debug_toolbar.toolbar import debug_toolbar_urls urlpatterns = [ @@ -24,5 +25,8 @@ urlpatterns = [ path('admin/', admin.site.urls, name='admin'), # path('admin/map/', views.show_map_view, name='admin_show_map'), path('', include('mainapp.urls')), - path('', include('mapsapp.urls')) + path('', include('mapsapp.urls')), + # Authentication URLs + path('login/', auth_views.LoginView.as_view(), name='login'), + path('logout/', views.custom_logout, name='logout'), ] + debug_toolbar_urls() diff --git a/dbapp/mainapp/admin.py b/dbapp/mainapp/admin.py index 6c9b831..88b9283 100644 --- a/dbapp/mainapp/admin.py +++ b/dbapp/mainapp/admin.py @@ -40,10 +40,17 @@ from .filters import GeoKupDistanceFilter, GeoValidDistanceFilter, UniqueToggleF admin.site.site_title = "Геолокация" admin.site.site_header = "Geolocation" admin.site.index_title = "Geo" +# Unregister default User and Group since we're customizing them admin.site.unregister(User) admin.site.unregister(Group) +class CustomUserInline(admin.StackedInline): + model = CustomUser + can_delete = False + verbose_name_plural = 'Дополнительная информация пользователя' + + class LocationForm(forms.ModelForm): latitude_geo = forms.FloatField(required=False, label="Широта") longitude_geo = forms.FloatField(required=False, label="Долгота") @@ -117,23 +124,18 @@ class GeoInline(admin.StackedInline): ) -class CustomUserInline(admin.StackedInline): - model = CustomUser - can_delete = False - verbose_name_plural = 'Дополнительная информация пользователя' - - -@admin.register(CustomUser) -class CustomUserAdmin(admin.ModelAdmin): - list_display = ('user', 'role') - list_filter = ('role',) - - class UserAdmin(BaseUserAdmin): inlines = [CustomUserInline] admin.site.register(User, UserAdmin) + +# @admin.register(CustomUser) +# class CustomUserAdmin(admin.ModelAdmin): +# list_display = ('user', 'role') +# list_filter = ('role',) +# raw_id_fields = ('user',) # For better performance with large number of users + @admin.register(SigmaParMark) class SigmaParMarkAdmin(admin.ModelAdmin): list_display = ("mark", "timestamp") @@ -410,7 +412,6 @@ class ParameterObjItemInline(admin.StackedInline): max_num = 1 verbose_name = "ВЧ загрузка" verbose_name_plural = "ВЧ загрузки" - @admin.register(ObjItem) @@ -430,6 +431,8 @@ class ObjectAdmin(admin.ModelAdmin): "distance_geo_kup", "distance_geo_valid", "distance_kup_valid", + "created_at", + "updated_at", ) list_display_links = ("name",) list_filter = ( @@ -452,18 +455,28 @@ class ObjectAdmin(admin.ModelAdmin): ordering = ("name",) inlines = [ParameterObjItemInline, GeoInline] actions = [show_on_map] - - + readonly_fields = ('created_at', 'created_by', 'updated_at', 'updated_by') def get_queryset(self, request): qs = super().get_queryset(request) - return qs.select_related('geo_obj').prefetch_related( + return qs.select_related('geo_obj', 'created_by', 'updated_by').prefetch_related( 'parameters_obj__id_satellite', 'parameters_obj__polarization', 'parameters_obj__modulation', 'parameters_obj__standard' ) + def get_readonly_fields(self, request, obj=None): + # Always make these fields readonly to preserve tracking + return self.readonly_fields + + def save_model(self, request, obj, form, change): + if not change: + if not obj.created_by_id: + obj.created_by = request.user.customuser if hasattr(request.user, 'customuser') else None + obj.updated_by = request.user.customuser if hasattr(request.user, 'customuser') else None + super().save_model(request, obj, form, change) + def sat_name(self, obj): param = next(iter(obj.parameters_obj.all()), None) if param and param.id_satellite: diff --git a/dbapp/mainapp/apps.py b/dbapp/mainapp/apps.py index 8aa89a8..7047f84 100644 --- a/dbapp/mainapp/apps.py +++ b/dbapp/mainapp/apps.py @@ -4,3 +4,6 @@ from django.apps import AppConfig class MainappConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'mainapp' + + def ready(self): + import mainapp.signals # noqa diff --git a/dbapp/mainapp/management/commands/create_user_profiles.py b/dbapp/mainapp/management/commands/create_user_profiles.py new file mode 100644 index 0000000..50910df --- /dev/null +++ b/dbapp/mainapp/management/commands/create_user_profiles.py @@ -0,0 +1,20 @@ +from django.core.management.base import BaseCommand +from django.contrib.auth.models import User +from mainapp.models import CustomUser + + +class Command(BaseCommand): + help = 'Create CustomUser profiles for existing users who do not have them' + + def handle(self, *args, **options): + # Find all users who don't have a CustomUser profile + for user in User.objects.all(): + if not hasattr(user, 'customuser'): + custom_user = CustomUser.objects.create(user=user) + self.stdout.write( + self.style.SUCCESS(f'Created CustomUser for {user.username}') + ) + + self.stdout.write( + self.style.SUCCESS('Successfully ensured all users have CustomUser profiles') + ) \ No newline at end of file diff --git a/dbapp/mainapp/migrations/0001_initial.py b/dbapp/mainapp/migrations/0001_initial.py index fe3ada0..66f8653 100644 --- a/dbapp/mainapp/migrations/0001_initial.py +++ b/dbapp/mainapp/migrations/0001_initial.py @@ -1,7 +1,9 @@ -# Generated by Django 5.2.7 on 2025-10-13 12:47 +# Generated by Django 5.2.7 on 2025-10-31 13:36 import django.contrib.gis.db.models.fields +import django.contrib.gis.db.models.functions import django.db.models.deletion +import django.db.models.expressions import mainapp.models from django.conf import settings from django.db import migrations, models @@ -31,7 +33,7 @@ class Migration(migrations.Migration): name='Modulation', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=20, unique=True, verbose_name='Модуляция')), + ('name', models.CharField(db_index=True, max_length=20, unique=True, verbose_name='Модуляция')), ], options={ 'verbose_name': 'Модуляция', @@ -53,7 +55,7 @@ class Migration(migrations.Migration): name='Satellite', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=30, unique=True, verbose_name='Имя спутника')), + ('name', models.CharField(db_index=True, max_length=100, unique=True, verbose_name='Имя спутника')), ('norad', models.IntegerField(blank=True, null=True, verbose_name='NORAD ID')), ], options={ @@ -61,6 +63,18 @@ class Migration(migrations.Migration): 'verbose_name_plural': 'Спутники', }, ), + migrations.CreateModel( + name='SigmaParMark', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('mark', models.BooleanField(blank=True, null=True, verbose_name='Наличие сигнала')), + ('timestamp', models.DateTimeField(blank=True, null=True, verbose_name='Время')), + ], + options={ + 'verbose_name': 'Отметка', + 'verbose_name_plural': 'Отметки', + }, + ), migrations.CreateModel( name='Standard', fields=[ @@ -85,35 +99,30 @@ class Migration(migrations.Migration): }, ), migrations.CreateModel( - name='Geo', + name='ObjItem', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('timestamp', models.DateTimeField(blank=True, null=True, verbose_name='Время')), - ('coords', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326, verbose_name='Координата геолокации')), - ('location', models.CharField(blank=True, max_length=255, null=True, verbose_name='Метоположение')), - ('comment', models.CharField(blank=True, max_length=255, verbose_name='Комментарий')), - ('coords_kupsat', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326, verbose_name='Координаты Кубсата')), - ('coords_valid', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326, verbose_name='Координаты оперативников')), - ('is_average', models.BooleanField(blank=True, null=True, verbose_name='Усреднённое')), - ('id_user_add', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='geos_added', to='mainapp.customuser', verbose_name='Пользователь')), - ('mirrors', models.ManyToManyField(related_name='geo_mirrors', to='mainapp.mirror', verbose_name='Зеркала')), + ('name', models.CharField(blank=True, db_index=True, max_length=100, null=True, verbose_name='Имя объекта')), + ('id_user_add', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='objitems', to='mainapp.customuser', verbose_name='Пользователь')), ], options={ - 'verbose_name': 'Гео', - 'verbose_name_plural': 'Гео', + 'verbose_name': 'Объект', + 'verbose_name_plural': 'Объекты', }, ), migrations.CreateModel( name='Parameter', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('frequency', models.FloatField(blank=True, default=0, null=True, verbose_name='Частота, МГц')), + ('frequency', models.FloatField(blank=True, db_index=True, default=0, null=True, verbose_name='Частота, МГц')), ('freq_range', models.FloatField(blank=True, default=0, null=True, verbose_name='Полоса частот, МГц')), ('bod_velocity', models.FloatField(blank=True, default=0, null=True, verbose_name='Символьная скорость, БОД')), ('snr', models.FloatField(blank=True, default=0, null=True, verbose_name='ОСШ')), ('id_user_add', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parameter_added', to='mainapp.customuser', verbose_name='Пользователь')), ('modulation', models.ForeignKey(blank=True, default=mainapp.models.get_default_modulation, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='modulations', to='mainapp.modulation', verbose_name='Модуляция')), + ('objitems', models.ManyToManyField(blank=True, related_name='parameters_obj', to='mainapp.objitem', verbose_name='Источники')), ('polarization', models.ForeignKey(blank=True, default=mainapp.models.get_default_polarization, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='polarizations', to='mainapp.polarization', verbose_name='Поляризация')), + ('id_satellite', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='parameters', to='mainapp.satellite', verbose_name='Спутник')), ('standard', models.ForeignKey(blank=True, default=mainapp.models.get_default_standard, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='standards', to='mainapp.standard', verbose_name='Стандарт')), ], options={ @@ -122,26 +131,74 @@ class Migration(migrations.Migration): }, ), migrations.CreateModel( - name='ObjItem', + name='SourceType', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(blank=True, max_length=100, null=True, verbose_name='Имя объекта')), - ('id_geo', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='objitems', to='mainapp.geo', verbose_name='Геоданные')), - ('id_user_add', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='objitems', to='mainapp.customuser', verbose_name='Пользователь')), - ('id_vch_load', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='objitems', to='mainapp.parameter', verbose_name='ВЧ загрузка')), - ('id_satellite', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='objitems', to='mainapp.satellite', verbose_name='Спутник')), + ('name', models.CharField(max_length=50, unique=True, verbose_name='Тип источника')), + ('objitem', models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='source_type_obj', to='mainapp.objitem', verbose_name='Гео')), ], options={ - 'verbose_name': 'Объект', - 'verbose_name_plural': 'Объекты', + 'verbose_name': 'Тип источника', + 'verbose_name_plural': 'Типы источников', }, ), - migrations.AddConstraint( - model_name='geo', - constraint=models.UniqueConstraint(fields=('timestamp', 'coords'), name='unique_geo_combination'), + migrations.CreateModel( + name='SigmaParameter', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('transfer', models.FloatField(choices=[(-1.0, '-'), (9750.0, '9750 МГц'), (10750.0, '10750 МГц')], default=-1.0, verbose_name='Перенос по частоте')), + ('status', models.CharField(blank=True, max_length=20, null=True, verbose_name='Статус')), + ('frequency', models.FloatField(blank=True, db_index=True, default=0, null=True, verbose_name='Частота, МГц')), + ('transfer_frequency', models.GeneratedField(db_persist=True, expression=models.ExpressionWrapper(django.db.models.expressions.CombinedExpression(models.F('frequency'), '+', models.F('transfer')), output_field=models.FloatField()), null=True, output_field=models.FloatField(), verbose_name='Частота в Ku, МГц')), + ('freq_range', models.FloatField(blank=True, default=0, null=True, verbose_name='Полоса частот, МГц')), + ('power', models.FloatField(blank=True, default=0, null=True, verbose_name='Мощность, дБм')), + ('bod_velocity', models.FloatField(blank=True, default=0, null=True, verbose_name='Символьная скорость, БОД')), + ('snr', models.FloatField(blank=True, default=0, null=True, verbose_name='ОСШ, Дб')), + ('packets', models.BooleanField(blank=True, null=True, verbose_name='Пакетность')), + ('datetime_begin', models.DateTimeField(blank=True, null=True, verbose_name='Время начала измерения')), + ('datetime_end', models.DateTimeField(blank=True, null=True, verbose_name='Время окончания измерения')), + ('id_satellite', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='sigmapar_sat', to='mainapp.satellite', verbose_name='Спутник')), + ('modulation', models.ForeignKey(blank=True, default=mainapp.models.get_default_modulation, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='modulations_sigma', to='mainapp.modulation', verbose_name='Модуляция')), + ('parameter', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sigma_parameter', to='mainapp.parameter', verbose_name='ВЧ')), + ('polarization', models.ForeignKey(blank=True, default=mainapp.models.get_default_polarization, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='polarizations_sigma', to='mainapp.polarization', verbose_name='Поляризация')), + ('mark', models.ManyToManyField(blank=True, to='mainapp.sigmaparmark', verbose_name='Отметка')), + ('standard', models.ForeignKey(blank=True, default=mainapp.models.get_default_standard, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='standards_sigma', to='mainapp.standard', verbose_name='Стандарт')), + ], + options={ + 'verbose_name': 'ВЧ sigma', + 'verbose_name_plural': 'ВЧ sigma', + }, ), - migrations.AddConstraint( - model_name='objitem', - constraint=models.UniqueConstraint(fields=('id_vch_load', 'id_geo'), name='unique_objitem_combination'), + migrations.CreateModel( + name='Geo', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('timestamp', models.DateTimeField(blank=True, db_index=True, null=True, verbose_name='Время')), + ('coords', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326, verbose_name='Координата геолокации')), + ('location', models.CharField(blank=True, max_length=255, null=True, verbose_name='Метоположение')), + ('comment', models.CharField(blank=True, max_length=255, verbose_name='Комментарий')), + ('coords_kupsat', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326, verbose_name='Координаты Кубсата')), + ('coords_valid', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326, verbose_name='Координаты оперативников')), + ('is_average', models.BooleanField(blank=True, null=True, verbose_name='Усреднённое')), + ('distance_coords_kup', models.GeneratedField(db_persist=True, expression=django.db.models.expressions.CombinedExpression(django.contrib.gis.db.models.functions.Distance('coords', 'coords_kupsat'), '/', models.Value(1000)), null=True, output_field=models.FloatField(), verbose_name='Расстояние между купсатом и гео, км')), + ('distance_coords_valid', models.GeneratedField(db_persist=True, expression=django.db.models.expressions.CombinedExpression(django.contrib.gis.db.models.functions.Distance('coords', 'coords_valid'), '/', models.Value(1000)), null=True, output_field=models.FloatField(), verbose_name='Расстояние между гео и оперативным отделом, км')), + ('distance_kup_valid', models.GeneratedField(db_persist=True, expression=django.db.models.expressions.CombinedExpression(django.contrib.gis.db.models.functions.Distance('coords_valid', 'coords_kupsat'), '/', models.Value(1000)), null=True, output_field=models.FloatField(), verbose_name='Расстояние между купсатом и оперативным отделом, км')), + ('id_user_add', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='geos_added', to='mainapp.customuser', verbose_name='Пользователь')), + ('mirrors', models.ManyToManyField(related_name='geo_mirrors', to='mainapp.mirror', verbose_name='Зеркала')), + ('objitem', models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='geo_obj', to='mainapp.objitem', verbose_name='Гео')), + ], + options={ + 'verbose_name': 'Гео', + 'verbose_name_plural': 'Гео', + 'constraints': [models.UniqueConstraint(fields=('timestamp', 'coords'), name='unique_geo_combination')], + }, + ), + migrations.AddIndex( + model_name='parameter', + index=models.Index(fields=['id_satellite', 'frequency'], name='mainapp_par_id_sate_cbfab2_idx'), + ), + migrations.AddIndex( + model_name='parameter', + index=models.Index(fields=['frequency', 'polarization'], name='mainapp_par_frequen_75a049_idx'), ), ] diff --git a/dbapp/mainapp/migrations/0002_geo_distance_coords_kup.py b/dbapp/mainapp/migrations/0002_geo_distance_coords_kup.py deleted file mode 100644 index 2765567..0000000 --- a/dbapp/mainapp/migrations/0002_geo_distance_coords_kup.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-15 09:23 - -import django.contrib.gis.db.models.functions -import django.db.models.expressions -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mainapp', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='geo', - name='distance_coords_kup', - field=models.GeneratedField(db_persist=True, expression=django.db.models.expressions.CombinedExpression(django.contrib.gis.db.models.functions.Distance('coords', 'coords_kupsat'), '/', models.Value(1000)), null=True, output_field=models.FloatField(), verbose_name='Расстояние между купсатом и гео'), - ), - ] diff --git a/dbapp/mainapp/migrations/0002_objitem_created_at_objitem_created_by_and_more.py b/dbapp/mainapp/migrations/0002_objitem_created_at_objitem_created_by_and_more.py new file mode 100644 index 0000000..8669b50 --- /dev/null +++ b/dbapp/mainapp/migrations/0002_objitem_created_at_objitem_created_by_and_more.py @@ -0,0 +1,35 @@ +# Generated by Django 5.2.7 on 2025-10-31 13:56 + +import django.db.models.deletion +import django.utils.timezone +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mainapp', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='objitem', + name='created_at', + field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Дата создания'), + ), + migrations.AddField( + model_name='objitem', + name='created_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='objitems_created', to='mainapp.customuser', verbose_name='Создан пользователем'), + ), + migrations.AddField( + model_name='objitem', + name='updated_at', + field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Дата последнего изменения'), + ), + migrations.AddField( + model_name='objitem', + name='updated_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='objitems_updated', to='mainapp.customuser', verbose_name='Изменен пользователем'), + ), + ] diff --git a/dbapp/mainapp/migrations/0003_alter_objitem_created_at_alter_objitem_updated_at.py b/dbapp/mainapp/migrations/0003_alter_objitem_created_at_alter_objitem_updated_at.py new file mode 100644 index 0000000..2503260 --- /dev/null +++ b/dbapp/mainapp/migrations/0003_alter_objitem_created_at_alter_objitem_updated_at.py @@ -0,0 +1,23 @@ +# Generated by Django 5.2.7 on 2025-10-31 14:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mainapp', '0002_objitem_created_at_objitem_created_by_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='objitem', + name='created_at', + field=models.DateTimeField(auto_now_add=True, verbose_name='Дата создания'), + ), + migrations.AlterField( + model_name='objitem', + name='updated_at', + field=models.DateTimeField(auto_now=True, verbose_name='Дата последнего изменения'), + ), + ] diff --git a/dbapp/mainapp/migrations/0003_geo_distance_coords_valid_geo_distance_kup_valid_and_more.py b/dbapp/mainapp/migrations/0003_geo_distance_coords_valid_geo_distance_kup_valid_and_more.py deleted file mode 100644 index 9db723c..0000000 --- a/dbapp/mainapp/migrations/0003_geo_distance_coords_valid_geo_distance_kup_valid_and_more.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-15 09:43 - -import django.contrib.gis.db.models.functions -import django.db.models.expressions -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mainapp', '0002_geo_distance_coords_kup'), - ] - - operations = [ - migrations.AddField( - model_name='geo', - name='distance_coords_valid', - field=models.GeneratedField(db_persist=True, expression=django.db.models.expressions.CombinedExpression(django.contrib.gis.db.models.functions.Distance('coords', 'coords_valid'), '/', models.Value(1000)), null=True, output_field=models.FloatField(), verbose_name='Расстояние между гео и оперативным отделом, км'), - ), - migrations.AddField( - model_name='geo', - name='distance_kup_valid', - field=models.GeneratedField(db_persist=True, expression=django.db.models.expressions.CombinedExpression(django.contrib.gis.db.models.functions.Distance('coords_valid', 'coords_kupsat'), '/', models.Value(1000)), null=True, output_field=models.FloatField(), verbose_name='Расстояние между купсатом и оперативным отделом, км'), - ), - migrations.AlterField( - model_name='geo', - name='distance_coords_kup', - field=models.GeneratedField(db_persist=True, expression=django.db.models.expressions.CombinedExpression(django.contrib.gis.db.models.functions.Distance('coords', 'coords_kupsat'), '/', models.Value(1000)), null=True, output_field=models.FloatField(), verbose_name='Расстояние между купсатом и гео, км'), - ), - ] diff --git a/dbapp/mainapp/migrations/0004_sigmaparameter.py b/dbapp/mainapp/migrations/0004_sigmaparameter.py deleted file mode 100644 index 75bbbeb..0000000 --- a/dbapp/mainapp/migrations/0004_sigmaparameter.py +++ /dev/null @@ -1,36 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-16 12:50 - -import django.db.models.deletion -import mainapp.models -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mainapp', '0003_geo_distance_coords_valid_geo_distance_kup_valid_and_more'), - ] - - operations = [ - migrations.CreateModel( - name='SigmaParameter', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('status', models.CharField(blank=True, max_length=20, null=True, verbose_name='Статус')), - ('frequency', models.FloatField(blank=True, default=0, null=True, verbose_name='Частота, МГц')), - ('freq_range', models.FloatField(blank=True, default=0, null=True, verbose_name='Полоса частот, МГц')), - ('power', models.FloatField(blank=True, default=0, null=True, verbose_name='Мощность, дБм')), - ('bod_velocity', models.FloatField(blank=True, default=0, null=True, verbose_name='Символьная скорость, БОД')), - ('snr', models.FloatField(blank=True, default=0, null=True, verbose_name='ОСШ, Дб')), - ('packets', models.BooleanField(blank=True, null=True, verbose_name='Пакетность')), - ('datetime_begin', models.DateTimeField(blank=True, null=True, verbose_name='Время начала измерения')), - ('datetime_end', models.DateTimeField(blank=True, null=True, verbose_name='Время окончания измерения')), - ('modulation', models.ForeignKey(blank=True, default=mainapp.models.get_default_modulation, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='modulations_sigma', to='mainapp.modulation', verbose_name='Модуляция')), - ('standard', models.ForeignKey(blank=True, default=mainapp.models.get_default_standard, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='standards_sigma', to='mainapp.standard', verbose_name='Стандарт')), - ], - options={ - 'verbose_name': 'ВЧ sigma', - 'verbose_name_plural': 'ВЧ sigma', - }, - ), - ] diff --git a/dbapp/mainapp/migrations/0005_sigmaparameter_id_satellite.py b/dbapp/mainapp/migrations/0005_sigmaparameter_id_satellite.py deleted file mode 100644 index 78de627..0000000 --- a/dbapp/mainapp/migrations/0005_sigmaparameter_id_satellite.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-20 07:57 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mainapp', '0004_sigmaparameter'), - ] - - operations = [ - migrations.AddField( - model_name='sigmaparameter', - name='id_satellite', - field=models.ForeignKey(default=2, on_delete=django.db.models.deletion.PROTECT, related_name='sigmapar_sat', to='mainapp.satellite', verbose_name='Спутник'), - preserve_default=False, - ), - ] diff --git a/dbapp/mainapp/migrations/0006_remove_objitem_id_satellite_parameter_id_satellite.py b/dbapp/mainapp/migrations/0006_remove_objitem_id_satellite_parameter_id_satellite.py deleted file mode 100644 index d32435e..0000000 --- a/dbapp/mainapp/migrations/0006_remove_objitem_id_satellite_parameter_id_satellite.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-20 11:57 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mainapp', '0005_sigmaparameter_id_satellite'), - ] - - operations = [ - migrations.RemoveField( - model_name='objitem', - name='id_satellite', - ), - migrations.AddField( - model_name='parameter', - name='id_satellite', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='parameters', to='mainapp.satellite', verbose_name='Спутник'), - ), - ] diff --git a/dbapp/mainapp/migrations/0007_alter_parameter_id_satellite.py b/dbapp/mainapp/migrations/0007_alter_parameter_id_satellite.py deleted file mode 100644 index d288cb7..0000000 --- a/dbapp/mainapp/migrations/0007_alter_parameter_id_satellite.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-20 11:58 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mainapp', '0006_remove_objitem_id_satellite_parameter_id_satellite'), - ] - - operations = [ - migrations.AlterField( - model_name='parameter', - name='id_satellite', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='parameters', to='mainapp.satellite', verbose_name='Спутник'), - ), - ] diff --git a/dbapp/mainapp/migrations/0008_alter_geo_timestamp_alter_objitem_name_and_more.py b/dbapp/mainapp/migrations/0008_alter_geo_timestamp_alter_objitem_name_and_more.py deleted file mode 100644 index d3bbd5a..0000000 --- a/dbapp/mainapp/migrations/0008_alter_geo_timestamp_alter_objitem_name_and_more.py +++ /dev/null @@ -1,38 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-22 11:21 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mainapp', '0007_alter_parameter_id_satellite'), - ] - - operations = [ - migrations.AlterField( - model_name='geo', - name='timestamp', - field=models.DateTimeField(blank=True, db_index=True, null=True, verbose_name='Время'), - ), - migrations.AlterField( - model_name='objitem', - name='name', - field=models.CharField(blank=True, db_index=True, max_length=100, null=True, verbose_name='Имя объекта'), - ), - migrations.AlterField( - model_name='parameter', - name='frequency', - field=models.FloatField(blank=True, db_index=True, default=0, null=True, verbose_name='Частота, МГц'), - ), - migrations.AlterField( - model_name='satellite', - name='name', - field=models.CharField(db_index=True, max_length=30, unique=True, verbose_name='Имя спутника'), - ), - migrations.AlterField( - model_name='sigmaparameter', - name='frequency', - field=models.FloatField(blank=True, db_index=True, default=0, null=True, verbose_name='Частота, МГц'), - ), - ] diff --git a/dbapp/mainapp/migrations/0009_parameter_id_sigma_parameter.py b/dbapp/mainapp/migrations/0009_parameter_id_sigma_parameter.py deleted file mode 100644 index af7c76d..0000000 --- a/dbapp/mainapp/migrations/0009_parameter_id_sigma_parameter.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-22 12:08 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mainapp', '0008_alter_geo_timestamp_alter_objitem_name_and_more'), - ] - - operations = [ - migrations.AddField( - model_name='parameter', - name='id_sigma_parameter', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sigma_parameter', to='mainapp.sigmaparameter', verbose_name='ВЧ с sigma'), - ), - ] diff --git a/dbapp/mainapp/migrations/0010_sigmaparmark_sigmaparameter_mark.py b/dbapp/mainapp/migrations/0010_sigmaparmark_sigmaparameter_mark.py deleted file mode 100644 index 6049caf..0000000 --- a/dbapp/mainapp/migrations/0010_sigmaparmark_sigmaparameter_mark.py +++ /dev/null @@ -1,31 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-22 12:38 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mainapp', '0009_parameter_id_sigma_parameter'), - ] - - operations = [ - migrations.CreateModel( - name='SigmaParMark', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('mark', models.BooleanField(blank=True, null=True, verbose_name='Наличие сигнала')), - ('timestamp', models.DateTimeField(blank=True, null=True, verbose_name='Время')), - ], - options={ - 'verbose_name': 'Отметка', - 'verbose_name_plural': 'Отметки', - }, - ), - migrations.AddField( - model_name='sigmaparameter', - name='mark', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mainapp.sigmaparmark', verbose_name='Отметка'), - ), - ] diff --git a/dbapp/mainapp/migrations/0011_remove_sigmaparameter_mark_sigmaparameter_mark.py b/dbapp/mainapp/migrations/0011_remove_sigmaparameter_mark_sigmaparameter_mark.py deleted file mode 100644 index 226aea9..0000000 --- a/dbapp/mainapp/migrations/0011_remove_sigmaparameter_mark_sigmaparameter_mark.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-22 13:15 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mainapp', '0010_sigmaparmark_sigmaparameter_mark'), - ] - - operations = [ - migrations.RemoveField( - model_name='sigmaparameter', - name='mark', - ), - migrations.AddField( - model_name='sigmaparameter', - name='mark', - field=models.ManyToManyField(blank=True, null=True, to='mainapp.sigmaparmark', verbose_name='Отметка'), - ), - ] diff --git a/dbapp/mainapp/migrations/0012_alter_sigmaparameter_mark.py b/dbapp/mainapp/migrations/0012_alter_sigmaparameter_mark.py deleted file mode 100644 index 7246fe2..0000000 --- a/dbapp/mainapp/migrations/0012_alter_sigmaparameter_mark.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-22 13:16 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mainapp', '0011_remove_sigmaparameter_mark_sigmaparameter_mark'), - ] - - operations = [ - migrations.AlterField( - model_name='sigmaparameter', - name='mark', - field=models.ManyToManyField(blank=True, to='mainapp.sigmaparmark', verbose_name='Отметка'), - ), - ] diff --git a/dbapp/mainapp/migrations/0013_parameter_mainapp_par_id_sate_cbfab2_idx_and_more.py b/dbapp/mainapp/migrations/0013_parameter_mainapp_par_id_sate_cbfab2_idx_and_more.py deleted file mode 100644 index 872fe8e..0000000 --- a/dbapp/mainapp/migrations/0013_parameter_mainapp_par_id_sate_cbfab2_idx_and_more.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-22 13:48 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mainapp', '0012_alter_sigmaparameter_mark'), - ] - - operations = [ - migrations.AddIndex( - model_name='parameter', - index=models.Index(fields=['id_satellite', 'frequency'], name='mainapp_par_id_sate_cbfab2_idx'), - ), - migrations.AddIndex( - model_name='parameter', - index=models.Index(fields=['frequency', 'polarization'], name='mainapp_par_frequen_75a049_idx'), - ), - ] diff --git a/dbapp/mainapp/migrations/0014_alter_modulation_name.py b/dbapp/mainapp/migrations/0014_alter_modulation_name.py deleted file mode 100644 index 71bce80..0000000 --- a/dbapp/mainapp/migrations/0014_alter_modulation_name.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-23 08:12 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mainapp', '0013_parameter_mainapp_par_id_sate_cbfab2_idx_and_more'), - ] - - operations = [ - migrations.AlterField( - model_name='modulation', - name='name', - field=models.CharField(db_index=True, max_length=20, unique=True, verbose_name='Модуляция'), - ), - ] diff --git a/dbapp/mainapp/migrations/0015_alter_parameter_id_sigma_parameter.py b/dbapp/mainapp/migrations/0015_alter_parameter_id_sigma_parameter.py deleted file mode 100644 index 9306a1f..0000000 --- a/dbapp/mainapp/migrations/0015_alter_parameter_id_sigma_parameter.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-23 09:40 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mainapp', '0014_alter_modulation_name'), - ] - - operations = [ - migrations.AlterField( - model_name='parameter', - name='id_sigma_parameter', - field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sigma_parameter', to='mainapp.sigmaparameter', verbose_name='ВЧ с sigma'), - ), - ] diff --git a/dbapp/mainapp/migrations/0016_remove_parameter_id_sigma_parameter_and_more.py b/dbapp/mainapp/migrations/0016_remove_parameter_id_sigma_parameter_and_more.py deleted file mode 100644 index 3171be4..0000000 --- a/dbapp/mainapp/migrations/0016_remove_parameter_id_sigma_parameter_and_more.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-23 09:58 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mainapp', '0015_alter_parameter_id_sigma_parameter'), - ] - - operations = [ - migrations.RemoveField( - model_name='parameter', - name='id_sigma_parameter', - ), - migrations.AddField( - model_name='sigmaparameter', - name='parameter', - field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='sigma_parameter', to='mainapp.parameter', verbose_name='ВЧ с sigma'), - ), - ] diff --git a/dbapp/mainapp/migrations/0017_alter_sigmaparameter_parameter.py b/dbapp/mainapp/migrations/0017_alter_sigmaparameter_parameter.py deleted file mode 100644 index 68c6211..0000000 --- a/dbapp/mainapp/migrations/0017_alter_sigmaparameter_parameter.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-23 12:52 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mainapp', '0016_remove_parameter_id_sigma_parameter_and_more'), - ] - - operations = [ - migrations.AlterField( - model_name='sigmaparameter', - name='parameter', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sigma_parameter', to='mainapp.parameter', verbose_name='ВЧ'), - ), - ] diff --git a/dbapp/mainapp/migrations/0018_sigmaparameter_polarization_sigmaparameter_transfer_and_more.py b/dbapp/mainapp/migrations/0018_sigmaparameter_polarization_sigmaparameter_transfer_and_more.py deleted file mode 100644 index b3059f3..0000000 --- a/dbapp/mainapp/migrations/0018_sigmaparameter_polarization_sigmaparameter_transfer_and_more.py +++ /dev/null @@ -1,31 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-27 13:10 - -import django.db.models.deletion -import django.db.models.expressions -import mainapp.models -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mainapp', '0017_alter_sigmaparameter_parameter'), - ] - - operations = [ - migrations.AddField( - model_name='sigmaparameter', - name='polarization', - field=models.ForeignKey(blank=True, default=mainapp.models.get_default_polarization, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='polarizations_sigma', to='mainapp.polarization', verbose_name='Поляризация'), - ), - migrations.AddField( - model_name='sigmaparameter', - name='transfer', - field=models.FloatField(choices=[(-1.0, '-'), (9750.0, '9750 МГц'), (10750.0, '10750 МГц')], default=-1.0, verbose_name='Перенос по частоте'), - ), - migrations.AddField( - model_name='sigmaparameter', - name='transfer_frequency', - field=models.GeneratedField(db_persist=True, expression=models.ExpressionWrapper(django.db.models.expressions.CombinedExpression(models.F('frequency'), '+', models.F('transfer')), output_field=models.FloatField()), null=True, output_field=models.FloatField(), verbose_name='Частота в Ku, МГц'), - ), - ] diff --git a/dbapp/mainapp/migrations/0019_alter_satellite_name.py b/dbapp/mainapp/migrations/0019_alter_satellite_name.py deleted file mode 100644 index eeec1fb..0000000 --- a/dbapp/mainapp/migrations/0019_alter_satellite_name.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-28 05:41 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mainapp', '0018_sigmaparameter_polarization_sigmaparameter_transfer_and_more'), - ] - - operations = [ - migrations.AlterField( - model_name='satellite', - name='name', - field=models.CharField(db_index=True, max_length=100, unique=True, verbose_name='Имя спутника'), - ), - ] diff --git a/dbapp/mainapp/migrations/0020_sourcetype_objitem_id_source_type.py b/dbapp/mainapp/migrations/0020_sourcetype_objitem_id_source_type.py deleted file mode 100644 index 4359cb1..0000000 --- a/dbapp/mainapp/migrations/0020_sourcetype_objitem_id_source_type.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-29 14:00 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mainapp', '0019_alter_satellite_name'), - ] - - operations = [ - migrations.CreateModel( - name='SourceType', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=50, unique=True, verbose_name='Тип источника')), - ], - options={ - 'verbose_name': 'Тип источника', - 'verbose_name_plural': 'Типы источников', - }, - ), - migrations.AddField( - model_name='objitem', - name='id_source_type', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='objitems', to='mainapp.sourcetype', verbose_name='Тип источника'), - ), - ] diff --git a/dbapp/mainapp/migrations/0021_remove_objitem_unique_objitem_combination_and_more.py b/dbapp/mainapp/migrations/0021_remove_objitem_unique_objitem_combination_and_more.py deleted file mode 100644 index 2d01e77..0000000 --- a/dbapp/mainapp/migrations/0021_remove_objitem_unique_objitem_combination_and_more.py +++ /dev/null @@ -1,45 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-30 06:40 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mainapp', '0020_sourcetype_objitem_id_source_type'), - ] - - operations = [ - migrations.RemoveConstraint( - model_name='objitem', - name='unique_objitem_combination', - ), - migrations.RemoveField( - model_name='objitem', - name='id_geo', - ), - migrations.RemoveField( - model_name='objitem', - name='id_source_type', - ), - migrations.RemoveField( - model_name='objitem', - name='id_vch_load', - ), - migrations.AddField( - model_name='geo', - name='objitem', - field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='geo_obj', to='mainapp.objitem', verbose_name='Гео'), - ), - migrations.AddField( - model_name='parameter', - name='objitem', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parameters_obj', to='mainapp.objitem', verbose_name='Источник'), - ), - migrations.AddField( - model_name='sourcetype', - name='objitem', - field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='source_type_obj', to='mainapp.objitem', verbose_name='Гео'), - ), - ] diff --git a/dbapp/mainapp/migrations/0022_remove_parameter_objitem_parameter_objitems.py b/dbapp/mainapp/migrations/0022_remove_parameter_objitem_parameter_objitems.py deleted file mode 100644 index 228f335..0000000 --- a/dbapp/mainapp/migrations/0022_remove_parameter_objitem_parameter_objitems.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-30 07:05 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mainapp', '0021_remove_objitem_unique_objitem_combination_and_more'), - ] - - operations = [ - migrations.RemoveField( - model_name='parameter', - name='objitem', - ), - migrations.AddField( - model_name='parameter', - name='objitems', - field=models.ManyToManyField(blank=True, related_name='parameters_obj', to='mainapp.objitem', verbose_name='Источники'), - ), - ] diff --git a/dbapp/mainapp/models.py b/dbapp/mainapp/models.py index 2a0296a..7bc3f23 100644 --- a/dbapp/mainapp/models.py +++ b/dbapp/mainapp/models.py @@ -3,6 +3,7 @@ from django.contrib.auth.models import User from django.contrib.gis.db import models as gis from django.contrib.gis.db.models import functions from django.db.models import F, ExpressionWrapper +from django.utils import timezone def get_default_polarization(): obj, created = Polarization.objects.get_or_create( @@ -113,8 +114,15 @@ class ObjItem(models.Model): # id_satellite = models.ForeignKey(Satellite, on_delete=models.PROTECT, related_name="objitems", verbose_name="Спутник") # id_vch_load = models.ForeignKey(Parameter, on_delete=models.CASCADE, related_name="objitems", verbose_name="ВЧ загрузка") # id_geo = models.ForeignKey(Geo, on_delete=models.CASCADE, related_name="objitems", verbose_name="Геоданные") - id_user_add = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, related_name="objitems", verbose_name="Пользователь", null=True, blank=True) + # id_user_add = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, related_name="objitems", verbose_name="Пользователь", null=True, blank=True) # id_source_type = models.ForeignKey(SourceType, on_delete=models.SET_NULL, related_name="objitems", verbose_name='Тип источника', null=True, blank=True) + + created_at = models.DateTimeField(auto_now_add=True, verbose_name="Дата создания") + created_by = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, related_name="objitems_created", + null=True, blank=True, verbose_name="Создан пользователем") + updated_at = models.DateTimeField(auto_now=True, verbose_name="Дата последнего изменения") + updated_by = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, related_name="objitems_updated", + null=True, blank=True, verbose_name="Изменен пользователем") def __str__(self): diff --git a/dbapp/mainapp/signals.py b/dbapp/mainapp/signals.py new file mode 100644 index 0000000..0efba85 --- /dev/null +++ b/dbapp/mainapp/signals.py @@ -0,0 +1,11 @@ +from django.db.models.signals import post_save +from django.dispatch import receiver +from django.contrib.auth.models import User +from .models import CustomUser + + +@receiver(post_save, sender=User) +def create_or_update_user_profile(sender, instance, created, **kwargs): + if created: + CustomUser.objects.create(user=instance) + instance.customuser.save() \ No newline at end of file diff --git a/dbapp/mainapp/templates/mainapp/base.html b/dbapp/mainapp/templates/mainapp/base.html index 839b041..5c8b524 100644 --- a/dbapp/mainapp/templates/mainapp/base.html +++ b/dbapp/mainapp/templates/mainapp/base.html @@ -22,6 +22,7 @@ diff --git a/dbapp/mainapp/templates/mainapp/login_required.html b/dbapp/mainapp/templates/mainapp/login_required.html new file mode 100644 index 0000000..e344016 --- /dev/null +++ b/dbapp/mainapp/templates/mainapp/login_required.html @@ -0,0 +1,19 @@ +{% extends 'mainapp/base.html' %} + +{% block title %}Войдите в систему{% endblock %} + +{% block content %} +
+
+
+
+
+

Требуется авторизация

+

Для просмотра содержимого сайта необходимо войти в систему.

+ Войти +
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/dbapp/mainapp/templates/mainapp/objitem_list.html b/dbapp/mainapp/templates/mainapp/objitem_list.html index 8495eeb..437f1f2 100644 --- a/dbapp/mainapp/templates/mainapp/objitem_list.html +++ b/dbapp/mainapp/templates/mainapp/objitem_list.html @@ -16,13 +16,84 @@
-
- - + +
+
+ + + +
+ + +
+ + + +
+ + +
+ + +
+ +
- - + {% if page_obj.paginator.num_pages > 1 %} + + {% endif %} + + + {% if page_obj %} +
+ {{ page_obj.start_index }}-{{ page_obj.end_index }} из {{ page_obj.paginator.count }} +
+ {% endif %}
@@ -32,7 +103,7 @@
-
+
Фильтры
@@ -152,18 +223,6 @@
- -
- - -
-
@@ -175,7 +234,7 @@
-
+
@@ -189,8 +248,8 @@ Спутник Част, МГц Полоса, МГц - Поляр - Сим. v + Поляризация + Сим. V Модул ОСШ Геолокация @@ -199,6 +258,8 @@ Гео-куб, км Гео-опер, км Куб-опер, км + Обновлено + Кем @@ -221,10 +282,12 @@ {{ item.distance_geo_kup }} {{ item.distance_geo_valid }} {{ item.distance_kup_valid }} + {{ item.obj.updated_at|date:"d.m.Y H:i" }} + {{ item.obj.updated_by }} {% empty %} - + {% if selected_satellite_id %} Нет данных для выбранных фильтров {% else %} @@ -236,50 +299,6 @@
- - - {% if page_obj.paginator.num_pages > 1 %} - - {% endif %} - - - {% if page_obj %} -
-
Показано {{ page_obj.start_index }}-{{ page_obj.end_index }} из {{ page_obj.paginator.count }} записей
-
- {% endif %}
@@ -417,6 +436,21 @@ document.addEventListener('DOMContentLoaded', function() { updateSatelliteSelection(); }); } + + // Function to update items per page + window.updateItemsPerPage = function() { + const itemsPerPageSelect = document.getElementById('items-per-page'); + const currentParams = new URLSearchParams(window.location.search); + + // Add or update the items_per_page parameter + currentParams.set('items_per_page', itemsPerPageSelect.value); + + // Remove page parameter to reset to first page when changing items per page + currentParams.delete('page'); + + // Update URL and reload + window.location.search = currentParams.toString(); + }; }); {% endblock %} \ No newline at end of file diff --git a/dbapp/mainapp/templates/registration/logged_out.html b/dbapp/mainapp/templates/registration/logged_out.html new file mode 100644 index 0000000..35ef87b --- /dev/null +++ b/dbapp/mainapp/templates/registration/logged_out.html @@ -0,0 +1,19 @@ +{% extends 'mainapp/base.html' %} + +{% block title %}Выход{% endblock %} + +{% block content %} +
+
+
+
+
+

Вы вышли из системы

+

Вы успешно вышли из системы.

+ Войти снова +
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/dbapp/mainapp/templates/registration/login.html b/dbapp/mainapp/templates/registration/login.html new file mode 100644 index 0000000..3a84f90 --- /dev/null +++ b/dbapp/mainapp/templates/registration/login.html @@ -0,0 +1,43 @@ +{% extends 'mainapp/base.html' %} + +{% block title %}Вход в систему{% endblock %} + +{% block content %} +
+
+
+
+
+

Вход в систему

+
+ {% csrf_token %} +
+ + {{ form.username }} +
+
+ + {{ form.password }} +
+ {% if form.errors %} +
+ {% for field in form %} + {% for error in field.errors %} +

{{ error }}

+ {% endfor %} + {% endfor %} + {% for error in form.non_field_errors %} +

{{ error }}

+ {% endfor %} +
+ {% endif %} +
+ +
+
+
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/dbapp/mainapp/urls.py b/dbapp/mainapp/urls.py index 786c4d8..036f1d1 100644 --- a/dbapp/mainapp/urls.py +++ b/dbapp/mainapp/urls.py @@ -5,8 +5,9 @@ from . import views urlpatterns = [ - path('', views.ObjItemListView.as_view(), name='home'), # Make objitems the main page - path('actions/', views.HomePageView.as_view(), name='actions'), # Move actions to a separate page + path('', views.HomePageView.as_view(), name='home'), # Home page that redirects based on auth + path('objitems/', views.ObjItemListView.as_view(), name='objitem_list'), # Objects list page + path('actions/', views.ActionsPageView.as_view(), name='actions'), # Move actions to a separate page path('excel-data', views.LoadExcelDataView.as_view(), name='load_excel_data'), path('satellites', views.AddSatellitesView.as_view(), name='add_sats'), path('api/locations//geojson/', views.GetLocationsView.as_view(), name='locations_by_id'), @@ -17,7 +18,6 @@ urlpatterns = [ path('vch-upload/', views.UploadVchLoadView.as_view(), name='vch_load'), path('vch-link/', views.LinkVchSigmaView.as_view(), name='link_vch_sigma'), path('kubsat-excel/', views.ProcessKubsatView.as_view(), name='kubsat_excel'), - path('objitems/', views.ObjItemListView.as_view(), name='objitem_list'), # path('upload/', views.upload_file, name='upload_file'), ] \ No newline at end of file diff --git a/dbapp/mainapp/utils.py b/dbapp/mainapp/utils.py index 7c4513d..09409c2 100644 --- a/dbapp/mainapp/utils.py +++ b/dbapp/mainapp/utils.py @@ -120,7 +120,7 @@ def fill_data_from_df(df: pd.DataFrame, sat: Satellite): bod_velocity=v, modulation=mod_obj, snr=snr, - defaults={'id_user_add': CustomUser.objects.get(id=1)} + # defaults={'id_user_add': CustomUser.objects.get(id=1)} ) geo, _ = Geo.objects.get_or_create( @@ -131,8 +131,7 @@ def fill_data_from_df(df: pd.DataFrame, sat: Satellite): 'coords_valid': valid_point, 'location': location, 'comment': comment, - 'is_average': (comment != -1.0), - 'id_user_add': CustomUser.objects.get(id=1) + 'is_average': (comment != -1.0) } ) geo.save() @@ -150,23 +149,6 @@ def fill_data_from_df(df: pd.DataFrame, sat: Satellite): obj_item.parameters_obj.set([vch_load_obj]) geo.objitem = obj_item geo.save() - # else: - - - # obj_item, _ = ObjItem.objects.get_or_create( - # defaults={ - # 'name': source, - # 'id_user_add': CustomUser.objects.get(id=1), - # # 'id_satellite': sat - # } - # ) - # obj_item.save() - # obj_item.parameters_obj.set([vch_load_obj]) - # if geo: - # obj_item.geo_obj = geo - # # или в зависимости от вашей модели Geo, вы можете установить обратную связь там: - # # geo.objitem = obj_item - # geo.save() @@ -262,7 +244,7 @@ def get_points_from_csv(file_content): polarization=pol_obj, frequency=row['freq'], freq_range=row['f_range'], - defaults={'id_user_add': CustomUser.objects.get(id=1)} + # defaults={'id_user_add': CustomUser.objects.get(id=1)} ) geo_obj, _ = Geo.objects.get_or_create( @@ -270,7 +252,7 @@ def get_points_from_csv(file_content): coords=Point(row['lon'], row['lat'], srid=4326), defaults={ 'is_average': False, - 'id_user_add': CustomUser.objects.get(id=1), + # 'id_user_add': CustomUser.objects.get(id=1), } ) geo_obj.mirrors.set(Mirror.objects.filter(name__in=mir_lst)) @@ -282,7 +264,7 @@ def get_points_from_csv(file_content): if not existing_obj_items.exists(): obj_item = ObjItem.objects.create( name=row['obj'], - id_user_add=CustomUser.objects.get(id=1) + # id_user_add=CustomUser.objects.get(id=1) ) obj_item.parameters_obj.set([vch_load_obj]) geo_obj.objitem = obj_item diff --git a/dbapp/mainapp/views.py b/dbapp/mainapp/views.py index e2fd354..061160e 100644 --- a/dbapp/mainapp/views.py +++ b/dbapp/mainapp/views.py @@ -3,10 +3,12 @@ from django.contrib import messages from django.http import JsonResponse, HttpResponse from django.views.decorators.http import require_GET from django.contrib.admin.views.decorators import staff_member_required +from django.contrib.auth.decorators import login_required from django.utils.decorators import method_decorator from django.views import View from django.views.generic import TemplateView, FormView -from django.contrib.auth.mixins import UserPassesTestMixin +from django.contrib.auth.mixins import UserPassesTestMixin, LoginRequiredMixin +from django.contrib.auth import logout from django.db import models import pandas as pd from .utils import ( @@ -25,7 +27,7 @@ from io import BytesIO -class AddSatellitesView(View): +class AddSatellitesView(LoginRequiredMixin, View): def get(self, request): add_satellite_list() return redirect('home') @@ -38,7 +40,7 @@ class AddSatellitesView(View): # print("Файл не найден") # return redirect('home') -class AddTranspondersView(FormView): +class AddTranspondersView(LoginRequiredMixin, FormView): template_name = 'mainapp/transponders_upload.html' form_class = UploadFileForm @@ -58,11 +60,26 @@ class AddTranspondersView(FormView): messages.error(self.request, "Форма заполнена некорректно.") return super().form_invalid(form) -class HomePageView(TemplateView): - template_name = 'mainapp/actions.html' +from django.views.generic import View + +class ActionsPageView(View): + def get(self, request): + if request.user.is_authenticated: + return render(request, 'mainapp/actions.html') + else: + return render(request, 'mainapp/login_required.html') -class LoadExcelDataView(FormView): +class HomePageView(View): + def get(self, request): + if request.user.is_authenticated: + # Redirect to objitem list if authenticated + return redirect('objitem_list') + else: + return render(request, 'mainapp/login_required.html') + + +class LoadExcelDataView(LoginRequiredMixin, FormView): template_name = 'mainapp/add_data_from_excel.html' form_class = LoadExcelData @@ -94,7 +111,7 @@ from django.core.paginator import Paginator from django.db.models import Prefetch from .models import Satellite, ObjItem, Parameter, Geo -class GetLocationsView(View): +class GetLocationsView(LoginRequiredMixin, View): def get(self, request, sat_id): locations = ObjItem.objects.filter(parameters_obj__id_satellite=sat_id) if not locations: @@ -122,7 +139,7 @@ class GetLocationsView(View): "features": features }) -class LoadCsvDataView(FormView): +class LoadCsvDataView(LoginRequiredMixin, FormView): template_name = 'mainapp/add_data_from_csv.html' form_class = LoadCsvData @@ -196,17 +213,23 @@ class ShowMapView(UserPassesTestMixin, View): return render(request, 'admin/map_custom.html', context) -class ClusterTestView(View): +class ClusterTestView(LoginRequiredMixin, View): def get(self, request): objs = ObjItem.objects.filter(name__icontains="! Astra 4A 12654,040 [1,962] МГц H") coords = [] for obj in objs: - coords.append((obj.id_geo.coords[1], obj.id_geo.coords[0])) + if obj.geo_obj and obj.geo_obj.coords: + coords.append((obj.geo_obj.coords.coords[1], obj.geo_obj.coords.coords[0])) get_clusters(coords) return JsonResponse({"success": "ок"}) -class UploadVchLoadView(FormView): + +def custom_logout(request): + logout(request) + return redirect('home') + +class UploadVchLoadView(LoginRequiredMixin, FormView): template_name = 'mainapp/upload_html.html' form_class = UploadVchLoad @@ -227,7 +250,7 @@ class UploadVchLoadView(FormView): return super().form_invalid(form) -class LinkVchSigmaView(FormView): +class LinkVchSigmaView(LoginRequiredMixin, FormView): template_name = 'mainapp/link_vch.html' form_class = VchLinkForm @@ -245,7 +268,7 @@ class LinkVchSigmaView(FormView): return self.render_to_response(self.get_context_data(form=form)) -class ProcessKubsatView(FormView): +class ProcessKubsatView(LoginRequiredMixin, FormView): template_name = 'mainapp/process_kubsat.html' form_class = NewEventForm @@ -278,15 +301,16 @@ class ProcessKubsatView(FormView): messages.error(self.request, "Форма заполнена некорректно.") return super().form_invalid(form) -class ObjItemListView(View): +from django.contrib.auth.mixins import LoginRequiredMixin + +class ObjItemListView(LoginRequiredMixin, View): def get(self, request): - # Get satellites that have associated objects, sorted alphabetically satellites = Satellite.objects.filter(parameters__objitems__isnull=False).distinct().order_by('name') # Get selected satellite from query parameters selected_sat_id = request.GET.get('satellite_id') page_number = request.GET.get('page', 1) - items_per_page = request.GET.get('items_per_page', '25') # Default to 25 items per page + items_per_page = request.GET.get('items_per_page', '50') # Get filter parameters freq_min = request.GET.get('freq_min') @@ -327,7 +351,8 @@ class ObjItemListView(View): # Start with the basic filter objects = ObjItem.objects.select_related( 'id_user_add__user', - 'geo_obj' + 'geo_obj', + 'updated_by__user' ).prefetch_related( 'parameters_obj__id_satellite', 'parameters_obj__polarization', @@ -338,7 +363,8 @@ class ObjItemListView(View): # If no satellites are selected, start with all objects objects = ObjItem.objects.select_related( 'id_user_add__user', - 'geo_obj' + 'geo_obj', + 'updated_by__user' ).prefetch_related( 'parameters_obj__id_satellite', 'parameters_obj__polarization', @@ -499,9 +525,9 @@ class ObjItemListView(View): 'frequency': f"{param.frequency:.3f}" if param and param.frequency else "-", 'freq_range': f"{param.freq_range:.3f}" if param and param.freq_range else "-", 'polarization': param.polarization.name if param and param.polarization else "-", - 'bod_velocity': f"{param.bod_velocity:.3f}" if param and param.bod_velocity else "-", + 'bod_velocity': f"{param.bod_velocity:.0f}" if param and param.bod_velocity else "-", 'modulation': param.modulation.name if param and param.modulation else "-", - 'snr': f"{param.snr:.3f}" if param and param.snr else "-", + 'snr': f"{param.snr:.0f}" if param and param.snr else "-", 'geo_coords': geo_coords, 'kupsat_coords': kupsat_coords, 'valid_coords': valid_coords, @@ -522,7 +548,7 @@ class ObjItemListView(View): 'page_obj': page_obj, 'processed_objects': processed_objects, 'items_per_page': items_per_page, - 'available_items_per_page': [10, 25, 50, 100], + 'available_items_per_page': [50, 100, 500, 1000], # Filter values 'freq_min': freq_min, 'freq_max': freq_max, diff --git a/dbapp/mapsapp/migrations/0001_initial.py b/dbapp/mapsapp/migrations/0001_initial.py index 2212bbb..e32dd58 100644 --- a/dbapp/mapsapp/migrations/0001_initial.py +++ b/dbapp/mapsapp/migrations/0001_initial.py @@ -1,6 +1,8 @@ -# Generated by Django 5.2.7 on 2025-10-13 12:47 +# Generated by Django 5.2.7 on 2025-10-31 13:36 import django.db.models.deletion +import django.db.models.expressions +import django.db.models.functions.math import mainapp.models from django.db import migrations, models @@ -19,9 +21,11 @@ class Migration(migrations.Migration): fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(blank=True, max_length=30, null=True, verbose_name='Название транспондера')), - ('frequency', models.FloatField(blank=True, null=True, verbose_name='Центральная частота')), - ('frequency_range', models.FloatField(blank=True, null=True, verbose_name='Полоса частот')), - ('zone_name', models.CharField(blank=True, max_length=60, null=True, verbose_name='Название зоны')), + ('downlink', models.FloatField(blank=True, null=True, verbose_name='Downlink')), + ('frequency_range', models.FloatField(blank=True, null=True, verbose_name='Полоса')), + ('uplink', models.FloatField(blank=True, null=True, verbose_name='Uplink')), + ('zone_name', models.CharField(blank=True, max_length=255, null=True, verbose_name='Название зоны')), + ('transfer', models.GeneratedField(db_persist=True, expression=models.ExpressionWrapper(django.db.models.functions.math.Abs(django.db.models.expressions.CombinedExpression(models.F('downlink'), '-', models.F('uplink'))), output_field=models.FloatField()), null=True, output_field=models.FloatField(), verbose_name='Перенос')), ('polarization', models.ForeignKey(blank=True, default=mainapp.models.get_default_polarization, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='tran_polarizations', to='mainapp.polarization', verbose_name='Поляризация')), ('sat_id', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='tran_satellite', to='mainapp.satellite', verbose_name='Спутник')), ], diff --git a/dbapp/mapsapp/migrations/0002_remove_transponders_frequency_transponders_downlink_and_more.py b/dbapp/mapsapp/migrations/0002_remove_transponders_frequency_transponders_downlink_and_more.py deleted file mode 100644 index 4629613..0000000 --- a/dbapp/mapsapp/migrations/0002_remove_transponders_frequency_transponders_downlink_and_more.py +++ /dev/null @@ -1,34 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-27 12:20 - -import django.db.models.expressions -import django.db.models.functions.math -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mapsapp', '0001_initial'), - ] - - operations = [ - migrations.RemoveField( - model_name='transponders', - name='frequency', - ), - migrations.AddField( - model_name='transponders', - name='downlink', - field=models.FloatField(blank=True, null=True, verbose_name='Downlink'), - ), - migrations.AddField( - model_name='transponders', - name='uplink', - field=models.FloatField(blank=True, null=True, verbose_name='Uplink'), - ), - migrations.AddField( - model_name='transponders', - name='transfer', - field=models.GeneratedField(db_persist=True, expression=models.ExpressionWrapper(django.db.models.functions.math.Abs(django.db.models.expressions.CombinedExpression(models.F('downlink'), '-', models.F('uplink'))), output_field=models.FloatField()), null=True, output_field=models.FloatField(), verbose_name='Расстояние между купсатом и гео, км'), - ), - ] diff --git a/dbapp/mapsapp/migrations/0003_alter_transponders_transfer.py b/dbapp/mapsapp/migrations/0003_alter_transponders_transfer.py deleted file mode 100644 index 50a890d..0000000 --- a/dbapp/mapsapp/migrations/0003_alter_transponders_transfer.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-27 13:10 - -import django.db.models.expressions -import django.db.models.functions.math -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mapsapp', '0002_remove_transponders_frequency_transponders_downlink_and_more'), - ] - - operations = [ - migrations.AlterField( - model_name='transponders', - name='transfer', - field=models.GeneratedField(db_persist=True, expression=models.ExpressionWrapper(django.db.models.functions.math.Abs(django.db.models.expressions.CombinedExpression(models.F('downlink'), '-', models.F('uplink'))), output_field=models.FloatField()), null=True, output_field=models.FloatField(), verbose_name='Перенос'), - ), - ] diff --git a/dbapp/mapsapp/migrations/0004_alter_transponders_zone_name.py b/dbapp/mapsapp/migrations/0004_alter_transponders_zone_name.py deleted file mode 100644 index ff585f7..0000000 --- a/dbapp/mapsapp/migrations/0004_alter_transponders_zone_name.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-28 05:44 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mapsapp', '0003_alter_transponders_transfer'), - ] - - operations = [ - migrations.AlterField( - model_name='transponders', - name='zone_name', - field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Название зоны'), - ), - ] diff --git a/dbapp/mapsapp/migrations/0005_alter_transponders_frequency_range.py b/dbapp/mapsapp/migrations/0005_alter_transponders_frequency_range.py deleted file mode 100644 index 99f1ab7..0000000 --- a/dbapp/mapsapp/migrations/0005_alter_transponders_frequency_range.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-29 14:00 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mapsapp', '0004_alter_transponders_zone_name'), - ] - - operations = [ - migrations.AlterField( - model_name='transponders', - name='frequency_range', - field=models.FloatField(blank=True, null=True, verbose_name='Полоса'), - ), - ]