205 lines
8.5 KiB
Python
205 lines
8.5 KiB
Python
"""
|
||
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
|