108 lines
3.4 KiB
Python
108 lines
3.4 KiB
Python
"""Методика Москва — лактирующие коровы (NORM_1_1_CALC)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Any
|
|
|
|
from app.lab.calc.racion.interp import popr_index
|
|
from app.lab.calc.norms_derived import apply_derived_norms
|
|
from app.lab.calc.racion.npitv_map import DAIRY_LACTIR_NPITV, NPITV_TO_INDICATOR
|
|
from app.lab.calc.racion.tables import load_moskwa_lactir
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class MoscowDairyParams:
|
|
mass_kg: float
|
|
milk_yield_kg: float
|
|
milk_fat_pct: float = 4.0
|
|
lactation_no: int = 2
|
|
body_condition: int = 1
|
|
housing_system: int = 1
|
|
|
|
|
|
def _row_lookup(rows: list[dict], npitv: int) -> dict | None:
|
|
for row in rows:
|
|
if row["npitv"] == npitv and row["pom"] == 1:
|
|
return row
|
|
return None
|
|
|
|
|
|
def _popr_value(row: dict, udoy: float, boundaries: list[float]) -> tuple[float | None, float | None]:
|
|
popr = row.get("popr_k") or []
|
|
if not popr:
|
|
return None, None
|
|
idx = popr_index(udoy, boundaries) - 1
|
|
idx = max(0, min(idx, len(popr) - 1))
|
|
p_k = popr[idx]
|
|
koef = float(row.get("koef") or 0)
|
|
return p_k, koef
|
|
|
|
|
|
def compute_moscow_norm(npitv: int, params: MoscowDairyParams) -> float | None:
|
|
data = load_moskwa_lactir()
|
|
boundaries = data.get("udoy_boundaries") or []
|
|
row = _row_lookup(data.get("rows") or [], npitv)
|
|
if row is None:
|
|
return None
|
|
|
|
mass = params.mass_kg
|
|
udoy = params.milk_yield_kg
|
|
jir = params.milk_fat_pct
|
|
wmassa = mass * 1.02 if params.body_condition > 1 else mass
|
|
|
|
p_k, koef = _popr_value(row, udoy, boundaries)
|
|
if p_k is None:
|
|
return None
|
|
|
|
norma: float | None = None
|
|
if npitv == 1:
|
|
temp = 0.005 if udoy <= 22 else 0.0025
|
|
norma = temp * (wmassa - 500) + p_k * udoy - ((4 - jir) * udoy) / 148
|
|
elif npitv == 2:
|
|
temp = 0.09 if udoy <= 22 else 0.065
|
|
norma = temp * (wmassa - 500) + p_k * udoy - ((4 - jir) * udoy) / 15
|
|
elif npitv == 3:
|
|
temp = 0.017 if udoy <= 22 else 0.015
|
|
norma = temp * (wmassa - 500) + p_k * udoy
|
|
elif 4 <= npitv <= 24:
|
|
norma = p_k * udoy + (wmassa - 500) * koef
|
|
if npitv == 10:
|
|
norma *= 0.393
|
|
else:
|
|
return None
|
|
|
|
if norma is None:
|
|
return None
|
|
if params.lactation_no == 1:
|
|
norma *= 0.95
|
|
elif params.lactation_no == 3:
|
|
norma *= 1.05
|
|
if params.housing_system == 2:
|
|
norma *= 1.1
|
|
return round(norma, 3)
|
|
|
|
|
|
def resolve_moscow_dairy_norms(params: MoscowDairyParams) -> tuple[dict[str, dict[str, float | None]], dict[str, Any]]:
|
|
resolved: dict[str, dict[str, float | None]] = {}
|
|
dynamic: dict[str, Any] = {}
|
|
for npitv in DAIRY_LACTIR_NPITV:
|
|
value = compute_moscow_norm(npitv, params)
|
|
if value is None:
|
|
continue
|
|
key = NPITV_TO_INDICATOR.get(npitv)
|
|
if not key:
|
|
continue
|
|
resolved[key] = {"min": value, "max": None}
|
|
dynamic[key] = {"min": value, "npitv": npitv, "method": "racion_moscow", "source": "racion"}
|
|
resolved, derived_dyn = apply_derived_norms(resolved, mass_kg=params.mass_kg)
|
|
for k, v in derived_dyn.items():
|
|
dynamic[k] = {**v, "method": "racion_moscow", "source": "derived"}
|
|
meta = {
|
|
"method": "racion_moscow",
|
|
"massKg": params.mass_kg,
|
|
"milkYieldKg": params.milk_yield_kg,
|
|
"milkFatPct": params.milk_fat_pct,
|
|
}
|
|
return resolved, {"meta": meta, "dynamic": dynamic}
|