- Enhanced `.env.example` to include `SQL_APP_DATABASE` for managing additional advisory-related tables. - Refactored `config.yaml` to remove the `crops` block, shifting to a database-driven approach for job discovery. - Updated `README.md` to clarify the new database structure and job discovery process, emphasizing the role of the `AI_agrosupport_an_pathogen_models` and `AI_agrosupport_agro_models` tables. - Modified SQL queries across multiple files to accommodate the new database schema, ensuring correct table references and improving overall data handling. - Adjusted `pipeline` logic to utilize the new configuration, enhancing the efficiency of the advisory generation process.
108 lines
3.6 KiB
Python
108 lines
3.6 KiB
Python
"""Unit tests for database-driven worklist discovery."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import unittest
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
from unittest.mock import patch
|
|
|
|
from pipeline.config import WorklistSettings
|
|
from pipeline.vocab import load_crop_vocab, load_disease_vocab
|
|
from pipeline.worklist import build_jobs
|
|
|
|
_ROOT = Path(__file__).resolve().parent.parent
|
|
_CROP_VOCAB = load_crop_vocab(_ROOT / "vocab" / "crops.yaml")
|
|
_DISEASE_VOCAB = load_disease_vocab(_ROOT / "vocab" / "diseases.yaml")
|
|
|
|
|
|
def _row(**overrides: object) -> SimpleNamespace:
|
|
fields = {
|
|
"cmplay_id": 4012,
|
|
"cmplay_colture": 1,
|
|
"cmplay_imp": 3,
|
|
"cmplay_station": 7,
|
|
"colture_name": "Vite",
|
|
"anmod_id": 10,
|
|
"anmod_model": "PERONOSPORA",
|
|
}
|
|
fields.update(overrides)
|
|
return SimpleNamespace(**fields)
|
|
|
|
|
|
def _build(rows: list[SimpleNamespace], allowlist: tuple[int, ...] = ()) -> tuple[list, str]:
|
|
worklist = WorklistSettings(field_allowlist=allowlist)
|
|
with patch("pipeline.worklist.fetch_all", return_value=rows) as fetch:
|
|
jobs = build_jobs(object(), worklist, _CROP_VOCAB, _DISEASE_VOCAB)
|
|
sql = fetch.call_args.args[1]
|
|
return jobs, sql
|
|
|
|
|
|
class BuildJobsTests(unittest.TestCase):
|
|
def test_peronospora_on_grapevine_becomes_one_job(self) -> None:
|
|
jobs, sql = _build([_row()])
|
|
|
|
self.assertEqual(len(jobs), 1)
|
|
job = jobs[0]
|
|
self.assertEqual(job.field_id, 4012)
|
|
self.assertEqual(job.model_name, "PERONOSPORA")
|
|
self.assertEqual(job.disease_english, "downy mildew")
|
|
self.assertEqual(job.crop_english, "grapevine")
|
|
self.assertEqual(job.crop_italian, "Vite")
|
|
self.assertTrue(job.organic)
|
|
self.assertEqual(job.station, 7)
|
|
self.assertEqual(job.anmod_id, 10)
|
|
self.assertIn("pat_mod_AI_model = 1", sql)
|
|
self.assertIn("anmod_AI_advice = 1", sql)
|
|
self.assertIn("anmod_cmplay", sql)
|
|
|
|
def test_allowlist_drops_other_field_ids(self) -> None:
|
|
jobs, _sql = _build(
|
|
[_row(cmplay_id=4012), _row(cmplay_id=4085, anmod_id=11)],
|
|
allowlist=(4085,),
|
|
)
|
|
|
|
self.assertEqual([job.field_id for job in jobs], [4085])
|
|
|
|
def test_unmapped_disease_and_colture_are_skipped(self) -> None:
|
|
jobs, _sql = _build([
|
|
_row(),
|
|
_row(anmod_id=11, anmod_model="NOT_A_DISEASE"),
|
|
_row(cmplay_id=4085, anmod_id=12, colture_name="Not A Crop"),
|
|
])
|
|
|
|
self.assertEqual([(job.field_id, job.anmod_id) for job in jobs], [(4012, 10)])
|
|
|
|
def test_blank_and_null_colture_are_skipped(self) -> None:
|
|
jobs, _sql = _build([
|
|
_row(cmplay_id=1, anmod_id=1, colture_name=""),
|
|
_row(cmplay_id=2, anmod_id=2, colture_name=None),
|
|
_row(cmplay_id=3, anmod_id=3),
|
|
])
|
|
|
|
self.assertEqual([job.field_id for job in jobs], [3])
|
|
|
|
def test_same_english_disease_keeps_distinct_anmod_rows(self) -> None:
|
|
jobs, _sql = _build([
|
|
_row(anmod_id=20, anmod_model="PERONOSPORA"),
|
|
_row(anmod_id=11, anmod_model="Peronospora"),
|
|
])
|
|
|
|
self.assertEqual(
|
|
[(job.anmod_id, job.model_name, job.disease_english) for job in jobs],
|
|
[
|
|
(20, "PERONOSPORA", "downy mildew"),
|
|
(11, "Peronospora", "downy mildew"),
|
|
],
|
|
)
|
|
|
|
def test_imp_other_than_3_is_not_organic(self) -> None:
|
|
jobs, _sql = _build([_row(cmplay_imp=1, cmplay_station=None)])
|
|
|
|
self.assertFalse(jobs[0].organic)
|
|
self.assertIsNone(jobs[0].station)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|