Commit bbe18f0d by McKenzie Welter Committed by GitHub

Merge pull request #16011 from edx/McKenzieW/learner-2457

Added util method to retrieve individual course_run data from Catalog
parents 53d52964 531a51f7
......@@ -484,9 +484,9 @@ def get_active_web_certificate(course, is_preview_mode=None):
return None
def get_certificate_template(course_key, mode):
def get_certificate_template(course_key, mode, language): # pylint: disable=unused-argument
"""
Retrieves the custom certificate template based on course_key and mode.
Retrieves the custom certificate template based on course_key, mode, and language.
"""
org_id, template = None, None
# fetch organization of the course
......
......@@ -910,6 +910,7 @@ class CertificateGenerationCourseSetting(TimeStampedModel):
defaults=default
)
@classmethod
def is_language_specific_templates_enabled_for_course(cls, course_key):
"""Check whether language-specific certificates are enabled for a course.
......
......@@ -13,7 +13,7 @@ from django.core.urlresolvers import reverse
from django.test.client import Client, RequestFactory
from django.test.utils import override_settings
from util.date_utils import strftime_localized
from mock import patch
from mock import Mock, patch
from nose.plugins.attrib import attr
from certificates.api import get_certificate_url
......@@ -884,11 +884,13 @@ class CertificatesViewsTests(CommonCertificatesTestCase):
@override_settings(FEATURES=FEATURES_WITH_CUSTOM_CERTS_ENABLED)
@override_settings(LANGUAGE_CODE='fr')
def test_certificate_custom_template_with_org_mode_course(self):
@patch('certificates.views.webview.get_course_run_details')
def test_certificate_custom_template_with_org_mode_course(self, mock_get_course_run_details):
"""
Tests custom template search and rendering.
This test should check template matching when org={org}, course={course}, mode={mode}.
"""
mock_get_course_run_details.return_value = {'language': 'en'}
self._add_course_certificates(count=1, signatory_count=2)
self._create_custom_template(org_id=1, mode='honor', course_key=unicode(self.course.id))
self._create_custom_template(org_id=2, mode='honor')
......@@ -913,12 +915,14 @@ class CertificatesViewsTests(CommonCertificatesTestCase):
self.assertContains(response, 'course name: course_title_0')
@override_settings(FEATURES=FEATURES_WITH_CUSTOM_CERTS_ENABLED)
def test_certificate_custom_template_with_org(self):
@patch('certificates.views.webview.get_course_run_details')
def test_certificate_custom_template_with_org(self, mock_get_course_run_details):
"""
Tests custom template search if we have a single template for organization and mode
with course set to Null.
This test should check template matching when org={org}, course=Null, mode={mode}.
"""
mock_get_course_run_details.return_value = {'language': 'en'}
course = CourseFactory.create(
org='cstX', number='cst_22', display_name='custom template course'
)
......@@ -940,11 +944,13 @@ class CertificatesViewsTests(CommonCertificatesTestCase):
self.assertContains(response, 'course name: course_title_0')
@override_settings(FEATURES=FEATURES_WITH_CUSTOM_CERTS_ENABLED)
def test_certificate_custom_template_with_organization(self):
@patch('certificates.views.webview.get_course_run_details')
def test_certificate_custom_template_with_organization(self, mock_get_course_run_details):
"""
Tests custom template search when we have a single template for a organization.
This test should check template matching when org={org}, course=Null, mode=null.
"""
mock_get_course_run_details.return_value = {'language': 'en'}
self._add_course_certificates(count=1, signatory_count=2)
self._create_custom_template(org_id=1, mode='honor')
self._create_custom_template(org_id=1, mode='honor', course_key=self.course.id)
......@@ -962,11 +968,13 @@ class CertificatesViewsTests(CommonCertificatesTestCase):
self.assertEqual(response.status_code, 200)
@override_settings(FEATURES=FEATURES_WITH_CUSTOM_CERTS_ENABLED)
def test_certificate_custom_template_with_course_mode(self):
@patch('certificates.views.webview.get_course_run_details')
def test_certificate_custom_template_with_course_mode(self, mock_get_course_run_details):
"""
Tests custom template search if we have a single template for a course mode.
This test should check template matching when org=null, course=Null, mode={mode}.
"""
mock_get_course_run_details.return_value = {'language': 'en'}
mode = 'honor'
self._add_course_certificates(count=1, signatory_count=2)
self._create_custom_template(mode=mode)
......@@ -982,10 +990,12 @@ class CertificatesViewsTests(CommonCertificatesTestCase):
self.assertContains(response, 'mode: {}'.format(mode))
@ddt.data(True, False)
def test_certificate_custom_template_with_unicode_data(self, custom_certs_enabled):
@patch('certificates.views.webview.get_course_run_details')
def test_certificate_custom_template_with_unicode_data(self, custom_certs_enabled, mock_get_course_run_details):
"""
Tests custom template renders properly with unicode data.
"""
mock_get_course_run_details.return_value = {'language': 'en'}
mode = 'honor'
self._add_course_certificates(count=1, signatory_count=2)
self._create_custom_template(mode=mode)
......@@ -1014,10 +1024,12 @@ class CertificatesViewsTests(CommonCertificatesTestCase):
self.assertContains(response, 'https://twitter.com/intent/tweet')
@override_settings(FEATURES=FEATURES_WITH_CUSTOM_CERTS_ENABLED)
def test_certificate_asset_by_slug(self):
@patch('certificates.views.webview.get_course_run_details')
def test_certificate_asset_by_slug(self, mock_get_course_run_details):
"""
Tests certificate template asset display by slug using static.certificate_asset_url method.
"""
mock_get_course_run_details.return_value = {'language': 'en'}
self._add_course_certificates(count=1, signatory_count=2)
self._create_custom_template(mode='honor')
test_url = get_certificate_url(
......
......@@ -30,6 +30,7 @@ from certificates.api import (
has_html_certificates_enabled
)
from certificates.models import (
CertificateGenerationCourseSetting,
CertificateHtmlViewConfiguration,
CertificateSocialNetworks,
CertificateStatuses,
......@@ -39,6 +40,7 @@ from courseware.access import has_access
from courseware.courses import get_course_by_id
from edxmako.shortcuts import render_to_response
from edxmako.template import Template
from openedx.core.djangoapps.catalog.utils import get_course_run_details
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.lib.courses import course_image_url
from openedx.core.djangoapps.certificates.api import display_date_for_certificate
......@@ -224,7 +226,7 @@ def _update_context_with_basic_info(context, course_id, platform_name, configura
)
def _update_course_context(request, context, course, platform_name):
def _update_course_context(request, context, course, course_key, platform_name):
"""
Updates context dictionary with course info.
"""
......@@ -248,6 +250,11 @@ def _update_course_context(request, context, course, platform_name):
'{partner_short_name}.').format(
partner_short_name=context['organization_short_name'],
platform_name=platform_name)
# If language specific templates are enabled for the course, add course_run specific information to the context
if CertificateGenerationCourseSetting.is_language_specific_templates_enabled_for_course(course_key):
fields = ['start', 'end', 'max_effort', 'language']
course_run_data = get_course_run_details(course_key, fields)
context.update(course_run_data)
def _update_social_context(request, context, course, user, user_certificate, platform_name):
......@@ -413,7 +420,7 @@ def _render_certificate_template(request, context, course, user_certificate):
Picks appropriate certificate templates and renders it.
"""
if settings.FEATURES.get('CUSTOM_CERTIFICATE_TEMPLATES_ENABLED', False):
custom_template = get_certificate_template(course.id, user_certificate.mode)
custom_template = get_certificate_template(course.id, user_certificate.mode, context.get('language'))
if custom_template:
template = Template(
custom_template,
......@@ -571,7 +578,7 @@ def render_html_view(request, user_id, course_id):
_update_organization_context(context, course)
# Append course info
_update_course_context(request, context, course, platform_name)
_update_course_context(request, context, course, course_key, platform_name)
# Append user info
_update_context_with_user_info(context, user, user_certificate)
......
......@@ -118,6 +118,8 @@ class CourseRunFactory(DictFactoryBase):
title = factory.Faker('catch_phrase')
type = 'verified'
uuid = factory.Faker('uuid4')
language = 'en'
max_effort = 5
class CourseFactory(DictFactoryBase):
......
......@@ -15,6 +15,7 @@ from openedx.core.djangoapps.catalog.tests.factories import CourseRunFactory, Pr
from openedx.core.djangoapps.catalog.tests.mixins import CatalogIntegrationMixin
from openedx.core.djangoapps.catalog.utils import (
get_course_runs,
get_course_run_details,
get_program_types,
get_programs,
get_programs_with_type
......@@ -304,3 +305,31 @@ class TestGetCourseRuns(CatalogIntegrationMixin, TestCase):
self.assertTrue(mock_get_edx_api_data.called)
self.assert_contract(mock_get_edx_api_data.call_args)
self.assertEqual(data, catalog_course_runs)
@skip_unless_lms
@mock.patch(UTILS_MODULE + '.get_edx_api_data')
class TestGetCourseRunDetails(CatalogIntegrationMixin, TestCase):
"""
Tests covering retrieval of information about a specific course run from the catalog service.
"""
def setUp(self):
super(TestGetCourseRunDetails, self).setUp()
self.catalog_integration = self.create_catalog_integration(cache_ttl=1)
self.user = UserFactory(username=self.catalog_integration.service_username)
def test_get_course_run_details(self, mock_get_edx_api_data):
"""
Test retrieval of details about a specific course run
"""
course_run = CourseRunFactory()
course_run_details = {
'language': course_run['language'],
'start': course_run['start'],
'end': course_run['end'],
'max_effort': course_run['max_effort']
}
mock_get_edx_api_data.return_value = course_run_details
data = get_course_run_details(course_run['key'], ['language', 'start', 'end', 'max_effort'])
self.assertTrue(mock_get_edx_api_data.called)
self.assertEqual(data, course_run_details)
......@@ -185,3 +185,39 @@ def get_course_runs():
course_runs = get_edx_api_data(catalog_integration, 'course_runs', api=api, querystring=querystring)
return course_runs
def get_course_run_details(course_run_key, fields):
"""
Retrieve information about the course run with the given id
Arguments:
course_run_key: key for the course_run about which we are retrieving information
Returns:
dict with language, start date, end date, and max_effort details about specified course run
"""
catalog_integration = CatalogIntegration.current()
course_run_details = dict()
if catalog_integration.enabled:
try:
user = catalog_integration.get_service_user()
except ObjectDoesNotExist:
msg = 'Catalog service user {} does not exist. Data for course_run {} will not be retrieved'.format(
catalog_integration.service_username,
course_run_key
)
logger.error(msg)
return course_run_details
api = create_catalog_api_client(user)
cache_key = '{base}.course_runs'.format(base=catalog_integration.CACHE_KEY)
course_run_details = get_edx_api_data(catalog_integration, 'course_runs', api, resource_id=course_run_key,
cache_key=cache_key, many=False, traverse_pagination=False, fields=fields)
else:
msg = 'Unable to retrieve details about course_run {} because Catalog Integration is not enabled'.format(
course_run_key
)
logger.error(msg)
return course_run_details
......@@ -15,8 +15,20 @@ from openedx.core.lib.token_utils import JwtBuilder
log = logging.getLogger(__name__)
def get_fields(fields, response):
"""Extracts desired fields from the API response"""
results = {}
for field in fields:
try:
results[field] = response[field]
# TODO: Determine what exception would be raised here if response does not have the specified field
except:
msg = '{resource} does not have the attribute {field}'.format(resource, field)
log.exception(msg)
def get_edx_api_data(api_config, resource, api, resource_id=None, querystring=None, cache_key=None, many=True,
traverse_pagination=True):
traverse_pagination=True, fields=None):
"""GET data from an edX REST API.
DRY utility for handling caching and pagination.
......@@ -59,7 +71,10 @@ def get_edx_api_data(api_config, resource, api, resource_id=None, querystring=No
response = endpoint(resource_id).get(**querystring)
if resource_id is not None:
results = response
if fields:
results = get_fields(fields, response)
else:
results = response
elif traverse_pagination:
results = _traverse_pagination(response, endpoint, querystring, no_data)
else:
......
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