2026-04-11 03:54:15 +03:30
|
|
|
from rest_framework import status
|
|
|
|
|
from rest_framework.response import Response
|
|
|
|
|
from rest_framework.views import APIView
|
2026-04-27 00:40:59 +03:30
|
|
|
from drf_spectacular.utils import extend_schema
|
2026-04-11 03:54:15 +03:30
|
|
|
|
2026-04-27 00:40:59 +03:30
|
|
|
from config.swagger import farm_uuid_query_param, status_response
|
|
|
|
|
from external_api_adapter import request as external_api_request
|
2026-04-11 03:54:15 +03:30
|
|
|
from farm_hub.models import FarmHub
|
|
|
|
|
|
|
|
|
|
from .serializers import (
|
|
|
|
|
SoilAnomalyDetectionSerializer,
|
|
|
|
|
SoilKpiSerializer,
|
|
|
|
|
SoilMoistureHeatmapSerializer,
|
|
|
|
|
SoilSummarySerializer,
|
|
|
|
|
)
|
|
|
|
|
from .services import (
|
|
|
|
|
get_anomaly_detection_card_data,
|
|
|
|
|
get_avg_soil_moisture_data,
|
|
|
|
|
get_soil_moisture_heatmap_data,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _get_farm_from_request(request):
|
|
|
|
|
farm_uuid = request.query_params.get("farm_uuid")
|
|
|
|
|
if not farm_uuid:
|
|
|
|
|
return None
|
|
|
|
|
try:
|
|
|
|
|
return FarmHub.objects.get(farm_uuid=farm_uuid)
|
|
|
|
|
except (FarmHub.DoesNotExist, Exception):
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
2026-04-27 00:40:59 +03:30
|
|
|
def _extract_adapter_result(adapter_data):
|
|
|
|
|
if not isinstance(adapter_data, dict):
|
|
|
|
|
return {}
|
2026-04-11 03:54:15 +03:30
|
|
|
|
2026-04-27 00:40:59 +03:30
|
|
|
data = adapter_data.get("data")
|
|
|
|
|
if isinstance(data, dict) and isinstance(data.get("result"), dict):
|
|
|
|
|
return data["result"]
|
|
|
|
|
if isinstance(data, dict):
|
|
|
|
|
return data
|
2026-04-11 03:54:15 +03:30
|
|
|
|
2026-04-27 00:40:59 +03:30
|
|
|
result = adapter_data.get("result")
|
|
|
|
|
if isinstance(result, dict):
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
return adapter_data
|
2026-04-11 03:54:15 +03:30
|
|
|
|
|
|
|
|
|
2026-04-27 00:40:59 +03:30
|
|
|
class AvgSoilMoistureView(APIView):
|
2026-04-11 03:54:15 +03:30
|
|
|
@extend_schema(
|
|
|
|
|
tags=["Soil"],
|
|
|
|
|
parameters=[
|
2026-04-27 00:40:59 +03:30
|
|
|
farm_uuid_query_param(required=False, description="UUID of the farm for average soil moisture."),
|
2026-04-11 03:54:15 +03:30
|
|
|
],
|
2026-04-27 00:40:59 +03:30
|
|
|
responses={200: status_response("AvgSoilMoistureResponse", data=SoilKpiSerializer())},
|
2026-04-11 03:54:15 +03:30
|
|
|
)
|
|
|
|
|
def get(self, request):
|
|
|
|
|
return Response(
|
2026-04-27 00:40:59 +03:30
|
|
|
{"status": "success", "data": get_avg_soil_moisture_data(_get_farm_from_request(request))},
|
2026-04-11 03:54:15 +03:30
|
|
|
status=status.HTTP_200_OK,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SoilAnomalyDetectionView(APIView):
|
|
|
|
|
@extend_schema(
|
|
|
|
|
tags=["Soil"],
|
|
|
|
|
parameters=[
|
2026-04-27 00:40:59 +03:30
|
|
|
farm_uuid_query_param(required=True, description="UUID of the farm for soil anomaly detection."),
|
2026-04-11 03:54:15 +03:30
|
|
|
],
|
|
|
|
|
responses={200: status_response("SoilAnomalyDetectionResponse", data=SoilAnomalyDetectionSerializer())},
|
|
|
|
|
)
|
|
|
|
|
def get(self, request):
|
2026-04-27 00:40:59 +03:30
|
|
|
farm_uuid = request.query_params.get("farm_uuid")
|
|
|
|
|
if not farm_uuid:
|
|
|
|
|
return Response(
|
|
|
|
|
{"code": 400, "msg": "error", "data": {"farm_uuid": ["This field is required."]}},
|
|
|
|
|
status=status.HTTP_400_BAD_REQUEST,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
farm = _get_farm_from_request(request)
|
|
|
|
|
if farm is None:
|
|
|
|
|
return Response(
|
|
|
|
|
{"code": 404, "msg": "error", "data": {"farm_uuid": ["Farm not found."]}},
|
|
|
|
|
status=status.HTTP_404_NOT_FOUND,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
adapter_response = external_api_request(
|
|
|
|
|
"ai",
|
|
|
|
|
"/api/soile/anomaly-detection/",
|
|
|
|
|
method="POST",
|
|
|
|
|
payload={"farm_uuid": str(farm.farm_uuid)},
|
|
|
|
|
)
|
|
|
|
|
if adapter_response.status_code >= 400:
|
|
|
|
|
response_data = (
|
|
|
|
|
adapter_response.data
|
|
|
|
|
if isinstance(adapter_response.data, dict)
|
|
|
|
|
else {"message": str(adapter_response.data)}
|
|
|
|
|
)
|
|
|
|
|
return Response(
|
|
|
|
|
{"code": adapter_response.status_code, "msg": "error", "data": response_data},
|
|
|
|
|
status=adapter_response.status_code,
|
|
|
|
|
)
|
|
|
|
|
|
2026-04-11 03:54:15 +03:30
|
|
|
return Response(
|
2026-04-27 00:40:59 +03:30
|
|
|
{"code": 200, "msg": "success", "data": _extract_adapter_result(adapter_response.data)},
|
2026-04-11 03:54:15 +03:30
|
|
|
status=status.HTTP_200_OK,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SoilMoistureHeatmapView(APIView):
|
|
|
|
|
@extend_schema(
|
|
|
|
|
tags=["Soil"],
|
|
|
|
|
parameters=[
|
2026-04-27 00:40:59 +03:30
|
|
|
farm_uuid_query_param(required=True, description="UUID of the farm for soil moisture heatmap."),
|
2026-04-11 03:54:15 +03:30
|
|
|
],
|
|
|
|
|
responses={200: status_response("SoilMoistureHeatmapResponse", data=SoilMoistureHeatmapSerializer())},
|
|
|
|
|
)
|
|
|
|
|
def get(self, request):
|
2026-04-27 00:40:59 +03:30
|
|
|
farm_uuid = request.query_params.get("farm_uuid")
|
|
|
|
|
if not farm_uuid:
|
|
|
|
|
return Response(
|
|
|
|
|
{"code": 400, "msg": "error", "data": {"farm_uuid": ["This field is required."]}},
|
|
|
|
|
status=status.HTTP_400_BAD_REQUEST,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
farm = _get_farm_from_request(request)
|
|
|
|
|
if farm is None:
|
|
|
|
|
return Response(
|
|
|
|
|
{"code": 404, "msg": "error", "data": {"farm_uuid": ["Farm not found."]}},
|
|
|
|
|
status=status.HTTP_404_NOT_FOUND,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
adapter_response = external_api_request(
|
|
|
|
|
"ai",
|
|
|
|
|
"/api/soile/moisture-heatmap/",
|
|
|
|
|
method="POST",
|
|
|
|
|
payload={"farm_uuid": str(farm.farm_uuid)},
|
|
|
|
|
)
|
|
|
|
|
if adapter_response.status_code >= 400:
|
|
|
|
|
response_data = (
|
|
|
|
|
adapter_response.data
|
|
|
|
|
if isinstance(adapter_response.data, dict)
|
|
|
|
|
else {"message": str(adapter_response.data)}
|
|
|
|
|
)
|
|
|
|
|
return Response(
|
|
|
|
|
{"code": adapter_response.status_code, "msg": "error", "data": response_data},
|
|
|
|
|
status=adapter_response.status_code,
|
|
|
|
|
)
|
|
|
|
|
|
2026-04-11 03:54:15 +03:30
|
|
|
return Response(
|
2026-04-27 00:40:59 +03:30
|
|
|
{"code": 200, "msg": "success", "data": _extract_adapter_result(adapter_response.data)},
|
2026-04-11 03:54:15 +03:30
|
|
|
status=status.HTTP_200_OK,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SoilSummaryView(APIView):
|
|
|
|
|
@extend_schema(
|
|
|
|
|
tags=["Soil"],
|
|
|
|
|
parameters=[
|
2026-04-27 00:40:59 +03:30
|
|
|
farm_uuid_query_param(required=True, description="UUID of the farm for soil health summary."),
|
2026-04-11 03:54:15 +03:30
|
|
|
],
|
|
|
|
|
responses={200: status_response("SoilSummaryResponse", data=SoilSummarySerializer())},
|
|
|
|
|
)
|
|
|
|
|
def get(self, request):
|
2026-04-27 00:40:59 +03:30
|
|
|
farm_uuid = request.query_params.get("farm_uuid")
|
|
|
|
|
if not farm_uuid:
|
|
|
|
|
return Response(
|
|
|
|
|
{"code": 400, "msg": "error", "data": {"farm_uuid": ["This field is required."]}},
|
|
|
|
|
status=status.HTTP_400_BAD_REQUEST,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
farm = _get_farm_from_request(request)
|
|
|
|
|
if farm is None:
|
|
|
|
|
return Response(
|
|
|
|
|
{"code": 404, "msg": "error", "data": {"farm_uuid": ["Farm not found."]}},
|
|
|
|
|
status=status.HTTP_404_NOT_FOUND,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
adapter_response = external_api_request(
|
|
|
|
|
"ai",
|
|
|
|
|
"/api/soile/health-summary/",
|
|
|
|
|
method="POST",
|
|
|
|
|
payload={"farm_uuid": str(farm.farm_uuid)},
|
|
|
|
|
)
|
|
|
|
|
if adapter_response.status_code >= 400:
|
|
|
|
|
response_data = (
|
|
|
|
|
adapter_response.data
|
|
|
|
|
if isinstance(adapter_response.data, dict)
|
|
|
|
|
else {"message": str(adapter_response.data)}
|
|
|
|
|
)
|
|
|
|
|
return Response(
|
|
|
|
|
{"code": adapter_response.status_code, "msg": "error", "data": response_data},
|
|
|
|
|
status=adapter_response.status_code,
|
|
|
|
|
)
|
|
|
|
|
|
2026-04-11 03:54:15 +03:30
|
|
|
return Response(
|
2026-04-27 00:40:59 +03:30
|
|
|
{"code": 200, "msg": "success", "data": _extract_adapter_result(adapter_response.data)},
|
2026-04-11 03:54:15 +03:30
|
|
|
status=status.HTTP_200_OK,
|
|
|
|
|
)
|