diff --git a/backend/experiment/serializers.py b/backend/experiment/serializers.py index f4018a45d..64ad666b2 100644 --- a/backend/experiment/serializers.py +++ b/backend/experiment/serializers.py @@ -1,5 +1,5 @@ from random import shuffle -from typing import Optional +from typing import Optional, Union from django_markup.markup import formatter from django.utils.translation import activate, get_language @@ -137,7 +137,9 @@ def serialize_block(block_object: Block, language: str = "en") -> dict: } -def get_upcoming_block(phase: Phase, participant: Participant, times_played: int): +def get_upcoming_block( + phase: Phase, participant: Participant, times_played: int +) -> dict: """return next block with minimum finished sessions for this participant if all blocks have been played an equal number of times, return None diff --git a/backend/experiment/tests/test_views.py b/backend/experiment/tests/test_views.py index 1b93b46b5..987653095 100644 --- a/backend/experiment/tests/test_views.py +++ b/backend/experiment/tests/test_views.py @@ -87,6 +87,32 @@ def test_get_experiment(self): self.assertIsNotNone(response_json) self.assertIn(response_json.get("nextBlock").get("slug"), ("block2", "block3")) + def test_get_experiment_returning_participant(self): + Session.objects.bulk_create( + self._get_session_objects( + [ + self.block1, + self.block2, + self.block3, + self.block4, + self.block1, + self.block2, + self.block3, + self.block4, + ] + ) + ) + response = self.client.get("/experiment/test_series/") + self.assertEqual(response.json().get("nextBlock").get("slug"), "block1") + + def _get_session_objects(self, block_list: list[Block]) -> list[Session]: + return [ + Session( + block=block, participant=self.participant, finished_at=timezone.now() + ) + for block in block_list + ] + def test_experiment_not_found(self): # if Experiment does not exist, return 404 response = self.client.get("/experiment/not_found/") diff --git a/backend/experiment/views.py b/backend/experiment/views.py index 41cc7bef6..31e0bf4c3 100644 --- a/backend/experiment/views.py +++ b/backend/experiment/views.py @@ -5,7 +5,7 @@ from django.utils.translation import gettext_lazy as _, get_language from django_markup.markup import formatter -from .models import Block, Experiment, Phase, Feedback, Session +from .models import Block, Experiment, Feedback, Session from section.models import Playlist from experiment.serializers import ( serialize_block, @@ -14,7 +14,7 @@ ) from experiment.rules import BLOCK_RULES from experiment.actions.utils import EXPERIMENT_KEY -from image.serializers import serialize_image +from participant.models import Participant from participant.utils import get_participant from theme.serializers import serialize_theme @@ -122,13 +122,29 @@ def get_experiment( serialized_phase = serialize_phase(phase, participant, times_played) if serialized_phase: return JsonResponse( - {**serialize_experiment(experiment), **serialized_phase} + { + **serialize_experiment(experiment), + **serialized_phase, + } ) - # if no phase was found, start from scratch by incrementing times_pleyd - times_played += 1 - request.session[times_played_key] = times_played - serialized_phase = serialize_phase(phases[0], participant, times_played) - return JsonResponse({**serialize_experiment(experiment), **serialized_phase}) + # if no phase was found, start from scratch with the minimum session count + request.session[times_played_key] = _get_min_session_count(experiment, participant) + return get_experiment(request, slug) + + +def _get_min_session_count(experiment: Experiment, participant: Participant) -> int: + phases = experiment.phases.all() + session_counts = [] + for phase in phases: + session_counts.extend( + [ + Session.objects.exclude(finished_at__isnull=True) + .filter(block=block, participant=participant) + .count() + for block in phase.blocks.all() + ] + ) + return min(session_counts) def get_associated_blocks(pk_list):