Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions cms/djangoapps/contentstore/exams.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@ def register_exams(course_key):
# Exams in courses not using an LTI based proctoring provider should use the original definition of due_date
# from contentstore/proctoring.py. These exams are powered by the edx-proctoring plugin and not the edx-exams
# microservice.
is_instructor_paced = not course.self_paced
if course.proctoring_provider == 'lti_external':
due_date = (
timed_exam.due.isoformat() if timed_exam.due
else (course.end.isoformat() if course.end else None)
)
due_date_source = timed_exam.due if is_instructor_paced else course.end
else:
due_date = timed_exam.due if not course.self_paced else None
due_date_source = timed_exam.due if is_instructor_paced else None

due_date = due_date_source.isoformat() if due_date_source else None

exams_list.append({
'course_id': str(course_key),
Expand Down
48 changes: 22 additions & 26 deletions cms/djangoapps/contentstore/tests/test_exams.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,21 @@ def _get_exam_due_date(self, course, sequential):
Return the expected exam due date for the exam, based on the selected course proctoring provider and the
exam due date or the course end date.

This is a copy of the due date computation logic in register_exams function.

Arguments:
* course: the course that the exam subsection is in; may have a course.end attribute
* sequential: the exam subsection; may have a sequential.due attribute
"""
is_instructor_paced = not course.self_paced
if course.proctoring_provider == 'lti_external':
return sequential.due.isoformat() if sequential.due else (course.end.isoformat() if course.end else None)
elif course.self_paced:
return None
due_date_source = sequential.due if is_instructor_paced else course.end
else:
return sequential.due
due_date_source = sequential.due if is_instructor_paced else None

due_date = due_date_source.isoformat() if due_date_source else None

return due_date

@ddt.data(*(tuple(base) + (extra,) for base, extra in itertools.product(
[
Expand Down Expand Up @@ -185,14 +190,13 @@ def test_feature_flag_off(self, mock_patch_course_exams):
def test_no_due_dates(self, is_self_paced, course_end_date, proctoring_provider, mock_patch_course_exams):
"""
Test that the the correct due date is registered for the exam when the subsection does not have a due date,
depending on the proctoring provider.
depending on the proctoring provider and course pacing type.

* lti_external
* The course end date is registered as the due date when the subsection does not have a due date for both
self-paced and instructor-paced exams.
* If the course is instructor-paced, the exam due date is the subsection due date if it exists, else None.
* If the course is self-paced, the exam due date is the course end date if it exists, else None.
* not lti_external
* None is registered as the due date when the subsection does not have a due date for both
self-paced and instructor-paced exams.
* The exam due date is always the subsection due date if it exists, else None.
"""
self.course.self_paced = is_self_paced
self.course.end = course_end_date
Expand Down Expand Up @@ -222,25 +226,17 @@ def test_no_due_dates(self, is_self_paced, course_end_date, proctoring_provider,
@ddt.data(*itertools.product((True, False), ('lti_external', 'null')))
@ddt.unpack
@freeze_time('2024-01-01')
def test_subsection_due_date_prioritized(self, is_self_paced, proctoring_provider, mock_patch_course_exams):
def test_subsection_due_date_prioritized_instructor_paced(
self,
is_self_paced,
proctoring_provider,
mock_patch_course_exams
):
"""
Test that the subsection due date is registered as the due date when both the subsection has a due date and the
course has an end date for both self-paced and instructor-paced exams.

Test that the the correct due date is registered for the exam when the subsection has a due date, depending on
the proctoring provider.

* lti_external
* The subsection due date is registered as the due date when both the subsection has a due date and the
course has an end date for both self-paced and instructor-paced exams
* not lti_external
* None is registered as the due date when both the subsection has a due date and the course has an end date
for self-paced exams.
* The subsection due date is registered as the due date when both the subsection has a due date and the
course has an end date for instructor-paced exams.
Test that exam due date is computed correctly.
"""
self.course.self_paced = is_self_paced
self.course.end = datetime(2035, 1, 1, 0, 0)
self.course.end = datetime(2035, 1, 1, 0, 0, tzinfo=timezone.utc)
self.course.proctoring_provider = proctoring_provider
self.course = self.update_course(self.course, 1)

Expand All @@ -260,7 +256,7 @@ def test_subsection_due_date_prioritized(self, is_self_paced, proctoring_provide
)

listen_for_course_publish(self, self.course.id)
called_exams, called_course = mock_patch_course_exams.call_args[0]
called_exams, _ = mock_patch_course_exams.call_args[0]

expected_due_date = self._get_exam_due_date(self.course, sequence)

Expand Down
Loading