Files
2026-hypn0-site/hypn0/hypn0_site/management/commands/rename_generations.py
2026-09-27 19:29:16 +03:00

165 lines
7.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import re
from typing import Optional
from django.core.management.base import BaseCommand
from django.db import transaction
from hypn0_site.models import TbHypn0Item
from hypn0_site.services.naming import generate_hypno_title
class Command(BaseCommand):
"""
Массовое переименование названий генераций (TbHypn0Item.s_title) на английский язык.
АРХИТЕКТУРНОЕ НАЗНАЧЕНИЕ:
=========================
1. Локализация каталога и прод-базы:
Переводит существующие русскоязычные названия картин в базе данных
на сочные англоязычные гипнотические тайтлы в стиле Гипножабы.
2. Детерминированность vs Случайность:
По умолчанию использует детерминированный сид на базе ID генерации (item.id + seed_offset),
что обеспечивает стабильность результатов при повторных запусках (идемпотентность).
Флаг --random позволяет генерировать полностью случайные тайтлы.
3. Безопасность и оптимизация:
- Пакетное обновление через bulk_update порциями (--batch-size).
- Режим предварительного просмотра (--dry-run).
- Фильтрация по умолчанию только кириллических / пустых названий (или всех с флагом --all).
"""
help = "Массовое переименование названий генераций на английский язык"
def add_arguments(self, parser):
parser.add_argument(
"--dry-run",
action="store_true",
help="Тестовый прогон: показать изменения без сохранения в базу данных",
)
parser.add_argument(
"--all",
action="store_true",
help="Переименовать абсолютно все генерации (по умолчанию только с кириллицей или пустые)",
)
parser.add_argument(
"--only-cyrillic",
action="store_true",
help="Переименовывать только записи с кириллическими символами в названии (включено по умолчанию)",
)
parser.add_argument(
"--batch-size",
type=int,
default=500,
help="Размер пакета для bulk_update (по умолчанию: 500)",
)
parser.add_argument(
"--limit",
type=int,
default=None,
help="Ограничить количество обрабатываемых записей",
)
parser.add_argument(
"--seed-offset",
type=int,
default=0,
help="Смещение для детерминированного генератора сидов (по умолчанию: 0)",
)
parser.add_argument(
"--random",
action="store_true",
help="Использовать недетерминированные случайные сиды вместо ID записи",
)
def handle(self, *args, **options):
dry_run: bool = options["dry_run"]
rename_all: bool = options["all"]
batch_size: int = options["batch_size"]
limit: Optional[int] = options["limit"]
seed_offset: int = options["seed_offset"]
use_random: bool = options["random"]
self.stdout.write(
self.style.NOTICE(
f"Старт процесса переименования генераций "
f"(dry_run={dry_run}, rename_all={rename_all}, batch_size={batch_size}, random={use_random})..."
)
)
cyrillic_pattern = re.compile(r"[а-яА-ЯёЁ]")
qs = TbHypn0Item.objects.all().only("id", "s_title").order_by("id")
total_in_db = qs.count()
if total_in_db == 0:
self.stdout.write(self.style.WARNING("В базе данных нет генераций для обработки."))
return
to_update = []
renamed_count = 0
preview_samples = []
for item in qs.iterator(chunk_size=batch_size):
old_title = item.s_title or ""
# Проверяем критерий необходимости переименования
needs_rename = False
if rename_all:
needs_rename = True
else:
# По умолчанию: кириллица или пустой заголовок
if not old_title.strip() or cyrillic_pattern.search(old_title):
needs_rename = True
if not needs_rename:
continue
# Генерация нового англоязычного тайтла
seed = None if use_random else (item.id + seed_offset)
new_title = generate_hypno_title(seed=seed)
if len(preview_samples) < 15:
preview_samples.append((item.id, old_title, new_title))
item.s_title = new_title
to_update.append(item)
renamed_count += 1
if limit is not None and renamed_count >= limit:
break
# Пакетное сохранение порциями
if not dry_run and len(to_update) >= batch_size:
with transaction.atomic():
TbHypn0Item.objects.bulk_update(to_update, fields=["s_title"])
to_update.clear()
# Сохранение остатка
if not dry_run and to_update:
with transaction.atomic():
TbHypn0Item.objects.bulk_update(to_update, fields=["s_title"])
to_update.clear()
# Вывод примеров переименования
if preview_samples:
self.stdout.write("\nПримеры переименований (первые до 15 записей):")
self.stdout.write("-" * 70)
for item_id, old_t, new_t in preview_samples:
self.stdout.write(f" [ID {item_id:5d}] «{old_t}» -> «{new_t}»")
self.stdout.write("-" * 70)
self.stdout.write(f"\nВсего записей в базе данных: {total_in_db}")
self.stdout.write(f"Отобрано для переименования: {renamed_count}")
if dry_run:
self.stdout.write(
self.style.WARNING(
f"\n[DRY RUN] Проверено {renamed_count} записей. Изменения НЕ сохранены в БД. "
"Запустите без флага --dry-run для применения."
)
)
else:
self.stdout.write(
self.style.SUCCESS(f"\nУспешно переименовано {renamed_count} генераций в базе данных!")
)