Merge pull request #12599 from edx/renzo/supplement-program-data
Supplement program data with course and enrollment data
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
"""
|
||||
Stub implementation of programs service for acceptance tests
|
||||
"""
|
||||
|
||||
import re
|
||||
import urlparse
|
||||
|
||||
from .http import StubHttpRequestHandler, StubHttpService
|
||||
|
||||
|
||||
@@ -11,10 +11,13 @@ class StubProgramsServiceHandler(StubHttpRequestHandler): # pylint: disable=mis
|
||||
|
||||
def do_GET(self): # pylint: disable=invalid-name, missing-docstring
|
||||
pattern_handlers = {
|
||||
"/api/v1/programs/$": self.get_programs_list,
|
||||
r'/api/v1/programs/$': self.get_programs_list,
|
||||
r'/api/v1/programs/(\d+)/$': self.get_program_details,
|
||||
}
|
||||
|
||||
if self.match_pattern(pattern_handlers):
|
||||
return
|
||||
|
||||
self.send_response(404, content="404 Not Found")
|
||||
|
||||
def match_pattern(self, pattern_handlers):
|
||||
@@ -25,7 +28,7 @@ class StubProgramsServiceHandler(StubHttpRequestHandler): # pylint: disable=mis
|
||||
for pattern in pattern_handlers:
|
||||
match = re.match(pattern, path)
|
||||
if match:
|
||||
pattern_handlers[pattern](**match.groupdict())
|
||||
pattern_handlers[pattern](*match.groups())
|
||||
return True
|
||||
return None
|
||||
|
||||
@@ -36,6 +39,13 @@ class StubProgramsServiceHandler(StubHttpRequestHandler): # pylint: disable=mis
|
||||
programs = self.server.config.get('programs', [])
|
||||
self.send_json_response(programs)
|
||||
|
||||
def get_program_details(self, program_id):
|
||||
"""
|
||||
Stubs a program details endpoint.
|
||||
"""
|
||||
program = self.server.config.get('programs.{}'.format(program_id), [])
|
||||
self.send_json_response(program)
|
||||
|
||||
|
||||
class StubProgramsService(StubHttpService): # pylint: disable=missing-docstring
|
||||
HANDLER_CLASS = StubProgramsServiceHandler
|
||||
|
||||
@@ -1,49 +1,31 @@
|
||||
"""
|
||||
Tools to create programs-related data for use in bok choy tests.
|
||||
"""
|
||||
from collections import namedtuple
|
||||
import json
|
||||
|
||||
import requests
|
||||
|
||||
from . import PROGRAMS_STUB_URL
|
||||
from .config import ConfigModelFixture
|
||||
from openedx.core.djangoapps.programs.tests import factories
|
||||
|
||||
|
||||
FakeProgram = namedtuple('FakeProgram', ['name', 'status', 'org_key', 'course_id'])
|
||||
|
||||
|
||||
class ProgramsFixture(object):
|
||||
"""
|
||||
Interface to set up mock responses from the Programs stub server.
|
||||
"""
|
||||
|
||||
def install_programs(self, fake_programs):
|
||||
"""
|
||||
Sets the response data for the programs list endpoint.
|
||||
|
||||
At present, `fake_programs` must be a iterable of FakeProgram named tuples.
|
||||
"""
|
||||
programs = []
|
||||
for program in fake_programs:
|
||||
run_mode = factories.RunMode(course_key=program.course_id)
|
||||
course_code = factories.CourseCode(run_modes=[run_mode])
|
||||
org = factories.Organization(key=program.org_key)
|
||||
|
||||
program = factories.Program(
|
||||
name=program.name,
|
||||
status=program.status,
|
||||
organizations=[org],
|
||||
course_codes=[course_code]
|
||||
)
|
||||
programs.append(program)
|
||||
|
||||
api_result = {'results': programs}
|
||||
def install_programs(self, programs, is_list=True):
|
||||
"""Sets the response data for Programs API endpoints."""
|
||||
if is_list:
|
||||
key = 'programs'
|
||||
api_result = {'results': programs}
|
||||
else:
|
||||
program = programs[0]
|
||||
key = 'programs.{}'.format(program['id'])
|
||||
api_result = program
|
||||
|
||||
requests.put(
|
||||
'{}/set_config'.format(PROGRAMS_STUB_URL),
|
||||
data={'programs': json.dumps(api_result)},
|
||||
data={key: json.dumps(api_result)},
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ class ProgramListingPage(PageObject):
|
||||
|
||||
class ProgramDetailsPage(PageObject):
|
||||
"""Program details page."""
|
||||
url = BASE_URL + '/dashboard/programs/123/program-name/'
|
||||
program_id = 123
|
||||
url = BASE_URL + '/dashboard/programs/{}/program-name/'.format(program_id)
|
||||
|
||||
def is_browser_on_page(self):
|
||||
return self.q(css='.js-program-details-wrapper').present
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
"""Acceptance tests for LMS-hosted Programs pages"""
|
||||
from nose.plugins.attrib import attr
|
||||
|
||||
from ...fixtures.programs import FakeProgram, ProgramsFixture, ProgramsConfigMixin
|
||||
from ...fixtures.programs import ProgramsFixture, ProgramsConfigMixin
|
||||
from ...fixtures.course import CourseFixture
|
||||
from ..helpers import UniqueCourseTest
|
||||
from ...pages.lms.auto_auth import AutoAuthPage
|
||||
from ...pages.lms.programs import ProgramListingPage, ProgramDetailsPage
|
||||
from openedx.core.djangoapps.programs.tests import factories
|
||||
|
||||
|
||||
class ProgramPageBase(ProgramsConfigMixin, UniqueCourseTest):
|
||||
@@ -15,16 +16,33 @@ class ProgramPageBase(ProgramsConfigMixin, UniqueCourseTest):
|
||||
|
||||
self.set_programs_api_configuration(is_enabled=True)
|
||||
|
||||
def stub_api(self, course_id=None):
|
||||
"""Stub out the programs API with fake data."""
|
||||
name = 'Fake Program'
|
||||
status = 'active'
|
||||
org_key = self.course_info['org']
|
||||
def create_program(self, program_id=None, course_id=None):
|
||||
"""DRY helper for creating test program data."""
|
||||
course_id = course_id if course_id else self.course_id
|
||||
|
||||
ProgramsFixture().install_programs([
|
||||
FakeProgram(name=name, status=status, org_key=org_key, course_id=course_id),
|
||||
])
|
||||
run_mode = factories.RunMode(course_key=course_id)
|
||||
course_code = factories.CourseCode(run_modes=[run_mode])
|
||||
org = factories.Organization(key=self.course_info['org'])
|
||||
|
||||
if program_id:
|
||||
program = factories.Program(
|
||||
id=program_id,
|
||||
status='active',
|
||||
organizations=[org],
|
||||
course_codes=[course_code]
|
||||
)
|
||||
else:
|
||||
program = factories.Program(
|
||||
status='active',
|
||||
organizations=[org],
|
||||
course_codes=[course_code]
|
||||
)
|
||||
|
||||
return program
|
||||
|
||||
def stub_api(self, programs, is_list=True):
|
||||
"""Stub out the programs API with fake data."""
|
||||
ProgramsFixture().install_programs(programs, is_list=is_list)
|
||||
|
||||
def auth(self, enroll=True):
|
||||
"""Authenticate, enrolling the user in the configured course if requested."""
|
||||
@@ -43,8 +61,10 @@ class ProgramListingPageTest(ProgramPageBase):
|
||||
|
||||
def test_no_enrollments(self):
|
||||
"""Verify that no cards appear when the user has no enrollments."""
|
||||
self.stub_api()
|
||||
program = self.create_program()
|
||||
self.stub_api([program])
|
||||
self.auth(enroll=False)
|
||||
|
||||
self.listing_page.visit()
|
||||
|
||||
self.assertTrue(self.listing_page.is_sidebar_present)
|
||||
@@ -59,8 +79,11 @@ class ProgramListingPageTest(ProgramPageBase):
|
||||
self.course_info['run'],
|
||||
'other_run'
|
||||
)
|
||||
self.stub_api(course_id=course_id)
|
||||
|
||||
program = self.create_program(course_id=course_id)
|
||||
self.stub_api([program])
|
||||
self.auth()
|
||||
|
||||
self.listing_page.visit()
|
||||
|
||||
self.assertTrue(self.listing_page.is_sidebar_present)
|
||||
@@ -71,8 +94,10 @@ class ProgramListingPageTest(ProgramPageBase):
|
||||
Verify that cards appear when the user has enrollments
|
||||
which are included in at least one active program.
|
||||
"""
|
||||
self.stub_api()
|
||||
program = self.create_program()
|
||||
self.stub_api([program])
|
||||
self.auth()
|
||||
|
||||
self.listing_page.visit()
|
||||
|
||||
self.assertTrue(self.listing_page.is_sidebar_present)
|
||||
@@ -87,9 +112,11 @@ class ProgramListingPageA11yTest(ProgramPageBase):
|
||||
|
||||
self.listing_page = ProgramListingPage(self.browser)
|
||||
|
||||
program = self.create_program()
|
||||
self.stub_api([program])
|
||||
|
||||
def test_empty_a11y(self):
|
||||
"""Test a11y of the page's empty state."""
|
||||
self.stub_api()
|
||||
self.auth(enroll=False)
|
||||
self.listing_page.visit()
|
||||
|
||||
@@ -100,7 +127,6 @@ class ProgramListingPageA11yTest(ProgramPageBase):
|
||||
|
||||
def test_cards_a11y(self):
|
||||
"""Test a11y when program cards are present."""
|
||||
self.stub_api()
|
||||
self.auth()
|
||||
self.listing_page.visit()
|
||||
|
||||
@@ -118,9 +144,12 @@ class ProgramDetailsPageA11yTest(ProgramPageBase):
|
||||
|
||||
self.details_page = ProgramDetailsPage(self.browser)
|
||||
|
||||
program = self.create_program(program_id=self.details_page.program_id)
|
||||
self.stub_api([program], is_list=False)
|
||||
|
||||
def test_a11y(self):
|
||||
"""Test a11y of the page's state."""
|
||||
self.auth(enroll=False)
|
||||
"""Test the page's a11y compliance."""
|
||||
self.auth()
|
||||
self.details_page.visit()
|
||||
|
||||
self.details_page.a11y_audit.check_for_accessibility_errors()
|
||||
|
||||
@@ -8,7 +8,7 @@ from uuid import uuid4
|
||||
|
||||
from ...fixtures import PROGRAMS_STUB_URL
|
||||
from ...fixtures.config import ConfigModelFixture
|
||||
from ...fixtures.programs import FakeProgram, ProgramsFixture, ProgramsConfigMixin
|
||||
from ...fixtures.programs import ProgramsFixture, ProgramsConfigMixin
|
||||
from ...pages.studio.auto_auth import AutoAuthPage
|
||||
from ...pages.studio.library import LibraryEditPage
|
||||
from ...pages.studio.index import DashboardPage, DashboardPageWithPrograms
|
||||
@@ -17,6 +17,7 @@ from ..helpers import (
|
||||
select_option_by_text,
|
||||
get_selected_option_text
|
||||
)
|
||||
from openedx.core.djangoapps.programs.tests import factories
|
||||
|
||||
|
||||
class CreateLibraryTest(WebAppTest):
|
||||
@@ -111,11 +112,24 @@ class DashboardProgramsTabTest(ProgramsConfigMixin, WebAppTest):
|
||||
via config, and the results of the program list should display when
|
||||
the list is nonempty.
|
||||
"""
|
||||
test_program_values = [
|
||||
FakeProgram(name='first program', status='unpublished', org_key='org1', course_id='foo/bar/baz'),
|
||||
FakeProgram(name='second program', status='unpublished', org_key='org2', course_id='qux/quux/corge'),
|
||||
test_program_values = [('first program', 'org1'), ('second program', 'org2')]
|
||||
|
||||
programs = [
|
||||
factories.Program(
|
||||
name=name,
|
||||
organizations=[
|
||||
factories.Organization(key=org),
|
||||
],
|
||||
course_codes=[
|
||||
factories.CourseCode(run_modes=[
|
||||
factories.RunMode(),
|
||||
]),
|
||||
]
|
||||
)
|
||||
for name, org in test_program_values
|
||||
]
|
||||
ProgramsFixture().install_programs(test_program_values)
|
||||
|
||||
ProgramsFixture().install_programs(programs)
|
||||
|
||||
self.set_programs_api_configuration(True)
|
||||
|
||||
@@ -126,8 +140,7 @@ class DashboardProgramsTabTest(ProgramsConfigMixin, WebAppTest):
|
||||
self.assertFalse(self.dashboard_page.is_empty_list_create_button_present())
|
||||
|
||||
results = self.dashboard_page.get_program_list()
|
||||
expected = [(p.name, p.org_key) for p in test_program_values]
|
||||
self.assertEqual(results, expected)
|
||||
self.assertEqual(results, test_program_values)
|
||||
|
||||
def test_tab_requires_staff(self):
|
||||
"""
|
||||
|
||||
@@ -24,7 +24,7 @@ from openedx.core.djangoapps.programs.tests.mixins import (
|
||||
ProgramsDataMixin)
|
||||
from student.models import CourseEnrollment
|
||||
from student.tests.factories import UserFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
|
||||
@@ -231,13 +231,18 @@ class TestProgramListing(
|
||||
@httpretty.activate
|
||||
@override_settings(MKTG_URLS={'ROOT': 'http://edx.org'})
|
||||
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
class TestProgramDetails(ProgramsApiConfigMixin, TestCase):
|
||||
class TestProgramDetails(ProgramsApiConfigMixin, SharedModuleStoreTestCase):
|
||||
"""
|
||||
Unit tests for the program details page
|
||||
"""
|
||||
program_id = 123
|
||||
password = 'test'
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestProgramDetails, cls).setUpClass()
|
||||
cls.course = CourseFactory()
|
||||
|
||||
def setUp(self):
|
||||
super(TestProgramDetails, self).setUp()
|
||||
|
||||
@@ -248,11 +253,12 @@ class TestProgramDetails(ProgramsApiConfigMixin, TestCase):
|
||||
|
||||
ClientFactory(name=ProgramsApiConfig.OAUTH2_CLIENT_NAME, client_type=CONFIDENTIAL)
|
||||
|
||||
self.organization = factories.Organization()
|
||||
self.run_mode = factories.RunMode(course_key=unicode(self.course.id)) # pylint: disable=no-member
|
||||
self.course_code = factories.CourseCode(run_modes=[self.run_mode])
|
||||
self.data = factories.Program(
|
||||
organizations=[factories.Organization()],
|
||||
course_codes=[
|
||||
factories.CourseCode(run_modes=[factories.RunMode()]),
|
||||
]
|
||||
organizations=[self.organization],
|
||||
course_codes=[self.course_code]
|
||||
)
|
||||
|
||||
def _mock_programs_api(self):
|
||||
|
||||
@@ -3,13 +3,13 @@ from urlparse import urljoin
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.views.decorators.http import require_GET
|
||||
from django.http import Http404
|
||||
from django.views.decorators.http import require_GET
|
||||
|
||||
from edxmako.shortcuts import render_to_response
|
||||
from openedx.core.djangoapps.credentials.utils import get_programs_credentials
|
||||
from openedx.core.djangoapps.programs.models import ProgramsApiConfig
|
||||
from openedx.core.djangoapps.programs.utils import ProgramProgressMeter, get_programs, get_display_category
|
||||
from openedx.core.djangoapps.programs import utils
|
||||
from student.views import get_course_enrollments
|
||||
|
||||
|
||||
@@ -22,13 +22,13 @@ def view_programs(request):
|
||||
raise Http404
|
||||
|
||||
enrollments = list(get_course_enrollments(request.user, None, []))
|
||||
meter = ProgramProgressMeter(request.user, enrollments)
|
||||
meter = utils.ProgramProgressMeter(request.user, enrollments)
|
||||
programs = meter.engaged_programs
|
||||
|
||||
# TODO: Pull 'xseries' string from configuration model.
|
||||
marketing_root = urljoin(settings.MKTG_URLS.get('ROOT'), 'xseries').strip('/')
|
||||
for program in programs:
|
||||
program['display_category'] = get_display_category(program)
|
||||
program['display_category'] = utils.get_display_category(program)
|
||||
program['marketing_url'] = '{root}/{slug}'.format(
|
||||
root=marketing_root,
|
||||
slug=program['marketing_slug']
|
||||
@@ -56,7 +56,8 @@ def program_details(request, program_id):
|
||||
if not show_program_details:
|
||||
raise Http404
|
||||
|
||||
program_data = get_programs(request.user, program_id=program_id)
|
||||
program_data = utils.get_programs(request.user, program_id=program_id)
|
||||
program_data = utils.supplement_program_data(program_data, request.user)
|
||||
|
||||
context = {
|
||||
'program_data': program_data,
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
"""Tests covering Programs utilities."""
|
||||
import copy
|
||||
import datetime
|
||||
import json
|
||||
from unittest import skipUnless
|
||||
|
||||
import ddt
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test import TestCase
|
||||
from django.utils import timezone
|
||||
import httpretty
|
||||
import mock
|
||||
from nose.plugins.attrib import attr
|
||||
@@ -12,6 +17,7 @@ from edx_oauth2_provider.tests.factories import ClientFactory
|
||||
from provider.constants import CONFIDENTIAL
|
||||
|
||||
from lms.djangoapps.certificates.api import MODES
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
from openedx.core.djangoapps.credentials.tests import factories as credentials_factories
|
||||
from openedx.core.djangoapps.credentials.tests.mixins import CredentialsApiConfigMixin, CredentialsDataMixin
|
||||
from openedx.core.djangoapps.programs import utils
|
||||
@@ -20,6 +26,8 @@ from openedx.core.djangoapps.programs.tests import factories
|
||||
from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin, ProgramsDataMixin
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
|
||||
from student.tests.factories import UserFactory, CourseEnrollmentFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
|
||||
UTILS_MODULE = 'openedx.core.djangoapps.programs.utils'
|
||||
@@ -597,3 +605,78 @@ class TestProgramProgressMeter(ProgramsApiConfigMixin, TestCase):
|
||||
meter,
|
||||
factories.Progress(id=program['id'], completed=self._extract_names(program, 0))
|
||||
)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
||||
class TestSupplementProgramData(ProgramsApiConfigMixin, ModuleStoreTestCase):
|
||||
"""Tests of the utility function used to supplement program data."""
|
||||
password = 'test'
|
||||
human_friendly_format = '%x'
|
||||
maxDiff = None
|
||||
|
||||
def setUp(self):
|
||||
super(TestSupplementProgramData, self).setUp()
|
||||
|
||||
self.user = UserFactory()
|
||||
self.client.login(username=self.user.username, password=self.password)
|
||||
|
||||
ClientFactory(name=ProgramsApiConfig.OAUTH2_CLIENT_NAME, client_type=CONFIDENTIAL)
|
||||
|
||||
self.course = CourseFactory()
|
||||
self.course.start = timezone.now() - datetime.timedelta(days=1)
|
||||
self.course.end = timezone.now() + datetime.timedelta(days=1)
|
||||
self.course = self.update_course(self.course, self.user.id) # pylint: disable=no-member
|
||||
|
||||
self.organization = factories.Organization()
|
||||
self.run_mode = factories.RunMode(course_key=unicode(self.course.id)) # pylint: disable=no-member
|
||||
self.course_code = factories.CourseCode(run_modes=[self.run_mode])
|
||||
self.program = factories.Program(
|
||||
organizations=[self.organization],
|
||||
course_codes=[self.course_code]
|
||||
)
|
||||
|
||||
def _assert_supplemented(self, actual, is_enrolled=False, is_enrollment_open=True):
|
||||
"""DRY helper used to verify that program data is extended correctly."""
|
||||
course_overview = CourseOverview.get_from_id(self.course.id) # pylint: disable=no-member
|
||||
|
||||
run_mode = factories.RunMode(
|
||||
course_key=unicode(self.course.id), # pylint: disable=no-member
|
||||
course_url=reverse('course_root', args=[self.course.id]), # pylint: disable=no-member
|
||||
course_image_url=course_overview.course_image_url,
|
||||
start_date=self.course.start.strftime(self.human_friendly_format),
|
||||
end_date=self.course.end.strftime(self.human_friendly_format),
|
||||
is_enrolled=is_enrolled,
|
||||
is_enrollment_open=is_enrollment_open,
|
||||
marketing_url='',
|
||||
)
|
||||
course_code = factories.CourseCode(display_name=self.course_code['display_name'], run_modes=[run_mode])
|
||||
expected = copy.deepcopy(self.program)
|
||||
expected['course_codes'] = [course_code]
|
||||
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
@ddt.data(True, False)
|
||||
def test_student_enrollment_status(self, is_enrolled):
|
||||
"""Verify that program data is supplemented correctly."""
|
||||
if is_enrolled:
|
||||
CourseEnrollmentFactory(user=self.user, course_id=self.course.id) # pylint: disable=no-member
|
||||
|
||||
data = utils.supplement_program_data(self.program, self.user)
|
||||
|
||||
self._assert_supplemented(data, is_enrolled=is_enrolled)
|
||||
|
||||
@ddt.data(
|
||||
[1, 1, False],
|
||||
[1, -1, True],
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_course_enrollment_status(self, start_offset, end_offset, is_enrollment_open):
|
||||
"""Verify that course enrollment status is reflected correctly."""
|
||||
self.course.enrollment_start = timezone.now() - datetime.timedelta(days=start_offset)
|
||||
self.course.enrollment_end = timezone.now() - datetime.timedelta(days=end_offset)
|
||||
self.course = self.update_course(self.course, self.user.id) # pylint: disable=no-member
|
||||
|
||||
data = utils.supplement_program_data(self.program, self.user)
|
||||
|
||||
self._assert_supplemented(data, is_enrollment_open=is_enrollment_open)
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Helper functions for working with Programs."""
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils import timezone
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
import pytz
|
||||
|
||||
from lms.djangoapps.certificates.api import get_certificates_for_user, is_passing_status
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
from openedx.core.djangoapps.programs.models import ProgramsApiConfig
|
||||
from openedx.core.lib.edx_api_utils import get_edx_api_data
|
||||
from student.models import CourseEnrollment
|
||||
from xmodule.course_metadata_utils import DEFAULT_START_DATE
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -275,3 +284,37 @@ class ProgramProgressMeter(object):
|
||||
}
|
||||
|
||||
return parsed
|
||||
|
||||
|
||||
def supplement_program_data(program_data, user):
|
||||
"""Supplement program course codes with CourseOverview and CourseEnrollment data.
|
||||
|
||||
Arguments:
|
||||
program_data (dict): Representation of a program.
|
||||
user (User): The user whose enrollments to inspect.
|
||||
"""
|
||||
for course_code in program_data['course_codes']:
|
||||
for run_mode in course_code['run_modes']:
|
||||
course_key = CourseKey.from_string(run_mode['course_key'])
|
||||
course_overview = CourseOverview.get_from_id(course_key)
|
||||
|
||||
run_mode['course_url'] = reverse('course_root', args=[course_key])
|
||||
run_mode['course_image_url'] = course_overview.course_image_url
|
||||
|
||||
human_friendly_format = '%x'
|
||||
start_date = course_overview.start or DEFAULT_START_DATE
|
||||
end_date = course_overview.end or datetime.datetime.max.replace(tzinfo=pytz.UTC)
|
||||
run_mode['start_date'] = start_date.strftime(human_friendly_format)
|
||||
run_mode['end_date'] = end_date.strftime(human_friendly_format)
|
||||
|
||||
run_mode['is_enrolled'] = CourseEnrollment.is_enrolled(user, course_key)
|
||||
|
||||
enrollment_start = course_overview.enrollment_start or datetime.datetime.min.replace(tzinfo=pytz.UTC)
|
||||
enrollment_end = course_overview.enrollment_end or datetime.datetime.max.replace(tzinfo=pytz.UTC)
|
||||
is_enrollment_open = enrollment_start <= timezone.now() < enrollment_end
|
||||
run_mode['is_enrollment_open'] = is_enrollment_open
|
||||
|
||||
# TODO: Currently unavailable on LMS.
|
||||
run_mode['marketing_url'] = ''
|
||||
|
||||
return program_data
|
||||
|
||||
Reference in New Issue
Block a user