Files
2026-05-11 03:27:21 +03:30

166 lines
7.3 KiB
Python

import json
from drf_spectacular.utils import OpenApiExample, extend_schema
from rest_framework import status
from rest_framework.parsers import FormParser, JSONParser, MultiPartParser
from rest_framework.response import Response
from rest_framework.views import APIView
from config.openapi import build_envelope_serializer, build_response
from rag.failure_contract import RAGServiceError
from rag.chat import encode_uploaded_image
from rag.services import get_pest_disease_detection, get_pest_disease_risk
from .serializers import (
PestDiseaseDetectionRequestSerializer,
PestDiseaseRiskRequestSerializer,
)
PestDiseaseValidationErrorSerializer = build_envelope_serializer(
"PestDiseaseValidationErrorSerializer",
data_required=False,
allow_null=True,
)
PestDiseaseDetectionResponseSerializer = build_envelope_serializer(
"PestDiseaseDetectionResponseSerializer",
data_schema=None,
)
PestDiseaseRiskResponseSerializer = build_envelope_serializer(
"PestDiseaseRiskResponseSerializer",
data_schema=None,
)
class _ImageMixin:
parser_classes = [JSONParser, MultiPartParser, FormParser]
def _collect_uploaded_images(self, request):
images = []
for uploaded in request.FILES.getlist("images"):
images.append(encode_uploaded_image(uploaded))
single_image = request.FILES.get("image")
if single_image is not None:
images.append(encode_uploaded_image(single_image))
image_urls = request.data.get("image_urls")
if isinstance(image_urls, str) and image_urls.strip():
try:
parsed = json.loads(image_urls)
except (json.JSONDecodeError, ValueError):
parsed = [image_urls]
image_urls = parsed
if isinstance(image_urls, list):
for item in image_urls:
if isinstance(item, str) and item.strip():
images.append({"url": item.strip(), "detail": "auto"})
elif isinstance(item, dict) and isinstance(item.get("url"), str):
images.append({"url": item["url"].strip(), "detail": item.get("detail", "auto")})
return images
class PestDiseaseDetectionView(_ImageMixin, APIView):
@extend_schema(
tags=["Pest & Disease"],
summary="تشخیص آفت یا بیماری از روی تصویر",
description="با دریافت farm_uuid و حداقل یک تصویر، تصویر گیاه را با کمک RAG بررسی می کند و نتیجه تشخیص را برمی گرداند.",
request=PestDiseaseDetectionRequestSerializer,
responses={
200: build_response(PestDiseaseDetectionResponseSerializer, "نتیجه تشخیص آفت/بیماری."),
400: build_response(PestDiseaseValidationErrorSerializer, "پارامتر ورودی نامعتبر است."),
500: build_response(PestDiseaseValidationErrorSerializer, "خطا در تحلیل تصویر گیاه."),
},
examples=[
OpenApiExample(
"نمونه درخواست تشخیص",
value={
"farm_uuid": "11111111-1111-1111-1111-111111111111",
"plant_name": "گوجه فرنگی",
"query": "این برگ ها مشکوک به آفت هستند؟",
"image_urls": ["https://example.com/leaf.jpg"],
},
request_only=True,
),
],
)
def post(self, request):
serializer = PestDiseaseDetectionRequestSerializer(data=request.data)
if not serializer.is_valid():
return Response(
{"code": 400, "msg": "داده نامعتبر.", "data": serializer.errors},
status=status.HTTP_400_BAD_REQUEST,
)
images = self._collect_uploaded_images(request)
if not images:
return Response(
{"code": 400, "msg": "حداقل یک تصویر باید ارسال شود.", "data": None},
status=status.HTTP_400_BAD_REQUEST,
)
validated = serializer.validated_data
try:
result = get_pest_disease_detection(
farm_uuid=validated["farm_uuid"],
plant_name=validated.get("plant_name"),
query=validated.get("query"),
images=images,
)
except RAGServiceError as exc:
return Response(
{"code": exc.http_status, "msg": exc.contract.message, "data": exc.to_dict()},
status=exc.http_status,
)
except Exception as exc:
return Response(
{"code": 500, "msg": f"خطا در تحلیل تصویر گیاه: {exc}", "data": None},
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
)
return Response({"code": 200, "msg": "success", "data": result}, status=status.HTTP_200_OK)
class PestDiseaseRiskView(APIView):
@extend_schema(
tags=["Pest & Disease"],
summary="پیش بینی ریسک آفات و بیماری",
description="با دریافت farm_uuid، داده های مزرعه و پایگاه دانش تخصصی را به RAG می دهد و ریسک آفات و بیماری را برمی گرداند.",
request=PestDiseaseRiskRequestSerializer,
responses={
200: build_response(PestDiseaseRiskResponseSerializer, "خروجی پیش بینی ریسک آفات و بیماری."),
400: build_response(PestDiseaseValidationErrorSerializer, "پارامتر ورودی نامعتبر است."),
500: build_response(PestDiseaseValidationErrorSerializer, "خطا در پیش بینی ریسک آفات و بیماری."),
},
examples=[
OpenApiExample(
"نمونه درخواست ریسک",
value={
"farm_uuid": "11111111-1111-1111-1111-111111111111",
"plant_name": "گوجه فرنگی",
"growth_stage": "گلدهی",
},
request_only=True,
),
],
)
def post(self, request):
serializer = PestDiseaseRiskRequestSerializer(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
try:
result = get_pest_disease_risk(
farm_uuid=validated["farm_uuid"],
plant_name=validated.get("plant_name"),
growth_stage=validated.get("growth_stage"),
query=validated.get("query"),
)
except RAGServiceError as exc:
return Response(
{"code": exc.http_status, "msg": exc.contract.message, "data": exc.to_dict()},
status=exc.http_status,
)
except Exception as exc:
return Response(
{"code": 500, "msg": f"خطا در پیش بینی ریسک آفات و بیماری: {exc}", "data": None},
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
)
return Response({"code": 200, "msg": "success", "data": result}, status=status.HTTP_200_OK)