- Updated README.md to reflect changes in the `json_for_advice_generation` structure, including the addition of `date_of_today` and clarification of `last_advice` fields. - Modified job.py to load and pass the issuance date of the last advice. - Enhanced advice_context.py to include the issuance date in the last advice retrieval. - Updated assemble.py to include `date_of_today` in the JSON assembly for advice generation. - Improved advice.py to sanitize the `advice_summary` by removing relative date references and ensuring compliance with new guidelines.
139 lines
5.9 KiB
Python
139 lines
5.9 KiB
Python
"""Unit tests for advice_summary sanitization and parse_advice wiring."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import unittest
|
|
from datetime import date, datetime
|
|
|
|
from pipeline.errors import PipelineError
|
|
from pipeline.stages.advice import SUMMARY_MAX_CHARS, parse_advice
|
|
from pipeline.stages.advice_summary import sanitize_advice_summary
|
|
|
|
_APR_13 = date(2026, 4, 13)
|
|
|
|
_APR13_SUMMARY = (
|
|
"Attenzione: il modello segnala un'infezione primaria di peronospora "
|
|
"(FASE5) per oggi, 13 aprile 2026. La vite si trova in una fase fenologica "
|
|
"di elevata suscettibilità (BBCH 15) e non risulta protetta. È "
|
|
"indispensabile intervenire per bloccare l'infezione in corso. Si raccomanda "
|
|
"di effettuare un trattamento con azione curativa domani, 14 aprile 2026. "
|
|
"Si consiglia l'impiego del prodotto PROFILER, dotato di attività sistemica "
|
|
"e curativa, alla dose di 3.0 kg/ha. Questo intervento è fondamentale per "
|
|
"impedire che l'infezione si stabilisca e per prevenire l'inizio "
|
|
"dell'epidemia stagionale. Seguire attentamente le istruzioni in etichetta."
|
|
)
|
|
|
|
|
|
def _advice_json(*, summary: str, full_advice: str = "Testo completo.") -> str:
|
|
return json.dumps(
|
|
{
|
|
"full_advice": full_advice,
|
|
"advice_summary": summary,
|
|
"apply_treatment": 1,
|
|
"treatments": ["PROFILER"],
|
|
"dosage": ["3.0 kg/ha"],
|
|
"apply_date": "2026-04-14",
|
|
},
|
|
ensure_ascii=False,
|
|
)
|
|
|
|
|
|
class SanitizeAdviceSummaryTests(unittest.TestCase):
|
|
def test_apr13_summary_strips_prefix_deictics_and_etichetta(self) -> None:
|
|
cleaned = sanitize_advice_summary(_APR13_SUMMARY, _APR_13)
|
|
self.assertFalse(cleaned.lower().startswith("attenzione"))
|
|
self.assertNotIn("oggi", cleaned.casefold())
|
|
self.assertNotIn("domani", cleaned.casefold())
|
|
self.assertNotIn("etichetta", cleaned.casefold())
|
|
self.assertIn("13 aprile 2026", cleaned)
|
|
self.assertIn("14 aprile 2026", cleaned)
|
|
self.assertIn("PROFILER", cleaned)
|
|
|
|
def test_english_today_tomorrow_before_date(self) -> None:
|
|
text = "Spray today, Apr 13th and again tomorrow, Apr 15th."
|
|
cleaned = sanitize_advice_summary(text, _APR_13)
|
|
self.assertEqual(cleaned, "Spray Apr 13th and again Apr 15th.")
|
|
self.assertNotIn("today", cleaned.casefold())
|
|
self.assertNotIn("tomorrow", cleaned.casefold())
|
|
|
|
def test_standalone_oggi_and_domani(self) -> None:
|
|
text = "Le condizioni di oggi restano favorevoli e domani peggiorano."
|
|
cleaned = sanitize_advice_summary(text, _APR_13)
|
|
self.assertEqual(
|
|
cleaned,
|
|
"Le condizioni di 13-04-2026 restano favorevoli e 14-04-2026 peggiorano.",
|
|
)
|
|
|
|
def test_standalone_ieri(self) -> None:
|
|
cleaned = sanitize_advice_summary("Pioggia ieri.", _APR_13)
|
|
self.assertEqual(cleaned, "Pioggia 12-04-2026.")
|
|
|
|
def test_etichetta_closer_variants(self) -> None:
|
|
variants = (
|
|
"Intervenire subito. Seguire attentamente le istruzioni in etichetta.",
|
|
"Intervenire subito. Seguire attentamente le istruzioni riportate in etichetta.",
|
|
"Intervenire subito. Rispettare scrupolosamente le indicazioni riportate in etichetta.",
|
|
)
|
|
for text in variants:
|
|
with self.subTest(text=text):
|
|
cleaned = sanitize_advice_summary(text, _APR_13)
|
|
self.assertEqual(cleaned, "Intervenire subito.")
|
|
self.assertNotIn("etichetta", cleaned.casefold())
|
|
|
|
def test_etichetta_with_real_content_is_kept(self) -> None:
|
|
text = (
|
|
"Non superare i 2 trattamenti per stagione previsti in etichetta "
|
|
"per questo formulato."
|
|
)
|
|
cleaned = sanitize_advice_summary(text, _APR_13)
|
|
self.assertEqual(cleaned, text)
|
|
|
|
def test_prefix_only(self) -> None:
|
|
cleaned = sanitize_advice_summary(
|
|
"Attenzione: trattare il 13 aprile 2026.",
|
|
_APR_13,
|
|
)
|
|
self.assertEqual(cleaned, "trattare il 13 aprile 2026.")
|
|
|
|
def test_already_clean_is_idempotent(self) -> None:
|
|
text = "Trattare il 13 aprile 2026 con PROFILER a 3.0 kg/ha."
|
|
once = sanitize_advice_summary(text, _APR_13)
|
|
twice = sanitize_advice_summary(once, _APR_13)
|
|
self.assertEqual(once, text)
|
|
self.assertEqual(twice, text)
|
|
|
|
def test_apr13_summary_is_idempotent(self) -> None:
|
|
once = sanitize_advice_summary(_APR13_SUMMARY, _APR_13)
|
|
twice = sanitize_advice_summary(once, _APR_13)
|
|
self.assertEqual(once, twice)
|
|
|
|
def test_datetime_issued_on(self) -> None:
|
|
cleaned = sanitize_advice_summary("Trattare oggi.", datetime(2026, 4, 13, 8, 0))
|
|
self.assertEqual(cleaned, "Trattare 13-04-2026.")
|
|
|
|
|
|
class ParseAdviceSanitizeTests(unittest.TestCase):
|
|
def test_parse_advice_sanitizes_summary(self) -> None:
|
|
result = parse_advice(_advice_json(summary=_APR13_SUMMARY), as_of=_APR_13)
|
|
self.assertNotIn("oggi", result.advice_summary.casefold())
|
|
self.assertFalse(result.advice_summary.lower().startswith("attenzione"))
|
|
self.assertIn("13 aprile 2026", result.advice_summary)
|
|
|
|
def test_empty_after_sanitize_raises(self) -> None:
|
|
summary = "Attenzione: Seguire attentamente le istruzioni in etichetta."
|
|
with self.assertRaises(PipelineError) as ctx:
|
|
parse_advice(_advice_json(summary=summary), as_of=_APR_13)
|
|
self.assertIn("empty after sanitization", str(ctx.exception))
|
|
|
|
def test_truncation_after_sanitize(self) -> None:
|
|
body = "Trattare il 13 aprile 2026. " * 80
|
|
self.assertGreater(len(body), SUMMARY_MAX_CHARS)
|
|
result = parse_advice(_advice_json(summary=body), as_of=_APR_13)
|
|
self.assertEqual(len(result.advice_summary), SUMMARY_MAX_CHARS)
|
|
self.assertTrue(result.advice_summary.startswith("Trattare il 13 aprile 2026."))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|