Enhance weather metrics handling and phenology logic
- Introduced a new function to round specific weather metric values to two decimal places in assemble.py. - Updated the JSON entry construction in assemble.py to utilize the new rounding function for weather metrics. - Modified the phenology loading logic in phenology.py to use yesterday's phase if today's phase is null after carry-forward. - Clarified documentation in README.md regarding the phenology phase handling.
This commit is contained in:
parent
e588d97e0f
commit
21927133a4
@ -346,7 +346,7 @@ Run once per `(field, disease)` job by `pipeline/job.py::run_job`:
|
|||||||
1. **Worklist** — crop → fields (`AI_agrosupport_an_colture` + `AI_agrosupport_cmp_layers`) → disease model (`AI_agrosupport_agro_models`); organic flag is `cmplay_imp == 3` (`pipeline/worklist.py`, run once per batch, not per job)
|
1. **Worklist** — crop → fields (`AI_agrosupport_an_colture` + `AI_agrosupport_cmp_layers`) → disease model (`AI_agrosupport_agro_models`); organic flag is `cmplay_imp == 3` (`pipeline/worklist.py`, run once per batch, not per job)
|
||||||
2. **Disease model** — per-day `FASE5` > `FASE2` resolution over as_of±5, from the worklist's known `anmod_id`
|
2. **Disease model** — per-day `FASE5` > `FASE2` resolution over as_of±5, from the worklist's known `anmod_id`
|
||||||
3. **Weather** — `TDatiMeteo_D` for past days, `TDatiMeteo_D_FRC` for today/future
|
3. **Weather** — `TDatiMeteo_D` for past days, `TDatiMeteo_D_FRC` for today/future
|
||||||
4. **Phenology** — carry-forward of latest observation ≤ day; future days null
|
4. **Phenology** — carry-forward of latest observation ≤ day; future days null; if today's phase is missing, use yesterday's
|
||||||
5. **Applied treatments** — products sprayed over `as_of-5 .. as_of`, from `AI_agrosupport_agro_ril_operations*` with `rilop_operation = 10`
|
5. **Applied treatments** — products sprayed over `as_of-5 .. as_of`, from `AI_agrosupport_agro_ril_operations*` with `rilop_operation = 10`
|
||||||
6. **LLM** — synthesise 1–2 anonymised search queries from the subset
|
6. **LLM** — synthesise 1–2 anonymised search queries from the subset
|
||||||
7. **Product prefilter** — organic / crop / disease / FASE rules against the batch-wide, in-memory `products` index
|
7. **Product prefilter** — organic / crop / disease / FASE rules against the batch-wide, in-memory `products` index
|
||||||
|
|||||||
@ -20,6 +20,20 @@ METRIC_KEYS = (
|
|||||||
"potential_evapotranspiration_mm",
|
"potential_evapotranspiration_mm",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_TWO_DECIMAL_KEYS = frozenset({
|
||||||
|
"min_air_temperature_c",
|
||||||
|
"max_air_temperature_c",
|
||||||
|
"average_air_temperature_c",
|
||||||
|
"rainfall_mm",
|
||||||
|
"potential_evapotranspiration_mm",
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def _metric_value(key: str, value: Any) -> Any:
|
||||||
|
if value is None or key not in _TWO_DECIMAL_KEYS:
|
||||||
|
return value
|
||||||
|
return round(float(value), 2)
|
||||||
|
|
||||||
|
|
||||||
def _day_entry(
|
def _day_entry(
|
||||||
day: date,
|
day: date,
|
||||||
@ -32,7 +46,7 @@ def _day_entry(
|
|||||||
metrics = weather.get(day, {})
|
metrics = weather.get(day, {})
|
||||||
entry: dict[str, Any] = {"date": format_date(day)}
|
entry: dict[str, Any] = {"date": format_date(day)}
|
||||||
for key in METRIC_KEYS:
|
for key in METRIC_KEYS:
|
||||||
entry[key] = metrics.get(key)
|
entry[key] = _metric_value(key, metrics.get(key))
|
||||||
entry["phenology_phase"] = phenology.get(day)
|
entry["phenology_phase"] = phenology.get(day)
|
||||||
entry["disease_forecast"] = forecasts.get(day)
|
entry["disease_forecast"] = forecasts.get(day)
|
||||||
# Treatments can only have been applied on or before as_of; future days
|
# Treatments can only have been applied on or before as_of; future days
|
||||||
@ -93,7 +107,7 @@ def build_json_for_query_synthesis(
|
|||||||
metrics = weather.get(day, {})
|
metrics = weather.get(day, {})
|
||||||
entry: dict[str, Any] = {"date": format_date(day)}
|
entry: dict[str, Any] = {"date": format_date(day)}
|
||||||
for key in METRIC_KEYS:
|
for key in METRIC_KEYS:
|
||||||
entry[key] = metrics.get(key)
|
entry[key] = _metric_value(key, metrics.get(key))
|
||||||
weather_forecasts.append(entry)
|
weather_forecasts.append(entry)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -56,8 +56,7 @@ def load_phenology(
|
|||||||
|
|
||||||
Observations with rilfeno_date <= as_of are carried forward to each day
|
Observations with rilfeno_date <= as_of are carried forward to each day
|
||||||
(latest observation at or before that day). Future days are always null.
|
(latest observation at or before that day). Future days are always null.
|
||||||
Today's phase equals yesterday's (which, under carry-forward, is the same
|
If today's phase is null after carry-forward, yesterday's phase is used.
|
||||||
observation as of as_of-1, or the latest available before as_of).
|
|
||||||
"""
|
"""
|
||||||
window = build_window(as_of)
|
window = build_window(as_of)
|
||||||
rows = fetch_all(conn, _FENO_SQL, (field_id, as_of))
|
rows = fetch_all(conn, _FENO_SQL, (field_id, as_of))
|
||||||
@ -92,9 +91,9 @@ def load_phenology(
|
|||||||
_, ff_id = applicable[-1]
|
_, ff_id = applicable[-1]
|
||||||
result[day] = name_by_id.get(ff_id)
|
result[day] = name_by_id.get(ff_id)
|
||||||
|
|
||||||
# Explicit rule: today's phase equals yesterday's.
|
# If today's phase is missing, fall back to yesterday's.
|
||||||
yesterday = as_of.fromordinal(as_of.toordinal() - 1)
|
yesterday = as_of.fromordinal(as_of.toordinal() - 1)
|
||||||
if yesterday in result:
|
if result[as_of] is None and yesterday in result:
|
||||||
result[as_of] = result[yesterday]
|
result[as_of] = result[yesterday]
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user