diff --git a/edx_proctoring/api.py b/edx_proctoring/api.py index 21d196faf40..c7675480f63 100644 --- a/edx_proctoring/api.py +++ b/edx_proctoring/api.py @@ -1069,10 +1069,13 @@ def update_attempt_status(exam_id, user_id, to_status, # call back to the backend to register the end of the exam, if necessary backend = get_backend_provider(exam) - if to_status == ProctoredExamStudentAttemptStatus.started: - backend.start_exam_attempt(exam['external_id'], attempt['external_id']) - if to_status == ProctoredExamStudentAttemptStatus.submitted: - backend.stop_exam_attempt(exam['external_id'], attempt['external_id']) + if backend: + # only proctored exams have a backend + # timed exams have no backend + if to_status == ProctoredExamStudentAttemptStatus.started: + backend.start_exam_attempt(exam['external_id'], attempt['external_id']) + if to_status == ProctoredExamStudentAttemptStatus.submitted: + backend.stop_exam_attempt(exam['external_id'], attempt['external_id']) # we user the 'status' field as the name of the event 'verb' emit_event(exam, attempt['status'], attempt=attempt) diff --git a/edx_proctoring/backends/__init__.py b/edx_proctoring/backends/__init__.py index 82ba7552292..d37388bb3f7 100644 --- a/edx_proctoring/backends/__init__.py +++ b/edx_proctoring/backends/__init__.py @@ -9,6 +9,10 @@ def get_backend_provider(exam=None): Returns an instance of the configured backend provider """ backend_name = None - if exam and exam['backend']: - backend_name = exam['backend'] + if exam: + if 'is_proctored' in exam and not exam['is_proctored']: + # timed exams don't have a backend + return None + elif exam['backend']: + backend_name = exam['backend'] return apps.get_app_config('edx_proctoring').get_backend(name=backend_name) diff --git a/edx_proctoring/backends/tests/test_backend.py b/edx_proctoring/backends/tests/test_backend.py index f6d722dccee..0ce25dcb859 100644 --- a/edx_proctoring/backends/tests/test_backend.py +++ b/edx_proctoring/backends/tests/test_backend.py @@ -226,6 +226,17 @@ def test_backend_choices(self): ] self.assertEqual(choices, expected) + def test_no_backend_for_timed_exams(self): + """ + Timed exams should not return a backend, even if one has accidentally been set + """ + exam = { + 'is_proctored': False, + 'backend': 'test' + } + backend = get_backend_provider(exam) + self.assertIsNone(backend) + def test_invalid_configurations(self): """ Test that invalid backends throw the right exceptions diff --git a/edx_proctoring/tests/test_views.py b/edx_proctoring/tests/test_views.py index 8b7adc687bb..7f4f8e1f145 100644 --- a/edx_proctoring/tests/test_views.py +++ b/edx_proctoring/tests/test_views.py @@ -2503,6 +2503,7 @@ def test_launch_for_course(self): external_id='123aXqe3', time_limit_mins=90, is_active=True, + is_proctored=True, ) expected_url = '/instructor/%s/' % course_id @@ -2521,6 +2522,7 @@ def test_launch_for_exam(self): external_id='123aXqe3', time_limit_mins=90, is_active=True, + is_proctored=True, ) exam_id = proctored_exam.id @@ -2540,6 +2542,7 @@ def test_error_with_multiple_backends(self): external_id='123aXqe3', time_limit_mins=90, is_active=True, + is_proctored=True, backend='test', ) ProctoredExam.objects.create( @@ -2549,6 +2552,7 @@ def test_error_with_multiple_backends(self): external_id='123aXqe4', time_limit_mins=90, is_active=True, + is_proctored=True, backend='null', ) response = self.client.get(