Commit 72dd85ce by Renzo Lucioni

Modify program progress meter for use on detail pages

The meter now supports use with a single program, and can return serialized courses instead of counts when measuring progress.

ECOM-6602
parent 4baf18fe
...@@ -34,7 +34,7 @@ def program_listing(request): ...@@ -34,7 +34,7 @@ def program_listing(request):
'marketing_url': get_program_marketing_url(programs_config), 'marketing_url': get_program_marketing_url(programs_config),
'nav_hidden': True, 'nav_hidden': True,
'programs': meter.engaged_programs, 'programs': meter.engaged_programs,
'progress': meter.progress, 'progress': meter.progress(),
'show_program_listing': programs_config.enabled, 'show_program_listing': programs_config.enabled,
'uses_pattern_library': True, 'uses_pattern_library': True,
} }
...@@ -50,7 +50,9 @@ def program_details(request, program_uuid): ...@@ -50,7 +50,9 @@ def program_details(request, program_uuid):
if not programs_config.enabled: if not programs_config.enabled:
raise Http404 raise Http404
program_data = get_programs(uuid=program_uuid) meter = ProgramProgressMeter(request.user, uuid=program_uuid)
program_data = meter.programs[0]
if not program_data: if not program_data:
raise Http404 raise Http404
...@@ -65,7 +67,6 @@ def program_details(request, program_uuid): ...@@ -65,7 +67,6 @@ def program_details(request, program_uuid):
} }
context = { context = {
'program_data': program_data,
'urls': urls, 'urls': urls,
'show_program_listing': programs_config.enabled, 'show_program_listing': programs_config.enabled,
'nav_hidden': True, 'nav_hidden': True,
...@@ -75,6 +76,16 @@ def program_details(request, program_uuid): ...@@ -75,6 +76,16 @@ def program_details(request, program_uuid):
} }
if waffle.switch_is_active('new_program_progress'): if waffle.switch_is_active('new_program_progress'):
course_progress = meter.progress(programs=[program_data], count_only=False)[0]
program_data.pop('courses')
context.update({
'program_data': program_data,
'course_progress': course_progress,
})
return render_to_response('learner_dashboard/program_details_2017.html', context) return render_to_response('learner_dashboard/program_details_2017.html', context)
else: else:
context.update({'program_data': program_data})
return render_to_response('learner_dashboard/program_details.html', context) return render_to_response('learner_dashboard/program_details.html', context)
...@@ -53,7 +53,7 @@ class TestProgramProgressMeter(TestCase): ...@@ -53,7 +53,7 @@ class TestProgramProgressMeter(TestCase):
def _assert_progress(self, meter, *progresses): def _assert_progress(self, meter, *progresses):
"""Variadic helper used to verify progress calculations.""" """Variadic helper used to verify progress calculations."""
self.assertEqual(meter.progress, list(progresses)) self.assertEqual(meter.progress(), list(progresses))
def _attach_detail_url(self, programs): def _attach_detail_url(self, programs):
"""Add expected detail URLs to a list of program dicts.""" """Add expected detail URLs to a list of program dicts."""
...@@ -113,6 +113,39 @@ class TestProgramProgressMeter(TestCase): ...@@ -113,6 +113,39 @@ class TestProgramProgressMeter(TestCase):
) )
self.assertEqual(meter.completed_programs, []) self.assertEqual(meter.completed_programs, [])
def test_course_progress(self, mock_get_programs):
"""
Verify that the progress meter can represent progress in terms of
serialized courses.
"""
course_run_key = generate_course_run_key()
data = [
ProgramFactory(
courses=[
CourseFactory(course_runs=[
CourseRunFactory(key=course_run_key),
]),
]
)
]
mock_get_programs.return_value = data
self._create_enrollments(course_run_key)
meter = ProgramProgressMeter(self.user)
program = data[0]
expected = [
ProgressFactory(
uuid=program['uuid'],
completed=[],
in_progress=[program['courses'][0]],
not_started=[]
)
]
self.assertEqual(meter.progress(count_only=False), expected)
def test_mutiple_program_engagement(self, mock_get_programs): def test_mutiple_program_engagement(self, mock_get_programs):
""" """
Verify that correct programs are returned in the correct order when the Verify that correct programs are returned in the correct order when the
......
...@@ -57,8 +57,11 @@ class ProgramProgressMeter(object): ...@@ -57,8 +57,11 @@ class ProgramProgressMeter(object):
Keyword Arguments: Keyword Arguments:
enrollments (list): List of the user's enrollments. enrollments (list): List of the user's enrollments.
uuid (str): UUID identifying a specific program. If provided, the meter
will only inspect this one program, not all programs the user may be
engaged with.
""" """
def __init__(self, user, enrollments=None): def __init__(self, user, enrollments=None, uuid=None):
self.user = user self.user = user
self.enrollments = enrollments or list(CourseEnrollment.enrollments_for_user(self.user)) self.enrollments = enrollments or list(CourseEnrollment.enrollments_for_user(self.user))
...@@ -67,7 +70,10 @@ class ProgramProgressMeter(object): ...@@ -67,7 +70,10 @@ class ProgramProgressMeter(object):
# enrollment.course_id is really a CourseKey (╯ಠ_ಠ)╯︵ ┻━┻ # enrollment.course_id is really a CourseKey (╯ಠ_ಠ)╯︵ ┻━┻
self.course_run_ids = [unicode(e.course_id) for e in self.enrollments] self.course_run_ids = [unicode(e.course_id) for e in self.enrollments]
self.programs = attach_program_detail_url(get_programs()) if uuid:
self.programs = [get_programs(uuid=uuid)]
else:
self.programs = attach_program_detail_url(get_programs())
def invert_programs(self): def invert_programs(self):
"""Intersect programs and enrollments. """Intersect programs and enrollments.
...@@ -119,31 +125,39 @@ class ProgramProgressMeter(object): ...@@ -119,31 +125,39 @@ class ProgramProgressMeter(object):
return programs return programs
@property def progress(self, programs=None, count_only=True):
def progress(self):
"""Gauge a user's progress towards program completion. """Gauge a user's progress towards program completion.
Keyword Arguments:
programs (list): Specific list of programs to check the user's progress
against. If left unspecified, self.engaged_programs will be used.
count_only (bool): Whether or not to return counts of completed, in
progress, and unstarted courses instead of serialized representations
of the courses.
Returns: Returns:
list of dict, each containing information about a user's progress list of dict, each containing information about a user's progress
towards completing a program. towards completing a program.
""" """
progress = [] progress = []
for program in self.engaged_programs: programs = programs or self.engaged_programs
completed, in_progress, not_started = 0, 0, 0 for program in programs:
completed, in_progress, not_started = [], [], []
for course in program['courses']: for course in program['courses']:
if self._is_course_complete(course): if self._is_course_complete(course):
completed += 1 completed.append(course)
elif self._is_course_in_progress(course): elif self._is_course_in_progress(course):
in_progress += 1 in_progress.append(course)
else: else:
not_started += 1 not_started.append(course)
progress.append({ progress.append({
'uuid': program['uuid'], 'uuid': program['uuid'],
'completed': completed, 'completed': len(completed) if count_only else completed,
'in_progress': in_progress, 'in_progress': len(in_progress) if count_only else in_progress,
'not_started': not_started, 'not_started': len(not_started) if count_only else not_started,
}) })
return progress return progress
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment