UPDATE
This commit is contained in:
+16
-2
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
from typing import Any
|
||||
|
||||
from farm_data.models import SensorData
|
||||
from farm_data.services import build_ai_farm_snapshot
|
||||
|
||||
from .services import get_forecast_for_location
|
||||
|
||||
@@ -39,7 +40,7 @@ def _weather_condition(weather_code):
|
||||
return WMO_CONDITIONS.get(weather_code, "نامشخص")
|
||||
|
||||
|
||||
def _build_farm_weather_card(forecasts: list[Any]) -> dict[str, Any]:
|
||||
def _build_farm_weather_card(forecasts: list[Any], *, farm_uuid: str | None = None, ai_snapshot: dict[str, Any] | None = None) -> dict[str, Any]:
|
||||
if not forecasts:
|
||||
return {
|
||||
"condition": "نامشخص",
|
||||
@@ -49,6 +50,10 @@ def _build_farm_weather_card(forecasts: list[Any]) -> dict[str, Any]:
|
||||
"windSpeed": 0,
|
||||
"windUnit": "km/h",
|
||||
"chartData": {"labels": [], "series": [[]]},
|
||||
"source_metadata": {
|
||||
"weather": {"source": "center_location_forecast", "policy": "center_location_latest_forecast"},
|
||||
"agronomic_metrics": {"source": "build_ai_farm_snapshot", "policy": "cluster_block_farm_aggregated"},
|
||||
},
|
||||
}
|
||||
|
||||
current_forecast = forecasts[0]
|
||||
@@ -66,6 +71,14 @@ def _build_farm_weather_card(forecasts: list[Any]) -> dict[str, Any]:
|
||||
"labels": labels,
|
||||
"series": series,
|
||||
},
|
||||
"source_metadata": {
|
||||
"weather": {"source": "center_location_forecast", "policy": "center_location_latest_forecast"},
|
||||
"agronomic_metrics": {
|
||||
"source": "build_ai_farm_snapshot",
|
||||
"policy": "cluster_block_farm_aggregated",
|
||||
"available": bool(ai_snapshot),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -80,4 +93,5 @@ class FarmWeatherService:
|
||||
raise ValueError("Farm not found.")
|
||||
|
||||
forecasts = get_forecast_for_location(sensor.center_location, days=7)
|
||||
return _build_farm_weather_card(forecasts)
|
||||
ai_snapshot = build_ai_farm_snapshot(farm_uuid)
|
||||
return _build_farm_weather_card(forecasts, farm_uuid=farm_uuid, ai_snapshot=ai_snapshot)
|
||||
|
||||
@@ -28,6 +28,10 @@ class FarmWeatherApiTests(TestCase):
|
||||
"labels": ["2026-04-01", "2026-04-02"],
|
||||
"series": [[28.0, 29.0]],
|
||||
},
|
||||
"source_metadata": {
|
||||
"weather": {"source": "center_location_forecast", "policy": "center_location_latest_forecast"},
|
||||
"agronomic_metrics": {"source": "build_ai_farm_snapshot", "policy": "cluster_block_farm_aggregated", "available": True},
|
||||
},
|
||||
}
|
||||
)
|
||||
mock_get_app_config.return_value = SimpleNamespace(
|
||||
@@ -44,6 +48,7 @@ class FarmWeatherApiTests(TestCase):
|
||||
payload = response.json()["data"]
|
||||
self.assertEqual(payload["condition"], "صاف")
|
||||
self.assertEqual(payload["chartData"]["labels"][0], "2026-04-01")
|
||||
self.assertEqual(payload["source_metadata"]["weather"]["policy"], "center_location_latest_forecast")
|
||||
|
||||
@patch("weather.views.apps.get_app_config")
|
||||
def test_farm_weather_api_returns_404_for_missing_farm(self, mock_get_app_config):
|
||||
@@ -106,6 +111,8 @@ class WaterNeedPredictionApiTests(TestCase):
|
||||
payload = response.json()["data"]
|
||||
self.assertEqual(payload["farm_uuid"], "550e8400-e29b-41d4-a716-446655440000")
|
||||
self.assertEqual(payload["insight"]["confidence"], 0.82)
|
||||
self.assertEqual(payload["source_metadata"]["agronomic_metrics"]["policy"], "cluster_block_farm_aggregated")
|
||||
self.assertEqual(payload["source_metadata"]["weather"]["policy"], "center_location_latest_forecast")
|
||||
|
||||
@patch("weather.views.apps.get_app_config")
|
||||
def test_water_need_api_returns_404_for_missing_farm(self, mock_get_app_config):
|
||||
@@ -150,3 +157,8 @@ class WaterNeedPredictionApiTests(TestCase):
|
||||
|
||||
self.assertEqual(response.status_code, 502)
|
||||
self.assertEqual(response.json()["data"]["error_code"], "invalid_json")
|
||||
|
||||
|
||||
@override_settings(ROOT_URLCONF="soile.urls")
|
||||
class SpatialPolicySmokeTests(TestCase):
|
||||
pass
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from farm_data.services import clone_snapshot_as_runtime_plant, get_primary_plant_snapshot
|
||||
from farm_data.services import build_ai_farm_snapshot, clone_snapshot_as_runtime_plant, get_primary_plant_snapshot, get_ai_snapshot_weather
|
||||
from irrigation.evapotranspiration import calculate_forecast_water_needs, resolve_crop_profile
|
||||
|
||||
from farm_data.models import SensorData
|
||||
@@ -11,7 +11,7 @@ from rag.services import get_water_need_prediction_insight
|
||||
from .services import get_forecast_for_location
|
||||
|
||||
|
||||
def build_water_need_prediction_payload(*, sensor: Any, forecasts: list[Any]) -> dict[str, Any]:
|
||||
def build_water_need_prediction_payload(*, sensor: Any, forecasts: list[Any], ai_snapshot: dict[str, Any] | None = None) -> dict[str, Any]:
|
||||
location = getattr(sensor, "center_location", None)
|
||||
plant = clone_snapshot_as_runtime_plant(get_primary_plant_snapshot(sensor))
|
||||
irrigation_method = getattr(sensor, "irrigation_method", None)
|
||||
@@ -46,6 +46,17 @@ def build_water_need_prediction_payload(*, sensor: Any, forecasts: list[Any]) ->
|
||||
"dailyBreakdown": daily,
|
||||
"cropProfile": crop_profile,
|
||||
"irrigationEfficiencyPercent": efficiency,
|
||||
"source_metadata": {
|
||||
"agronomic_metrics": {
|
||||
"source": "build_ai_farm_snapshot",
|
||||
"policy": "cluster_block_farm_aggregated",
|
||||
"available": bool(ai_snapshot),
|
||||
},
|
||||
"weather": {
|
||||
"source": "center_location_forecast",
|
||||
"policy": "center_location_latest_forecast",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +72,8 @@ class WaterNeedPredictionService:
|
||||
raise ValueError("Farm not found.")
|
||||
|
||||
forecasts = get_forecast_for_location(sensor.center_location, days=7)
|
||||
payload = build_water_need_prediction_payload(sensor=sensor, forecasts=forecasts)
|
||||
ai_snapshot = build_ai_farm_snapshot(farm_uuid)
|
||||
payload = build_water_need_prediction_payload(sensor=sensor, forecasts=forecasts, ai_snapshot=ai_snapshot)
|
||||
insight = get_water_need_prediction_insight(
|
||||
farm_uuid=farm_uuid,
|
||||
prediction_payload=payload,
|
||||
|
||||
Reference in New Issue
Block a user