diff --git a/.pylintrc b/.pylintrc index a38492d526b184470b00af41da106143c0122e1e..0deca7bc1ce42b9f694aaa973329a5d344013766 100644 --- a/.pylintrc +++ b/.pylintrc @@ -3,4 +3,9 @@ fail-under=10 [MESSAGES CONTROL] disable=line-too-long, - django-not-configured + django-not-configured, + too-few-public-methods, + no-self-use, + abstract-method, + arguments-differ, + invalid-name diff --git a/README.md b/README.md index fa9b5435047925eda043a98975c7f10b4283980b..075146a4cb0ac01f5e58a25ff1f64718db803781 100644 --- a/README.md +++ b/README.md @@ -113,4 +113,25 @@ activate(cur_language) # Resets active language to the one before manual activat ``` -This might be particularly useful for entering data from the Admin Frontend into the database, or choosing the language for the User Frontend if the Browser Locale cannot be used. \ No newline at end of file +This might be particularly useful for entering data from the Admin Frontend into the database, or choosing the language for the User Frontend if the Browser Locale cannot be used. + + +## Pagination in Views + +Every list that needs to be paginated needs a paginator. +Here, the Paginator object is created in the GET call. +The Queryset which is supposed to be paginated needs to be run through the function: + +```python +new_queryset = paginator.paginate_queryset(complete_queryset, request) +``` + +The new_queryset is a List, not a Manager, so it can be directly iterated upon. +After the data has been worked on and run through the Serializer as normal, +instead of returning Result(data), the paginator needs to be used again so it can add its page metadata: + +```python +return paginator.get_paginated_response(serializer.data) +``` + +This function already returns a fully valid Response, so it can be directly returned. \ No newline at end of file diff --git a/unisportomat/quiz/models.py b/unisportomat/quiz/models.py index fd3c4463e32186dd3c0ff04a86cd7407f992e4be..5a4db07a69178961c70d85c826cd2875063b4511 100644 --- a/unisportomat/quiz/models.py +++ b/unisportomat/quiz/models.py @@ -1,6 +1,5 @@ """ Model definitions for the quiz """ -from django.core.validators import MaxValueValidator, MinValueValidator from django.core.exceptions import ValidationError from django.db import models @@ -10,7 +9,7 @@ def validate_rating(value): TODO: Validations are only run when using Model Forms. """ - if not (value <= 10 and value >= 1 or value == -1): + if not ((10 >= value >= 1) or value == -1): raise ValidationError(u"%s is not a valid rating!" % value) @@ -40,6 +39,9 @@ class SportManager(models.Manager): """ def create_sport(self, **kwargs): + """ + Creates new Sport Object and every CriterionRating for it + """ sport = self.create(**kwargs) for crit in Criterion.objects.iterator(): @@ -101,6 +103,9 @@ class CriterionManager(models.Manager): """ def create_criterion(self, **kwargs): + """ + Creates a Criterion Object and Rates every existing Sport with -1 + """ crit = Criterion(**kwargs) # Criterion needs to be saved before it can be connected to a sport @@ -126,16 +131,19 @@ class Criterion(models.Model): return self.name def get_active_sum(self): + """ + Get Number of Sports with Rating larger than 1 and the cumulated sum of all ratings + """ num_active = 0 - sum = 0 + rating_sum = 0 for rating_obj in CriterionRating.objects.filter(criterion=self): - if rating_obj.rating != -1: + rating_sum += rating_obj.rating + if rating_obj.rating > 1: num_active += 1 - sum += rating_obj.rating - return num_active, sum + return num_active, rating_sum class CallToMove(models.Model): diff --git a/unisportomat/quiz/serializers.py b/unisportomat/quiz/serializers.py index cbfc2b44bb40518b4870416533e3597658e342e7..61aa9781f59e42080692ac196a1ef51f6e87670a 100644 --- a/unisportomat/quiz/serializers.py +++ b/unisportomat/quiz/serializers.py @@ -2,7 +2,7 @@ Serializers creating JSONs for every Model from .models """ from rest_framework import serializers -from .models import Sport, Criterion, Question, CriterionRating, validate_rating +from .models import Sport, Criterion, Question, validate_rating class SportListSerializer(serializers.ModelSerializer): @@ -36,11 +36,19 @@ class CriterionListSerializer(serializers.ModelSerializer): class SmallSportListSerializer(serializers.BaseSerializer): - def to_representation(self, sport_instances): + """ + Serializes Lists of Sport Objects in a "Simple" Manner, with Criterions being represented in a Bool. + """ + + def to_representation(self, sport_filled_tuples): + """ + Takes a List of (Sport, bool) tuples to Serialize. + The Bool Represents whether the Sport is Filled or not. + """ serialized_data = [] - for sport, boolean in sport_instances: + for sport, boolean in sport_filled_tuples: serialized_data.append( { "pk": sport.pk, @@ -54,7 +62,14 @@ class SmallSportListSerializer(serializers.BaseSerializer): class SingleSportSerializer(serializers.BaseSerializer): + """ + Serializes and Deserializes a Single Sport Object + """ + def to_representation(self, sport): + """ + Takes a Single Sport Object and Serializes it and all its Criteria + """ serialized_data = {} @@ -107,7 +122,7 @@ class SingleSportSerializer(serializers.BaseSerializer): try: validate_rating(value) crit = Criterion.objects.get(pk=criterion["id"]) - except: + except: # pylint: disable=bare-except return None sport_dictionary["criteria"].append((crit, value)) @@ -116,7 +131,15 @@ class SingleSportSerializer(serializers.BaseSerializer): class IncompleteSportSerializer(serializers.BaseSerializer): + """ + Serializes every Sport Object with Incomplete Criteria Ratings. + Includes the Name and ID of both the Sport and the Criteria. + """ + def to_representation(self, incomplete_sports): + """ + Serializes Every given Sport Object and goes through every Criterium to serialize those that are unrated. + """ incomplete_sport_list = [] @@ -145,7 +168,16 @@ class IncompleteSportSerializer(serializers.BaseSerializer): class CriteriaSerializer(serializers.BaseSerializer): + """ + Serializes Every Criterium and Metadata + """ + def to_representation(self, data): + """ + Takes Tuples of (Criterium, Int, Int), + where the Integers are the Number of Sports in which the Rating is >1 + and the cumulated sum of Ratings >1, respectively + """ criteria_list = [] diff --git a/unisportomat/quiz/views.py b/unisportomat/quiz/views.py index f3d56cd302f13c0770ac6750ce92722facc8501f..61514311cf64081e888a1e5f49df2e1d77a4d4c7 100644 --- a/unisportomat/quiz/views.py +++ b/unisportomat/quiz/views.py @@ -7,7 +7,7 @@ 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 JsonResponse, HttpResponse +from django.http import HttpResponse from .pagination import PageNumberWithPageSizePagination from .serializers import ( @@ -59,33 +59,22 @@ class QuestionListView(viewsets.ModelViewSet): # pylint: disable=too-many-ances class SmallSportListView(viewsets.ViewSet): """ - View for the List of Sports on the Sport Homepage - TODO: More Documentation + 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): - - paginator = PageNumberWithPageSizePagination() """ - Comments on Pagination: - Every list that needs to be paginated needs a paginator. - Here, the Paginator object is created in the GET call. - The Queryset which is supposed to be paginated needs to be run through the function: - - > new_queryset = paginator.paginate_queryset(complete_queryset, request) - - The new_queryset is a List, not a Manager, so it can be directly iterated upon. - After the data has been worked on and run through the Serializer as normal, - instead of returning Result(data), the paginator needs to be used again so it can add its page metadata: - - > return paginator.get_paginated_response(serializer.data) - - This function already returns a fully valid Response, so it can be directly returned. + 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() sports = paginator.paginate_queryset(sports, request) is_filled_tuples = [] @@ -100,22 +89,26 @@ class SmallSportListView(viewsets.ViewSet): # POST for api/admin/sport/ def create(self, request): + """ + POST for api/admin/sport/ + View for Creating a New Sport + """ - data_dict = SingleSportSerializer().to_internal_value(request) + request_data = SingleSportSerializer().to_internal_value(request) # A Try at Error Catching - if data_dict == None: + if request_data is None: return HttpResponse(status=400) new_sport = Sport.objects.create_sport() - new_sport.name = data_dict["name"] - new_sport.url = data_dict["url"] + new_sport.name = request_data["name"] + new_sport.url = request_data["url"] # Before writing other Database Entries for Rating, the Sport needs to be saved once new_sport.save() - for criterion, value in data_dict["criteria"]: + for criterion, value in request_data["criteria"]: new_sport.rate(criterion, value) new_sport.save() @@ -126,6 +119,10 @@ class SmallSportListView(viewsets.ViewSet): # 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) @@ -136,13 +133,15 @@ class SmallSportListView(viewsets.ViewSet): # 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 """ # Get Data from Serializer request_data = SingleSportSerializer().to_internal_value(request) - if request_data == None: + if request_data is None: # Something is Broke, so Refuse Changing Data return HttpResponse(status=400) @@ -164,11 +163,16 @@ class SmallSportListView(viewsets.ViewSet): # 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 + TODO: Id From URL or JSON? + """ # Get Data from Serializer request_data = SingleSportSerializer().to_internal_value(request) - if request_data == None: + if request_data is None: # Something is Broke, so Refuse Changing Data return HttpResponse(status=400) @@ -193,6 +197,10 @@ class SmallSportListView(viewsets.ViewSet): # 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) @@ -202,11 +210,18 @@ class SmallSportListView(viewsets.ViewSet): class IncompleteSportView(APIView): + """ + Returns all Sport Objects with Incomplete Ratings + """ authentication_classes = [] - # GET for api/admin/sport/incomplete + # GET for api/admin/sport/incomplete/ def get(self, request): + """ + GET for api/admin/sport/incomplete/ + Returns every incomplete Sport with its incomplete Ratings + """ incomplete_sport_list = [] @@ -221,16 +236,20 @@ class IncompleteSportView(APIView): class CriteriaView(APIView): - """ View for the List of Criteria and their Metadata - TODO: More Documentation """ authentication_classes = [] - # GET for api/admin/criteria + # 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 + """ data = []