Skip to content
Snippets Groups Projects
Select Git revision
  • e0c3bc1b71f1c1fe50d2656cc60814daf93f99ee
  • develop default protected
  • ical-export
  • feature/audit_log
  • fix/index
  • badge-redeem-404
  • 720-schedule_source
  • room-docu
  • chore/event-views
  • 511-schedule-foo-fixed
  • 607-schedule-versions
  • deploy/curl-verbose
  • fix/public-badge-access-rights
  • 445-schedule-redirects
  • 623-wiki-im-baustellenmodus-sollte-mal-als-wiki-admin-trotzdem-seiten-anlegen-bearbeiten-konnen
  • fix/registration_mail_subject
  • feature/conference-query-set
  • feature/568-habitatmanagement
  • feat/unit-integration-tests
  • camp23-prod
  • production
  • prod-2024-12-27_20-15
  • prod-2024-12-27_16-37
  • prod-2024-12-27_16-01
  • prod-2024-12-27_13-29
  • prod-2024-12-27_00-34
  • prod-2024-12-26_21-45
  • prod-2024-12-26_13-12
  • prod-2024-12-26_00-21
  • prod-2024-12-25_21-04
  • prod-2024-12-25_15-54
  • prod-2024-12-25_01-29
  • prod-2024-12-24_14-48
  • prod-2024-12-23_23-39
  • prod-2024-12-22_21-12
  • prod-2024-12-22_17-25
  • prod-2024-12-22_01-34
  • prod-2024-12-22_00-55
  • prod-2024-12-21_13-42
  • prod-2024-12-21_10-44
  • prod-2024-12-20_12-25
41 results

serializers.py

Blame
  • Forked from hub / hub
    Source project has a limited visibility.
    serializers.py 14.68 KiB
    import contextlib
    
    from rest_framework import serializers
    from rest_framework.relations import HyperlinkedIdentityField
    from rest_framework.reverse import reverse
    
    from django.conf import settings
    from django.core.exceptions import SuspiciousOperation, ValidationError
    
    from core.models.assemblies import Assembly
    from core.models.badges import Badge, BadgeToken, BadgeTokenTimeConstraint
    from core.models.conference import Conference, ConferenceMember, ConferenceTrack
    from core.models.events import Event
    from core.models.links import Link
    from core.models.messages import DirectMessage
    from core.models.metanavi import MetaNavItem
    from core.models.rooms import Room
    from core.models.users import PlatformUser, UserTimelineEntry
    
    
    class ParameterisedHyperlinkedIdentityField(HyperlinkedIdentityField):
        """
        Represents the instance, or a property on the instance, using hyperlinking.
    
        lookup_fields is a tuple of tuples of the form:
            ('model_field', 'url_parameter')
    
        taken from https://github.com/encode/django-rest-framework/issues/1024
        """
    
        lookup_fields = (('pk', 'pk'),)
    
        def __init__(self, *args, **kwargs):
            self.lookup_fields = kwargs.pop('lookup_fields', self.lookup_fields)
            super().__init__(*args, **kwargs)
    
        def get_url(self, obj, view_name, request, format):  # pylint: disable=redefined-builtin
            """
            Given an object, return the URL that hyperlinks to the object.
    
            May raise a `NoReverseMatch` if the `view_name` and `lookup_field`
            attributes are not configured to correctly match the URL conf.
            """
            kwargs = {}
            for model_field, url_param in self.lookup_fields:
                attr = obj
                for field in model_field.split('.'):
                    attr = getattr(attr, field)
                kwargs[url_param] = attr
    
            return reverse(view_name, kwargs=kwargs, request=request, format=format)
    
    
    class HubHyperlinkedIdentityField(HyperlinkedIdentityField):
        """
        Represents the instance, or a property on the instance, using hyperlinking.
    
        lookup_fields is a tuple of tuples of the form:
            ('model_field', 'url_parameter')
    
        taken from https://github.com/encode/django-rest-framework/issues/1024
        """
    
        lookup_fields = (('pk', 'pk'),)
    
        def __init__(self, *args, **kwargs):
            self.lookup_fields = kwargs.pop('lookup_fields', self.lookup_fields)
            super().__init__(*args, **kwargs)
    
        def get_url(self, obj, view_name, request, format):  # pylint: disable=redefined-builtin
            """
            Given an object, return the URL that hyperlinks to the object.
    
            May raise a `NoReverseMatch` if the `view_name` and `lookup_field`
            attributes are not configured to correctly match the URL conf.
            """
            kwargs = {}
            for model_field, url_param in self.lookup_fields:
                attr = obj
                for field in model_field.split('.'):
                    attr = getattr(attr, field)
                kwargs[url_param] = attr
    
            from core.templatetags.hub_absolute import hub_absolute  # pylint: disable=import-outside-toplevel
    
            # TODO: add request=request, format=format,
            return hub_absolute(view_name, **kwargs, i18n=False)
    
    
    class LinkRelatedField(serializers.RelatedField):
        """
        A read only field that represents its targets using their
        plain string representation.
        """
    
        def __init__(self, **kwargs):
            kwargs['read_only'] = True
            super().__init__(**kwargs)
    
        def to_representation(self, value: Link):
            return value.to_dict()
    
    
    class ValidatingModelSerializer(serializers.ModelSerializer):
        def validate(self, data):
            instance = self.Meta.model(**{field: value for field, value in data.items() if field in self.Meta.model._meta.fields})
            for f, v in {field: value for field, value in data.items() if field in self.Meta.model._meta.fields}.items():
                getattr(instance, f).set(v)
            try:
                instance.clean()
            except ValidationError as err:
                raise serializers.ValidationError(err.args[0])
            return data
    
    
    class HubModelSerializer(ValidatingModelSerializer):
        def __init__(self, *args, **kwargs):
            # Don't pass the 'fields' arg up to the superclass
            kwargs.pop('fields', None)
            self.conference = Conference.objects.get(pk=settings.SELECTED_CONFERENCE_ID)
    
            # instantiate superclass(es)
            super().__init__(*args, **kwargs)
    
            # fetch 'staff_only_fields' configuration value
            if hasattr(self.Meta, 'staff_only_fields'):
                staff_only_fields = self.Meta.staff_only_fields
            else:
                staff_only_fields = None
    
            # now check the user's conference membership based on the request
            if 'request' not in self.context:
                from django.contrib.auth.models import AnonymousUser
    
                self.request_user = request_user = AnonymousUser()
                self.conference_member = None
            else:
                self.request_user = request_user = self.context['request'].user
                self.conference_member = None
                if request_user.is_authenticated:
                    with contextlib.suppress(ConferenceMember.DoesNotExist):
                        self.conference_member = ConferenceMember.objects.select_related('conference').get(
                            conference=self.conference,
                            user=request_user,
                        )
    
            # store if the request's user has staff permissions in the conference (either direct or globally)
            self.is_staff = request_user.is_superuser or request_user.is_staff or (self.conference_member is not None and self.conference_member.is_staff)
    
            # filter out staff-only fields if the user isn't staff
            if staff_only_fields is not None and not self.is_staff:
                # remove the extended fields
                for field_name in staff_only_fields:
                    self.fields.pop(field_name)  # a KeyError here indicates a staff-only field not mentioned in the regular fields!
    
    
    class ConferenceSerializer(HubModelSerializer):
        tracks = serializers.SlugRelatedField(many=True, read_only=True, slug_field='slug')
    
        class Meta:
            model = Conference
            fields = [
                'slug',
                'name',
                'is_public',
                'start',
                'end',
                'publication_date',
                'registration_start',
                'registration_deadline',
                'tracks',
            ]
            staff_only_fields = [
                'is_public',
                'publication_date',
                'registration_start',
            ]
    
    
    class ConferenceTrackSerializer(HubModelSerializer):
        class Meta:
            model = ConferenceTrack
            read_only_fields = ['id']
            fields = ['conference', 'slug', 'name', 'color', 'is_public', 'id']
            staff_only_fields = ['is_public']
    
    
    class AssemblySerializer(HubModelSerializer):
        parent = serializers.SlugRelatedField(slug_field='slug', queryset=Assembly.objects.none)
    
        events_url = ParameterisedHyperlinkedIdentityField(view_name='api:assembly-events', lookup_fields=(('slug', 'assembly'),))
        rooms_url = ParameterisedHyperlinkedIdentityField(view_name='api:assembly-rooms', lookup_fields=(('slug', 'assembly'),))
        badges_url = ParameterisedHyperlinkedIdentityField(view_name='api:assembly-badges-list-create', lookup_fields=(('slug', 'assembly'),))
    
        class Meta:
            model = Assembly
            fields = [
                'slug',
                'id',
                'name',
                'state',
                'hierarchy',
                'parent',
                'assembly_location',
                'assembly_link',
                'is_official',
                'events_url',
                'rooms_url',
                'badges_url',
            ]
            staff_only_fields = ['state', 'hierarchy']
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
    
            cluster_qs = Assembly.objects.associated_with_user(conference=self.conference, user=self.request_user).exclude(hierarchy=Assembly.Hierarchy.REGULAR)
            self.fields['parent'].queryset = cluster_qs
    
    
    class BadgeSerializer(HubModelSerializer):
        class Meta:
            model = Badge
            exclude = ['issuing_assembly', 'conference', 'issuing_token']
    
        badge_token_url = ParameterisedHyperlinkedIdentityField(
            view_name='api:badge-token-list-create',
            lookup_fields=(('issuing_assembly.slug', 'assembly'), ('pk', 'pk')),
        )
    
        def create(self, validated_data):
            issuing_assembly = Assembly.objects.filter(slug__iexact=self.context['assembly'])
            conference = Conference.objects.filter(slug=self.context['conference'])
            validated_data.update({'conference': conference, 'issuing_assembly': issuing_assembly})
            return super().create(validated_data)
    
    
    class BadgeTokenTimeConstraintSerializer(serializers.ModelSerializer):
        class Meta:
            model = BadgeTokenTimeConstraint
            fields = ['date_time_range']
    
    
    class BadgeTokenSerializer(HubModelSerializer):
        time_constraints = BadgeTokenTimeConstraintSerializer(many=True, allow_null=True, required=False)
        redeemable_count = serializers.IntegerField(initial=0, required=False)
        valid = serializers.ReadOnlyField()
        redeemed_count = serializers.ReadOnlyField()
    
        class Meta:
            model = BadgeToken
            exclude = ['badge']
            read_only_fields = ['redeemed_count']
    
        def create(self, validated_data):
            time_constraints = validated_data.pop('time_constraints', [])
            badge = Badge.objects.filter(pk=self.context['badge']).first()
            badge_token = BadgeToken.objects.create(**validated_data, badge=badge)
            for time_constraint in time_constraints:
                BadgeTokenTimeConstraint.objects.create(badge_token=badge_token, **time_constraint)
            return badge_token
    
    
    class BadgeTokenUpdateSerializer(BadgeTokenSerializer):
        class Meta:
            read_only_fields = ['redeemed_count', 'badge']
    
    
    class RoomSerializer(HubModelSerializer):
        assembly = serializers.SlugRelatedField(read_only=True, slug_field='slug')
    
        links = LinkRelatedField(
            many=True,
            read_only=True,
        )
    
        public_url = HubHyperlinkedIdentityField(view_name='plainui:room', lookup_fields=(('slug', 'slug'),))
    
        class Meta:
            model = Room
            read_only_fields = ['id']
            fields = [
                'id',
                'name',
                'slug',
                'blocked',
                'room_type',
                'capacity',
                'links',
                'assembly',
                'links',
                'backend_link',
                'public_url',
            ]
            staff_only_fields = ['blocked', 'backend_link']
    
    
    class EventSerializer(HubModelSerializer):
        url = ParameterisedHyperlinkedIdentityField(view_name='api:event-detail', lookup_fields=(('pk', 'pk'),))
        track = serializers.SlugRelatedField(slug_field='slug', read_only=True)
        assembly = serializers.SlugRelatedField(slug_field='slug', read_only=True)
        room = serializers.SlugRelatedField(slug_field='id', read_only=True)
    
        class Meta:
            model = Event
            extra_kwargs = {'id': {'read_only': False}}
            fields = [
                'id',
                'kind',
                'name',
                'slug',
                'url',
                'track',
                'assembly',
                'room',
                'location',
                'language',
                'description',
                'is_public',
                'schedule_start',
                'schedule_duration',
                'schedule_end',
            ]
            staff_only_fields = ['is_public']
    
        def update(self, instance, validated_data):
            # is somebody trying to change the event ID?
            if (given_id := validated_data.get('id')) is not None and given_id != instance.id:
                raise SuspiciousOperation("You cannot update an event's id.")
    
            # prevent some fields from being updated, no matter what
            for protected_field in ['id', 'conference']:
                validated_data.pop(protected_field, None)
    
            # do the normal stuff
            return super().update(instance, validated_data)
    
    
    class UserTimelineEntrySerializer(ValidatingModelSerializer):
        class Meta:
            model = UserTimelineEntry
            read_only_fields = ['id']
            fields = [
                'id',
                'timestamp',
                'target',
                'is_bookmark',
                'comment',
            ]
    
    
    class MetaNavItemSerializer(HubModelSerializer):
        conference = serializers.SlugRelatedField(read_only=True, slug_field='slug')
    
        class Meta:
            model = MetaNavItem
            read_only_fields = ['id']
            fields = [
                'id',
                'conference',
                'slug',
                'title_de',
                'title_en',
                'enabled',
                'url',
                'graphic_light',
                'graphic_dark',
            ]
    
    
    class PlatformUserVisibleSerializer(serializers.ModelSerializer):
        class Meta:
            model = PlatformUser
            fields = ['username', 'display_name']
            read_only_fields = fields
    
    
    class PlatformUserByUsernameFieldSerializer(serializers.SlugRelatedField):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, slug_field='username', **kwargs)
    
        def get_queryset(self):
            return PlatformUser.objects.all()
    
    
    class DirectMessageSerializerSentShort(HubModelSerializer):
        class Meta:
            model = DirectMessage
            fields = ['id', 'sender', 'recipient', 'timestamp', 'in_reply_to', 'subject']
            read_only_fields = fields
    
        sender = PlatformUserVisibleSerializer(read_only=True)
        recipient = PlatformUserVisibleSerializer(read_only=True)
    
    
    class DirectMessageSerializerReceivedShort(HubModelSerializer):
        class Meta:
            model = DirectMessage
            fields = [*DirectMessageSerializerSentShort.Meta.fields, 'has_responded']
            read_only_fields = fields
    
        sender = PlatformUserVisibleSerializer(read_only=True)
        recipient = PlatformUserVisibleSerializer(read_only=True)
    
    
    class DirectMessageSerializerSent(HubModelSerializer):
        class Meta:
            model = DirectMessage
            fields = [*DirectMessageSerializerSentShort.Meta.fields, 'body']
            read_only_fields = fields
    
        sender = PlatformUserVisibleSerializer(read_only=True)
        recipient = PlatformUserVisibleSerializer(read_only=True)
    
    
    class DirectMessageSerializerReceived(HubModelSerializer):
        class Meta:
            model = DirectMessage
            fields = [*DirectMessageSerializerReceivedShort.Meta.fields, 'body']
            read_only_fields = [f for f in fields if f != 'was_read']
    
        sender = PlatformUserVisibleSerializer(read_only=True)
        recipient = PlatformUserVisibleSerializer(read_only=True)
    
    
    class DirectMessageSendSerializer(HubModelSerializer):
        class Meta:
            model = DirectMessage
            fields = ['id', 'recipient', 'subject', 'body']
            read_only_fields = ['id']
            write_only_fields = [f for f in fields if f != 'id']
    
        recipient = PlatformUserByUsernameFieldSerializer()