serializers.py 13.7 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.models import Payment
Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
11
from thaliawebsite.api.services import create_image_thumbnail_dict
12
13
14
from events import services
from events.exceptions import RegistrationError
from events.models import Event, Registration, RegistrationInformationField
15
from pizzas.models import PizzaEvent
16
from thaliawebsite.templatetags.bleach_tags import bleach
17
from utils.snippets import create_google_maps_url
18
19
20


class CalenderJSSerializer(serializers.ModelSerializer):
21
22
23
    """
    Serializer using the right format for CalendarJS
    """
24
25
26
27
    class Meta:
        fields = (
            'start', 'end', 'all_day', 'is_birthday',
            'url', 'title', 'description',
28
            'backgroundColor', 'textColor', 'blank'
29
30
31
32
33
34
35
36
37
        )

    start = serializers.SerializerMethodField('_start')
    end = serializers.SerializerMethodField('_end')
    all_day = serializers.SerializerMethodField('_all_day')
    is_birthday = serializers.SerializerMethodField('_is_birthday')
    url = serializers.SerializerMethodField('_url')
    title = serializers.SerializerMethodField('_title')
    description = serializers.SerializerMethodField('_description')
38
39
40
    backgroundColor = serializers.SerializerMethodField('_background_color')
    textColor = serializers.SerializerMethodField('_text_color')
    blank = serializers.SerializerMethodField('_target_blank')
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

    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):
61
        return unescape(strip_tags(instance.description))
62
63
64
65
66
67
68
69
70
71
72

    def _background_color(self, instance):
        pass

    def _text_color(self, instance):
        pass

    def _target_blank(self, instance):
        return False


73
class EventCalenderJSSerializer(CalenderJSSerializer):
74
75
76
77
    class Meta(CalenderJSSerializer.Meta):
        model = Event

    def _url(self, instance):
78
        return reverse('events:event', kwargs={'pk': instance.id})
79

80
    def _background_color(self, instance):
81
        try:
82
83
84
            if services.is_user_registered(self.context['member'],
                                           instance):
                return "#E62272"
85
        except AttributeError:
86
87
            pass
        return "#616161"
88
89
90


class UnpublishedEventSerializer(CalenderJSSerializer):
91
92
93
    """
    See CalenderJSSerializer, customised colors
    """
94
95
96
97
    class Meta(CalenderJSSerializer.Meta):
        model = Event

    def _background_color(self, instance):
98
        return "rgba(255,0,0,0.3)"
99
100

    def _text_color(self, instance):
101
        return "black"
102
103

    def _url(self, instance):
104
        return reverse('admin:events_event_details', kwargs={
105
            'pk': instance.id})
Gijs Hendriksen's avatar
Gijs Hendriksen committed
106
107


108
class EventRetrieveSerializer(serializers.ModelSerializer):
109
110
111
    """
    Serializer for events
    """
Gijs Hendriksen's avatar
Gijs Hendriksen committed
112
113
    class Meta:
        model = Event
114
115
116
        fields = ('pk', 'title', 'description', 'start', 'end', 'organiser',
                  'category', 'registration_start', 'registration_end',
                  'cancel_deadline', 'location', 'map_location', 'price',
117
                  'fine', 'max_participants', 'num_participants',
118
                  'user_registration', 'registration_allowed',
119
                  'no_registration_message', 'has_fields', 'is_pizza_event',
120
                  'google_maps_url', 'is_admin')
AuckeBos's avatar
AuckeBos committed
121

122
    description = serializers.SerializerMethodField('_description')
123
124
125
126
127
    user_registration = serializers.SerializerMethodField('_user_registration')
    num_participants = serializers.SerializerMethodField('_num_participants')
    registration_allowed = serializers.SerializerMethodField(
        '_registration_allowed')
    has_fields = serializers.SerializerMethodField('_has_fields')
128
    is_pizza_event = serializers.SerializerMethodField('_is_pizza_event')
129
    google_maps_url = serializers.SerializerMethodField('_google_maps_url')
130
    is_admin = serializers.SerializerMethodField('_is_admin')
AuckeBos's avatar
AuckeBos committed
131

132
    def _description(self, instance):
133
        return strip_spaces_between_tags(bleach(instance.description))
134

135
    def _num_participants(self, instance):
136
        if (instance.max_participants and
137
                instance.participants.count() > instance.max_participants):
138
            return instance.max_participants
139
        return instance.participants.count()
140
141
142
143

    def _user_registration(self, instance):
        try:
            reg = instance.registration_set.get(
144
                member=self.context['request'].member)
145
146
            return RegistrationAdminListSerializer(reg,
                                                   context=self.context).data
147
148
149
150
        except Registration.DoesNotExist:
            return None

    def _registration_allowed(self, instance):
151
152
153
        member = self.context['request'].member
        return (member.has_active_membership and
                member.can_attend_events)
154
155
156
157

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

158
159
160
    def _is_pizza_event(self, instance):
        return instance.is_pizza_event()

161
    def _google_maps_url(self, instance):
Thom Wiggers's avatar
Thom Wiggers committed
162
163
164
165
        return create_google_maps_url(
                instance.map_location,
                zoom=13,
                size='450x250')
166

167
168
169
170
    def _is_admin(self, instance):
        member = self.context['request'].member
        return services.is_organiser(member, instance)

171

172
class EventListSerializer(serializers.ModelSerializer):
173
    """Custom list serializer for events"""
AuckeBos's avatar
AuckeBos committed
174
175
    class Meta:
        model = Event
176
        fields = ('pk', 'title', 'description', 'start', 'end',
177
                  'location', 'price', 'registered', 'pizza')
178
179

    description = serializers.SerializerMethodField('_description')
180
    registered = serializers.SerializerMethodField('_registered')
181
    pizza = serializers.SerializerMethodField('_pizza')
182
183

    def _description(self, instance):
184
        return unescape(strip_tags(instance.description))
185
186
187

    def _registered(self, instance):
        try:
188
189
            return services.is_user_registered(self.context['request'].user,
                                               instance)
190
191
192
        except AttributeError:
            return None

193
194
195
196
    def _pizza(self, instance):
        pizza_events = PizzaEvent.objects.filter(event=instance)
        return pizza_events.exists()

197

198
class RegistrationListSerializer(serializers.ModelSerializer):
199
    """Custom registration list serializer"""
200
201
    class Meta:
        model = Registration
202
        fields = ('pk', 'member', 'name', 'avatar')
203
204

    name = serializers.SerializerMethodField('_name')
205
    avatar = serializers.SerializerMethodField('_avatar')
206
    member = serializers.SerializerMethodField('_member')
207

208
209
    def _member(self, instance):
        if instance.member:
210
            return instance.member.pk
211
        return None
212

213
214
    def _name(self, instance):
        if instance.member:
215
            return instance.member.profile.display_name()
216
217
        return instance.name

218
219
220
    def _avatar(self, instance):
        placeholder = self.context['request'].build_absolute_uri(
            static('members/images/default-avatar.jpg'))
Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
221
        file = None
222
        if instance.member and instance.member.profile.photo:
Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
223
224
225
226
            file = instance.member.profile.photo
        return create_image_thumbnail_dict(
            self.context['request'], file, placeholder=placeholder,
            size_large='800x800')
227

228

229
230
231
232
233
234
235
236
class PaymentTypeField(serializers.ChoiceField):

    def get_attribute(self, instance):
        if not instance.payment:
            return Payment.NONE
        return super().get_attribute(instance)


237
238
239
240
241
242
243
244
245
246
247
248
249
class RegistrationAdminListSerializer(RegistrationListSerializer):
    """Custom registration admin list serializer"""
    class Meta:
        model = Registration
        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')
250
251
    payment = PaymentTypeField(source='payment.type',
                               choices=Payment.PAYMENT_TYPE)
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268

    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


269
class RegistrationSerializer(serializers.ModelSerializer):
270
    """Registration serializer"""
271
272
273
274
    information_fields = None

    class Meta:
        model = Registration
275
        fields = ('pk', 'member', 'name', 'photo', 'avatar', 'registered_on',
276
                  'is_late_cancellation', 'is_cancelled',
277
278
                  'queue_position', 'fields',
                  'payment', 'present')
279
280
281

    name = serializers.SerializerMethodField('_name')
    photo = serializers.SerializerMethodField('_photo')
282
    avatar = serializers.SerializerMethodField('_avatar')
283
    member = serializers.SerializerMethodField('_member')
284
285
    payment = PaymentTypeField(source='payment.type',
                               choices=Payment.PAYMENT_TYPE)
286
287
288
289
290
291
    registered_on = serializers.DateTimeField(source='date', read_only=True)
    is_cancelled = serializers.SerializerMethodField('_is_cancelled')
    is_late_cancellation = serializers.SerializerMethodField(
        '_is_late_cancellation')
    queue_position = serializers.SerializerMethodField(
        '_queue_position', read_only=False)
292
    fields = serializers.HiddenField(default='')
293
294
295
296
297

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

298
    def _is_cancelled(self, instance):
299
300
301
302
303
        return instance.date_cancelled is not None

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

    def _member(self, instance):
        if instance.member:
307
            return instance.member.pk
308
309
310
311
        return None

    def _name(self, instance):
        if instance.member:
312
            return instance.member.profile.display_name()
313
314
315
        return instance.name

    def _photo(self, instance):
316
        if instance.member and instance.member.profile.photo:
317
            return self.context['request'].build_absolute_uri(
318
                '%s%s' % (settings.MEDIA_URL, instance.member.profile.photo))
319
        else:
320
            return self.context['request'].build_absolute_uri(
Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
321
                static('members/images/default-avatar.jpg'))
322

323
324
325
    def _avatar(self, instance):
        placeholder = self.context['request'].build_absolute_uri(
            static('members/images/default-avatar.jpg'))
Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
326
        file = None
327
        if instance.member and instance.member.profile.photo:
Sébastiaan Versteeg's avatar
Sébastiaan Versteeg committed
328
329
330
331
            file = instance.member.profile.photo
        return create_image_thumbnail_dict(
            self.context['request'], file, placeholder=placeholder,
            size_large='800x800')
332

333
334
335
336
337
    def __init__(self, instance=None, data=empty, **kwargs):
        super().__init__(instance, data, **kwargs)
        try:
            if instance:
                self.information_fields = services.registration_fields(
338
                    kwargs['context']['request'],
339
                    instance.member, instance.event)
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
        except RegistrationError:
            pass

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

        if self.information_fields:
            for key, field in self.information_fields.items():
                key = 'fields[{}]'.format(key)
                field_type = field['type']

                if field_type == RegistrationInformationField.BOOLEAN_FIELD:
                    fields[key] = serializers.BooleanField(
                        required=False,
                        write_only=True
                    )
                elif field_type == RegistrationInformationField.INTEGER_FIELD:
                    fields[key] = serializers.IntegerField(
                        required=field['required'],
                        write_only=True
                    )
                elif field_type == RegistrationInformationField.TEXT_FIELD:
                    fields[key] = serializers.CharField(
                        required=field['required'],
                        write_only=True
                    )

                fields[key].label = field['label']
                fields[key].help_text = field['description']
                fields[key].initial = field['value']
                fields[key].default = field['value']

                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)
        data['fields'] = self.information_fields
        return data

    def field_values(self):
        return ((name[7:len(name) - 1], value)
                for name, value in self.validated_data.items()
                if "info_field" in name)