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]