Coverage for website/pizzas/api/v2/views.py: 41.12%
87 statements
« prev ^ index » next coverage.py v7.6.7, created at 2025-08-14 10:31 +0000
« prev ^ index » next coverage.py v7.6.7, created at 2025-08-14 10:31 +0000
1from django.db.models import Prefetch
2from django.utils import timezone
4from oauth2_provider.contrib.rest_framework import IsAuthenticatedOrTokenHasScope
5from rest_framework import filters as framework_filters
6from rest_framework import status
7from rest_framework.exceptions import PermissionDenied
8from rest_framework.generics import (
9 CreateAPIView,
10 DestroyAPIView,
11 ListAPIView,
12 RetrieveAPIView,
13 UpdateAPIView,
14 get_object_or_404,
15)
16from rest_framework.response import Response
18from events.models.event_registration import EventRegistration
19from payments.exceptions import PaymentError
20from payments.services import delete_payment
21from pizzas.api.v2 import filters
22from pizzas.api.v2.serializers import (
23 FoodOrderCreateSerializer,
24 FoodOrderSerializer,
25 FoodOrderUpdateSerializer,
26 ProductSerializer,
27)
28from pizzas.api.v2.serializers.food_event import FoodEventSerializer
29from pizzas.models import FoodEvent, FoodOrder, Product
30from thaliawebsite.api.v2.permissions import IsAuthenticatedOrTokenHasScopeForMethod
33class FoodEventListView(ListAPIView):
34 """Returns an overview of all food events."""
36 serializer_class = FoodEventSerializer
37 filter_backends = (
38 framework_filters.OrderingFilter,
39 filters.FoodEventDateFilterBackend,
40 )
41 ordering_fields = ("start", "end")
42 permission_classes = [
43 IsAuthenticatedOrTokenHasScope,
44 ]
45 required_scopes = ["food:read"]
47 def get_queryset(self):
48 events = FoodEvent.objects.all()
49 if self.request.member:
50 events = events.prefetch_related(
51 Prefetch(
52 "event__eventregistration_set",
53 to_attr="member_registration",
54 queryset=EventRegistration.objects.filter(
55 member=self.request.member
56 ).select_properties("queue_position"),
57 )
58 )
59 return events
62class FoodEventDetailView(RetrieveAPIView):
63 """Returns one single food event."""
65 serializer_class = FoodEventSerializer
66 permission_classes = [
67 IsAuthenticatedOrTokenHasScope,
68 ]
69 required_scopes = ["food:read"]
71 def get_queryset(self):
72 events = FoodEvent.objects.all()
73 if self.request.member:
74 events = events.prefetch_related(
75 Prefetch(
76 "event__eventregistration_set",
77 to_attr="member_registration",
78 queryset=EventRegistration.objects.filter(
79 member=self.request.member
80 ).select_properties("queue_position"),
81 )
82 )
83 return events
86class FoodEventProductsListView(ListAPIView):
87 """Returns an overview of all products."""
89 serializer_class = ProductSerializer
90 queryset = Product.available_products.all()
91 filter_backends = (framework_filters.SearchFilter,)
92 search_fields = ("name",)
93 permission_classes = [
94 IsAuthenticatedOrTokenHasScope,
95 ]
96 required_scopes = ["food:read"]
99class FoodEventOrderDetailView(
100 RetrieveAPIView, CreateAPIView, UpdateAPIView, DestroyAPIView
101):
102 """Returns details of a food order."""
104 permission_classes = [
105 IsAuthenticatedOrTokenHasScopeForMethod,
106 ]
107 required_scopes_per_method = {
108 "GET": ["food:read"],
109 "POST": ["food:order"],
110 "PUT": ["food:order"],
111 "PATCH": ["food:order"],
112 "DELETE": ["food:order"],
113 }
115 def get_serializer_class(self):
116 if self.request.method.lower() == "get":
117 return FoodOrderSerializer
118 if self.request.method.lower() == "post":
119 return FoodOrderCreateSerializer
120 return FoodOrderUpdateSerializer
122 def get_queryset(self):
123 return FoodOrder.objects.filter(food_event=self.food_event)
125 def get_object(self):
126 queryset = self.filter_queryset(self.get_queryset())
127 obj = get_object_or_404(queryset, member=self.request.member)
129 # May raise a permission denied
130 self.check_object_permissions(self.request, obj)
132 return obj
134 def dispatch(self, request, *args, **kwargs):
135 self.food_event = get_object_or_404(FoodEvent, pk=self.kwargs.get("pk"))
136 try:
137 return super().dispatch(request, *args, **kwargs)
138 except PaymentError as e:
139 return Response(
140 str(e),
141 status=status.HTTP_403_FORBIDDEN,
142 )
144 def update(self, request, *args, **kwargs):
145 instance = self.get_object()
146 if instance.food_event.has_ended:
147 raise PermissionDenied
148 if instance.payment:
149 delete_payment(instance, member=request.member, ignore_change_window=True)
151 super().update(request, *args, **kwargs)
153 return Response(
154 FoodOrderSerializer(instance, context=self.get_serializer_context()).data
155 )
157 def create(self, request, *args, **kwargs):
158 if self.food_event.start > timezone.now():
159 raise PermissionDenied("You cannot order food yet")
160 if self.food_event.has_ended:
161 raise PermissionDenied("Event has ended")
163 event = self.food_event.event
164 if event.registration_required:
165 registration = event.registrations.filter(
166 member=request.member, date_cancelled=None
167 ).first()
168 if registration is None or not registration.is_invited:
169 raise PermissionDenied("You are not registered for this event")
171 serializer = self.get_serializer(data=request.data)
172 serializer.is_valid(raise_exception=True)
173 self.perform_create(serializer)
174 return Response(
175 FoodOrderSerializer(
176 serializer.instance, context=self.get_serializer_context()
177 ).data,
178 status=status.HTTP_201_CREATED,
179 )