viewsets.py 8.08 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
26
27
    RegistrationListSerializer,
    RegistrationAdminListSerializer,
    RegistrationSerializer
)
28
from events.exceptions import RegistrationError
29
from events.models import Event, Registration
30
from utils.snippets import extract_date_range
31
32


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

43
44
45
    def get_queryset(self):
        queryset = Event.objects.filter(published=True)

46
47
48
        if self.action == 'retrieve':
            return queryset

49
        start, end = extract_date_range(self.request, allow_empty=True)
50
51
52
53
54
55
56
57
58
59

        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

60
61
62
63
64
65
66
    def get_serializer_class(self):
        if self.action == 'list':
            return EventListSerializer
        if self.action == 'retrieve':
            return EventRetrieveSerializer
        return EventCalenderJSSerializer

67
68
69
    def get_serializer_context(self):
        return super().get_serializer_context()

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

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

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

94
95
        status = request.query_params.get('status', None)

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

101
102
103
104
105
106
107
108
109
110
        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(
111
                    event=pk, date_cancelled=None)[:event.max_participants]
112

113
114
115
116
117
118
119
        context = {'request': request}
        if services.is_organiser(self.request.member, event):
            serializer = RegistrationAdminListSerializer(queryset, many=True,
                                                         context=context)
        else:
            serializer = RegistrationListSerializer(queryset, many=True,
                                                    context=context)
120
121
122

        return Response(serializer.data)

123
    @action(detail=False, permission_classes=(IsAuthenticatedOrReadOnly,))
124
    def calendarjs(self, request):
125
126
127
128
        """
        Defines a custom route that outputs the correctly formatted
        events information for CalendarJS, published events only
        :param request: the request object
129

130
131
        :return: response containing the data
        """
132
        start, end = extract_date_range(request)
133

134
        queryset = Event.objects.filter(
135
136
137
138
139
            end__gte=start,
            start__lte=end,
            published=True
        )

140
141
        serializer = EventCalenderJSSerializer(
                queryset, many=True, context={'member': request.member})
142
        return Response(serializer.data)
143

Thom Wiggers's avatar
Thom Wiggers committed
144
145
    @action(detail=False,
            permission_classes=(IsAdminUser, UnpublishedEventPermissions,))
146
    def unpublished(self, request):
147
148
149
        """
        Defines a custom route that outputs the correctly formatted
        events information for CalendarJS, unpublished events only
150

151
152
153
        :param request: the request object
        :return: response containing the data
        """
154
        start, end = extract_date_range(request)
155

156
        queryset = Event.objects.filter(
157
158
159
160
161
            end__gte=start,
            start__lte=end,
            published=False
        )

162
163
        serializer = UnpublishedEventSerializer(
                queryset, many=True, context={'member': request.member})
164
        return Response(serializer.data)
165
166
167
168


class RegistrationViewSet(GenericViewSet, RetrieveModelMixin,
                          UpdateModelMixin):
169
170
171
172
    """
    Defines the viewset for registrations, requires an authenticated user.
    Has custom update and destroy methods that use the services.
    """
173
174
175
176
177
178
179
180
181
182
183
    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()
184
        if ((instance.name or instance.member.pk != self.request.member.pk) and
185
                not services.is_organiser(self.request.member,
186
187
188
189
190
191
192
193
194
195
196
197
198
                                          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
199

200
201
202
        member = self.request.member
        if (member and member.has_perm('events.change_registration') and
                services.is_organiser(member, registration.event)):
203
204
205
206
207
            services.update_registration_by_organiser(
                registration,
                self.request.member,
                serializer.validated_data)

208
        services.update_registration(registration.member,
209
210
211
                                     registration.event,
                                     serializer.field_values())
        serializer.information_fields = services.registration_fields(
212
            serializer.context['request'],
213
            registration.member, registration.event)
214
215
216
217
218

    def destroy(self, request, pk=None, **kwargs):
        registration = self.get_object()
        try:
            services.cancel_registration(request,
219
                                         registration.member,
220
221
222
223
                                         registration.event)
            return Response(status=204)
        except RegistrationError as e:
            raise PermissionDenied(detail=e)