diff --git a/edx_proctoring/api.py b/edx_proctoring/api.py index 0f63361509f..763e485bd97 100644 --- a/edx_proctoring/api.py +++ b/edx_proctoring/api.py @@ -1309,6 +1309,11 @@ def _resolve_prerequisite_links(exam, prerequisites): 'short_description': _('Failed Proctoring'), 'suggested_icon': 'fa-exclamation-triangle', 'in_completed_state': True + }, + ProctoredExamStudentAttemptStatus.expired: { + 'short_description': _('Proctored Option No Longer Available'), + 'suggested_icon': 'fa-times-circle', + 'in_completed_state': False } } @@ -1375,12 +1380,17 @@ def get_attempt_status_summary(user_id, course_id, content_id): # practice exams always has an attempt status regardless of # eligibility if credit_service and not exam['is_practice_exam']: - credit_state = credit_service.get_credit_state(user_id, unicode(course_id)) + credit_state = credit_service.get_credit_state(user_id, unicode(course_id), return_course_info=True) if not _check_eligibility_of_enrollment_mode(credit_state): return None attempt = get_exam_attempt(exam['id'], user_id) - status = attempt['status'] if attempt else ProctoredExamStudentAttemptStatus.eligible + if attempt: + status = attempt['status'] + elif not exam['is_practice_exam'] and has_due_date_passed(credit_state.get('course_end_date', None)): + status = ProctoredExamStudentAttemptStatus.expired + else: + status = ProctoredExamStudentAttemptStatus.eligible status_map = STATUS_SUMMARY_MAP if not exam['is_practice_exam'] else PRACTICE_STATUS_SUMMARY_MAP diff --git a/edx_proctoring/models.py b/edx_proctoring/models.py index 9906f51bdc1..64e221f49c8 100644 --- a/edx_proctoring/models.py +++ b/edx_proctoring/models.py @@ -165,6 +165,9 @@ class ProctoredExamStudentAttemptStatus(object): # the exam is believed to be in error error = 'error' + # the course end date has passed + expired = 'expired' + # status alias for sending email status_alias_mapping = { submitted: _('pending'), diff --git a/edx_proctoring/tests/test_api.py b/edx_proctoring/tests/test_api.py index aa3ca56f65c..322d5749e51 100644 --- a/edx_proctoring/tests/test_api.py +++ b/edx_proctoring/tests/test_api.py @@ -1215,6 +1215,24 @@ def test_proctored_exam_passed_end_date(self): ) self.assertIsNone(rendered_response) + def test_proctored_status_summary_passed_end_date(self): + """ + Assert that we get the expected status summaries + """ + + set_runtime_service('credit', MockCreditServiceWithCourseEndDate()) + + exam = get_exam_by_id(self.proctored_exam_id) + summary = get_attempt_status_summary(self.user.id, exam['course_id'], exam['content_id']) + + expected = { + 'status': ProctoredExamStudentAttemptStatus.expired, + 'short_description': 'Proctored Option No Longer Available', + 'suggested_icon': 'fa-times-circle', + 'in_completed_state': False + } + self.assertIn(summary, [expected]) + def test_practice_exam_passed_end_date(self): """ Verify that we get a None back on a practice exam @@ -2331,6 +2349,14 @@ def test_update_unexisting_attempt(self): 'suggested_icon': 'fa-pencil-square-o', 'in_completed_state': False } + ), + ( + ProctoredExamStudentAttemptStatus.expired, { + 'status': ProctoredExamStudentAttemptStatus.expired, + 'short_description': 'Proctored Option No Longer Available', + 'suggested_icon': 'fa-times-circle', + 'in_completed_state': False + } ) ) @ddt.unpack diff --git a/setup.py b/setup.py index 5a5b355fec0..c3f48b4644e 100755 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ def load_requirements(*requirements_paths): setup( name='edx-proctoring', - version='0.12.14', + version='0.12.15', description='Proctoring subsystem for Open edX', long_description=open('README.md').read(), author='edX',