This commit is contained in:
2026-04-30 03:25:31 +03:30
parent e2c70ec8b6
commit f704e1188c
17 changed files with 1950 additions and 1 deletions
+9
View File
@@ -58,3 +58,12 @@ class IrrigationConfig(AppConfig):
def get_water_stress_service(self):
return self.water_stress_service
@cached_property
def free_text_plan_parser_service(self):
from rag.services.irrigation_plan_parser import IrrigationPlanParserService
return IrrigationPlanParserService()
def get_free_text_plan_parser_service(self):
return self.free_text_plan_parser_service
+34
View File
@@ -80,3 +80,37 @@ class WaterStressResponseSerializer(serializers.Serializer):
waterStressIndex = serializers.IntegerField()
level = serializers.CharField()
sourceMetric = serializers.JSONField()
class IrrigationPlanParserRequestSerializer(serializers.Serializer):
message = serializers.CharField(required=False, allow_blank=True, help_text="توضیح آزاد کاربر درباره برنامه آبیاری")
answers = serializers.JSONField(required=False, help_text="پاسخ های تکمیلی کاربر به سوالات مرحله قبل")
partial_plan = serializers.JSONField(required=False, help_text="داده استخراج شده مرحله قبل برای ادامه تکمیل")
farm_uuid = serializers.CharField(required=False, allow_blank=True, help_text="شناسه مزرعه برای غنی سازی context")
def validate(self, attrs):
message = (attrs.get("message") or "").strip()
answers = attrs.get("answers")
partial_plan = attrs.get("partial_plan")
if not message and not isinstance(answers, dict) and not isinstance(partial_plan, dict):
raise serializers.ValidationError(
"حداقل یکی از message، answers یا partial_plan باید ارسال شود."
)
return attrs
class PlanClarificationQuestionSerializer(serializers.Serializer):
id = serializers.CharField()
field = serializers.CharField()
question = serializers.CharField()
rationale = serializers.CharField(required=False, allow_blank=True)
class IrrigationPlanParserResponseSerializer(serializers.Serializer):
status = serializers.CharField()
status_fa = serializers.CharField()
summary = serializers.CharField()
missing_fields = serializers.ListField(child=serializers.CharField())
questions = PlanClarificationQuestionSerializer(many=True)
collected_data = serializers.JSONField()
final_plan = serializers.JSONField(required=False, allow_null=True)
+2
View File
@@ -3,6 +3,7 @@ from django.urls import path
from .views import (
IrrigationMethodDetailView,
IrrigationMethodListCreateView,
IrrigationPlanParserView,
IrrigationRecommendView,
WaterStressView,
)
@@ -11,5 +12,6 @@ urlpatterns = [
path("", IrrigationMethodListCreateView.as_view(), name="irrigation-list-create"),
path("<int:pk>/", IrrigationMethodDetailView.as_view(), name="irrigation-detail"),
path("recommend/", IrrigationRecommendView.as_view(), name="irrigation-recommend"),
path("plan-from-text/", IrrigationPlanParserView.as_view(), name="irrigation-plan-from-text"),
path("water-stress/", WaterStressView.as_view(), name="water-stress"),
]
+83
View File
@@ -13,6 +13,8 @@ from config.openapi import (
from .models import IrrigationMethod
from .serializers import (
IrrigationMethodSerializer,
IrrigationPlanParserRequestSerializer,
IrrigationPlanParserResponseSerializer,
IrrigationRecommendRequestSerializer,
WaterStressRequestSerializer,
WaterStressResponseSerializer,
@@ -37,6 +39,10 @@ IrrigationRecommendResponseSerializer = build_envelope_serializer(
"IrrigationRecommendResponseSerializer",
data_schema=None,
)
IrrigationPlanParserEnvelopeSerializer = build_envelope_serializer(
"IrrigationPlanParserEnvelopeSerializer",
IrrigationPlanParserResponseSerializer,
)
WaterStressEnvelopeSerializer = build_envelope_serializer(
"WaterStressEnvelopeSerializer",
WaterStressResponseSerializer,
@@ -219,6 +225,83 @@ class IrrigationRecommendView(APIView):
)
class IrrigationPlanParserView(APIView):
@extend_schema(
tags=["Irrigation Recommendation"],
summary="استخراج برنامه آبیاری از متن آزاد",
description=(
"توضیح متنی کاربر درباره برنامه آبیاری را می گیرد و آن را به JSON ساختاریافته تبدیل می کند. "
"اگر اطلاعات کافی نباشد، سوالات تکمیلی لازم را برمی گرداند تا در درخواست بعدی پاسخ داده شوند."
),
request=IrrigationPlanParserRequestSerializer,
responses={
200: build_response(
IrrigationPlanParserEnvelopeSerializer,
"نتیجه استخراج یا سوالات تکمیلی برنامه آبیاری.",
),
400: build_response(
IrrigationValidationErrorSerializer,
"داده ورودی نامعتبر است.",
),
500: build_response(
IrrigationValidationErrorSerializer,
"خطا در پردازش برنامه آبیاری.",
),
},
examples=[
OpenApiExample(
"نمونه درخواست کامل",
value={
"message": "برای گوجه فرنگی با آبیاری قطره ای هر سه روز یک بار صبح زود 25 دقیقه آبیاری می کنم و حدود 18 لیتر برای هر بوته می دهم.",
"farm_uuid": "11111111-1111-1111-1111-111111111111",
},
request_only=True,
),
OpenApiExample(
"نمونه درخواست تکمیلی",
value={
"partial_plan": {
"crop_name": "گوجه فرنگی",
"irrigation_method": "قطره ای",
},
"answers": {
"water_amount_per_event": "18 لیتر برای هر بوته",
"preferred_time_of_day": "صبح زود",
},
},
request_only=True,
),
],
)
def post(self, request):
serializer = IrrigationPlanParserRequestSerializer(data=request.data)
if not serializer.is_valid():
return Response(
{"code": 400, "msg": "داده نامعتبر.", "data": serializer.errors},
status=status.HTTP_400_BAD_REQUEST,
)
validated = serializer.validated_data
service = apps.get_app_config("irrigation").get_free_text_plan_parser_service()
try:
result = service.parse_plan(
message=validated.get("message", ""),
answers=validated.get("answers"),
partial_plan=validated.get("partial_plan"),
farm_uuid=validated.get("farm_uuid"),
)
except Exception as exc:
return Response(
{"code": 500, "msg": f"خطا در پردازش برنامه آبیاری: {exc}", "data": None},
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
)
return Response(
{"code": 200, "msg": "موفق", "data": result},
status=status.HTTP_200_OK,
)
class IrrigationMethodDetailView(APIView):
"""دریافت، ویرایش و حذف یک روش آبیاری."""