From 028e08fdb00100b5cdfe8061cb1b3f61ee6fafbd Mon Sep 17 00:00:00 2001 From: Julian Baumann Date: Sat, 16 Mar 2024 18:15:06 +0100 Subject: [PATCH] add api route to get participations for user (#1222) Co-authored-by: Felix Rindt --- ephios/api/fields.py | 6 +++++ ephios/api/urls.py | 15 +++++++++++-- ephios/api/views/events.py | 38 ++++++++++++++++++++++++++++++- ephios/api/views/users.py | 46 +++++++++++++++++++++++++++++++++++++- 4 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 ephios/api/fields.py diff --git a/ephios/api/fields.py b/ephios/api/fields.py new file mode 100644 index 000000000..9df464968 --- /dev/null +++ b/ephios/api/fields.py @@ -0,0 +1,6 @@ +from rest_framework.fields import ChoiceField + + +class ChoiceDisplayField(ChoiceField): + def to_representation(self, value): + return {"value": value, "label": self.choices[value]} diff --git a/ephios/api/urls.py b/ephios/api/urls.py index 47cf07590..ab589ddae 100644 --- a/ephios/api/urls.py +++ b/ephios/api/urls.py @@ -14,12 +14,23 @@ ApplicationDetail, ApplicationUpdate, ) -from ephios.api.views.events import EventViewSet -from ephios.api.views.users import UserProfileMeView +from ephios.api.views.events import EventViewSet, ShiftViewSet +from ephios.api.views.users import ( + UserByMailView, + UserParticipationView, + UserProfileMeView, + UserViewSet, +) from ephios.extra.permissions import staff_required router = routers.DefaultRouter() router.register(r"events", EventViewSet) +router.register(r"shifts", ShiftViewSet) +router.register(r"users", UserViewSet) +router.register(r"users/by_email", UserByMailView, basename="user-by-email") +router.register( + r"users/(?P[\d]+)/participations", UserParticipationView, basename="user-participations" +) app_name = "api" urlpatterns = [ diff --git a/ephios/api/views/events.py b/ephios/api/views/events.py index afa4fc48d..c63e9868d 100644 --- a/ephios/api/views/events.py +++ b/ephios/api/views/events.py @@ -5,7 +5,8 @@ from rest_framework.permissions import DjangoObjectPermissions from rest_framework_guardian import filters as guardian_filters -from ephios.core.models import Event, EventType, Shift +from ephios.api.fields import ChoiceDisplayField +from ephios.core.models import AbstractParticipation, Event, EventType, LocalParticipation, Shift from ephios.core.templatetags.settings_extras import make_absolute @@ -69,6 +70,14 @@ class Meta: ] +class ShiftViewSet(viewsets.ReadOnlyModelViewSet): + serializer_class = ShiftSerializer + permission_classes = [DjangoObjectPermissions, IsAuthenticatedOrTokenHasScope] + filter_backends = [guardian_filters.ObjectPermissionsFilter] + required_scopes = ["PUBLIC_READ"] + queryset = Shift.objects.all() + + class EventViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = EventSerializer filterset_fields = ["type"] @@ -96,3 +105,30 @@ class EventViewSet(viewsets.ReadOnlyModelViewSet): .prefetch_related(Prefetch("shifts__participations")) .order_by("start_time") ) + + +class ParticipationSerializer(serializers.ModelSerializer): + event_title = serializers.CharField(source="shift.event.title") + state = ChoiceDisplayField(choices=AbstractParticipation.States.choices) + duration = serializers.SerializerMethodField() + + class Meta: + model = LocalParticipation + fields = [ + "id", + "shift", + "event_title", + "state", + "comment", + "start_time", + "end_time", + "duration", + ] + + def build_unknown_field(self, field_name, model_class): + if field_name in {"start_time", "end_time"}: + return self.build_property_field(field_name, model_class) + return super().build_unknown_field(field_name, model_class) + + def get_duration(self, obj): + return (obj.end_time - obj.start_time).total_seconds() diff --git a/ephios/api/views/users.py b/ephios/api/views/users.py index 3cd6245a9..f64f34a86 100644 --- a/ephios/api/views/users.py +++ b/ephios/api/views/users.py @@ -1,13 +1,21 @@ from django.db.models import Q from django.utils import timezone +from django_filters.rest_framework import DjangoFilterBackend from oauth2_provider.contrib.rest_framework import IsAuthenticatedOrTokenHasScope +from rest_framework import viewsets from rest_framework.exceptions import PermissionDenied from rest_framework.fields import SerializerMethodField +from rest_framework.filters import SearchFilter from rest_framework.generics import RetrieveAPIView +from rest_framework.mixins import RetrieveModelMixin +from rest_framework.permissions import DjangoObjectPermissions from rest_framework.relations import SlugRelatedField from rest_framework.serializers import ModelSerializer +from rest_framework.viewsets import GenericViewSet +from rest_framework_guardian.filters import ObjectPermissionsFilter -from ephios.core.models import Qualification, UserProfile +from ephios.api.views.events import ParticipationSerializer +from ephios.core.models import LocalParticipation, Qualification, UserProfile from ephios.core.services.qualification import collect_all_included_qualifications @@ -35,6 +43,7 @@ class UserProfileSerializer(ModelSerializer): class Meta: model = UserProfile fields = [ + "id", "display_name", "date_of_birth", "email", @@ -61,3 +70,38 @@ def get_object(self): if self.request.user is None: raise PermissionDenied() return self.request.user + + +class UserViewSet(viewsets.ReadOnlyModelViewSet): + serializer_class = UserProfileSerializer + queryset = UserProfile.objects.all() + permission_classes = [IsAuthenticatedOrTokenHasScope, DjangoObjectPermissions] + required_scopes = ["CONFIDENTIAL_READ"] + search_fields = ["display_name", "email"] + + filter_backends = [ + DjangoFilterBackend, + SearchFilter, + ObjectPermissionsFilter, + ] + + +class UserByMailView(RetrieveModelMixin, GenericViewSet): + serializer_class = UserProfileSerializer + queryset = UserProfile.objects.all() + permission_classes = [IsAuthenticatedOrTokenHasScope, DjangoObjectPermissions] + required_scopes = ["CONFIDENTIAL_READ"] + filter_backends = [ObjectPermissionsFilter] + lookup_url_kwarg = "email" + lookup_field = "email" + + +class UserParticipationView(viewsets.ReadOnlyModelViewSet): + serializer_class = ParticipationSerializer + permission_classes = [IsAuthenticatedOrTokenHasScope] + filter_backends = [ObjectPermissionsFilter, DjangoFilterBackend] + filterset_fields = ["state"] + required_scopes = ["CONFIDENTIAL_READ"] + + def get_queryset(self): + return LocalParticipation.objects.filter(user=self.kwargs["user"])