This commit is contained in:
2026-05-09 16:55:06 +03:30
parent 1679825ae2
commit cead7dafe2
51 changed files with 7514 additions and 1221 deletions
+6 -9
View File
@@ -11,6 +11,7 @@ from django.core.paginator import EmptyPage, Paginator
from farm_data.models import SensorData
from farm_data.services import get_canonical_farm_record, get_runtime_plant_for_farm
from location_data.satellite_snapshot import build_location_satellite_snapshot
from plant.gdd import calculate_daily_gdd, resolve_growth_profile
from weather.models import WeatherForecast
@@ -188,14 +189,11 @@ def _build_weather_from_farm(sensor: SensorData) -> list[dict[str, Any]]:
def _build_soil_and_site_from_farm(sensor: SensorData) -> tuple[dict[str, Any], dict[str, Any]]:
depths = list(sensor.center_location.depths.all())
top_depth = depths[0] if depths else None
smfcf = _safe_float(getattr(top_depth, "wv0033", None), 0.34)
smw = _safe_float(getattr(top_depth, "wv1500", None), 0.14)
sm0 = _safe_float(
_pick_first_not_none(getattr(top_depth, "porosity", None), getattr(top_depth, "wv0000", None)),
min(max(smfcf + 0.08, smw + 0.12), 0.6),
)
satellite_metrics = build_location_satellite_snapshot(sensor.center_location).get("resolved_metrics") or {}
ndwi = _safe_float(satellite_metrics.get("ndwi"), 0.28)
smfcf = _safe_float(ndwi, 0.34)
smw = max(round(smfcf * 0.45, 3), 0.12)
sm0 = min(max(smfcf + 0.08, smw + 0.12), 0.6)
soil_moisture = None
payload = sensor.sensor_payload or {}
if isinstance(payload, dict):
@@ -292,7 +290,6 @@ def build_growth_context(payload: dict[str, Any]) -> GrowthSimulationContext:
if payload.get("farm_uuid"):
sensor = (
SensorData.objects.select_related("center_location")
.prefetch_related("center_location__depths")
.filter(farm_uuid=payload["farm_uuid"])
.first()
)
+9 -7
View File
@@ -6,6 +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 crop_simulation.services import CropSimulationService
@@ -141,14 +142,15 @@ def _build_weather_records(forecasts: list[Any], *, latitude: float, longitude:
def _build_soil_parameters(sensor: Any) -> tuple[dict[str, Any], dict[str, Any]]:
moisture_pct = _sensor_metric(sensor, "soil_moisture")
depths = []
center_location = getattr(sensor, "center_location", None)
if center_location is not None:
depths = list(center_location.depths.all())
top_depth = depths[0] if depths else None
wv0033 = _safe_float(getattr(top_depth, "wv0033", None), 0.34)
wv1500 = _safe_float(getattr(top_depth, "wv1500", None), 0.14)
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)
wv0033 = ndwi if ndwi > 0 else 0.34
wv1500 = max(round(wv0033 * 0.45, 3), 0.14)
smfcf = _clamp(wv0033 if wv0033 > 0 else 0.34, 0.2, 0.55)
smw = _clamp(wv1500 if wv1500 > 0 else 0.12, 0.05, smfcf - 0.02)
+13 -15
View File
@@ -7,6 +7,7 @@ from datetime import date, datetime, timedelta
from typing import Any
from django.db import transaction
from location_data.satellite_snapshot import build_location_satellite_snapshot
from .models import SimulationRun, SimulationScenario
@@ -475,15 +476,12 @@ def build_simulation_payload_from_farm(
longitude=float(farm.center_location.longitude),
)
depths = list(farm.center_location.depths.all())
top_depth = depths[0] if depths else None
smfcf = _clamp(_safe_float(getattr(top_depth, "wv0033", None), 0.34), 0.2, 0.55)
smw = _clamp(_safe_float(getattr(top_depth, "wv1500", None), 0.14), 0.05, max(smfcf - 0.02, 0.06))
satellite_metrics = build_location_satellite_snapshot(farm.center_location).get("resolved_metrics") or {}
ndwi = _safe_float(satellite_metrics.get("ndwi"), 0.28)
smfcf = _clamp(ndwi if ndwi is not None else 0.34, 0.2, 0.55)
smw = _clamp(smfcf * 0.45, 0.05, max(smfcf - 0.02, 0.06))
sm0 = _clamp(
_safe_float(
_pick_first_not_none(getattr(top_depth, "porosity", None), getattr(top_depth, "wv0000", None)),
min(max(smfcf + 0.08, smw + 0.12), 0.6),
),
min(max(smfcf + 0.08, smw + 0.12), 0.6),
max(smfcf + 0.02, smw + 0.05),
0.8,
)
@@ -493,10 +491,10 @@ def build_simulation_payload_from_farm(
if soil_moisture is not None
else DEFAULT_WAV
)
nitrogen = _pick_first_not_none(_sensor_metric(farm, "nitrogen"), getattr(top_depth, "nitrogen", None))
nitrogen = _pick_first_not_none(_sensor_metric(farm, "nitrogen"), satellite_metrics.get("soil_vv_db"))
phosphorus = _sensor_metric(farm, "phosphorus")
potassium = _sensor_metric(farm, "potassium")
soil_ph = _pick_first_not_none(_sensor_metric(farm, "soil_ph"), getattr(top_depth, "phh2o", None))
soil_ph = _pick_first_not_none(_sensor_metric(farm, "soil_ph"), None)
ec = _sensor_metric(farm, "electrical_conductivity")
resolved_soil = {
@@ -513,11 +511,11 @@ def build_simulation_payload_from_farm(
"potassium": _safe_float(potassium, 0.0),
"soil_ph": _safe_float(soil_ph, 7.0),
"electrical_conductivity": _safe_float(ec, 0.0),
"clay": _safe_float(getattr(top_depth, "clay", None), 0.0),
"sand": _safe_float(getattr(top_depth, "sand", None), 0.0),
"silt": _safe_float(getattr(top_depth, "silt", None), 0.0),
"cec": _safe_float(getattr(top_depth, "cec", None), 0.0),
"soc": _safe_float(getattr(top_depth, "soc", None), 0.0),
"clay": 0.0,
"sand": 0.0,
"silt": 0.0,
"cec": 0.0,
"soc": 0.0,
}
if soil:
resolved_soil.update(soil)
+1 -1
View File
@@ -708,7 +708,7 @@ class YieldHarvestSummaryService:
) -> dict[str, Any]:
farm = (
SensorData.objects.select_related("center_location", "weather_forecast")
.prefetch_related("center_location__depths", "plant_assignments__plant")
.prefetch_related("plant_assignments__plant")
.filter(farm_uuid=farm_uuid)
.first()
)