Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
views.py 7.27 KiB
"""
Defines the views for the API
"""

# from django.shortcuts import render
from rest_framework import viewsets
from rest_framework.views import APIView
from rest_framework.response import Response
from django.shortcuts import get_object_or_404
from django.http import HttpResponse
from .pagination import PageNumberWithPageSizePagination

from .serializers import (
    SmallSportListSerializer,
    SportListSerializer,
    CriterionListSerializer,
    QuestionListSerializer,
    SingleSportSerializer,
    CriteriaSerializer,
    IncompleteSportSerializer,
)
from .models import Sport, Criterion, Question

# Create your views here.


class SportListView(viewsets.ModelViewSet):  # pylint: disable=too-many-ancestors
    """
    A View returning every Sport Object
    """

    serializer_class = SportListSerializer
    queryset = Sport.objects.all()


class CriterionListView(viewsets.ModelViewSet):  # pylint: disable=too-many-ancestors
    """
    A View returning every Criterion Object
    """

    serializer_class = CriterionListSerializer
    queryset = Criterion.objects.all()


class QuestionListView(viewsets.ModelViewSet):  # pylint: disable=too-many-ancestors
    """
    A View returning every Question Object
    """

    serializer_class = QuestionListSerializer
    queryset = Question.objects.all()


# Dev Notes:
# - If we want to include a View in the Router in urls.py, the View needs to be a Viewset
# - Those are mostly meant for Lists of Objects, so instead of get() and post(), list() and create() are used respectively
# https://stackoverflow.com/questions/30389248/how-can-i-register-a-single-view-not-a-viewset-on-my-router


class SmallSportListView(viewsets.ViewSet):
    """
    View for Sports and List of Sport:
    List returns every Sport with the is_filled Field
    Detail returns single Sports with every Criterium
    """

    authentication_classes = []

    # GET for api/admin/sport/
    def list(self, request):
        """
        GET for api/admin/sport/
        Returns a List of Every Sport with the is_filled Field, stating whether every Criterion is given a Rating or not
        """

        paginator = PageNumberWithPageSizePagination()

        sports = Sport.objects.all().order_by("name")
        sports = paginator.paginate_queryset(sports, request)
        is_filled_tuples = []

        for sport in sports:

            is_filled_tuples.append((sport, sport.is_filled()))

        serializer = SmallSportListSerializer(is_filled_tuples)

        return paginator.get_paginated_response(serializer.data)

    # POST for api/admin/sport/
    def create(self, request):
        """
        POST for api/admin/sport/
        View for Creating a New Sport
        """

        request_data = SingleSportSerializer().to_internal_value(request)

        # A Try at Error Catching
        if request_data is None:
            return HttpResponse(status=400)

        new_sport = Sport.objects.create_sport()

        new_sport.name = request_data["name"]
        new_sport.url = request_data["url"]

        new_sport.save()

        response = SingleSportSerializer(new_sport)

        return Response(response.data)

    # GET for api/admin/sport/<id>/
    def retrieve(self, request, pk=None):
        """
        GET for api/admin/sport/<pk>/
        View for getting a Single Sport, with the pk to the Sport being the argument in the URL
        """

        sport = get_object_or_404(Sport, pk=pk)

        response = SingleSportSerializer(sport)

        return Response(response.data)

    # PUT for api/admin/sport/<id>/
    def update(self, request, pk=None):
        """
        PUT for api/admin/sport/<id>/
        Creates a Sport if it doesn't exist, otherwise overwrites it.
        TODO: Maybe Rework PUT if needed of Admin Frontend
        """

        # Get Data from Serializer
        request_data = SingleSportSerializer().to_internal_value(request)

        if request_data is None:
            # Something is Broke, so Refuse Changing Data
            return HttpResponse(status=400)

        # Get Sport from Data
        sport = Sport.objects.get(pk=pk)

        # Apply New Data to Sport
        sport.name = request_data["name"]
        sport.url = request_data["url"]

        # Overwrite Criterion Ratings
        for criterion, value in request_data["criteria"]:
            sport.rate(criterion, value)

        # Re-Serialize changed Sport and Send it back
        response = SingleSportSerializer(sport)

        return Response(response.data)

    # PATCH for api/admin/sport/<id>/
    def partial_update(self, request, pk=None):
        """
        PATCH for api/admin/sport/<id>/
        Fills in the given Values into the Sport specified in the URL
        """

        # Get Data from Serializer
        request_data = SingleSportSerializer().to_internal_value(request)

        if request_data is None:
            # Something is Broke, so Refuse Changing Data
            return HttpResponse(status=400)

        # Get Sport from Data
        sport = Sport.objects.get(pk=pk)

        # Apply New Data to Sport, if it exists
        if "name" in request_data.keys():
            sport.name = request_data["name"]

        if "url" in request_data.keys():
            sport.url = request_data["url"]

        # Overwrite Criterion Ratings
        for criterion, value in request_data["criteria"]:
            sport.rate(criterion, value)

        # Re-Serialize changed Sport and Send it back
        response = SingleSportSerializer(sport)

        return Response(response.data)

    # DELETE for api/admin/sport/<id>/
    def destroy(self, request, pk=None):
        """
        DELETE for api/admin/sport/<id>/
        Removes Sport Object specified in the URL
        """

        sport = get_object_or_404(Sport, pk=pk)

        sport.delete()

        return HttpResponse(status=404)


class IncompleteSportView(APIView):
    """
    Returns all Sport Objects with Incomplete Ratings
    """

    authentication_classes = []

    # GET for api/admin/sport/incomplete/
    def get(self, request):
        """
        GET for api/admin/sport/incomplete/
        Returns every incomplete Sport with its incomplete Ratings in a paginated manner
        """

        paginator = PageNumberWithPageSizePagination()
        queryset = Sport.objects.all().order_by("name")
        queryset = paginator.paginate_queryset(queryset, request)

        incomplete_sport_list = []

        for sport in queryset:

            if not sport.is_filled():
                incomplete_sport_list.append(sport)

        response = IncompleteSportSerializer(incomplete_sport_list)

        return paginator.get_paginated_response(response.data)


class CriteriaView(APIView):
    """
    View for the List of Criteria and their Metadata
    """

    authentication_classes = []

    # GET for api/admin/criteria/
    def get(self, request):
        """
        GET for api/admin/criteria/
        Returns every Criterium and the Metadata of
        Number of Sports in which the Rating is >1
        and the cumulated sum of Ratings >1
        TODO: Also Pagination
        """

        data = []

        for crit in Criterion.objects.iterator():

            active_sports, sum_of_weights = crit.get_active_sum()

            data.append((crit, active_sports, sum_of_weights))

        response = CriteriaSerializer(data)

        return Response(response.data)