Display programs from all categories on the student dashboard
Removes most remaining hardcoded references to XSeries from the LMS. Part of ECOM-4638.
This commit is contained in:
@@ -2,10 +2,11 @@
|
||||
"""Helper functions for working with Programs."""
|
||||
import datetime
|
||||
import logging
|
||||
from urlparse import urljoin
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils import timezone
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.text import slugify
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
import pytz
|
||||
@@ -52,63 +53,6 @@ def get_programs(user, program_id=None):
|
||||
return get_edx_api_data(programs_config, user, 'programs', resource_id=program_id, cache_key=cache_key)
|
||||
|
||||
|
||||
def flatten_programs(programs, course_ids):
|
||||
"""Flatten the result returned by the Programs API.
|
||||
|
||||
Arguments:
|
||||
programs (list): Serialized programs
|
||||
course_ids (list): Course IDs to key on.
|
||||
|
||||
Returns:
|
||||
dict, programs keyed by course ID
|
||||
"""
|
||||
flattened = {}
|
||||
|
||||
for program in programs:
|
||||
try:
|
||||
for course_code in program['course_codes']:
|
||||
for run in course_code['run_modes']:
|
||||
run_id = run['course_key']
|
||||
if run_id in course_ids:
|
||||
flattened.setdefault(run_id, []).append(program)
|
||||
except KeyError:
|
||||
log.exception('Unable to parse Programs API response: %r', program)
|
||||
|
||||
return flattened
|
||||
|
||||
|
||||
def get_programs_for_dashboard(user, course_keys):
|
||||
"""Build a dictionary of programs, keyed by course.
|
||||
|
||||
Given a user and an iterable of course keys, find all the programs relevant
|
||||
to the user's dashboard and return them in a dictionary keyed by course key.
|
||||
|
||||
Arguments:
|
||||
user (User): The user to authenticate as when requesting programs.
|
||||
course_keys (list): List of course keys representing the courses in which
|
||||
the given user has active enrollments.
|
||||
|
||||
Returns:
|
||||
dict, containing programs keyed by course. Empty if programs cannot be retrieved.
|
||||
"""
|
||||
programs_config = ProgramsApiConfig.current()
|
||||
course_programs = {}
|
||||
|
||||
if not programs_config.is_student_dashboard_enabled:
|
||||
log.debug('Display of programs on the student dashboard is disabled.')
|
||||
return course_programs
|
||||
|
||||
programs = get_programs(user)
|
||||
if not programs:
|
||||
log.debug('No programs found for the user with ID %d.', user.id)
|
||||
return course_programs
|
||||
|
||||
course_ids = [unicode(c) for c in course_keys]
|
||||
course_programs = flatten_programs(programs, course_ids)
|
||||
|
||||
return course_programs
|
||||
|
||||
|
||||
def get_programs_for_credentials(user, programs_credentials):
|
||||
""" Given a user and an iterable of credentials, get corresponding programs
|
||||
data and return it as a list of dictionaries.
|
||||
@@ -137,24 +81,71 @@ def get_programs_for_credentials(user, programs_credentials):
|
||||
return certificate_programs
|
||||
|
||||
|
||||
def get_program_detail_url(program, marketing_root):
|
||||
"""Construct the URL to be used when linking to program details.
|
||||
def get_programs_by_run(programs, enrollments):
|
||||
"""Intersect programs and enrollments.
|
||||
|
||||
Builds a dictionary of program dict lists keyed by course ID. The resulting dictionary
|
||||
is suitable for use in applications where programs must be filtered by the course
|
||||
runs they contain (e.g., student dashboard).
|
||||
|
||||
Arguments:
|
||||
program (dict): Representation of a program.
|
||||
marketing_root (str): Root URL used to build links to program marketing pages.
|
||||
programs (list): Containing dictionaries representing programs.
|
||||
enrollments (list): Enrollments from which course IDs to key on can be extracted.
|
||||
|
||||
Returns:
|
||||
str, a link to program details
|
||||
tuple, dict of programs keyed by course ID and list of course IDs themselves
|
||||
"""
|
||||
if ProgramsApiConfig.current().show_program_details:
|
||||
base = reverse('program_details_view', kwargs={'program_id': program['id']}).rstrip('/')
|
||||
slug = slugify(program['name'])
|
||||
else:
|
||||
base = marketing_root.rstrip('/')
|
||||
slug = program['marketing_slug']
|
||||
programs_by_run = {}
|
||||
# enrollment.course_id is really a course key (╯ಠ_ಠ)╯︵ ┻━┻
|
||||
course_ids = [unicode(e.course_id) for e in enrollments]
|
||||
|
||||
return '{base}/{slug}'.format(base=base, slug=slug)
|
||||
for program in programs:
|
||||
for course_code in program['course_codes']:
|
||||
for run in course_code['run_modes']:
|
||||
run_id = run['course_key']
|
||||
if run_id in course_ids:
|
||||
program_list = programs_by_run.setdefault(run_id, list())
|
||||
if program not in program_list:
|
||||
program_list.append(program)
|
||||
|
||||
# Sort programs by name for consistent presentation.
|
||||
for program_list in programs_by_run.itervalues():
|
||||
program_list.sort(key=lambda p: p['name'])
|
||||
|
||||
return programs_by_run, course_ids
|
||||
|
||||
|
||||
def get_program_marketing_url(programs_config):
|
||||
"""Build a URL to be used when linking to program details on a marketing site."""
|
||||
return urljoin(settings.MKTG_URLS.get('ROOT'), programs_config.marketing_path).rstrip('/')
|
||||
|
||||
|
||||
def attach_program_detail_url(programs):
|
||||
"""Extend program representations by attaching a URL to be used when linking to program details.
|
||||
|
||||
Facilitates the building of context to be passed to templates containing program data.
|
||||
|
||||
Arguments:
|
||||
programs (list): Containing dicts representing programs.
|
||||
|
||||
Returns:
|
||||
list, containing extended program dicts
|
||||
"""
|
||||
programs_config = ProgramsApiConfig.current()
|
||||
marketing_url = get_program_marketing_url(programs_config)
|
||||
|
||||
for program in programs:
|
||||
if programs_config.show_program_details:
|
||||
base = reverse('program_details_view', kwargs={'program_id': program['id']}).rstrip('/')
|
||||
slug = slugify(program['name'])
|
||||
else:
|
||||
# TODO: Remove. Learners should always be sent to the LMS' program details page.
|
||||
base = marketing_url
|
||||
slug = program['marketing_slug']
|
||||
|
||||
program['detail_url'] = '{base}/{slug}'.format(base=base, slug=slug)
|
||||
|
||||
return programs
|
||||
|
||||
|
||||
def get_completed_courses(student):
|
||||
@@ -182,35 +173,40 @@ class ProgramProgressMeter(object):
|
||||
|
||||
Arguments:
|
||||
user (User): The user for which to find programs.
|
||||
|
||||
Keyword Arguments:
|
||||
enrollments (list): List of the user's enrollments.
|
||||
"""
|
||||
def __init__(self, user):
|
||||
def __init__(self, user, enrollments=None):
|
||||
self.user = user
|
||||
self.enrollments = enrollments
|
||||
self.course_ids = None
|
||||
self.course_certs = None
|
||||
|
||||
self.programs = get_programs(self.user)
|
||||
self.course_certs = get_completed_courses(self.user)
|
||||
self.programs = attach_program_detail_url(get_programs(self.user))
|
||||
|
||||
@cached_property
|
||||
def engaged_programs(self):
|
||||
def engaged_programs(self, by_run=False):
|
||||
"""Derive a list of programs in which the given user is engaged.
|
||||
|
||||
Returns:
|
||||
list of program dicts, ordered by most recent enrollment.
|
||||
list of program dicts, ordered by most recent enrollment,
|
||||
or dict of programs, keyed by course ID.
|
||||
"""
|
||||
enrollments = CourseEnrollment.enrollments_for_user(self.user)
|
||||
enrollments = sorted(enrollments, key=lambda e: e.created, reverse=True)
|
||||
# enrollment.course_id is really a course key ಠ_ಠ
|
||||
self.course_ids = [unicode(e.course_id) for e in enrollments]
|
||||
self.enrollments = self.enrollments or list(CourseEnrollment.enrollments_for_user(self.user))
|
||||
self.enrollments.sort(key=lambda e: e.created, reverse=True)
|
||||
|
||||
flattened = flatten_programs(self.programs, self.course_ids)
|
||||
programs_by_run, self.course_ids = get_programs_by_run(self.programs, self.enrollments)
|
||||
|
||||
engaged_programs = []
|
||||
if by_run:
|
||||
return programs_by_run
|
||||
|
||||
programs = []
|
||||
for course_id in self.course_ids:
|
||||
for program in flattened.get(course_id, []):
|
||||
if program not in engaged_programs:
|
||||
engaged_programs.append(program)
|
||||
for program in programs_by_run.get(course_id, []):
|
||||
if program not in programs:
|
||||
programs.append(program)
|
||||
|
||||
return engaged_programs
|
||||
return programs
|
||||
|
||||
@property
|
||||
def progress(self):
|
||||
@@ -221,7 +217,7 @@ class ProgramProgressMeter(object):
|
||||
towards completing a program.
|
||||
"""
|
||||
progress = []
|
||||
for program in self.engaged_programs:
|
||||
for program in self.engaged_programs():
|
||||
completed, in_progress, not_started = [], [], []
|
||||
|
||||
for course_code in program['course_codes']:
|
||||
@@ -277,6 +273,8 @@ class ProgramProgressMeter(object):
|
||||
Returns:
|
||||
bool, whether the course code is complete.
|
||||
"""
|
||||
self.course_certs = self.course_certs or get_completed_courses(self.user)
|
||||
|
||||
return any(self._parse(run_mode) in self.course_certs for run_mode in course_code['run_modes'])
|
||||
|
||||
def _is_course_code_in_progress(self, course_code):
|
||||
|
||||
Reference in New Issue
Block a user