viewsets.py 7.66 KB
Newer Older
1
"""Defines the viewsets of the events package"""
2
3

from django.utils import timezone
4
from rest_framework import viewsets, filters
5
from rest_framework.decorators import action
6
from rest_framework.exceptions import PermissionDenied, NotFound
7
from rest_framework.generics import get_object_or_404
8
from rest_framework.mixins import RetrieveModelMixin, UpdateModelMixin
9
10
11
12
13
from rest_framework.permissions import (
    IsAuthenticated,
    IsAdminUser,
    IsAuthenticatedOrReadOnly
)
14
from rest_framework.response import Response
15
from rest_framework.viewsets import GenericViewSet
16

17
from events import services
18
from events.api.permissions import UnpublishedEventPermissions
Luuk Scholten's avatar
Luuk Scholten committed
19
20
21
22
from events.api.serializers import (
    EventCalenderJSSerializer,
    UnpublishedEventSerializer,
    EventRetrieveSerializer,
23
    EventListSerializer,
24
25
    RegistrationListSerializer, RegistrationSerializer)
from events.exceptions import RegistrationError
26
from events.models import Event, Registration
27
from utils.snippets import extract_date_range
28
29


30
class EventViewset(viewsets.ReadOnlyModelViewSet):
31
32
33
34
    """
    Defines the viewset for events, requires an authenticated user
    and enables ordering on the event start/end.
    """
35
    queryset = Event.objects.filter(published=True)
36
    permission_classes = [IsAuthenticated]
37
38
    filter_backends = (filters.OrderingFilter,)
    ordering_fields = ('start', 'end')
39

40
41
42
    def get_queryset(self):
        queryset = Event.objects.filter(published=True)

43
44
45
        if self.action == 'retrieve':
            return queryset

46
        start, end = extract_date_range(self.request, allow_empty=True)
47
48
49
50
51
52
53
54
55
56

        if start is not None:
            queryset = queryset.filter(start__gte=start)
        if end is not None:
            queryset = queryset.filter(end__lte=end)
        if start is None and end is None:
            queryset = queryset.filter(end__gte=timezone.now())

        return queryset

57
58
59
60
61
62
63
    def get_serializer_class(self):
        if self.action == 'list':
            return EventListSerializer
        if self.action == 'retrieve':
            return EventRetrieveSerializer
        return EventCalenderJSSerializer

64
65
66
    def get_serializer_context(self):
        return super().get_serializer_context()

67
    @action(detail=True, methods=['get', 'post'])
68
    def registrations(self, request, pk):
69
70
71
        """
        Defines a custom route for the event's registrations,
        can filter on registration status if the user is an organiser
72

73
74
75
76
        :param request: the request object
        :param pk: the primary key of the event
        :return: the registrations of the event
        """
77
        event = get_object_or_404(Event, pk=pk)
78
79
80
81

        if request.method.lower() == 'post':
            try:
                registration = services.create_registration(
82
                    request.member, event)
83
84
85
86
87
88
89
90
                serializer = RegistrationSerializer(
                    instance=registration,
                    context={'request': request}
                )
                return Response(status=201, data=serializer.data)
            except RegistrationError as e:
                raise PermissionDenied(detail=e)

91
92
        status = request.query_params.get('status', None)

93
94
        # Make sure you can only access other registrations when you have
        # the permissions to do so
95
        if not services.is_organiser(request.member, event):
96
97
            status = 'registered'

98
99
100
101
102
103
104
105
106
107
        queryset = Registration.objects.filter(event=pk)
        if status is not None:
            if status == 'queued':
                queryset = Registration.objects.filter(
                    event=pk, date_cancelled=None)[event.max_participants:]
            elif status == 'cancelled':
                queryset = Registration.objects.filter(
                    event=pk, date_cancelled__not=None)
            elif status == 'registered':
                queryset = Registration.objects.filter(
108
                    event=pk, date_cancelled=None)[:event.max_participants]
109

110
111
        serializer = RegistrationListSerializer(queryset, many=True,
                                                context={'request': request})
112
113
114

        return Response(serializer.data)

115
    @action(detail=False, permission_classes=(IsAuthenticatedOrReadOnly,))
116
    def calendarjs(self, request):
117
118
119
120
        """
        Defines a custom route that outputs the correctly formatted
        events information for CalendarJS, published events only
        :param request: the request object
121

122
123
        :return: response containing the data
        """
124
        start, end = extract_date_range(request)
125

126
        queryset = Event.objects.filter(
127
128
129
130
131
            end__gte=start,
            start__lte=end,
            published=True
        )

132
133
        serializer = EventCalenderJSSerializer(
                queryset, many=True, context={'member': request.member})
134
        return Response(serializer.data)
135

Thom Wiggers's avatar
Thom Wiggers committed
136
137
    @action(detail=False,
            permission_classes=(IsAdminUser, UnpublishedEventPermissions,))
138
    def unpublished(self, request):
139
140
141
        """
        Defines a custom route that outputs the correctly formatted
        events information for CalendarJS, unpublished events only
142

143
144
145
        :param request: the request object
        :return: response containing the data
        """
146
        start, end = extract_date_range(request)
147

148
        queryset = Event.objects.filter(
149
150
151
152
153
            end__gte=start,
            start__lte=end,
            published=False
        )

154
155
        serializer = UnpublishedEventSerializer(
                queryset, many=True, context={'member': request.member})
156
        return Response(serializer.data)
157
158
159
160


class RegistrationViewSet(GenericViewSet, RetrieveModelMixin,
                          UpdateModelMixin):
161
162
163
164
    """
    Defines the viewset for registrations, requires an authenticated user.
    Has custom update and destroy methods that use the services.
    """
165
166
167
168
169
170
171
172
173
174
175
    queryset = Registration.objects.all()
    serializer_class = RegistrationSerializer
    permission_classes = [IsAuthenticated]

    def get_serializer_context(self):
        context = super().get_serializer_context()
        context['request'] = self.request
        return context

    def get_object(self):
        instance = super().get_object()
176
177
        if (instance.member.pk != self.request.member.pk and
                not services.is_organiser(self.request.member,
178
179
180
181
182
183
184
185
186
187
188
189
190
                                          instance.event)):
            raise NotFound()

        return instance

    # Always set instance so that OPTIONS call will show the info fields too
    def get_serializer(self, *args, **kwargs):
        if len(args) == 0 and "instance" not in kwargs:
            kwargs["instance"] = self.get_object()
        return super().get_serializer(*args, **kwargs)

    def perform_update(self, serializer):
        registration = serializer.instance
191
192
193
194
195
196
197

        if services.is_organiser(self.request.member, registration.event):
            services.update_registration_by_organiser(
                registration,
                self.request.member,
                serializer.validated_data)

198
        services.update_registration(registration.member,
199
200
201
                                     registration.event,
                                     serializer.field_values())
        serializer.information_fields = services.registration_fields(
202
            serializer.context['request'],
203
            registration.member, registration.event)
204
205
206
207
208

    def destroy(self, request, pk=None, **kwargs):
        registration = self.get_object()
        try:
            services.cancel_registration(request,
209
                                         registration.member,
210
211
212
213
                                         registration.event)
            return Response(status=204)
        except RegistrationError as e:
            raise PermissionDenied(detail=e)