fix: deleted courses do not break program details page

Sometimes learners have certificates in old course runs which've been
deleted. When this happens loading the learner's program progress can
result in 500 errors. This corrects those by choosing to count any
non-existent course the learner has certificates for as counting
towards program completion, effectively assuming that availability
dates have passed for all such courses.

Also fixes an error with a condition related to how we determine
whether an enrolled course is considered "in progress". The previous
version of the code had a bug that would result in a lot more courses
being marked "in progress" for the purpose of program completion than
actually were.

JIRA:EDUCATOR-5787
This commit is contained in:
Matt Hughes
2021-05-13 15:21:06 -04:00
committed by Albert (AJ) St. Aubin
parent 0598cf71d3
commit 8023a09191
2 changed files with 34 additions and 4 deletions

View File

@@ -598,7 +598,7 @@ class TestProgramProgressMeter(ModuleStoreTestCase):
ProgressFactory(
uuid=program_uuid,
completed=2,
in_progress=1,
not_started=1,
)
)
assert list(meter.completed_programs_with_available_dates.keys()) == [program_uuid]
@@ -619,6 +619,32 @@ class TestProgramProgressMeter(ModuleStoreTestCase):
assert list(meter.completed_programs_with_available_dates.keys()) == [program_uuid]
assert meter.completed_programs_with_available_dates[program_uuid].date() == today.date()
def test_old_course_runs(self, mock_get_programs):
"""
Test that old course runs may exist for a program which do not exist in LMS.
In that case, continue considering the course run to've been failed by the learner
"""
course_run = CourseRunFactory.create()
course = CourseFactory.create(course_runs=[course_run])
program_data = ProgramFactory(courses=[course])
data = [program_data]
mock_get_programs.return_value = data
course_run_key = str(course_run['key'])
self._create_enrollments(course_run_key)
self._create_certificates(course_run_key, mode=MODES.verified)
meter = ProgramProgressMeter(self.site, self.user)
self._assert_progress(
meter,
ProgressFactory(
uuid=program_data['uuid'],
not_started=1
)
)
def test_nonverified_course_run_completion(self, mock_get_programs):
"""
Course runs aren't necessarily of type verified. Verify that a program can

View File

@@ -206,7 +206,7 @@ class ProgramProgressMeter:
]
if runs_with_required_mode:
not_failed_runs = [run for run in runs_with_required_mode if run not in self.failed_course_runs]
not_failed_runs = [run for run in runs_with_required_mode if run['key'] not in self.failed_course_runs]
if not_failed_runs:
return True
@@ -417,7 +417,7 @@ class ProgramProgressMeter:
Determine which course runs have been failed by the user.
Returns:
list of dicts, each a course run ID
list of strings, each a course run ID
"""
return [run['course_run_id'] for run in self.course_runs_with_state['failed']]
@@ -442,9 +442,13 @@ class ProgramProgressMeter:
'type': self._certificate_mode_translation(certificate['type']),
}
try:
may_certify = CourseOverview.get_from_id(course_key).may_certify()
except CourseOverview.DoesNotExist:
may_certify = True
if (
certificate_api.is_passing_status(certificate['status'])
and CourseOverview.get_from_id(course_key).may_certify()
and may_certify
):
completed_runs.append(course_data)
else: