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

1from django.db.models import Prefetch 

2from django.utils import timezone 

3 

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 

17 

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 

31 

32 

33class FoodEventListView(ListAPIView): 

34 """Returns an overview of all food events.""" 

35 

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"] 

46 

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 

60 

61 

62class FoodEventDetailView(RetrieveAPIView): 

63 """Returns one single food event.""" 

64 

65 serializer_class = FoodEventSerializer 

66 permission_classes = [ 

67 IsAuthenticatedOrTokenHasScope, 

68 ] 

69 required_scopes = ["food:read"] 

70 

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 

84 

85 

86class FoodEventProductsListView(ListAPIView): 

87 """Returns an overview of all products.""" 

88 

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"] 

97 

98 

99class FoodEventOrderDetailView( 

100 RetrieveAPIView, CreateAPIView, UpdateAPIView, DestroyAPIView 

101): 

102 """Returns details of a food order.""" 

103 

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 } 

114 

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 

121 

122 def get_queryset(self): 

123 return FoodOrder.objects.filter(food_event=self.food_event) 

124 

125 def get_object(self): 

126 queryset = self.filter_queryset(self.get_queryset()) 

127 obj = get_object_or_404(queryset, member=self.request.member) 

128 

129 # May raise a permission denied 

130 self.check_object_permissions(self.request, obj) 

131 

132 return obj 

133 

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 ) 

143 

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) 

150 

151 super().update(request, *args, **kwargs) 

152 

153 return Response( 

154 FoodOrderSerializer(instance, context=self.get_serializer_context()).data 

155 ) 

156 

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") 

162 

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") 

170 

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 )