from __future__ import annotations import time from app import db from app.lab.calc.engine import calculate_ration from app.lab.commands.write_audit import write_audit, write_calculation_run from app.lab.db_guard import retry_locked from app.lab.commands.seed_demo_component_nutrients import ( ensure_component_nutrients_if_empty, supplement_component_nutrients_if_sparse, ) from app.lab.loaders.ration_loader import load_ration, ration_to_calc_lines from app.lab.models import LabAnimalProfile, LabRecipeRation from app.lab.services.ration_calc_store import save_calc_result @retry_locked def recalculate_ration(recipe_id: str, user_id: str = "system") -> dict: snapshot = load_ration(recipe_id) if not snapshot.lines: raise ValueError("Пустой мастер — добавьте строки рациона") seeded_any = False for line in snapshot.lines: if not line.component_id: continue if ensure_component_nutrients_if_empty(line.component_id, user_id=user_id): seeded_any = True elif supplement_component_nutrients_if_sparse(line.component_id, user_id=user_id): seeded_any = True if seeded_any: db.session.commit() snapshot = load_ration(recipe_id) profile_mass_kg = None if snapshot.animal_profile_id: prof = LabAnimalProfile.query.get(snapshot.animal_profile_id) if prof is not None: profile_mass_kg = prof.mass_kg started = time.perf_counter() result = calculate_ration( snapshot.ration_type, ration_to_calc_lines(snapshot), snapshot.norms, heads_per_trip=snapshot.heads_per_trip, profile_mass_kg=profile_mass_kg, ) duration_ms = int((time.perf_counter() - started) * 1000) header = LabRecipeRation.query.filter_by(recipe_id=recipe_id).first() if header is None: header = LabRecipeRation(recipe_id=recipe_id, created_by=user_id, updated_by=user_id) db.session.add(header) save_calc_result(recipe_id, result, header) header.updated_by = user_id status = "FAILED" if result.get("errors") else "COMPLETED" write_calculation_run( recipe_id, status, {"indicators": result.get("indicators", [])}, duration_ms=duration_ms, error_message="; ".join(result.get("errors") or []) or None, ) write_audit("RECALCULATE", "lab_recipe_ration", recipe_id, user_id) db.session.commit() return result