from drf_spectacular.utils import ( OpenApiExample, OpenApiResponse, extend_schema, inline_serializer, ) from rest_framework import serializers as drf_serializers from rest_framework import status from rest_framework.response import Response from rest_framework.views import APIView from config.openapi import build_envelope_serializer, build_response from .models import Plant from .serializers import ( PlantNameStageSerializer, PlantSerializer, normalize_growth_stage_values, ) 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, ) PlantNameStageListResponseSerializer = build_envelope_serializer( "PlantNameStageListResponseSerializer", PlantNameStageSerializer, many=True, ) class PlantListCreateView(APIView): """لیست تمام گیاهان و ایجاد گیاه جدید.""" @extend_schema( tags=["Plant"], summary="لیست گیاهان", description="لیست تمام گیاهان ذخیره‌شده را برمی‌گرداند.", responses={ 200: build_response( PlantListResponseSerializer, "لیست گیاهان ذخیره‌شده.", ), }, ) def get(self, request): plants = Plant.objects.all() serializer = PlantSerializer(plants, many=True) return Response( {"code": 200, "msg": "success", "data": serializer.data}, status=status.HTTP_200_OK, ) @extend_schema( tags=["Plant"], summary="ایجاد گیاه جدید", description="یک گیاه جدید با مشخصات داده‌شده ایجاد می‌کند.", request=PlantSerializer, responses={ 201: build_response( PlantDetailResponseSerializer, "گیاه جدید با موفقیت ایجاد شد.", ), 400: build_response( PlantValidationErrorSerializer, "داده ورودی نامعتبر است.", ), }, examples=[ OpenApiExample( "نمونه درخواست", value={ "name": "گوجه‌فرنگی", "light": "آفتاب کامل", "watering": "منظم، هفته‌ای ۲-۳ بار", "soil": "لومی، غنی از مواد آلی", "temperature": "۲۰-۳۰ درجه سانتی‌گراد", "growth_stage": "رشد رویشی", "planting_season": "بهار", "harvest_time": "۷۰-۹۰ روز پس از کاشت", "spacing": "۴۵-۶۰ سانتی‌متر", "fertilizer": "کود NPK متعادل", }, request_only=True, ), ], ) def post(self, request): serializer = PlantSerializer(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 PlantNameStageListView(APIView): """لیست سبک از نام گیاه، آیکون و مراحل رشد.""" @extend_schema( tags=["Plant"], summary="لیست نام گیاهان با مراحل رشد", description=( "فقط نام گیاه، آیکون و مراحل رشد را برمی‌گرداند. " "اگر برای گیاهی مرحله رشد ثبت نشده باشد، مراحل پیش‌فرض به آن اضافه و ذخیره می‌شود." ), responses={ 200: build_response( PlantNameStageListResponseSerializer, "لیست نام گیاهان به همراه مراحل رشد و آیکون.", ), }, ) def get(self, request): payload = [] for plant in Plant.objects.all(): growth_stages = normalize_growth_stage_values(plant) serialized_stages = ", ".join(growth_stages) update_fields: list[str] = [] if plant.growth_stage != serialized_stages: plant.growth_stage = serialized_stages update_fields.append("growth_stage") if not plant.icon: plant.icon = "leaf" update_fields.append("icon") if update_fields: update_fields.append("updated_at") plant.save(update_fields=update_fields) payload.append( { "name": plant.name, "icon": plant.icon, "growth_stages": growth_stages, } ) serializer = PlantNameStageSerializer(payload, many=True) return Response( {"code": 200, "msg": "success", "data": serializer.data}, status=status.HTTP_200_OK, ) class PlantDetailView(APIView): """دریافت، ویرایش و حذف یک گیاه.""" def _get_plant(self, pk): return Plant.objects.filter(pk=pk).first() @extend_schema( tags=["Plant"], summary="جزئیات گیاه", description="مشخصات یک گیاه را بر اساس شناسه برمی‌گرداند.", responses={ 200: build_response( PlantDetailResponseSerializer, "جزئیات گیاه.", ), 404: build_response( PlantValidationErrorSerializer, "گیاه یافت نشد.", ), }, ) def get(self, request, pk): plant = self._get_plant(pk) if not plant: return Response( {"code": 404, "msg": "گیاه یافت نشد.", "data": None}, status=status.HTTP_404_NOT_FOUND, ) serializer = PlantSerializer(plant) return Response( {"code": 200, "msg": "success", "data": serializer.data}, status=status.HTTP_200_OK, ) @extend_schema( tags=["Plant"], summary="ویرایش کامل گیاه", description="تمام فیلدهای یک گیاه را آپدیت می‌کند.", request=PlantSerializer, responses={ 200: build_response( PlantDetailResponseSerializer, "گیاه با موفقیت به‌روزرسانی شد.", ), 400: build_response( PlantValidationErrorSerializer, "داده ورودی نامعتبر است.", ), 404: build_response( PlantValidationErrorSerializer, "گیاه یافت نشد.", ), }, ) def put(self, request, pk): plant = self._get_plant(pk) if not plant: return Response( {"code": 404, "msg": "گیاه یافت نشد.", "data": None}, status=status.HTTP_404_NOT_FOUND, ) serializer = PlantSerializer(plant, 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": 200, "msg": "success", "data": serializer.data}, status=status.HTTP_200_OK, ) @extend_schema( tags=["Plant"], summary="ویرایش جزئی گیاه", description="فقط فیلدهای ارسال‌شده آپدیت می‌شوند.", request=PlantSerializer, responses={ 200: build_response( PlantDetailResponseSerializer, "گیاه با موفقیت به‌روزرسانی شد.", ), 400: build_response( PlantValidationErrorSerializer, "داده ورودی نامعتبر است.", ), 404: build_response( PlantValidationErrorSerializer, "گیاه یافت نشد.", ), }, ) def patch(self, request, pk): plant = self._get_plant(pk) if not plant: return Response( {"code": 404, "msg": "گیاه یافت نشد.", "data": None}, status=status.HTTP_404_NOT_FOUND, ) serializer = PlantSerializer(plant, data=request.data, partial=True) if not serializer.is_valid(): return Response( {"code": 400, "msg": "داده نامعتبر.", "data": serializer.errors}, status=status.HTTP_400_BAD_REQUEST, ) serializer.save() return Response( {"code": 200, "msg": "success", "data": serializer.data}, status=status.HTTP_200_OK, ) @extend_schema( tags=["Plant"], summary="حذف گیاه", description="یک گیاه را حذف می‌کند.", responses={ 200: build_response( PlantValidationErrorSerializer, "گیاه با موفقیت حذف شد.", ), 404: build_response( PlantValidationErrorSerializer, "گیاه یافت نشد.", ), }, ) def delete(self, request, pk): plant = self._get_plant(pk) if not plant: return Response( {"code": 404, "msg": "گیاه یافت نشد.", "data": None}, status=status.HTTP_404_NOT_FOUND, ) plant.delete() return Response( {"code": 200, "msg": "گیاه با موفقیت حذف شد.", "data": None}, status=status.HTTP_200_OK, ) class PlantFetchInfoView(APIView): """دریافت مشخصات گیاه از API خارجی بر اساس نام.""" @extend_schema( tags=["Plant"], summary="دریافت مشخصات گیاه از API خارجی", description="بر اساس نام گیاه، مشخصات آن را از API خارجی دریافت می‌کند. (فعلاً خالی)", request=inline_serializer( name="PlantFetchInfoRequest", fields={ "name": drf_serializers.CharField(help_text="نام گیاه"), }, ), responses={ 200: build_response( PlantFetchInfoResponseSerializer, "اطلاعات گیاه از سرویس خارجی دریافت شد.", ), 400: build_response( PlantValidationErrorSerializer, "نام گیاه ارسال نشده است.", ), 503: build_response( PlantValidationErrorSerializer, "سرویس خارجی در دسترس نیست.", ), }, examples=[ OpenApiExample( "نمونه درخواست", value={"name": "گوجه‌فرنگی"}, request_only=True, ), ], ) def post(self, request): plant_name = request.data.get("name") if not plant_name: return Response( {"code": 400, "msg": "نام گیاه الزامی است.", "data": None}, status=status.HTTP_400_BAD_REQUEST, ) result = fetch_plant_info_from_api(plant_name) if result is None: return Response( { "code": 503, "msg": "سرویس API هنوز پیاده‌سازی نشده است.", "data": None, }, status=status.HTTP_503_SERVICE_UNAVAILABLE, ) return Response( {"code": 200, "msg": "success", "data": result}, status=status.HTTP_200_OK, )