From bdd0d3d76ac67fb27af6768d35bd5af7456c7cdc Mon Sep 17 00:00:00 2001 From: BeritJanssen Date: Tue, 12 Nov 2024 08:46:30 +0100 Subject: [PATCH] fix: let request.session track the number of times an experiment was played --- backend/experiment/serializers.py | 17 ++++------- backend/experiment/tests/test_serializers.py | 14 +++++---- backend/experiment/tests/test_views.py | 31 ++++++++++++++++---- backend/experiment/views.py | 21 +++++++------ 4 files changed, 50 insertions(+), 33 deletions(-) diff --git a/backend/experiment/serializers.py b/backend/experiment/serializers.py index 1b5030233..f4018a45d 100644 --- a/backend/experiment/serializers.py +++ b/backend/experiment/serializers.py @@ -91,7 +91,7 @@ def serialize_social_media_config( } -def serialize_phase(phase: Phase, participant: Participant) -> dict: +def serialize_phase(phase: Phase, participant: Participant, times_played: int) -> dict: """Serialize phase Args: @@ -103,7 +103,7 @@ def serialize_phase(phase: Phase, participant: Participant) -> dict: """ blocks = list(phase.blocks.order_by("index").all()) - next_block = get_upcoming_block(phase, participant) + next_block = get_upcoming_block(phase, participant, times_played) if not next_block: return None @@ -137,7 +137,7 @@ def serialize_block(block_object: Block, language: str = "en") -> dict: } -def get_upcoming_block(phase: Phase, participant: Participant): +def get_upcoming_block(phase: Phase, participant: Participant, times_played: int): """return next block with minimum finished sessions for this participant if all blocks have been played an equal number of times, return None @@ -154,15 +154,8 @@ def get_upcoming_block(phase: Phase, participant: Participant): min_session_count = min(finished_session_counts) if not phase.dashboard: - phase_profile, _created = Result.objects.get_or_create( - participant=participant, question_key=f"{str(phase)}-xplayed" - ) - max_session_count = max(finished_session_counts) - if max_session_count == min_session_count: - if phase_profile.score != min_session_count: - phase_profile.score += 1 - phase_profile.save() - return None + if times_played != min_session_count: + return None next_block_index = finished_session_counts.index(min_session_count) return serialize_block(blocks[next_block_index]) diff --git a/backend/experiment/tests/test_serializers.py b/backend/experiment/tests/test_serializers.py index efc72f78b..969156a8e 100644 --- a/backend/experiment/tests/test_serializers.py +++ b/backend/experiment/tests/test_serializers.py @@ -39,7 +39,7 @@ def setUpTestData(cls): block.save() def test_serialize_phase(self): - phase = serialize_phase(self.phase1, self.participant) + phase = serialize_phase(self.phase1, self.participant, 0) self.assertIsNotNone(phase) next_block_slug = phase.get("nextBlock").get("slug") self.assertEqual(phase.get("dashboard"), []) @@ -50,21 +50,21 @@ def test_serialize_phase(self): block=Block.objects.get(slug=next_block_slug), finished_at=timezone.now(), ) - phase = serialize_phase(self.phase1, self.participant) + phase = serialize_phase(self.phase1, self.participant, 0) self.assertIsNone(phase) def test_upcoming_block(self): - block = get_upcoming_block(self.phase1, self.participant) + block = get_upcoming_block(self.phase1, self.participant, 0) self.assertEqual(block.get("slug"), "rhythm_intro") Session.objects.create( block=Block.objects.get(slug=block.get("slug")), participant=self.participant, finished_at=timezone.now(), ) - block = get_upcoming_block(self.phase1, self.participant) + block = get_upcoming_block(self.phase1, self.participant, 0) self.assertIsNone(block) for i in range(3): - block = get_upcoming_block(self.phase2, self.participant) + block = get_upcoming_block(self.phase2, self.participant, 0) self.assertIsNotNone(block) self.assertIn(block.get("slug"), ["ddi", "hbat_bit", "rhdis"]) Session.objects.create( @@ -72,8 +72,10 @@ def test_upcoming_block(self): participant=self.participant, finished_at=timezone.now(), ) - block = get_upcoming_block(self.phase2, self.participant) + block = get_upcoming_block(self.phase2, self.participant, 0) self.assertIsNone(block) + block = get_upcoming_block(self.phase1, self.participant, 1) + self.assertIsNotNone(block) def test_serialize_block(self): # Create the experiment & phase for the block diff --git a/backend/experiment/tests/test_views.py b/backend/experiment/tests/test_views.py index 605fefa75..d3a291564 100644 --- a/backend/experiment/tests/test_views.py +++ b/backend/experiment/tests/test_views.py @@ -23,9 +23,6 @@ class TestExperimentViews(TestCase): @classmethod def setUpTestData(cls): cls.participant = Participant.objects.create() - session = cls.client.session - session["participant_id"] = cls.participant.id - session.save() theme_config = create_theme_config() experiment = Experiment.objects.create( slug="test_series", @@ -47,6 +44,11 @@ def setUpTestData(cls): cls.final_phase = Phase.objects.create(experiment=experiment, index=3) cls.block4 = Block.objects.create(slug="block4", phase=cls.final_phase) + def setUp(self): + session = self.client.session + session["participant_id"] = self.participant.id + session.save() + def test_get_experiment(self): # check that the correct block is returned correctly response = self.client.get("/experiment/test_series/") @@ -69,6 +71,21 @@ def test_get_experiment(self): self.assertEqual(response_json.get("socialMedia").get("content"), "Please play this Test experiment!") self.assertEqual(response_json.get("socialMedia").get("tags"), ["aml", "toontjehoger"]) self.assertEqual(response_json.get("socialMedia").get("channels"), ["facebook", "twitter", "weibo"]) + Session.objects.create( + block=self.block4, participant=self.participant, finished_at=timezone.now() + ) + # starting second round of experiment + response = self.client.get("/experiment/test_series/") + response_json = response.json() + self.assertIsNotNone(response_json) + self.assertEqual(response_json.get("nextBlock").get("slug"), "block1") + Session.objects.create( + block=self.block1, participant=self.participant, finished_at=timezone.now() + ) + response = self.client.get("/experiment/test_series/") + response_json = response.json() + self.assertIsNotNone(response_json) + self.assertIn(response_json.get("nextBlock").get("slug"), ("block2", "block3")) def test_experiment_not_found(self): # if Experiment does not exist, return 404 @@ -164,9 +181,9 @@ def test_experiment_get_fallback_content(self): # request experiment with language set to English (British) response = self.client.get( - "/experiment/test_experiment_translated_content/", headers={"Accept-Language": "en-Gb"} + "/experiment/test_experiment_translated_content/", + headers={"Accept-Language": "en-Gb"}, ) - # since English translation is available, the English content should be returned self.assertEqual(response.json().get("name"), "Test Experiment Fallback Content") @@ -180,7 +197,9 @@ def test_experiment_get_fallback_content(self): response = self.client.get("/experiment/test_experiment_translated_content/", headers={"Accept-Language": "nl"}) # since no Dutch translation is available, the fallback content should be returned - self.assertEqual(response.json().get("name"), "Test Experiment Fallback Content")=å + self.assertEqual( + response.json().get("name"), "Test Experiment Fallback Content" + ) def test_get_block(self): # Create a block diff --git a/backend/experiment/views.py b/backend/experiment/views.py index 6ebaa832c..41cc7bef6 100644 --- a/backend/experiment/views.py +++ b/backend/experiment/views.py @@ -116,15 +116,18 @@ def get_experiment( {"error": "This experiment does not have phases and blocks configured"}, status=500, ) - phase_key = 'f"{slug}-phase"' - phase_index = request.session.get(phase_key, 0) - if phase_index == len(phases): - phase_index = 0 - serialized_phase = serialize_phase(phases[phase_index], participant) - if not serialized_phase: - phase_index += 1 - request.session[phase_key] = phase_index - serialized_phase = serialize_phase(phases[phase_index], participant) + times_played_key = 'f"{slug}-xplayed"' + times_played = request.session.get(times_played_key, 0) + for phase in phases: + serialized_phase = serialize_phase(phase, participant, times_played) + if serialized_phase: + return JsonResponse( + {**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})