Files
Ai/sensor_data/views.py
T
2026-03-25 01:56:41 +03:30

245 lines
8.8 KiB
Python

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 config.openapi import build_envelope_serializer, build_response
from location_data.models import SoilLocation
from .models import ParameterUpdateLog, SensorData, SensorDataHistory, SensorParameter
from .serializers import (
SensorDataResponseSerializer,
SensorDataUpdateSerializer,
SensorParameterSerializer,
)
SensorDataEnvelopeSerializer = build_envelope_serializer(
"SensorDataEnvelopeSerializer",
SensorDataResponseSerializer,
)
SensorDataValidationErrorSerializer = build_envelope_serializer(
"SensorDataValidationErrorSerializer",
data_required=False,
allow_null=True,
)
SensorDataNotFoundSerializer = build_envelope_serializer(
"SensorDataNotFoundSerializer",
data_required=False,
allow_null=True,
)
SensorParameterResponseSerializer = build_envelope_serializer(
"SensorParameterEnvelopeSerializer",
inline_serializer(
name="SensorParameterPayloadSerializer",
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(),
},
),
)
class SensorDataUpdateView(APIView):
"""
آپدیت داده سنسور. هنگام آپدیت، نسخه فعلی در SensorDataHistory ذخیره می‌شود.
"""
@extend_schema(
tags=["Sensor Data"],
summary="آپدیت کامل داده سنسور",
description="داده سنسور را بر اساس uuid_sensor آپدیت (یا ایجاد) می‌کند. نسخه قبلی در تاریخچه ذخیره می‌شود.",
request=SensorDataUpdateSerializer,
responses={
200: build_response(
SensorDataEnvelopeSerializer,
"داده سنسور با موفقیت ایجاد یا به‌روزرسانی شد.",
),
400: build_response(
SensorDataValidationErrorSerializer,
"داده ورودی نامعتبر است.",
),
404: build_response(
SensorDataNotFoundSerializer,
"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: build_response(
SensorDataEnvelopeSerializer,
"داده سنسور با موفقیت به‌روزرسانی شد.",
),
400: build_response(
SensorDataValidationErrorSerializer,
"داده ورودی نامعتبر است.",
),
404: build_response(
SensorDataNotFoundSerializer,
"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: build_response(
SensorParameterResponseSerializer,
"پارامتر سنسور با موفقیت ایجاد یا ویرایش شد.",
),
400: build_response(
SensorDataValidationErrorSerializer,
"داده ورودی نامعتبر است.",
),
},
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,
)