diff --git a/apps/calendar/views.py b/apps/calendar/views.py index 8d98799..ff9d580 100644 --- a/apps/calendar/views.py +++ b/apps/calendar/views.py @@ -35,6 +35,7 @@ def get(self, request, year=datetime.now().year, month=datetime.now().month): event_serializer = CalendarEventSerializer(events, many=True) session_serializer = CalendarTrainingSerializer(sessions, many=True) booking_serializer = BookingSerializer(bookings, many=True) + return Response( { "events": event_serializer.data, diff --git a/apps/connections/urls.py b/apps/connections/urls.py index 484cf82..80a44c1 100644 --- a/apps/connections/urls.py +++ b/apps/connections/urls.py @@ -10,5 +10,4 @@ path("top/positions/", views.TopPositionsView.as_view()), path("sessions//", views.ControllerSessionsView.as_view()), path("daily//", views.DailyStatisticsView.as_view()), - path("daily///", views.UserDailyStatisticsView.as_view()), ] diff --git a/apps/connections/views.py b/apps/connections/views.py index a5acacb..8602fe7 100644 --- a/apps/connections/views.py +++ b/apps/connections/views.py @@ -106,15 +106,3 @@ def get(self, request, year): connections = get_daily_statistics(year) serializer = DailyConnectionsSerializer(connections, many=True) return Response(serializer.data) - - -class UserDailyStatisticsView(APIView): - def get(self, request, year, cid): - """ - Get list of controlling hours for every - day of the given year for the given user. - """ - user = get_object_or_404(User, cid=cid) - connections = get_daily_statistics(year, user) - serializer = DailyConnectionsSerializer(connections, many=True) - return Response(serializer.data) diff --git a/apps/events/serializers.py b/apps/events/serializers.py index 4c168ba..139d820 100644 --- a/apps/events/serializers.py +++ b/apps/events/serializers.py @@ -85,15 +85,12 @@ class Meta: class BasicEventSerializer(serializers.ModelSerializer): archived = serializers.BooleanField(read_only=True, source="is_archived") - available_shifts = serializers.SerializerMethodField(read_only=True) + available_shifts = serializers.IntegerField(read_only=True) class Meta: model = Event fields = ["id", "name", "banner", "start", "end", "host", "hidden", "archived", "available_shifts"] - def get_available_shifts(self, obj): - return PositionShift.objects.filter(position__event=obj, user__isnull=True).count() - class EventSerializer(serializers.ModelSerializer): archived = serializers.BooleanField(read_only=True, source="is_archived") diff --git a/apps/events/views.py b/apps/events/views.py index 796ad6a..24b89f9 100644 --- a/apps/events/views.py +++ b/apps/events/views.py @@ -1,6 +1,7 @@ import os from discord_webhook import DiscordEmbed, DiscordWebhook +from django.db.models import Count, Q from django.utils import timezone from rest_framework import status from rest_framework.exceptions import PermissionDenied @@ -36,7 +37,12 @@ def get(self, request): """ Get list of all events. """ - events = Event.objects.filter(end__gt=timezone.now()).order_by("start") + events = ( + Event.objects.filter(end__gt=timezone.now()) + .prefetch_related("positions", "positions__shifts") + .annotate(available_shifts=Count("positions__shifts", filter=Q(positions__shifts__user__isnull=True))) + .order_by("start") + ) if not (request.user.is_authenticated and request.user.is_staff): events = events.exclude(hidden=True) @@ -68,7 +74,12 @@ def get(self, request): """ Get list of all archived events. """ - events = Event.objects.filter(end__lt=timezone.now()).order_by("-start") + events = ( + Event.objects.filter(end__lt=timezone.now()) + .prefetch_related("positions", "positions__shifts") + .annotate(available_shifts=Count("positions__shifts", filter=Q(positions__shifts__user__isnull=True))) + .order_by("-start") + ) if not (request.user.is_authenticated and request.user.is_staff): events = events.exclude(hidden=True) diff --git a/apps/resources/serializers.py b/apps/resources/serializers.py index 4dcff7c..ac74451 100644 --- a/apps/resources/serializers.py +++ b/apps/resources/serializers.py @@ -1,7 +1,7 @@ from django.template.defaultfilters import filesizeformat from rest_framework import serializers -from .models import Category, Resource +from .models import Resource class ResourceSerializer(serializers.ModelSerializer): @@ -17,39 +17,3 @@ def get_extension(self, resource): def get_size(self, resource): return filesizeformat(resource.size) - - -class ResourceGroupedSerializer(serializers.ModelSerializer): - poly = serializers.SerializerMethodField() - proc = serializers.SerializerMethodField() - loa = serializers.SerializerMethodField() - vatis = serializers.SerializerMethodField() - rvm = serializers.SerializerMethodField() - ref = serializers.SerializerMethodField() - - def _build_resource_list(self, category): - resources = Resource.objects.filter(category=category) - serializer = ResourceSerializer(resources, many=True) - return serializer.data - - def get_poly(self, obj): - return self._build_resource_list(Category.POLY) - - def get_proc(self, obj): - return self._build_resource_list(Category.PROC) - - def get_loa(self, obj): - return self._build_resource_list(Category.LOA) - - def get_vatis(self, obj): - return self._build_resource_list(Category.VATIS) - - def get_rvm(self, obj): - return self._build_resource_list(Category.RVM) - - def get_ref(self, obj): - return self._build_resource_list(Category.REF) - - class Meta: - model = Resource - fields = ["poly", "proc", "loa", "vatis", "rvm", "ref"] diff --git a/apps/resources/views.py b/apps/resources/views.py index 364c67c..f6e513e 100644 --- a/apps/resources/views.py +++ b/apps/resources/views.py @@ -1,3 +1,5 @@ +from collections import defaultdict + from rest_framework import status from rest_framework.generics import get_object_or_404 from rest_framework.response import Response @@ -6,7 +8,7 @@ from zhu_core.permissions import IsGet, IsStaff from .models import Resource -from .serializers import ResourceGroupedSerializer, ResourceSerializer +from .serializers import ResourceSerializer class ResourceListView(APIView): @@ -16,9 +18,17 @@ def get(self, request): """ Get list of all resources. """ - resources = Resource.objects.all() - serializer = ResourceGroupedSerializer(resources) - return Response(serializer.data) + sorted_resources = defaultdict(list) + + for resource in Resource.objects.all(): + sorted_resources[resource.category].append(resource) + + return Response( + { + category.lower(): ResourceSerializer(resources, many=True).data + for category, resources in sorted_resources.items() + } + ) def post(self, request): """ diff --git a/apps/training/serializers.py b/apps/training/serializers.py index 074f453..0e49e3d 100644 --- a/apps/training/serializers.py +++ b/apps/training/serializers.py @@ -32,11 +32,17 @@ class TrainingSessionSerializer(BaseTrainingSessionSerializer): class BaseTrainingRequestSerializer(serializers.ModelSerializer): + class Meta: + model = TrainingRequest + fields = ["id", "start", "end", "type", "level", "remarks"] + + +class TrainingRequestSerializer(BaseTrainingRequestSerializer): user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), default=serializers.CurrentUserDefault()) class Meta: model = TrainingRequest - fields = ["id", "user", "start", "end", "type", "level", "remarks"] + fields = BaseTrainingRequestSerializer.Meta.fields + ["user"] def validate(self, data): if data.get("user").training_requests.filter(end__gt=timezone.now()).count() >= 7: diff --git a/apps/training/views.py b/apps/training/views.py index f82f53c..d1422f8 100644 --- a/apps/training/views.py +++ b/apps/training/views.py @@ -1,3 +1,6 @@ +from collections import defaultdict + +from django.db.models import Max, Q from django.shortcuts import get_object_or_404 from django.template.loader import render_to_string from django.utils import timezone @@ -12,9 +15,9 @@ from .models import DayOfWeek, MentorAvailability, Status, TrainingRequest, TrainingSession from .serializers import ( BaseTrainingRequestSerializer, - BaseTrainingSessionSerializer, BasicMentorAvailabilitySerializer, MentorAvailabilitySerializer, + TrainingRequestSerializer, TrainingSessionSerializer, ) @@ -60,7 +63,7 @@ def post(self, request, session_id): """ session = get_object_or_404(TrainingSession, id=session_id) request.data["status"] = Status.COMPLETED - serializer = BaseTrainingSessionSerializer(session, data=request.data, partial=True) + serializer = TrainingRequestSerializer(session, data=request.data, partial=True) if serializer.is_valid(): serializer.save() @@ -80,7 +83,7 @@ def put(self, request, session_id): Modify training session details. """ session = get_object_or_404(TrainingSession, id=session_id) - serializer = BaseTrainingSessionSerializer(session, data=request.data, partial=True) + serializer = TrainingRequestSerializer(session, data=request.data, partial=True) if serializer.is_valid(): serializer.save() return Response(serializer.data) @@ -113,14 +116,14 @@ def get(self, request): Get list of own pending training requests. """ requests = TrainingRequest.objects.filter(user=request.user, end__gt=timezone.now()) - serializer = BaseTrainingRequestSerializer(requests, many=True) + serializer = TrainingRequestSerializer(requests, many=True) return Response(data=serializer.data) def post(self, request): """ Submit a new training request. """ - serializer = BaseTrainingRequestSerializer(data=request.data, context={"request": request}) + serializer = TrainingRequestSerializer(data=request.data, context={"request": request}) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) @@ -128,27 +131,37 @@ def post(self, request): class PendingTrainingRequestListView(APIView): - permission_classes = [IsTrainingStaff] + # permission_classes = [IsTrainingStaff] def get(self, request): """ Get list of all pending training requests. """ - requests = TrainingRequest.objects.filter(end__gt=timezone.now()) - data = [] - for cid in requests.values_list("user", flat=True).distinct(): - last_session = ( - TrainingSession.objects.filter(student=cid, status=Status.COMPLETED).order_by("-start").first() + requests = ( + TrainingRequest.objects.select_related("user") + .annotate( + last_session=Max( + "user__student_sessions__start", + filter=Q(user__student_sessions__status=Status.COMPLETED), + ) ) - user_requests = requests.filter(user=cid) - data.append( + .filter(end__gt=timezone.now()) + ) + + sorted_requests = defaultdict(list) + + for request in requests: + sorted_requests[request.user.cid].append(request) + + return Response( + [ { - "user": BasicUserSerializer(user_requests[0].user).data, - "requests": BaseTrainingRequestSerializer(user_requests, many=True).data, - "last_session": last_session.start.isoformat() if last_session else None, - } - ) - return Response(data=data) + "user": BasicUserSerializer(requests[0].user).data, + "requests": BaseTrainingRequestSerializer(requests, many=True).data, + "last_session": requests[0].last_session, + } for requests in sorted_requests.values() + ] + ) class TrainingRequestInstanceView(APIView): @@ -164,7 +177,7 @@ def put(self, request, request_id): Accept training request. """ training_request = get_object_or_404(TrainingRequest, id=request_id) - serializer = BaseTrainingSessionSerializer( + serializer = TrainingRequestSerializer( data={"student": training_request.user.cid, **request.data}, context={"request": request} ) if serializer.is_valid():