Files
e-trznice/backend/booking/views.py
2025-10-02 00:54:34 +02:00

257 lines
9.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from rest_framework import viewsets, filters
from rest_framework.decorators import api_view, permission_classes
from rest_framework.response import Response
from rest_framework import status
from rest_framework.parsers import MultiPartParser, FormParser
from django_filters.rest_framework import DjangoFilterBackend
from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiResponse, OpenApiExample
from .models import Event, Reservation, MarketSlot, Square, ReservationCheck
from .serializers import EventSerializer, ReservationSerializer, MarketSlotSerializer, SquareSerializer, ReservationAvailabilitySerializer, ReservedDaysSerializer, ReservationCheckSerializer
from .filters import EventFilter, ReservationFilter
from rest_framework.permissions import IsAuthenticated
from rest_framework.exceptions import PermissionDenied
from django.core.exceptions import ObjectDoesNotExist
from rest_framework.views import APIView
from account.permissions import *
import logging
import logging
from account.tasks import send_email_verification_task
@extend_schema(
tags=["Square"],
description=(
"Správa náměstí vytvoření, aktualizace a výpis s doplňkovými informacemi (`quarks`) "
"a připojenými eventy. Možno filtrovat podle města, PSČ a velikosti.\n\n"
"🔍 **Fulltextové vyhledávání (`?search=`)** prohledává následující pole:\n"
"- název náměstí (`name`)\n"
"- popis (`description`)\n"
"- ulice (`street`)\n"
"- město (`city`)\n\n"
"**Příklady:** `?search=Ostrava`, `?search=Hlavní třída`"
)
)
class SquareViewSet(viewsets.ModelViewSet):
queryset = Square.objects.prefetch_related("square_events").all().order_by("name")
serializer_class = SquareSerializer
parser_classes = [MultiPartParser, FormParser] # Accept image uploads
filter_backends = [DjangoFilterBackend, filters.OrderingFilter, filters.SearchFilter]
filterset_fields = ["city", "psc", "width", "height"]
ordering_fields = ["name", "width", "height"]
search_fields = [
"name", # název náměstí
"description", # popis
"street", # ulice
"city", # město
# "psc" je číslo, obvykle do search_fields nepatří, ale můžeš ho filtrovat přes filterset_fields
]
permission_classes = [RoleAllowed("admin", "squareManager")]
def get_queryset(self):
return super().get_queryset()
@extend_schema(
tags=["Event"],
description=(
"Základní operace pro správu událostí (Event). Lze filtrovat podle času, města a velikosti náměstí.\n\n"
"🔍 **Fulltextové vyhledávání (`?search=`)** prohledává:\n"
"- název události (`name`)\n"
"- popis (`description`)\n"
"- název náměstí (`square.name`)\n"
"- město (`square.city`)\n"
"- popis náměstí (`square.description`)\n"
"- ulice (`square.street`)\n\n"
"**Příklady:** `?search=Jarmark`, `?search=Ostrava`, `?search=Masarykovo`"
)
)
class EventViewSet(viewsets.ModelViewSet):
queryset = Event.objects.prefetch_related("event_marketSlots", "event_products").all().order_by("start")
serializer_class = EventSerializer
filter_backends = [DjangoFilterBackend, filters.OrderingFilter, filters.SearchFilter]
filterset_class = EventFilter
ordering_fields = ["start", "end", "price_per_m2"]
search_fields = [
"name", # název události
"description", # popis události
"square__name", # název náměstí
"square__city", # město
"square__description", # popis náměstí (volitelný)
"square__street", # ulice
]
permission_classes = [RoleAllowed("admin", "squareManager")]
@extend_schema(
tags=["MarketSlot"],
description="Vytváření, aktualizace a mazání konkrétních prodejních míst pro události."
)
class MarketSlotViewSet(viewsets.ModelViewSet):
# queryset = MarketSlot.objects.select_related("event").all().order_by("event")
queryset = MarketSlot.objects.all().order_by("event")
serializer_class = MarketSlotSerializer
filter_backends = [DjangoFilterBackend, filters.OrderingFilter]
filterset_fields = ["event", "status"]
ordering_fields = ["price_per_m2", "x", "y"]
permission_classes = [RoleAllowed("admin", "squareManager")]
@extend_schema(
tags=["Reservation"],
description=(
"Správa rezervací vytvoření, úprava a výpis. Filtrování podle eventu, statusu, uživatele atd."
)
)
class ReservationViewSet(viewsets.ModelViewSet):
queryset = Reservation.objects.all()
serializer_class = ReservationSerializer
filter_backends = [DjangoFilterBackend, filters.OrderingFilter]
filterset_class = ReservationFilter
ordering_fields = ["reserved_from", "reserved_to", "created_at"]
search_fields = [
"event__name",
"event__square__name",
"event__square__city",
"note",
"user__email",
"user__first_name",
"user__last_name",
]
permission_classes = [RoleAllowed("admin", "squareManager", "seller")]
def get_queryset(self):
# queryset = Reservation.objects.select_related("event", "marketSlot", "user").prefetch_related("event_products").order_by("-created_at")
queryset = Reservation.objects.all().order_by("-created_at")
user = self.request.user
if hasattr(user, "role") and user.role == "seller":
return queryset.filter(user=user)
return queryset
# Optionally, override create() to add logging or debug info
def create(self, request, *args, **kwargs):
logger = logging.getLogger(__name__)
logger.debug(f"Reservation create POST data: {request.data}")
try:
return super().create(request, *args, **kwargs)
except Exception as e:
logger.error(f"Error in ReservationViewSet.create: {e}", exc_info=True)
raise
def perform_create(self, serializer):
self._check_blocked_permission(serializer.validated_data)
serializer.save()
def perform_update(self, serializer):
self._check_blocked_permission(serializer.validated_data)
serializer.save()
def _check_blocked_permission(self, data):
# FIX: Always get the MarketSlot instance, not just the ID
# Accept both "market_slot" (object or int) and "marketSlot" (legacy)
slot = data.get("market_slot") or data.get("marketSlot")
# If slot is a MarketSlot instance, get its id
if hasattr(slot, "id"):
slot_id = slot.id
else:
slot_id = slot
if not isinstance(slot_id, int):
raise PermissionDenied("Neplatné ID prodejního místa.")
try:
market_slot = MarketSlot.objects.get(pk=slot_id)
except ObjectDoesNotExist:
raise PermissionDenied("Prodejní místo nebylo nalezeno.")
if market_slot.status == "blocked":
user = self.request.user
if getattr(user, "role", None) not in ["admin", "clerk"]:
raise PermissionDenied("Toto prodejní místo je zablokované.")
@extend_schema(
tags=["Reservation"],
summary="Check reservation availability",
request=ReservationAvailabilitySerializer,
responses={200: OpenApiExample(
'Availability Response',
value={"available": True},
response_only=True
)}
)
class ReservationAvailabilityCheckView(APIView):
def post(self, request):
serializer = ReservationAvailabilitySerializer(data=request.data)
if serializer.is_valid():
return Response({"available": True}, status=status.HTTP_200_OK)
return Response({"available": False}, status=status.HTTP_200_OK)
logger = logging.getLogger(__name__)
@extend_schema(
tags=["Reservation"],
summary="Get reserved days for a market slot in an event",
description=(
"Returns a list of reserved days for a given event and market slot. "
"Useful for visualizing slot occupancy and preventing double bookings. "
"Provide `event_id` and `market_slot_id` as query parameters."
),
parameters=[
OpenApiParameter(
name="market_slot_id",
type=int,
location=OpenApiParameter.QUERY,
required=True,
description="ID of the market slot"
),
],
responses={200: ReservedDaysSerializer}
)
class ReservedDaysView(APIView):
"""
Returns reserved days for a given event and market slot.
GET params: event_id, market_slot_id
"""
def get(self, request, *args, **kwargs):
market_slot_id = request.query_params.get("market_slot_id")
if not market_slot_id:
return Response(
{"detail": "market_slot_id is required."},
status=status.HTTP_400_BAD_REQUEST
)
serializer = ReservedDaysSerializer({
"market_slot_id": market_slot_id
})
logger.debug(f"ReservedDaysView GET market_slot_id={market_slot_id}")
return Response(serializer.data)
@extend_schema(
tags=["Reservation Checks"],
description="Správa kontrol rezervací vytváření záznamů o kontrole a jejich výpis."
)
class ReservationCheckViewSet(viewsets.ModelViewSet):
queryset = ReservationCheck.objects.select_related("reservation", "checker").all().order_by("-checked_at")
serializer_class = ReservationCheckSerializer
permission_classes = [OnlyRolesAllowed("admin", "checker")] # Only checkers & admins can use it
def get_queryset(self):
user = self.request.user
if hasattr(user, "role") and user.role == "checker":
return self.queryset.filter(checker=user) # Checkers only see their own logs
return self.queryset
def perform_create(self, serializer):
serializer.save()