@@ -0,0 +1,86 @@
|
||||
"""Производные min/max норм из базовых показателей RACION."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from app.lab.indicators import RATION_ALL_INDICATORS, indicator_by_key
|
||||
|
||||
|
||||
def _norm_min(bounds: dict[str, float | None] | None) -> float | None:
|
||||
if not bounds:
|
||||
return None
|
||||
v = bounds.get("min")
|
||||
return float(v) if v is not None else None
|
||||
|
||||
|
||||
def _apply_derived_min(
|
||||
norms: dict[str, dict[str, float | None]],
|
||||
defn: dict[str, Any],
|
||||
*,
|
||||
mass_kg: float | None = None,
|
||||
) -> float | None:
|
||||
derived = defn.get("derived")
|
||||
key = defn["key"]
|
||||
if derived == "alias":
|
||||
src = norms.get(defn.get("alias_of") or "")
|
||||
if src and src.get("min") is not None:
|
||||
return src["min"]
|
||||
return None
|
||||
if derived == "pct_of_dm":
|
||||
src = _norm_min(norms.get(defn.get("from_key") or ""))
|
||||
dm = _norm_min(norms.get("dry_matter"))
|
||||
if src is None or not dm or dm <= 0:
|
||||
return None
|
||||
return src / dm * 100.0
|
||||
if derived == "g_per_kg_dm":
|
||||
src = _norm_min(norms.get(defn.get("from_key") or ""))
|
||||
dm = _norm_min(norms.get("dry_matter"))
|
||||
if src is None or not dm or dm <= 0:
|
||||
return None
|
||||
return src / (dm / 1000.0)
|
||||
if derived == "nel_per_kg_dm":
|
||||
dm = _norm_min(norms.get("dry_matter"))
|
||||
nel = _norm_min(norms.get("nel"))
|
||||
if not dm or dm <= 0 or nel is None:
|
||||
return None
|
||||
return nel / (dm / 1000.0)
|
||||
if derived == "ratio":
|
||||
num = _norm_min(norms.get(defn.get("ratio_num") or ""))
|
||||
den = _norm_min(norms.get(defn.get("ratio_den") or ""))
|
||||
if num is None or den is None or den == 0:
|
||||
return None
|
||||
return num / den
|
||||
if derived == "dm_pct_bw":
|
||||
dm = _norm_min(norms.get("dry_matter"))
|
||||
if dm is None or not mass_kg or mass_kg <= 0:
|
||||
return None
|
||||
return (dm / 1000.0 / mass_kg) * 100.0
|
||||
if derived == "ration_pct_bw":
|
||||
return None
|
||||
if derived in ("rnb", "bra_rnb"):
|
||||
return _norm_min(norms.get(key))
|
||||
return None
|
||||
|
||||
|
||||
def apply_derived_norms(
|
||||
norms: dict[str, dict[str, float | None]],
|
||||
*,
|
||||
mass_kg: float | None = None,
|
||||
) -> tuple[dict[str, dict[str, float | None]], dict[str, Any]]:
|
||||
"""Дополняет norms производными min; max не трогает."""
|
||||
out = {k: dict(v) for k, v in norms.items()}
|
||||
dynamic: dict[str, Any] = {}
|
||||
for defn in RATION_ALL_INDICATORS:
|
||||
if not defn.get("derived"):
|
||||
continue
|
||||
key = defn["key"]
|
||||
if _norm_min(out.get(key)) is not None:
|
||||
continue
|
||||
val = _apply_derived_min(out, defn, mass_kg=mass_kg)
|
||||
if val is None:
|
||||
continue
|
||||
rounded = round(val, 3)
|
||||
out[key] = {"min": rounded, "max": out.get(key, {}).get("max")}
|
||||
dynamic[key] = {"min": rounded, "derived": defn.get("derived")}
|
||||
return out, dynamic
|
||||
Reference in New Issue
Block a user