from django.db import transaction 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 location_data.models import SoilLocation from .models import ParameterUpdateLog, SensorData, SensorDataHistory, SensorParameter from .serializers import ( SensorDataResponseSerializer, SensorDataUpdateSerializer, SensorParameterSerializer, ) class SensorDataUpdateView(APIView): """ آپدیت داده سنسور. هنگام آپدیت، نسخه فعلی در SensorDataHistory ذخیره می‌شود. """ @extend_schema( tags=["Sensor Data"], summary="آپدیت کامل داده سنسور", description="داده سنسور را بر اساس uuid_sensor آپدیت (یا ایجاد) می‌کند. نسخه قبلی در تاریخچه ذخیره می‌شود.", request=SensorDataUpdateSerializer, responses={ 200: SensorDataResponseSerializer, 400: OpenApiResponse(description="داده نامعتبر"), 404: OpenApiResponse(description="location_id یافت نشد"), }, examples=[ OpenApiExample( "نمونه درخواست", value={ "location_id": 1, "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, }, request_only=True, ), ], ) def put(self, request, uuid_sensor): return self._update(request, uuid_sensor) @extend_schema( tags=["Sensor Data"], summary="آپدیت جزئی داده سنسور", description="فقط فیلدهای ارسال‌شده آپدیت می‌شوند.", request=SensorDataUpdateSerializer, responses={ 200: SensorDataResponseSerializer, 400: OpenApiResponse(description="داده نامعتبر"), 404: OpenApiResponse(description="location_id یافت نشد"), }, ) def patch(self, request, uuid_sensor): return self._update(request, uuid_sensor, partial=True) def _update(self, request, uuid_sensor, partial=False): serializer = SensorDataUpdateSerializer( data=request.data, partial=partial ) if not serializer.is_valid(): return Response( {"code": 400, "msg": "داده نامعتبر.", "data": serializer.errors}, status=status.HTTP_400_BAD_REQUEST, ) location_id = serializer.validated_data.pop("location_id") plant_ids = serializer.validated_data.pop("plant_ids", None) location = SoilLocation.objects.filter(pk=location_id).first() if not location: return Response( {"code": 404, "msg": "location_id یافت نشد.", "data": None}, status=status.HTTP_404_NOT_FOUND, ) with transaction.atomic(): sensor_data, created = SensorData.objects.get_or_create( uuid_sensor=uuid_sensor, defaults={"location": location, **serializer.validated_data}, ) if not created: # آپدیت رکورد اصلی for key, value in serializer.validated_data.items(): setattr(sensor_data, key, value) sensor_data.save() # ذخیره نسخه جدید (همان مقادیر جدول اصلی) در تاریخچه SensorDataHistory.objects.create( uuid_sensor=sensor_data.uuid_sensor, location_id=sensor_data.location_id, soil_moisture=sensor_data.soil_moisture, soil_temperature=sensor_data.soil_temperature, soil_ph=sensor_data.soil_ph, electrical_conductivity=sensor_data.electrical_conductivity, nitrogen=sensor_data.nitrogen, phosphorus=sensor_data.phosphorus, potassium=sensor_data.potassium, ) if plant_ids is not None: sensor_data.plants.set(plant_ids) return Response( { "code": 200, "msg": "success", "data": SensorDataResponseSerializer(sensor_data).data, }, status=status.HTTP_200_OK, ) class SensorParameterCreateView(APIView): """ اضافه کردن پارامتر جدید و ثبت در ParameterUpdateLog. """ @extend_schema( tags=["Sensor Parameters"], summary="افزودن/ویرایش پارامتر سنسور", description="پارامتر جدید اضافه یا پارامتر موجود را ویرایش می‌کند و در لاگ ثبت می‌شود.", request=SensorParameterSerializer, responses={ 201: inline_serializer( name="SensorParameterResponse", 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: OpenApiResponse(description="داده نامعتبر"), }, examples=[ OpenApiExample( "نمونه درخواست", value={"code": "soil_moisture", "name_fa": "رطوبت خاک", "unit": "%"}, request_only=True, ), ], ) def post(self, request): serializer = SensorParameterSerializer(data=request.data) if not serializer.is_valid(): return Response( {"code": 400, "msg": "داده نامعتبر.", "data": serializer.errors}, status=status.HTTP_400_BAD_REQUEST, ) code = serializer.validated_data["code"] name_fa = serializer.validated_data["name_fa"] unit = serializer.validated_data.get("unit", "") with transaction.atomic(): parameter, created = SensorParameter.objects.update_or_create( code=code, defaults={"name_fa": name_fa, "unit": unit}, ) action = ( ParameterUpdateLog.ACTION_ADDED if created else ParameterUpdateLog.ACTION_MODIFIED ) ParameterUpdateLog.objects.create(parameter=parameter, action=action) return Response( { "code": 201, "msg": "success", "data": { "id": parameter.id, "code": parameter.code, "name_fa": parameter.name_fa, "unit": parameter.unit, "created_at": parameter.created_at, "action": action, }, }, status=status.HTTP_201_CREATED, )