@@ -0,0 +1,132 @@
|
||||
"""Динамические нормы уСП по GfE."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
from app import create_app, db
|
||||
from app.lab.calc.gfe_norms import apply_dynamic_norms, gfe_nel_min_mj, gfe_usp_min_g
|
||||
from app.lab.commands.upsert_profile import upsert_animal_profile
|
||||
from app.lab.loaders.profile_loader import load_animal_profile
|
||||
from app.lab.models import LabAnimalProfile, LabProfileNorm
|
||||
from app.lab.services.profile_norms import resolve_norms_for_profile
|
||||
from app.models.base import default_uuid
|
||||
|
||||
|
||||
class GfeUspNormTests(unittest.TestCase):
|
||||
def test_gfe_example_650kg_30milk(self) -> None:
|
||||
val = gfe_usp_min_g(650, 30)
|
||||
self.assertIsNotNone(val)
|
||||
self.assertAlmostEqual(val, 2622.4, places=1)
|
||||
|
||||
def test_gfe_maintenance_only(self) -> None:
|
||||
val = gfe_usp_min_g(300, 0)
|
||||
self.assertIsNotNone(val)
|
||||
self.assertAlmostEqual(val, 0.09 * (300**0.75) * 6.25, places=2)
|
||||
|
||||
def test_stored_usp_min_not_overwritten(self) -> None:
|
||||
stored = {"usp": {"min": 1800.0, "max": None}}
|
||||
resolved, dynamic = apply_dynamic_norms(stored, mass_kg=650, milk_yield_kg=30)
|
||||
self.assertEqual(resolved["usp"]["min"], 1800.0)
|
||||
self.assertNotIn("usp", dynamic)
|
||||
self.assertIn("nel", dynamic)
|
||||
|
||||
def test_force_dynamic_overwrites_stored_usp_nel(self) -> None:
|
||||
stored = {"usp": {"min": 310.0, "max": None}, "nel": {"min": 21.1, "max": None}}
|
||||
resolved, dynamic = apply_dynamic_norms(
|
||||
stored,
|
||||
mass_kg=650,
|
||||
milk_yield_kg=30,
|
||||
force_dynamic=True,
|
||||
)
|
||||
self.assertAlmostEqual(resolved["usp"]["min"], 2622.4, places=1)
|
||||
self.assertAlmostEqual(resolved["nel"]["min"], 136.7, places=1)
|
||||
self.assertIn("usp", dynamic)
|
||||
self.assertIn("nel", dynamic)
|
||||
|
||||
def test_dynamic_usp_when_min_missing(self) -> None:
|
||||
stored = {"dry_matter": {"min": 10.0, "max": 20.0}}
|
||||
resolved, dynamic = apply_dynamic_norms(stored, mass_kg=650, milk_yield_kg=30)
|
||||
self.assertAlmostEqual(resolved["usp"]["min"], 2622.4, places=1)
|
||||
self.assertIn("usp", dynamic)
|
||||
|
||||
def test_gfe_nel_example_650kg_30milk(self) -> None:
|
||||
val = gfe_nel_min_mj(650, 30)
|
||||
self.assertIsNotNone(val)
|
||||
self.assertAlmostEqual(val, 136.7, places=1)
|
||||
|
||||
def test_dynamic_nel_when_min_missing(self) -> None:
|
||||
resolved, dynamic = apply_dynamic_norms({}, mass_kg=650, milk_yield_kg=30)
|
||||
self.assertAlmostEqual(resolved["nel"]["min"], 136.7, places=1)
|
||||
self.assertIn("nel", dynamic)
|
||||
|
||||
|
||||
class GfeUspProfileIntegrationTests(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from config import Config
|
||||
|
||||
self._tmp = tempfile.mkdtemp(prefix="wesp-gfe-")
|
||||
recipes = Path(self._tmp) / "recipes.db"
|
||||
reports = Path(self._tmp) / "reports.db"
|
||||
|
||||
class GfeTestConfig(Config):
|
||||
TESTING = False
|
||||
WESP_BACKGROUND_STARTUP = False
|
||||
SQLALCHEMY_DATABASE_URI = f"sqlite:///{recipes}"
|
||||
SQLALCHEMY_BINDS = {"reports": f"sqlite:///{reports}"}
|
||||
SECRET_KEY = "gfe-test-secret"
|
||||
SYNC_BACKGROUND_REQUEUE = False
|
||||
SYNC_CLIENT_AUTOSTART = False
|
||||
WESP_ADMIN_LOG_PATH = ""
|
||||
WESP_ADMIN_HARDWARE_LOG_PATH = ""
|
||||
WESP_ADMIN_UI_ACTIVITY_LOG_PATH = ""
|
||||
|
||||
self.app = create_app(GfeTestConfig, run_migrations=True)
|
||||
self.ctx = self.app.app_context()
|
||||
self.ctx.push()
|
||||
|
||||
def tearDown(self) -> None:
|
||||
db.session.remove()
|
||||
self.ctx.pop()
|
||||
|
||||
def test_profile_api_and_resolve(self) -> None:
|
||||
key = f"lab_gfe_test_{default_uuid()[:8]}"
|
||||
upsert_animal_profile(
|
||||
None,
|
||||
{
|
||||
"profileKey": key,
|
||||
"label": "GfE test",
|
||||
"rationType": "DAIRY",
|
||||
"massKg": 650,
|
||||
"milkYieldKg": 30,
|
||||
"indicators": {},
|
||||
},
|
||||
)
|
||||
profile = LabAnimalProfile.query.filter_by(profile_key=key).first()
|
||||
self.assertIsNotNone(profile)
|
||||
resolved = resolve_norms_for_profile(profile)
|
||||
self.assertAlmostEqual(resolved["usp"]["min"], 2622.4, places=1)
|
||||
self.assertAlmostEqual(resolved["nel"]["min"], 136.7, places=1)
|
||||
|
||||
api = load_animal_profile(profile.id)
|
||||
self.assertEqual(api["milkYieldKg"], 30)
|
||||
self.assertAlmostEqual(api["resolvedNorms"]["usp"]["min"], 2622.4, places=1)
|
||||
self.assertAlmostEqual(api["resolvedNorms"]["nel"]["min"], 136.7, places=1)
|
||||
self.assertIn("usp", api["dynamicNorms"])
|
||||
self.assertIn("nel", api["dynamicNorms"])
|
||||
|
||||
db.session.add(
|
||||
LabProfileNorm(
|
||||
id=default_uuid(),
|
||||
profile_id=profile.id,
|
||||
indicator_key="usp",
|
||||
min_value=1500.0,
|
||||
max_value=None,
|
||||
)
|
||||
)
|
||||
db.session.commit()
|
||||
resolved2 = resolve_norms_for_profile(profile)
|
||||
self.assertEqual(resolved2["usp"]["min"], 1500.0)
|
||||
Reference in New Issue
Block a user