UPDATE
This commit is contained in:
@@ -470,7 +470,12 @@ def _run_projection_engine(context: GrowthSimulationContext) -> dict[str, Any]:
|
||||
}
|
||||
|
||||
|
||||
def _run_simulation(context: GrowthSimulationContext) -> tuple[dict[str, Any], int | None, str | None]:
|
||||
def _run_simulation(
|
||||
context: GrowthSimulationContext,
|
||||
*,
|
||||
irrigation_recommendation: dict[str, Any] | None = None,
|
||||
fertilization_recommendation: dict[str, Any] | None = None,
|
||||
) -> tuple[dict[str, Any], int | None, str | None]:
|
||||
try:
|
||||
response = CropSimulationService().run_single_simulation(
|
||||
farm_uuid=context.farm_uuid,
|
||||
@@ -480,6 +485,8 @@ def _run_simulation(context: GrowthSimulationContext) -> tuple[dict[str, Any], i
|
||||
crop_parameters=context.crop_parameters,
|
||||
agromanagement=context.agromanagement,
|
||||
site_parameters=context.site_parameters,
|
||||
irrigation_recommendation=irrigation_recommendation,
|
||||
fertilization_recommendation=fertilization_recommendation,
|
||||
name=f"growth:{context.plant_name}",
|
||||
)
|
||||
return response["result"], response.get("scenario_id"), None
|
||||
@@ -619,7 +626,11 @@ def run_growth_simulation(payload: dict[str, Any], progress_callback=None) -> di
|
||||
meta={"current": 1, "total": 3, "message": "simulation input resolved"},
|
||||
)
|
||||
|
||||
simulation_result, scenario_id, simulation_error = _run_simulation(context)
|
||||
simulation_result, scenario_id, simulation_error = _run_simulation(
|
||||
context,
|
||||
irrigation_recommendation=payload.get("irrigation_recommendation"),
|
||||
fertilization_recommendation=payload.get("fertilization_recommendation"),
|
||||
)
|
||||
if progress_callback is not None:
|
||||
progress_callback(
|
||||
state="PROGRESS",
|
||||
@@ -751,7 +762,14 @@ def _build_current_farm_chart_payload(
|
||||
class CurrentFarmChartSimulator:
|
||||
"""سازنده chart وضعیت فعلی مزرعه برای خروجی مستقل از dashboard."""
|
||||
|
||||
def simulate(self, *, farm_uuid: str, plant_name: str | None = None) -> dict[str, Any]:
|
||||
def simulate(
|
||||
self,
|
||||
*,
|
||||
farm_uuid: str,
|
||||
plant_name: str | None = None,
|
||||
irrigation_recommendation: dict[str, Any] | None = None,
|
||||
fertilization_recommendation: dict[str, Any] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
if not farm_uuid:
|
||||
raise GrowthSimulationError("ارسال farm_uuid الزامی است.")
|
||||
|
||||
@@ -777,7 +795,11 @@ class CurrentFarmChartSimulator:
|
||||
"page_size": DEFAULT_PAGE_SIZE,
|
||||
}
|
||||
)
|
||||
simulation_result, scenario_id, simulation_warning = _run_simulation(context)
|
||||
simulation_result, scenario_id, simulation_warning = _run_simulation(
|
||||
context,
|
||||
irrigation_recommendation=irrigation_recommendation,
|
||||
fertilization_recommendation=fertilization_recommendation,
|
||||
)
|
||||
return _build_current_farm_chart_payload(
|
||||
context,
|
||||
simulation_result,
|
||||
|
||||
@@ -46,7 +46,13 @@ def _harvest_description(
|
||||
)
|
||||
|
||||
|
||||
def build_harvest_prediction_payload(*, farm_uuid: str, plant_name: str | None = None) -> dict[str, Any]:
|
||||
def build_harvest_prediction_payload(
|
||||
*,
|
||||
farm_uuid: str,
|
||||
plant_name: str | None = None,
|
||||
irrigation_recommendation: dict[str, Any] | None = None,
|
||||
fertilization_recommendation: dict[str, Any] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
resolved_plant_name = plant_name
|
||||
if not resolved_plant_name:
|
||||
sensor = SensorData.objects.prefetch_related("plants").filter(farm_uuid=farm_uuid).first()
|
||||
@@ -65,7 +71,11 @@ def build_harvest_prediction_payload(*, farm_uuid: str, plant_name: str | None =
|
||||
"page_size": DEFAULT_PAGE_SIZE,
|
||||
}
|
||||
)
|
||||
simulation_result, scenario_id, simulation_warning = _run_simulation(context)
|
||||
simulation_result, scenario_id, simulation_warning = _run_simulation(
|
||||
context,
|
||||
irrigation_recommendation=irrigation_recommendation,
|
||||
fertilization_recommendation=fertilization_recommendation,
|
||||
)
|
||||
daily_output = simulation_result.get("daily_output") or []
|
||||
if not daily_output:
|
||||
raise GrowthSimulationError("هیچ خروجی شبیه سازی در دسترس نیست.")
|
||||
@@ -146,5 +156,17 @@ def build_harvest_prediction_payload(*, farm_uuid: str, plant_name: str | None =
|
||||
|
||||
|
||||
class HarvestPredictionService:
|
||||
def get_harvest_prediction(self, *, farm_uuid: str, plant_name: str | None = None) -> dict[str, Any]:
|
||||
return build_harvest_prediction_payload(farm_uuid=farm_uuid, plant_name=plant_name)
|
||||
def get_harvest_prediction(
|
||||
self,
|
||||
*,
|
||||
farm_uuid: str,
|
||||
plant_name: str | None = None,
|
||||
irrigation_recommendation: dict[str, Any] | None = None,
|
||||
fertilization_recommendation: dict[str, Any] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
return build_harvest_prediction_payload(
|
||||
farm_uuid=farm_uuid,
|
||||
plant_name=plant_name,
|
||||
irrigation_recommendation=irrigation_recommendation,
|
||||
fertilization_recommendation=fertilization_recommendation,
|
||||
)
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
|
||||
class QueryJSONField(serializers.JSONField):
|
||||
def to_internal_value(self, data):
|
||||
if isinstance(data, str):
|
||||
data = data.strip()
|
||||
if not data:
|
||||
return None
|
||||
try:
|
||||
data = json.loads(data)
|
||||
except json.JSONDecodeError as exc:
|
||||
raise serializers.ValidationError("فرمت JSON نامعتبر است.") from exc
|
||||
return super().to_internal_value(data)
|
||||
|
||||
|
||||
class GrowthSimulationRequestSerializer(serializers.Serializer):
|
||||
plant_name = serializers.CharField(help_text="نام گیاه")
|
||||
dynamic_parameters = serializers.ListField(
|
||||
@@ -16,6 +31,8 @@ class GrowthSimulationRequestSerializer(serializers.Serializer):
|
||||
site_parameters = serializers.JSONField(required=False)
|
||||
crop_parameters = serializers.JSONField(required=False)
|
||||
agromanagement = serializers.JSONField(required=False)
|
||||
irrigation_recommendation = serializers.JSONField(required=False)
|
||||
fertilization_recommendation = serializers.JSONField(required=False)
|
||||
page_size = serializers.IntegerField(required=False, min_value=1, max_value=50)
|
||||
|
||||
def validate(self, attrs):
|
||||
@@ -78,6 +95,8 @@ class GrowthSimulationResultSerializer(serializers.Serializer):
|
||||
class CurrentFarmChartRequestSerializer(serializers.Serializer):
|
||||
farm_uuid = serializers.UUIDField(help_text="شناسه یکتای مزرعه")
|
||||
plant_name = serializers.CharField(required=False, allow_blank=True, help_text="نام گیاه")
|
||||
irrigation_recommendation = serializers.JSONField(required=False)
|
||||
fertilization_recommendation = serializers.JSONField(required=False)
|
||||
|
||||
|
||||
class CurrentFarmChartResponseSerializer(serializers.Serializer):
|
||||
@@ -98,6 +117,8 @@ class CurrentFarmChartResponseSerializer(serializers.Serializer):
|
||||
class HarvestPredictionRequestSerializer(serializers.Serializer):
|
||||
farm_uuid = serializers.UUIDField(help_text="شناسه یکتای مزرعه")
|
||||
plant_name = serializers.CharField(required=False, allow_blank=True, help_text="نام گیاه")
|
||||
irrigation_recommendation = serializers.JSONField(required=False)
|
||||
fertilization_recommendation = serializers.JSONField(required=False)
|
||||
|
||||
|
||||
class HarvestPredictionResponseSerializer(serializers.Serializer):
|
||||
@@ -113,6 +134,8 @@ class HarvestPredictionResponseSerializer(serializers.Serializer):
|
||||
class YieldPredictionRequestSerializer(serializers.Serializer):
|
||||
farm_uuid = serializers.UUIDField(help_text="شناسه یکتای مزرعه")
|
||||
plant_name = serializers.CharField(required=False, allow_blank=True, help_text="نام گیاه")
|
||||
irrigation_recommendation = serializers.JSONField(required=False)
|
||||
fertilization_recommendation = serializers.JSONField(required=False)
|
||||
|
||||
|
||||
class YieldPredictionResponseSerializer(serializers.Serializer):
|
||||
@@ -138,6 +161,16 @@ class YieldHarvestSummaryQuerySerializer(serializers.Serializer):
|
||||
default=False,
|
||||
help_text="در صورت true بودن، بخش روایت نیز در آینده اضافه می شود.",
|
||||
)
|
||||
irrigation_recommendation = QueryJSONField(
|
||||
required=False,
|
||||
allow_null=True,
|
||||
help_text="برنامه آبیاری به صورت JSON برای تزریق به PCSE.",
|
||||
)
|
||||
fertilization_recommendation = QueryJSONField(
|
||||
required=False,
|
||||
allow_null=True,
|
||||
help_text="برنامه کودهی به صورت JSON برای تزریق به PCSE.",
|
||||
)
|
||||
|
||||
|
||||
class YieldHarvestSummaryResponseSerializer(serializers.Serializer):
|
||||
|
||||
@@ -76,6 +76,10 @@ class PlantGrowthSimulationApiTests(TestCase):
|
||||
"weather": self.weather,
|
||||
"soil_parameters": {"SMFCF": 0.34, "SMW": 0.14, "RDMSOL": 120.0},
|
||||
"site_parameters": {"WAV": 40.0},
|
||||
"irrigation_recommendation": {"events": [{"date": "2026-04-02", "amount": 2.5}]},
|
||||
"fertilization_recommendation": {
|
||||
"events": [{"date": "2026-04-02", "N_amount": 45, "N_recovery": 0.7}]
|
||||
},
|
||||
"page_size": 2,
|
||||
}
|
||||
)
|
||||
@@ -95,12 +99,17 @@ class PlantGrowthSimulationApiTests(TestCase):
|
||||
"plant_name": self.plant.name,
|
||||
"dynamic_parameters": ["DVS", "LAI"],
|
||||
"weather": self.weather,
|
||||
"irrigation_recommendation": {"events": [{"date": "2026-04-02", "amount": 2.5}]},
|
||||
"fertilization_recommendation": {
|
||||
"events": [{"date": "2026-04-02", "N_amount": 45, "N_recovery": 0.7}]
|
||||
},
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 202)
|
||||
self.assertEqual(response.json()["data"]["task_id"], "growth-task-1")
|
||||
self.assertEqual(mock_delay.call_args.args[0]["irrigation_recommendation"]["events"][0]["amount"], 2.5)
|
||||
|
||||
def test_queue_api_returns_400_for_missing_weather_and_farm_uuid(self):
|
||||
response = self.client.post(
|
||||
@@ -438,6 +447,8 @@ class PlantGrowthSimulationApiTests(TestCase):
|
||||
response = self.client.get(
|
||||
"/yield-harvest-summary/?farm_uuid=550e8400-e29b-41d4-a716-446655440000"
|
||||
"&season_year=1404&crop_name=wheat&include_narrative=true"
|
||||
"&irrigation_recommendation=%7B%22events%22%3A%5B%7B%22date%22%3A%222026-04-25%22%2C%22amount%22%3A2.5%7D%5D%7D"
|
||||
"&fertilization_recommendation=%7B%22events%22%3A%5B%7B%22date%22%3A%222026-04-20%22%2C%22N_amount%22%3A45%2C%22N_recovery%22%3A0.7%7D%5D%7D"
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
@@ -449,6 +460,23 @@ class PlantGrowthSimulationApiTests(TestCase):
|
||||
season_year="1404",
|
||||
crop_name="wheat",
|
||||
include_narrative=True,
|
||||
irrigation_recommendation={
|
||||
"events": [
|
||||
{
|
||||
"date": "2026-04-25",
|
||||
"amount": 2.5,
|
||||
}
|
||||
]
|
||||
},
|
||||
fertilization_recommendation={
|
||||
"events": [
|
||||
{
|
||||
"date": "2026-04-20",
|
||||
"N_amount": 45,
|
||||
"N_recovery": 0.7,
|
||||
}
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
def test_yield_harvest_summary_api_returns_400_for_missing_farm_uuid(self):
|
||||
@@ -456,3 +484,12 @@ class PlantGrowthSimulationApiTests(TestCase):
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(response.json()["code"], 400)
|
||||
|
||||
def test_yield_harvest_summary_api_returns_400_for_invalid_json_recommendations(self):
|
||||
response = self.client.get(
|
||||
"/yield-harvest-summary/?farm_uuid=550e8400-e29b-41d4-a716-446655440000"
|
||||
"&irrigation_recommendation=%7Binvalid-json%7D"
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(response.json()["code"], 400)
|
||||
|
||||
@@ -110,6 +110,10 @@ class PlantGrowthSimulationView(APIView):
|
||||
],
|
||||
"soil_parameters": {"SMFCF": 0.34, "SMW": 0.14, "RDMSOL": 120.0},
|
||||
"site_parameters": {"WAV": 40.0},
|
||||
"irrigation_recommendation": {"events": [{"date": "2026-04-02", "amount": 2.5}]},
|
||||
"fertilization_recommendation": {
|
||||
"events": [{"date": "2026-04-02", "N_amount": 45, "N_recovery": 0.7}]
|
||||
},
|
||||
"page_size": 2,
|
||||
},
|
||||
request_only=True,
|
||||
@@ -120,6 +124,10 @@ class PlantGrowthSimulationView(APIView):
|
||||
"plant_name": "گوجهفرنگی",
|
||||
"dynamic_parameters": ["DVS", "LAI", "TAGP"],
|
||||
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"irrigation_recommendation": {"events": [{"date": "2026-04-25", "amount": 2.5}]},
|
||||
"fertilization_recommendation": {
|
||||
"events": [{"date": "2026-04-20", "N_amount": 45, "N_recovery": 0.7}]
|
||||
},
|
||||
},
|
||||
request_only=True,
|
||||
),
|
||||
@@ -246,6 +254,10 @@ class CurrentFarmSimulationChartView(APIView):
|
||||
value={
|
||||
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"plant_name": "گوجهفرنگی",
|
||||
"irrigation_recommendation": {"events": [{"date": "2026-04-25", "amount": 2.5}]},
|
||||
"fertilization_recommendation": {
|
||||
"events": [{"date": "2026-04-20", "N_amount": 45, "N_recovery": 0.7}]
|
||||
},
|
||||
},
|
||||
request_only=True,
|
||||
),
|
||||
@@ -303,6 +315,10 @@ class HarvestPredictionView(APIView):
|
||||
value={
|
||||
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"plant_name": "گوجهفرنگی",
|
||||
"irrigation_recommendation": {"events": [{"date": "2026-04-25", "amount": 2.5}]},
|
||||
"fertilization_recommendation": {
|
||||
"events": [{"date": "2026-04-20", "N_amount": 45, "N_recovery": 0.7}]
|
||||
},
|
||||
},
|
||||
request_only=True,
|
||||
),
|
||||
@@ -348,6 +364,10 @@ class YieldPredictionView(APIView):
|
||||
value={
|
||||
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"plant_name": "گوجهفرنگی",
|
||||
"irrigation_recommendation": {"events": [{"date": "2026-04-25", "amount": 2.5}]},
|
||||
"fertilization_recommendation": {
|
||||
"events": [{"date": "2026-04-20", "N_amount": 45, "N_recovery": 0.7}]
|
||||
},
|
||||
},
|
||||
request_only=True,
|
||||
),
|
||||
@@ -408,6 +428,20 @@ class YieldHarvestSummaryView(APIView):
|
||||
required=False,
|
||||
description="در آینده روایت متنی را نیز اضافه می کند.",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="irrigation_recommendation",
|
||||
type=str,
|
||||
location=OpenApiParameter.QUERY,
|
||||
required=False,
|
||||
description="JSON برنامه آبیاری برای تزریق به شبیه سازی PCSE.",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="fertilization_recommendation",
|
||||
type=str,
|
||||
location=OpenApiParameter.QUERY,
|
||||
required=False,
|
||||
description="JSON برنامه کودهی برای تزریق به شبیه سازی PCSE.",
|
||||
),
|
||||
],
|
||||
responses={
|
||||
200: build_response(
|
||||
@@ -455,5 +489,7 @@ class YieldHarvestSummaryView(APIView):
|
||||
season_year=str(validated.get("season_year") or ""),
|
||||
crop_name=validated.get("crop_name") or "",
|
||||
include_narrative=validated.get("include_narrative", False),
|
||||
irrigation_recommendation=validated.get("irrigation_recommendation"),
|
||||
fertilization_recommendation=validated.get("fertilization_recommendation"),
|
||||
)
|
||||
return Response({"code": 200, "msg": "موفق", "data": payload}, status=status.HTTP_200_OK)
|
||||
|
||||
@@ -103,7 +103,14 @@ class WaterStressSimulationService:
|
||||
raise GrowthSimulationError("Plant not found for the selected farm.")
|
||||
return plant.name
|
||||
|
||||
def get_water_stress(self, *, farm_uuid: str, plant_name: str | None = None) -> dict[str, Any]:
|
||||
def get_water_stress(
|
||||
self,
|
||||
*,
|
||||
farm_uuid: str,
|
||||
plant_name: str | None = None,
|
||||
irrigation_recommendation: dict[str, Any] | None = None,
|
||||
fertilization_recommendation: dict[str, Any] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
resolved_plant_name = self._resolve_plant_name(farm_uuid=farm_uuid, plant_name=plant_name)
|
||||
context = build_growth_context(
|
||||
{
|
||||
@@ -111,7 +118,11 @@ class WaterStressSimulationService:
|
||||
"plant_name": resolved_plant_name,
|
||||
}
|
||||
)
|
||||
simulation_result, _scenario_id, simulation_warning = _run_simulation(context)
|
||||
simulation_result, _scenario_id, simulation_warning = _run_simulation(
|
||||
context,
|
||||
irrigation_recommendation=irrigation_recommendation,
|
||||
fertilization_recommendation=fertilization_recommendation,
|
||||
)
|
||||
daily_output = simulation_result.get("daily_output") or []
|
||||
if not daily_output:
|
||||
raise GrowthSimulationError("Water stress simulation produced no daily output.")
|
||||
|
||||
@@ -32,16 +32,22 @@ class YieldHarvestSummaryService:
|
||||
season_year: str,
|
||||
crop_name: str,
|
||||
include_narrative: bool = True,
|
||||
irrigation_recommendation: dict[str, Any] | None = None,
|
||||
fertilization_recommendation: dict[str, Any] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
farm_context = self._get_farm_context(farm_uuid)
|
||||
farm_context["season_year"] = season_year
|
||||
farm_context["crop_name"] = crop_name or farm_context.get("crop_name") or ""
|
||||
farm_context["irrigation_recommendation"] = irrigation_recommendation or {}
|
||||
farm_context["fertilization_recommendation"] = fertilization_recommendation or {}
|
||||
yield_prediction = self._build_yield_prediction(
|
||||
farm_uuid=farm_uuid,
|
||||
season_year=season_year,
|
||||
crop_name=crop_name,
|
||||
include_narrative=include_narrative,
|
||||
farm_context=farm_context,
|
||||
irrigation_recommendation=irrigation_recommendation,
|
||||
fertilization_recommendation=fertilization_recommendation,
|
||||
)
|
||||
harvest_prediction_card = self._build_harvest_prediction_card(
|
||||
farm_uuid=farm_uuid,
|
||||
@@ -49,6 +55,8 @@ class YieldHarvestSummaryService:
|
||||
crop_name=crop_name,
|
||||
include_narrative=include_narrative,
|
||||
farm_context=farm_context,
|
||||
irrigation_recommendation=irrigation_recommendation,
|
||||
fertilization_recommendation=fertilization_recommendation,
|
||||
)
|
||||
harvest_readiness_zones = self._build_harvest_readiness_zones(
|
||||
farm_uuid=farm_uuid,
|
||||
@@ -75,6 +83,8 @@ class YieldHarvestSummaryService:
|
||||
crop_name=crop_name,
|
||||
include_narrative=include_narrative,
|
||||
farm_context=farm_context,
|
||||
irrigation_recommendation=irrigation_recommendation,
|
||||
fertilization_recommendation=fertilization_recommendation,
|
||||
)
|
||||
season_highlights_card = self._build_season_highlights_card(
|
||||
farm_uuid=farm_uuid,
|
||||
@@ -126,11 +136,15 @@ class YieldHarvestSummaryService:
|
||||
crop_name: str,
|
||||
include_narrative: bool,
|
||||
farm_context: dict[str, Any],
|
||||
irrigation_recommendation: dict[str, Any] | None = None,
|
||||
fertilization_recommendation: dict[str, Any] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
service = apps.get_app_config("crop_simulation").get_yield_prediction_service()
|
||||
result = service.get_yield_prediction(
|
||||
farm_uuid=farm_uuid,
|
||||
plant_name=crop_name or None,
|
||||
irrigation_recommendation=irrigation_recommendation,
|
||||
fertilization_recommendation=fertilization_recommendation,
|
||||
)
|
||||
supporting_metrics = dict(result.get("supportingMetrics") or {})
|
||||
|
||||
@@ -173,11 +187,15 @@ class YieldHarvestSummaryService:
|
||||
crop_name: str,
|
||||
include_narrative: bool,
|
||||
farm_context: dict[str, Any],
|
||||
irrigation_recommendation: dict[str, Any] | None = None,
|
||||
fertilization_recommendation: dict[str, Any] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
service = apps.get_app_config("crop_simulation").get_harvest_prediction_service()
|
||||
result = service.get_harvest_prediction(
|
||||
farm_uuid=farm_uuid,
|
||||
plant_name=crop_name or None,
|
||||
irrigation_recommendation=irrigation_recommendation,
|
||||
fertilization_recommendation=fertilization_recommendation,
|
||||
)
|
||||
|
||||
fallback_description = (
|
||||
@@ -211,11 +229,15 @@ class YieldHarvestSummaryService:
|
||||
crop_name: str,
|
||||
include_narrative: bool,
|
||||
farm_context: dict[str, Any],
|
||||
irrigation_recommendation: dict[str, Any] | None = None,
|
||||
fertilization_recommendation: dict[str, Any] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
simulator = apps.get_app_config("crop_simulation").get_current_farm_chart_simulator()
|
||||
result = simulator.simulate(
|
||||
farm_uuid=farm_uuid,
|
||||
plant_name=crop_name or None,
|
||||
irrigation_recommendation=irrigation_recommendation,
|
||||
fertilization_recommendation=fertilization_recommendation,
|
||||
)
|
||||
pcse_timeseries = list(result.get("daily_output") or [])
|
||||
|
||||
|
||||
@@ -10,9 +10,20 @@ from .growth_simulation import (
|
||||
)
|
||||
|
||||
|
||||
def build_yield_prediction_payload(*, farm_uuid: str, plant_name: str | None = None) -> dict[str, Any]:
|
||||
def build_yield_prediction_payload(
|
||||
*,
|
||||
farm_uuid: str,
|
||||
plant_name: str | None = None,
|
||||
irrigation_recommendation: dict[str, Any] | None = None,
|
||||
fertilization_recommendation: dict[str, Any] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
simulator = CurrentFarmChartSimulator()
|
||||
result = simulator.simulate(farm_uuid=farm_uuid, plant_name=plant_name)
|
||||
result = simulator.simulate(
|
||||
farm_uuid=farm_uuid,
|
||||
plant_name=plant_name,
|
||||
irrigation_recommendation=irrigation_recommendation,
|
||||
fertilization_recommendation=fertilization_recommendation,
|
||||
)
|
||||
yield_estimate = float((result.get("metrics") or {}).get("yield_estimate") or 0.0)
|
||||
predicted_yield_tons = round(max(yield_estimate / 1000.0, 0.0), 2)
|
||||
return {
|
||||
@@ -31,8 +42,20 @@ def build_yield_prediction_payload(*, farm_uuid: str, plant_name: str | None = N
|
||||
|
||||
|
||||
class YieldPredictionService:
|
||||
def get_yield_prediction(self, *, farm_uuid: str, plant_name: str | None = None) -> dict[str, Any]:
|
||||
def get_yield_prediction(
|
||||
self,
|
||||
*,
|
||||
farm_uuid: str,
|
||||
plant_name: str | None = None,
|
||||
irrigation_recommendation: dict[str, Any] | None = None,
|
||||
fertilization_recommendation: dict[str, Any] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
try:
|
||||
return build_yield_prediction_payload(farm_uuid=farm_uuid, plant_name=plant_name)
|
||||
return build_yield_prediction_payload(
|
||||
farm_uuid=farm_uuid,
|
||||
plant_name=plant_name,
|
||||
irrigation_recommendation=irrigation_recommendation,
|
||||
fertilization_recommendation=fertilization_recommendation,
|
||||
)
|
||||
except GrowthSimulationError:
|
||||
raise
|
||||
|
||||
Reference in New Issue
Block a user