diff --git a/CHANGELOG.rst b/CHANGELOG.rst index fa8377a9e9..7603e3b54f 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -14,6 +14,10 @@ Change Log Unreleased ~~~~~~~~~~ +[4.13.0] - 2022-10-26 +~~~~~~~~~~~~~~~~~~~~~ +* Do not use course's due date when a timed exam has one specified + [4.12.2] - 2022-10-19 ~~~~~~~~~~~~~~~~~~~~~ * Return external id when requesting exam attempt diff --git a/edx_proctoring/__init__.py b/edx_proctoring/__init__.py index 28dffb0fc6..1e1c9c8864 100644 --- a/edx_proctoring/__init__.py +++ b/edx_proctoring/__init__.py @@ -3,6 +3,6 @@ """ # Be sure to update the version number in edx_proctoring/package.json -__version__ = '4.12.2' +__version__ = '4.13.0' default_app_config = 'edx_proctoring.apps.EdxProctoringConfig' # pylint: disable=invalid-name diff --git a/edx_proctoring/api.py b/edx_proctoring/api.py index dd4f56474d..3d3e77ce26 100644 --- a/edx_proctoring/api.py +++ b/edx_proctoring/api.py @@ -882,11 +882,16 @@ def is_exam_passed_due(exam, user=None): Return whether the due date has passed. Uses edx_when to lookup the date for the subsection. """ + due_date = get_exam_due_date(exam, user=user) return ( - has_due_date_passed(get_exam_due_date(exam, user=user)) - # if the exam is timed and passed the course end date, it should also be considered passed due + has_due_date_passed(due_date) + # If the exam is timed and passed the course end date, it should also be considered passed due, unless + # it has a due date set. The exam's due date has a higher priority than the course's end date. or ( - not exam['is_proctored'] and not exam['is_practice_exam'] and has_end_date_passed(exam['course_id']) + not due_date + and not exam['is_proctored'] + and not exam['is_practice_exam'] + and has_end_date_passed(exam['course_id']) ) ) diff --git a/edx_proctoring/tests/test_student_view.py b/edx_proctoring/tests/test_student_view.py index 1fedf4ae29..dba2657aaf 100644 --- a/edx_proctoring/tests/test_student_view.py +++ b/edx_proctoring/tests/test_student_view.py @@ -873,6 +873,33 @@ def test_timed_exam_attempt_with_past_due_datetime(self): ) self.assertIn(self.exam_expired_msg, rendered_response) + @patch('edx_when.api.get_date_for_block') + @patch('edx_when.api.get_dates_for_course') + def test_timed_exam_attempt_with_past_course_due_date_and_future_exam_due_date( + self, mock_course_dates, mock_exam_due_date + ): + """ + When the course due date is in the past, but the exam-specific due date is in the future, students should still + be able to start this exam. + """ + current_date = datetime.now(pytz.UTC) + course_end_date = current_date - timedelta(days=14) + exam_due_date = current_date + timedelta(days=14) + + mock_course_dates.return_value = {('dummy', 'end'): course_end_date} + mock_exam_due_date.return_value = exam_due_date + + self._create_exam_with_due_time(due_date=exam_due_date, is_proctored=False) + + with freeze_time(current_date): + rendered_response = get_student_view( + user_id=self.user_id, + course_id=self.course_id, + content_id=self.content_id_for_exam_with_due_date, + context={}, + ) + self.assertIn(self.timed_footer_msg, rendered_response) + @patch.dict('django.conf.settings.PROCTORING_SETTINGS', {'ALLOW_TIMED_OUT_STATE': True}) def test_get_studentview_timedout(self): """ diff --git a/package.json b/package.json index bfbd5e420a..7816ac0c0c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@edx/edx-proctoring", "//": "Note that the version format is slightly different than that of the Python version when using prereleases.", - "version": "4.12.2", + "version": "4.13.0", "main": "edx_proctoring/static/index.js", "scripts": { "test": "gulp test"