Remove credential listing from program list view

The new design for the program detail page introduced a sidebar which includes a program-specific listing of credentials. This is an improvement over the old list of credentials found on the program list page and is meant to replace it. The new detail page is stable, so the credential listing on the program list page can be removed.

LEARNER-492
This commit is contained in:
Renzo Lucioni
2017-05-22 11:40:41 -04:00
parent 78d8895880
commit 2955a6e639
17 changed files with 66 additions and 614 deletions

View File

@@ -4,7 +4,6 @@ Unit tests covering the program listing and detail pages.
"""
import json
import re
import unittest
from urlparse import urljoin
from uuid import uuid4
@@ -16,8 +15,6 @@ import mock
from openedx.core.djangoapps.catalog.tests.factories import ProgramFactory, CourseFactory, CourseRunFactory
from openedx.core.djangoapps.catalog.tests.mixins import CatalogIntegrationMixin
from openedx.core.djangoapps.credentials.tests.factories import UserCredential, ProgramCredential
from openedx.core.djangoapps.credentials.tests.mixins import CredentialsApiConfigMixin
from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin
from openedx.core.djangolib.testing.utils import skip_unless_lms
from student.tests.factories import UserFactory, CourseEnrollmentFactory
@@ -25,15 +22,13 @@ from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory as ModuleStoreCourseFactory
CATALOG_UTILS_MODULE = 'openedx.core.djangoapps.catalog.utils'
CREDENTIALS_UTILS_MODULE = 'openedx.core.djangoapps.credentials.utils'
PROGRAMS_UTILS_MODULE = 'openedx.core.djangoapps.programs.utils'
@skip_unless_lms
@override_settings(MKTG_URLS={'ROOT': 'https://www.example.com'})
@mock.patch(PROGRAMS_UTILS_MODULE + '.get_programs')
class TestProgramListing(ProgramsApiConfigMixin, CredentialsApiConfigMixin, SharedModuleStoreTestCase):
class TestProgramListing(ProgramsApiConfigMixin, SharedModuleStoreTestCase):
"""Unit tests for the program listing page."""
maxDiff = None
password = 'test'
@@ -65,15 +60,6 @@ class TestProgramListing(ProgramsApiConfigMixin, CredentialsApiConfigMixin, Shar
"""
return program['title']
def credential_sort_key(self, credential):
"""
Helper function used to sort dictionaries representing credentials.
"""
try:
return credential['certificate_url']
except KeyError:
return credential['credential_url']
def load_serialized_data(self, response, key):
"""
Extract and deserialize serialized data from the response.
@@ -184,48 +170,6 @@ class TestProgramListing(ProgramsApiConfigMixin, CredentialsApiConfigMixin, Shar
expected_url = reverse('program_details_view', kwargs={'program_uuid': expected_program['uuid']})
self.assertEqual(actual_program['detail_url'], expected_url)
@mock.patch(CREDENTIALS_UTILS_MODULE + '.get_credentials')
@mock.patch(CREDENTIALS_UTILS_MODULE + '.get_programs')
def test_certificates_listed(self, mock_get_programs, mock_get_credentials, __):
"""
Verify that the response contains accurate certificate data when certificates are available.
"""
self.create_programs_config()
self.create_credentials_config(is_learner_issuance_enabled=True)
mock_get_programs.return_value = self.data
first_credential = UserCredential(
username=self.user.username,
credential=ProgramCredential(
program_uuid=self.first_program['uuid']
)
)
second_credential = UserCredential(
username=self.user.username,
credential=ProgramCredential(
program_uuid=self.second_program['uuid']
)
)
credentials_data = sorted([first_credential, second_credential], key=self.credential_sort_key)
mock_get_credentials.return_value = credentials_data
response = self.client.get(self.url)
actual = self.load_serialized_data(response, 'certificatesData')
actual = sorted(actual, key=self.credential_sort_key)
self.assertEqual(len(actual), len(credentials_data))
for index, actual_credential in enumerate(actual):
expected_credential = credentials_data[index]
self.assertEqual(
# TODO: certificate_url is needlessly transformed to credential_url. (╯°□°)╯︵ ┻━┻
# Clean this up!
actual_credential['credential_url'],
expected_credential['certificate_url']
)
@skip_unless_lms
@mock.patch(PROGRAMS_UTILS_MODULE + '.get_programs')

View File

@@ -7,7 +7,6 @@ from django.views.decorators.http import require_GET
from edxmako.shortcuts import render_to_response
from lms.djangoapps.learner_dashboard.utils import strip_course_id, FAKE_COURSE_KEY
from openedx.core.djangoapps.catalog.utils import get_programs
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 (
get_program_marketing_url,
@@ -29,7 +28,6 @@ def program_listing(request):
meter = ProgramProgressMeter(request.user)
context = {
'credentials': get_programs_credentials(request.user),
'disable_courseware_js': True,
'marketing_url': get_program_marketing_url(programs_config),
'nav_hidden': True,

View File

@@ -1,39 +0,0 @@
(function(define) {
'use strict';
define(['backbone',
'jquery',
'underscore',
'gettext',
'text!../../../templates/learner_dashboard/certificate.underscore'
],
function(
Backbone,
$,
_,
gettext,
certificateTpl
) {
return Backbone.View.extend({
el: '.certificates-list',
tpl: _.template(certificateTpl),
initialize: function(data) {
this.context = data.context;
this.render();
},
render: function() {
var certificatesData = this.context.certificatesData || [];
if (certificatesData.length) {
this.$el.html(this.tpl(this.context));
} else {
/**
* If not rendering remove el because
* styles are applied to it
*/
this.remove();
}
}
});
}
);
}).call(this, define || RequireJS.define);

View File

@@ -6,7 +6,6 @@
'underscore',
'gettext',
'js/learner_dashboard/views/explore_new_programs_view',
'js/learner_dashboard/views/certificate_view',
'text!../../../templates/learner_dashboard/sidebar.underscore'
],
function(
@@ -15,7 +14,6 @@
_,
gettext,
NewProgramsView,
CertificateView,
sidebarTpl
) {
return Backbone.View.extend({
@@ -36,10 +34,6 @@
this.newProgramsView = new NewProgramsView({
context: this.context
});
this.newCertificateView = new CertificateView({
context: this.context
});
}
});
}

View File

@@ -1,62 +0,0 @@
define([
'backbone',
'jquery',
'js/learner_dashboard/views/certificate_view'
], function(Backbone, $, CertificateView) {
'use strict';
describe('Certificate View', function() {
var view = null,
data = {
context: {
certificatesData: [
{
'display_name': 'Testing',
'credential_url': 'https://credentials.stage.edx.org/credentials/dummy-uuid-1/'
},
{
'display_name': 'Testing2',
'credential_url': 'https://credentials.stage.edx.org/credentials/dummy-uuid-2/'
}
],
sampleCertImageSrc: '/images/programs/sample-cert.png'
}
};
beforeEach(function() {
setFixtures('<div class="certificates-list"></div>');
view = new CertificateView(data);
view.render();
});
afterEach(function() {
view.remove();
});
it('should exist', function() {
expect(view).toBeDefined();
});
it('should load the certificates based on passed in certificates list', function() {
var $certificates = view.$el.find('.certificate-link');
expect($certificates.length).toBe(2);
$certificates.each(function(index, el) {
expect($(el).html().trim()).toEqual(data.context.certificatesData[index].display_name);
expect($(el).attr('href')).toEqual(data.context.certificatesData[index].credential_url);
});
expect(view.$el.find('.hd-6').html().trim()).toEqual('Program Certificates');
expect(view.$el.find('img').attr('src')).toEqual(data.context.sampleCertImageSrc);
});
it('should display no certificate box if certificates list is empty', function() {
view.remove();
setFixtures('<div class="certificates-list"></div>');
view = new CertificateView({
context: {certificatesData: []}
});
view.render();
expect(view.$('.certificates-list').length).toBe(0);
});
});
}
);

View File

@@ -9,14 +9,7 @@ define([
describe('Sidebar View', function() {
var view = null,
context = {
marketingUrl: 'https://www.example.org/programs',
certificatesData: [
{
'display_name': 'Testing',
'credential_url': 'https://credentials.example.com/credentials/uuid/'
}
],
sampleCertImageSrc: '/images/programs/sample-cert.png'
marketingUrl: 'https://www.example.org/programs'
};
beforeEach(function() {
@@ -44,21 +37,16 @@ define([
expect($sidebar.find('.program-advertise .ad-link a').attr('href')).toEqual(context.marketingUrl);
});
it('should load the certificates based on passed in certificates list', function() {
expect(view.$('.certificate-link').length).toBe(1);
});
it('should not load the advertising panel if no marketing URL is provided', function() {
var $ad;
view.remove();
view = new SidebarView({
el: '.sidebar',
context: {certificatesData: []}
context: {}
});
view.render();
$ad = view.$el.find('.program-advertise');
expect($ad.length).toBe(0);
expect(view.$('.certificate-link').length).toBe(0);
});
});
}

View File

@@ -737,7 +737,6 @@
'js/spec/instructor_dashboard/certificates_spec.js',
'js/spec/instructor_dashboard/ecommerce_spec.js',
'js/spec/instructor_dashboard/student_admin_spec.js',
'js/spec/learner_dashboard/certificate_view_spec.js',
'js/spec/learner_dashboard/collection_list_view_spec.js',
'js/spec/learner_dashboard/program_card_view_spec.js',
'js/spec/learner_dashboard/sidebar_view_spec.js',

View File

@@ -43,27 +43,6 @@
margin-bottom: $baseline;
}
}
.certificate-container {
.hd-6 {
color: palette(grayscale, dark);
font-weight: font-weight(normal);
margin-bottom: $baseline;
}
.certificate-link {
padding-top: $baseline;
color: palette(primary, base);
display: block;
&:active,
&:focus,
&:hover {
color: $link-hover;
text-decoration: underline;
}
}
}
}
.empty-programs-message {

View File

@@ -1,7 +0,0 @@
<div class="certificate-container">
<h2 class="hd-6"><%- gettext('Program Certificates') %></h2>
<img src="<%- sampleCertImageSrc %>" alt="">
<% _.each(certificatesData, function(certificate){ %>
<a class="certificate-link" href="<%- gettext(certificate.credential_url) %>"><%- gettext(certificate.display_name) %></a>
<% }); %>
</div>

View File

@@ -15,10 +15,8 @@ from openedx.core.djangolib.js_utils import (
<%block name="js_extra">
<%static:require_module module_name="js/learner_dashboard/program_list_factory" class_name="ProgramListFactory">
ProgramListFactory({
certificatesData: ${credentials | n, dump_js_escaped_json},
marketingUrl: '${marketing_url | n, js_escaped_string}',
programsData: ${programs | n, dump_js_escaped_json},
sampleCertImageSrc: '${static.url('images/programs/sample-cert.png') | n, js_escaped_string}',
userProgress: ${progress | n, dump_js_escaped_json}
});
</%static:require_module>

View File

@@ -1,2 +1 @@
<aside class="aside program-advertise"></aside>
<aside class="aside certificates-list"></aside>

View File

@@ -35,6 +35,10 @@ def generate_zulu_datetime():
class DictFactoryBase(factory.Factory):
"""
Subclass this to make factories that can be used to produce fake API response
bodies for testing.
"""
class Meta(object):
model = dict

View File

@@ -1,43 +1,31 @@
"""Factories for generating fake credentials-related data."""
import uuid
# pylint: disable=missing-docstring, invalid-name
from functools import partial
import factory
from factory.fuzzy import FuzzyText
from openedx.core.djangoapps.catalog.tests.factories import (
generate_instances,
generate_course_run_key,
DictFactoryBase,
)
class UserCredential(factory.Factory):
"""Factory for stubbing user credentials resources from the User Credentials
API (v1).
"""
class Meta(object):
model = dict
id = factory.Sequence(lambda n: n) # pylint: disable=invalid-name
username = FuzzyText(prefix='user_')
status = 'awarded'
uuid = FuzzyText(prefix='uuid_')
certificate_url = FuzzyText(prefix='https://www.example.com/credentials/')
credential = {}
class ProgramCredential(DictFactoryBase):
credential_id = factory.Faker('random_int')
program_uuid = factory.Faker('uuid4')
class ProgramCredential(factory.Factory):
"""Factory for stubbing program credentials resources from the Program
Credentials API (v1).
"""
class Meta(object):
model = dict
credential_id = factory.Sequence(lambda n: n)
program_uuid = factory.LazyAttribute(lambda obj: str(uuid.uuid4()))
class CourseCredential(factory.Factory):
"""Factory for stubbing course credentials resources from the Course
Credentials API (v1).
"""
class Meta(object):
model = dict
course_id = 'edx/test01/2015'
credential_id = factory.Sequence(lambda n: n)
class CourseCredential(DictFactoryBase):
credential_id = factory.Faker('random_int')
course_id = factory.LazyFunction(generate_course_run_key)
certificate_type = 'verified'
class UserCredential(DictFactoryBase):
id = factory.Faker('random_int')
username = factory.Faker('word')
status = 'awarded'
uuid = factory.Faker('uuid4')
certificate_url = factory.Faker('url')
credential = factory.LazyFunction(partial(generate_instances, ProgramCredential, count=1))

View File

@@ -27,94 +27,3 @@ class CredentialsApiConfigMixin(object):
CredentialsApiConfig(**fields).save()
return CredentialsApiConfig.current()
class CredentialsDataMixin(object):
"""Mixin mocking Credentials API URLs and providing fake data for testing."""
CREDENTIALS_API_RESPONSE = {
"next": None,
"results": [
factories.UserCredential(
id=1,
username='test',
credential=factories.ProgramCredential()
),
factories.UserCredential(
id=2,
username='test',
credential=factories.ProgramCredential()
),
factories.UserCredential(
id=3,
status='revoked',
username='test',
credential=factories.ProgramCredential()
),
factories.UserCredential(
id=4,
username='test',
credential=factories.CourseCredential(
certificate_type='honor'
)
),
factories.UserCredential(
id=5,
username='test',
credential=factories.CourseCredential(
course_id='edx/test02/2015'
)
),
factories.UserCredential(
id=6,
username='test',
credential=factories.CourseCredential(
course_id='edx/test02/2015'
)
),
]
}
CREDENTIALS_NEXT_API_RESPONSE = {
"next": None,
"results": [
factories.UserCredential(
id=7,
username='test',
credential=factories.ProgramCredential()
),
factories.UserCredential(
id=8,
username='test',
credential=factories.ProgramCredential()
)
]
}
def mock_credentials_api(self, user, data=None, status_code=200, reset_url=True, is_next_page=False):
"""Utility for mocking out Credentials API URLs."""
self.assertTrue(httpretty.is_enabled(), msg='httpretty must be enabled to mock Credentials API calls.')
internal_api_url = CredentialsApiConfig.current().internal_api_url.strip('/')
url = internal_api_url + '/credentials/?status=awarded&username=' + user.username
if reset_url:
httpretty.reset()
if data is None:
data = self.CREDENTIALS_API_RESPONSE
body = json.dumps(data)
if is_next_page:
next_page_url = internal_api_url + '/credentials/?page=2&status=awarded&username=' + user.username
self.CREDENTIALS_NEXT_API_RESPONSE['next'] = next_page_url
next_page_body = json.dumps(self.CREDENTIALS_NEXT_API_RESPONSE)
httpretty.register_uri(
httpretty.GET, next_page_url, body=body, content_type='application/json', status=status_code
)
httpretty.register_uri(
httpretty.GET, url, body=next_page_body, content_type='application/json', status=status_code
)
else:
httpretty.register_uri(
httpretty.GET, url, body=body, content_type='application/json', status=status_code
)

View File

@@ -1,218 +1,70 @@
"""Tests covering Credentials utilities."""
import uuid
from django.core.cache import cache
from edx_oauth2_provider.tests.factories import ClientFactory
import httpretty
import mock
from nose.plugins.attrib import attr
from provider.constants import CONFIDENTIAL
from openedx.core.djangoapps.catalog.tests.factories import ProgramFactory
from openedx.core.djangoapps.credentials.models import CredentialsApiConfig
from openedx.core.djangoapps.credentials.tests.mixins import CredentialsApiConfigMixin, CredentialsDataMixin
from openedx.core.djangoapps.credentials.utils import (
get_credentials,
get_user_program_credentials,
get_programs_credentials,
get_programs_for_credentials
)
from openedx.core.djangoapps.credentials.tests.mixins import CredentialsApiConfigMixin
from openedx.core.djangoapps.credentials.utils import get_credentials
from openedx.core.djangoapps.credentials.tests import factories
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
from student.tests.factories import UserFactory
UTILS_MODULE = 'openedx.core.djangoapps.credentials.utils'
@skip_unless_lms
@attr(shard=2)
class TestCredentialsRetrieval(CredentialsApiConfigMixin, CredentialsDataMixin, CacheIsolationTestCase):
""" Tests covering the retrieval of user credentials from the Credentials
service.
"""
@mock.patch(UTILS_MODULE + '.get_edx_api_data')
class TestGetCredentials(CredentialsApiConfigMixin, CacheIsolationTestCase):
ENABLED_CACHES = ['default']
def setUp(self):
super(TestCredentialsRetrieval, self).setUp()
super(TestGetCredentials, self).setUp()
ClientFactory(name=CredentialsApiConfig.OAUTH2_CLIENT_NAME, client_type=CONFIDENTIAL)
self.user = UserFactory()
self.primary_uuid = str(uuid.uuid4())
self.alternate_uuid = str(uuid.uuid4())
cache.clear()
def expected_credentials_display_data(self, programs):
""" Returns expected credentials data to be represented. """
return [
{
'display_name': programs[0]['title'],
'subtitle': programs[0]['subtitle'],
'credential_url': programs[0]['credential_url']
},
{
'display_name': programs[1]['title'],
'subtitle': programs[1]['subtitle'],
'credential_url': programs[1]['credential_url']
}
]
@httpretty.activate
def test_get_credentials(self):
"""Verify user credentials data can be retrieve."""
self.create_credentials_config()
self.mock_credentials_api(self.user)
self.user = UserFactory()
def test_get_many(self, mock_get_edx_api_data):
expected = factories.UserCredential.create_batch(3)
mock_get_edx_api_data.return_value = expected
actual = get_credentials(self.user)
self.assertEqual(actual, self.CREDENTIALS_API_RESPONSE['results'])
@httpretty.activate
def test_get_credentials_caching(self):
"""Verify that when enabled, the cache is used for non-staff users."""
self.create_credentials_config(cache_ttl=1)
self.mock_credentials_api(self.user)
mock_get_edx_api_data.assert_called_once()
call = mock_get_edx_api_data.mock_calls[0]
__, __, kwargs = call
# Warm up the cache.
get_credentials(self.user)
# Hit the cache.
get_credentials(self.user)
# Verify only one request was made.
self.assertEqual(len(httpretty.httpretty.latest_requests), 1)
staff_user = UserFactory(is_staff=True)
# Hit the Credentials API twice.
for _ in range(2):
get_credentials(staff_user)
# Verify that three requests have been made (one for student, two for staff).
self.assertEqual(len(httpretty.httpretty.latest_requests), 3)
def test_get_user_program_credentials_issuance_disable(self):
"""Verify that user program credentials cannot be retrieved if issuance is disabled."""
self.create_credentials_config(enable_learner_issuance=False)
actual = get_user_program_credentials(self.user)
self.assertEqual(actual, [])
@httpretty.activate
def test_get_user_program_credentials_no_credential(self):
"""Verify behavior if no credential exist."""
self.create_credentials_config()
self.mock_credentials_api(self.user, data={'results': []})
actual = get_user_program_credentials(self.user)
self.assertEqual(actual, [])
@httpretty.activate
def test_get_user_programs_credentials(self):
"""Verify program credentials data can be retrieved and parsed correctly."""
# create credentials and program configuration
self.create_credentials_config()
# Mocking the API responses from programs and credentials
primary_uuid, alternate_uuid = str(uuid.uuid4()), str(uuid.uuid4())
credentials_api_response = {
"next": None,
"results": [
factories.UserCredential(
username='test',
credential=factories.ProgramCredential(program_uuid=primary_uuid)
),
factories.UserCredential(
username='test',
credential=factories.ProgramCredential(program_uuid=alternate_uuid)
)
]
querystring = {
'username': self.user.username,
'status': 'awarded',
}
self.mock_credentials_api(self.user, data=credentials_api_response, reset_url=False)
programs = [
ProgramFactory(uuid=primary_uuid), ProgramFactory(uuid=alternate_uuid)
]
self.assertEqual(kwargs['querystring'], querystring)
with mock.patch("openedx.core.djangoapps.credentials.utils.get_programs_for_credentials") as mock_get_programs:
mock_get_programs.return_value = programs
actual = get_user_program_credentials(self.user)
self.assertEqual(actual, expected)
# checking response from API is as expected
self.assertEqual(len(actual), 2)
self.assertEqual(actual, programs)
def test_get_one(self, mock_get_edx_api_data):
expected = factories.UserCredential()
mock_get_edx_api_data.return_value = expected
@httpretty.activate
def test_get_programs_credentials(self):
""" Verify that the program credentials data required for display can
be retrieved.
"""
# create credentials and program configuration
self.create_credentials_config()
program_uuid = str(uuid.uuid4())
actual = get_credentials(self.user, program_uuid=program_uuid)
# Mocking the API responses from programs and credentials
primary_uuid, alternate_uuid = str(uuid.uuid4()), str(uuid.uuid4())
credentials_api_response = {
"next": None,
"results": [
factories.UserCredential(
username='test',
credential=factories.ProgramCredential(program_uuid=primary_uuid)
),
factories.UserCredential(
username='test',
credential=factories.ProgramCredential(program_uuid=alternate_uuid)
)
]
mock_get_edx_api_data.assert_called_once()
call = mock_get_edx_api_data.mock_calls[0]
__, __, kwargs = call
querystring = {
'username': self.user.username,
'status': 'awarded',
'program_uuid': program_uuid,
}
self.mock_credentials_api(self.user, data=credentials_api_response, reset_url=False)
programs = [
ProgramFactory(uuid=primary_uuid), ProgramFactory(uuid=alternate_uuid)
]
self.assertEqual(kwargs['querystring'], querystring)
with mock.patch("openedx.core.djangoapps.credentials.utils.get_programs") as mock_get_programs:
mock_get_programs.return_value = programs
actual = get_programs_credentials(self.user)
expected = self.expected_credentials_display_data(programs)
# Checking result is as expected
self.assertEqual(len(actual), 2)
self.assertEqual(actual, expected)
def _expected_program_credentials_data(self):
"""
Dry method for getting expected program credentials response data.
"""
return [
factories.UserCredential(
username='test',
credential=factories.ProgramCredential(
program_uuid=self.primary_uuid
)
),
factories.UserCredential(
username='test',
credential=factories.ProgramCredential(
program_uuid=self.alternate_uuid
)
)
]
def test_get_program_for_certificates(self):
"""Verify programs data can be retrieved and parsed correctly for certificates."""
programs = [
ProgramFactory(uuid=self.primary_uuid),
ProgramFactory(uuid=self.alternate_uuid)
]
program_credentials_data = self._expected_program_credentials_data()
with mock.patch("openedx.core.djangoapps.credentials.utils.get_programs") as patched_get_programs:
patched_get_programs.return_value = programs
actual = get_programs_for_credentials(program_credentials_data)
self.assertEqual(len(actual), 2)
self.assertEqual(actual, programs)
def test_get_program_for_certificates_no_data(self):
"""Verify behavior when no programs data is found for the user."""
program_credentials_data = self._expected_program_credentials_data()
with mock.patch("openedx.core.djangoapps.credentials.utils.get_programs") as patched_get_programs:
patched_get_programs.return_value = []
actual = get_programs_for_credentials(program_credentials_data)
self.assertEqual(actual, [])
self.assertEqual(actual, expected)

View File

@@ -1,18 +1,13 @@
"""Helper functions for working with Credentials."""
from __future__ import unicode_literals
import logging
from django.conf import settings
from edx_rest_api_client.client import EdxRestApiClient
from openedx.core.djangoapps.catalog.utils import get_programs
from openedx.core.djangoapps.credentials.models import CredentialsApiConfig
from openedx.core.lib.edx_api_utils import get_edx_api_data
from openedx.core.lib.token_utils import JwtBuilder
log = logging.getLogger(__name__)
def get_credentials_api_client(user):
""" Returns an authenticated Credentials API client. """
@@ -53,89 +48,3 @@ def get_credentials(user, program_uuid=None):
return get_edx_api_data(
credential_configuration, 'credentials', api=api, querystring=querystring, cache_key=cache_key
)
def get_programs_for_credentials(programs_credentials):
""" Given a user and an iterable of credentials, get corresponding programs
data and return it as a list of dictionaries.
Arguments:
programs_credentials (list): List of credentials awarded to the user
for completion of a program.
Returns:
list, containing programs dictionaries.
"""
certified_programs = []
programs = get_programs()
for program in programs:
for credential in programs_credentials:
if program['uuid'] == credential['credential']['program_uuid']:
program['credential_url'] = credential['certificate_url']
certified_programs.append(program)
return certified_programs
def get_user_program_credentials(user):
"""Given a user, get the list of all program credentials earned and returns
list of dictionaries containing related programs data.
Arguments:
user (User): The user object for getting programs credentials.
Returns:
list, containing programs dictionaries.
"""
programs_credentials_data = []
credential_configuration = CredentialsApiConfig.current()
if not credential_configuration.is_learner_issuance_enabled:
log.debug('Display of certificates for programs is disabled.')
return programs_credentials_data
credentials = get_credentials(user)
if not credentials:
log.info('No credential earned by the given user.')
return programs_credentials_data
programs_credentials = []
for credential in credentials:
try:
if 'program_uuid' in credential['credential']:
programs_credentials.append(credential)
except KeyError:
log.exception('Invalid credential structure: %r', credential)
if programs_credentials:
programs_credentials_data = get_programs_for_credentials(programs_credentials)
return programs_credentials_data
def get_programs_credentials(user):
"""Return program credentials data required for display.
Given a user, find all programs for which certificates have been earned
and return list of dictionaries of required program data.
Arguments:
user (User): user object for getting programs credentials.
Returns:
list of dict, containing data corresponding to the programs for which
the user has been awarded a credential.
"""
programs_credentials = get_user_program_credentials(user)
credentials_data = []
for program in programs_credentials:
try:
program_data = {
'display_name': program['title'],
'subtitle': program['subtitle'],
'credential_url': program['credential_url'],
}
credentials_data.append(program_data)
except KeyError:
log.warning('Program structure is invalid: %r', program)
return credentials_data

View File

@@ -19,7 +19,6 @@ from django.test import RequestFactory, TestCase, override_settings
from django.conf import settings
from django.contrib import sites
from nose.plugins import Plugin
from waffle.models import Switch
from request_cache.middleware import RequestCache