@@ -0,0 +1,125 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from app.modules.zootech.lab.calc.nutrients import norm_diff
|
||||
from app.modules.zootech.lab.indicators import label_to_indicator_key
|
||||
from app.modules.zootech.lab.dto.ration import RationLineSnapshot, RationSnapshot
|
||||
from app.modules.zootech.lab.models import LabAnimalProfile, LabRationLine, LabRecipeRation
|
||||
from app.modules.zootech.lab.services.component_nutrients import nutrients_calc_dict
|
||||
from app.modules.zootech.lab.services.profile_norms import resolve_norms_for_profile
|
||||
from app.modules.zootech.lab.services.ration_calc_store import (
|
||||
load_compound_results,
|
||||
load_params,
|
||||
load_ration_results,
|
||||
)
|
||||
from app.modules.zootech.wesp_bridge_models import Component, Recipe
|
||||
|
||||
|
||||
def _refresh_indicator_norms(
|
||||
indicators: list[dict],
|
||||
norms: dict[str, dict[str, float | None]],
|
||||
) -> list[dict]:
|
||||
"""min/max/diff в сохранённом расчёте — по текущему профилю норм."""
|
||||
if not indicators or not norms:
|
||||
return indicators
|
||||
out: list[dict] = []
|
||||
for row in indicators:
|
||||
item = dict(row)
|
||||
key = item.get("key") or label_to_indicator_key(str(item.get("label") or ""))
|
||||
if key and key in norms:
|
||||
bounds = norms[key]
|
||||
min_v = bounds.get("min")
|
||||
max_v = bounds.get("max")
|
||||
item["min"] = min_v
|
||||
item["max"] = max_v
|
||||
item["diff"] = norm_diff(item.get("content"), min_v, max_v)
|
||||
out.append(item)
|
||||
return out
|
||||
|
||||
|
||||
def load_ration(recipe_id: str) -> RationSnapshot:
|
||||
recipe = Recipe.query.filter_by(id=recipe_id, is_deleted=False).first()
|
||||
if recipe is None:
|
||||
raise LookupError("Рецепт не найден")
|
||||
header = LabRecipeRation.query.filter_by(recipe_id=recipe_id, is_deleted=False).first()
|
||||
ration_type = recipe.ration_type or "BEEF"
|
||||
norms: dict = {}
|
||||
params: dict = {}
|
||||
ration_results: dict = {}
|
||||
compound_results: dict = {}
|
||||
calculated_at = None
|
||||
animal_profile_id = None
|
||||
norms_profile_key = None
|
||||
if header:
|
||||
animal_profile_id = header.animal_profile_id
|
||||
params = load_params(recipe_id, header)
|
||||
ration_results = load_ration_results(recipe_id, header)
|
||||
compound_results = load_compound_results(recipe_id)
|
||||
calculated_at = header.calculated_at.isoformat() if header.calculated_at else None
|
||||
if header.animal_profile_id:
|
||||
profile = LabAnimalProfile.query.filter_by(
|
||||
id=header.animal_profile_id, is_deleted=False
|
||||
).first()
|
||||
if profile:
|
||||
norms_profile_key = profile.profile_key
|
||||
norms = resolve_norms_for_profile(profile)
|
||||
if ration_results.get("indicators") and norms:
|
||||
ration_results = {
|
||||
**ration_results,
|
||||
"indicators": _refresh_indicator_norms(ration_results["indicators"], norms),
|
||||
}
|
||||
lines_q = (
|
||||
LabRationLine.query.filter_by(recipe_id=recipe_id, is_deleted=False)
|
||||
.order_by(LabRationLine.row_index)
|
||||
.all()
|
||||
)
|
||||
line_snaps = []
|
||||
for line in lines_q:
|
||||
comp = Component.query.get(line.component_id) if line.component_id else None
|
||||
nutrients = nutrients_calc_dict(line.component_id) if line.component_id else {}
|
||||
dry_matter_pct = comp.dry_matter if comp else None
|
||||
line_snaps.append(
|
||||
RationLineSnapshot(
|
||||
id=line.id,
|
||||
component_id=line.component_id,
|
||||
ingredient_name=line.ingredient_name or (comp.name if comp else None),
|
||||
row_index=line.row_index,
|
||||
daily_kg=line.daily_kg,
|
||||
in_ration=bool(line.in_ration),
|
||||
in_compound=bool(line.in_compound),
|
||||
dry_matter=dry_matter_pct,
|
||||
price_per_kg=comp.price if comp else None,
|
||||
nutrients=nutrients,
|
||||
)
|
||||
)
|
||||
return RationSnapshot(
|
||||
recipe_id=recipe_id,
|
||||
recipe_name=recipe.name,
|
||||
ration_type=ration_type,
|
||||
heads_per_trip=int(recipe.heads_per_trip or 1),
|
||||
animal_profile_id=animal_profile_id,
|
||||
params=params,
|
||||
lines=tuple(line_snaps),
|
||||
norms=norms,
|
||||
ration_results=ration_results,
|
||||
compound_results=compound_results,
|
||||
calculated_at=calculated_at,
|
||||
exists=header is not None,
|
||||
norms_profile_key=norms_profile_key,
|
||||
legacy_norms_remapped=False,
|
||||
)
|
||||
|
||||
|
||||
def ration_to_calc_lines(snapshot: RationSnapshot) -> list[dict]:
|
||||
return [
|
||||
{
|
||||
"daily_kg": line.daily_kg,
|
||||
"in_ration": line.in_ration,
|
||||
"in_compound": line.in_compound,
|
||||
"ingredient_name": line.ingredient_name,
|
||||
"price_per_kg": line.price_per_kg,
|
||||
"dry_matter": line.dry_matter,
|
||||
"nutrients": line.nutrients,
|
||||
"component_id": line.component_id,
|
||||
}
|
||||
for line in snapshot.lines
|
||||
]
|
||||
Reference in New Issue
Block a user