Commit 2863325c authored by Sevastian Slavov's avatar Sevastian Slavov
Browse files

fix: finish on-demand exercise when trainee reaches a final milestone

parent d7cff085
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -371,6 +371,17 @@ class ExerciseStateUpdater:
        # not accounted for in the old exercise loop
        start = time.time()

        team_ids = [team_updater.team.id for team_updater in self.team_updaters]
        team_finish_times = dict(
            Team.objects.filter(id__in=team_ids).values_list(
                "id", "finish_time"
            )
        )
        for team_updater in self.team_updaters:
            team_updater.team.finish_time = team_finish_times.get(
                team_updater.team.id
            )

        should_stop = True
        logs = []
        for team_updater in self.team_updaters:
+3 −0
Original line number Diff line number Diff line
@@ -67,6 +67,9 @@ class MilestoneHandler:
        }

    def final_milestone_reached(self) -> bool:
        if self.team.finish_time is not None:
            return True

        for name in self.final_milestone_names:
            if self.milestones[name].reached:
                return True
+6 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ from common_lib.utils import has_role, ensure_exists, ensure_all_exist
from exercise.models import (
    Team,
    Exercise,
    ExerciseState,
    MilestoneState,
    EmailParticipant,
    TeamLearningObjective,
@@ -420,6 +421,11 @@ class Query(graphene.ObjectType):
            ).filter(id=int(team_id))
        )

        if team.exercise_state.status == ExerciseState.Status.FINISHED:
            return 0

        if team.exercise.on_demand and team.finish_time is not None:
            return 0
        # should be checking the SHOW_EXERCISE_TIME feature
        exercise_duration_s = team.exercise.config.exercise_duration * 60
        return max(0, exercise_duration_s - int(team.exercise_state.elapsed_s))
+39 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ from running_exercise.lib.exercise_updater import (
    ExerciseStateUpdater,
)
from running_exercise.models import LogType, ActionLog, TeamFileAccess
from running_exercise.lib.milestone_handler import MilestoneHandler

TEST_DATA_STORAGE = "exercise_updater_test_data"

@@ -742,6 +743,44 @@ class ExerciseUpdaterTestCase(TestCase):
        self.assertTrue(should_finish2)
        self.assertTrue(should_finish3)

    def test_reaching_final_milestone_on_demand_persists(self):
        team: Team = self.on_demand_base_exercise.teams.first()
        updater = ExerciseStateUpdater(team.exercise_state)

        _, should_finish = updater.update(0)
        self.assertFalse(should_finish)

        external_team = Team.objects.get(id=team.id)
        handler = MilestoneHandler(external_team)

        handler.update(Control(activate_milestone=["evaluation_great"]))
        self.assertIsNotNone(Team.objects.get(id=team.id).finish_time)
        handler.update(Control(deactivate_milestone=["evaluation_great"]))

        milestone = self.on_demand_base_exercise.definition.milestones.get(
            name="evaluation_great"
        )
        self.assertFalse(
            is_milestone_reached(Team.objects.get(id=team.id), milestone),
            msg=f"Milestone `{milestone.name}` should not be reached",
        )

        self.assertIsNotNone(Team.objects.get(id=team.id).finish_time)

        _, should_finish = updater.update(1)
        self.assertTrue(should_finish)

    def test_finish_time_set_outside_loop_is_respected(self):
        team: Team = self.on_demand_base_exercise.teams.first()
        self.assertIsNotNone(team)

        updater = ExerciseStateUpdater(team.exercise_state)

        Team.objects.filter(id=team.id).update(finish_time=timezone.now())

        _, should_stop = updater.update(1)
        self.assertTrue(should_stop)

    def test_show_exercise_overview_enabled_on_finish(self):
        exercise_state = self.base_exercise.single_state()
        updater = ExerciseStateUpdater(exercise_state)