from django.conf import settings
from django.templatetags.static import static
from django.urls import reverse
from django.utils import timezone
from django.utils.html import strip_tags, strip_spaces_between_tags
from html import unescape
from rest_framework import serializers
from rest_framework.fields import empty

from payments.api.fields import PaymentTypeField
from payments.models import Payment
from thaliawebsite.api.services import create_image_thumbnail_dict
from events import services
from events.exceptions import RegistrationError
from events.models import Event, Registration, RegistrationInformationField
from pizzas.models import PizzaEvent
from thaliawebsite.templatetags.bleach_tags import bleach
from utils.snippets import create_google_maps_url


class CalenderJSSerializer(serializers.ModelSerializer):
    """
    Serializer using the right format for CalendarJS
    """

    class Meta:
        fields = (
            "start",
            "end",
            "allDay",
            "isBirthday",
            "url",
            "title",
            "description",
            "classNames",
            "blank",
        )

    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")
    classNames = serializers.SerializerMethodField("_class_names")
    blank = serializers.SerializerMethodField("_target_blank")

    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):
        return unescape(strip_tags(instance.description))

    def _class_names(self, instance):
        pass

    def _target_blank(self, instance):
        return False


class EventCalenderJSSerializer(CalenderJSSerializer):
    class Meta(CalenderJSSerializer.Meta):
        model = Event

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

    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


class UnpublishedEventSerializer(CalenderJSSerializer):
    """
    See CalenderJSSerializer, customised classes
    """

    class Meta(CalenderJSSerializer.Meta):
        model = Event

    def _class_names(self, instance):
        return ["unpublished-event"]

    def _url(self, instance):
        return reverse("admin:events_event_details", kwargs={"pk": instance.id})


class EventRetrieveSerializer(serializers.ModelSerializer):
    """
    Serializer for events
    """

    class Meta:
        model = Event
        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")

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

    def _num_participants(self, instance):
        if (
            instance.max_participants
            and instance.participants.count() > instance.max_participants
        ):
            return instance.max_participants
        return instance.participants.count()

    def _user_registration(self, instance):
        try:
            if self.context["request"].member:
                reg = instance.registration_set.get(
                    member=self.context["request"].member
                )
                return RegistrationAdminListSerializer(reg, context=self.context).data
        except Registration.DoesNotExist:
            pass
        return None

    def _registration_allowed(self, instance):
        member = self.context["request"].member
        return (
            self.context["request"].user.is_authenticated
            and member.has_active_membership
            and member.can_attend_events
        )

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

    def _is_pizza_event(self, instance):
        return instance.is_pizza_event()

    def _google_maps_url(self, instance):
        return create_google_maps_url(instance.map_location, zoom=13, size="450x250")

    def _is_admin(self, instance):
        member = self.context["request"].member
        return services.is_organiser(member, instance)


class EventListSerializer(serializers.ModelSerializer):
    """Custom list serializer for events"""

    class Meta:
        model = Event
        fields = (
            "pk",
            "title",
            "description",
            "start",
            "end",
            "location",
            "price",
            "registered",
            "pizza",
            "registration_allowed",
        )

    description = serializers.SerializerMethodField("_description")
    registered = serializers.SerializerMethodField("_registered")
    pizza = serializers.SerializerMethodField("_pizza")

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

    def _registered(self, instance):
        try:
            registered = services.is_user_registered(
                self.context["request"].user, instance,
            )
            if registered is None:
                return False
            return registered
        except AttributeError:
            return False

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


class RegistrationListSerializer(serializers.ModelSerializer):
    """Custom registration list serializer"""

    class Meta:
        model = Registration
        fields = ("pk", "member", "name", "avatar")

    name = serializers.SerializerMethodField("_name")
    avatar = serializers.SerializerMethodField("_avatar")
    member = serializers.SerializerMethodField("_member")

    def _member(self, instance):
        if instance.member:
            return instance.member.pk
        return None

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

    def _avatar(self, instance):
        placeholder = self.context["request"].build_absolute_uri(
            static("members/images/default-avatar.jpg")
        )
        file = None
        if instance.member and instance.member.profile.photo:
            file = instance.member.profile.photo
        return create_image_thumbnail_dict(
            self.context["request"], file, placeholder=placeholder, size_large="800x800"
        )


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")
    payment = PaymentTypeField(source="payment.type", choices=Payment.PAYMENT_TYPE)

    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


class RegistrationSerializer(serializers.ModelSerializer):
    """Registration serializer"""

    information_fields = None

    class Meta:
        model = Registration
        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")
    queue_position = serializers.SerializerMethodField(
        "_queue_position", read_only=False
    )
    fields = serializers.HiddenField(default="")

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

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

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

    def _member(self, instance):
        if instance.member:
            return instance.member.pk
        return None

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

    def _photo(self, instance):
        if instance.member and instance.member.profile.photo:
            return self.context["request"].build_absolute_uri(
                "%s%s" % (settings.MEDIA_URL, instance.member.profile.photo)
            )
        else:
            return self.context["request"].build_absolute_uri(
                static("members/images/default-avatar.jpg")
            )

    def _avatar(self, instance):
        placeholder = self.context["request"].build_absolute_uri(
            static("members/images/default-avatar.jpg")
        )
        file = None
        if instance.member and instance.member.profile.photo:
            file = instance.member.profile.photo
        return create_image_thumbnail_dict(
            self.context["request"], file, placeholder=placeholder, size_large="800x800"
        )

    def __init__(self, instance=None, data=empty, **kwargs):
        super().__init__(instance, data, **kwargs)
        try:
            if instance:
                self.information_fields = services.registration_fields(
                    kwargs["context"]["request"], registration=instance
                )
        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,
                        allow_null=not field["required"],
                    )
                elif field_type == RegistrationInformationField.TEXT_FIELD:
                    fields[key] = serializers.CharField(
                        required=field["required"],
                        write_only=True,
                        allow_blank=not field["required"],
                        allow_null=not field["required"],
                    )

                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
        )

website/events/api/serializers.py
from django.conf import settings
from django.templatetags.static import static
from django.urls import reverse
from django.utils import timezone
from django.utils.html import strip_tags, strip_spaces_between_tags
from html import unescape
from rest_framework import serializers
from rest_framework.fields import empty

from payments.api.fields import PaymentTypeField
from payments.models import Payment
from thaliawebsite.api.services import create_image_thumbnail_dict
from events import services
from events.exceptions import RegistrationError
from events.models import Event, Registration, RegistrationInformationField
from pizzas.models import PizzaEvent
from thaliawebsite.templatetags.bleach_tags import bleach
from utils.snippets import create_google_maps_url


class CalenderJSSerializer(serializers.ModelSerializer):
    """
    Serializer using the right format for CalendarJS
    """

    class Meta:
        fields = (
            "start",
            "end",
            "allDay",
            "isBirthday",
            "url",
            "title",
            "description",
            "classNames",
            "blank",
        )

    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")
    classNames = serializers.SerializerMethodField("_class_names")
    blank = serializers.SerializerMethodField("_target_blank")

    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):
        return unescape(strip_tags(instance.description))

    def _class_names(self, instance):
        pass

    def _target_blank(self, instance):
        return False


class EventCalenderJSSerializer(CalenderJSSerializer):
    class Meta(CalenderJSSerializer.Meta):
        model = Event

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

    def _class_names(self, instance):
        class_names = ["regular-event"]
        if self.context["member"] and services.is_user_registered(
            self.context["member"], instance
        ):
            class_names.append("has-registration")
        return class_names


class UnpublishedEventSerializer(CalenderJSSerializer):
    """
    See CalenderJSSerializer, customised classes
    """

    class Meta(CalenderJSSerializer.Meta):
        model = Event

    def _class_names(self, instance):
        return ["unpublished-event"]

    def _url(self, instance):
        return reverse("admin:events_event_details", kwargs={"pk": instance.id})


class EventRetrieveSerializer(serializers.ModelSerializer):
    """
    Serializer for events
    """

    class Meta:
        model = Event
        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")

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

    def _num_participants(self, instance):
        if (
            instance.max_participants
            and instance.participants.count() > instance.max_participants
        ):
            return instance.max_participants
        return instance.participants.count()

    def _user_registration(self, instance):
        try:
            if self.context["request"].member:
                reg = instance.registration_set.get(
                    member=self.context["request"].member
                )
                return RegistrationAdminListSerializer(reg, context=self.context).data
        except Registration.DoesNotExist:
            pass
        return None

    def _registration_allowed(self, instance):
        member = self.context["request"].member
        return (
            self.context["request"].user.is_authenticated
            and member.has_active_membership
            and member.can_attend_events
        )

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

    def _is_pizza_event(self, instance):
        return instance.is_pizza_event()

    def _google_maps_url(self, instance):
        return create_google_maps_url(instance.map_location, zoom=13, size="450x250")

    def _is_admin(self, instance):
        member = self.context["request"].member
        return services.is_organiser(member, instance)


class EventListSerializer(serializers.ModelSerializer):
    """Custom list serializer for events"""

    class Meta:
        model = Event
        fields = (
            "pk",
            "title",
            "description",
            "start",
            "end",
            "location",
            "price",
            "registered",
            "pizza",
            "registration_allowed",
        )

    description = serializers.SerializerMethodField("_description")
    registered = serializers.SerializerMethodField("_registered")
    pizza = serializers.SerializerMethodField("_pizza")

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

    def _registered(self, instance):
        try:
            registered = services.is_user_registered(
                self.context["request"].user, instance,
            )
            if registered is None:
                return False
            return registered
        except AttributeError:
            return False

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


class RegistrationListSerializer(serializers.ModelSerializer):
    """Custom registration list serializer"""

    class Meta:
        model = Registration
        fields = ("pk", "member", "name", "avatar")

    name = serializers.SerializerMethodField("_name")
    avatar = serializers.SerializerMethodField("_avatar")
    member = serializers.SerializerMethodField("_member")

    def _member(self, instance):
        if instance.member:
            return instance.member.pk
        return None

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

    def _avatar(self, instance):
        placeholder = self.context["request"].build_absolute_uri(
            static("members/images/default-avatar.jpg")
        )
        file = None
        if instance.member and instance.member.profile.photo:
            file = instance.member.profile.photo
        return create_image_thumbnail_dict(
            self.context["request"], file, placeholder=placeholder, size_large="800x800"
        )


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")
    payment = PaymentTypeField(source="payment.type", choices=Payment.PAYMENT_TYPE)

    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


class RegistrationSerializer(serializers.ModelSerializer):
    """Registration serializer"""

    information_fields = None

    class Meta:
        model = Registration
        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")
    queue_position = serializers.SerializerMethodField(
        "_queue_position", read_only=False
    )
    fields = serializers.HiddenField(default="")

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

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

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

    def _member(self, instance):
        if instance.member:
            return instance.member.pk
        return None

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

    def _photo(self, instance):
        if instance.member and instance.member.profile.photo:
            return self.context["request"].build_absolute_uri(
                "%s%s" % (settings.MEDIA_URL, instance.member.profile.photo)
            )
        else:
            return self.context["request"].build_absolute_uri(
                static("members/images/default-avatar.jpg")
            )

    def _avatar(self, instance):
        placeholder = self.context["request"].build_absolute_uri(
            static("members/images/default-avatar.jpg")
        )
        file = None
        if instance.member and instance.member.profile.photo:
            file = instance.member.profile.photo
        return create_image_thumbnail_dict(
            self.context["request"], file, placeholder=placeholder, size_large="800x800"
        )

    def __init__(self, instance=None, data=empty, **kwargs):
        super().__init__(instance, data, **kwargs)
        try:
            if instance:
                self.information_fields = services.registration_fields(
                    kwargs["context"]["request"], registration=instance
                )
        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,
                        allow_null=not field["required"],
                    )
                elif field_type == RegistrationInformationField.TEXT_FIELD:
                    fields[key] = serializers.CharField(
                        required=field["required"],
                        write_only=True,
                        allow_blank=not field["required"],
                        allow_null=not field["required"],
                    )

                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
        )

website/events/api/serializers.py
diff --git a/website/events/api/serializers.py b/website/events/api/serializers.py
index e6e363598..9221a2bc1 100644
--- a/website/events/api/serializers.py
+++ b/website/events/api/serializers.py
@@ -83,7 +83,9 @@ def _url(self, instance):
 
     def _class_names(self, instance):
         class_names = ["regular-event"]
-        if services.is_user_registered(self.context["member"], instance):
+        if self.context["member"] and services.is_user_registered(
+            self.context["member"], instance
+        ):
             class_names.append("has-registration")
         return class_names
 

AttributeError: 'NoneType' object has no attribute 'is_authenticated'
In GitLab by _thaliatechnicie on Mar 11, 2020, 23:02

Sentry Issue: [CONCREXIT-2A](https://sentry.io/organizations/thalia/issues/1560438241/?referrer=gitlab_integration)

```
AttributeError: 'NoneType' object has no attribute 'is_authenticated'
(14 additional frame(s) were not displayed)
...
  File "rest_framework/serializers.py", line 529, in to_representation
    ret[field.field_name] = field.to_representation(attribute)
  File "rest_framework/fields.py", line 1905, in to_representation
    return method(value)
  File "events/api/serializers.py", line 86, in _class_names
    if services.is_user_registered(self.context["member"], instance):
  File "events/services.py", line 24, in is_user_registered
    if not event.registration_required or not member.is_authenticated:
  File "django/utils/functional.py", line 225, in inner
    return func(self._wrapped, *args)
```
