304 lines
10 KiB
Python
304 lines
10 KiB
Python
"""
|
||
Модели заявок на источники (SourceRequest, SourceRequestStatusHistory).
|
||
"""
|
||
from django.contrib.gis.db import models as gis
|
||
from django.db import models
|
||
|
||
|
||
class SourceRequest(models.Model):
|
||
"""
|
||
Модель заявки на источник.
|
||
|
||
Хранит информацию о заявках на обработку источников с различными статусами.
|
||
"""
|
||
|
||
STATUS_CHOICES = [
|
||
('planned', 'Запланировано'),
|
||
('canceled_gso', 'Отменено ГСО'),
|
||
('canceled_kub', 'Отменено МКА'),
|
||
('error_gso', 'Ошибка ГСО'),
|
||
('error_kub', 'Ошибка МКА'),
|
||
('wait_exec', 'Ожидают проведения'),
|
||
('suggested', 'Предложено'),
|
||
('gso_fault', 'Не проведены по вине ГСО'),
|
||
('conducted', 'Проведён'),
|
||
('successful', 'Успешно'),
|
||
('no_correlation', 'Нет корреляции'),
|
||
('no_signal', 'Нет сигнала в спектре'),
|
||
('unsuccessful', 'Неуспешно'),
|
||
('downloading', 'Скачивание'),
|
||
('processing', 'Обработка'),
|
||
('result_received', 'Результат получен'),
|
||
]
|
||
|
||
PRIORITY_CHOICES = [
|
||
('low', 'Низкий'),
|
||
('medium', 'Средний'),
|
||
('high', 'Высокий'),
|
||
]
|
||
|
||
# Связь с источником (опционально для заявок без привязки)
|
||
source = models.ForeignKey(
|
||
'mainapp.Source',
|
||
on_delete=models.CASCADE,
|
||
related_name='source_requests',
|
||
verbose_name='Источник',
|
||
null=True,
|
||
blank=True,
|
||
help_text='Связанный источник',
|
||
)
|
||
|
||
# Связь со спутником
|
||
satellite = models.ForeignKey(
|
||
'mainapp.Satellite',
|
||
on_delete=models.SET_NULL,
|
||
related_name='satellite_requests',
|
||
verbose_name='Спутник',
|
||
null=True,
|
||
blank=True,
|
||
help_text='Связанный спутник',
|
||
)
|
||
|
||
# Основные поля
|
||
status = models.CharField(
|
||
max_length=20,
|
||
choices=STATUS_CHOICES,
|
||
default='planned',
|
||
verbose_name='Статус',
|
||
db_index=True,
|
||
help_text='Текущий статус заявки',
|
||
)
|
||
priority = models.CharField(
|
||
max_length=10,
|
||
choices=PRIORITY_CHOICES,
|
||
default='medium',
|
||
verbose_name='Приоритет',
|
||
db_index=True,
|
||
help_text='Приоритет заявки',
|
||
)
|
||
|
||
# Даты
|
||
planned_at = models.DateTimeField(
|
||
null=True,
|
||
blank=True,
|
||
verbose_name='Дата и время планирования',
|
||
help_text='Запланированная дата и время',
|
||
)
|
||
request_date = models.DateField(
|
||
null=True,
|
||
blank=True,
|
||
verbose_name='Дата заявки',
|
||
help_text='Дата подачи заявки',
|
||
)
|
||
card_date = models.DateField(
|
||
null=True,
|
||
blank=True,
|
||
verbose_name='Дата формирования карточки',
|
||
help_text='Дата формирования карточки',
|
||
)
|
||
status_updated_at = models.DateTimeField(
|
||
auto_now=True,
|
||
verbose_name='Дата обновления статуса',
|
||
help_text='Дата и время последнего обновления статуса',
|
||
)
|
||
|
||
# Частоты и перенос
|
||
downlink = models.FloatField(
|
||
null=True,
|
||
blank=True,
|
||
verbose_name='Частота Downlink, МГц',
|
||
help_text='Частота downlink в МГц',
|
||
)
|
||
uplink = models.FloatField(
|
||
null=True,
|
||
blank=True,
|
||
verbose_name='Частота Uplink, МГц',
|
||
help_text='Частота uplink в МГц',
|
||
)
|
||
transfer = models.FloatField(
|
||
null=True,
|
||
blank=True,
|
||
verbose_name='Перенос, МГц',
|
||
help_text='Перенос по частоте в МГц',
|
||
)
|
||
|
||
# Результаты
|
||
gso_success = models.BooleanField(
|
||
null=True,
|
||
blank=True,
|
||
verbose_name='ГСО успешно?',
|
||
help_text='Успешность ГСО',
|
||
)
|
||
kubsat_success = models.BooleanField(
|
||
null=True,
|
||
blank=True,
|
||
verbose_name='Кубсат успешно?',
|
||
help_text='Успешность Кубсат',
|
||
)
|
||
|
||
# Район
|
||
region = models.CharField(
|
||
max_length=255,
|
||
null=True,
|
||
blank=True,
|
||
verbose_name='Район',
|
||
help_text='Район/местоположение',
|
||
)
|
||
|
||
# Комментарий
|
||
comment = models.TextField(
|
||
null=True,
|
||
blank=True,
|
||
verbose_name='Комментарий',
|
||
help_text='Дополнительные комментарии к заявке',
|
||
)
|
||
|
||
# Координаты ГСО (усреднённые по выбранным точкам)
|
||
coords = gis.PointField(
|
||
srid=4326,
|
||
null=True,
|
||
blank=True,
|
||
verbose_name='Координаты ГСО',
|
||
help_text='Координаты ГСО (WGS84)',
|
||
)
|
||
|
||
# Координаты источника
|
||
coords_source = gis.PointField(
|
||
srid=4326,
|
||
null=True,
|
||
blank=True,
|
||
verbose_name='Координаты источника',
|
||
help_text='Координаты источника (WGS84)',
|
||
)
|
||
|
||
# Координаты объекта
|
||
coords_object = gis.PointField(
|
||
srid=4326,
|
||
null=True,
|
||
blank=True,
|
||
verbose_name='Координаты объекта',
|
||
help_text='Координаты объекта (WGS84)',
|
||
)
|
||
|
||
# Количество точек, использованных для расчёта координат
|
||
points_count = models.PositiveIntegerField(
|
||
default=0,
|
||
verbose_name='Количество точек',
|
||
help_text='Количество точек ГЛ, использованных для расчёта координат',
|
||
)
|
||
|
||
# Метаданные
|
||
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_requests_created',
|
||
null=True,
|
||
blank=True,
|
||
verbose_name='Создан пользователем',
|
||
help_text='Пользователь, создавший запись',
|
||
)
|
||
updated_by = models.ForeignKey(
|
||
'mainapp.CustomUser',
|
||
on_delete=models.SET_NULL,
|
||
related_name='source_requests_updated',
|
||
null=True,
|
||
blank=True,
|
||
verbose_name='Изменен пользователем',
|
||
help_text='Пользователь, последним изменивший запись',
|
||
)
|
||
|
||
def __str__(self):
|
||
return f"Заявка #{self.pk} - {self.source_id} ({self.get_status_display()})"
|
||
|
||
def save(self, *args, **kwargs):
|
||
# Определяем, изменился ли статус
|
||
old_status = None
|
||
if self.pk:
|
||
try:
|
||
old_instance = SourceRequest.objects.get(pk=self.pk)
|
||
old_status = old_instance.status
|
||
except SourceRequest.DoesNotExist:
|
||
pass
|
||
|
||
super().save(*args, **kwargs)
|
||
|
||
# Если статус изменился, создаем запись в истории
|
||
if old_status is not None and old_status != self.status:
|
||
SourceRequestStatusHistory.objects.create(
|
||
source_request=self,
|
||
old_status=old_status,
|
||
new_status=self.status,
|
||
changed_by=self.updated_by,
|
||
)
|
||
|
||
class Meta:
|
||
verbose_name = 'Заявка на источник'
|
||
verbose_name_plural = 'Заявки на источники'
|
||
ordering = ['-created_at']
|
||
indexes = [
|
||
models.Index(fields=['-created_at']),
|
||
models.Index(fields=['status']),
|
||
models.Index(fields=['priority']),
|
||
models.Index(fields=['source', '-created_at']),
|
||
]
|
||
|
||
|
||
class SourceRequestStatusHistory(models.Model):
|
||
"""
|
||
Модель истории изменений статусов заявок.
|
||
|
||
Хранит полную хронологию изменений статусов заявок.
|
||
"""
|
||
|
||
source_request = models.ForeignKey(
|
||
SourceRequest,
|
||
on_delete=models.CASCADE,
|
||
related_name='status_history',
|
||
verbose_name='Заявка',
|
||
help_text='Связанная заявка',
|
||
)
|
||
old_status = models.CharField(
|
||
max_length=20,
|
||
choices=SourceRequest.STATUS_CHOICES,
|
||
verbose_name='Старый статус',
|
||
help_text='Статус до изменения',
|
||
)
|
||
new_status = models.CharField(
|
||
max_length=20,
|
||
choices=SourceRequest.STATUS_CHOICES,
|
||
verbose_name='Новый статус',
|
||
help_text='Статус после изменения',
|
||
)
|
||
changed_at = models.DateTimeField(
|
||
auto_now_add=True,
|
||
verbose_name='Дата изменения',
|
||
db_index=True,
|
||
help_text='Дата и время изменения статуса',
|
||
)
|
||
changed_by = models.ForeignKey(
|
||
'mainapp.CustomUser',
|
||
on_delete=models.SET_NULL,
|
||
related_name='status_changes',
|
||
null=True,
|
||
blank=True,
|
||
verbose_name='Изменен пользователем',
|
||
help_text='Пользователь, изменивший статус',
|
||
)
|
||
|
||
def __str__(self):
|
||
return f"{self.source_request_id}: {self.get_old_status_display()} → {self.get_new_status_display()}"
|
||
|
||
class Meta:
|
||
verbose_name = 'История статуса заявки'
|
||
verbose_name_plural = 'История статусов заявок'
|
||
ordering = ['-changed_at']
|
||
indexes = [
|
||
models.Index(fields=['-changed_at']),
|
||
models.Index(fields=['source_request', '-changed_at']),
|
||
]
|