Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
531a51f7
Commit
531a51f7
authored
7 years ago
by
McKenzie Welter
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added util method to retrieve individual course_run data from Catalog
parent
53d52964
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
116 additions
and
14 deletions
+116
-14
lms/djangoapps/certificates/api.py
+2
-2
lms/djangoapps/certificates/models.py
+1
-0
lms/djangoapps/certificates/tests/test_webview_views.py
+19
-7
lms/djangoapps/certificates/views/webview.py
+10
-3
openedx/core/djangoapps/catalog/tests/factories.py
+2
-0
openedx/core/djangoapps/catalog/tests/test_utils.py
+29
-0
openedx/core/djangoapps/catalog/utils.py
+36
-0
openedx/core/lib/edx_api_utils.py
+17
-2
No files found.
lms/djangoapps/certificates/api.py
View file @
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 mod
e.
Retrieves the custom certificate template based on course_key
, mode, and languag
e.
"""
org_id
,
template
=
None
,
None
# fetch organization of the course
...
...
This diff is collapsed.
Click to expand it.
lms/djangoapps/certificates/models.py
View file @
531a51f7
...
...
@@ -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.
...
...
This diff is collapsed.
Click to expand it.
lms/djangoapps/certificates/tests/test_webview_views.py
View file @
531a51f7
...
...
@@ -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
(
...
...
This diff is collapsed.
Click to expand it.
lms/djangoapps/certificates/views/webview.py
View file @
531a51f7
...
...
@@ -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
)
...
...
This diff is collapsed.
Click to expand it.
openedx/core/djangoapps/catalog/tests/factories.py
View file @
531a51f7
...
...
@@ -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
):
...
...
This diff is collapsed.
Click to expand it.
openedx/core/djangoapps/catalog/tests/test_utils.py
View file @
531a51f7
...
...
@@ -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
)
This diff is collapsed.
Click to expand it.
openedx/core/djangoapps/catalog/utils.py
View file @
531a51f7
...
...
@@ -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
This diff is collapsed.
Click to expand it.
openedx/core/lib/edx_api_utils.py
View file @
531a51f7
...
...
@@ -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
:
...
...
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment