2026-04-24 18:34:17 +03:30
|
|
|
from __future__ import annotations
|
|
|
|
|
|
2026-04-25 17:22:41 +03:30
|
|
|
from django.apps import apps
|
|
|
|
|
|
2026-04-30 00:53:47 +03:30
|
|
|
from drf_spectacular.utils import OpenApiExample, OpenApiParameter, extend_schema
|
2026-04-24 18:34:17 +03:30
|
|
|
from rest_framework import status
|
|
|
|
|
from rest_framework.response import Response
|
|
|
|
|
from rest_framework.views import APIView
|
|
|
|
|
|
|
|
|
|
from config.openapi import (
|
|
|
|
|
build_envelope_serializer,
|
|
|
|
|
build_response,
|
|
|
|
|
build_task_status_data_serializer,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
from .growth_simulation import MAX_PAGE_SIZE, paginate_growth_stages
|
|
|
|
|
from .serializers import (
|
2026-04-25 17:22:41 +03:30
|
|
|
CurrentFarmChartRequestSerializer,
|
|
|
|
|
CurrentFarmChartResponseSerializer,
|
2026-04-24 18:34:17 +03:30
|
|
|
GrowthSimulationQueuedSerializer,
|
|
|
|
|
GrowthSimulationRequestSerializer,
|
|
|
|
|
GrowthSimulationResultSerializer,
|
2026-04-25 17:22:41 +03:30
|
|
|
HarvestPredictionRequestSerializer,
|
|
|
|
|
HarvestPredictionResponseSerializer,
|
2026-04-30 00:53:47 +03:30
|
|
|
YieldHarvestSummaryQuerySerializer,
|
|
|
|
|
YieldHarvestSummaryResponseSerializer,
|
2026-04-25 17:22:41 +03:30
|
|
|
YieldPredictionRequestSerializer,
|
|
|
|
|
YieldPredictionResponseSerializer,
|
2026-04-24 18:34:17 +03:30
|
|
|
)
|
|
|
|
|
from .tasks import run_growth_simulation_task
|
2026-04-30 00:53:47 +03:30
|
|
|
from .yield_harvest_summary import YieldHarvestSummaryService
|
2026-04-24 18:34:17 +03:30
|
|
|
|
|
|
|
|
|
|
|
|
|
GrowthSimulationQueuedResponseSerializer = build_envelope_serializer(
|
|
|
|
|
"GrowthSimulationQueuedResponseSerializer",
|
|
|
|
|
GrowthSimulationQueuedSerializer,
|
|
|
|
|
)
|
|
|
|
|
GrowthSimulationStatusResponseSerializer = build_envelope_serializer(
|
|
|
|
|
"GrowthSimulationStatusResponseSerializer",
|
|
|
|
|
build_task_status_data_serializer(
|
|
|
|
|
"GrowthSimulationTaskStatusDataSerializer",
|
|
|
|
|
GrowthSimulationResultSerializer,
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
GrowthSimulationErrorSerializer = build_envelope_serializer(
|
|
|
|
|
"GrowthSimulationErrorSerializer",
|
|
|
|
|
data_required=False,
|
|
|
|
|
allow_null=True,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _get_async_result(task_id: str):
|
|
|
|
|
from celery.result import AsyncResult
|
|
|
|
|
|
|
|
|
|
return AsyncResult(task_id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _coerce_positive_int(value, default: int) -> int:
|
|
|
|
|
try:
|
|
|
|
|
parsed = int(value)
|
|
|
|
|
except (TypeError, ValueError):
|
|
|
|
|
return default
|
|
|
|
|
return max(parsed, 1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PlantGrowthSimulationView(APIView):
|
|
|
|
|
@extend_schema(
|
|
|
|
|
tags=["Crop Simulation"],
|
|
|
|
|
summary="شروع شبیه سازی رشد گیاه",
|
|
|
|
|
description=(
|
|
|
|
|
"نوع گیاه و پارامترهای متغیر رشد را می گیرد، "
|
|
|
|
|
"شبیه سازی را داخل Celery اجرا می کند و فقط task_id برمی گرداند."
|
|
|
|
|
),
|
|
|
|
|
request=GrowthSimulationRequestSerializer,
|
|
|
|
|
responses={
|
|
|
|
|
202: build_response(
|
|
|
|
|
GrowthSimulationQueuedResponseSerializer,
|
|
|
|
|
"تسک شبیه سازی رشد گیاه در صف قرار گرفت.",
|
|
|
|
|
),
|
|
|
|
|
400: build_response(
|
|
|
|
|
GrowthSimulationErrorSerializer,
|
|
|
|
|
"داده ورودی نامعتبر است.",
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
examples=[
|
|
|
|
|
OpenApiExample(
|
|
|
|
|
"نمونه درخواست با weather مستقیم",
|
|
|
|
|
value={
|
|
|
|
|
"plant_name": "گوجهفرنگی",
|
|
|
|
|
"dynamic_parameters": ["DVS", "LAI", "TAGP", "TWSO", "SM"],
|
|
|
|
|
"weather": [
|
|
|
|
|
{
|
|
|
|
|
"DAY": "2026-04-01",
|
|
|
|
|
"LAT": 35.7,
|
|
|
|
|
"LON": 51.4,
|
|
|
|
|
"TMIN": 12,
|
|
|
|
|
"TMAX": 24,
|
|
|
|
|
"RAIN": 0.0,
|
|
|
|
|
"ET0": 0.32,
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"soil_parameters": {"SMFCF": 0.34, "SMW": 0.14, "RDMSOL": 120.0},
|
|
|
|
|
"site_parameters": {"WAV": 40.0},
|
|
|
|
|
"page_size": 2,
|
|
|
|
|
},
|
|
|
|
|
request_only=True,
|
|
|
|
|
),
|
|
|
|
|
OpenApiExample(
|
|
|
|
|
"نمونه درخواست با farm",
|
|
|
|
|
value={
|
|
|
|
|
"plant_name": "گوجهفرنگی",
|
|
|
|
|
"dynamic_parameters": ["DVS", "LAI", "TAGP"],
|
2026-04-25 17:22:41 +03:30
|
|
|
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
2026-04-24 18:34:17 +03:30
|
|
|
},
|
|
|
|
|
request_only=True,
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
def post(self, request):
|
|
|
|
|
serializer = GrowthSimulationRequestSerializer(data=request.data)
|
|
|
|
|
if not serializer.is_valid():
|
|
|
|
|
return Response(
|
|
|
|
|
{"code": 400, "msg": "داده نامعتبر.", "data": serializer.errors},
|
|
|
|
|
status=status.HTTP_400_BAD_REQUEST,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
task = run_growth_simulation_task.delay(serializer.validated_data)
|
|
|
|
|
return Response(
|
|
|
|
|
{
|
|
|
|
|
"code": 202,
|
|
|
|
|
"msg": "تسک شبیه سازی رشد در صف قرار گرفت.",
|
|
|
|
|
"data": {
|
|
|
|
|
"task_id": task.id,
|
|
|
|
|
"status_url": f"/api/crop-simulation/growth/{task.id}/status/",
|
|
|
|
|
"plant_name": serializer.validated_data["plant_name"],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
status=status.HTTP_202_ACCEPTED,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PlantGrowthSimulationStatusView(APIView):
|
|
|
|
|
@extend_schema(
|
|
|
|
|
tags=["Crop Simulation"],
|
|
|
|
|
summary="وضعیت شبیه سازی رشد گیاه",
|
|
|
|
|
description="وضعیت تسک Celery را برمی گرداند و در صورت موفقیت مراحل رشد را به صورت صفحه بندی شده بازمی گرداند.",
|
|
|
|
|
responses={
|
|
|
|
|
200: build_response(
|
|
|
|
|
GrowthSimulationStatusResponseSerializer,
|
|
|
|
|
"وضعیت فعلی تسک شبیه سازی رشد گیاه.",
|
|
|
|
|
)
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
def get(self, request, task_id: str):
|
|
|
|
|
result = _get_async_result(task_id)
|
|
|
|
|
payload = {"task_id": task_id, "status": result.state}
|
|
|
|
|
|
|
|
|
|
if result.state == "PENDING":
|
|
|
|
|
payload["message"] = "تسک در صف یا یافت نشد."
|
|
|
|
|
elif result.state == "PROGRESS":
|
|
|
|
|
payload["progress"] = result.info
|
|
|
|
|
elif result.state == "SUCCESS":
|
|
|
|
|
task_result = dict(result.result or {})
|
|
|
|
|
page = _coerce_positive_int(request.query_params.get("page", 1), 1)
|
|
|
|
|
page_size = min(
|
|
|
|
|
_coerce_positive_int(
|
|
|
|
|
request.query_params.get("page_size", task_result.get("default_page_size", 10)),
|
|
|
|
|
10,
|
|
|
|
|
),
|
|
|
|
|
MAX_PAGE_SIZE,
|
|
|
|
|
)
|
|
|
|
|
paginated = paginate_growth_stages(
|
|
|
|
|
task_result.get("stage_timeline", []),
|
|
|
|
|
page=page,
|
|
|
|
|
page_size=page_size,
|
|
|
|
|
)
|
|
|
|
|
task_result["stages_page"] = paginated["items"]
|
|
|
|
|
task_result["pagination"] = paginated["pagination"]
|
|
|
|
|
payload["result"] = task_result
|
|
|
|
|
elif result.state == "FAILURE":
|
|
|
|
|
payload["error"] = str(result.result)
|
|
|
|
|
|
|
|
|
|
return Response(
|
|
|
|
|
{"code": 200, "msg": "success", "data": payload},
|
|
|
|
|
status=status.HTTP_200_OK,
|
|
|
|
|
)
|
2026-04-25 17:22:41 +03:30
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CurrentFarmChartEnvelopeSerializer = build_envelope_serializer(
|
|
|
|
|
"CurrentFarmChartEnvelopeSerializer",
|
|
|
|
|
CurrentFarmChartResponseSerializer,
|
|
|
|
|
)
|
|
|
|
|
HarvestPredictionEnvelopeSerializer = build_envelope_serializer(
|
|
|
|
|
"HarvestPredictionEnvelopeSerializer",
|
|
|
|
|
HarvestPredictionResponseSerializer,
|
|
|
|
|
)
|
|
|
|
|
YieldPredictionEnvelopeSerializer = build_envelope_serializer(
|
|
|
|
|
"YieldPredictionEnvelopeSerializer",
|
|
|
|
|
YieldPredictionResponseSerializer,
|
|
|
|
|
)
|
2026-04-30 00:53:47 +03:30
|
|
|
YieldHarvestSummaryEnvelopeSerializer = build_envelope_serializer(
|
|
|
|
|
"YieldHarvestSummaryEnvelopeSerializer",
|
|
|
|
|
YieldHarvestSummaryResponseSerializer,
|
|
|
|
|
)
|
2026-04-25 17:22:41 +03:30
|
|
|
|
|
|
|
|
|
|
|
|
|
class CurrentFarmSimulationChartView(APIView):
|
|
|
|
|
@extend_schema(
|
|
|
|
|
tags=["Crop Simulation"],
|
|
|
|
|
summary="chart شبیه سازی وضعیت فعلی مزرعه",
|
|
|
|
|
description=(
|
|
|
|
|
"با دریافت farm_uuid، یک شبیه سازی از وضعیت فعلی مزرعه اجرا می کند و داده chart شامل برگ، وزن، بیوماس، رطوبت و خروجی روزانه را برمی گرداند."
|
|
|
|
|
),
|
|
|
|
|
request=CurrentFarmChartRequestSerializer,
|
|
|
|
|
responses={
|
|
|
|
|
200: build_response(
|
|
|
|
|
CurrentFarmChartEnvelopeSerializer,
|
|
|
|
|
"خروجی chart شبیه سازی وضعیت فعلی مزرعه.",
|
|
|
|
|
),
|
|
|
|
|
400: build_response(
|
|
|
|
|
GrowthSimulationErrorSerializer,
|
|
|
|
|
"داده ورودی نامعتبر است.",
|
|
|
|
|
),
|
|
|
|
|
500: build_response(
|
|
|
|
|
GrowthSimulationErrorSerializer,
|
|
|
|
|
"خطا در اجرای chart شبیه سازی مزرعه.",
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
examples=[
|
|
|
|
|
OpenApiExample(
|
|
|
|
|
"نمونه درخواست chart",
|
|
|
|
|
value={
|
|
|
|
|
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
|
|
|
|
"plant_name": "گوجهفرنگی",
|
|
|
|
|
},
|
|
|
|
|
request_only=True,
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
def post(self, request):
|
|
|
|
|
serializer = CurrentFarmChartRequestSerializer(data=request.data)
|
|
|
|
|
if not serializer.is_valid():
|
|
|
|
|
return Response(
|
|
|
|
|
{"code": 400, "msg": "داده نامعتبر.", "data": serializer.errors},
|
|
|
|
|
status=status.HTTP_400_BAD_REQUEST,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
simulator = apps.get_app_config("crop_simulation").get_current_farm_chart_simulator()
|
|
|
|
|
try:
|
|
|
|
|
result = simulator.simulate(**serializer.validated_data)
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
return Response(
|
|
|
|
|
{"code": 500, "msg": f"خطا در اجرای chart شبیه سازی مزرعه: {exc}", "data": None},
|
|
|
|
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return Response(
|
|
|
|
|
{"code": 200, "msg": "success", "data": result},
|
|
|
|
|
status=status.HTTP_200_OK,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class HarvestPredictionView(APIView):
|
|
|
|
|
@extend_schema(
|
|
|
|
|
tags=["Crop Simulation"],
|
|
|
|
|
summary="پیش بینی زمان تقریبی برداشت",
|
|
|
|
|
description=(
|
|
|
|
|
"با دریافت farm_uuid، از شبیه ساز رشد برای برآورد زمان باقی مانده تا برداشت استفاده می کند "
|
|
|
|
|
"و تاریخ تقریبی برداشت را برمی گرداند."
|
|
|
|
|
),
|
|
|
|
|
request=HarvestPredictionRequestSerializer,
|
|
|
|
|
responses={
|
|
|
|
|
200: build_response(
|
|
|
|
|
HarvestPredictionEnvelopeSerializer,
|
|
|
|
|
"خروجی پیش بینی زمان برداشت مزرعه.",
|
|
|
|
|
),
|
|
|
|
|
400: build_response(
|
|
|
|
|
GrowthSimulationErrorSerializer,
|
|
|
|
|
"داده ورودی نامعتبر است.",
|
|
|
|
|
),
|
|
|
|
|
500: build_response(
|
|
|
|
|
GrowthSimulationErrorSerializer,
|
|
|
|
|
"خطا در پیش بینی زمان برداشت.",
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
examples=[
|
|
|
|
|
OpenApiExample(
|
|
|
|
|
"نمونه درخواست harvest prediction",
|
|
|
|
|
value={
|
|
|
|
|
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
|
|
|
|
"plant_name": "گوجهفرنگی",
|
|
|
|
|
},
|
|
|
|
|
request_only=True,
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
def post(self, request):
|
|
|
|
|
serializer = HarvestPredictionRequestSerializer(data=request.data)
|
|
|
|
|
if not serializer.is_valid():
|
|
|
|
|
return Response(
|
|
|
|
|
{"code": 400, "msg": "داده نامعتبر.", "data": serializer.errors},
|
|
|
|
|
status=status.HTTP_400_BAD_REQUEST,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
service = apps.get_app_config("crop_simulation").get_harvest_prediction_service()
|
|
|
|
|
try:
|
|
|
|
|
result = service.get_harvest_prediction(**serializer.validated_data)
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
return Response(
|
|
|
|
|
{"code": 500, "msg": f"خطا در پیش بینی زمان برداشت: {exc}", "data": None},
|
|
|
|
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return Response(
|
|
|
|
|
{"code": 200, "msg": "success", "data": result},
|
|
|
|
|
status=status.HTTP_200_OK,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class YieldPredictionView(APIView):
|
|
|
|
|
@extend_schema(
|
|
|
|
|
tags=["Crop Simulation"],
|
|
|
|
|
summary="پیش بینی عملکرد مزرعه",
|
|
|
|
|
description="با دریافت farm_uuid، خروجی شبیه ساز رشد را به برآورد عملکرد قابل استفاده در KPI تبدیل می کند.",
|
|
|
|
|
request=YieldPredictionRequestSerializer,
|
|
|
|
|
responses={
|
|
|
|
|
200: build_response(YieldPredictionEnvelopeSerializer, "خروجی پیش بینی عملکرد مزرعه."),
|
|
|
|
|
400: build_response(GrowthSimulationErrorSerializer, "داده ورودی نامعتبر است."),
|
|
|
|
|
500: build_response(GrowthSimulationErrorSerializer, "خطا در پیش بینی عملکرد."),
|
|
|
|
|
},
|
|
|
|
|
examples=[
|
|
|
|
|
OpenApiExample(
|
|
|
|
|
"نمونه درخواست yield prediction",
|
|
|
|
|
value={
|
|
|
|
|
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
|
|
|
|
"plant_name": "گوجهفرنگی",
|
|
|
|
|
},
|
|
|
|
|
request_only=True,
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
def post(self, request):
|
|
|
|
|
serializer = YieldPredictionRequestSerializer(data=request.data)
|
|
|
|
|
if not serializer.is_valid():
|
|
|
|
|
return Response(
|
|
|
|
|
{"code": 400, "msg": "داده نامعتبر.", "data": serializer.errors},
|
|
|
|
|
status=status.HTTP_400_BAD_REQUEST,
|
|
|
|
|
)
|
|
|
|
|
service = apps.get_app_config("crop_simulation").get_yield_prediction_service()
|
|
|
|
|
try:
|
|
|
|
|
result = service.get_yield_prediction(**serializer.validated_data)
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
return Response(
|
|
|
|
|
{"code": 500, "msg": f"خطا در پیش بینی عملکرد: {exc}", "data": None},
|
|
|
|
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
)
|
|
|
|
|
return Response({"code": 200, "msg": "success", "data": result}, status=status.HTTP_200_OK)
|
2026-04-30 00:53:47 +03:30
|
|
|
|
|
|
|
|
|
|
|
|
|
class YieldHarvestSummaryView(APIView):
|
|
|
|
|
@extend_schema(
|
|
|
|
|
tags=["Crop Simulation"],
|
|
|
|
|
summary="خلاصه عملکرد و برداشت",
|
|
|
|
|
description=(
|
|
|
|
|
"خروجی داشبورد Yield & Harvest Summary را با اتکا به داده های قطعی شبیه سازی برمی گرداند. "
|
|
|
|
|
"فعلا پاسخ به صورت mock با کارت های خالی بازگردانده می شود."
|
|
|
|
|
),
|
|
|
|
|
parameters=[
|
|
|
|
|
OpenApiParameter(
|
|
|
|
|
name="farm_uuid",
|
|
|
|
|
type=str,
|
|
|
|
|
location=OpenApiParameter.QUERY,
|
|
|
|
|
required=True,
|
|
|
|
|
description="شناسه یکتای مزرعه",
|
|
|
|
|
),
|
|
|
|
|
OpenApiParameter(
|
|
|
|
|
name="season_year",
|
|
|
|
|
type=int,
|
|
|
|
|
location=OpenApiParameter.QUERY,
|
|
|
|
|
required=False,
|
|
|
|
|
description="سال زراعی",
|
|
|
|
|
),
|
|
|
|
|
OpenApiParameter(
|
|
|
|
|
name="crop_name",
|
|
|
|
|
type=str,
|
|
|
|
|
location=OpenApiParameter.QUERY,
|
|
|
|
|
required=False,
|
|
|
|
|
description="نام محصول",
|
|
|
|
|
),
|
|
|
|
|
OpenApiParameter(
|
|
|
|
|
name="include_narrative",
|
|
|
|
|
type=bool,
|
|
|
|
|
location=OpenApiParameter.QUERY,
|
|
|
|
|
required=False,
|
|
|
|
|
description="در آینده روایت متنی را نیز اضافه می کند.",
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
responses={
|
|
|
|
|
200: build_response(
|
|
|
|
|
YieldHarvestSummaryEnvelopeSerializer,
|
|
|
|
|
"خروجی خلاصه عملکرد و برداشت مزرعه.",
|
|
|
|
|
),
|
|
|
|
|
400: build_response(
|
|
|
|
|
GrowthSimulationErrorSerializer,
|
|
|
|
|
"پارامترهای query نامعتبر است.",
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
examples=[
|
|
|
|
|
OpenApiExample(
|
|
|
|
|
"نمونه پاسخ yield harvest summary",
|
|
|
|
|
value={
|
|
|
|
|
"code": 200,
|
|
|
|
|
"msg": "success",
|
|
|
|
|
"data": {
|
|
|
|
|
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
|
|
|
|
"season_highlights_card": {},
|
|
|
|
|
"yield_prediction": {},
|
|
|
|
|
"harvest_prediction_card": {},
|
|
|
|
|
"harvest_readiness_zones": {},
|
|
|
|
|
"yield_quality_bands": {},
|
|
|
|
|
"harvest_operations_card": {},
|
|
|
|
|
"yield_prediction_chart": {},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
response_only=True,
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
def get(self, request):
|
|
|
|
|
serializer = YieldHarvestSummaryQuerySerializer(data=request.query_params)
|
|
|
|
|
if not serializer.is_valid():
|
|
|
|
|
return Response(
|
|
|
|
|
{"code": 400, "msg": "داده نامعتبر.", "data": serializer.errors},
|
|
|
|
|
status=status.HTTP_400_BAD_REQUEST,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
validated = serializer.validated_data
|
|
|
|
|
service = YieldHarvestSummaryService()
|
|
|
|
|
payload = service.get_summary(
|
|
|
|
|
farm_uuid=str(validated["farm_uuid"]),
|
|
|
|
|
season_year=str(validated.get("season_year") or ""),
|
|
|
|
|
crop_name=validated.get("crop_name") or "",
|
|
|
|
|
include_narrative=validated.get("include_narrative", False),
|
|
|
|
|
)
|
|
|
|
|
return Response({"code": 200, "msg": "success", "data": payload}, status=status.HTTP_200_OK)
|