@@ -0,0 +1,107 @@
|
||||
"""Методика Москва — лактирующие коровы (NORM_1_1_CALC)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from app.modules.zootech.lab.calc.racion.interp import popr_index
|
||||
from app.modules.zootech.lab.calc.norms_derived import apply_derived_norms
|
||||
from app.modules.zootech.lab.calc.racion.npitv_map import DAIRY_LACTIR_NPITV, NPITV_TO_INDICATOR
|
||||
from app.modules.zootech.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}
|
||||
Reference in New Issue
Block a user