This commit is contained in:
2025-10-02 00:54:34 +02:00
commit 84b34c9615
200 changed files with 42048 additions and 0 deletions

257
backend/booking/views.py Normal file
View File

@@ -0,0 +1,257 @@
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()