""" Модели заявок на источники (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', 'Отменено МКА'), ('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']), ]