@@ -0,0 +1,86 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from app.lab.calc.feed_groups import classify_feed_group
|
||||
from app.lab.calc.gfe_policies import default_om_digestibility_pct
|
||||
from app.lab.nutrient_schema import read_from_mapping
|
||||
from app.lab.services.component_nutrients import derive_context_for_component, nutrients_full_dict, nutrients_is_empty
|
||||
from app.models import Component
|
||||
|
||||
_REQUIRED_EAV_KEYS = ("Сыр. Протеин", "Сырая клетч", "Сырой жир")
|
||||
_MAIN_FEED_KEYS = ("Осн.Корм", "СВ Основной корм")
|
||||
_OMD_KEYS = ("ВРХ Орг Вещ", "КРС Орг Вещ")
|
||||
|
||||
|
||||
def main_feed_dm_g_per_kg(component_id: str | None) -> float | None:
|
||||
if not component_id:
|
||||
return None
|
||||
full = nutrients_full_dict(component_id)
|
||||
val = read_from_mapping(full, _MAIN_FEED_KEYS)
|
||||
return float(val) if val is not None else None
|
||||
|
||||
|
||||
def validate_component(component_id: str) -> dict[str, Any]:
|
||||
comp = Component.query.filter_by(id=component_id, is_deleted=False).first()
|
||||
if comp is None:
|
||||
return {
|
||||
"id": component_id,
|
||||
"name": None,
|
||||
"eligible": False,
|
||||
"missing": ["component_not_found"],
|
||||
"warnings": [],
|
||||
"dryMatterPct": None,
|
||||
"hasPrice": False,
|
||||
"price": None,
|
||||
}
|
||||
|
||||
missing: list[str] = []
|
||||
warnings: list[str] = []
|
||||
dry_matter_pct = comp.dry_matter
|
||||
if dry_matter_pct is None or float(dry_matter_pct) <= 0:
|
||||
missing.append("dry_matter")
|
||||
|
||||
full = nutrients_full_dict(component_id)
|
||||
if nutrients_is_empty(component_id):
|
||||
missing.append("nutrients_empty")
|
||||
else:
|
||||
for key in _REQUIRED_EAV_KEYS:
|
||||
if read_from_mapping(full, (key,)) is None:
|
||||
missing.append(key)
|
||||
|
||||
has_price = comp.price is not None and float(comp.price) >= 0
|
||||
if not has_price:
|
||||
warnings.append("price_missing")
|
||||
|
||||
main_feed_dm = read_from_mapping(full, _MAIN_FEED_KEYS)
|
||||
if main_feed_dm is None:
|
||||
warnings.append("main_feed_unset")
|
||||
|
||||
feed_group = classify_feed_group(comp)
|
||||
omd = read_from_mapping(full, _OMD_KEYS)
|
||||
if feed_group in ("rough", "succulent") and omd is None:
|
||||
warnings.append("omd_missing")
|
||||
elif omd is None and comp.dry_matter and float(comp.dry_matter) > 0:
|
||||
ctx = derive_context_for_component(component_id, full)
|
||||
warnings.append(
|
||||
f"omd_defaulted:{default_om_digestibility_pct(ctx):.0f}"
|
||||
)
|
||||
|
||||
return {
|
||||
"id": comp.id,
|
||||
"name": comp.name,
|
||||
"eligible": len(missing) == 0,
|
||||
"missing": missing,
|
||||
"warnings": warnings,
|
||||
"dryMatterPct": dry_matter_pct,
|
||||
"hasPrice": has_price,
|
||||
"price": comp.price,
|
||||
"mainFeedDmGPerKg": main_feed_dm,
|
||||
"isMainFeed": main_feed_dm is not None and float(main_feed_dm) > 0,
|
||||
"feedGroup": feed_group,
|
||||
}
|
||||
|
||||
|
||||
def validate_components(component_ids: list[str]) -> list[dict[str, Any]]:
|
||||
return [validate_component(cid) for cid in component_ids]
|
||||
Reference in New Issue
Block a user