""" Модель источника сигнала (ИРИ). """ from django.contrib.gis.db import models as gis from django.db import models class Source(models.Model): """ Модель источника сигнала. """ info = models.ForeignKey( 'mainapp.ObjectInfo', on_delete=models.SET_NULL, related_name="source_info", null=True, blank=True, verbose_name="Тип объекта", help_text="Тип объекта", ) ownership = models.ForeignKey( 'mainapp.ObjectOwnership', on_delete=models.SET_NULL, related_name="source_ownership", null=True, blank=True, verbose_name="Принадлежность объекта", help_text="Принадлежность объекта (страна, организация и т.д.)", ) note = models.TextField( null=True, blank=True, verbose_name="Примечание", help_text="Дополнительное описание объекта", ) confirm_at = models.DateTimeField( null=True, blank=True, verbose_name="Дата подтверждения", help_text="Дата и время добавления последней полученной точки ГЛ", ) last_signal_at = models.DateTimeField( null=True, blank=True, verbose_name="Последний сигнал", help_text="Дата и время последней отметки о наличии сигнала", ) coords_average = gis.PointField( srid=4326, null=True, blank=True, verbose_name="Координаты ГЛ", help_text="Усреднённые координаты, полученные от в ходе геолокации (WGS84)", ) coords_kupsat = gis.PointField( srid=4326, null=True, blank=True, verbose_name="Координаты Кубсата", help_text="Координаты, полученные от кубсата (WGS84)", ) coords_valid = gis.PointField( srid=4326, null=True, blank=True, verbose_name="Координаты оперативников", help_text="Координаты, предоставленные оперативным отделом (WGS84)", ) coords_reference = gis.PointField( srid=4326, null=True, blank=True, verbose_name="Координаты справочные", help_text="Координаты, ещё кем-то проверенные (WGS84)", ) created_at = models.DateTimeField( auto_now_add=True, verbose_name="Дата создания", help_text="Дата и время создания записи", ) created_by = models.ForeignKey( 'mainapp.CustomUser', on_delete=models.SET_NULL, related_name="source_created", null=True, blank=True, verbose_name="Создан пользователем", help_text="Пользователь, создавший запись", ) updated_at = models.DateTimeField( auto_now=True, verbose_name="Дата последнего изменения", help_text="Дата и время последнего изменения", ) updated_by = models.ForeignKey( 'mainapp.CustomUser', on_delete=models.SET_NULL, related_name="source_updated", null=True, blank=True, verbose_name="Изменен пользователем", help_text="Пользователь, последним изменивший запись", ) def update_coords_average(self, new_coord_tuple): """ Обновляет coords_average в зависимости от типа объекта (info). Логика: - Если info == "Подвижные": coords_average = последняя добавленная координата - Иначе (Стационарные и др.): coords_average = инкрементальное среднее Args: new_coord_tuple: кортеж (longitude, latitude) новой координаты """ from django.contrib.gis.geos import Point from ..utils import calculate_mean_coords # Если тип объекта "Подвижные" - просто устанавливаем последнюю координату if self.info and self.info.name == "Подвижные": self.coords_average = Point(new_coord_tuple, srid=4326) else: # Для стационарных объектов - вычисляем среднее if self.coords_average: # Есть предыдущее среднее - вычисляем новое среднее current_coord = (self.coords_average.x, self.coords_average.y) new_avg, _ = calculate_mean_coords(current_coord, new_coord_tuple) self.coords_average = Point(new_avg, srid=4326) else: # Первая координата - просто устанавливаем её self.coords_average = Point(new_coord_tuple, srid=4326) def get_last_geo_coords(self): """ Получает координаты последней добавленной точки ГЛ для этого источника. Сортировка по ID (последняя добавленная в базу). Returns: tuple: (longitude, latitude) или None если точек нет """ # Получаем последний ObjItem для этого Source (по ID) last_objitem = self.source_objitems.filter( geo_obj__coords__isnull=False ).select_related('geo_obj').order_by('-id').first() if last_objitem and last_objitem.geo_obj and last_objitem.geo_obj.coords: return (last_objitem.geo_obj.coords.x, last_objitem.geo_obj.coords.y) return None def update_confirm_at(self): """ Обновляет дату confirm_at на дату создания последней добавленной точки ГЛ. """ last_objitem = self.source_objitems.order_by('-created_at').first() if last_objitem: self.confirm_at = last_objitem.created_at def save(self, *args, **kwargs): """ Переопределенный метод save для автоматического обновления coords_average при изменении типа объекта. """ from django.contrib.gis.geos import Point # Проверяем, изменился ли тип объекта if self.pk: # Объект уже существует try: old_instance = Source.objects.get(pk=self.pk) old_info = old_instance.info new_info = self.info # Если тип изменился на "Подвижные" if new_info and new_info.name == "Подвижные" and (not old_info or old_info.name != "Подвижные"): # Устанавливаем координату последней точки last_coords = self.get_last_geo_coords() if last_coords: self.coords_average = Point(last_coords, srid=4326) # Если тип изменился с "Подвижные" на что-то другое elif old_info and old_info.name == "Подвижные" and (not new_info or new_info.name != "Подвижные"): # Пересчитываем среднюю координату по всем точкам self._recalculate_average_coords() except Source.DoesNotExist: pass super().save(*args, **kwargs) def _recalculate_average_coords(self): """ Пересчитывает среднюю координату по всем точкам источника. Используется при переключении с "Подвижные" на "Стационарные". Сортировка по ID (порядок добавления в базу), инкрементальное усреднение как в функциях импорта. """ from django.contrib.gis.geos import Point from ..utils import calculate_mean_coords # Получаем все точки для этого источника, сортируем по ID (порядок добавления) objitems = self.source_objitems.filter( geo_obj__coords__isnull=False ).select_related('geo_obj').order_by('id') if not objitems.exists(): return # Вычисляем среднюю координату инкрементально (как в функциях импорта) coords_average = None for objitem in objitems: if objitem.geo_obj and objitem.geo_obj.coords: coord = (objitem.geo_obj.coords.x, objitem.geo_obj.coords.y) if coords_average is None: # Первая точка - просто устанавливаем её coords_average = coord else: # Последующие точки - вычисляем среднее между текущим средним и новой точкой coords_average, _ = calculate_mean_coords(coords_average, coord) if coords_average: self.coords_average = Point(coords_average, srid=4326) class Meta: verbose_name = "Источник" verbose_name_plural = "Источники"