from __future__ import annotations from typing import Any from app.modules.zootech.lab.calc.derived import apply_content_derived from app.modules.zootech.lab.calc.nutrients import norm_diff, weighted_average from app.modules.zootech.lab.indicators import RATION_ALL_INDICATORS def _compound_active(lines: list[dict[str, Any]]) -> list[dict[str, Any]]: return [ l for l in lines if l.get("in_compound") and (l.get("daily_kg") or 0) > 0 ] def _sum_kg(lines: list[dict[str, Any]]) -> float: return sum(float(l.get("daily_kg") or 0) for l in lines) def _compound_indicators( lines: list[dict[str, Any]], total_kg: float, norms: dict[str, dict[str, float | None]], ) -> list[dict[str, Any]]: content_by_key: dict[str, float | None] = {} for defn in RATION_ALL_INDICATORS: if defn.get("derived"): continue content_by_key[defn["key"]] = weighted_average( lines, total_kg, defn["nutrient_keys"], indicator_key=defn.get("key"), ) for defn in RATION_ALL_INDICATORS: if not defn.get("derived"): continue content_by_key[defn["key"]] = apply_content_derived( content_by_key, defn, compound_mode=True, ) rows = [] for defn in RATION_ALL_INDICATORS: key = defn["key"] content = content_by_key.get(key) bounds = norms.get(key, {}) min_v = bounds.get("min") max_v = bounds.get("max") diff = norm_diff(content, min_v, max_v) if content is None and min_v is None and max_v is None: continue rows.append( { "key": key, "label": defn["label"], "unit": defn["unit"], "min": min_v, "max": max_v, "content": content, "diff": diff, } ) return rows def calculate_compound_feed( lines: list[dict[str, Any]], norms: dict[str, dict[str, float | None]] | None = None, *, profile_mass_kg: float | None = None, heads_per_trip: int = 1, ) -> dict[str, Any] | None: del profile_mass_kg, heads_per_trip active = _compound_active(lines) if not active: return None total_kg = _sum_kg(active) cost = None any_cost = False for line in active: kg = line.get("daily_kg") price = line.get("price_per_kg") if kg is None or price is None: continue cost = (cost or 0) + float(kg) * float(price) any_cost = True return { "totals": [ {"key": "compound_kg", "label": "Масса комбикорма, кг", "value": total_kg}, { "key": "compound_cost", "label": "Стоимость комбикорма", "value": cost if any_cost else None, }, ], "indicators": _compound_indicators(active, total_kg, norms or {}), "lines": [ { "ingredient_name": line.get("ingredient_name") or "—", "daily_kg": line.get("daily_kg"), "share_pct": ( (float(line["daily_kg"]) / total_kg) * 100 if total_kg > 0 and line.get("daily_kg") is not None else None ), } for line in active ], }