"""Удаление legacy/fp_* профилей стада (одноразовая ops-команда).""" from __future__ import annotations import logging import re from dataclasses import dataclass, field from app import db from app.lab.models import LabAnimalProfile, LabProfileNorm, LabRecipeRation _log = logging.getLogger(__name__) _LEGACY_KEY = re.compile(r"^(dairy|beef)-\d+$", re.IGNORECASE) _FP_KEY = re.compile(r"^fp_", re.IGNORECASE) @dataclass class CleanupReport: dry_run: bool = True profiles_to_delete: list[str] = field(default_factory=list) recipe_ids_cleared: list[str] = field(default_factory=list) norms_deleted: int = 0 profiles_deleted: int = 0 errors: list[str] = field(default_factory=list) def _should_delete(profile_key: str | None) -> bool: key = (profile_key or "").strip() if not key: return False if key.startswith("lab_math_"): return False if _LEGACY_KEY.match(key): return True if _FP_KEY.match(key): return True return False def cleanup_legacy_profiles(*, dry_run: bool = True, user_id: str = "system") -> CleanupReport: report = CleanupReport(dry_run=dry_run) profiles = LabAnimalProfile.query.filter_by(is_deleted=False).all() to_delete = [p for p in profiles if _should_delete(p.profile_key)] for profile in to_delete: report.profiles_to_delete.append(profile.profile_key or profile.id) norms_count = LabProfileNorm.query.filter_by(profile_id=profile.id).count() report.norms_deleted += norms_count rations = LabRecipeRation.query.filter_by(animal_profile_id=profile.id, is_deleted=False).all() for header in rations: if header.recipe_id not in report.recipe_ids_cleared: report.recipe_ids_cleared.append(header.recipe_id) if dry_run: continue LabProfileNorm.query.filter_by(profile_id=profile.id).delete(synchronize_session=False) for header in rations: header.animal_profile_id = None header.updated_by = user_id if hasattr(profile, "soft_delete"): profile.soft_delete(user_id) else: db.session.delete(profile) report.profiles_deleted += 1 if not dry_run: db.session.commit() _log.info( "cleanup_legacy_profiles user=%s deleted=%s rations_cleared=%s", user_id, report.profiles_deleted, len(report.recipe_ids_cleared), ) else: _log.info( "cleanup_legacy_profiles dry_run profiles=%s rations=%s", len(report.profiles_to_delete), len(report.recipe_ids_cleared), ) return report