UPDATE
This commit is contained in:
@@ -22,6 +22,8 @@ class SensorSerializer(serializers.ModelSerializer):
|
||||
|
||||
|
||||
class SensorCreateSerializer(serializers.ModelSerializer):
|
||||
area_geojson = serializers.JSONField(write_only=True, required=False)
|
||||
|
||||
class Meta:
|
||||
model = Sensor
|
||||
fields = [
|
||||
@@ -29,8 +31,26 @@ class SensorCreateSerializer(serializers.ModelSerializer):
|
||||
"specifications",
|
||||
"power_source",
|
||||
"customized_sensors",
|
||||
"area_geojson",
|
||||
]
|
||||
|
||||
def validate_area_geojson(self, value):
|
||||
if not isinstance(value, dict):
|
||||
raise serializers.ValidationError("`area_geojson` must be a GeoJSON object.")
|
||||
|
||||
geometry = value.get("geometry") if value.get("type") == "Feature" else value
|
||||
if not isinstance(geometry, dict):
|
||||
raise serializers.ValidationError("`area_geojson.geometry` is required.")
|
||||
|
||||
if geometry.get("type") != "Polygon":
|
||||
raise serializers.ValidationError("`area_geojson.geometry.type` must be `Polygon`.")
|
||||
|
||||
coordinates = geometry.get("coordinates")
|
||||
if not isinstance(coordinates, list) or not coordinates or not isinstance(coordinates[0], list):
|
||||
raise serializers.ValidationError("`area_geojson.geometry.coordinates` must be a polygon ring.")
|
||||
|
||||
return value
|
||||
|
||||
|
||||
class SensorToggleSerializer(serializers.Serializer):
|
||||
uuid_sensor = serializers.UUIDField()
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
from django.db import transaction
|
||||
|
||||
from crop_zoning.services import create_zones_and_dispatch, get_initial_zones_payload, normalize_area_feature
|
||||
|
||||
|
||||
def create_sensor_with_zoning(serializer, owner):
|
||||
area_feature = serializer.validated_data.pop("area_geojson", None)
|
||||
|
||||
with transaction.atomic():
|
||||
sensor = serializer.save(owner=owner)
|
||||
zoning_payload = None
|
||||
|
||||
if area_feature is not None:
|
||||
crop_area, _zones = create_zones_and_dispatch(normalize_area_feature(area_feature))
|
||||
zoning_payload = get_initial_zones_payload(crop_area)
|
||||
|
||||
return sensor, zoning_payload
|
||||
@@ -0,0 +1,65 @@
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.test import TestCase, override_settings
|
||||
from rest_framework.test import APIRequestFactory, force_authenticate
|
||||
|
||||
from crop_zoning.models import CropArea
|
||||
from sensor_hub.views import SensorListCreateView
|
||||
|
||||
|
||||
AREA_GEOJSON = {
|
||||
"type": "Feature",
|
||||
"properties": {},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[51.418934, 35.706815],
|
||||
[51.423054, 35.691062],
|
||||
[51.384258, 35.689389],
|
||||
[51.418934, 35.706815],
|
||||
]
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@override_settings(
|
||||
USE_EXTERNAL_API_MOCK=True,
|
||||
CROP_ZONE_CHUNK_AREA_SQM=200000,
|
||||
)
|
||||
class SensorListCreateViewTests(TestCase):
|
||||
def setUp(self):
|
||||
self.factory = APIRequestFactory()
|
||||
self.user = get_user_model().objects.create_user(
|
||||
username="farmer",
|
||||
password="secret123",
|
||||
email="farmer@example.com",
|
||||
phone_number="09120000000",
|
||||
)
|
||||
|
||||
def test_create_sensor_with_area_geojson_creates_crop_zoning_payload(self):
|
||||
request = self.factory.post(
|
||||
"/api/sensor-hub/",
|
||||
{
|
||||
"name": "zone-sensor",
|
||||
"specifications": {"model": "SH-1"},
|
||||
"power_source": {"type": "battery"},
|
||||
"customized_sensors": {"report_interval_sec": 300},
|
||||
"area_geojson": AREA_GEOJSON,
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
force_authenticate(request, user=self.user)
|
||||
|
||||
response = SensorListCreateView.as_view()(request)
|
||||
|
||||
self.assertEqual(response.status_code, 201)
|
||||
self.assertEqual(response.data["code"], 201)
|
||||
self.assertEqual(response.data["data"]["name"], "zone-sensor")
|
||||
self.assertIn("zoning", response.data["data"])
|
||||
self.assertGreater(response.data["data"]["zoning"]["zone_count"], 1)
|
||||
self.assertEqual(
|
||||
response.data["data"]["zoning"]["zone_count"],
|
||||
CropArea.objects.get().zone_count,
|
||||
)
|
||||
self.assertEqual(CropArea.objects.count(), 1)
|
||||
+10
-1
@@ -1,4 +1,5 @@
|
||||
from rest_framework import serializers, status
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
@@ -7,6 +8,7 @@ from drf_spectacular.utils import extend_schema
|
||||
from config.swagger import code_response
|
||||
from .models import Sensor
|
||||
from .serializers import SensorCreateSerializer, SensorSerializer, SensorToggleSerializer
|
||||
from .services import create_sensor_with_zoning
|
||||
|
||||
|
||||
class SensorHubBaseView(APIView):
|
||||
@@ -37,8 +39,15 @@ class SensorListCreateView(SensorHubBaseView):
|
||||
def post(self, request):
|
||||
serializer = SensorCreateSerializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
sensor = serializer.save(owner=request.user)
|
||||
try:
|
||||
sensor, zoning_payload = create_sensor_with_zoning(serializer, owner=request.user)
|
||||
except ValueError as exc:
|
||||
raise serializers.ValidationError({"area_geojson": [str(exc)]}) from exc
|
||||
except ImproperlyConfigured as exc:
|
||||
return Response({"code": 500, "msg": str(exc)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
data = SensorSerializer(sensor).data
|
||||
if zoning_payload is not None:
|
||||
data["zoning"] = zoning_payload
|
||||
return Response({"code": 201, "msg": "success", "data": data}, status=status.HTTP_201_CREATED)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user