Переосмыслил отметки по ВЧ загрузке. Улучшил статистику

This commit is contained in:
2025-12-10 17:43:38 +03:00
parent 4949a03e68
commit 41e8dc30fd
16 changed files with 1834 additions and 980 deletions

View File

@@ -0,0 +1 @@
# Management commands package

View File

@@ -0,0 +1 @@
# Commands package

View File

@@ -0,0 +1,204 @@
"""
Management command для генерации тестовых отметок сигналов.
Использование:
python manage.py generate_test_marks --satellite_id=1 --days=90 --marks_per_day=5
Параметры:
--satellite_id: ID спутника (обязательный)
--days: Количество дней для генерации (по умолчанию 90)
--marks_per_day: Количество отметок в день (по умолчанию 3)
--clear: Удалить существующие отметки перед генерацией
"""
import random
from datetime import timedelta
from django.core.management.base import BaseCommand, CommandError
from django.utils import timezone
from mainapp.models import TechAnalyze, ObjectMark, Satellite, CustomUser
class Command(BaseCommand):
help = 'Генерирует тестовые отметки сигналов для теханализов выбранного спутника'
def add_arguments(self, parser):
parser.add_argument(
'--satellite_id',
type=int,
required=True,
help='ID спутника для генерации отметок'
)
parser.add_argument(
'--days',
type=int,
default=90,
help='Количество дней для генерации (по умолчанию 90)'
)
parser.add_argument(
'--marks_per_day',
type=int,
default=3,
help='Среднее количество отметок в день на теханализ (по умолчанию 3)'
)
parser.add_argument(
'--clear',
action='store_true',
help='Удалить существующие отметки перед генерацией'
)
def handle(self, *args, **options):
satellite_id = options['satellite_id']
days = options['days']
marks_per_day = options['marks_per_day']
clear = options['clear']
# Проверяем существование спутника
try:
satellite = Satellite.objects.get(id=satellite_id)
except Satellite.DoesNotExist:
raise CommandError(f'Спутник с ID {satellite_id} не найден')
# Получаем теханализы для спутника
tech_analyzes = TechAnalyze.objects.filter(satellite=satellite)
ta_count = tech_analyzes.count()
if ta_count == 0:
raise CommandError(f'Нет теханализов для спутника "{satellite.name}"')
self.stdout.write(f'Спутник: {satellite.name}')
self.stdout.write(f'Теханализов: {ta_count}')
self.stdout.write(f'Период: {days} дней')
self.stdout.write(f'Отметок в день: ~{marks_per_day}')
# Удаляем существующие отметки если указан флаг
if clear:
deleted_count = ObjectMark.objects.filter(
tech_analyze__satellite=satellite
).delete()[0]
self.stdout.write(
self.style.WARNING(f'Удалено существующих отметок: {deleted_count}')
)
# Получаем или создаём тестового пользователя
test_users = self._get_or_create_test_users()
# Генерируем отметки
now = timezone.now()
start_date = now - timedelta(days=days)
total_marks = 0
marks_to_create = []
for ta in tech_analyzes:
# Для каждого теханализа генерируем отметки
current_date = start_date
# Начальное состояние сигнала (случайное)
signal_present = random.choice([True, False])
while current_date < now:
# Случайное количество отметок в этот день (от 0 до marks_per_day * 2)
day_marks = random.randint(0, marks_per_day * 2)
for _ in range(day_marks):
# Случайное время в течение дня
random_hours = random.randint(0, 23)
random_minutes = random.randint(0, 59)
mark_time = current_date.replace(
hour=random_hours,
minute=random_minutes,
second=random.randint(0, 59)
)
# Пропускаем если время в будущем
if mark_time > now:
continue
# С вероятностью 70% сигнал остаётся в том же состоянии
# С вероятностью 30% меняется
if random.random() > 0.7:
signal_present = not signal_present
marks_to_create.append(ObjectMark(
tech_analyze=ta,
mark=signal_present,
created_by=random.choice(test_users),
))
total_marks += 1
current_date += timedelta(days=1)
# Bulk create для производительности
self.stdout.write(f'Создание {total_marks} отметок...')
# Создаём партиями по 1000
batch_size = 1000
for i in range(0, len(marks_to_create), batch_size):
batch = marks_to_create[i:i + batch_size]
ObjectMark.objects.bulk_create(batch)
self.stdout.write(f' Создано: {min(i + batch_size, len(marks_to_create))}/{total_marks}')
# Обновляем timestamp для созданных отметок (bulk_create не вызывает auto_now_add корректно)
self.stdout.write('Обновление временных меток...')
# Получаем созданные отметки и обновляем их timestamp
created_marks = ObjectMark.objects.filter(
tech_analyze__satellite=satellite
).order_by('id')
# Распределяем временные метки
current_date = start_date
mark_index = 0
for ta in tech_analyzes:
ta_marks = list(created_marks.filter(tech_analyze=ta).order_by('id'))
if not ta_marks:
continue
# Распределяем отметки по времени
time_step = timedelta(days=days) / len(ta_marks) if ta_marks else timedelta(hours=1)
for i, mark in enumerate(ta_marks):
mark_time = start_date + (time_step * i)
# Добавляем случайное смещение
mark_time += timedelta(
hours=random.randint(0, 23),
minutes=random.randint(0, 59)
)
if mark_time > now:
mark_time = now - timedelta(minutes=random.randint(1, 60))
ObjectMark.objects.filter(id=mark.id).update(timestamp=mark_time)
self.stdout.write(
self.style.SUCCESS(
f'Успешно создано {total_marks} отметок для {ta_count} теханализов'
)
)
def _get_or_create_test_users(self):
"""Получает или создаёт тестовых пользователей для отметок."""
from django.contrib.auth.models import User
test_usernames = ['operator1', 'operator2', 'operator3', 'analyst1', 'analyst2']
custom_users = []
for username in test_usernames:
user, created = User.objects.get_or_create(
username=username,
defaults={
'first_name': username.capitalize(),
'last_name': 'Тестовый',
'is_active': True,
}
)
custom_user, _ = CustomUser.objects.get_or_create(
user=user,
defaults={'role': 'user'}
)
custom_users.append(custom_user)
return custom_users