165 lines
6.9 KiB
Python
165 lines
6.9 KiB
Python
|
|
from drf_spectacular.types import OpenApiTypes
|
||
|
|
from drf_spectacular.utils import OpenApiParameter, extend_schema
|
||
|
|
from rest_framework.exceptions import NotFound
|
||
|
|
from rest_framework import serializers, status
|
||
|
|
from rest_framework.permissions import IsAuthenticated
|
||
|
|
from rest_framework.response import Response
|
||
|
|
from rest_framework.views import APIView
|
||
|
|
|
||
|
|
from farm_hub.models import FarmHub
|
||
|
|
|
||
|
|
from .enums import FARMER_TAG_ITEMS
|
||
|
|
from .models import FarmerCalendarEvent
|
||
|
|
from .serializers import (
|
||
|
|
FarmerCalendarEventResponseSerializer,
|
||
|
|
FarmerCalendarEventWriteSerializer,
|
||
|
|
FarmerCalendarListQuerySerializer,
|
||
|
|
FarmerCalendarTagIdSerializer,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
class FarmerCalendarBaseView(APIView):
|
||
|
|
permission_classes = [IsAuthenticated]
|
||
|
|
|
||
|
|
@staticmethod
|
||
|
|
def error_response(message, details=None, code="EVENT_VALIDATION_ERROR", status_code=status.HTTP_400_BAD_REQUEST):
|
||
|
|
payload = {
|
||
|
|
"code": code,
|
||
|
|
"message": message,
|
||
|
|
}
|
||
|
|
if details is not None:
|
||
|
|
payload["details"] = details
|
||
|
|
return Response(payload, status=status_code)
|
||
|
|
|
||
|
|
def handle_exception(self, exc):
|
||
|
|
if isinstance(exc, serializers.ValidationError):
|
||
|
|
details = exc.detail
|
||
|
|
message = "Invalid event payload"
|
||
|
|
if isinstance(details, dict):
|
||
|
|
first_value = next(iter(details.values()), None)
|
||
|
|
if isinstance(first_value, list) and first_value:
|
||
|
|
message = str(first_value[0])
|
||
|
|
elif first_value:
|
||
|
|
message = str(first_value)
|
||
|
|
elif isinstance(details, list) and details:
|
||
|
|
message = str(details[0])
|
||
|
|
return self.error_response(message=message, details=details)
|
||
|
|
if isinstance(exc, NotFound):
|
||
|
|
return self.error_response(
|
||
|
|
message=str(exc.detail),
|
||
|
|
code="EVENT_NOT_FOUND",
|
||
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
||
|
|
)
|
||
|
|
return super().handle_exception(exc)
|
||
|
|
|
||
|
|
def _get_user_farms(self, request):
|
||
|
|
return FarmHub.objects.filter(owner=request.user).order_by("id")
|
||
|
|
|
||
|
|
def _resolve_farm(self, request, farm_uuid=None, required=False):
|
||
|
|
farms = self._get_user_farms(request)
|
||
|
|
if farm_uuid:
|
||
|
|
try:
|
||
|
|
return farms.get(farm_uuid=farm_uuid)
|
||
|
|
except FarmHub.DoesNotExist as exc:
|
||
|
|
raise serializers.ValidationError({"farm_uuid": ["Farm not found."]}) from exc
|
||
|
|
|
||
|
|
if required:
|
||
|
|
farm_count = farms.count()
|
||
|
|
if farm_count == 1:
|
||
|
|
return farms.first()
|
||
|
|
if farm_count == 0:
|
||
|
|
raise serializers.ValidationError({"farm_uuid": ["No farm found for this user."]})
|
||
|
|
raise serializers.ValidationError({"farm_uuid": ["farm_uuid is required when multiple farms exist."]})
|
||
|
|
return None
|
||
|
|
|
||
|
|
def _get_event(self, request, event_id):
|
||
|
|
queryset = FarmerCalendarEvent.objects.select_related("farm").prefetch_related("tags")
|
||
|
|
try:
|
||
|
|
return queryset.get(uuid=event_id, farm__owner=request.user)
|
||
|
|
except FarmerCalendarEvent.DoesNotExist as exc:
|
||
|
|
raise NotFound("Event not found.") from exc
|
||
|
|
|
||
|
|
|
||
|
|
class EventListCreateView(FarmerCalendarBaseView):
|
||
|
|
@extend_schema(
|
||
|
|
tags=["Farmer Calendar"],
|
||
|
|
parameters=[
|
||
|
|
OpenApiParameter(name="start", type=OpenApiTypes.DATETIME, location=OpenApiParameter.QUERY, required=False),
|
||
|
|
OpenApiParameter(name="end", type=OpenApiTypes.DATETIME, location=OpenApiParameter.QUERY, required=False),
|
||
|
|
OpenApiParameter(name="farm_uuid", type=OpenApiTypes.UUID, location=OpenApiParameter.QUERY, required=False),
|
||
|
|
],
|
||
|
|
)
|
||
|
|
def get(self, request):
|
||
|
|
query_serializer = FarmerCalendarListQuerySerializer(data=request.query_params)
|
||
|
|
query_serializer.is_valid(raise_exception=True)
|
||
|
|
|
||
|
|
queryset = FarmerCalendarEvent.objects.filter(farm__owner=request.user).prefetch_related("tags")
|
||
|
|
farm = self._resolve_farm(request, query_serializer.validated_data.get("farm_uuid"), required=False)
|
||
|
|
if farm is not None:
|
||
|
|
queryset = queryset.filter(farm=farm)
|
||
|
|
|
||
|
|
start = query_serializer.validated_data.get("start")
|
||
|
|
end = query_serializer.validated_data.get("end")
|
||
|
|
if start:
|
||
|
|
queryset = queryset.filter(end__gte=start)
|
||
|
|
if end:
|
||
|
|
queryset = queryset.filter(start__lte=end)
|
||
|
|
|
||
|
|
events = queryset.order_by("start", "created_at")
|
||
|
|
data = FarmerCalendarEventResponseSerializer(events, many=True).data
|
||
|
|
return Response({"events": data, "meta": {"total": len(data)}}, status=status.HTTP_200_OK)
|
||
|
|
|
||
|
|
@extend_schema(tags=["Farmer Calendar"])
|
||
|
|
def post(self, request):
|
||
|
|
serializer = FarmerCalendarEventWriteSerializer(data=request.data)
|
||
|
|
serializer.is_valid(raise_exception=True)
|
||
|
|
|
||
|
|
farm = self._resolve_farm(request, serializer.validated_data.get("farm_uuid"), required=True)
|
||
|
|
event = serializer.save(farm=farm)
|
||
|
|
data = FarmerCalendarEventResponseSerializer(event).data
|
||
|
|
return Response({"event": data}, status=status.HTTP_201_CREATED)
|
||
|
|
|
||
|
|
|
||
|
|
class EventTagListView(FarmerCalendarBaseView):
|
||
|
|
@extend_schema(
|
||
|
|
tags=["Farmer Calendar"],
|
||
|
|
parameters=[
|
||
|
|
OpenApiParameter(name="farm_uuid", type=OpenApiTypes.UUID, location=OpenApiParameter.QUERY, required=False),
|
||
|
|
],
|
||
|
|
)
|
||
|
|
def get(self, request):
|
||
|
|
self._resolve_farm(request, request.query_params.get("farm_uuid"), required=False)
|
||
|
|
data = FarmerCalendarTagIdSerializer(FARMER_TAG_ITEMS, many=True).data
|
||
|
|
return Response({"tags": data, "meta": {"total": len(data)}}, status=status.HTTP_200_OK)
|
||
|
|
|
||
|
|
|
||
|
|
class EventDetailView(FarmerCalendarBaseView):
|
||
|
|
@extend_schema(tags=["Farmer Calendar"])
|
||
|
|
def get(self, request, event_id):
|
||
|
|
event = self._get_event(request, event_id)
|
||
|
|
data = FarmerCalendarEventResponseSerializer(event).data
|
||
|
|
return Response({"event": data}, status=status.HTTP_200_OK)
|
||
|
|
|
||
|
|
@extend_schema(tags=["Farmer Calendar"])
|
||
|
|
def put(self, request, event_id):
|
||
|
|
event = self._get_event(request, event_id)
|
||
|
|
serializer = FarmerCalendarEventWriteSerializer(event, data=request.data)
|
||
|
|
serializer.is_valid(raise_exception=True)
|
||
|
|
|
||
|
|
requested_farm_uuid = serializer.validated_data.get("farm_uuid")
|
||
|
|
if requested_farm_uuid and str(event.farm.farm_uuid) != str(requested_farm_uuid):
|
||
|
|
return self.error_response(
|
||
|
|
message="farm_uuid cannot change an existing event",
|
||
|
|
details={"farm_uuid": ["farm_uuid cannot change an existing event"]},
|
||
|
|
)
|
||
|
|
|
||
|
|
event = serializer.save()
|
||
|
|
data = FarmerCalendarEventResponseSerializer(event).data
|
||
|
|
return Response({"event": data}, status=status.HTTP_200_OK)
|
||
|
|
|
||
|
|
@extend_schema(tags=["Farmer Calendar"])
|
||
|
|
def delete(self, request, event_id):
|
||
|
|
event = self._get_event(request, event_id)
|
||
|
|
event.delete()
|
||
|
|
return Response({"success": True}, status=status.HTTP_200_OK)
|