This commit is contained in:
2026-05-13 16:45:54 +03:30
parent 948c062b93
commit 46fe62fa04
96 changed files with 3834 additions and 155 deletions
+17 -10
View File
@@ -11,7 +11,10 @@ from django.db import transaction
from farm_data.models import SensorData
from farm_data.services import (
build_ai_farm_snapshot,
clone_snapshot_as_runtime_plant,
get_ai_snapshot_metric,
get_ai_snapshot_weather,
get_farm_plant_snapshot_by_name,
)
from irrigation.evapotranspiration import (
@@ -55,13 +58,15 @@ def _safe_float(value: Any, default: float = 0.0) -> float:
return default
def _sensor_metric(sensor: SensorData | None, metric: str) -> float | None:
if sensor is None or not isinstance(sensor.sensor_payload, dict):
return None
for payload in sensor.sensor_payload.values():
if isinstance(payload, dict) and payload.get(metric) is not None:
return _safe_float(payload.get(metric), default=0.0)
return None
def _aggregated_metric(ai_snapshot: dict[str, Any] | None, metric: str) -> float | None:
value = get_ai_snapshot_metric(ai_snapshot, metric)
return _safe_float(value, default=0.0) if value is not None else None
def _aggregated_metric_fallback(ai_snapshot: dict[str, Any] | None, metric: str) -> float | None:
"""Limited fallback for missing aggregated metrics only; raw payload is intentionally not consulted."""
return _aggregated_metric(ai_snapshot, metric)
def _coerce_list(value: Any) -> list[Any]:
@@ -275,9 +280,9 @@ def _build_irrigation_ui_payload(
crop_profile: dict[str, Any],
active_kc: float,
irrigation_method: IrrigationMethod | None,
sensor: SensorData | None,
ai_snapshot: dict[str, Any] | None,
) -> dict[str, Any]:
soil_moisture = _sensor_metric(sensor, "soil_moisture")
soil_moisture = _aggregated_metric_fallback(ai_snapshot, "soil_moisture")
plan = _normalize_plan(
llm_result,
optimizer_result,
@@ -380,6 +385,7 @@ def get_irrigation_recommendation(
.filter(farm_uuid=resolved_farm_uuid)
.first()
)
ai_snapshot = build_ai_farm_snapshot(resolved_farm_uuid)
irrigation_method = _resolve_irrigation_method(sensor, irrigation_method_name)
_persist_irrigation_method_on_farm(sensor, irrigation_method)
@@ -423,6 +429,7 @@ def get_irrigation_recommendation(
if plant is not None and forecasts:
optimized_result = _get_optimizer().optimize_irrigation(
sensor=sensor,
ai_snapshot=ai_snapshot,
plant=plant,
forecasts=forecasts,
daily_water_needs=daily_water_needs,
@@ -518,7 +525,7 @@ def get_irrigation_recommendation(
crop_profile,
active_kc,
irrigation_method,
sensor,
ai_snapshot,
)
result["raw_response"] = raw
result["simulation_optimizer"] = optimized_result