204 lines
8.9 KiB
Python
204 lines
8.9 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.chat import encode_uploaded_image
|
|
from rag.services import get_pest_disease_detection, get_pest_disease_risk
|
|
|
|
from .serializers import (
|
|
PestDiseaseDetectionRequestSerializer,
|
|
PestDiseaseRiskRequestSerializer,
|
|
PestDiseaseRiskSummaryRequestSerializer,
|
|
PestDiseaseRiskSummaryResponseSerializer,
|
|
)
|
|
|
|
|
|
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,
|
|
)
|
|
PestDiseaseRiskSummaryEnvelopeSerializer = build_envelope_serializer(
|
|
"PestDiseaseRiskSummaryEnvelopeSerializer",
|
|
PestDiseaseRiskSummaryResponseSerializer,
|
|
)
|
|
|
|
|
|
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 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 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 PestDiseaseRiskSummaryView(APIView):
|
|
@extend_schema(
|
|
tags=["Pest & Disease"],
|
|
summary="خلاصه ریسک بیماری و آفات",
|
|
description=(
|
|
"با دریافت farm_uuid، خلاصه ریسک بیماری و آفات را از سرویس RAG "
|
|
"گرفته و در قالب سبک تر برای KPI بازمی گرداند."
|
|
),
|
|
request=PestDiseaseRiskSummaryRequestSerializer,
|
|
responses={
|
|
200: build_response(PestDiseaseRiskSummaryEnvelopeSerializer, "خلاصه ریسک بیماری و آفات."),
|
|
400: build_response(PestDiseaseValidationErrorSerializer, "پارامتر ورودی نامعتبر است."),
|
|
404: build_response(PestDiseaseValidationErrorSerializer, "مزرعه یافت نشد."),
|
|
},
|
|
examples=[
|
|
OpenApiExample(
|
|
"نمونه درخواست risk summary",
|
|
value={"farm_uuid": "11111111-1111-1111-1111-111111111111"},
|
|
request_only=True,
|
|
)
|
|
],
|
|
)
|
|
def post(self, request):
|
|
from .services import build_pest_disease_risk_summary
|
|
|
|
serializer = PestDiseaseRiskSummaryRequestSerializer(data=request.data)
|
|
if not serializer.is_valid():
|
|
return Response(
|
|
{"code": 400, "msg": "داده نامعتبر.", "data": serializer.errors},
|
|
status=status.HTTP_400_BAD_REQUEST,
|
|
)
|
|
try:
|
|
result = build_pest_disease_risk_summary(farm_uuid=serializer.validated_data["farm_uuid"])
|
|
except ValueError as exc:
|
|
return Response(
|
|
{"code": 404, "msg": str(exc), "data": None},
|
|
status=status.HTTP_404_NOT_FOUND,
|
|
)
|
|
return Response({"code": 200, "msg": "success", "data": result}, status=status.HTTP_200_OK)
|