UPDATE
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from drf_spectacular.utils import OpenApiExample, extend_schema
|
||||
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 (
|
||||
GrowthSimulationQueuedSerializer,
|
||||
GrowthSimulationRequestSerializer,
|
||||
GrowthSimulationResultSerializer,
|
||||
)
|
||||
from .tasks import run_growth_simulation_task
|
||||
|
||||
|
||||
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"],
|
||||
"farm_uuid": "550e8400-e29b-41d4-a716-446655440000",
|
||||
},
|
||||
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,
|
||||
)
|
||||
Reference in New Issue
Block a user