Интегрирован wesp в сайт
CI / quality (push) Canceled after 0s

This commit is contained in:
влад
2026-07-17 12:57:18 +03:00
parent 5dfa06ddbe
commit 355c0ef9f1
883 changed files with 194576 additions and 177 deletions
+125
View File
@@ -0,0 +1,125 @@
from __future__ import annotations
from app.lab.calc.nutrients import norm_diff
from app.lab.indicators import label_to_indicator_key
from app.lab.dto.ration import RationLineSnapshot, RationSnapshot
from app.lab.models import LabAnimalProfile, LabRationLine, LabRecipeRation
from app.lab.services.component_nutrients import nutrients_calc_dict
from app.lab.services.profile_norms import resolve_norms_for_profile
from app.lab.services.ration_calc_store import (
load_compound_results,
load_params,
load_ration_results,
)
from app.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
]