# admin.py from django.contrib import admin from .models import ( Polarization, Modulation, Standard, SigmaParMark, SigmaParameter, SourceType, Parameter, Satellite, Mirror, Geo, ObjItem, CustomUser ) from leaflet.admin import LeafletGeoAdmin from django import forms from django.contrib.auth.models import Group from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from django.contrib.auth.models import User from django.contrib.gis.db import models as gis from django.shortcuts import redirect from django.urls import reverse from django.utils import timezone from leaflet.forms.widgets import LeafletWidget from rangefilter.filters import ( DateRangeFilterBuilder, DateTimeRangeFilterBuilder, NumericRangeFilterBuilder, DateRangeQuickSelectListFilterBuilder, ) from dynamic_raw_id.admin import DynamicRawIDMixin from more_admin_filters import MultiSelectDropdownFilter, MultiSelectFilter, MultiSelectRelatedDropdownFilter from import_export.admin import ImportExportActionModelAdmin from .filters import GeoKupDistanceFilter, GeoValidDistanceFilter, UniqueToggleFilter, HasSigmaParameterFilter 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="Долгота") latitude_kupsat = forms.FloatField(required=False, label="Широта") longitude_kupsat = forms.FloatField(required=False, label="Долгота") latitude_valid = forms.FloatField(required=False, label="Широта") longitude_valid = forms.FloatField(required=False, label="Долгота") class Meta: model = Geo fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if self.instance and self.instance.coords: self.fields['latitude_geo'].initial = self.instance.coords[1] self.fields['longitude_geo'].initial = self.instance.coords[0] if self.instance and self.instance.coords_kupsat: self.fields['latitude_kupsat'].initial = self.instance.coords_kupsat[1] self.fields['longitude_kupsat'].initial = self.instance.coords_kupsat[0] if self.instance and self.instance.coords_valid: self.fields['latitude_valid'].initial = self.instance.coords_valid[1] self.fields['longitude_valid'].initial = self.instance.coords_valid[0] def save(self, commit=True): instance = super().save(commit=False) from django.contrib.gis.geos import Point lat = self.cleaned_data.get('latitude_geo') lon = self.cleaned_data.get('longitude_geo') if lat is not None and lon is not None: instance.coords = Point(lon, lat, srid=4326) lat = self.cleaned_data.get('latitude_kupsat') lon = self.cleaned_data.get('longitude_kupsat') if lat is not None and lon is not None: instance.coords_kupsat = Point(lon, lat, srid=4326) lat = self.cleaned_data.get('latitude_valid') lon = self.cleaned_data.get('longitude_valid') if lat is not None and lon is not None: instance.coords_valid = Point(lon, lat, srid=4326) if commit: instance.save() return instance class GeoInline(admin.StackedInline): model = Geo extra = 0 verbose_name = "Гео" verbose_name_plural = "Гео" form = LocationForm readonly_fields = ("distance_coords_kup", "distance_coords_valid", "distance_kup_valid") prefetch_related = ("mirrors",) autocomplete_fields = ('mirrors',) fieldsets = ( ("Основная информация", { "fields": ("mirrors", "location", "distance_coords_kup", "distance_coords_valid", "distance_kup_valid", "timestamp", "comment", "id_user_add") }), ("Координаты: геолокация", { "fields": ("longitude_geo", "latitude_geo", "coords"), }), ("Координаты: Кубсат", { "fields": ("longitude_kupsat", "latitude_kupsat", "coords_kupsat"), }), ("Координаты: Оперативный отдел", { "fields": ("longitude_valid", "latitude_valid", "coords_valid"), }), ) 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") search_fields = ("mark", ) ordering = ("timestamp",) @admin.register(Polarization) class PolarizationAdmin(admin.ModelAdmin): list_display = ("name",) search_fields = ("name",) ordering = ("name",) @admin.register(Modulation) class ModulationAdmin(admin.ModelAdmin): list_display = ("name",) search_fields = ("name",) ordering = ("name",) @admin.register(SourceType) class SourceTypeAdmin(admin.ModelAdmin): list_display = ("name",) search_fields = ("name",) ordering = ("name",) @admin.register(Standard) class StandardAdmin(admin.ModelAdmin): list_display = ("name",) search_fields = ("name",) ordering = ("name",) class SigmaParameterInline(admin.StackedInline): model = SigmaParameter extra = 0 autocomplete_fields = ['mark'] readonly_fields = ( "datetime_begin", "datetime_end", ) def has_add_permission(self, request, obj=None): return False @admin.register(Parameter) class ParameterAdmin(ImportExportActionModelAdmin, admin.ModelAdmin): list_display = ( "id_satellite", "frequency", "freq_range", "polarization", "modulation", "bod_velocity", "snr", "standard", "sigma_parameter" ) # fields = ( "id_satellite", # "frequency", # "freq_range", # "polarization", # "modulation", # "bod_velocity", # "snr", # "standard", # "id_sigma_parameter") list_display_links = ("frequency", "id_satellite", ) list_filter = ( HasSigmaParameterFilter, ("id_satellite", MultiSelectRelatedDropdownFilter), ("polarization__name", MultiSelectDropdownFilter), ("modulation", MultiSelectRelatedDropdownFilter), ("standard", MultiSelectRelatedDropdownFilter), ("frequency", NumericRangeFilterBuilder()), ("freq_range", NumericRangeFilterBuilder()), ("snr", NumericRangeFilterBuilder()), ) search_fields = ( "id_satellite", "frequency", "freq_range", "bod_velocity", "snr", "modulation__name", "polarization__name", "standard__name", ) ordering = ("frequency",) list_select_related = ("polarization", "modulation", "standard", "id_satellite",) autocomplete_fields = ('objitems',) # raw_id_fields = ("id_sigma_parameter", ) inlines = [SigmaParameterInline] # autocomplete_fields = ("id_sigma_parameter", ) def sigma_parameter(self, obj): sigma_obj = obj.sigma_parameter.all() if sigma_obj: return f"{sigma_obj[0].frequency}: {sigma_obj[0].freq_range}" return '-' sigma_parameter.short_description = "ВЧ sigma" @admin.register(SigmaParameter) class SigmaParameterAdmin(ImportExportActionModelAdmin, admin.ModelAdmin): list_display = ( "id_satellite", # "status", "frequency", "transfer_frequency", "freq_range", # "power", "polarization", "modulation", "bod_velocity", "snr", # "standard", "parameter", # "packets", "datetime_begin", "datetime_end", ) readonly_fields = ( "datetime_begin", "datetime_end", "transfer_frequency" ) list_display_links = ("id_satellite",) list_filter = ( ("id_satellite__name", MultiSelectDropdownFilter), ("modulation__name", MultiSelectDropdownFilter), ("standard__name", MultiSelectDropdownFilter), ("frequency", NumericRangeFilterBuilder()), ("freq_range", NumericRangeFilterBuilder()), ("snr", NumericRangeFilterBuilder()), ) search_fields = ( "id_satellite__name", "frequency", "freq_range", "bod_velocity", "snr", "modulation__name", "standard__name", ) autocomplete_fields = ('mark',) ordering = ("frequency",) list_select_related = ("modulation", "standard", "id_satellite", "parameter") prefetch_related = ("mark",) @admin.register(Satellite) class SatelliteAdmin(admin.ModelAdmin): list_display = ("name",) search_fields = ("name",) ordering = ("name",) @admin.register(Mirror) class MirrorAdmin(ImportExportActionModelAdmin, admin.ModelAdmin): list_display = ("name",) search_fields = ("name",) ordering = ("name",) @admin.register(Geo) class GeoAdmin(ImportExportActionModelAdmin, LeafletGeoAdmin): form = LocationForm readonly_fields = ("distance_coords_kup", "distance_coords_valid", "distance_kup_valid") fieldsets = ( ("Основная информация", { "fields": ("mirrors", "location", "distance_coords_kup", "distance_coords_valid", "distance_kup_valid", "timestamp", "comment", "id_user_add") }), ("Координаты: геолокация", { "fields": ("longitude_geo", "latitude_geo", "coords"), }), ("Координаты: Кубсат", { "fields": ("longitude_kupsat", "latitude_kupsat", "coords_kupsat"), }), ("Координаты: Оперативный отдел", { "fields": ("longitude_valid", "latitude_valid", "coords_valid"), }), ) list_display = ( "formatted_timestamp", "location", "mirrors_names", "geo_coords", "kupsat_coords", "valid_coords", "is_average", ) autocomplete_fields = ('mirrors',) list_display_links = ("formatted_timestamp",) list_filter = ( ("mirrors", MultiSelectRelatedDropdownFilter), "is_average", ("location", MultiSelectDropdownFilter), ("timestamp", DateRangeQuickSelectListFilterBuilder()), ("id_user_add", MultiSelectRelatedDropdownFilter), ) search_fields = ( "mirrors__name", "location", "coords", "coords_kupsat", "coords_valid" ) list_select_related = ("id_user_add", ) prefetch_related = ("mirrors", ) settings_overrides = { 'DEFAULT_CENTER': (55.7558, 37.6173), 'DEFAULT_ZOOM': 12, } def mirrors_names(self, obj): return ", ".join(m.name for m in obj.mirrors.all()) mirrors_names.short_description = "Зеркала" def formatted_timestamp(self, obj): if not obj.timestamp: return "" local_time = timezone.localtime(obj.timestamp) return local_time.strftime("%d.%m.%Y %H:%M:%S") formatted_timestamp.short_description = "Дата и время" formatted_timestamp.admin_order_field = "timestamp" def geo_coords(self, obj): longitude = obj.coords.coords[0] latitude = obj.coords.coords[1] lon = f"{longitude}E" if longitude > 0 else f"{abs(longitude)}W" lat = f"{latitude}N" if latitude > 0 else f"{abs(latitude)}S" return f"{lat} {lon}" geo_coords.short_description = "Координаты геолокации" def kupsat_coords(self, obj): if obj.coords_kupsat is None: return "-" longitude = obj.coords_kupsat.coords[0] latitude = obj.coords_kupsat.coords[1] lon = f"{longitude}E" if longitude > 0 else f"{abs(longitude)}W" lat = f"{latitude}N" if latitude > 0 else f"{abs(latitude)}S" return f"{lat} {lon}" kupsat_coords.short_description = "Координаты Кубсата" def valid_coords(self, obj): if obj.coords_valid is None: return "-" longitude = obj.coords_valid.coords[0] latitude = obj.coords_valid.coords[1] lon = f"{longitude}E" if longitude > 0 else f"{abs(longitude)}W" lat = f"{latitude}N" if latitude > 0 else f"{abs(latitude)}S" return f"{lat} {lon}" valid_coords.short_description = "Координаты оперативного отдела" def show_on_map(modeladmin, request, queryset): # Получаем список ID выбранных объектов selected_ids = queryset.values_list('id', flat=True) # Формируем строку вида "1,2,3" ids_str = ','.join(str(pk) for pk in selected_ids) # Перенаправляем на ваш кастомный view с картой return redirect(reverse('admin_show_map') + f'?ids={ids_str}') show_on_map.short_description = "Показать выбранные на карте" class ParameterObjItemInline(admin.StackedInline): model = ObjItem.parameters_obj.through extra = 0 max_num = 1 verbose_name = "ВЧ загрузка" verbose_name_plural = "ВЧ загрузки" @admin.register(ObjItem) class ObjectAdmin(admin.ModelAdmin): list_display = ( "name", "sat_name", "freq", "freq_range", "pol", "bod_velocity", "modulation", "snr", "geo_coords", "kupsat_coords", "valid_coords", "distance_geo_kup", "distance_geo_valid", "distance_kup_valid", "created_at", "updated_at", ) list_display_links = ("name",) list_filter = ( UniqueToggleFilter, ("parameters_obj__id_satellite", MultiSelectRelatedDropdownFilter), ("parameters_obj__frequency", NumericRangeFilterBuilder()), ("parameters_obj__freq_range", NumericRangeFilterBuilder()), ("parameters_obj__snr", NumericRangeFilterBuilder()), ("parameters_obj__modulation", MultiSelectRelatedDropdownFilter), ("parameters_obj__polarization", MultiSelectRelatedDropdownFilter), GeoKupDistanceFilter, GeoValidDistanceFilter ) search_fields = ( "name", "geo_obj__coords", "parameters_obj__frequency", ) 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', '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: return param.id_satellite.name return "-" sat_name.short_description = "Спутник" sat_name.admin_order_field = "parameters_obj__id_satellite__name" def freq(self, obj): # param = obj.parameters_obj.first() param = next(iter(obj.parameters_obj.all()), None) if param: return param.frequency return "-" freq.short_description = "Частота, МГц" freq.admin_order_field = "parameters_obj__frequency" def distance_geo_kup(self, obj): geo = obj.geo_obj if not geo or geo.distance_coords_kup is None: return "-" return round(geo.distance_coords_kup, 3) distance_geo_kup.short_description = "Гео-куб, км" def distance_geo_valid(self, obj): geo = obj.geo_obj if not geo or geo.distance_coords_valid is None: return "-" return round(geo.distance_coords_valid, 3) distance_geo_valid.short_description = "Гео-опер, км" def distance_kup_valid(self, obj): geo = obj.geo_obj if not geo or geo.distance_kup_valid is None: return "-" return round(geo.distance_kup_valid, 3) distance_kup_valid.short_description = "Куб-опер, км" def pol(self, obj): # Get the first parameter associated with this objitem to display polarization param = next(iter(obj.parameters_obj.all()), None) if param and param.polarization: return param.polarization.name return "-" pol.short_description = "Поляризация" def freq_range(self, obj): # Get the first parameter associated with this objitem to display freq_range param = next(iter(obj.parameters_obj.all()), None) if param: return param.freq_range return "-" freq_range.short_description = "Полоса, МГц" freq_range.admin_order_field = "parameters_obj__freq_range" def bod_velocity(self, obj): # Get the first parameter associated with this objitem to display bod_velocity param = next(iter(obj.parameters_obj.all()), None) if param: return param.bod_velocity return "-" bod_velocity.short_description = "Сим. v, БОД" def modulation(self, obj): # Get the first parameter associated with this objitem to display modulation param = next(iter(obj.parameters_obj.all()), None) if param and param.modulation: return param.modulation.name return "-" modulation.short_description = "Модуляция" def snr(self, obj): # Get the first parameter associated with this objitem to display snr param = next(iter(obj.parameters_obj.all()), None) if param: return param.snr return "-" snr.short_description = "ОСШ" def geo_coords(self, obj): geo = obj.geo_obj if not geo or not geo.coords: return "-" longitude = geo.coords.coords[0] latitude = geo.coords.coords[1] lon = f"{longitude}E" if longitude > 0 else f"{abs(longitude)}W" lat = f"{latitude}N" if latitude > 0 else f"{abs(latitude)}S" return f"{lat} {lon}" geo_coords.short_description = "Координаты геолокации" geo_coords.admin_order_field = "geo_obj__coords" def kupsat_coords(self, obj): geo = obj.geo_obj if not geo or not geo.coords_kupsat: return "-" longitude = geo.coords_kupsat.coords[0] latitude = geo.coords_kupsat.coords[1] lon = f"{longitude}E" if longitude > 0 else f"{abs(longitude)}W" lat = f"{latitude}N" if latitude > 0 else f"{abs(latitude)}S" return f"{lat} {lon}" kupsat_coords.short_description = "Координаты Кубсата" def valid_coords(self, obj): geo = obj.geo_obj if not geo or not geo.coords_valid: return "-" longitude = geo.coords_valid.coords[0] latitude = geo.coords_valid.coords[1] lon = f"{longitude}E" if longitude > 0 else f"{abs(longitude)}W" lat = f"{latitude}N" if latitude > 0 else f"{abs(latitude)}S" return f"{lat} {lon}" valid_coords.short_description = "Координаты оперативного отдела"