UPDATE
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
# Generated by Django 5.1.15 on 2026-04-04 21:00
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
initial = True
|
||||
|
||||
dependencies = []
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="SensorExternalRequestLog",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("farm_uuid", models.UUIDField(db_index=True)),
|
||||
("sensor_catalog_uuid", models.UUIDField(blank=True, db_index=True, null=True)),
|
||||
("physical_device_uuid", models.UUIDField(db_index=True)),
|
||||
("payload", models.JSONField(blank=True, default=dict)),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
],
|
||||
options={
|
||||
"db_table": "sensor_external_request_logs",
|
||||
"ordering": ["-created_at", "-id"],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,16 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
class SensorExternalRequestLog(models.Model):
|
||||
farm_uuid = models.UUIDField(db_index=True)
|
||||
sensor_catalog_uuid = models.UUIDField(null=True, blank=True, db_index=True)
|
||||
physical_device_uuid = models.UUIDField(db_index=True)
|
||||
payload = models.JSONField(default=dict, blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "sensor_external_request_logs"
|
||||
ordering = ["-created_at", "-id"]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.physical_device_uuid}:{self.created_at.isoformat()}"
|
||||
@@ -1,5 +1,86 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from farm_hub.models import FarmSensor
|
||||
from sensor_catalog.models import SensorCatalog
|
||||
|
||||
from .models import SensorExternalRequestLog
|
||||
|
||||
|
||||
class SensorExternalRequestSerializer(serializers.Serializer):
|
||||
uuid = serializers.UUIDField()
|
||||
payload = serializers.JSONField(required=False, default=dict)
|
||||
|
||||
|
||||
class SensorExternalRequestLogQuerySerializer(serializers.Serializer):
|
||||
farm_uuid = serializers.UUIDField()
|
||||
page = serializers.IntegerField(required=False, min_value=1, default=1)
|
||||
page_size = serializers.IntegerField(required=False, min_value=1, max_value=100, default=20)
|
||||
|
||||
|
||||
class SensorExternalRequestLogSerializer(serializers.ModelSerializer):
|
||||
farm_sensor = serializers.SerializerMethodField()
|
||||
sensor_catalog = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = SensorExternalRequestLog
|
||||
fields = [
|
||||
"id",
|
||||
"farm_uuid",
|
||||
"sensor_catalog_uuid",
|
||||
"physical_device_uuid",
|
||||
"farm_sensor",
|
||||
"sensor_catalog",
|
||||
"payload",
|
||||
"created_at",
|
||||
]
|
||||
|
||||
def get_farm_sensor(self, obj):
|
||||
farm_sensor_map = self.context.get("farm_sensor_map", {})
|
||||
farm_sensor = farm_sensor_map.get((obj.farm_uuid, obj.sensor_catalog_uuid, obj.physical_device_uuid))
|
||||
if farm_sensor is None:
|
||||
return None
|
||||
return FarmSensorLogSerializer(farm_sensor).data
|
||||
|
||||
def get_sensor_catalog(self, obj):
|
||||
farm_sensor_map = self.context.get("farm_sensor_map", {})
|
||||
farm_sensor = farm_sensor_map.get((obj.farm_uuid, obj.sensor_catalog_uuid, obj.physical_device_uuid))
|
||||
if farm_sensor is None or farm_sensor.sensor_catalog is None:
|
||||
return None
|
||||
return SensorCatalogLogSerializer(farm_sensor.sensor_catalog).data
|
||||
|
||||
|
||||
class FarmSensorLogSerializer(serializers.ModelSerializer):
|
||||
sensor_catalog_uuid = serializers.UUIDField(source="sensor_catalog.uuid", read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = FarmSensor
|
||||
fields = [
|
||||
"uuid",
|
||||
"sensor_catalog_uuid",
|
||||
"physical_device_uuid",
|
||||
"name",
|
||||
"sensor_type",
|
||||
"is_active",
|
||||
"specifications",
|
||||
"power_source",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
]
|
||||
|
||||
|
||||
class SensorCatalogLogSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = SensorCatalog
|
||||
fields = [
|
||||
"uuid",
|
||||
"code",
|
||||
"name",
|
||||
"description",
|
||||
"customizable_fields",
|
||||
"supported_power_sources",
|
||||
"returned_data_fields",
|
||||
"sample_payload",
|
||||
"is_active",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
]
|
||||
|
||||
@@ -1,20 +1,91 @@
|
||||
from django.db import ProgrammingError, OperationalError
|
||||
from django.db import OperationalError, ProgrammingError, transaction
|
||||
|
||||
from farm_hub.models import FarmSensor
|
||||
from notifications.services import create_notification_for_farm_uuid
|
||||
|
||||
|
||||
DEFAULT_SENSOR_EXTERNAL_FARM_UUID = "11111111-1111-1111-1111-111111111111"
|
||||
from .models import SensorExternalRequestLog
|
||||
|
||||
|
||||
def create_sensor_external_notification(*, payload=None):
|
||||
payload = payload or {}
|
||||
def get_sensor_external_request_logs_for_farm(*, farm_uuid):
|
||||
try:
|
||||
return create_notification_for_farm_uuid(
|
||||
farm_uuid=DEFAULT_SENSOR_EXTERNAL_FARM_UUID,
|
||||
title="Sensor external API request",
|
||||
message="A request was received by sensor_external_api.",
|
||||
level="info",
|
||||
metadata={"payload": payload},
|
||||
return SensorExternalRequestLog.objects.filter(farm_uuid=farm_uuid).order_by("-created_at", "-id")
|
||||
except (ProgrammingError, OperationalError) as exc:
|
||||
raise ValueError("Sensor external API tables are not migrated.") from exc
|
||||
|
||||
|
||||
def get_farm_sensor_map_for_logs(*, logs):
|
||||
try:
|
||||
logs = list(logs)
|
||||
if not logs:
|
||||
return {}
|
||||
|
||||
farm_sensor_queryset = (
|
||||
FarmSensor.objects.select_related("farm", "sensor_catalog")
|
||||
.filter(
|
||||
farm__farm_uuid__in={log.farm_uuid for log in logs},
|
||||
physical_device_uuid__in={log.physical_device_uuid for log in logs},
|
||||
)
|
||||
.order_by("-created_at", "-id")
|
||||
)
|
||||
|
||||
farm_sensor_map = {}
|
||||
for farm_sensor in farm_sensor_queryset:
|
||||
key = (
|
||||
farm_sensor.farm.farm_uuid,
|
||||
farm_sensor.sensor_catalog.uuid if farm_sensor.sensor_catalog else None,
|
||||
farm_sensor.physical_device_uuid,
|
||||
)
|
||||
farm_sensor_map.setdefault(key, farm_sensor)
|
||||
|
||||
return farm_sensor_map
|
||||
except (ProgrammingError, OperationalError) as exc:
|
||||
raise ValueError("Sensor external API tables are not migrated.") from exc
|
||||
|
||||
|
||||
def get_latest_sensor_external_request_log(*, farm_uuid, sensor_catalog_uuid, physical_device_uuid):
|
||||
try:
|
||||
return (
|
||||
SensorExternalRequestLog.objects.filter(
|
||||
farm_uuid=farm_uuid,
|
||||
sensor_catalog_uuid=sensor_catalog_uuid,
|
||||
physical_device_uuid=physical_device_uuid,
|
||||
)
|
||||
.order_by("-created_at", "-id")
|
||||
.first()
|
||||
)
|
||||
except (ProgrammingError, OperationalError) as exc:
|
||||
raise ValueError("Notifications table is not migrated.") from exc
|
||||
raise ValueError("Sensor external API tables are not migrated.") from exc
|
||||
|
||||
|
||||
def create_sensor_external_notification(*, physical_device_uuid, payload=None):
|
||||
payload = payload or {}
|
||||
sensor = (
|
||||
FarmSensor.objects.select_related("farm", "sensor_catalog")
|
||||
.filter(physical_device_uuid=physical_device_uuid)
|
||||
.first()
|
||||
)
|
||||
if sensor is None:
|
||||
raise ValueError("Physical device not found.")
|
||||
|
||||
try:
|
||||
with transaction.atomic():
|
||||
SensorExternalRequestLog.objects.create(
|
||||
farm_uuid=sensor.farm.farm_uuid,
|
||||
sensor_catalog_uuid=sensor.sensor_catalog.uuid if sensor.sensor_catalog else None,
|
||||
physical_device_uuid=sensor.physical_device_uuid,
|
||||
payload=payload,
|
||||
)
|
||||
return create_notification_for_farm_uuid(
|
||||
farm_uuid=sensor.farm.farm_uuid,
|
||||
title="Sensor external API request",
|
||||
message=f"Payload received from device {sensor.physical_device_uuid}.",
|
||||
level="info",
|
||||
metadata={
|
||||
"farm_uuid": str(sensor.farm.farm_uuid),
|
||||
"sensor_catalog_uuid": str(sensor.sensor_catalog.uuid) if sensor.sensor_catalog else None,
|
||||
"physical_device_uuid": str(sensor.physical_device_uuid),
|
||||
"payload": payload,
|
||||
},
|
||||
)
|
||||
except (ProgrammingError, OperationalError) as exc:
|
||||
raise ValueError("Sensor external API tables are not migrated.") from exc
|
||||
|
||||
@@ -2,10 +2,13 @@ from django.contrib.auth import get_user_model
|
||||
from django.test import TestCase, override_settings
|
||||
from rest_framework.test import APIRequestFactory
|
||||
|
||||
from farm_hub.models import FarmHub, FarmType
|
||||
from farm_hub.models import FarmHub, FarmSensor, FarmType
|
||||
from notifications.models import FarmNotification
|
||||
from sensor_catalog.models import SensorCatalog
|
||||
|
||||
from .views import SensorExternalAPIView
|
||||
from .models import SensorExternalRequestLog
|
||||
from .services import get_latest_sensor_external_request_log
|
||||
from .views import SensorExternalAPIView, SensorExternalRequestLogListAPIView
|
||||
|
||||
|
||||
@override_settings(SENSOR_EXTERNAL_API_KEY="12345")
|
||||
@@ -23,20 +26,34 @@ class SensorExternalAPIViewTests(TestCase):
|
||||
owner=self.user,
|
||||
farm_type=self.farm_type,
|
||||
name="Farm External",
|
||||
farm_uuid="11111111-1111-1111-1111-111111111111",
|
||||
)
|
||||
self.sensor_catalog = SensorCatalog.objects.create(
|
||||
code="ext-sensor-v1",
|
||||
name="External Sensor",
|
||||
)
|
||||
self.sensor = FarmSensor.objects.create(
|
||||
farm=self.farm,
|
||||
sensor_catalog=self.sensor_catalog,
|
||||
physical_device_uuid="11111111-1111-1111-1111-111111111111",
|
||||
name="External device",
|
||||
sensor_type="weather_station",
|
||||
)
|
||||
|
||||
def test_requires_api_key(self):
|
||||
request = self.factory.post("/api/sensor-external-api/", {"payload": {"temp": 12}}, format="json")
|
||||
request = self.factory.post(
|
||||
"/api/sensor-external-api/",
|
||||
{"uuid": str(self.sensor.physical_device_uuid), "payload": {"temp": 12}},
|
||||
format="json",
|
||||
)
|
||||
|
||||
response = SensorExternalAPIView.as_view()(request)
|
||||
|
||||
self.assertEqual(response.status_code, 401)
|
||||
|
||||
def test_creates_notification_for_fixed_farm_uuid(self):
|
||||
def test_creates_notification_and_request_log_for_device_uuid(self):
|
||||
request = self.factory.post(
|
||||
"/api/sensor-external-api/",
|
||||
{"payload": {"temp": 12}},
|
||||
{"uuid": str(self.sensor.physical_device_uuid), "payload": {"temp": 12}},
|
||||
format="json",
|
||||
HTTP_X_API_KEY="12345",
|
||||
)
|
||||
@@ -50,3 +67,166 @@ class SensorExternalAPIViewTests(TestCase):
|
||||
title="Sensor external API request",
|
||||
).exists()
|
||||
)
|
||||
self.assertTrue(
|
||||
SensorExternalRequestLog.objects.filter(
|
||||
farm_uuid=self.farm.farm_uuid,
|
||||
sensor_catalog_uuid=self.sensor_catalog.uuid,
|
||||
physical_device_uuid=self.sensor.physical_device_uuid,
|
||||
payload={"temp": 12},
|
||||
).exists()
|
||||
)
|
||||
|
||||
def test_returns_404_for_unknown_device_uuid(self):
|
||||
request = self.factory.post(
|
||||
"/api/sensor-external-api/",
|
||||
{"uuid": "22222222-2222-2222-2222-222222222222", "payload": {"temp": 12}},
|
||||
format="json",
|
||||
HTTP_X_API_KEY="12345",
|
||||
)
|
||||
|
||||
response = SensorExternalAPIView.as_view()(request)
|
||||
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
|
||||
class SensorExternalServiceTests(TestCase):
|
||||
def test_get_latest_sensor_external_request_log_returns_latest_matching_record(self):
|
||||
first_log = SensorExternalRequestLog.objects.create(
|
||||
farm_uuid="11111111-1111-1111-1111-111111111111",
|
||||
sensor_catalog_uuid="22222222-2222-2222-2222-222222222222",
|
||||
physical_device_uuid="33333333-3333-3333-3333-333333333333",
|
||||
payload={"temp": 12},
|
||||
)
|
||||
latest_log = SensorExternalRequestLog.objects.create(
|
||||
farm_uuid=first_log.farm_uuid,
|
||||
sensor_catalog_uuid=first_log.sensor_catalog_uuid,
|
||||
physical_device_uuid=first_log.physical_device_uuid,
|
||||
payload={"temp": 18},
|
||||
)
|
||||
SensorExternalRequestLog.objects.create(
|
||||
farm_uuid=first_log.farm_uuid,
|
||||
sensor_catalog_uuid=first_log.sensor_catalog_uuid,
|
||||
physical_device_uuid="44444444-4444-4444-4444-444444444444",
|
||||
payload={"temp": 25},
|
||||
)
|
||||
|
||||
log = get_latest_sensor_external_request_log(
|
||||
farm_uuid=first_log.farm_uuid,
|
||||
sensor_catalog_uuid=first_log.sensor_catalog_uuid,
|
||||
physical_device_uuid=first_log.physical_device_uuid,
|
||||
)
|
||||
|
||||
self.assertIsNotNone(log)
|
||||
self.assertEqual(log.id, latest_log.id)
|
||||
self.assertEqual(log.payload, {"temp": 18})
|
||||
|
||||
|
||||
@override_settings(SENSOR_EXTERNAL_API_KEY="12345")
|
||||
class SensorExternalRequestLogListAPIViewTests(TestCase):
|
||||
def setUp(self):
|
||||
self.factory = APIRequestFactory()
|
||||
self.user = get_user_model().objects.create_user(
|
||||
username="sensor-external-log-user",
|
||||
password="secret123",
|
||||
email="sensor-external-log@example.com",
|
||||
phone_number="09120000016",
|
||||
)
|
||||
self.farm_type = FarmType.objects.create(name="لاگ سنسور خارجی")
|
||||
self.farm = FarmHub.objects.create(
|
||||
owner=self.user,
|
||||
farm_type=self.farm_type,
|
||||
name="Farm Log External",
|
||||
farm_uuid="11111111-1111-1111-1111-111111111111",
|
||||
)
|
||||
self.farm_uuid = self.farm.farm_uuid
|
||||
self.other_farm_uuid = "aaaaaaaa-1111-1111-1111-111111111111"
|
||||
self.first_catalog = SensorCatalog.objects.create(
|
||||
code="ext-sensor-log-1",
|
||||
name="External Sensor Log 1",
|
||||
description="Sensor catalog for first log",
|
||||
returned_data_fields=["temp"],
|
||||
)
|
||||
self.second_catalog = SensorCatalog.objects.create(
|
||||
code="ext-sensor-log-2",
|
||||
name="External Sensor Log 2",
|
||||
description="Sensor catalog for second log",
|
||||
returned_data_fields=["humidity"],
|
||||
)
|
||||
self.first_sensor = FarmSensor.objects.create(
|
||||
farm=self.farm,
|
||||
sensor_catalog=self.first_catalog,
|
||||
physical_device_uuid="33333333-3333-3333-3333-333333333333",
|
||||
name="External device 1",
|
||||
sensor_type="weather_station",
|
||||
specifications={"model": "FH-1"},
|
||||
power_source={"type": "battery"},
|
||||
)
|
||||
self.second_sensor = FarmSensor.objects.create(
|
||||
farm=self.farm,
|
||||
sensor_catalog=self.second_catalog,
|
||||
physical_device_uuid="55555555-5555-5555-5555-555555555555",
|
||||
name="External device 2",
|
||||
sensor_type="soil_sensor",
|
||||
specifications={"model": "FH-2"},
|
||||
power_source={"type": "solar"},
|
||||
)
|
||||
|
||||
self.first_log = SensorExternalRequestLog.objects.create(
|
||||
farm_uuid=self.farm_uuid,
|
||||
sensor_catalog_uuid=self.first_catalog.uuid,
|
||||
physical_device_uuid=self.first_sensor.physical_device_uuid,
|
||||
payload={"temp": 12},
|
||||
)
|
||||
self.second_log = SensorExternalRequestLog.objects.create(
|
||||
farm_uuid=self.farm_uuid,
|
||||
sensor_catalog_uuid=self.second_catalog.uuid,
|
||||
physical_device_uuid=self.second_sensor.physical_device_uuid,
|
||||
payload={"temp": 18},
|
||||
)
|
||||
SensorExternalRequestLog.objects.create(
|
||||
farm_uuid=self.other_farm_uuid,
|
||||
sensor_catalog_uuid="66666666-6666-6666-6666-666666666666",
|
||||
physical_device_uuid="77777777-7777-7777-7777-777777777777",
|
||||
payload={"temp": 24},
|
||||
)
|
||||
|
||||
def test_requires_api_key(self):
|
||||
request = self.factory.get(f"/api/sensor-external-api/logs/?farm_uuid={self.farm_uuid}")
|
||||
|
||||
response = SensorExternalRequestLogListAPIView.as_view()(request)
|
||||
|
||||
self.assertEqual(response.status_code, 401)
|
||||
|
||||
def test_returns_paginated_logs_for_farm_uuid(self):
|
||||
request = self.factory.get(
|
||||
f"/api/sensor-external-api/logs/?farm_uuid={self.farm_uuid}&page=1&page_size=1",
|
||||
HTTP_X_API_KEY="12345",
|
||||
)
|
||||
|
||||
response = SensorExternalRequestLogListAPIView.as_view()(request)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data["code"], 200)
|
||||
self.assertEqual(response.data["count"], 2)
|
||||
self.assertEqual(len(response.data["data"]), 1)
|
||||
self.assertEqual(response.data["data"][0]["id"], self.second_log.id)
|
||||
self.assertEqual(
|
||||
response.data["data"][0]["physical_device_uuid"],
|
||||
str(self.second_log.physical_device_uuid),
|
||||
)
|
||||
self.assertEqual(
|
||||
response.data["data"][0]["sensor_catalog"]["uuid"],
|
||||
str(self.second_catalog.uuid),
|
||||
)
|
||||
self.assertEqual(
|
||||
response.data["data"][0]["sensor_catalog"]["name"],
|
||||
self.second_catalog.name,
|
||||
)
|
||||
self.assertEqual(
|
||||
response.data["data"][0]["farm_sensor"]["uuid"],
|
||||
str(self.second_sensor.uuid),
|
||||
)
|
||||
self.assertEqual(
|
||||
response.data["data"][0]["farm_sensor"]["physical_device_uuid"],
|
||||
str(self.second_sensor.physical_device_uuid),
|
||||
)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from django.urls import path
|
||||
|
||||
from .views import SensorExternalAPIView
|
||||
from .views import SensorExternalAPIView, SensorExternalRequestLogListAPIView
|
||||
|
||||
urlpatterns = [
|
||||
path("", SensorExternalAPIView.as_view(), name="sensor-external-api"),
|
||||
path("logs/", SensorExternalRequestLogListAPIView.as_view(), name="sensor-external-api-log-list"),
|
||||
]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from rest_framework import status
|
||||
from rest_framework import serializers, status
|
||||
from rest_framework.pagination import PageNumberPagination
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
@@ -8,8 +9,22 @@ from config.swagger import code_response
|
||||
from notifications.serializers import FarmNotificationSerializer
|
||||
|
||||
from .authentication import SensorExternalAPIKeyAuthentication
|
||||
from .serializers import SensorExternalRequestSerializer
|
||||
from .services import create_sensor_external_notification
|
||||
from .serializers import (
|
||||
SensorExternalRequestLogQuerySerializer,
|
||||
SensorExternalRequestLogSerializer,
|
||||
SensorExternalRequestSerializer,
|
||||
)
|
||||
from .services import (
|
||||
create_sensor_external_notification,
|
||||
get_farm_sensor_map_for_logs,
|
||||
get_sensor_external_request_logs_for_farm,
|
||||
)
|
||||
|
||||
|
||||
class SensorExternalRequestLogPagination(PageNumberPagination):
|
||||
page_size = 20
|
||||
page_size_query_param = "page_size"
|
||||
max_page_size = 100
|
||||
|
||||
|
||||
class SensorExternalAPIView(APIView):
|
||||
@@ -32,8 +47,8 @@ class SensorExternalAPIView(APIView):
|
||||
responses={
|
||||
201: code_response("SensorExternalAPIResponse", data=FarmNotificationSerializer()),
|
||||
401: code_response("SensorExternalAPIUnauthorizedResponse"),
|
||||
404: code_response("SensorExternalAPIFarmNotFoundResponse"),
|
||||
503: code_response("SensorExternalAPINotificationsUnavailableResponse"),
|
||||
404: code_response("SensorExternalAPIDeviceNotFoundResponse"),
|
||||
503: code_response("SensorExternalAPIUnavailableResponse"),
|
||||
},
|
||||
)
|
||||
def post(self, request):
|
||||
@@ -41,14 +56,87 @@ class SensorExternalAPIView(APIView):
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
try:
|
||||
notification = create_sensor_external_notification(payload=serializer.validated_data.get("payload"))
|
||||
notification = create_sensor_external_notification(
|
||||
physical_device_uuid=serializer.validated_data["uuid"],
|
||||
payload=serializer.validated_data.get("payload"),
|
||||
)
|
||||
except ValueError as exc:
|
||||
if str(exc) == "Notifications table is not migrated.":
|
||||
if "not migrated" in str(exc):
|
||||
return Response(
|
||||
{"code": 503, "msg": "Notifications table is not ready. Run migrations."},
|
||||
{"code": 503, "msg": "Required tables are not ready. Run migrations."},
|
||||
status=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||
)
|
||||
return Response({"code": 404, "msg": "Farm not found."}, status=status.HTTP_404_NOT_FOUND)
|
||||
return Response({"code": 404, "msg": "Physical device not found."}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
data = FarmNotificationSerializer(notification).data
|
||||
return Response({"code": 201, "msg": "success", "data": data}, status=status.HTTP_201_CREATED)
|
||||
|
||||
|
||||
class SensorExternalRequestLogListAPIView(APIView):
|
||||
authentication_classes = [SensorExternalAPIKeyAuthentication]
|
||||
permission_classes = [AllowAny]
|
||||
pagination_class = SensorExternalRequestLogPagination
|
||||
|
||||
@extend_schema(
|
||||
tags=["Sensor External API"],
|
||||
parameters=[
|
||||
OpenApiParameter(name="farm_uuid", type=OpenApiTypes.UUID, location=OpenApiParameter.QUERY, required=True),
|
||||
OpenApiParameter(name="page", type=OpenApiTypes.INT, location=OpenApiParameter.QUERY, required=False),
|
||||
OpenApiParameter(name="page_size", type=OpenApiTypes.INT, location=OpenApiParameter.QUERY, required=False),
|
||||
OpenApiParameter(
|
||||
name="X-API-Key",
|
||||
type=OpenApiTypes.STR,
|
||||
location=OpenApiParameter.HEADER,
|
||||
required=True,
|
||||
default="12345",
|
||||
description="API key for sensor external API.",
|
||||
),
|
||||
],
|
||||
responses={
|
||||
200: code_response(
|
||||
"SensorExternalRequestLogListResponse",
|
||||
data=SensorExternalRequestLogSerializer(many=True),
|
||||
extra_fields={
|
||||
"count": serializers.IntegerField(),
|
||||
"next": serializers.CharField(allow_null=True),
|
||||
"previous": serializers.CharField(allow_null=True),
|
||||
},
|
||||
),
|
||||
401: code_response("SensorExternalRequestLogListUnauthorizedResponse"),
|
||||
503: code_response("SensorExternalRequestLogListUnavailableResponse"),
|
||||
},
|
||||
)
|
||||
def get(self, request):
|
||||
serializer = SensorExternalRequestLogQuerySerializer(data=request.query_params)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
try:
|
||||
queryset = get_sensor_external_request_logs_for_farm(
|
||||
farm_uuid=serializer.validated_data["farm_uuid"],
|
||||
)
|
||||
except ValueError:
|
||||
return Response(
|
||||
{"code": 503, "msg": "Required tables are not ready. Run migrations."},
|
||||
status=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||
)
|
||||
|
||||
paginator = self.pagination_class()
|
||||
paginator.page_size = serializer.validated_data["page_size"]
|
||||
page = paginator.paginate_queryset(queryset, request, view=self)
|
||||
farm_sensor_map = get_farm_sensor_map_for_logs(logs=page)
|
||||
data = SensorExternalRequestLogSerializer(
|
||||
page,
|
||||
many=True,
|
||||
context={"farm_sensor_map": farm_sensor_map},
|
||||
).data
|
||||
return Response(
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"count": paginator.page.paginator.count,
|
||||
"next": paginator.get_next_link(),
|
||||
"previous": paginator.get_previous_link(),
|
||||
"data": data,
|
||||
},
|
||||
status=status.HTTP_200_OK,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user