From a6b972549a07c8bca75a286f103e18b2be5d202c Mon Sep 17 00:00:00 2001 From: Renzo Lucioni Date: Thu, 26 May 2016 13:30:59 -0400 Subject: [PATCH] Include raw program data on the detail page Uses the view's ID argument to retrieve and drop unmodified programs API data on the details page. Future work will supplement with CourseOverviews data. Part of ECOM-4415. --- .../learner_dashboard/tests/test_programs.py | 52 +++++++++++++++---- lms/djangoapps/learner_dashboard/views.py | 7 ++- .../learner_dashboard/program_details.html | 4 +- openedx/core/djangoapps/programs/utils.py | 7 ++- 4 files changed, 56 insertions(+), 14 deletions(-) diff --git a/lms/djangoapps/learner_dashboard/tests/test_programs.py b/lms/djangoapps/learner_dashboard/tests/test_programs.py index ad98aed18a..bfbf6737e9 100644 --- a/lms/djangoapps/learner_dashboard/tests/test_programs.py +++ b/lms/djangoapps/learner_dashboard/tests/test_programs.py @@ -2,6 +2,7 @@ Unit tests covering the program listing and detail pages. """ import datetime +import json import unittest from urlparse import urljoin @@ -16,10 +17,11 @@ from provider.constants import CONFIDENTIAL from openedx.core.djangoapps.credentials.models import CredentialsApiConfig from openedx.core.djangoapps.credentials.tests import factories as credentials_factories from openedx.core.djangoapps.credentials.tests.mixins import CredentialsDataMixin, CredentialsApiConfigMixin +from openedx.core.djangoapps.programs.models import ProgramsApiConfig +from openedx.core.djangoapps.programs.tests import factories from openedx.core.djangoapps.programs.tests.mixins import ( ProgramsApiConfigMixin, ProgramsDataMixin) -from openedx.core.djangoapps.programs.models import ProgramsApiConfig from student.models import CourseEnrollment from student.tests.factories import UserFactory from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase @@ -226,25 +228,56 @@ class TestProgramListing( self.assertNotContains(response, certificate['credential_url']) -@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') +@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): """ Unit tests for the program details page """ + program_id = 123 + password = 'test' + def setUp(self): super(TestProgramDetails, self).setUp() - self.user = UserFactory() - self.details_page = reverse('program_details_view', args=['123']) + self.details_page = reverse('program_details_view', args=[self.program_id]) - self.client.login(username=self.user.username, password='test') + self.user = UserFactory() + self.client.login(username=self.user.username, password=self.password) + + ClientFactory(name=ProgramsApiConfig.OAUTH2_CLIENT_NAME, client_type=CONFIDENTIAL) + + self.data = factories.Program( + organizations=[factories.Organization()], + course_codes=[ + factories.CourseCode(run_modes=[factories.RunMode()]), + ] + ) + + def _mock_programs_api(self): + """Helper for mocking out Programs API URLs.""" + self.assertTrue(httpretty.is_enabled(), msg='httpretty must be enabled to mock Programs API calls.') + + url = '{api_root}/programs/{id}/'.format( + api_root=ProgramsApiConfig.current().internal_api_url.strip('/'), + id=self.program_id + ) + body = json.dumps(self.data) + + httpretty.register_uri(httpretty.GET, url, body=body, content_type='application/json') + + def _assert_program_data_present(self, response): + """Verify that program data is present.""" + self.assertContains(response, 'programData') + self.assertContains(response, self.data['name']) def test_login_required(self): """ Verify that login is required to access the page. """ self.create_programs_config() + self._mock_programs_api() self.client.logout() @@ -254,10 +287,10 @@ class TestProgramDetails(ProgramsApiConfigMixin, TestCase): '{}?next={}'.format(reverse('signin_user'), self.details_page) ) - self.client.login(username=self.user.username, password='test') + self.client.login(username=self.user.username, password=self.password) response = self.client.get(self.details_page) - self.assertEquals(response.status_code, 200) + self._assert_program_data_present(response) def test_404_if_disabled(self): """ @@ -271,12 +304,13 @@ class TestProgramDetails(ProgramsApiConfigMixin, TestCase): def test_page_routing(self): """Verify that the page can be hit with or without a program name in the URL.""" self.create_programs_config() + self._mock_programs_api() response = self.client.get(self.details_page) - self.assertEquals(response.status_code, 200) + self._assert_program_data_present(response) response = self.client.get(self.details_page + 'program_name/') - self.assertEquals(response.status_code, 200) + self._assert_program_data_present(response) response = self.client.get(self.details_page + 'program_name/invalid/') self.assertEquals(response.status_code, 404) diff --git a/lms/djangoapps/learner_dashboard/views.py b/lms/djangoapps/learner_dashboard/views.py index 0735934687..0a9a6482fe 100644 --- a/lms/djangoapps/learner_dashboard/views.py +++ b/lms/djangoapps/learner_dashboard/views.py @@ -9,7 +9,7 @@ from django.http import Http404 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_display_category +from openedx.core.djangoapps.programs.utils import ProgramProgressMeter, get_programs, get_display_category from student.views import get_course_enrollments @@ -50,13 +50,16 @@ def view_programs(request): @login_required @require_GET -def program_details(request, program_id): # pylint: disable=unused-argument +def program_details(request, program_id): """View details about a specific program.""" show_program_details = ProgramsApiConfig.current().show_program_details if not show_program_details: raise Http404 + program_data = get_programs(request.user, program_id=program_id) + context = { + 'program_data': program_data, 'nav_hidden': True, 'disable_courseware_js': True, 'uses_pattern_library': True diff --git a/lms/templates/learner_dashboard/program_details.html b/lms/templates/learner_dashboard/program_details.html index 7999fb2a81..2b4b0e9a6d 100644 --- a/lms/templates/learner_dashboard/program_details.html +++ b/lms/templates/learner_dashboard/program_details.html @@ -13,7 +13,9 @@ from openedx.core.djangolib.js_utils import ( <%block name="js_extra"> <%static:require_module module_name="js/learner_dashboard/program_details_factory" class_name="ProgramDetailsFactory"> -ProgramDetailsFactory({}); +ProgramDetailsFactory({ + programData: ${program_data | n, dump_js_escaped_json} +}); diff --git a/openedx/core/djangoapps/programs/utils.py b/openedx/core/djangoapps/programs/utils.py index 080e0cfe5a..afb4a27ad5 100644 --- a/openedx/core/djangoapps/programs/utils.py +++ b/openedx/core/djangoapps/programs/utils.py @@ -10,7 +10,7 @@ from openedx.core.lib.edx_api_utils import get_edx_api_data log = logging.getLogger(__name__) -def get_programs(user): +def get_programs(user, program_id=None): """Given a user, get programs from the Programs service. Returned value is cached depending on user permissions. Staff users making requests against Programs will receive unpublished programs, while regular users will only receive @@ -19,6 +19,9 @@ def get_programs(user): Arguments: user (User): The user to authenticate as when requesting programs. + Keyword Arguments: + program_id (int): Identifies a specific program for which to retrieve data. + Returns: list of dict, representing programs returned by the Programs service. """ @@ -27,7 +30,7 @@ def get_programs(user): # Bypass caching for staff users, who may be creating Programs and want # to see them displayed immediately. cache_key = programs_config.CACHE_KEY if programs_config.is_cache_enabled and not user.is_staff else None - return get_edx_api_data(programs_config, user, 'programs', cache_key=cache_key) + return get_edx_api_data(programs_config, user, 'programs', resource_id=program_id, cache_key=cache_key) def flatten_programs(programs, course_ids):