diff --git a/commcare_connect/opportunity/api/serializers.py b/commcare_connect/opportunity/api/serializers.py index 06d251a7..493b40ef 100644 --- a/commcare_connect/opportunity/api/serializers.py +++ b/commcare_connect/opportunity/api/serializers.py @@ -2,6 +2,7 @@ from commcare_connect.cache import quickcache from commcare_connect.opportunity.models import ( + Assessment, CommCareApp, CompletedModule, LearnModule, @@ -86,12 +87,29 @@ def _get_opp_access(user, opportunity): return OpportunityAccess.objects.filter(user=user, opportunity=opportunity).first() -class UserLearnProgressSerializer(serializers.ModelSerializer): +class CompletedModuleSerializer(serializers.ModelSerializer): class Meta: model = CompletedModule fields = ["module", "date", "duration"] +class AssessmentSerializer(serializers.ModelSerializer): + class Meta: + model = Assessment + fields = ["date", "score", "passing_score", "passed"] + + +class UserLearnProgressSerializer(serializers.Serializer): + completed_modules = serializers.SerializerMethodField() + assessments = serializers.SerializerMethodField() + + def get_completed_modules(self, obj: dict): + return CompletedModuleSerializer(obj.get("completed_modules"), many=True).data + + def get_assessments(self, obj: dict): + return AssessmentSerializer(obj.get("assessments"), many=True).data + + class UserVisitSerializer(serializers.ModelSerializer): deliver_unit_name = serializers.CharField(source="deliver_unit.name") deliver_unit_slug = serializers.CharField(source="deliver_unit.slug") diff --git a/commcare_connect/opportunity/api/views.py b/commcare_connect/opportunity/api/views.py index 094f12a6..2edb5ea1 100644 --- a/commcare_connect/opportunity/api/views.py +++ b/commcare_connect/opportunity/api/views.py @@ -1,7 +1,7 @@ import datetime from rest_framework import viewsets -from rest_framework.generics import ListAPIView, RetrieveAPIView, get_object_or_404 +from rest_framework.generics import RetrieveAPIView, get_object_or_404 from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView @@ -13,6 +13,7 @@ UserVisitSerializer, ) from commcare_connect.opportunity.models import ( + Assessment, CompletedModule, Opportunity, OpportunityAccess, @@ -31,15 +32,20 @@ def get_queryset(self): return Opportunity.objects.filter(opportunityaccess__user=self.request.user) -class UserLearnProgressView(ListAPIView): +class UserLearnProgressView(RetrieveAPIView): permission_classes = [IsAuthenticated] serializer_class = UserLearnProgressSerializer - def get_queryset(self): + def get_object(self): opportunity_access = get_object_or_404( OpportunityAccess, user=self.request.user, opportunity=self.kwargs.get("pk") ) - return CompletedModule.objects.filter(user=self.request.user, opportunity=opportunity_access.opportunity) + return dict( + completed_modules=CompletedModule.objects.filter( + user=self.request.user, opportunity=opportunity_access.opportunity + ), + assessments=Assessment.objects.filter(user=self.request.user, opportunity=opportunity_access.opportunity), + ) class UserVisitViewSet(viewsets.GenericViewSet, viewsets.mixins.ListModelMixin): diff --git a/commcare_connect/opportunity/tests/test_api_views.py b/commcare_connect/opportunity/tests/test_api_views.py index d9cb6cec..c1eb44fd 100644 --- a/commcare_connect/opportunity/tests/test_api_views.py +++ b/commcare_connect/opportunity/tests/test_api_views.py @@ -1,9 +1,14 @@ import datetime +import pytest from rest_framework.test import APIClient -from commcare_connect.opportunity.models import OpportunityClaim -from commcare_connect.opportunity.tests.factories import OpportunityAccessFactory, OpportunityFactory +from commcare_connect.opportunity.models import Assessment, CompletedModule, OpportunityClaim +from commcare_connect.opportunity.tests.factories import ( + LearnModuleFactory, + OpportunityAccessFactory, + OpportunityFactory, +) from commcare_connect.users.models import User from commcare_connect.users.tests.factories import ConnectIdUserLinkFactory @@ -61,3 +66,36 @@ def test_claim_endpoint_already_claimed_opportunity(mobile_user: User, api_clien response = api_client.post(f"/api/opportunity/{opportunity.id}/claim") assert response.status_code == 200 assert response.data == "Opportunity is already claimed" + + +@pytest.mark.django_db +def test_learn_progress_endpoint(mobile_user: User, api_client: APIClient): + opportunity, opportunity_access = _setup_opportunity_and_access( + mobile_user, total_budget=1000, end_date=datetime.date.today() + datetime.timedelta(days=100) + ) + learn_module = LearnModuleFactory(slug="module_1", app=opportunity.learn_app) + CompletedModule.objects.create( + module=learn_module, + user=mobile_user, + opportunity=opportunity, + date=datetime.datetime.now(), + duration=datetime.timedelta(hours=10), + ) + Assessment.objects.create( + user=mobile_user, + app=opportunity.learn_app, + opportunity=opportunity, + date=datetime.datetime.now(), + score=100, + passing_score=opportunity.learn_app.passing_score, + passed=True, + ) + api_client.force_authenticate(mobile_user) + response = api_client.get(f"/api/opportunity/{opportunity.id}/learn_progress") + assert response.status_code == 200 + assert "completed_modules" in response.data + assert len(response.data["completed_modules"]) == 1 + assert "assessments" in response.data + assert len(response.data["assessments"]) == 1 + assert list(response.data["completed_modules"][0].keys()) == ["module", "date", "duration"] + assert list(response.data["assessments"][0].keys()) == ["date", "score", "passing_score", "passed"]