serializers.py 14.1 KB
Newer Older
1
from django.conf import settings
Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
2
from django.templatetags.static import static
3
from django.urls import reverse
4
from django.utils import timezone
5
from django.utils.html import strip_tags, strip_spaces_between_tags
6
from html import unescape
7
from rest_framework import serializers
8
from rest_framework.fields import empty
9

10
from payments.api.fields import PaymentTypeField
11
from payments.models import Payment
Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
12
from thaliawebsite.api.services import create_image_thumbnail_dict
13
14
15
from events import services
from events.exceptions import RegistrationError
from events.models import Event, Registration, RegistrationInformationField
16
from pizzas.models import PizzaEvent
17
from thaliawebsite.templatetags.bleach_tags import bleach
18
from utils.snippets import create_google_maps_url
19
20
21


class CalenderJSSerializer(serializers.ModelSerializer):
22
23
24
    """
    Serializer using the right format for CalendarJS
    """
Luko van der Maas's avatar
Luko van der Maas committed
25

26
27
    class Meta:
        fields = (
Luko van der Maas's avatar
Luko van der Maas committed
28
29
30
31
32
33
34
            "start",
            "end",
            "allDay",
            "isBirthday",
            "url",
            "title",
            "description",
Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
35
            "classNames",
Luko van der Maas's avatar
Luko van der Maas committed
36
            "blank",
37
38
        )

Luko van der Maas's avatar
Luko van der Maas committed
39
40
41
42
43
44
45
    start = serializers.SerializerMethodField("_start")
    end = serializers.SerializerMethodField("_end")
    allDay = serializers.SerializerMethodField("_all_day")
    isBirthday = serializers.SerializerMethodField("_is_birthday")
    url = serializers.SerializerMethodField("_url")
    title = serializers.SerializerMethodField("_title")
    description = serializers.SerializerMethodField("_description")
Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
46
    classNames = serializers.SerializerMethodField("_class_names")
Luko van der Maas's avatar
Luko van der Maas committed
47
    blank = serializers.SerializerMethodField("_target_blank")
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

    def _start(self, instance):
        return timezone.localtime(instance.start)

    def _end(self, instance):
        return timezone.localtime(instance.end)

    def _all_day(self, instance):
        return False

    def _is_birthday(self, instance):
        return False

    def _url(self, instance):
        raise NotImplementedError

    def _title(self, instance):
        return instance.title

    def _description(self, instance):
68
        return unescape(strip_tags(instance.description))
69

Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
70
    def _class_names(self, instance):
71
72
73
74
75
76
        pass

    def _target_blank(self, instance):
        return False


77
class EventCalenderJSSerializer(CalenderJSSerializer):
78
79
80
81
    class Meta(CalenderJSSerializer.Meta):
        model = Event

    def _url(self, instance):
Luko van der Maas's avatar
Luko van der Maas committed
82
        return reverse("events:event", kwargs={"pk": instance.id})
83

Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
84
85
86
87
88
    def _class_names(self, instance):
        class_names = ["regular-event"]
        if services.is_user_registered(self.context["member"], instance):
            class_names.append("has-registration")
        return class_names
Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
89

90
91

class UnpublishedEventSerializer(CalenderJSSerializer):
92
    """
Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
93
    See CalenderJSSerializer, customised classes
94
    """
Luko van der Maas's avatar
Luko van der Maas committed
95

96
97
98
    class Meta(CalenderJSSerializer.Meta):
        model = Event

Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
99
100
    def _class_names(self, instance):
        return ["unpublished-event"]
101
102

    def _url(self, instance):
Luko van der Maas's avatar
Luko van der Maas committed
103
        return reverse("admin:events_event_details", kwargs={"pk": instance.id})
Gijs Hendriksen's avatar
Gijs Hendriksen committed
104
105


106
class EventRetrieveSerializer(serializers.ModelSerializer):
107
108
109
    """
    Serializer for events
    """
Luko van der Maas's avatar
Luko van der Maas committed
110

Gijs Hendriksen's avatar
Gijs Hendriksen committed
111
112
    class Meta:
        model = Event
Luko van der Maas's avatar
Luko van der Maas committed
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
        fields = (
            "pk",
            "title",
            "description",
            "start",
            "end",
            "organiser",
            "category",
            "registration_start",
            "registration_end",
            "cancel_deadline",
            "location",
            "map_location",
            "price",
            "fine",
            "max_participants",
            "num_participants",
            "user_registration",
            "registration_allowed",
            "no_registration_message",
            "has_fields",
            "is_pizza_event",
            "google_maps_url",
            "is_admin",
        )

    description = serializers.SerializerMethodField("_description")
    user_registration = serializers.SerializerMethodField("_user_registration")
    num_participants = serializers.SerializerMethodField("_num_participants")
    registration_allowed = serializers.SerializerMethodField("_registration_allowed")
    has_fields = serializers.SerializerMethodField("_has_fields")
    is_pizza_event = serializers.SerializerMethodField("_is_pizza_event")
    google_maps_url = serializers.SerializerMethodField("_google_maps_url")
    is_admin = serializers.SerializerMethodField("_is_admin")
AuckeBos's avatar
AuckeBos committed
147

148
    def _description(self, instance):
149
        return strip_spaces_between_tags(bleach(instance.description))
150

151
    def _num_participants(self, instance):
Luko van der Maas's avatar
Luko van der Maas committed
152
153
154
155
        if (
            instance.max_participants
            and instance.participants.count() > instance.max_participants
        ):
156
            return instance.max_participants
157
        return instance.participants.count()
158
159
160

    def _user_registration(self, instance):
        try:
Luko van der Maas's avatar
Luko van der Maas committed
161
            if self.context["request"].member:
162
                reg = instance.registration_set.get(
Luko van der Maas's avatar
Luko van der Maas committed
163
164
165
                    member=self.context["request"].member
                )
                return RegistrationAdminListSerializer(reg, context=self.context).data
166
        except Registration.DoesNotExist:
167
168
            pass
        return None
169
170

    def _registration_allowed(self, instance):
Luko van der Maas's avatar
Luko van der Maas committed
171
172
173
174
175
176
        member = self.context["request"].member
        return (
            self.context["request"].user.is_authenticated
            and member.has_active_membership
            and member.can_attend_events
        )
177
178
179
180

    def _has_fields(self, instance):
        return instance.has_fields()

181
182
183
    def _is_pizza_event(self, instance):
        return instance.is_pizza_event()

184
    def _google_maps_url(self, instance):
Luko van der Maas's avatar
Luko van der Maas committed
185
        return create_google_maps_url(instance.map_location, zoom=13, size="450x250")
186

187
    def _is_admin(self, instance):
Luko van der Maas's avatar
Luko van der Maas committed
188
        member = self.context["request"].member
189
190
        return services.is_organiser(member, instance)

191

192
class EventListSerializer(serializers.ModelSerializer):
193
    """Custom list serializer for events"""
Luko van der Maas's avatar
Luko van der Maas committed
194

AuckeBos's avatar
AuckeBos committed
195
196
    class Meta:
        model = Event
Luko van der Maas's avatar
Luko van der Maas committed
197
198
199
200
201
202
203
204
205
206
207
208
        fields = (
            "pk",
            "title",
            "description",
            "start",
            "end",
            "location",
            "price",
            "registered",
            "pizza",
            "registration_allowed",
        )
209

Luko van der Maas's avatar
Luko van der Maas committed
210
211
212
    description = serializers.SerializerMethodField("_description")
    registered = serializers.SerializerMethodField("_registered")
    pizza = serializers.SerializerMethodField("_pizza")
213
214

    def _description(self, instance):
215
        return unescape(strip_tags(instance.description))
216
217
218

    def _registered(self, instance):
        try:
219
            registered = services.is_user_registered(
Luko van der Maas's avatar
Luko van der Maas committed
220
                self.context["request"].user, instance,
221
222
223
224
            )
            if registered is None:
                return False
            return registered
225
        except AttributeError:
226
            return False
227

228
229
230
231
    def _pizza(self, instance):
        pizza_events = PizzaEvent.objects.filter(event=instance)
        return pizza_events.exists()

232

233
class RegistrationListSerializer(serializers.ModelSerializer):
234
    """Custom registration list serializer"""
Luko van der Maas's avatar
Luko van der Maas committed
235

236
237
    class Meta:
        model = Registration
Luko van der Maas's avatar
Luko van der Maas committed
238
        fields = ("pk", "member", "name", "avatar")
239

Luko van der Maas's avatar
Luko van der Maas committed
240
241
242
    name = serializers.SerializerMethodField("_name")
    avatar = serializers.SerializerMethodField("_avatar")
    member = serializers.SerializerMethodField("_member")
243

244
245
    def _member(self, instance):
        if instance.member:
246
            return instance.member.pk
247
        return None
248

249
250
    def _name(self, instance):
        if instance.member:
251
            return instance.member.profile.display_name()
252
253
        return instance.name

254
    def _avatar(self, instance):
Luko van der Maas's avatar
Luko van der Maas committed
255
256
257
        placeholder = self.context["request"].build_absolute_uri(
            static("members/images/default-avatar.jpg")
        )
Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
258
        file = None
259
        if instance.member and instance.member.profile.photo:
Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
260
261
            file = instance.member.profile.photo
        return create_image_thumbnail_dict(
Luko van der Maas's avatar
Luko van der Maas committed
262
263
            self.context["request"], file, placeholder=placeholder, size_large="800x800"
        )
264

265

266
267
class RegistrationAdminListSerializer(RegistrationListSerializer):
    """Custom registration admin list serializer"""
Luko van der Maas's avatar
Luko van der Maas committed
268

269
270
    class Meta:
        model = Registration
Luko van der Maas's avatar
Luko van der Maas committed
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
        fields = (
            "pk",
            "member",
            "name",
            "registered_on",
            "is_cancelled",
            "is_late_cancellation",
            "queue_position",
            "payment",
            "present",
            "avatar",
        )

    registered_on = serializers.DateTimeField(source="date")
    is_cancelled = serializers.SerializerMethodField("_is_cancelled")
    is_late_cancellation = serializers.SerializerMethodField("_is_late_cancellation")
    queue_position = serializers.SerializerMethodField("_queue_position")
    payment = PaymentTypeField(source="payment.type", choices=Payment.PAYMENT_TYPE)
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305

    def _is_late_cancellation(self, instance):
        return instance.is_late_cancellation()

    def _queue_position(self, instance):
        pos = instance.queue_position
        return pos if pos > 0 else None

    def _is_cancelled(self, instance):
        return instance.date_cancelled is not None

    def _name(self, instance):
        if instance.member:
            return instance.member.get_full_name()
        return instance.name


306
class RegistrationSerializer(serializers.ModelSerializer):
307
    """Registration serializer"""
Luko van der Maas's avatar
Luko van der Maas committed
308

309
310
311
312
    information_fields = None

    class Meta:
        model = Registration
Luko van der Maas's avatar
Luko van der Maas committed
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
        fields = (
            "pk",
            "member",
            "name",
            "photo",
            "avatar",
            "registered_on",
            "is_late_cancellation",
            "is_cancelled",
            "queue_position",
            "fields",
            "payment",
            "present",
        )

    name = serializers.SerializerMethodField("_name")
    photo = serializers.SerializerMethodField("_photo")
    avatar = serializers.SerializerMethodField("_avatar")
    member = serializers.SerializerMethodField("_member")
    payment = PaymentTypeField(source="payment.type", choices=Payment.PAYMENT_TYPE)
    registered_on = serializers.DateTimeField(source="date", read_only=True)
    is_cancelled = serializers.SerializerMethodField("_is_cancelled")
    is_late_cancellation = serializers.SerializerMethodField("_is_late_cancellation")
336
    queue_position = serializers.SerializerMethodField(
Luko van der Maas's avatar
Luko van der Maas committed
337
338
339
        "_queue_position", read_only=False
    )
    fields = serializers.HiddenField(default="")
340
341
342
343
344

    def _is_late_cancellation(self, instance):
        val = instance.is_late_cancellation()
        return False if val is None else val

345
    def _is_cancelled(self, instance):
346
347
348
349
350
        return instance.date_cancelled is not None

    def _queue_position(self, instance):
        pos = instance.queue_position
        return pos if pos > 0 else None
351
352
353

    def _member(self, instance):
        if instance.member:
354
            return instance.member.pk
355
356
357
358
        return None

    def _name(self, instance):
        if instance.member:
359
            return instance.member.profile.display_name()
360
361
362
        return instance.name

    def _photo(self, instance):
363
        if instance.member and instance.member.profile.photo:
Luko van der Maas's avatar
Luko van der Maas committed
364
365
366
            return self.context["request"].build_absolute_uri(
                "%s%s" % (settings.MEDIA_URL, instance.member.profile.photo)
            )
367
        else:
Luko van der Maas's avatar
Luko van der Maas committed
368
369
370
            return self.context["request"].build_absolute_uri(
                static("members/images/default-avatar.jpg")
            )
371

372
    def _avatar(self, instance):
Luko van der Maas's avatar
Luko van der Maas committed
373
374
375
        placeholder = self.context["request"].build_absolute_uri(
            static("members/images/default-avatar.jpg")
        )
Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
376
        file = None
377
        if instance.member and instance.member.profile.photo:
Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
378
379
            file = instance.member.profile.photo
        return create_image_thumbnail_dict(
Luko van der Maas's avatar
Luko van der Maas committed
380
381
            self.context["request"], file, placeholder=placeholder, size_large="800x800"
        )
382

383
384
385
386
387
    def __init__(self, instance=None, data=empty, **kwargs):
        super().__init__(instance, data, **kwargs)
        try:
            if instance:
                self.information_fields = services.registration_fields(
Luko van der Maas's avatar
Luko van der Maas committed
388
389
                    kwargs["context"]["request"], registration=instance
                )
390
391
392
393
394
395
396
397
        except RegistrationError:
            pass

    def get_fields(self):
        fields = super().get_fields()

        if self.information_fields:
            for key, field in self.information_fields.items():
Luko van der Maas's avatar
Luko van der Maas committed
398
399
                key = "fields[{}]".format(key)
                field_type = field["type"]
400
401
402

                if field_type == RegistrationInformationField.BOOLEAN_FIELD:
                    fields[key] = serializers.BooleanField(
Luko van der Maas's avatar
Luko van der Maas committed
403
                        required=False, write_only=True
404
405
406
                    )
                elif field_type == RegistrationInformationField.INTEGER_FIELD:
                    fields[key] = serializers.IntegerField(
Luko van der Maas's avatar
Luko van der Maas committed
407
                        required=field["required"],
408
                        write_only=True,
Luko van der Maas's avatar
Luko van der Maas committed
409
                        allow_null=not field["required"],
410
411
412
                    )
                elif field_type == RegistrationInformationField.TEXT_FIELD:
                    fields[key] = serializers.CharField(
Luko van der Maas's avatar
Luko van der Maas committed
413
                        required=field["required"],
414
                        write_only=True,
Luko van der Maas's avatar
Luko van der Maas committed
415
416
                        allow_blank=not field["required"],
                        allow_null=not field["required"],
417
418
                    )

Luko van der Maas's avatar
Luko van der Maas committed
419
420
421
422
                fields[key].label = field["label"]
                fields[key].help_text = field["description"]
                fields[key].initial = field["value"]
                fields[key].default = field["value"]
423
424
425
426
427
428
429
430
431
432
433

                try:
                    if key in self.information_fields:
                        fields[key].initial = self.validated_data[key]
                except AssertionError:
                    pass

        return fields

    def to_representation(self, instance):
        data = super().to_representation(instance)
Luko van der Maas's avatar
Luko van der Maas committed
434
        data["fields"] = self.information_fields
435
436
437
        return data

    def field_values(self):
Luko van der Maas's avatar
Luko van der Maas committed
438
439
440
441
442
        return (
            (name[7 : len(name) - 1], value)
            for name, value in self.validated_data.items()
            if "info_field" in name
        )