Files
site/WESP_REL/tests/test_lab_math_ration_seed.py
T
2026-07-17 12:57:18 +03:00

92 lines
4.3 KiB
Python

"""Тестовые рецепты LAB — полная матрица и «в нормах»."""
from __future__ import annotations
import unittest
from app import create_app, db
from app.lab.commands.seed_lab_math_ration import (
RECIPE_IN_NORMS,
RECIPE_MATRIX,
seed_lab_math_ration,
)
from app.lab.commands.seed_math_test_profiles import seed_math_test_profiles
from app.lab.loaders.ration_loader import load_ration
from app.lab.nutrient_schema import read_from_mapping
from app.models import Recipe
from app.services.setup_state import mark_setup_complete
from tests.helpers.zootech_test_helpers import ZootechTestConfig
class LabMathRationSeedTests(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.app = create_app(ZootechTestConfig)
cls.ctx = cls.app.app_context()
cls.ctx.push()
db.create_all()
mark_setup_complete(cls.app)
@classmethod
def tearDownClass(cls) -> None:
db.session.remove()
db.drop_all()
cls.ctx.pop()
def test_read_mapping_ca_not_dcab(self) -> None:
data = {"Ca": 3.5, "DCAB Форм": 120.0}
self.assertEqual(read_from_mapping(data, ("DCAB Форм", "DCAB")), 120.0)
self.assertEqual(read_from_mapping(data, ("Ca",)), 3.5)
def _snap(self, name: str):
recipe = Recipe.query.filter_by(name=name, is_deleted=False).first()
self.assertIsNotNone(recipe)
return load_ration(recipe.id)
def test_seed_matrix_recipe(self) -> None:
seed_math_test_profiles()
stats = seed_lab_math_ration(force=True)
self.assertFalse(stats.errors, stats.errors)
snap = self._snap(RECIPE_MATRIX)
self.assertIn("rnb", snap.norms)
labels = {i["label"] for i in (snap.ration_results.get("indicators") or [])}
self.assertIn("RNB", labels)
self.assertIn("Fe", labels)
dm = next(i for i in snap.ration_results["indicators"] if i.get("key") == "dry_matter")
self.assertGreater(dm["content"], 10000)
cp = next(i for i in snap.ration_results["indicators"] if i.get("key") == "crude_protein")
usp = next(i for i in snap.ration_results["indicators"] if i.get("key") == "usp")
rnb = next(i for i in snap.ration_results["indicators"] if i.get("key") == "rnb")
expected_rnb = (cp["content"] - usp["content"]) / 6.25
self.assertAlmostEqual(rnb["content"], expected_rnb, places=1)
self.assertAlmostEqual(rnb["content"], 71.68, places=1)
def test_seed_in_norms_mostly_green(self) -> None:
seed_math_test_profiles()
seed_lab_math_ration(force=True)
snap = self._snap(RECIPE_IN_NORMS)
indicators = snap.ration_results.get("indicators") or []
with_norm = [i for i in indicators if i.get("min") is not None or i.get("max") is not None]
greens = [i for i in with_norm if abs(i.get("diff") or 0) < 0.01]
reds = [i for i in with_norm if abs(i.get("diff") or 0) >= 0.01]
self.assertGreaterEqual(len(greens), len(reds), f"reds: {[r['key'] for r in reds]}")
dm = next(i for i in indicators if i.get("key") == "dry_matter")
self.assertLess(dm["content"], 6500)
sf = next(i for i in indicators if i.get("key") == "structural_fiber")
self.assertGreaterEqual(sf["content"], sf["min"])
fat_pct = next(i for i in indicators if i.get("key") == "fat_pct_per_kg_dm")
nfc_pct = next(i for i in indicators if i.get("key") == "nfc_pct_dm")
cp_kg = next(i for i in indicators if i.get("key") == "cp_per_kg_dm")
usp = next(i for i in indicators if i.get("key") == "usp")
usp_kg = next(i for i in indicators if i.get("key") == "usp_per_kg_dm")
self.assertAlmostEqual(fat_pct["content"], 3.5, delta=0.5)
self.assertAlmostEqual(nfc_pct["content"], 23.0, delta=2.0)
self.assertAlmostEqual(cp_kg["content"], 109.0, delta=15.0)
dom = next(i for i in indicators if i.get("key") == "digestible_organic_matter")
dig_nfe = next(i for i in indicators if i.get("key") == "digestible_nfe")
self.assertGreater(usp_kg["content"], 0)
self.assertAlmostEqual(usp_kg["content"], usp["content"] / (dm["content"] / 1000), delta=2.0)
self.assertGreater(dom["content"], 0)
self.assertGreater(dig_nfe["content"], 0)