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 -26
View File
@@ -6,7 +6,7 @@ from statistics import mean
from typing import Any
from django.apps import apps
from location_data.satellite_snapshot import build_location_satellite_snapshot
from farm_data.services import get_ai_snapshot_metric
from crop_simulation.services import CropSimulationService
@@ -47,20 +47,9 @@ def _first_not_none(*values: Any) -> Any:
return None
def _sensor_metric(sensor: Any, metric: str) -> float | None:
if sensor is None:
return None
if hasattr(sensor, metric):
value = getattr(sensor, metric)
return _safe_float(value, default=0.0) if value is not None else None
payload = getattr(sensor, "sensor_payload", None) or {}
if not isinstance(payload, dict):
return None
for block in payload.values():
if isinstance(block, dict) and block.get(metric) is not None:
return _safe_float(block.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 _parse_temperature_range(plant: Any) -> tuple[float, float]:
@@ -140,15 +129,9 @@ def _build_weather_records(forecasts: list[Any], *, latitude: float, longitude:
return records
def _build_soil_parameters(sensor: Any) -> tuple[dict[str, Any], dict[str, Any]]:
moisture_pct = _sensor_metric(sensor, "soil_moisture")
center_location = getattr(sensor, "center_location", None)
satellite_metrics = (
build_location_satellite_snapshot(center_location).get("resolved_metrics") or {}
if center_location is not None
else {}
)
ndwi = _safe_float(satellite_metrics.get("ndwi"), 0.34)
def _build_soil_parameters(sensor: Any, ai_snapshot: dict[str, Any] | None = None) -> tuple[dict[str, Any], dict[str, Any]]:
moisture_pct = _aggregated_metric(ai_snapshot, "soil_moisture")
ndwi = _safe_float(_aggregated_metric(ai_snapshot, "ndwi"), 0.34)
wv0033 = ndwi if ndwi > 0 else 0.34
wv1500 = max(round(wv0033 * 0.45, 3), 0.14)
@@ -288,6 +271,7 @@ class SimulationRecommendationOptimizer:
daily_water_needs: list[dict[str, Any]],
growth_stage: str | None,
irrigation_method: Any | None,
ai_snapshot: dict[str, Any] | None = None,
) -> dict[str, Any] | None:
if sensor is None or plant is None or not forecasts:
return None
@@ -301,6 +285,7 @@ class SimulationRecommendationOptimizer:
daily_water_needs=daily_water_needs,
growth_stage=growth_stage,
crop_blueprint=crop_blueprint,
ai_snapshot=ai_snapshot,
)
if pcse_result is not None:
return pcse_result
@@ -312,6 +297,7 @@ class SimulationRecommendationOptimizer:
daily_water_needs=daily_water_needs,
growth_stage=growth_stage,
irrigation_method=irrigation_method,
ai_snapshot=ai_snapshot,
)
def optimize_fertilization(
@@ -321,6 +307,7 @@ class SimulationRecommendationOptimizer:
plant: Any,
forecasts: list[Any],
growth_stage: str | None,
ai_snapshot: dict[str, Any] | None = None,
) -> dict[str, Any] | None:
if sensor is None or plant is None:
return None
@@ -333,6 +320,7 @@ class SimulationRecommendationOptimizer:
forecasts=forecasts,
growth_stage=growth_stage,
crop_blueprint=crop_blueprint,
ai_snapshot=ai_snapshot,
)
if pcse_result is not None:
return pcse_result
@@ -342,6 +330,7 @@ class SimulationRecommendationOptimizer:
plant=plant,
forecasts=forecasts,
growth_stage=growth_stage,
ai_snapshot=ai_snapshot,
)
def _optimize_irrigation_with_pcse(
@@ -353,10 +342,11 @@ class SimulationRecommendationOptimizer:
daily_water_needs: list[dict[str, Any]],
growth_stage: str | None,
crop_blueprint: tuple[dict[str, Any], list[dict[str, Any]]],
ai_snapshot: dict[str, Any] | None = None,
) -> dict[str, Any] | None:
defaults = apps.get_app_config("irrigation").get_optimizer_defaults()
crop_parameters, agromanagement = crop_blueprint
soil, site = _build_soil_parameters(sensor)
soil, site = _build_soil_parameters(sensor, ai_snapshot=ai_snapshot)
weather = _build_weather_records(
forecasts,
latitude=_safe_float(sensor.center_location.latitude),
@@ -572,10 +562,11 @@ class SimulationRecommendationOptimizer:
forecasts: list[Any],
growth_stage: str | None,
crop_blueprint: tuple[dict[str, Any], list[dict[str, Any]]],
ai_snapshot: dict[str, Any] | None = None,
) -> dict[str, Any] | None:
defaults = apps.get_app_config("fertilization").get_optimizer_defaults()
crop_parameters, agromanagement = crop_blueprint
soil, site = _build_soil_parameters(sensor)
soil, site = _build_soil_parameters(sensor, ai_snapshot=ai_snapshot)
weather = _build_weather_records(
forecasts,
latitude=_safe_float(sensor.center_location.latitude),