230 lines
10 KiB
Python
230 lines
10 KiB
Python
"""
|
||
Модель источника сигнала (ИРИ).
|
||
"""
|
||
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 = "Источники"
|