diff --git a/mpbackend/excluded_path.py b/mpbackend/excluded_path.py index 2b0c159..f0f06bb 100644 --- a/mpbackend/excluded_path.py +++ b/mpbackend/excluded_path.py @@ -4,6 +4,7 @@ "/api/v1/question/", "/api/v1/answer/", "/api/v1/postalcoderesult/", + "/api/v1/cumulativeresult/", "/api/account/", ] diff --git a/profiles/api/serializers.py b/profiles/api/serializers.py index bbcbc1e..4f67dea 100644 --- a/profiles/api/serializers.py +++ b/profiles/api/serializers.py @@ -2,6 +2,7 @@ from profiles.models import ( Answer, + CumulativeResultCount, Option, PostalCode, PostalCodeResult, @@ -149,3 +150,17 @@ class PostalCodeTypeSerializer(serializers.ModelSerializer): class Meta: model = PostalCodeType fields = "__all__" + + +class CumulativeResultSerializer(serializers.ModelSerializer): + class Meta: + model = CumulativeResultCount + fields = "__all__" + + def to_representation(self, instance): + type_name = self.context.get("type_name") + representation = super().to_representation(instance) + representation["sum_of_count"] = instance.get_sum_of_count( + postal_code_type_name=type_name + ) + return representation diff --git a/profiles/api/views.py b/profiles/api/views.py index dcccdc5..a751d38 100644 --- a/profiles/api/views.py +++ b/profiles/api/views.py @@ -17,7 +17,8 @@ from rest_framework import status, viewsets from rest_framework.authtoken.models import Token from rest_framework.decorators import action -from rest_framework.mixins import CreateModelMixin +from rest_framework.exceptions import ParseError +from rest_framework.mixins import CreateModelMixin, ListModelMixin from rest_framework.permissions import AllowAny, IsAuthenticated from rest_framework.response import Response from rest_framework.viewsets import GenericViewSet @@ -27,6 +28,7 @@ from profiles.api.serializers import ( AnswerRequestSerializer, AnswerSerializer, + CumulativeResultSerializer, InConditionResponseSerializer, OptionSerializer, PostalCodeResultSerializer, @@ -44,6 +46,7 @@ ) from profiles.models import ( Answer, + CumulativeResultCount, Option, PostalCode, PostalCodeResult, @@ -738,6 +741,44 @@ class PostalCodeViewSet(viewsets.ReadOnlyModelViewSet): register_view(PostalCodeViewSet, "postalcode") +@extend_schema_view( + list=extend_schema( + parameters=[ + POSTAL_CODE_TYPE_PARAM, + ], + description="Returns cumulative result count for every result.", + ) +) +class CumulativeResultsViewSet(ListModelMixin, GenericViewSet): + queryset = CumulativeResultCount.objects.all() + serializer_class = CumulativeResultSerializer + + def list(self, request, *args, **kwargs): + queryset = self.queryset + type_name = "" + postal_code_type_id = request.query_params.get( + "postal_code_type", + PostalCodeType.objects.get(type_name=PostalCodeType.HOME_POSTAL_CODE).id, + ) + try: + postal_code_type_id = int(postal_code_type_id) + except ValueError: + raise ParseError("'postal_code_type' must be int") + postal_code_type = PostalCodeType.objects.filter(id=postal_code_type_id).first() + if not postal_code_type: + queryset = CumulativeResultCount.objects.none() + else: + type_name = postal_code_type.type_name + page = self.paginate_queryset(queryset) + serializer = self.serializer_class( + page, many=True, context={"type_name": type_name} + ) + return self.get_paginated_response(serializer.data) + + +register_view(CumulativeResultsViewSet, "cumulativeresult") + + class PostalCodeTypeViewSet(viewsets.ReadOnlyModelViewSet): queryset = PostalCodeType.objects.all() serializer_class = PostalCodeTypeSerializer diff --git a/profiles/migrations/0022_cumulativeresultcount.py b/profiles/migrations/0022_cumulativeresultcount.py new file mode 100644 index 0000000..0d36f13 --- /dev/null +++ b/profiles/migrations/0022_cumulativeresultcount.py @@ -0,0 +1,23 @@ +# Generated by Django 4.1.13 on 2024-04-30 07:23 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("profiles", "0021_result_num_options"), + ] + + operations = [ + migrations.CreateModel( + name="CumulativeResultCount", + fields=[], + options={ + "proxy": True, + "indexes": [], + "constraints": [], + }, + bases=("profiles.result",), + ), + ] diff --git a/profiles/tests/api/test_postal_code_result.py b/profiles/tests/api/test_postal_code_result.py index 46a30a9..53909a2 100644 --- a/profiles/tests/api/test_postal_code_result.py +++ b/profiles/tests/api/test_postal_code_result.py @@ -305,3 +305,15 @@ def test_non_existing_postal_code_string(api_client): response = api_client.get(url) assert response.status_code == 200 assert response.json()["count"] == 0 + + +@pytest.mark.django_db +def test_cumulative_results( + api_client, results, postal_code_types, postal_code_results +): + url = "/api/v1/cumulativeresult/" + response = api_client.get(url) + assert response.status_code == 200 + json_data = response.json() + assert json_data["count"] == results.count() + assert json_data["results"][0]["sum_of_count"] == 6 diff --git a/profiles/tests/conftest.py b/profiles/tests/conftest.py index 87034b8..450ec0e 100644 --- a/profiles/tests/conftest.py +++ b/profiles/tests/conftest.py @@ -7,6 +7,7 @@ Answer, Option, PostalCode, + PostalCodeResult, PostalCodeType, Question, QuestionCondition, @@ -65,6 +66,24 @@ def postal_code_types(): return PostalCodeType.objects.all() +@pytest.mark.django_db +@pytest.fixture +def postal_code_results(postal_codes, postal_code_types, results): + PostalCodeResult.objects.create( + postal_code=postal_codes.first(), + postal_code_type=postal_code_types.first(), + result=results.first(), + count=4, + ) + PostalCodeResult.objects.create( + postal_code=postal_codes.last(), + postal_code_type=postal_code_types.first(), + result=results.first(), + count=2, + ) + return PostalCodeResult.objects.all() + + @pytest.mark.django_db @pytest.fixture def questions_test_result():