UPDATE
This commit is contained in:
@@ -0,0 +1,103 @@
|
|||||||
|
import copy
|
||||||
|
|
||||||
|
from drf_spectacular.utils import OpenApiResponse
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
|
||||||
|
def _build_schema_field(schema, *, many=False, required=True, allow_null=False):
|
||||||
|
if schema is None:
|
||||||
|
return serializers.JSONField(required=required, allow_null=allow_null)
|
||||||
|
|
||||||
|
if isinstance(schema, serializers.Field):
|
||||||
|
field = copy.deepcopy(schema)
|
||||||
|
field.required = required
|
||||||
|
if hasattr(field, "allow_null"):
|
||||||
|
field.allow_null = allow_null
|
||||||
|
return field
|
||||||
|
|
||||||
|
if isinstance(schema, serializers.BaseSerializer):
|
||||||
|
serializer = copy.deepcopy(schema)
|
||||||
|
serializer.required = required
|
||||||
|
serializer.allow_null = allow_null
|
||||||
|
return serializer
|
||||||
|
|
||||||
|
if isinstance(schema, type) and issubclass(schema, serializers.BaseSerializer):
|
||||||
|
return schema(many=many, required=required, allow_null=allow_null)
|
||||||
|
|
||||||
|
raise TypeError(f"Unsupported schema type: {type(schema)!r}")
|
||||||
|
|
||||||
|
|
||||||
|
def build_message_response_serializer(name):
|
||||||
|
return type(
|
||||||
|
name,
|
||||||
|
(serializers.Serializer,),
|
||||||
|
{
|
||||||
|
"__module__": __name__,
|
||||||
|
"code": serializers.IntegerField(),
|
||||||
|
"msg": serializers.CharField(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build_envelope_serializer(
|
||||||
|
name,
|
||||||
|
data_schema=None,
|
||||||
|
*,
|
||||||
|
many=False,
|
||||||
|
data_required=True,
|
||||||
|
allow_null=False,
|
||||||
|
):
|
||||||
|
return type(
|
||||||
|
name,
|
||||||
|
(serializers.Serializer,),
|
||||||
|
{
|
||||||
|
"__module__": __name__,
|
||||||
|
"code": serializers.IntegerField(),
|
||||||
|
"msg": serializers.CharField(),
|
||||||
|
"data": _build_schema_field(
|
||||||
|
data_schema,
|
||||||
|
many=many,
|
||||||
|
required=data_required,
|
||||||
|
allow_null=allow_null,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build_task_queue_data_serializer(name, extra_fields=None):
|
||||||
|
fields = {
|
||||||
|
"__module__": __name__,
|
||||||
|
"task_id": serializers.CharField(),
|
||||||
|
"status_url": serializers.CharField(),
|
||||||
|
}
|
||||||
|
if extra_fields:
|
||||||
|
fields.update(extra_fields)
|
||||||
|
return type(name, (serializers.Serializer,), fields)
|
||||||
|
|
||||||
|
|
||||||
|
def build_task_status_data_serializer(name, result_schema=None):
|
||||||
|
result_field = (
|
||||||
|
_build_schema_field(result_schema, required=False, allow_null=True)
|
||||||
|
if result_schema is not None
|
||||||
|
else serializers.JSONField(required=False)
|
||||||
|
)
|
||||||
|
return type(
|
||||||
|
name,
|
||||||
|
(serializers.Serializer,),
|
||||||
|
{
|
||||||
|
"__module__": __name__,
|
||||||
|
"task_id": serializers.CharField(),
|
||||||
|
"status": serializers.CharField(),
|
||||||
|
"message": serializers.CharField(required=False),
|
||||||
|
"progress": serializers.DictField(
|
||||||
|
child=serializers.JSONField(),
|
||||||
|
required=False,
|
||||||
|
),
|
||||||
|
"result": result_field,
|
||||||
|
"error": serializers.CharField(required=False),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build_response(serializer, description):
|
||||||
|
return OpenApiResponse(response=serializer, description=description)
|
||||||
@@ -122,10 +122,15 @@ SPECTACULAR_SETTINGS = {
|
|||||||
"TAGS": [
|
"TAGS": [
|
||||||
{"name": "Dashboard Data", "description": "تجمیع دادههای داشبورد مزرعه"},
|
{"name": "Dashboard Data", "description": "تجمیع دادههای داشبورد مزرعه"},
|
||||||
{"name": "RAG Chat", "description": "چت هوشمند RAG"},
|
{"name": "RAG Chat", "description": "چت هوشمند RAG"},
|
||||||
|
{"name": "RAG Recommendations", "description": "توصیههای آبیاری و کودهی مبتنی بر RAG"},
|
||||||
{"name": "Tasks", "description": "مدیریت تسکهای Celery"},
|
{"name": "Tasks", "description": "مدیریت تسکهای Celery"},
|
||||||
{"name": "Soil Data", "description": "دادههای خاک (SoilGrids)"},
|
{"name": "Soil Data", "description": "دادههای خاک (SoilGrids)"},
|
||||||
{"name": "Sensor Data", "description": "دادههای سنسور"},
|
{"name": "Sensor Data", "description": "دادههای سنسور"},
|
||||||
{"name": "Sensor Parameters", "description": "پارامترهای سنسور"},
|
{"name": "Sensor Parameters", "description": "پارامترهای سنسور"},
|
||||||
|
{"name": "Plant", "description": "مدیریت گیاهان و دریافت اطلاعات گیاه"},
|
||||||
|
{"name": "Irrigation", "description": "مدیریت روشهای آبیاری"},
|
||||||
|
{"name": "Irrigation Recommendation", "description": "درخواست و پیگیری توصیه آبیاری"},
|
||||||
|
{"name": "Fertilization Recommendation", "description": "درخواست و پیگیری توصیه کودهی"},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+37
-13
@@ -7,9 +7,34 @@ from rest_framework import status
|
|||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
|
from config.openapi import (
|
||||||
|
build_envelope_serializer,
|
||||||
|
build_response,
|
||||||
|
build_task_queue_data_serializer,
|
||||||
|
build_task_status_data_serializer,
|
||||||
|
)
|
||||||
|
|
||||||
from .tasks import generate_dashboard_data_task
|
from .tasks import generate_dashboard_data_task
|
||||||
|
|
||||||
|
|
||||||
|
DashboardDataGenerateDataSerializer = build_task_queue_data_serializer(
|
||||||
|
"DashboardDataGenerateDataSerializer"
|
||||||
|
)
|
||||||
|
DashboardDataGenerateResponseSerializer = build_envelope_serializer(
|
||||||
|
"DashboardDataGenerateResponseSerializer",
|
||||||
|
DashboardDataGenerateDataSerializer,
|
||||||
|
)
|
||||||
|
DashboardDataErrorResponseSerializer = build_envelope_serializer(
|
||||||
|
"DashboardDataErrorResponseSerializer",
|
||||||
|
data_required=False,
|
||||||
|
allow_null=True,
|
||||||
|
)
|
||||||
|
DashboardDataStatusResponseSerializer = build_envelope_serializer(
|
||||||
|
"DashboardDataStatusResponseSerializer",
|
||||||
|
build_task_status_data_serializer("DashboardDataStatusDataSerializer"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DashboardDataGenerateView(APIView):
|
class DashboardDataGenerateView(APIView):
|
||||||
@extend_schema(
|
@extend_schema(
|
||||||
tags=["Dashboard Data"],
|
tags=["Dashboard Data"],
|
||||||
@@ -22,21 +47,14 @@ class DashboardDataGenerateView(APIView):
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
responses={
|
responses={
|
||||||
202: inline_serializer(
|
202: build_response(
|
||||||
name="DashboardDataGenerateResponse",
|
DashboardDataGenerateResponseSerializer,
|
||||||
fields={
|
"تسک ساخت داده داشبورد در صف قرار گرفت.",
|
||||||
"code": drf_serializers.IntegerField(),
|
|
||||||
"msg": drf_serializers.CharField(),
|
|
||||||
"data": inline_serializer(
|
|
||||||
name="DashboardDataGenerateResponseData",
|
|
||||||
fields={
|
|
||||||
"task_id": drf_serializers.CharField(),
|
|
||||||
"status_url": drf_serializers.CharField(),
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
},
|
400: build_response(
|
||||||
|
DashboardDataErrorResponseSerializer,
|
||||||
|
"پارامتر ورودی نامعتبر است.",
|
||||||
),
|
),
|
||||||
400: OpenApiResponse(description="Invalid input"),
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
@@ -72,6 +90,12 @@ class DashboardDataStatusView(APIView):
|
|||||||
@extend_schema(
|
@extend_schema(
|
||||||
tags=["Dashboard Data"],
|
tags=["Dashboard Data"],
|
||||||
summary="Dashboard task status",
|
summary="Dashboard task status",
|
||||||
|
responses={
|
||||||
|
200: build_response(
|
||||||
|
DashboardDataStatusResponseSerializer,
|
||||||
|
"وضعیت فعلی تسک داده داشبورد.",
|
||||||
|
),
|
||||||
|
},
|
||||||
)
|
)
|
||||||
def get(self, request, task_id):
|
def get(self, request, task_id):
|
||||||
result = AsyncResult(task_id)
|
result = AsyncResult(task_id)
|
||||||
|
|||||||
+34
-3
@@ -7,9 +7,31 @@ from rest_framework import status
|
|||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
|
from config.openapi import (
|
||||||
|
build_envelope_serializer,
|
||||||
|
build_response,
|
||||||
|
build_task_queue_data_serializer,
|
||||||
|
build_task_status_data_serializer,
|
||||||
|
)
|
||||||
|
|
||||||
from .serializers import FertilizationRecommendRequestSerializer
|
from .serializers import FertilizationRecommendRequestSerializer
|
||||||
|
|
||||||
|
|
||||||
|
FertilizationQueueResponseSerializer = build_envelope_serializer(
|
||||||
|
"FertilizationQueueResponseSerializer",
|
||||||
|
build_task_queue_data_serializer("FertilizationQueueDataSerializer"),
|
||||||
|
)
|
||||||
|
FertilizationValidationErrorSerializer = build_envelope_serializer(
|
||||||
|
"FertilizationValidationErrorSerializer",
|
||||||
|
data_required=False,
|
||||||
|
allow_null=True,
|
||||||
|
)
|
||||||
|
FertilizationStatusResponseSerializer = build_envelope_serializer(
|
||||||
|
"FertilizationStatusResponseSerializer",
|
||||||
|
build_task_status_data_serializer("FertilizationStatusDataSerializer"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class FertilizationRecommendView(APIView):
|
class FertilizationRecommendView(APIView):
|
||||||
"""
|
"""
|
||||||
توصیه کودهی با Celery.
|
توصیه کودهی با Celery.
|
||||||
@@ -29,8 +51,14 @@ class FertilizationRecommendView(APIView):
|
|||||||
),
|
),
|
||||||
request=FertilizationRecommendRequestSerializer,
|
request=FertilizationRecommendRequestSerializer,
|
||||||
responses={
|
responses={
|
||||||
202: OpenApiResponse(description="تسک در صف قرار گرفت"),
|
202: build_response(
|
||||||
400: OpenApiResponse(description="پارامتر ورودی نامعتبر"),
|
FertilizationQueueResponseSerializer,
|
||||||
|
"تسک توصیه کودهی در صف قرار گرفت.",
|
||||||
|
),
|
||||||
|
400: build_response(
|
||||||
|
FertilizationValidationErrorSerializer,
|
||||||
|
"پارامتر ورودی نامعتبر است.",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
examples=[
|
examples=[
|
||||||
OpenApiExample(
|
OpenApiExample(
|
||||||
@@ -87,7 +115,10 @@ class FertilizationRecommendStatusView(APIView):
|
|||||||
summary="وضعیت تسک توصیه کودهی",
|
summary="وضعیت تسک توصیه کودهی",
|
||||||
description="وضعیت تسک Celery توصیه کودهی را برمیگرداند.",
|
description="وضعیت تسک Celery توصیه کودهی را برمیگرداند.",
|
||||||
responses={
|
responses={
|
||||||
200: OpenApiResponse(description="وضعیت تسک"),
|
200: build_response(
|
||||||
|
FertilizationStatusResponseSerializer,
|
||||||
|
"وضعیت فعلی تسک توصیه کودهی.",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
def get(self, request, task_id):
|
def get(self, request, task_id):
|
||||||
|
|||||||
+136
-59
@@ -1,12 +1,15 @@
|
|||||||
from drf_spectacular.utils import (
|
from drf_spectacular.utils import OpenApiExample, extend_schema
|
||||||
OpenApiExample,
|
|
||||||
OpenApiResponse,
|
|
||||||
extend_schema,
|
|
||||||
)
|
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
|
from config.openapi import (
|
||||||
|
build_envelope_serializer,
|
||||||
|
build_response,
|
||||||
|
build_task_queue_data_serializer,
|
||||||
|
build_task_status_data_serializer,
|
||||||
|
)
|
||||||
|
|
||||||
from .models import IrrigationMethod
|
from .models import IrrigationMethod
|
||||||
from .serializers import (
|
from .serializers import (
|
||||||
IrrigationMethodSerializer,
|
IrrigationMethodSerializer,
|
||||||
@@ -14,6 +17,30 @@ from .serializers import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
IrrigationMethodListResponseSerializer = build_envelope_serializer(
|
||||||
|
"IrrigationMethodListResponseSerializer",
|
||||||
|
IrrigationMethodSerializer,
|
||||||
|
many=True,
|
||||||
|
)
|
||||||
|
IrrigationMethodDetailResponseSerializer = build_envelope_serializer(
|
||||||
|
"IrrigationMethodDetailResponseSerializer",
|
||||||
|
IrrigationMethodSerializer,
|
||||||
|
)
|
||||||
|
IrrigationValidationErrorSerializer = build_envelope_serializer(
|
||||||
|
"IrrigationValidationErrorSerializer",
|
||||||
|
data_required=False,
|
||||||
|
allow_null=True,
|
||||||
|
)
|
||||||
|
IrrigationQueueResponseSerializer = build_envelope_serializer(
|
||||||
|
"IrrigationQueueResponseSerializer",
|
||||||
|
build_task_queue_data_serializer("IrrigationQueueDataSerializer"),
|
||||||
|
)
|
||||||
|
IrrigationStatusResponseSerializer = build_envelope_serializer(
|
||||||
|
"IrrigationStatusResponseSerializer",
|
||||||
|
build_task_status_data_serializer("IrrigationStatusDataSerializer"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class IrrigationMethodListCreateView(APIView):
|
class IrrigationMethodListCreateView(APIView):
|
||||||
"""لیست تمام روشهای آبیاری و ایجاد روش جدید."""
|
"""لیست تمام روشهای آبیاری و ایجاد روش جدید."""
|
||||||
|
|
||||||
@@ -21,7 +48,12 @@ class IrrigationMethodListCreateView(APIView):
|
|||||||
tags=["Irrigation"],
|
tags=["Irrigation"],
|
||||||
summary="لیست روشهای آبیاری",
|
summary="لیست روشهای آبیاری",
|
||||||
description="لیست تمام روشهای آبیاری ذخیرهشده را برمیگرداند.",
|
description="لیست تمام روشهای آبیاری ذخیرهشده را برمیگرداند.",
|
||||||
responses={200: IrrigationMethodSerializer(many=True)},
|
responses={
|
||||||
|
200: build_response(
|
||||||
|
IrrigationMethodListResponseSerializer,
|
||||||
|
"لیست روشهای آبیاری ذخیرهشده.",
|
||||||
|
),
|
||||||
|
},
|
||||||
)
|
)
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
methods = IrrigationMethod.objects.all()
|
methods = IrrigationMethod.objects.all()
|
||||||
@@ -31,6 +63,52 @@ class IrrigationMethodListCreateView(APIView):
|
|||||||
status=status.HTTP_200_OK,
|
status=status.HTTP_200_OK,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
tags=["Irrigation"],
|
||||||
|
summary="ایجاد روش آبیاری جدید",
|
||||||
|
description="یک روش آبیاری جدید ایجاد میکند.",
|
||||||
|
request=IrrigationMethodSerializer,
|
||||||
|
responses={
|
||||||
|
201: build_response(
|
||||||
|
IrrigationMethodDetailResponseSerializer,
|
||||||
|
"روش آبیاری جدید با موفقیت ایجاد شد.",
|
||||||
|
),
|
||||||
|
400: build_response(
|
||||||
|
IrrigationValidationErrorSerializer,
|
||||||
|
"داده ورودی نامعتبر است.",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
examples=[
|
||||||
|
OpenApiExample(
|
||||||
|
"نمونه درخواست",
|
||||||
|
value={
|
||||||
|
"name": "آبیاری قطرهای",
|
||||||
|
"category": "موضعی",
|
||||||
|
"description": "آبیاری با دبی کم و فشار مناسب",
|
||||||
|
"water_efficiency_percent": 90.0,
|
||||||
|
"water_pressure_required": "۱-۲ اتمسفر",
|
||||||
|
"flow_rate": "۲-۸ لیتر در ساعت",
|
||||||
|
"coverage_area": "بسته به طراحی سیستم",
|
||||||
|
"soil_type": "تمام انواع خاک",
|
||||||
|
"climate_suitability": "گرم و خشک",
|
||||||
|
},
|
||||||
|
request_only=True,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def post(self, request):
|
||||||
|
serializer = IrrigationMethodSerializer(data=request.data)
|
||||||
|
if not serializer.is_valid():
|
||||||
|
return Response(
|
||||||
|
{"code": 400, "msg": "داده نامعتبر.", "data": serializer.errors},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
|
)
|
||||||
|
serializer.save()
|
||||||
|
return Response(
|
||||||
|
{"code": 201, "msg": "success", "data": serializer.data},
|
||||||
|
status=status.HTTP_201_CREATED,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class IrrigationRecommendView(APIView):
|
class IrrigationRecommendView(APIView):
|
||||||
"""
|
"""
|
||||||
@@ -50,8 +128,14 @@ class IrrigationRecommendView(APIView):
|
|||||||
),
|
),
|
||||||
request=IrrigationRecommendRequestSerializer,
|
request=IrrigationRecommendRequestSerializer,
|
||||||
responses={
|
responses={
|
||||||
202: OpenApiResponse(description="تسک در صف قرار گرفت"),
|
202: build_response(
|
||||||
400: OpenApiResponse(description="پارامتر ورودی نامعتبر"),
|
IrrigationQueueResponseSerializer,
|
||||||
|
"تسک توصیه آبیاری در صف قرار گرفت.",
|
||||||
|
),
|
||||||
|
400: build_response(
|
||||||
|
IrrigationValidationErrorSerializer,
|
||||||
|
"پارامتر ورودی نامعتبر است.",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
examples=[
|
examples=[
|
||||||
OpenApiExample(
|
OpenApiExample(
|
||||||
@@ -111,7 +195,10 @@ class IrrigationRecommendStatusView(APIView):
|
|||||||
summary="وضعیت تسک توصیه آبیاری",
|
summary="وضعیت تسک توصیه آبیاری",
|
||||||
description="وضعیت تسک Celery توصیه آبیاری را برمیگرداند.",
|
description="وضعیت تسک Celery توصیه آبیاری را برمیگرداند.",
|
||||||
responses={
|
responses={
|
||||||
200: OpenApiResponse(description="وضعیت تسک"),
|
200: build_response(
|
||||||
|
IrrigationStatusResponseSerializer,
|
||||||
|
"وضعیت فعلی تسک توصیه آبیاری.",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
def get(self, request, task_id):
|
def get(self, request, task_id):
|
||||||
@@ -132,46 +219,6 @@ class IrrigationRecommendStatusView(APIView):
|
|||||||
status=status.HTTP_200_OK,
|
status=status.HTTP_200_OK,
|
||||||
)
|
)
|
||||||
|
|
||||||
@extend_schema(
|
|
||||||
tags=["Irrigation"],
|
|
||||||
summary="ایجاد روش آبیاری جدید",
|
|
||||||
description="یک روش آبیاری جدید ایجاد میکند.",
|
|
||||||
request=IrrigationMethodSerializer,
|
|
||||||
responses={
|
|
||||||
201: IrrigationMethodSerializer,
|
|
||||||
400: OpenApiResponse(description="داده نامعتبر"),
|
|
||||||
},
|
|
||||||
examples=[
|
|
||||||
OpenApiExample(
|
|
||||||
"نمونه درخواست",
|
|
||||||
value={
|
|
||||||
"name": "آبیاری قطرهای",
|
|
||||||
"category": "موضعی",
|
|
||||||
"description": "آبیاری با دبی کم و فشار مناسب",
|
|
||||||
"water_efficiency_percent": 90.0,
|
|
||||||
"water_pressure_required": "۱-۲ اتمسفر",
|
|
||||||
"flow_rate": "۲-۸ لیتر در ساعت",
|
|
||||||
"coverage_area": "بسته به طراحی سیستم",
|
|
||||||
"soil_type": "تمام انواع خاک",
|
|
||||||
"climate_suitability": "گرم و خشک",
|
|
||||||
},
|
|
||||||
request_only=True,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def post(self, request):
|
|
||||||
serializer = IrrigationMethodSerializer(data=request.data)
|
|
||||||
if not serializer.is_valid():
|
|
||||||
return Response(
|
|
||||||
{"code": 400, "msg": "داده نامعتبر.", "data": serializer.errors},
|
|
||||||
status=status.HTTP_400_BAD_REQUEST,
|
|
||||||
)
|
|
||||||
serializer.save()
|
|
||||||
return Response(
|
|
||||||
{"code": 201, "msg": "success", "data": serializer.data},
|
|
||||||
status=status.HTTP_201_CREATED,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class IrrigationMethodDetailView(APIView):
|
class IrrigationMethodDetailView(APIView):
|
||||||
"""دریافت، ویرایش و حذف یک روش آبیاری."""
|
"""دریافت، ویرایش و حذف یک روش آبیاری."""
|
||||||
@@ -184,8 +231,14 @@ class IrrigationMethodDetailView(APIView):
|
|||||||
summary="جزئیات روش آبیاری",
|
summary="جزئیات روش آبیاری",
|
||||||
description="مشخصات یک روش آبیاری را بر اساس شناسه برمیگرداند.",
|
description="مشخصات یک روش آبیاری را بر اساس شناسه برمیگرداند.",
|
||||||
responses={
|
responses={
|
||||||
200: IrrigationMethodSerializer,
|
200: build_response(
|
||||||
404: OpenApiResponse(description="روش آبیاری یافت نشد"),
|
IrrigationMethodDetailResponseSerializer,
|
||||||
|
"جزئیات روش آبیاری.",
|
||||||
|
),
|
||||||
|
404: build_response(
|
||||||
|
IrrigationValidationErrorSerializer,
|
||||||
|
"روش آبیاری یافت نشد.",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
def get(self, request, pk):
|
def get(self, request, pk):
|
||||||
@@ -207,9 +260,18 @@ class IrrigationMethodDetailView(APIView):
|
|||||||
description="تمام فیلدهای یک روش آبیاری را آپدیت میکند.",
|
description="تمام فیلدهای یک روش آبیاری را آپدیت میکند.",
|
||||||
request=IrrigationMethodSerializer,
|
request=IrrigationMethodSerializer,
|
||||||
responses={
|
responses={
|
||||||
200: IrrigationMethodSerializer,
|
200: build_response(
|
||||||
400: OpenApiResponse(description="داده نامعتبر"),
|
IrrigationMethodDetailResponseSerializer,
|
||||||
404: OpenApiResponse(description="روش آبیاری یافت نشد"),
|
"روش آبیاری با موفقیت بهروزرسانی شد.",
|
||||||
|
),
|
||||||
|
400: build_response(
|
||||||
|
IrrigationValidationErrorSerializer,
|
||||||
|
"داده ورودی نامعتبر است.",
|
||||||
|
),
|
||||||
|
404: build_response(
|
||||||
|
IrrigationValidationErrorSerializer,
|
||||||
|
"روش آبیاری یافت نشد.",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
def put(self, request, pk):
|
def put(self, request, pk):
|
||||||
@@ -237,9 +299,18 @@ class IrrigationMethodDetailView(APIView):
|
|||||||
description="فقط فیلدهای ارسالشده آپدیت میشوند.",
|
description="فقط فیلدهای ارسالشده آپدیت میشوند.",
|
||||||
request=IrrigationMethodSerializer,
|
request=IrrigationMethodSerializer,
|
||||||
responses={
|
responses={
|
||||||
200: IrrigationMethodSerializer,
|
200: build_response(
|
||||||
400: OpenApiResponse(description="داده نامعتبر"),
|
IrrigationMethodDetailResponseSerializer,
|
||||||
404: OpenApiResponse(description="روش آبیاری یافت نشد"),
|
"روش آبیاری با موفقیت بهروزرسانی شد.",
|
||||||
|
),
|
||||||
|
400: build_response(
|
||||||
|
IrrigationValidationErrorSerializer,
|
||||||
|
"داده ورودی نامعتبر است.",
|
||||||
|
),
|
||||||
|
404: build_response(
|
||||||
|
IrrigationValidationErrorSerializer,
|
||||||
|
"روش آبیاری یافت نشد.",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
def patch(self, request, pk):
|
def patch(self, request, pk):
|
||||||
@@ -266,8 +337,14 @@ class IrrigationMethodDetailView(APIView):
|
|||||||
summary="حذف روش آبیاری",
|
summary="حذف روش آبیاری",
|
||||||
description="یک روش آبیاری را حذف میکند.",
|
description="یک روش آبیاری را حذف میکند.",
|
||||||
responses={
|
responses={
|
||||||
200: OpenApiResponse(description="حذف موفق"),
|
200: build_response(
|
||||||
404: OpenApiResponse(description="روش آبیاری یافت نشد"),
|
IrrigationValidationErrorSerializer,
|
||||||
|
"روش آبیاری با موفقیت حذف شد.",
|
||||||
|
),
|
||||||
|
404: build_response(
|
||||||
|
IrrigationValidationErrorSerializer,
|
||||||
|
"روش آبیاری یافت نشد.",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
def delete(self, request, pk):
|
def delete(self, request, pk):
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"code": 202,
|
||||||
|
"msg": "dashboard task queued",
|
||||||
|
"data": {
|
||||||
|
"task_id": "dashboard-task-123",
|
||||||
|
"status_url": "/api/dashboard-data/dashboard-task-123/status/"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"code": 400,
|
||||||
|
"msg": "پارامتر sensor_id الزامی است.",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "dashboard-task-123",
|
||||||
|
"status": "FAILURE",
|
||||||
|
"error": "خطا در ساخت کارتهای داشبورد."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "dashboard-task-123",
|
||||||
|
"status": "PENDING",
|
||||||
|
"message": "تسک در صف یا یافت نشد."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "dashboard-task-123",
|
||||||
|
"status": "PROGRESS",
|
||||||
|
"progress": {
|
||||||
|
"current": 5,
|
||||||
|
"total": 15,
|
||||||
|
"card": "sensorValuesList",
|
||||||
|
"message": "processing sensorValuesList"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "dashboard-task-123",
|
||||||
|
"status": "SUCCESS",
|
||||||
|
"result": {
|
||||||
|
"sensor_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||||
|
"all_cards": {
|
||||||
|
"farmOverviewKpis": {
|
||||||
|
"healthScore": 82,
|
||||||
|
"activeAlerts": 2,
|
||||||
|
"waterNeedMm": 18.4
|
||||||
|
},
|
||||||
|
"sensorValuesList": {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"label": "رطوبت خاک",
|
||||||
|
"value": 45.2,
|
||||||
|
"unit": "%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "دما خاک",
|
||||||
|
"value": 22.5,
|
||||||
|
"unit": "°C"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"recommendationsList": {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"recommendation_title": "تنظیم نوبت آبیاری",
|
||||||
|
"suggested_action": "آبیاری بعدی را صبح فردا انجام دهید.",
|
||||||
|
"urgency_level": "high"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"code": 202,
|
||||||
|
"msg": "تسک توصیه کودهی در صف قرار گرفت.",
|
||||||
|
"data": {
|
||||||
|
"task_id": "fert-task-123",
|
||||||
|
"status_url": "/api/fertilization/recommend/fert-task-123/status/"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 400,
|
||||||
|
"msg": "داده نامعتبر.",
|
||||||
|
"data": {
|
||||||
|
"sensor_uuid": [
|
||||||
|
"This field is required."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "fert-task-123",
|
||||||
|
"status": "FAILURE",
|
||||||
|
"error": "خطا در دریافت توصیه کودهی."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "fert-task-123",
|
||||||
|
"status": "PENDING",
|
||||||
|
"message": "تسک در صف یا یافت نشد."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "fert-task-123",
|
||||||
|
"status": "PROGRESS",
|
||||||
|
"progress": {
|
||||||
|
"message": "در حال پردازش توصیه کودهی..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "fert-task-123",
|
||||||
|
"status": "SUCCESS",
|
||||||
|
"result": {
|
||||||
|
"plan": {
|
||||||
|
"npkRatio": "20-20-20",
|
||||||
|
"amountPerHectare": "150 kg/ha",
|
||||||
|
"applicationMethod": "کودآبیاری در دو نوبت",
|
||||||
|
"applicationInterval": "هر ۱۰ روز",
|
||||||
|
"reasoning": "نیتروژن و پتاسیم خاک در محدوده متوسط است و گیاه در فاز رویشی نیاز تغذیهای بالاتری دارد."
|
||||||
|
},
|
||||||
|
"raw_response": "{\"plan\":{\"npkRatio\":\"20-20-20\"}}",
|
||||||
|
"status": "completed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,604 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/dashboard-data/generate/",
|
||||||
|
"status_code": 202,
|
||||||
|
"description": "Dashboard data task queued",
|
||||||
|
"file": "json/mock_data/dashboard-data/generate/post_202.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/dashboard-data/generate/",
|
||||||
|
"status_code": 400,
|
||||||
|
"description": "Missing sensor_id",
|
||||||
|
"file": "json/mock_data/dashboard-data/generate/post_400.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/dashboard-data/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Pending dashboard task",
|
||||||
|
"file": "json/mock_data/dashboard-data/status/get_200_pending.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/dashboard-data/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Dashboard task in progress",
|
||||||
|
"file": "json/mock_data/dashboard-data/status/get_200_progress.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/dashboard-data/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Successful dashboard task",
|
||||||
|
"file": "json/mock_data/dashboard-data/status/get_200_success.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/dashboard-data/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Failed dashboard task",
|
||||||
|
"file": "json/mock_data/dashboard-data/status/get_200_failure.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/fertilization/recommend/",
|
||||||
|
"status_code": 202,
|
||||||
|
"description": "Fertilization task queued",
|
||||||
|
"file": "json/mock_data/fertilization/recommend/post_202.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/fertilization/recommend/",
|
||||||
|
"status_code": 400,
|
||||||
|
"description": "Validation error",
|
||||||
|
"file": "json/mock_data/fertilization/recommend/post_400.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/fertilization/recommend/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Fertilization status pending",
|
||||||
|
"file": "json/mock_data/fertilization/status/get_200_pending.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/fertilization/recommend/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Fertilization status progress",
|
||||||
|
"file": "json/mock_data/fertilization/status/get_200_progress.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/fertilization/recommend/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Fertilization status success",
|
||||||
|
"file": "json/mock_data/fertilization/status/get_200_success.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/fertilization/recommend/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Fertilization status failure",
|
||||||
|
"file": "json/mock_data/fertilization/status/get_200_failure.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/irrigation/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "List irrigation methods",
|
||||||
|
"file": "json/mock_data/irrigation/methods/get_200.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/irrigation/",
|
||||||
|
"status_code": 201,
|
||||||
|
"description": "Create irrigation method",
|
||||||
|
"file": "json/mock_data/irrigation/methods/post_201.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/irrigation/",
|
||||||
|
"status_code": 400,
|
||||||
|
"description": "Irrigation create validation error",
|
||||||
|
"file": "json/mock_data/irrigation/methods/post_400.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/irrigation/recommend/",
|
||||||
|
"status_code": 202,
|
||||||
|
"description": "Irrigation recommendation task queued",
|
||||||
|
"file": "json/mock_data/irrigation/recommend/post_202.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/irrigation/recommend/",
|
||||||
|
"status_code": 400,
|
||||||
|
"description": "Irrigation recommendation validation error",
|
||||||
|
"file": "json/mock_data/irrigation/recommend/post_400.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/irrigation/recommend/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Irrigation recommendation status pending",
|
||||||
|
"file": "json/mock_data/irrigation/recommend/status/get_200_pending.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/irrigation/recommend/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Irrigation recommendation status progress",
|
||||||
|
"file": "json/mock_data/irrigation/recommend/status/get_200_progress.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/irrigation/recommend/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Irrigation recommendation status success",
|
||||||
|
"file": "json/mock_data/irrigation/recommend/status/get_200_success.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/irrigation/recommend/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Irrigation recommendation status failure",
|
||||||
|
"file": "json/mock_data/irrigation/recommend/status/get_200_failure.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/irrigation/{pk}/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Irrigation method get success",
|
||||||
|
"file": "json/mock_data/irrigation/method-detail/get_200.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/irrigation/{pk}/",
|
||||||
|
"status_code": 404,
|
||||||
|
"description": "Irrigation method get not found",
|
||||||
|
"file": "json/mock_data/irrigation/method-detail/get_404.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "PUT",
|
||||||
|
"path": "/api/irrigation/{pk}/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Irrigation method put success",
|
||||||
|
"file": "json/mock_data/irrigation/method-detail/put_200.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "PUT",
|
||||||
|
"path": "/api/irrigation/{pk}/",
|
||||||
|
"status_code": 400,
|
||||||
|
"description": "Irrigation method put validation error",
|
||||||
|
"file": "json/mock_data/irrigation/method-detail/put_400.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "PUT",
|
||||||
|
"path": "/api/irrigation/{pk}/",
|
||||||
|
"status_code": 404,
|
||||||
|
"description": "Irrigation method put not found",
|
||||||
|
"file": "json/mock_data/irrigation/method-detail/put_404.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "PATCH",
|
||||||
|
"path": "/api/irrigation/{pk}/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Irrigation method patch success",
|
||||||
|
"file": "json/mock_data/irrigation/method-detail/patch_200.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "PATCH",
|
||||||
|
"path": "/api/irrigation/{pk}/",
|
||||||
|
"status_code": 400,
|
||||||
|
"description": "Irrigation method patch validation error",
|
||||||
|
"file": "json/mock_data/irrigation/method-detail/patch_400.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "PATCH",
|
||||||
|
"path": "/api/irrigation/{pk}/",
|
||||||
|
"status_code": 404,
|
||||||
|
"description": "Irrigation method patch not found",
|
||||||
|
"file": "json/mock_data/irrigation/method-detail/patch_404.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "DELETE",
|
||||||
|
"path": "/api/irrigation/{pk}/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Delete irrigation method",
|
||||||
|
"file": "json/mock_data/irrigation/method-detail/delete_200.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "DELETE",
|
||||||
|
"path": "/api/irrigation/{pk}/",
|
||||||
|
"status_code": 404,
|
||||||
|
"description": "Delete irrigation method not found",
|
||||||
|
"file": "json/mock_data/irrigation/method-detail/delete_404.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/soil-data/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Soil data served from database",
|
||||||
|
"file": "json/mock_data/soil-data/get_200_database.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/soil-data/",
|
||||||
|
"status_code": 202,
|
||||||
|
"description": "Soil data fetch task queued",
|
||||||
|
"file": "json/mock_data/soil-data/get_202_queued.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/soil-data/",
|
||||||
|
"status_code": 400,
|
||||||
|
"description": "Soil data validation error",
|
||||||
|
"file": "json/mock_data/soil-data/get_400.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/soil-data/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Soil data POST served from database",
|
||||||
|
"file": "json/mock_data/soil-data/post_200_database.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/soil-data/",
|
||||||
|
"status_code": 202,
|
||||||
|
"description": "Soil data POST task queued",
|
||||||
|
"file": "json/mock_data/soil-data/post_202_queued.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/soil-data/",
|
||||||
|
"status_code": 400,
|
||||||
|
"description": "Soil data POST validation error",
|
||||||
|
"file": "json/mock_data/soil-data/post_400.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/soil-data/tasks/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Soil task status pending",
|
||||||
|
"file": "json/mock_data/soil-data/status/get_200_pending.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/soil-data/tasks/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Soil task status progress",
|
||||||
|
"file": "json/mock_data/soil-data/status/get_200_progress.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/soil-data/tasks/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Soil task status success",
|
||||||
|
"file": "json/mock_data/soil-data/status/get_200_success.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/soil-data/tasks/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Soil task status failure",
|
||||||
|
"file": "json/mock_data/soil-data/status/get_200_failure.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/plants/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "List plants",
|
||||||
|
"file": "json/mock_data/plant/list-get_200.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/plants/",
|
||||||
|
"status_code": 201,
|
||||||
|
"description": "Create plant",
|
||||||
|
"file": "json/mock_data/plant/create-post_201.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/plants/",
|
||||||
|
"status_code": 400,
|
||||||
|
"description": "Plant create validation error",
|
||||||
|
"file": "json/mock_data/plant/create-post_400.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/plants/{pk}/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Plant detail get success",
|
||||||
|
"file": "json/mock_data/plant/detail-get_200.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/plants/{pk}/",
|
||||||
|
"status_code": 404,
|
||||||
|
"description": "Plant detail get not found",
|
||||||
|
"file": "json/mock_data/plant/detail-get_404.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "PUT",
|
||||||
|
"path": "/api/plants/{pk}/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Plant detail put success",
|
||||||
|
"file": "json/mock_data/plant/detail-put_200.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "PUT",
|
||||||
|
"path": "/api/plants/{pk}/",
|
||||||
|
"status_code": 400,
|
||||||
|
"description": "Plant detail put validation error",
|
||||||
|
"file": "json/mock_data/plant/detail-put_400.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "PUT",
|
||||||
|
"path": "/api/plants/{pk}/",
|
||||||
|
"status_code": 404,
|
||||||
|
"description": "Plant detail put not found",
|
||||||
|
"file": "json/mock_data/plant/detail-put_404.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "PATCH",
|
||||||
|
"path": "/api/plants/{pk}/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Plant detail patch success",
|
||||||
|
"file": "json/mock_data/plant/detail-patch_200.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "PATCH",
|
||||||
|
"path": "/api/plants/{pk}/",
|
||||||
|
"status_code": 400,
|
||||||
|
"description": "Plant detail patch validation error",
|
||||||
|
"file": "json/mock_data/plant/detail-patch_400.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "PATCH",
|
||||||
|
"path": "/api/plants/{pk}/",
|
||||||
|
"status_code": 404,
|
||||||
|
"description": "Plant detail patch not found",
|
||||||
|
"file": "json/mock_data/plant/detail-patch_404.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "DELETE",
|
||||||
|
"path": "/api/plants/{pk}/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Delete plant success",
|
||||||
|
"file": "json/mock_data/plant/detail-delete_200.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "DELETE",
|
||||||
|
"path": "/api/plants/{pk}/",
|
||||||
|
"status_code": 404,
|
||||||
|
"description": "Delete plant not found",
|
||||||
|
"file": "json/mock_data/plant/detail-delete_404.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/plants/fetch-info/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Fetch plant info success",
|
||||||
|
"file": "json/mock_data/plant/fetch-info-post_200.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/plants/fetch-info/",
|
||||||
|
"status_code": 400,
|
||||||
|
"description": "Fetch plant info missing name",
|
||||||
|
"file": "json/mock_data/plant/fetch-info-post_400.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/plants/fetch-info/",
|
||||||
|
"status_code": 503,
|
||||||
|
"description": "Fetch plant info service unavailable",
|
||||||
|
"file": "json/mock_data/plant/fetch-info-post_503.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/rag/chat/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "RAG chat streaming response",
|
||||||
|
"file": "json/mock_data/rag/chat-post_200_stream.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/rag/chat/",
|
||||||
|
"status_code": 400,
|
||||||
|
"description": "Missing query",
|
||||||
|
"file": "json/mock_data/rag/chat-post_400_missing_query.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/rag/chat/",
|
||||||
|
"status_code": 400,
|
||||||
|
"description": "Invalid service id",
|
||||||
|
"file": "json/mock_data/rag/chat-post_400_invalid_service.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/rag/chat/",
|
||||||
|
"status_code": 400,
|
||||||
|
"description": "Missing user_id for service",
|
||||||
|
"file": "json/mock_data/rag/chat-post_400_missing_user.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/rag/recommend/irrigation/",
|
||||||
|
"status_code": 202,
|
||||||
|
"description": "RAG irrigation task queued",
|
||||||
|
"file": "json/mock_data/rag/irrigation/post_202.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/rag/recommend/irrigation/",
|
||||||
|
"status_code": 400,
|
||||||
|
"description": "RAG irrigation validation error",
|
||||||
|
"file": "json/mock_data/rag/irrigation/post_400.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/rag/recommend/irrigation/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "RAG irrigation status pending",
|
||||||
|
"file": "json/mock_data/rag/irrigation/status/get_200_pending.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/rag/recommend/irrigation/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "RAG irrigation status progress",
|
||||||
|
"file": "json/mock_data/rag/irrigation/status/get_200_progress.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/rag/recommend/irrigation/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "RAG irrigation status success",
|
||||||
|
"file": "json/mock_data/rag/irrigation/status/get_200_success.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/rag/recommend/irrigation/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "RAG irrigation status failure",
|
||||||
|
"file": "json/mock_data/rag/irrigation/status/get_200_failure.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/rag/recommend/fertilization/",
|
||||||
|
"status_code": 202,
|
||||||
|
"description": "RAG fertilization task queued",
|
||||||
|
"file": "json/mock_data/rag/fertilization/post_202.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/rag/recommend/fertilization/",
|
||||||
|
"status_code": 400,
|
||||||
|
"description": "RAG fertilization validation error",
|
||||||
|
"file": "json/mock_data/rag/fertilization/post_400.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/rag/recommend/fertilization/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "RAG fertilization status pending",
|
||||||
|
"file": "json/mock_data/rag/fertilization/status/get_200_pending.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/rag/recommend/fertilization/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "RAG fertilization status progress",
|
||||||
|
"file": "json/mock_data/rag/fertilization/status/get_200_progress.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/rag/recommend/fertilization/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "RAG fertilization status success",
|
||||||
|
"file": "json/mock_data/rag/fertilization/status/get_200_success.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/rag/recommend/fertilization/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "RAG fertilization status failure",
|
||||||
|
"file": "json/mock_data/rag/fertilization/status/get_200_failure.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "PUT",
|
||||||
|
"path": "/api/sensor-data/{uuid_sensor}/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Sensor update put success",
|
||||||
|
"file": "json/mock_data/sensor-data/update-put_200.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "PUT",
|
||||||
|
"path": "/api/sensor-data/{uuid_sensor}/",
|
||||||
|
"status_code": 400,
|
||||||
|
"description": "Sensor update put validation error",
|
||||||
|
"file": "json/mock_data/sensor-data/update-put_400.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "PUT",
|
||||||
|
"path": "/api/sensor-data/{uuid_sensor}/",
|
||||||
|
"status_code": 404,
|
||||||
|
"description": "Sensor update put location not found",
|
||||||
|
"file": "json/mock_data/sensor-data/update-put_404.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "PATCH",
|
||||||
|
"path": "/api/sensor-data/{uuid_sensor}/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Sensor update patch success",
|
||||||
|
"file": "json/mock_data/sensor-data/update-patch_200.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "PATCH",
|
||||||
|
"path": "/api/sensor-data/{uuid_sensor}/",
|
||||||
|
"status_code": 400,
|
||||||
|
"description": "Sensor update patch validation error",
|
||||||
|
"file": "json/mock_data/sensor-data/update-patch_400.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "PATCH",
|
||||||
|
"path": "/api/sensor-data/{uuid_sensor}/",
|
||||||
|
"status_code": 404,
|
||||||
|
"description": "Sensor update patch location not found",
|
||||||
|
"file": "json/mock_data/sensor-data/update-patch_404.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/sensor-data/parameters/",
|
||||||
|
"status_code": 201,
|
||||||
|
"description": "Create sensor parameter",
|
||||||
|
"file": "json/mock_data/sensor-data/parameters-post_201.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/sensor-data/parameters/",
|
||||||
|
"status_code": 400,
|
||||||
|
"description": "Sensor parameter validation error",
|
||||||
|
"file": "json/mock_data/sensor-data/parameters-post_400.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/api/tasks/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Task trigger success",
|
||||||
|
"file": "json/mock_data/tasks/post_200.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/tasks/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Task status pending",
|
||||||
|
"file": "json/mock_data/tasks/status/get_200_pending.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/tasks/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Task status progress",
|
||||||
|
"file": "json/mock_data/tasks/status/get_200_progress.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/tasks/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Task status success",
|
||||||
|
"file": "json/mock_data/tasks/status/get_200_success.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/tasks/{task_id}/status/",
|
||||||
|
"status_code": 200,
|
||||||
|
"description": "Task status failure",
|
||||||
|
"file": "json/mock_data/tasks/status/get_200_failure.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "روش آبیاری با موفقیت حذف شد.",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"code": 404,
|
||||||
|
"msg": "روش آبیاری یافت نشد.",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "آبیاری قطرهای",
|
||||||
|
"category": "موضعی",
|
||||||
|
"description": "آبیاری با دبی کم و راندمان بالا",
|
||||||
|
"water_efficiency_percent": 90.0,
|
||||||
|
"water_pressure_required": "۱-۲ اتمسفر",
|
||||||
|
"flow_rate": "۲-۸ لیتر در ساعت",
|
||||||
|
"coverage_area": "بسته به طراحی سیستم",
|
||||||
|
"soil_type": "اکثر خاکها",
|
||||||
|
"climate_suitability": "گرم و خشک",
|
||||||
|
"created_at": "2025-03-20T10:00:00Z",
|
||||||
|
"updated_at": "2025-03-24T10:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"code": 404,
|
||||||
|
"msg": "روش آبیاری یافت نشد.",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "آبیاری قطرهای",
|
||||||
|
"category": "موضعی",
|
||||||
|
"description": "آبیاری با دبی کم و راندمان بالا",
|
||||||
|
"water_efficiency_percent": 90.0,
|
||||||
|
"water_pressure_required": "۱-۲ اتمسفر",
|
||||||
|
"flow_rate": "۲-۸ لیتر در ساعت",
|
||||||
|
"coverage_area": "بسته به طراحی سیستم",
|
||||||
|
"soil_type": "اکثر خاکها",
|
||||||
|
"climate_suitability": "گرم و خشک",
|
||||||
|
"created_at": "2025-03-20T10:00:00Z",
|
||||||
|
"updated_at": "2025-03-24T10:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 400,
|
||||||
|
"msg": "داده نامعتبر.",
|
||||||
|
"data": {
|
||||||
|
"name": [
|
||||||
|
"This field may not be blank."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"code": 404,
|
||||||
|
"msg": "روش آبیاری یافت نشد.",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "آبیاری قطرهای",
|
||||||
|
"category": "موضعی",
|
||||||
|
"description": "آبیاری با دبی کم و راندمان بالا",
|
||||||
|
"water_efficiency_percent": 90.0,
|
||||||
|
"water_pressure_required": "۱-۲ اتمسفر",
|
||||||
|
"flow_rate": "۲-۸ لیتر در ساعت",
|
||||||
|
"coverage_area": "بسته به طراحی سیستم",
|
||||||
|
"soil_type": "اکثر خاکها",
|
||||||
|
"climate_suitability": "گرم و خشک",
|
||||||
|
"created_at": "2025-03-20T10:00:00Z",
|
||||||
|
"updated_at": "2025-03-24T10:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 400,
|
||||||
|
"msg": "داده نامعتبر.",
|
||||||
|
"data": {
|
||||||
|
"name": [
|
||||||
|
"This field may not be blank."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"code": 404,
|
||||||
|
"msg": "روش آبیاری یافت نشد.",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "آبیاری قطرهای",
|
||||||
|
"category": "موضعی",
|
||||||
|
"description": "آبیاری با دبی کم و راندمان بالا",
|
||||||
|
"water_efficiency_percent": 90.0,
|
||||||
|
"water_pressure_required": "۱-۲ اتمسفر",
|
||||||
|
"flow_rate": "۲-۸ لیتر در ساعت",
|
||||||
|
"coverage_area": "بسته به طراحی سیستم",
|
||||||
|
"soil_type": "اکثر خاکها",
|
||||||
|
"climate_suitability": "گرم و خشک",
|
||||||
|
"created_at": "2025-03-20T10:00:00Z",
|
||||||
|
"updated_at": "2025-03-24T10:00:00Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"code": 201,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "آبیاری قطرهای",
|
||||||
|
"category": "موضعی",
|
||||||
|
"description": "آبیاری با دبی کم و راندمان بالا",
|
||||||
|
"water_efficiency_percent": 90.0,
|
||||||
|
"water_pressure_required": "۱-۲ اتمسفر",
|
||||||
|
"flow_rate": "۲-۸ لیتر در ساعت",
|
||||||
|
"coverage_area": "بسته به طراحی سیستم",
|
||||||
|
"soil_type": "اکثر خاکها",
|
||||||
|
"climate_suitability": "گرم و خشک",
|
||||||
|
"created_at": "2025-03-20T10:00:00Z",
|
||||||
|
"updated_at": "2025-03-24T10:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 400,
|
||||||
|
"msg": "داده نامعتبر.",
|
||||||
|
"data": {
|
||||||
|
"name": [
|
||||||
|
"This field is required."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"code": 202,
|
||||||
|
"msg": "تسک توصیه آبیاری در صف قرار گرفت.",
|
||||||
|
"data": {
|
||||||
|
"task_id": "irr-task-123",
|
||||||
|
"status_url": "/api/irrigation/recommend/irr-task-123/status/"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 400,
|
||||||
|
"msg": "داده نامعتبر.",
|
||||||
|
"data": {
|
||||||
|
"sensor_uuid": [
|
||||||
|
"This field is required."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "irr-task-123",
|
||||||
|
"status": "FAILURE",
|
||||||
|
"error": "خطا در دریافت توصیه آبیاری."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "irr-task-123",
|
||||||
|
"status": "PENDING",
|
||||||
|
"message": "تسک در صف یا یافت نشد."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "irr-task-123",
|
||||||
|
"status": "PROGRESS",
|
||||||
|
"progress": {
|
||||||
|
"message": "در حال پردازش توصیه آبیاری..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "irr-task-123",
|
||||||
|
"status": "SUCCESS",
|
||||||
|
"result": {
|
||||||
|
"plan": {
|
||||||
|
"frequencyPerWeek": 3,
|
||||||
|
"durationMinutes": 42,
|
||||||
|
"bestTimeOfDay": "صبح زود",
|
||||||
|
"moistureLevel": 68,
|
||||||
|
"warning": "در صورت بارش موثر، نوبت سوم این هفته را حذف کنید."
|
||||||
|
},
|
||||||
|
"raw_response": "{\"plan\":{\"frequencyPerWeek\":3,\"durationMinutes\":42}}",
|
||||||
|
"water_balance": {
|
||||||
|
"daily": [
|
||||||
|
{
|
||||||
|
"forecast_date": "2025-03-25",
|
||||||
|
"et0_mm": 4.7,
|
||||||
|
"etc_mm": 5.6,
|
||||||
|
"effective_rainfall_mm": 0.0,
|
||||||
|
"gross_irrigation_mm": 6.2,
|
||||||
|
"irrigation_timing": "06:00-08:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"crop_profile": {
|
||||||
|
"kc_initial": 0.6,
|
||||||
|
"kc_mid": 1.15,
|
||||||
|
"kc_end": 0.8
|
||||||
|
},
|
||||||
|
"active_kc": 1.15
|
||||||
|
},
|
||||||
|
"status": "completed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"code": 201,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "گوجهفرنگی",
|
||||||
|
"light": "آفتاب کامل",
|
||||||
|
"watering": "منظم، هفتهای ۲ تا ۳ بار",
|
||||||
|
"soil": "لومی، غنی از مواد آلی",
|
||||||
|
"temperature": "۲۰ تا ۳۰ درجه سانتیگراد",
|
||||||
|
"planting_season": "بهار",
|
||||||
|
"harvest_time": "۷۰ تا ۹۰ روز پس از کاشت",
|
||||||
|
"spacing": "۴۵ تا ۶۰ سانتیمتر",
|
||||||
|
"fertilizer": "کود NPK متعادل",
|
||||||
|
"created_at": "2025-03-20T10:00:00Z",
|
||||||
|
"updated_at": "2025-03-24T10:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 400,
|
||||||
|
"msg": "داده نامعتبر.",
|
||||||
|
"data": {
|
||||||
|
"name": [
|
||||||
|
"This field is required."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "گیاه با موفقیت حذف شد.",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"code": 404,
|
||||||
|
"msg": "گیاه یافت نشد.",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "گوجهفرنگی",
|
||||||
|
"light": "آفتاب کامل",
|
||||||
|
"watering": "منظم، هفتهای ۲ تا ۳ بار",
|
||||||
|
"soil": "لومی، غنی از مواد آلی",
|
||||||
|
"temperature": "۲۰ تا ۳۰ درجه سانتیگراد",
|
||||||
|
"planting_season": "بهار",
|
||||||
|
"harvest_time": "۷۰ تا ۹۰ روز پس از کاشت",
|
||||||
|
"spacing": "۴۵ تا ۶۰ سانتیمتر",
|
||||||
|
"fertilizer": "کود NPK متعادل",
|
||||||
|
"created_at": "2025-03-20T10:00:00Z",
|
||||||
|
"updated_at": "2025-03-24T10:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"code": 404,
|
||||||
|
"msg": "گیاه یافت نشد.",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "گوجهفرنگی",
|
||||||
|
"light": "آفتاب کامل",
|
||||||
|
"watering": "منظم، هفتهای ۲ تا ۳ بار",
|
||||||
|
"soil": "لومی، غنی از مواد آلی",
|
||||||
|
"temperature": "۲۰ تا ۳۰ درجه سانتیگراد",
|
||||||
|
"planting_season": "بهار",
|
||||||
|
"harvest_time": "۷۰ تا ۹۰ روز پس از کاشت",
|
||||||
|
"spacing": "۴۵ تا ۶۰ سانتیمتر",
|
||||||
|
"fertilizer": "کود NPK متعادل",
|
||||||
|
"created_at": "2025-03-20T10:00:00Z",
|
||||||
|
"updated_at": "2025-03-24T10:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 400,
|
||||||
|
"msg": "داده نامعتبر.",
|
||||||
|
"data": {
|
||||||
|
"name": [
|
||||||
|
"This field may not be blank."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"code": 404,
|
||||||
|
"msg": "گیاه یافت نشد.",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "گوجهفرنگی",
|
||||||
|
"light": "آفتاب کامل",
|
||||||
|
"watering": "منظم، هفتهای ۲ تا ۳ بار",
|
||||||
|
"soil": "لومی، غنی از مواد آلی",
|
||||||
|
"temperature": "۲۰ تا ۳۰ درجه سانتیگراد",
|
||||||
|
"planting_season": "بهار",
|
||||||
|
"harvest_time": "۷۰ تا ۹۰ روز پس از کاشت",
|
||||||
|
"spacing": "۴۵ تا ۶۰ سانتیمتر",
|
||||||
|
"fertilizer": "کود NPK متعادل",
|
||||||
|
"created_at": "2025-03-20T10:00:00Z",
|
||||||
|
"updated_at": "2025-03-24T10:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 400,
|
||||||
|
"msg": "داده نامعتبر.",
|
||||||
|
"data": {
|
||||||
|
"name": [
|
||||||
|
"This field may not be blank."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"code": 404,
|
||||||
|
"msg": "گیاه یافت نشد.",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "گوجهفرنگی",
|
||||||
|
"light": "آفتاب کامل",
|
||||||
|
"watering": "منظم، هفتهای ۲ تا ۳ بار",
|
||||||
|
"soil": "لومی، غنی از مواد آلی",
|
||||||
|
"temperature": "۲۰ تا ۳۰ درجه سانتیگراد",
|
||||||
|
"planting_season": "بهار",
|
||||||
|
"harvest_time": "۷۰ تا ۹۰ روز پس از کاشت",
|
||||||
|
"spacing": "۴۵ تا ۶۰ سانتیمتر",
|
||||||
|
"fertilizer": "کود NPK متعادل",
|
||||||
|
"created_at": "2025-03-20T10:00:00Z",
|
||||||
|
"updated_at": "2025-03-24T10:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"code": 400,
|
||||||
|
"msg": "نام گیاه الزامی است.",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"code": 503,
|
||||||
|
"msg": "سرویس API هنوز پیادهسازی نشده است.",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "گوجهفرنگی",
|
||||||
|
"light": "آفتاب کامل",
|
||||||
|
"watering": "منظم، هفتهای ۲ تا ۳ بار",
|
||||||
|
"soil": "لومی، غنی از مواد آلی",
|
||||||
|
"temperature": "۲۰ تا ۳۰ درجه سانتیگراد",
|
||||||
|
"planting_season": "بهار",
|
||||||
|
"harvest_time": "۷۰ تا ۹۰ روز پس از کاشت",
|
||||||
|
"spacing": "۴۵ تا ۶۰ سانتیمتر",
|
||||||
|
"fertilizer": "کود NPK متعادل",
|
||||||
|
"created_at": "2025-03-20T10:00:00Z",
|
||||||
|
"updated_at": "2025-03-24T10:00:00Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"content_type": "text/plain; charset=utf-8",
|
||||||
|
"body": "سلام، برای بازیابی رطوبت خاک بهتر است آبیاری صبحگاهی را تنظیم کنید."
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"code": 400,
|
||||||
|
"msg": "service_id نامعتبر است: unknown_service"
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"code": 400,
|
||||||
|
"msg": "پارامتر query الزامی است."
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"code": 400,
|
||||||
|
"msg": "برای این service_id، پارامتر user_id الزامی است."
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"code": 202,
|
||||||
|
"msg": "تسک توصیه کودهی در صف قرار گرفت.",
|
||||||
|
"data": {
|
||||||
|
"task_id": "rag-fert-123",
|
||||||
|
"status_url": "/api/rag/recommend/fertilization/rag-fert-123/status/"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"code": 400,
|
||||||
|
"msg": "پارامتر sensor_uuid الزامی است.",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "rag-fert-123",
|
||||||
|
"status": "FAILURE",
|
||||||
|
"error": "خطا در دریافت توصیه کودهی."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "rag-fert-123",
|
||||||
|
"status": "PENDING",
|
||||||
|
"message": "تسک در صف یا یافت نشد."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "rag-fert-123",
|
||||||
|
"status": "PROGRESS",
|
||||||
|
"progress": {
|
||||||
|
"message": "در حال پردازش توصیه کودهی..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "rag-fert-123",
|
||||||
|
"status": "SUCCESS",
|
||||||
|
"result": {
|
||||||
|
"plan": {
|
||||||
|
"npkRatio": "20-20-20",
|
||||||
|
"amountPerHectare": "150 kg/ha",
|
||||||
|
"applicationMethod": "کودآبیاری در دو نوبت",
|
||||||
|
"applicationInterval": "هر ۱۰ روز",
|
||||||
|
"reasoning": "نیتروژن و پتاسیم خاک در محدوده متوسط است و گیاه در فاز رویشی نیاز تغذیهای بالاتری دارد."
|
||||||
|
},
|
||||||
|
"raw_response": "{\"plan\":{\"npkRatio\":\"20-20-20\"}}",
|
||||||
|
"status": "completed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"code": 202,
|
||||||
|
"msg": "تسک توصیه آبیاری در صف قرار گرفت.",
|
||||||
|
"data": {
|
||||||
|
"task_id": "rag-irr-123",
|
||||||
|
"status_url": "/api/rag/recommend/irrigation/rag-irr-123/status/"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"code": 400,
|
||||||
|
"msg": "پارامتر sensor_uuid الزامی است.",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "rag-irr-123",
|
||||||
|
"status": "FAILURE",
|
||||||
|
"error": "خطا در دریافت توصیه آبیاری."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "rag-irr-123",
|
||||||
|
"status": "PENDING",
|
||||||
|
"message": "تسک در صف یا یافت نشد."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "rag-irr-123",
|
||||||
|
"status": "PROGRESS",
|
||||||
|
"progress": {
|
||||||
|
"message": "در حال پردازش توصیه آبیاری..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "rag-irr-123",
|
||||||
|
"status": "SUCCESS",
|
||||||
|
"result": {
|
||||||
|
"plan": {
|
||||||
|
"frequencyPerWeek": 3,
|
||||||
|
"durationMinutes": 42,
|
||||||
|
"bestTimeOfDay": "صبح زود",
|
||||||
|
"moistureLevel": 68,
|
||||||
|
"warning": "در صورت بارش موثر، نوبت سوم این هفته را حذف کنید."
|
||||||
|
},
|
||||||
|
"raw_response": "{\"plan\":{\"frequencyPerWeek\":3,\"durationMinutes\":42}}",
|
||||||
|
"water_balance": {
|
||||||
|
"daily": [
|
||||||
|
{
|
||||||
|
"forecast_date": "2025-03-25",
|
||||||
|
"et0_mm": 4.7,
|
||||||
|
"etc_mm": 5.6,
|
||||||
|
"effective_rainfall_mm": 0.0,
|
||||||
|
"gross_irrigation_mm": 6.2,
|
||||||
|
"irrigation_timing": "06:00-08:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"crop_profile": {
|
||||||
|
"kc_initial": 0.6,
|
||||||
|
"kc_mid": 1.15,
|
||||||
|
"kc_end": 0.8
|
||||||
|
},
|
||||||
|
"active_kc": 1.15
|
||||||
|
},
|
||||||
|
"status": "completed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"code": 201,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"id": 3,
|
||||||
|
"code": "soil_moisture",
|
||||||
|
"name_fa": "رطوبت خاک",
|
||||||
|
"unit": "%",
|
||||||
|
"created_at": "2025-03-24T10:00:00Z",
|
||||||
|
"action": "added"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 400,
|
||||||
|
"msg": "داده نامعتبر.",
|
||||||
|
"data": {
|
||||||
|
"code": [
|
||||||
|
"This field is required."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"uuid_sensor": "550e8400-e29b-41d4-a716-446655440000",
|
||||||
|
"location_id": 12,
|
||||||
|
"soil_moisture": 45.2,
|
||||||
|
"soil_temperature": 22.5,
|
||||||
|
"soil_ph": 6.8,
|
||||||
|
"electrical_conductivity": 1.2,
|
||||||
|
"nitrogen": 30.0,
|
||||||
|
"phosphorus": 15.0,
|
||||||
|
"potassium": 20.0,
|
||||||
|
"plant_ids": [
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"created_at": "2025-03-20T10:00:00Z",
|
||||||
|
"updated_at": "2025-03-24T10:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 400,
|
||||||
|
"msg": "داده نامعتبر.",
|
||||||
|
"data": {
|
||||||
|
"location_id": [
|
||||||
|
"This field is required."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"code": 404,
|
||||||
|
"msg": "location_id یافت نشد.",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"uuid_sensor": "550e8400-e29b-41d4-a716-446655440000",
|
||||||
|
"location_id": 12,
|
||||||
|
"soil_moisture": 45.2,
|
||||||
|
"soil_temperature": 22.5,
|
||||||
|
"soil_ph": 6.8,
|
||||||
|
"electrical_conductivity": 1.2,
|
||||||
|
"nitrogen": 30.0,
|
||||||
|
"phosphorus": 15.0,
|
||||||
|
"potassium": 20.0,
|
||||||
|
"plant_ids": [
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"created_at": "2025-03-20T10:00:00Z",
|
||||||
|
"updated_at": "2025-03-24T10:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 400,
|
||||||
|
"msg": "داده نامعتبر.",
|
||||||
|
"data": {
|
||||||
|
"location_id": [
|
||||||
|
"This field is required."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"code": 404,
|
||||||
|
"msg": "location_id یافت نشد.",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"source": "database",
|
||||||
|
"id": 12,
|
||||||
|
"lon": "51.389000",
|
||||||
|
"lat": "35.689200",
|
||||||
|
"depths": [
|
||||||
|
{
|
||||||
|
"depth_label": "0-5cm",
|
||||||
|
"bdod": 1.31,
|
||||||
|
"cec": 18.4,
|
||||||
|
"cfvo": 2.0,
|
||||||
|
"clay": 24.0,
|
||||||
|
"nitrogen": 0.18,
|
||||||
|
"ocd": 32.0,
|
||||||
|
"ocs": 4.1,
|
||||||
|
"phh2o": 7.2,
|
||||||
|
"sand": 34.0,
|
||||||
|
"silt": 42.0,
|
||||||
|
"soc": 1.6,
|
||||||
|
"wv0010": 0.31,
|
||||||
|
"wv0033": 0.22,
|
||||||
|
"wv1500": 0.11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depth_label": "5-15cm",
|
||||||
|
"bdod": 1.35,
|
||||||
|
"cec": 17.2,
|
||||||
|
"cfvo": 2.3,
|
||||||
|
"clay": 26.0,
|
||||||
|
"nitrogen": 0.16,
|
||||||
|
"ocd": 28.0,
|
||||||
|
"ocs": 3.7,
|
||||||
|
"phh2o": 7.1,
|
||||||
|
"sand": 36.0,
|
||||||
|
"silt": 38.0,
|
||||||
|
"soc": 1.4,
|
||||||
|
"wv0010": 0.29,
|
||||||
|
"wv0033": 0.2,
|
||||||
|
"wv1500": 0.1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depth_label": "15-30cm",
|
||||||
|
"bdod": 1.39,
|
||||||
|
"cec": 15.8,
|
||||||
|
"cfvo": 2.8,
|
||||||
|
"clay": 28.0,
|
||||||
|
"nitrogen": 0.13,
|
||||||
|
"ocd": 22.0,
|
||||||
|
"ocs": 3.2,
|
||||||
|
"phh2o": 7.0,
|
||||||
|
"sand": 38.0,
|
||||||
|
"silt": 34.0,
|
||||||
|
"soc": 1.1,
|
||||||
|
"wv0010": 0.26,
|
||||||
|
"wv0033": 0.18,
|
||||||
|
"wv1500": 0.09
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"code": 202,
|
||||||
|
"msg": "تسک در صف. وضعیت را با task_id بررسی کنید.",
|
||||||
|
"data": {
|
||||||
|
"source": "task",
|
||||||
|
"task_id": "soil-task-123",
|
||||||
|
"lon": 51.389,
|
||||||
|
"lat": 35.6892,
|
||||||
|
"status_url": "/api/soil-data/tasks/soil-task-123/status/"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"code": 400,
|
||||||
|
"msg": "داده نامعتبر.",
|
||||||
|
"data": {
|
||||||
|
"lat": [
|
||||||
|
"This field is required."
|
||||||
|
],
|
||||||
|
"lon": [
|
||||||
|
"This field is required."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"source": "database",
|
||||||
|
"id": 12,
|
||||||
|
"lon": "51.389000",
|
||||||
|
"lat": "35.689200",
|
||||||
|
"depths": [
|
||||||
|
{
|
||||||
|
"depth_label": "0-5cm",
|
||||||
|
"bdod": 1.31,
|
||||||
|
"cec": 18.4,
|
||||||
|
"cfvo": 2.0,
|
||||||
|
"clay": 24.0,
|
||||||
|
"nitrogen": 0.18,
|
||||||
|
"ocd": 32.0,
|
||||||
|
"ocs": 4.1,
|
||||||
|
"phh2o": 7.2,
|
||||||
|
"sand": 34.0,
|
||||||
|
"silt": 42.0,
|
||||||
|
"soc": 1.6,
|
||||||
|
"wv0010": 0.31,
|
||||||
|
"wv0033": 0.22,
|
||||||
|
"wv1500": 0.11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depth_label": "5-15cm",
|
||||||
|
"bdod": 1.35,
|
||||||
|
"cec": 17.2,
|
||||||
|
"cfvo": 2.3,
|
||||||
|
"clay": 26.0,
|
||||||
|
"nitrogen": 0.16,
|
||||||
|
"ocd": 28.0,
|
||||||
|
"ocs": 3.7,
|
||||||
|
"phh2o": 7.1,
|
||||||
|
"sand": 36.0,
|
||||||
|
"silt": 38.0,
|
||||||
|
"soc": 1.4,
|
||||||
|
"wv0010": 0.29,
|
||||||
|
"wv0033": 0.2,
|
||||||
|
"wv1500": 0.1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depth_label": "15-30cm",
|
||||||
|
"bdod": 1.39,
|
||||||
|
"cec": 15.8,
|
||||||
|
"cfvo": 2.8,
|
||||||
|
"clay": 28.0,
|
||||||
|
"nitrogen": 0.13,
|
||||||
|
"ocd": 22.0,
|
||||||
|
"ocs": 3.2,
|
||||||
|
"phh2o": 7.0,
|
||||||
|
"sand": 38.0,
|
||||||
|
"silt": 34.0,
|
||||||
|
"soc": 1.1,
|
||||||
|
"wv0010": 0.26,
|
||||||
|
"wv0033": 0.18,
|
||||||
|
"wv1500": 0.09
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"code": 202,
|
||||||
|
"msg": "تسک در صف. وضعیت را با task_id بررسی کنید.",
|
||||||
|
"data": {
|
||||||
|
"source": "task",
|
||||||
|
"task_id": "soil-task-123",
|
||||||
|
"lon": 51.389,
|
||||||
|
"lat": 35.6892,
|
||||||
|
"status_url": "/api/soil-data/tasks/soil-task-123/status/"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 400,
|
||||||
|
"msg": "داده نامعتبر.",
|
||||||
|
"data": {
|
||||||
|
"lat": [
|
||||||
|
"A valid number is required."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "soil-task-123",
|
||||||
|
"status": "FAILURE",
|
||||||
|
"error": "خطا در واکشی داده خاک."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "soil-task-123",
|
||||||
|
"status": "PENDING",
|
||||||
|
"message": "تسک در صف یا یافت نشد."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "soil-task-123",
|
||||||
|
"status": "PROGRESS",
|
||||||
|
"progress": {
|
||||||
|
"step": "fetch",
|
||||||
|
"percent": 60
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "soil-task-123",
|
||||||
|
"status": "SUCCESS",
|
||||||
|
"result": {
|
||||||
|
"source": "database",
|
||||||
|
"id": 12,
|
||||||
|
"lon": "51.389000",
|
||||||
|
"lat": "35.689200",
|
||||||
|
"depths": [
|
||||||
|
{
|
||||||
|
"depth_label": "0-5cm",
|
||||||
|
"bdod": 1.31,
|
||||||
|
"cec": 18.4,
|
||||||
|
"cfvo": 2.0,
|
||||||
|
"clay": 24.0,
|
||||||
|
"nitrogen": 0.18,
|
||||||
|
"ocd": 32.0,
|
||||||
|
"ocs": 4.1,
|
||||||
|
"phh2o": 7.2,
|
||||||
|
"sand": 34.0,
|
||||||
|
"silt": 42.0,
|
||||||
|
"soc": 1.6,
|
||||||
|
"wv0010": 0.31,
|
||||||
|
"wv0033": 0.22,
|
||||||
|
"wv1500": 0.11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depth_label": "5-15cm",
|
||||||
|
"bdod": 1.35,
|
||||||
|
"cec": 17.2,
|
||||||
|
"cfvo": 2.3,
|
||||||
|
"clay": 26.0,
|
||||||
|
"nitrogen": 0.16,
|
||||||
|
"ocd": 28.0,
|
||||||
|
"ocs": 3.7,
|
||||||
|
"phh2o": 7.1,
|
||||||
|
"sand": 36.0,
|
||||||
|
"silt": 38.0,
|
||||||
|
"soc": 1.4,
|
||||||
|
"wv0010": 0.29,
|
||||||
|
"wv0033": 0.2,
|
||||||
|
"wv1500": 0.1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depth_label": "15-30cm",
|
||||||
|
"bdod": 1.39,
|
||||||
|
"cec": 15.8,
|
||||||
|
"cfvo": 2.8,
|
||||||
|
"clay": 28.0,
|
||||||
|
"nitrogen": 0.13,
|
||||||
|
"ocd": 22.0,
|
||||||
|
"ocs": 3.2,
|
||||||
|
"phh2o": 7.0,
|
||||||
|
"sand": 38.0,
|
||||||
|
"silt": 34.0,
|
||||||
|
"soc": 1.1,
|
||||||
|
"wv0010": 0.26,
|
||||||
|
"wv0033": 0.18,
|
||||||
|
"wv1500": 0.09
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "sample-task-123"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "sample-task-123",
|
||||||
|
"status": "FAILURE",
|
||||||
|
"error": "Sample task failed."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "sample-task-123",
|
||||||
|
"status": "PENDING",
|
||||||
|
"message": "تسک در صف یا یافت نشد."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "sample-task-123",
|
||||||
|
"status": "PROGRESS",
|
||||||
|
"progress": {
|
||||||
|
"current": 1,
|
||||||
|
"total": 3,
|
||||||
|
"message": "در حال پردازش..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "success",
|
||||||
|
"data": {
|
||||||
|
"task_id": "sample-task-123",
|
||||||
|
"status": "SUCCESS",
|
||||||
|
"result": "done"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -73,4 +73,4 @@ class SoilDataTaskResponseSerializer(serializers.Serializer):
|
|||||||
task_id = serializers.CharField()
|
task_id = serializers.CharField()
|
||||||
lon = serializers.FloatField(source="longitude")
|
lon = serializers.FloatField(source="longitude")
|
||||||
lat = serializers.FloatField(source="latitude")
|
lat = serializers.FloatField(source="latitude")
|
||||||
status_url = serializers.URLField(required=False)
|
status_url = serializers.CharField(required=False)
|
||||||
|
|||||||
+62
-23
@@ -9,15 +9,50 @@ from rest_framework import serializers as drf_serializers
|
|||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
|
from config.openapi import (
|
||||||
|
build_envelope_serializer,
|
||||||
|
build_response,
|
||||||
|
build_task_status_data_serializer,
|
||||||
|
)
|
||||||
from .models import SoilLocation
|
from .models import SoilLocation
|
||||||
from .serializers import (
|
from .serializers import (
|
||||||
SoilDataRequestSerializer,
|
SoilDataRequestSerializer,
|
||||||
|
SoilDepthDataSerializer,
|
||||||
SoilDataTaskResponseSerializer,
|
SoilDataTaskResponseSerializer,
|
||||||
SoilLocationResponseSerializer,
|
SoilLocationResponseSerializer,
|
||||||
)
|
)
|
||||||
from .tasks import fetch_soil_data_task
|
from .tasks import fetch_soil_data_task
|
||||||
|
|
||||||
|
|
||||||
|
SoilLocationPayloadSerializer = inline_serializer(
|
||||||
|
name="SoilLocationPayloadSerializer",
|
||||||
|
fields={
|
||||||
|
"source": drf_serializers.CharField(),
|
||||||
|
"id": drf_serializers.IntegerField(),
|
||||||
|
"lon": drf_serializers.DecimalField(max_digits=9, decimal_places=6),
|
||||||
|
"lat": drf_serializers.DecimalField(max_digits=9, decimal_places=6),
|
||||||
|
"depths": SoilDepthDataSerializer(many=True),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
SoilDataResponseSerializer = build_envelope_serializer(
|
||||||
|
"SoilDataResponseSerializer",
|
||||||
|
SoilLocationPayloadSerializer,
|
||||||
|
)
|
||||||
|
SoilTaskQueuedResponseSerializer = build_envelope_serializer(
|
||||||
|
"SoilTaskQueuedResponseSerializer",
|
||||||
|
SoilDataTaskResponseSerializer,
|
||||||
|
)
|
||||||
|
SoilErrorResponseSerializer = build_envelope_serializer(
|
||||||
|
"SoilErrorResponseSerializer",
|
||||||
|
data_required=False,
|
||||||
|
allow_null=True,
|
||||||
|
)
|
||||||
|
SoilTaskStatusResponseSerializer = build_envelope_serializer(
|
||||||
|
"SoilTaskStatusResponseSerializer",
|
||||||
|
build_task_status_data_serializer("SoilTaskStatusDataSerializer"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class SoilDataView(APIView):
|
class SoilDataView(APIView):
|
||||||
"""
|
"""
|
||||||
API خاک: مختصات جغرافیایی را میگیرد.
|
API خاک: مختصات جغرافیایی را میگیرد.
|
||||||
@@ -49,9 +84,18 @@ class SoilDataView(APIView):
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
responses={
|
responses={
|
||||||
200: OpenApiResponse(description="داده خاک از دیتابیس"),
|
200: build_response(
|
||||||
202: OpenApiResponse(description="تسک در صف قرار گرفت"),
|
SoilDataResponseSerializer,
|
||||||
400: OpenApiResponse(description="داده نامعتبر"),
|
"داده خاک از دیتابیس بازگردانده شد.",
|
||||||
|
),
|
||||||
|
202: build_response(
|
||||||
|
SoilTaskQueuedResponseSerializer,
|
||||||
|
"تسک واکشی داده خاک در صف قرار گرفت.",
|
||||||
|
),
|
||||||
|
400: build_response(
|
||||||
|
SoilErrorResponseSerializer,
|
||||||
|
"پارامترهای ورودی نامعتبر هستند.",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
@@ -63,9 +107,18 @@ class SoilDataView(APIView):
|
|||||||
description="با ارسال lat و lon در بدنه، داده خاک از DB یا از طریق تسک Celery برگردانده میشود.",
|
description="با ارسال lat و lon در بدنه، داده خاک از DB یا از طریق تسک Celery برگردانده میشود.",
|
||||||
request=SoilDataRequestSerializer,
|
request=SoilDataRequestSerializer,
|
||||||
responses={
|
responses={
|
||||||
200: OpenApiResponse(description="داده خاک از دیتابیس"),
|
200: build_response(
|
||||||
202: OpenApiResponse(description="تسک در صف قرار گرفت"),
|
SoilDataResponseSerializer,
|
||||||
400: OpenApiResponse(description="داده نامعتبر"),
|
"داده خاک از دیتابیس بازگردانده شد.",
|
||||||
|
),
|
||||||
|
202: build_response(
|
||||||
|
SoilTaskQueuedResponseSerializer,
|
||||||
|
"تسک واکشی داده خاک در صف قرار گرفت.",
|
||||||
|
),
|
||||||
|
400: build_response(
|
||||||
|
SoilErrorResponseSerializer,
|
||||||
|
"پارامترهای ورودی نامعتبر هستند.",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
examples=[
|
examples=[
|
||||||
OpenApiExample(
|
OpenApiExample(
|
||||||
@@ -141,23 +194,9 @@ class SoilDataTaskStatusView(APIView):
|
|||||||
summary="وضعیت تسک داده خاک",
|
summary="وضعیت تسک داده خاک",
|
||||||
description="وضعیت تسک Celery واکشی داده خاک را برمیگرداند.",
|
description="وضعیت تسک Celery واکشی داده خاک را برمیگرداند.",
|
||||||
responses={
|
responses={
|
||||||
200: inline_serializer(
|
200: build_response(
|
||||||
name="SoilTaskStatusResponse",
|
SoilTaskStatusResponseSerializer,
|
||||||
fields={
|
"وضعیت فعلی تسک واکشی داده خاک.",
|
||||||
"code": drf_serializers.IntegerField(),
|
|
||||||
"msg": drf_serializers.CharField(),
|
|
||||||
"data": inline_serializer(
|
|
||||||
name="SoilTaskStatusData",
|
|
||||||
fields={
|
|
||||||
"task_id": drf_serializers.CharField(),
|
|
||||||
"status": drf_serializers.CharField(),
|
|
||||||
"message": drf_serializers.CharField(required=False),
|
|
||||||
"progress": drf_serializers.DictField(required=False),
|
|
||||||
"result": drf_serializers.JSONField(required=False),
|
|
||||||
"error": drf_serializers.CharField(required=False),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
+87
-16
@@ -9,11 +9,32 @@ from rest_framework import status
|
|||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
|
from config.openapi import build_envelope_serializer, build_response
|
||||||
from .models import Plant
|
from .models import Plant
|
||||||
from .serializers import PlantSerializer
|
from .serializers import PlantSerializer
|
||||||
from .services import fetch_plant_info_from_api
|
from .services import fetch_plant_info_from_api
|
||||||
|
|
||||||
|
|
||||||
|
PlantListResponseSerializer = build_envelope_serializer(
|
||||||
|
"PlantListResponseSerializer",
|
||||||
|
PlantSerializer,
|
||||||
|
many=True,
|
||||||
|
)
|
||||||
|
PlantDetailResponseSerializer = build_envelope_serializer(
|
||||||
|
"PlantDetailResponseSerializer",
|
||||||
|
PlantSerializer,
|
||||||
|
)
|
||||||
|
PlantValidationErrorSerializer = build_envelope_serializer(
|
||||||
|
"PlantValidationErrorSerializer",
|
||||||
|
data_required=False,
|
||||||
|
allow_null=True,
|
||||||
|
)
|
||||||
|
PlantFetchInfoResponseSerializer = build_envelope_serializer(
|
||||||
|
"PlantFetchInfoResponseSerializer",
|
||||||
|
PlantSerializer,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PlantListCreateView(APIView):
|
class PlantListCreateView(APIView):
|
||||||
"""لیست تمام گیاهان و ایجاد گیاه جدید."""
|
"""لیست تمام گیاهان و ایجاد گیاه جدید."""
|
||||||
|
|
||||||
@@ -21,7 +42,12 @@ class PlantListCreateView(APIView):
|
|||||||
tags=["Plant"],
|
tags=["Plant"],
|
||||||
summary="لیست گیاهان",
|
summary="لیست گیاهان",
|
||||||
description="لیست تمام گیاهان ذخیرهشده را برمیگرداند.",
|
description="لیست تمام گیاهان ذخیرهشده را برمیگرداند.",
|
||||||
responses={200: PlantSerializer(many=True)},
|
responses={
|
||||||
|
200: build_response(
|
||||||
|
PlantListResponseSerializer,
|
||||||
|
"لیست گیاهان ذخیرهشده.",
|
||||||
|
),
|
||||||
|
},
|
||||||
)
|
)
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
plants = Plant.objects.all()
|
plants = Plant.objects.all()
|
||||||
@@ -37,8 +63,14 @@ class PlantListCreateView(APIView):
|
|||||||
description="یک گیاه جدید با مشخصات دادهشده ایجاد میکند.",
|
description="یک گیاه جدید با مشخصات دادهشده ایجاد میکند.",
|
||||||
request=PlantSerializer,
|
request=PlantSerializer,
|
||||||
responses={
|
responses={
|
||||||
201: PlantSerializer,
|
201: build_response(
|
||||||
400: OpenApiResponse(description="داده نامعتبر"),
|
PlantDetailResponseSerializer,
|
||||||
|
"گیاه جدید با موفقیت ایجاد شد.",
|
||||||
|
),
|
||||||
|
400: build_response(
|
||||||
|
PlantValidationErrorSerializer,
|
||||||
|
"داده ورودی نامعتبر است.",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
examples=[
|
examples=[
|
||||||
OpenApiExample(
|
OpenApiExample(
|
||||||
@@ -83,8 +115,14 @@ class PlantDetailView(APIView):
|
|||||||
summary="جزئیات گیاه",
|
summary="جزئیات گیاه",
|
||||||
description="مشخصات یک گیاه را بر اساس شناسه برمیگرداند.",
|
description="مشخصات یک گیاه را بر اساس شناسه برمیگرداند.",
|
||||||
responses={
|
responses={
|
||||||
200: PlantSerializer,
|
200: build_response(
|
||||||
404: OpenApiResponse(description="گیاه یافت نشد"),
|
PlantDetailResponseSerializer,
|
||||||
|
"جزئیات گیاه.",
|
||||||
|
),
|
||||||
|
404: build_response(
|
||||||
|
PlantValidationErrorSerializer,
|
||||||
|
"گیاه یافت نشد.",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
def get(self, request, pk):
|
def get(self, request, pk):
|
||||||
@@ -106,9 +144,18 @@ class PlantDetailView(APIView):
|
|||||||
description="تمام فیلدهای یک گیاه را آپدیت میکند.",
|
description="تمام فیلدهای یک گیاه را آپدیت میکند.",
|
||||||
request=PlantSerializer,
|
request=PlantSerializer,
|
||||||
responses={
|
responses={
|
||||||
200: PlantSerializer,
|
200: build_response(
|
||||||
400: OpenApiResponse(description="داده نامعتبر"),
|
PlantDetailResponseSerializer,
|
||||||
404: OpenApiResponse(description="گیاه یافت نشد"),
|
"گیاه با موفقیت بهروزرسانی شد.",
|
||||||
|
),
|
||||||
|
400: build_response(
|
||||||
|
PlantValidationErrorSerializer,
|
||||||
|
"داده ورودی نامعتبر است.",
|
||||||
|
),
|
||||||
|
404: build_response(
|
||||||
|
PlantValidationErrorSerializer,
|
||||||
|
"گیاه یافت نشد.",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
def put(self, request, pk):
|
def put(self, request, pk):
|
||||||
@@ -136,9 +183,18 @@ class PlantDetailView(APIView):
|
|||||||
description="فقط فیلدهای ارسالشده آپدیت میشوند.",
|
description="فقط فیلدهای ارسالشده آپدیت میشوند.",
|
||||||
request=PlantSerializer,
|
request=PlantSerializer,
|
||||||
responses={
|
responses={
|
||||||
200: PlantSerializer,
|
200: build_response(
|
||||||
400: OpenApiResponse(description="داده نامعتبر"),
|
PlantDetailResponseSerializer,
|
||||||
404: OpenApiResponse(description="گیاه یافت نشد"),
|
"گیاه با موفقیت بهروزرسانی شد.",
|
||||||
|
),
|
||||||
|
400: build_response(
|
||||||
|
PlantValidationErrorSerializer,
|
||||||
|
"داده ورودی نامعتبر است.",
|
||||||
|
),
|
||||||
|
404: build_response(
|
||||||
|
PlantValidationErrorSerializer,
|
||||||
|
"گیاه یافت نشد.",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
def patch(self, request, pk):
|
def patch(self, request, pk):
|
||||||
@@ -165,8 +221,14 @@ class PlantDetailView(APIView):
|
|||||||
summary="حذف گیاه",
|
summary="حذف گیاه",
|
||||||
description="یک گیاه را حذف میکند.",
|
description="یک گیاه را حذف میکند.",
|
||||||
responses={
|
responses={
|
||||||
200: OpenApiResponse(description="حذف موفق"),
|
200: build_response(
|
||||||
404: OpenApiResponse(description="گیاه یافت نشد"),
|
PlantValidationErrorSerializer,
|
||||||
|
"گیاه با موفقیت حذف شد.",
|
||||||
|
),
|
||||||
|
404: build_response(
|
||||||
|
PlantValidationErrorSerializer,
|
||||||
|
"گیاه یافت نشد.",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
def delete(self, request, pk):
|
def delete(self, request, pk):
|
||||||
@@ -197,9 +259,18 @@ class PlantFetchInfoView(APIView):
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
responses={
|
responses={
|
||||||
200: PlantSerializer,
|
200: build_response(
|
||||||
400: OpenApiResponse(description="نام گیاه ارسال نشده"),
|
PlantFetchInfoResponseSerializer,
|
||||||
503: OpenApiResponse(description="سرویس در دسترس نیست"),
|
"اطلاعات گیاه از سرویس خارجی دریافت شد.",
|
||||||
|
),
|
||||||
|
400: build_response(
|
||||||
|
PlantValidationErrorSerializer,
|
||||||
|
"نام گیاه ارسال نشده است.",
|
||||||
|
),
|
||||||
|
503: build_response(
|
||||||
|
PlantValidationErrorSerializer,
|
||||||
|
"سرویس خارجی در دسترس نیست.",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
examples=[
|
examples=[
|
||||||
OpenApiExample(
|
OpenApiExample(
|
||||||
|
|||||||
+58
-61
@@ -1,7 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
ویوهای RAG — چت با استریم
|
ویوهای RAG — چت با استریم
|
||||||
"""
|
"""
|
||||||
|
import logging
|
||||||
|
|
||||||
from django.http import StreamingHttpResponse
|
from django.http import StreamingHttpResponse
|
||||||
|
from drf_spectacular.types import OpenApiTypes
|
||||||
from drf_spectacular.utils import (
|
from drf_spectacular.utils import (
|
||||||
OpenApiExample,
|
OpenApiExample,
|
||||||
OpenApiResponse,
|
OpenApiResponse,
|
||||||
@@ -13,14 +16,46 @@ from rest_framework import serializers as drf_serializers
|
|||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
import logging
|
|
||||||
|
|
||||||
|
from config.openapi import (
|
||||||
|
build_envelope_serializer,
|
||||||
|
build_message_response_serializer,
|
||||||
|
build_response,
|
||||||
|
build_task_queue_data_serializer,
|
||||||
|
build_task_status_data_serializer,
|
||||||
|
)
|
||||||
from .chat import chat_rag_stream
|
from .chat import chat_rag_stream
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
RagChatErrorResponseSerializer = build_message_response_serializer(
|
||||||
|
"RagChatErrorResponseSerializer"
|
||||||
|
)
|
||||||
|
RagIrrigationQueueResponseSerializer = build_envelope_serializer(
|
||||||
|
"RagIrrigationQueueResponseSerializer",
|
||||||
|
build_task_queue_data_serializer("RagIrrigationQueueDataSerializer"),
|
||||||
|
)
|
||||||
|
RagIrrigationStatusResponseSerializer = build_envelope_serializer(
|
||||||
|
"RagIrrigationStatusResponseSerializer",
|
||||||
|
build_task_status_data_serializer("RagIrrigationStatusDataSerializer"),
|
||||||
|
)
|
||||||
|
RagFertilizationQueueResponseSerializer = build_envelope_serializer(
|
||||||
|
"RagFertilizationQueueResponseSerializer",
|
||||||
|
build_task_queue_data_serializer("RagFertilizationQueueDataSerializer"),
|
||||||
|
)
|
||||||
|
RagFertilizationStatusResponseSerializer = build_envelope_serializer(
|
||||||
|
"RagFertilizationStatusResponseSerializer",
|
||||||
|
build_task_status_data_serializer("RagFertilizationStatusDataSerializer"),
|
||||||
|
)
|
||||||
|
RagValidationErrorResponseSerializer = build_envelope_serializer(
|
||||||
|
"RagValidationErrorResponseSerializer",
|
||||||
|
data_required=False,
|
||||||
|
allow_null=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ChatView(APIView):
|
class ChatView(APIView):
|
||||||
"""
|
"""
|
||||||
چت RAG با استریم.
|
چت RAG با استریم.
|
||||||
@@ -44,10 +79,12 @@ class ChatView(APIView):
|
|||||||
),
|
),
|
||||||
responses={
|
responses={
|
||||||
200: OpenApiResponse(
|
200: OpenApiResponse(
|
||||||
|
response=OpenApiTypes.STR,
|
||||||
description="پاسخ استریم متنی (text/plain)",
|
description="پاسخ استریم متنی (text/plain)",
|
||||||
),
|
),
|
||||||
400: OpenApiResponse(
|
400: build_response(
|
||||||
description="پارامتر ورودی نامعتبر",
|
RagChatErrorResponseSerializer,
|
||||||
|
"پارامترهای ورودی نامعتبر هستند.",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
examples=[
|
examples=[
|
||||||
@@ -152,21 +189,14 @@ class IrrigationRecommendationView(APIView):
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
responses={
|
responses={
|
||||||
202: inline_serializer(
|
202: build_response(
|
||||||
name="IrrigationRecommendationResponse",
|
RagIrrigationQueueResponseSerializer,
|
||||||
fields={
|
"تسک توصیه آبیاری در صف قرار گرفت.",
|
||||||
"code": drf_serializers.IntegerField(),
|
|
||||||
"msg": drf_serializers.CharField(),
|
|
||||||
"data": inline_serializer(
|
|
||||||
name="IrrigationRecommendationData",
|
|
||||||
fields={
|
|
||||||
"task_id": drf_serializers.CharField(),
|
|
||||||
"status_url": drf_serializers.CharField(),
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
},
|
400: build_response(
|
||||||
|
RagValidationErrorResponseSerializer,
|
||||||
|
"پارامتر ورودی نامعتبر است.",
|
||||||
),
|
),
|
||||||
400: OpenApiResponse(description="پارامتر ورودی نامعتبر"),
|
|
||||||
},
|
},
|
||||||
examples=[
|
examples=[
|
||||||
OpenApiExample(
|
OpenApiExample(
|
||||||
@@ -219,22 +249,9 @@ class IrrigationRecommendationStatusView(APIView):
|
|||||||
summary="وضعیت تسک توصیه آبیاری",
|
summary="وضعیت تسک توصیه آبیاری",
|
||||||
description="وضعیت تسک Celery توصیه آبیاری را برمیگرداند.",
|
description="وضعیت تسک Celery توصیه آبیاری را برمیگرداند.",
|
||||||
responses={
|
responses={
|
||||||
200: inline_serializer(
|
200: build_response(
|
||||||
name="IrrigationRecommendationStatusResponse",
|
RagIrrigationStatusResponseSerializer,
|
||||||
fields={
|
"وضعیت فعلی تسک توصیه آبیاری.",
|
||||||
"code": drf_serializers.IntegerField(),
|
|
||||||
"msg": drf_serializers.CharField(),
|
|
||||||
"data": inline_serializer(
|
|
||||||
name="IrrigationRecommendationStatusData",
|
|
||||||
fields={
|
|
||||||
"task_id": drf_serializers.CharField(),
|
|
||||||
"status": drf_serializers.CharField(),
|
|
||||||
"result": drf_serializers.JSONField(required=False),
|
|
||||||
"progress": drf_serializers.DictField(required=False),
|
|
||||||
"error": drf_serializers.CharField(required=False),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -281,21 +298,14 @@ class FertilizationRecommendationView(APIView):
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
responses={
|
responses={
|
||||||
202: inline_serializer(
|
202: build_response(
|
||||||
name="FertilizationRecommendationResponse",
|
RagFertilizationQueueResponseSerializer,
|
||||||
fields={
|
"تسک توصیه کودهی در صف قرار گرفت.",
|
||||||
"code": drf_serializers.IntegerField(),
|
|
||||||
"msg": drf_serializers.CharField(),
|
|
||||||
"data": inline_serializer(
|
|
||||||
name="FertilizationRecommendationData",
|
|
||||||
fields={
|
|
||||||
"task_id": drf_serializers.CharField(),
|
|
||||||
"status_url": drf_serializers.CharField(),
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
},
|
400: build_response(
|
||||||
|
RagValidationErrorResponseSerializer,
|
||||||
|
"پارامتر ورودی نامعتبر است.",
|
||||||
),
|
),
|
||||||
400: OpenApiResponse(description="پارامتر ورودی نامعتبر"),
|
|
||||||
},
|
},
|
||||||
examples=[
|
examples=[
|
||||||
OpenApiExample(
|
OpenApiExample(
|
||||||
@@ -346,22 +356,9 @@ class FertilizationRecommendationStatusView(APIView):
|
|||||||
summary="وضعیت تسک توصیه کودهی",
|
summary="وضعیت تسک توصیه کودهی",
|
||||||
description="وضعیت تسک Celery توصیه کودهی را برمیگرداند.",
|
description="وضعیت تسک Celery توصیه کودهی را برمیگرداند.",
|
||||||
responses={
|
responses={
|
||||||
200: inline_serializer(
|
200: build_response(
|
||||||
name="FertilizationRecommendationStatusResponse",
|
RagFertilizationStatusResponseSerializer,
|
||||||
fields={
|
"وضعیت فعلی تسک توصیه کودهی.",
|
||||||
"code": drf_serializers.IntegerField(),
|
|
||||||
"msg": drf_serializers.CharField(),
|
|
||||||
"data": inline_serializer(
|
|
||||||
name="FertilizationRecommendationStatusData",
|
|
||||||
fields={
|
|
||||||
"task_id": drf_serializers.CharField(),
|
|
||||||
"status": drf_serializers.CharField(),
|
|
||||||
"result": drf_serializers.JSONField(required=False),
|
|
||||||
"progress": drf_serializers.DictField(required=False),
|
|
||||||
"error": drf_serializers.CharField(required=False),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,829 @@
|
|||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
BASE_DIR = Path(__file__).resolve().parents[1]
|
||||||
|
MOCK_DIR = BASE_DIR / "json" / "mock_data"
|
||||||
|
|
||||||
|
|
||||||
|
def queue_response(message, task_id, status_url, **extra):
|
||||||
|
data = {
|
||||||
|
"task_id": task_id,
|
||||||
|
"status_url": status_url,
|
||||||
|
}
|
||||||
|
data.update(extra)
|
||||||
|
return {
|
||||||
|
"code": 202,
|
||||||
|
"msg": message,
|
||||||
|
"data": data,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def ok_response(data, message="success", code=200):
|
||||||
|
return {
|
||||||
|
"code": code,
|
||||||
|
"msg": message,
|
||||||
|
"data": data,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def error_response(code, message, data=None):
|
||||||
|
return {
|
||||||
|
"code": code,
|
||||||
|
"msg": message,
|
||||||
|
"data": data,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def task_pending(task_id):
|
||||||
|
return ok_response(
|
||||||
|
{
|
||||||
|
"task_id": task_id,
|
||||||
|
"status": "PENDING",
|
||||||
|
"message": "تسک در صف یا یافت نشد.",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def task_progress(task_id, progress):
|
||||||
|
return ok_response(
|
||||||
|
{
|
||||||
|
"task_id": task_id,
|
||||||
|
"status": "PROGRESS",
|
||||||
|
"progress": progress,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def task_success(task_id, result):
|
||||||
|
return ok_response(
|
||||||
|
{
|
||||||
|
"task_id": task_id,
|
||||||
|
"status": "SUCCESS",
|
||||||
|
"result": result,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def task_failure(task_id, error):
|
||||||
|
return ok_response(
|
||||||
|
{
|
||||||
|
"task_id": task_id,
|
||||||
|
"status": "FAILURE",
|
||||||
|
"error": error,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
PLANT = {
|
||||||
|
"id": 1,
|
||||||
|
"name": "گوجهفرنگی",
|
||||||
|
"light": "آفتاب کامل",
|
||||||
|
"watering": "منظم، هفتهای ۲ تا ۳ بار",
|
||||||
|
"soil": "لومی، غنی از مواد آلی",
|
||||||
|
"temperature": "۲۰ تا ۳۰ درجه سانتیگراد",
|
||||||
|
"planting_season": "بهار",
|
||||||
|
"harvest_time": "۷۰ تا ۹۰ روز پس از کاشت",
|
||||||
|
"spacing": "۴۵ تا ۶۰ سانتیمتر",
|
||||||
|
"fertilizer": "کود NPK متعادل",
|
||||||
|
"created_at": "2025-03-20T10:00:00Z",
|
||||||
|
"updated_at": "2025-03-24T10:00:00Z",
|
||||||
|
}
|
||||||
|
|
||||||
|
IRRIGATION_METHOD = {
|
||||||
|
"id": 1,
|
||||||
|
"name": "آبیاری قطرهای",
|
||||||
|
"category": "موضعی",
|
||||||
|
"description": "آبیاری با دبی کم و راندمان بالا",
|
||||||
|
"water_efficiency_percent": 90.0,
|
||||||
|
"water_pressure_required": "۱-۲ اتمسفر",
|
||||||
|
"flow_rate": "۲-۸ لیتر در ساعت",
|
||||||
|
"coverage_area": "بسته به طراحی سیستم",
|
||||||
|
"soil_type": "اکثر خاکها",
|
||||||
|
"climate_suitability": "گرم و خشک",
|
||||||
|
"created_at": "2025-03-20T10:00:00Z",
|
||||||
|
"updated_at": "2025-03-24T10:00:00Z",
|
||||||
|
}
|
||||||
|
|
||||||
|
SOIL_DEPTHS = [
|
||||||
|
{
|
||||||
|
"depth_label": "0-5cm",
|
||||||
|
"bdod": 1.31,
|
||||||
|
"cec": 18.4,
|
||||||
|
"cfvo": 2.0,
|
||||||
|
"clay": 24.0,
|
||||||
|
"nitrogen": 0.18,
|
||||||
|
"ocd": 32.0,
|
||||||
|
"ocs": 4.1,
|
||||||
|
"phh2o": 7.2,
|
||||||
|
"sand": 34.0,
|
||||||
|
"silt": 42.0,
|
||||||
|
"soc": 1.6,
|
||||||
|
"wv0010": 0.31,
|
||||||
|
"wv0033": 0.22,
|
||||||
|
"wv1500": 0.11,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depth_label": "5-15cm",
|
||||||
|
"bdod": 1.35,
|
||||||
|
"cec": 17.2,
|
||||||
|
"cfvo": 2.3,
|
||||||
|
"clay": 26.0,
|
||||||
|
"nitrogen": 0.16,
|
||||||
|
"ocd": 28.0,
|
||||||
|
"ocs": 3.7,
|
||||||
|
"phh2o": 7.1,
|
||||||
|
"sand": 36.0,
|
||||||
|
"silt": 38.0,
|
||||||
|
"soc": 1.4,
|
||||||
|
"wv0010": 0.29,
|
||||||
|
"wv0033": 0.2,
|
||||||
|
"wv1500": 0.1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depth_label": "15-30cm",
|
||||||
|
"bdod": 1.39,
|
||||||
|
"cec": 15.8,
|
||||||
|
"cfvo": 2.8,
|
||||||
|
"clay": 28.0,
|
||||||
|
"nitrogen": 0.13,
|
||||||
|
"ocd": 22.0,
|
||||||
|
"ocs": 3.2,
|
||||||
|
"phh2o": 7.0,
|
||||||
|
"sand": 38.0,
|
||||||
|
"silt": 34.0,
|
||||||
|
"soc": 1.1,
|
||||||
|
"wv0010": 0.26,
|
||||||
|
"wv0033": 0.18,
|
||||||
|
"wv1500": 0.09,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
SOIL_LOCATION = {
|
||||||
|
"source": "database",
|
||||||
|
"id": 12,
|
||||||
|
"lon": "51.389000",
|
||||||
|
"lat": "35.689200",
|
||||||
|
"depths": SOIL_DEPTHS,
|
||||||
|
}
|
||||||
|
|
||||||
|
SOIL_TASK_DATA = {
|
||||||
|
"source": "task",
|
||||||
|
"task_id": "soil-task-123",
|
||||||
|
"lon": 51.389,
|
||||||
|
"lat": 35.6892,
|
||||||
|
"status_url": "/api/soil-data/tasks/soil-task-123/status/",
|
||||||
|
}
|
||||||
|
|
||||||
|
SENSOR_DATA = {
|
||||||
|
"uuid_sensor": "550e8400-e29b-41d4-a716-446655440000",
|
||||||
|
"location_id": 12,
|
||||||
|
"soil_moisture": 45.2,
|
||||||
|
"soil_temperature": 22.5,
|
||||||
|
"soil_ph": 6.8,
|
||||||
|
"electrical_conductivity": 1.2,
|
||||||
|
"nitrogen": 30.0,
|
||||||
|
"phosphorus": 15.0,
|
||||||
|
"potassium": 20.0,
|
||||||
|
"plant_ids": [1],
|
||||||
|
"created_at": "2025-03-20T10:00:00Z",
|
||||||
|
"updated_at": "2025-03-24T10:00:00Z",
|
||||||
|
}
|
||||||
|
|
||||||
|
SENSOR_PARAMETER = {
|
||||||
|
"id": 3,
|
||||||
|
"code": "soil_moisture",
|
||||||
|
"name_fa": "رطوبت خاک",
|
||||||
|
"unit": "%",
|
||||||
|
"created_at": "2025-03-24T10:00:00Z",
|
||||||
|
"action": "added",
|
||||||
|
}
|
||||||
|
|
||||||
|
IRRIGATION_RECOMMENDATION_RESULT = {
|
||||||
|
"plan": {
|
||||||
|
"frequencyPerWeek": 3,
|
||||||
|
"durationMinutes": 42,
|
||||||
|
"bestTimeOfDay": "صبح زود",
|
||||||
|
"moistureLevel": 68,
|
||||||
|
"warning": "در صورت بارش موثر، نوبت سوم این هفته را حذف کنید.",
|
||||||
|
},
|
||||||
|
"raw_response": "{\"plan\":{\"frequencyPerWeek\":3,\"durationMinutes\":42}}",
|
||||||
|
"water_balance": {
|
||||||
|
"daily": [
|
||||||
|
{
|
||||||
|
"forecast_date": "2025-03-25",
|
||||||
|
"et0_mm": 4.7,
|
||||||
|
"etc_mm": 5.6,
|
||||||
|
"effective_rainfall_mm": 0.0,
|
||||||
|
"gross_irrigation_mm": 6.2,
|
||||||
|
"irrigation_timing": "06:00-08:00",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"crop_profile": {
|
||||||
|
"kc_initial": 0.6,
|
||||||
|
"kc_mid": 1.15,
|
||||||
|
"kc_end": 0.8,
|
||||||
|
},
|
||||||
|
"active_kc": 1.15,
|
||||||
|
},
|
||||||
|
"status": "completed",
|
||||||
|
}
|
||||||
|
|
||||||
|
FERTILIZATION_RECOMMENDATION_RESULT = {
|
||||||
|
"plan": {
|
||||||
|
"npkRatio": "20-20-20",
|
||||||
|
"amountPerHectare": "150 kg/ha",
|
||||||
|
"applicationMethod": "کودآبیاری در دو نوبت",
|
||||||
|
"applicationInterval": "هر ۱۰ روز",
|
||||||
|
"reasoning": "نیتروژن و پتاسیم خاک در محدوده متوسط است و گیاه در فاز رویشی نیاز تغذیهای بالاتری دارد.",
|
||||||
|
},
|
||||||
|
"raw_response": "{\"plan\":{\"npkRatio\":\"20-20-20\"}}",
|
||||||
|
"status": "completed",
|
||||||
|
}
|
||||||
|
|
||||||
|
DASHBOARD_RESULT = {
|
||||||
|
"sensor_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||||
|
"all_cards": {
|
||||||
|
"farmOverviewKpis": {
|
||||||
|
"healthScore": 82,
|
||||||
|
"activeAlerts": 2,
|
||||||
|
"waterNeedMm": 18.4,
|
||||||
|
},
|
||||||
|
"sensorValuesList": {
|
||||||
|
"items": [
|
||||||
|
{"label": "رطوبت خاک", "value": 45.2, "unit": "%"},
|
||||||
|
{"label": "دما خاک", "value": 22.5, "unit": "°C"},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"recommendationsList": {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"recommendation_title": "تنظیم نوبت آبیاری",
|
||||||
|
"suggested_action": "آبیاری بعدی را صبح فردا انجام دهید.",
|
||||||
|
"urgency_level": "high",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
TASK_PROGRESS = {"current": 1, "total": 3, "message": "در حال پردازش..."}
|
||||||
|
|
||||||
|
RAG_STREAM_SUCCESS = {
|
||||||
|
"content_type": "text/plain; charset=utf-8",
|
||||||
|
"body": "سلام، برای بازیابی رطوبت خاک بهتر است آبیاری صبحگاهی را تنظیم کنید.",
|
||||||
|
}
|
||||||
|
|
||||||
|
index = []
|
||||||
|
|
||||||
|
|
||||||
|
def register(path, method, api_path, status_code, description, payload):
|
||||||
|
full_path = MOCK_DIR / path
|
||||||
|
full_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
full_path.write_text(
|
||||||
|
json.dumps(payload, ensure_ascii=False, indent=2) + "\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
index.append(
|
||||||
|
{
|
||||||
|
"method": method,
|
||||||
|
"path": api_path,
|
||||||
|
"status_code": status_code,
|
||||||
|
"description": description,
|
||||||
|
"file": str(Path("json/mock_data") / path),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
register(
|
||||||
|
"dashboard-data/generate/post_202.json",
|
||||||
|
"POST",
|
||||||
|
"/api/dashboard-data/generate/",
|
||||||
|
202,
|
||||||
|
"Dashboard data task queued",
|
||||||
|
queue_response(
|
||||||
|
"dashboard task queued",
|
||||||
|
"dashboard-task-123",
|
||||||
|
"/api/dashboard-data/dashboard-task-123/status/",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"dashboard-data/generate/post_400.json",
|
||||||
|
"POST",
|
||||||
|
"/api/dashboard-data/generate/",
|
||||||
|
400,
|
||||||
|
"Missing sensor_id",
|
||||||
|
error_response(400, "پارامتر sensor_id الزامی است.", None),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"dashboard-data/status/get_200_pending.json",
|
||||||
|
"GET",
|
||||||
|
"/api/dashboard-data/{task_id}/status/",
|
||||||
|
200,
|
||||||
|
"Pending dashboard task",
|
||||||
|
task_pending("dashboard-task-123"),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"dashboard-data/status/get_200_progress.json",
|
||||||
|
"GET",
|
||||||
|
"/api/dashboard-data/{task_id}/status/",
|
||||||
|
200,
|
||||||
|
"Dashboard task in progress",
|
||||||
|
task_progress(
|
||||||
|
"dashboard-task-123",
|
||||||
|
{"current": 5, "total": 15, "card": "sensorValuesList", "message": "processing sensorValuesList"},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"dashboard-data/status/get_200_success.json",
|
||||||
|
"GET",
|
||||||
|
"/api/dashboard-data/{task_id}/status/",
|
||||||
|
200,
|
||||||
|
"Successful dashboard task",
|
||||||
|
task_success("dashboard-task-123", DASHBOARD_RESULT),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"dashboard-data/status/get_200_failure.json",
|
||||||
|
"GET",
|
||||||
|
"/api/dashboard-data/{task_id}/status/",
|
||||||
|
200,
|
||||||
|
"Failed dashboard task",
|
||||||
|
task_failure("dashboard-task-123", "خطا در ساخت کارتهای داشبورد."),
|
||||||
|
)
|
||||||
|
|
||||||
|
register(
|
||||||
|
"fertilization/recommend/post_202.json",
|
||||||
|
"POST",
|
||||||
|
"/api/fertilization/recommend/",
|
||||||
|
202,
|
||||||
|
"Fertilization task queued",
|
||||||
|
queue_response(
|
||||||
|
"تسک توصیه کودهی در صف قرار گرفت.",
|
||||||
|
"fert-task-123",
|
||||||
|
"/api/fertilization/recommend/fert-task-123/status/",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"fertilization/recommend/post_400.json",
|
||||||
|
"POST",
|
||||||
|
"/api/fertilization/recommend/",
|
||||||
|
400,
|
||||||
|
"Validation error",
|
||||||
|
error_response(400, "داده نامعتبر.", {"sensor_uuid": ["This field is required."]}),
|
||||||
|
)
|
||||||
|
for name, payload in {
|
||||||
|
"pending": task_pending("fert-task-123"),
|
||||||
|
"progress": task_progress("fert-task-123", {"message": "در حال پردازش توصیه کودهی..."}),
|
||||||
|
"success": task_success("fert-task-123", FERTILIZATION_RECOMMENDATION_RESULT),
|
||||||
|
"failure": task_failure("fert-task-123", "خطا در دریافت توصیه کودهی."),
|
||||||
|
}.items():
|
||||||
|
register(
|
||||||
|
f"fertilization/status/get_200_{name}.json",
|
||||||
|
"GET",
|
||||||
|
"/api/fertilization/recommend/{task_id}/status/",
|
||||||
|
200,
|
||||||
|
f"Fertilization status {name}",
|
||||||
|
payload,
|
||||||
|
)
|
||||||
|
|
||||||
|
register(
|
||||||
|
"irrigation/methods/get_200.json",
|
||||||
|
"GET",
|
||||||
|
"/api/irrigation/",
|
||||||
|
200,
|
||||||
|
"List irrigation methods",
|
||||||
|
ok_response([IRRIGATION_METHOD]),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"irrigation/methods/post_201.json",
|
||||||
|
"POST",
|
||||||
|
"/api/irrigation/",
|
||||||
|
201,
|
||||||
|
"Create irrigation method",
|
||||||
|
ok_response(IRRIGATION_METHOD, code=201),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"irrigation/methods/post_400.json",
|
||||||
|
"POST",
|
||||||
|
"/api/irrigation/",
|
||||||
|
400,
|
||||||
|
"Irrigation create validation error",
|
||||||
|
error_response(400, "داده نامعتبر.", {"name": ["This field is required."]}),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"irrigation/recommend/post_202.json",
|
||||||
|
"POST",
|
||||||
|
"/api/irrigation/recommend/",
|
||||||
|
202,
|
||||||
|
"Irrigation recommendation task queued",
|
||||||
|
queue_response(
|
||||||
|
"تسک توصیه آبیاری در صف قرار گرفت.",
|
||||||
|
"irr-task-123",
|
||||||
|
"/api/irrigation/recommend/irr-task-123/status/",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"irrigation/recommend/post_400.json",
|
||||||
|
"POST",
|
||||||
|
"/api/irrigation/recommend/",
|
||||||
|
400,
|
||||||
|
"Irrigation recommendation validation error",
|
||||||
|
error_response(400, "داده نامعتبر.", {"sensor_uuid": ["This field is required."]}),
|
||||||
|
)
|
||||||
|
for name, payload in {
|
||||||
|
"pending": task_pending("irr-task-123"),
|
||||||
|
"progress": task_progress("irr-task-123", {"message": "در حال پردازش توصیه آبیاری..."}),
|
||||||
|
"success": task_success("irr-task-123", IRRIGATION_RECOMMENDATION_RESULT),
|
||||||
|
"failure": task_failure("irr-task-123", "خطا در دریافت توصیه آبیاری."),
|
||||||
|
}.items():
|
||||||
|
register(
|
||||||
|
f"irrigation/recommend/status/get_200_{name}.json",
|
||||||
|
"GET",
|
||||||
|
"/api/irrigation/recommend/{task_id}/status/",
|
||||||
|
200,
|
||||||
|
f"Irrigation recommendation status {name}",
|
||||||
|
payload,
|
||||||
|
)
|
||||||
|
|
||||||
|
for method, success_code in (("get", 200), ("put", 200), ("patch", 200)):
|
||||||
|
register(
|
||||||
|
f"irrigation/method-detail/{method}_{success_code}.json",
|
||||||
|
method.upper(),
|
||||||
|
"/api/irrigation/{pk}/",
|
||||||
|
success_code,
|
||||||
|
f"Irrigation method {method} success",
|
||||||
|
ok_response(IRRIGATION_METHOD, code=success_code),
|
||||||
|
)
|
||||||
|
if method in {"put", "patch"}:
|
||||||
|
register(
|
||||||
|
f"irrigation/method-detail/{method}_400.json",
|
||||||
|
method.upper(),
|
||||||
|
"/api/irrigation/{pk}/",
|
||||||
|
400,
|
||||||
|
f"Irrigation method {method} validation error",
|
||||||
|
error_response(400, "داده نامعتبر.", {"name": ["This field may not be blank."]}),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
f"irrigation/method-detail/{method}_404.json",
|
||||||
|
method.upper(),
|
||||||
|
"/api/irrigation/{pk}/",
|
||||||
|
404,
|
||||||
|
f"Irrigation method {method} not found",
|
||||||
|
error_response(404, "روش آبیاری یافت نشد.", None),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"irrigation/method-detail/delete_200.json",
|
||||||
|
"DELETE",
|
||||||
|
"/api/irrigation/{pk}/",
|
||||||
|
200,
|
||||||
|
"Delete irrigation method",
|
||||||
|
error_response(200, "روش آبیاری با موفقیت حذف شد.", None),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"irrigation/method-detail/delete_404.json",
|
||||||
|
"DELETE",
|
||||||
|
"/api/irrigation/{pk}/",
|
||||||
|
404,
|
||||||
|
"Delete irrigation method not found",
|
||||||
|
error_response(404, "روش آبیاری یافت نشد.", None),
|
||||||
|
)
|
||||||
|
|
||||||
|
register(
|
||||||
|
"soil-data/get_200_database.json",
|
||||||
|
"GET",
|
||||||
|
"/api/soil-data/",
|
||||||
|
200,
|
||||||
|
"Soil data served from database",
|
||||||
|
ok_response(SOIL_LOCATION),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"soil-data/get_202_queued.json",
|
||||||
|
"GET",
|
||||||
|
"/api/soil-data/",
|
||||||
|
202,
|
||||||
|
"Soil data fetch task queued",
|
||||||
|
{
|
||||||
|
"code": 202,
|
||||||
|
"msg": "تسک در صف. وضعیت را با task_id بررسی کنید.",
|
||||||
|
"data": SOIL_TASK_DATA,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"soil-data/get_400.json",
|
||||||
|
"GET",
|
||||||
|
"/api/soil-data/",
|
||||||
|
400,
|
||||||
|
"Soil data validation error",
|
||||||
|
error_response(400, "داده نامعتبر.", {"lat": ["This field is required."], "lon": ["This field is required."]}),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"soil-data/post_200_database.json",
|
||||||
|
"POST",
|
||||||
|
"/api/soil-data/",
|
||||||
|
200,
|
||||||
|
"Soil data POST served from database",
|
||||||
|
ok_response(SOIL_LOCATION),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"soil-data/post_202_queued.json",
|
||||||
|
"POST",
|
||||||
|
"/api/soil-data/",
|
||||||
|
202,
|
||||||
|
"Soil data POST task queued",
|
||||||
|
{
|
||||||
|
"code": 202,
|
||||||
|
"msg": "تسک در صف. وضعیت را با task_id بررسی کنید.",
|
||||||
|
"data": SOIL_TASK_DATA,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"soil-data/post_400.json",
|
||||||
|
"POST",
|
||||||
|
"/api/soil-data/",
|
||||||
|
400,
|
||||||
|
"Soil data POST validation error",
|
||||||
|
error_response(400, "داده نامعتبر.", {"lat": ["A valid number is required."]}),
|
||||||
|
)
|
||||||
|
for name, payload in {
|
||||||
|
"pending": task_pending("soil-task-123"),
|
||||||
|
"progress": task_progress("soil-task-123", {"step": "fetch", "percent": 60}),
|
||||||
|
"success": task_success("soil-task-123", SOIL_LOCATION | {"source": "database"}),
|
||||||
|
"failure": task_failure("soil-task-123", "خطا در واکشی داده خاک."),
|
||||||
|
}.items():
|
||||||
|
register(
|
||||||
|
f"soil-data/status/get_200_{name}.json",
|
||||||
|
"GET",
|
||||||
|
"/api/soil-data/tasks/{task_id}/status/",
|
||||||
|
200,
|
||||||
|
f"Soil task status {name}",
|
||||||
|
payload,
|
||||||
|
)
|
||||||
|
|
||||||
|
register(
|
||||||
|
"plant/list-get_200.json",
|
||||||
|
"GET",
|
||||||
|
"/api/plants/",
|
||||||
|
200,
|
||||||
|
"List plants",
|
||||||
|
ok_response([PLANT]),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"plant/create-post_201.json",
|
||||||
|
"POST",
|
||||||
|
"/api/plants/",
|
||||||
|
201,
|
||||||
|
"Create plant",
|
||||||
|
ok_response(PLANT, code=201),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"plant/create-post_400.json",
|
||||||
|
"POST",
|
||||||
|
"/api/plants/",
|
||||||
|
400,
|
||||||
|
"Plant create validation error",
|
||||||
|
error_response(400, "داده نامعتبر.", {"name": ["This field is required."]}),
|
||||||
|
)
|
||||||
|
for method in ("get", "put", "patch"):
|
||||||
|
register(
|
||||||
|
f"plant/detail-{method}_200.json",
|
||||||
|
method.upper(),
|
||||||
|
"/api/plants/{pk}/",
|
||||||
|
200,
|
||||||
|
f"Plant detail {method} success",
|
||||||
|
ok_response(PLANT),
|
||||||
|
)
|
||||||
|
if method in {"put", "patch"}:
|
||||||
|
register(
|
||||||
|
f"plant/detail-{method}_400.json",
|
||||||
|
method.upper(),
|
||||||
|
"/api/plants/{pk}/",
|
||||||
|
400,
|
||||||
|
f"Plant detail {method} validation error",
|
||||||
|
error_response(400, "داده نامعتبر.", {"name": ["This field may not be blank."]}),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
f"plant/detail-{method}_404.json",
|
||||||
|
method.upper(),
|
||||||
|
"/api/plants/{pk}/",
|
||||||
|
404,
|
||||||
|
f"Plant detail {method} not found",
|
||||||
|
error_response(404, "گیاه یافت نشد.", None),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"plant/detail-delete_200.json",
|
||||||
|
"DELETE",
|
||||||
|
"/api/plants/{pk}/",
|
||||||
|
200,
|
||||||
|
"Delete plant success",
|
||||||
|
error_response(200, "گیاه با موفقیت حذف شد.", None),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"plant/detail-delete_404.json",
|
||||||
|
"DELETE",
|
||||||
|
"/api/plants/{pk}/",
|
||||||
|
404,
|
||||||
|
"Delete plant not found",
|
||||||
|
error_response(404, "گیاه یافت نشد.", None),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"plant/fetch-info-post_200.json",
|
||||||
|
"POST",
|
||||||
|
"/api/plants/fetch-info/",
|
||||||
|
200,
|
||||||
|
"Fetch plant info success",
|
||||||
|
ok_response(PLANT),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"plant/fetch-info-post_400.json",
|
||||||
|
"POST",
|
||||||
|
"/api/plants/fetch-info/",
|
||||||
|
400,
|
||||||
|
"Fetch plant info missing name",
|
||||||
|
error_response(400, "نام گیاه الزامی است.", None),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"plant/fetch-info-post_503.json",
|
||||||
|
"POST",
|
||||||
|
"/api/plants/fetch-info/",
|
||||||
|
503,
|
||||||
|
"Fetch plant info service unavailable",
|
||||||
|
error_response(503, "سرویس API هنوز پیادهسازی نشده است.", None),
|
||||||
|
)
|
||||||
|
|
||||||
|
register(
|
||||||
|
"rag/chat-post_200_stream.json",
|
||||||
|
"POST",
|
||||||
|
"/api/rag/chat/",
|
||||||
|
200,
|
||||||
|
"RAG chat streaming response",
|
||||||
|
RAG_STREAM_SUCCESS,
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"rag/chat-post_400_missing_query.json",
|
||||||
|
"POST",
|
||||||
|
"/api/rag/chat/",
|
||||||
|
400,
|
||||||
|
"Missing query",
|
||||||
|
{"code": 400, "msg": "پارامتر query الزامی است."},
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"rag/chat-post_400_invalid_service.json",
|
||||||
|
"POST",
|
||||||
|
"/api/rag/chat/",
|
||||||
|
400,
|
||||||
|
"Invalid service id",
|
||||||
|
{"code": 400, "msg": "service_id نامعتبر است: unknown_service"},
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"rag/chat-post_400_missing_user.json",
|
||||||
|
"POST",
|
||||||
|
"/api/rag/chat/",
|
||||||
|
400,
|
||||||
|
"Missing user_id for service",
|
||||||
|
{"code": 400, "msg": "برای این service_id، پارامتر user_id الزامی است."},
|
||||||
|
)
|
||||||
|
|
||||||
|
register(
|
||||||
|
"rag/irrigation/post_202.json",
|
||||||
|
"POST",
|
||||||
|
"/api/rag/recommend/irrigation/",
|
||||||
|
202,
|
||||||
|
"RAG irrigation task queued",
|
||||||
|
queue_response(
|
||||||
|
"تسک توصیه آبیاری در صف قرار گرفت.",
|
||||||
|
"rag-irr-123",
|
||||||
|
"/api/rag/recommend/irrigation/rag-irr-123/status/",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"rag/irrigation/post_400.json",
|
||||||
|
"POST",
|
||||||
|
"/api/rag/recommend/irrigation/",
|
||||||
|
400,
|
||||||
|
"RAG irrigation validation error",
|
||||||
|
error_response(400, "پارامتر sensor_uuid الزامی است.", None),
|
||||||
|
)
|
||||||
|
for name, payload in {
|
||||||
|
"pending": task_pending("rag-irr-123"),
|
||||||
|
"progress": task_progress("rag-irr-123", {"message": "در حال پردازش توصیه آبیاری..."}),
|
||||||
|
"success": task_success("rag-irr-123", IRRIGATION_RECOMMENDATION_RESULT),
|
||||||
|
"failure": task_failure("rag-irr-123", "خطا در دریافت توصیه آبیاری."),
|
||||||
|
}.items():
|
||||||
|
register(
|
||||||
|
f"rag/irrigation/status/get_200_{name}.json",
|
||||||
|
"GET",
|
||||||
|
"/api/rag/recommend/irrigation/{task_id}/status/",
|
||||||
|
200,
|
||||||
|
f"RAG irrigation status {name}",
|
||||||
|
payload,
|
||||||
|
)
|
||||||
|
|
||||||
|
register(
|
||||||
|
"rag/fertilization/post_202.json",
|
||||||
|
"POST",
|
||||||
|
"/api/rag/recommend/fertilization/",
|
||||||
|
202,
|
||||||
|
"RAG fertilization task queued",
|
||||||
|
queue_response(
|
||||||
|
"تسک توصیه کودهی در صف قرار گرفت.",
|
||||||
|
"rag-fert-123",
|
||||||
|
"/api/rag/recommend/fertilization/rag-fert-123/status/",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"rag/fertilization/post_400.json",
|
||||||
|
"POST",
|
||||||
|
"/api/rag/recommend/fertilization/",
|
||||||
|
400,
|
||||||
|
"RAG fertilization validation error",
|
||||||
|
error_response(400, "پارامتر sensor_uuid الزامی است.", None),
|
||||||
|
)
|
||||||
|
for name, payload in {
|
||||||
|
"pending": task_pending("rag-fert-123"),
|
||||||
|
"progress": task_progress("rag-fert-123", {"message": "در حال پردازش توصیه کودهی..."}),
|
||||||
|
"success": task_success("rag-fert-123", FERTILIZATION_RECOMMENDATION_RESULT),
|
||||||
|
"failure": task_failure("rag-fert-123", "خطا در دریافت توصیه کودهی."),
|
||||||
|
}.items():
|
||||||
|
register(
|
||||||
|
f"rag/fertilization/status/get_200_{name}.json",
|
||||||
|
"GET",
|
||||||
|
"/api/rag/recommend/fertilization/{task_id}/status/",
|
||||||
|
200,
|
||||||
|
f"RAG fertilization status {name}",
|
||||||
|
payload,
|
||||||
|
)
|
||||||
|
|
||||||
|
for method in ("put", "patch"):
|
||||||
|
register(
|
||||||
|
f"sensor-data/update-{method}_200.json",
|
||||||
|
method.upper(),
|
||||||
|
"/api/sensor-data/{uuid_sensor}/",
|
||||||
|
200,
|
||||||
|
f"Sensor update {method} success",
|
||||||
|
ok_response(SENSOR_DATA),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
f"sensor-data/update-{method}_400.json",
|
||||||
|
method.upper(),
|
||||||
|
"/api/sensor-data/{uuid_sensor}/",
|
||||||
|
400,
|
||||||
|
f"Sensor update {method} validation error",
|
||||||
|
error_response(400, "داده نامعتبر.", {"location_id": ["This field is required."]}),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
f"sensor-data/update-{method}_404.json",
|
||||||
|
method.upper(),
|
||||||
|
"/api/sensor-data/{uuid_sensor}/",
|
||||||
|
404,
|
||||||
|
f"Sensor update {method} location not found",
|
||||||
|
error_response(404, "location_id یافت نشد.", None),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"sensor-data/parameters-post_201.json",
|
||||||
|
"POST",
|
||||||
|
"/api/sensor-data/parameters/",
|
||||||
|
201,
|
||||||
|
"Create sensor parameter",
|
||||||
|
ok_response(SENSOR_PARAMETER, code=201),
|
||||||
|
)
|
||||||
|
register(
|
||||||
|
"sensor-data/parameters-post_400.json",
|
||||||
|
"POST",
|
||||||
|
"/api/sensor-data/parameters/",
|
||||||
|
400,
|
||||||
|
"Sensor parameter validation error",
|
||||||
|
error_response(400, "داده نامعتبر.", {"code": ["This field is required."]}),
|
||||||
|
)
|
||||||
|
|
||||||
|
register(
|
||||||
|
"tasks/post_200.json",
|
||||||
|
"POST",
|
||||||
|
"/api/tasks/",
|
||||||
|
200,
|
||||||
|
"Task trigger success",
|
||||||
|
ok_response({"task_id": "sample-task-123"}),
|
||||||
|
)
|
||||||
|
for name, payload in {
|
||||||
|
"pending": task_pending("sample-task-123"),
|
||||||
|
"progress": task_progress("sample-task-123", TASK_PROGRESS),
|
||||||
|
"success": task_success("sample-task-123", "done"),
|
||||||
|
"failure": task_failure("sample-task-123", "Sample task failed."),
|
||||||
|
}.items():
|
||||||
|
register(
|
||||||
|
f"tasks/status/get_200_{name}.json",
|
||||||
|
"GET",
|
||||||
|
"/api/tasks/{task_id}/status/",
|
||||||
|
200,
|
||||||
|
f"Task status {name}",
|
||||||
|
payload,
|
||||||
|
)
|
||||||
|
|
||||||
|
(MOCK_DIR / "index.json").write_text(
|
||||||
|
json.dumps(index, ensure_ascii=False, indent=2) + "\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
+61
-23
@@ -10,6 +10,7 @@ from rest_framework import status
|
|||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
|
from config.openapi import build_envelope_serializer, build_response
|
||||||
from location_data.models import SoilLocation
|
from location_data.models import SoilLocation
|
||||||
|
|
||||||
from .models import ParameterUpdateLog, SensorData, SensorDataHistory, SensorParameter
|
from .models import ParameterUpdateLog, SensorData, SensorDataHistory, SensorParameter
|
||||||
@@ -20,6 +21,36 @@ from .serializers import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
SensorDataEnvelopeSerializer = build_envelope_serializer(
|
||||||
|
"SensorDataEnvelopeSerializer",
|
||||||
|
SensorDataResponseSerializer,
|
||||||
|
)
|
||||||
|
SensorDataValidationErrorSerializer = build_envelope_serializer(
|
||||||
|
"SensorDataValidationErrorSerializer",
|
||||||
|
data_required=False,
|
||||||
|
allow_null=True,
|
||||||
|
)
|
||||||
|
SensorDataNotFoundSerializer = build_envelope_serializer(
|
||||||
|
"SensorDataNotFoundSerializer",
|
||||||
|
data_required=False,
|
||||||
|
allow_null=True,
|
||||||
|
)
|
||||||
|
SensorParameterResponseSerializer = build_envelope_serializer(
|
||||||
|
"SensorParameterEnvelopeSerializer",
|
||||||
|
inline_serializer(
|
||||||
|
name="SensorParameterPayloadSerializer",
|
||||||
|
fields={
|
||||||
|
"id": drf_serializers.IntegerField(),
|
||||||
|
"code": drf_serializers.CharField(),
|
||||||
|
"name_fa": drf_serializers.CharField(),
|
||||||
|
"unit": drf_serializers.CharField(),
|
||||||
|
"created_at": drf_serializers.DateTimeField(),
|
||||||
|
"action": drf_serializers.CharField(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class SensorDataUpdateView(APIView):
|
class SensorDataUpdateView(APIView):
|
||||||
"""
|
"""
|
||||||
آپدیت داده سنسور. هنگام آپدیت، نسخه فعلی در SensorDataHistory ذخیره میشود.
|
آپدیت داده سنسور. هنگام آپدیت، نسخه فعلی در SensorDataHistory ذخیره میشود.
|
||||||
@@ -31,9 +62,18 @@ class SensorDataUpdateView(APIView):
|
|||||||
description="داده سنسور را بر اساس uuid_sensor آپدیت (یا ایجاد) میکند. نسخه قبلی در تاریخچه ذخیره میشود.",
|
description="داده سنسور را بر اساس uuid_sensor آپدیت (یا ایجاد) میکند. نسخه قبلی در تاریخچه ذخیره میشود.",
|
||||||
request=SensorDataUpdateSerializer,
|
request=SensorDataUpdateSerializer,
|
||||||
responses={
|
responses={
|
||||||
200: SensorDataResponseSerializer,
|
200: build_response(
|
||||||
400: OpenApiResponse(description="داده نامعتبر"),
|
SensorDataEnvelopeSerializer,
|
||||||
404: OpenApiResponse(description="location_id یافت نشد"),
|
"داده سنسور با موفقیت ایجاد یا بهروزرسانی شد.",
|
||||||
|
),
|
||||||
|
400: build_response(
|
||||||
|
SensorDataValidationErrorSerializer,
|
||||||
|
"داده ورودی نامعتبر است.",
|
||||||
|
),
|
||||||
|
404: build_response(
|
||||||
|
SensorDataNotFoundSerializer,
|
||||||
|
"location_id یافت نشد.",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
examples=[
|
examples=[
|
||||||
OpenApiExample(
|
OpenApiExample(
|
||||||
@@ -61,9 +101,18 @@ class SensorDataUpdateView(APIView):
|
|||||||
description="فقط فیلدهای ارسالشده آپدیت میشوند.",
|
description="فقط فیلدهای ارسالشده آپدیت میشوند.",
|
||||||
request=SensorDataUpdateSerializer,
|
request=SensorDataUpdateSerializer,
|
||||||
responses={
|
responses={
|
||||||
200: SensorDataResponseSerializer,
|
200: build_response(
|
||||||
400: OpenApiResponse(description="داده نامعتبر"),
|
SensorDataEnvelopeSerializer,
|
||||||
404: OpenApiResponse(description="location_id یافت نشد"),
|
"داده سنسور با موفقیت بهروزرسانی شد.",
|
||||||
|
),
|
||||||
|
400: build_response(
|
||||||
|
SensorDataValidationErrorSerializer,
|
||||||
|
"داده ورودی نامعتبر است.",
|
||||||
|
),
|
||||||
|
404: build_response(
|
||||||
|
SensorDataNotFoundSerializer,
|
||||||
|
"location_id یافت نشد.",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
def patch(self, request, uuid_sensor):
|
def patch(self, request, uuid_sensor):
|
||||||
@@ -137,25 +186,14 @@ class SensorParameterCreateView(APIView):
|
|||||||
description="پارامتر جدید اضافه یا پارامتر موجود را ویرایش میکند و در لاگ ثبت میشود.",
|
description="پارامتر جدید اضافه یا پارامتر موجود را ویرایش میکند و در لاگ ثبت میشود.",
|
||||||
request=SensorParameterSerializer,
|
request=SensorParameterSerializer,
|
||||||
responses={
|
responses={
|
||||||
201: inline_serializer(
|
201: build_response(
|
||||||
name="SensorParameterResponse",
|
SensorParameterResponseSerializer,
|
||||||
fields={
|
"پارامتر سنسور با موفقیت ایجاد یا ویرایش شد.",
|
||||||
"code": drf_serializers.IntegerField(),
|
|
||||||
"msg": drf_serializers.CharField(),
|
|
||||||
"data": inline_serializer(
|
|
||||||
name="SensorParameterData",
|
|
||||||
fields={
|
|
||||||
"id": drf_serializers.IntegerField(),
|
|
||||||
"code": drf_serializers.CharField(),
|
|
||||||
"name_fa": drf_serializers.CharField(),
|
|
||||||
"unit": drf_serializers.CharField(),
|
|
||||||
"created_at": drf_serializers.DateTimeField(),
|
|
||||||
"action": drf_serializers.CharField(),
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
},
|
400: build_response(
|
||||||
|
SensorDataValidationErrorSerializer,
|
||||||
|
"داده ورودی نامعتبر است.",
|
||||||
),
|
),
|
||||||
400: OpenApiResponse(description="داده نامعتبر"),
|
|
||||||
},
|
},
|
||||||
examples=[
|
examples=[
|
||||||
OpenApiExample(
|
OpenApiExample(
|
||||||
|
|||||||
+27
-29
@@ -11,9 +11,30 @@ from rest_framework import serializers as drf_serializers
|
|||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
|
from config.openapi import (
|
||||||
|
build_envelope_serializer,
|
||||||
|
build_response,
|
||||||
|
build_task_status_data_serializer,
|
||||||
|
)
|
||||||
|
|
||||||
from .celery_tasks import sample_task
|
from .celery_tasks import sample_task
|
||||||
|
|
||||||
|
|
||||||
|
TaskTriggerResponseSerializer = build_envelope_serializer(
|
||||||
|
"TaskTriggerResponseSerializer",
|
||||||
|
inline_serializer(
|
||||||
|
name="TaskTriggerPayloadSerializer",
|
||||||
|
fields={
|
||||||
|
"task_id": drf_serializers.CharField(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
TaskStatusResponseSerializer = build_envelope_serializer(
|
||||||
|
"TaskStatusEnvelopeSerializer",
|
||||||
|
build_task_status_data_serializer("TaskStatusPayloadSerializer"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TaskTriggerView(APIView):
|
class TaskTriggerView(APIView):
|
||||||
"""
|
"""
|
||||||
ثبت و اجرای تسک.
|
ثبت و اجرای تسک.
|
||||||
@@ -33,18 +54,9 @@ class TaskTriggerView(APIView):
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
responses={
|
responses={
|
||||||
200: inline_serializer(
|
200: build_response(
|
||||||
name="TaskTriggerResponse",
|
TaskTriggerResponseSerializer,
|
||||||
fields={
|
"تسک نمونه با موفقیت در صف قرار گرفت.",
|
||||||
"code": drf_serializers.IntegerField(),
|
|
||||||
"msg": drf_serializers.CharField(),
|
|
||||||
"data": inline_serializer(
|
|
||||||
name="TaskTriggerData",
|
|
||||||
fields={
|
|
||||||
"task_id": drf_serializers.CharField(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
examples=[
|
examples=[
|
||||||
@@ -85,23 +97,9 @@ class TaskStatusView(APIView):
|
|||||||
summary="وضعیت تسک",
|
summary="وضعیت تسک",
|
||||||
description="وضعیت یک تسک Celery را بر اساس task_id برمیگرداند.",
|
description="وضعیت یک تسک Celery را بر اساس task_id برمیگرداند.",
|
||||||
responses={
|
responses={
|
||||||
200: inline_serializer(
|
200: build_response(
|
||||||
name="TaskStatusResponse",
|
TaskStatusResponseSerializer,
|
||||||
fields={
|
"وضعیت فعلی تسک Celery.",
|
||||||
"code": drf_serializers.IntegerField(),
|
|
||||||
"msg": drf_serializers.CharField(),
|
|
||||||
"data": inline_serializer(
|
|
||||||
name="TaskStatusData",
|
|
||||||
fields={
|
|
||||||
"task_id": drf_serializers.CharField(),
|
|
||||||
"status": drf_serializers.CharField(),
|
|
||||||
"message": drf_serializers.CharField(required=False),
|
|
||||||
"progress": drf_serializers.DictField(required=False),
|
|
||||||
"result": drf_serializers.JSONField(required=False),
|
|
||||||
"error": drf_serializers.CharField(required=False),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
examples=[
|
examples=[
|
||||||
|
|||||||
Reference in New Issue
Block a user