Commit 149412d2 by Simon Chen

ECOM-3198 Add banner image to the program listing program cards

parent b5b7ea78
...@@ -919,6 +919,7 @@ class DashboardTestXSeriesPrograms(ModuleStoreTestCase, ProgramsApiConfigMixin): ...@@ -919,6 +919,7 @@ class DashboardTestXSeriesPrograms(ModuleStoreTestCase, ProgramsApiConfigMixin):
self.course_3 = CourseFactory.create() self.course_3 = CourseFactory.create()
self.program_name = 'Testing Program' self.program_name = 'Testing Program'
self.category = 'xseries' self.category = 'xseries'
self.display_category = 'XSeries'
CourseModeFactory.create( CourseModeFactory.create(
course_id=self.course_1.id, course_id=self.course_1.id,
...@@ -938,6 +939,7 @@ class DashboardTestXSeriesPrograms(ModuleStoreTestCase, ProgramsApiConfigMixin): ...@@ -938,6 +939,7 @@ class DashboardTestXSeriesPrograms(ModuleStoreTestCase, ProgramsApiConfigMixin):
programs[unicode(course)] = [{ programs[unicode(course)] = [{
'id': _id, 'id': _id,
'category': self.category, 'category': self.category,
'display_category': self.display_category,
'organization': {'display_name': 'Test Organization 1', 'key': 'edX'}, 'organization': {'display_name': 'Test Organization 1', 'key': 'edX'},
'marketing_slug': 'fake-marketing-slug-xseries-1', 'marketing_slug': 'fake-marketing-slug-xseries-1',
'status': program_status, 'status': program_status,
...@@ -980,6 +982,7 @@ class DashboardTestXSeriesPrograms(ModuleStoreTestCase, ProgramsApiConfigMixin): ...@@ -980,6 +982,7 @@ class DashboardTestXSeriesPrograms(ModuleStoreTestCase, ProgramsApiConfigMixin):
u'edx/demox/Run_1': [{ u'edx/demox/Run_1': [{
'id': 0, 'id': 0,
'category': self.category, 'category': self.category,
'display_category': self.display_category,
'organization': {'display_name': 'Test Organization 1', 'key': 'edX'}, 'organization': {'display_name': 'Test Organization 1', 'key': 'edX'},
'marketing_slug': marketing_slug, 'marketing_slug': marketing_slug,
'status': program_status, 'status': program_status,
......
...@@ -2446,7 +2446,7 @@ def _get_course_programs(user, user_enrolled_courses): # pylint: disable=invali ...@@ -2446,7 +2446,7 @@ def _get_course_programs(user, user_enrolled_courses): # pylint: disable=invali
'xseries' + '/{}' 'xseries' + '/{}'
).format(program['marketing_slug']) ).format(program['marketing_slug'])
}) })
programs_for_course['display_category'] = 'XSeries' programs_for_course['display_category'] = program.get('display_category')
programs_for_course['category'] = program.get('category') programs_for_course['category'] = program.get('category')
except KeyError: except KeyError:
log.warning('Program structure is invalid, skipping display: %r', program) log.warning('Program structure is invalid, skipping display: %r', program)
......
...@@ -1249,6 +1249,8 @@ base_vendor_js = [ ...@@ -1249,6 +1249,8 @@ base_vendor_js = [
'js/vendor/url.min.js', 'js/vendor/url.min.js',
'common/js/vendor/underscore.js', 'common/js/vendor/underscore.js',
'common/js/vendor/underscore.string.js', 'common/js/vendor/underscore.string.js',
'js/vendor/underscore.string.min.js',
'common/js/vendor/picturefill.min.js',
# Make some edX UI Toolkit utilities available in the global "edx" namespace # Make some edX UI Toolkit utilities available in the global "edx" namespace
'edx-ui-toolkit/js/utils/global-loader.js', 'edx-ui-toolkit/js/utils/global-loader.js',
......
...@@ -12,10 +12,21 @@ ...@@ -12,10 +12,21 @@
if (data){ if (data){
this.set({ this.set({
name: data.name, name: data.name,
category: data.category, type: data.display_category + ' Program',
subtitle: data.subtitle, subtitle: data.subtitle,
organizations: data.organizations, organizations: data.organizations,
marketingUrl: data.marketing_url marketingUrl: data.marketing_url,
smallBannerUrl: data.banner_image_urls.w348h116,
mediumBannerUrl: data.banner_image_urls.w435h145,
largeBannerUrl: data.banner_image_urls.w726h242,
breakpoints: {
max: {
tiny: '320px',
small: '540px',
medium: '768px',
large: '979px'
}
}
}); });
} }
} }
......
...@@ -5,14 +5,16 @@ ...@@ -5,14 +5,16 @@
'jquery', 'jquery',
'underscore', 'underscore',
'gettext', 'gettext',
'text!../../../templates/learner_dashboard/program_card.underscore' 'text!../../../templates/learner_dashboard/program_card.underscore',
'picturefill'
], ],
function( function(
Backbone, Backbone,
$, $,
_, _,
gettext, gettext,
programCardTpl programCardTpl,
picturefill
) { ) {
return Backbone.View.extend({ return Backbone.View.extend({
className: 'program-card', className: 'program-card',
...@@ -23,6 +25,35 @@ ...@@ -23,6 +25,35 @@
render: function() { render: function() {
var templated = this.tpl(this.model.toJSON()); var templated = this.tpl(this.model.toJSON());
this.$el.html(templated); this.$el.html(templated);
this.postRender();
},
postRender: function() {
if(navigator.userAgent.indexOf('MSIE') !== -1 ||
navigator.appVersion.indexOf('Trident/') > 0){
/* Microsoft Internet Explorer detected in. */
window.setTimeout( function() {
this.reLoadBannerImage();
}.bind(this), 100);
}
},
// Defer loading the rest of the page to limit FOUC
reLoadBannerImage: function() {
var $img = this.$('.program_card .banner-image'),
imgSrcAttr = $img ? $img.attr('src') : {};
if (!imgSrcAttr || imgSrcAttr.length < 0) {
try{
this.reEvaluatePicture();
}catch(err){
//Swallow the error here
}
}
},
reEvaluatePicture: function(){
picturefill({
reevaluate: true
});
} }
}); });
} }
......
...@@ -29,7 +29,12 @@ define([ ...@@ -29,7 +29,12 @@ define([
modified: '2016-03-25T13:45:21.220732Z', modified: '2016-03-25T13:45:21.220732Z',
marketing_slug: 'p_2?param=haha&test=b', marketing_slug: 'p_2?param=haha&test=b',
id: 146, id: 146,
marketing_url: 'http://www.edx.org/xseries/p_2?param=haha&test=b' marketing_url: 'http://www.edx.org/xseries/p_2?param=haha&test=b',
banner_image_urls: {
w348h116: 'http://www.edx.org/images/org1/test1',
w435h145: 'http://www.edx.org/images/org1/test2',
w726h242: 'http://www.edx.org/images/org1/test3'
}
}, },
{ {
category: 'xseries', category: 'xseries',
...@@ -46,7 +51,12 @@ define([ ...@@ -46,7 +51,12 @@ define([
modified: '2016-03-09T14:30:52.840898Z', modified: '2016-03-09T14:30:52.840898Z',
marketing_slug: 'gdaf', marketing_slug: 'gdaf',
id: 147, id: 147,
marketing_url: 'http://www.edx.org/xseries/gdaf' marketing_url: 'http://www.edx.org/xseries/gdaf',
banner_image_urls: {
w348h116: 'http://www.edx.org/images/org2/test1',
w435h145: 'http://www.edx.org/images/org2/test2',
w726h242: 'http://www.edx.org/images/org2/test3'
}
} }
] ]
}; };
......
...@@ -13,6 +13,7 @@ define([ ...@@ -13,6 +13,7 @@ define([
programModel, programModel,
program = { program = {
category: 'xseries', category: 'xseries',
display_category: 'XSeries',
status: 'active', status: 'active',
subtitle: 'program 1', subtitle: 'program 1',
name: 'test program 1', name: 'test program 1',
...@@ -26,7 +27,12 @@ define([ ...@@ -26,7 +27,12 @@ define([
modified: '2016-03-25T13:45:21.220732Z', modified: '2016-03-25T13:45:21.220732Z',
marketing_slug: 'p_2?param=haha&test=b', marketing_slug: 'p_2?param=haha&test=b',
id: 146, id: 146,
marketing_url: 'http://www.edx.org/xseries/p_2?param=haha&test=b' marketing_url: 'http://www.edx.org/xseries/p_2?param=haha&test=b',
banner_image_urls: {
w348h116: 'http://www.edx.org/images/test1',
w435h145: 'http://www.edx.org/images/test2',
w726h242: 'http://www.edx.org/images/test3'
}
}; };
beforeEach(function() { beforeEach(function() {
...@@ -49,10 +55,26 @@ define([ ...@@ -49,10 +55,26 @@ define([
var $cards = view.$el; var $cards = view.$el;
expect($cards).toBeDefined(); expect($cards).toBeDefined();
expect($cards.find('.title').html().trim()).toEqual(program.name); expect($cards.find('.title').html().trim()).toEqual(program.name);
expect($cards.find('.category span').html().trim()).toEqual(program.category); expect($cards.find('.category span').html().trim()).toEqual('XSeries Program');
expect($cards.find('.organization span').html().trim()).toEqual(program.organizations[0].display_name); expect($cards.find('.organization').html().trim()).toEqual(program.organizations[0].display_name);
expect($cards.find('.card-link').attr('href')).toEqual(program.marketing_url); expect($cards.find('.card-link').attr('href')).toEqual(program.marketing_url);
}); });
it('should call reEvaluatePicture if reLoadBannerImage is called', function(){
spyOn(view, 'reEvaluatePicture');
view.reLoadBannerImage();
expect(view.reEvaluatePicture).toHaveBeenCalled();
});
it('should handle exceptions from reEvaluatePicture', function(){
spyOn(view, 'reEvaluatePicture').andCallFake(function(){
throw {name:'Picturefill had exceptions'};
});
view.reLoadBannerImage();
expect(view.reEvaluatePicture).toHaveBeenCalled();
expect(view.reLoadBannerImage).not.toThrow('Picturefill had exceptions');
});
}); });
} }
); );
...@@ -66,6 +66,7 @@ ...@@ -66,6 +66,7 @@
'_split': 'js/split', '_split': 'js/split',
'mathjax_delay_renderer': 'coffee/src/mathjax_delay_renderer', 'mathjax_delay_renderer': 'coffee/src/mathjax_delay_renderer',
'MathJaxProcessor': 'coffee/src/customwmd', 'MathJaxProcessor': 'coffee/src/customwmd',
'picturefill': 'common/js/vendor/picturefill.min',
// Manually specify LMS files that are not converted to RequireJS // Manually specify LMS files that are not converted to RequireJS
'history': 'js/vendor/history', 'history': 'js/vendor/history',
......
...@@ -69,6 +69,7 @@ lib_paths: ...@@ -69,6 +69,7 @@ lib_paths:
- xmodule_js/common_static/js/vendor/slick.core.js - xmodule_js/common_static/js/vendor/slick.core.js
- xmodule_js/common_static/js/vendor/slick.grid.js - xmodule_js/common_static/js/vendor/slick.grid.js
- xmodule_js/common_static/js/vendor/jquery.event.drag-2.2.js - xmodule_js/common_static/js/vendor/jquery.event.drag-2.2.js
- xmodule_js/common_static/common/js/vendor/picturefill.min.js
# Paths to source JavaScript files # Paths to source JavaScript files
src_paths: src_paths:
......
...@@ -94,7 +94,8 @@ ...@@ -94,7 +94,8 @@
"catch": "js/vendor/ova/catch/js/catch", "catch": "js/vendor/ova/catch/js/catch",
"handlebars": "js/vendor/ova/catch/js/handlebars-1.1.2", "handlebars": "js/vendor/ova/catch/js/handlebars-1.1.2",
"tinymce": "js/vendor/tinymce/js/tinymce/tinymce.full.min", "tinymce": "js/vendor/tinymce/js/tinymce/tinymce.full.min",
"jquery.tinymce": "js/vendor/tinymce/js/tinymce/jquery.tinymce.min" "jquery.tinymce": "js/vendor/tinymce/js/tinymce/jquery.tinymce.min",
"picturefill": "common/js/vendor/picturefill.min"
// end of files needed by OVA // end of files needed by OVA
}, },
shim: { shim: {
......
...@@ -3,57 +3,90 @@ ...@@ -3,57 +3,90 @@
@import '../base/grid-settings'; @import '../base/grid-settings';
@import 'neat/neat'; // lib - Neat @import 'neat/neat'; // lib - Neat
$card-height: 150px;
.program-card{ .program-card{
@include span-columns(12); @include span-columns(12);
height: $card-height; border: 1px solid $border-color-l3;
border:1px solid $border-color-l3; box-sizing: border-box;
box-sizing:border-box;
padding: $baseline; padding: $baseline;
margin-bottom: $baseline; margin-bottom: $baseline;
position:relative; position: relative;
display: inline;
.card-link{ .card-link{
position:absolute; position: absolute;
top:0; top: 0;
bottom:0; bottom: 0;
right:0; right: 0;
left:0; left: 0;
outline:0; border: 0;
border:0; z-index: 1;
z-index:1; opacity: 0.5;
height: $card-height; &:hover,
&:focus{
opacity: 1;
}
.banner-image-container{
position: relative;
overflow: hidden;
height: 116px;
.banner-image{
position: absolute;
top: 0;
left: 50%;
z-index: 0;
transform: translate(-50%, 0);
min-height: 100%;
}
}
} }
.text-section{ .text-section{
margin-top: 106px;
.meta-info{ .meta-info{
@include outer-container; @include outer-container;
margin-bottom: $baseline; margin-bottom: $baseline*0.25;
font-size: em(12); font-size: em(12);
color: $gray;
.organization{ .organization{
@include span-columns(6); @include span-columns(6);
color: $gray; white-space: nowrap;
overflow: hidden;
} }
.category{ .category{
@include span-columns(6); @include span-columns(6);
text-align:right; text-align: right;
span{ .category-text{
@include float(right); @include float(right);
} }
.xseries-icon{ .xseries-icon{
@include float(right); @include float(right);
@include margin-right($baseline*0.2); @include margin-right($baseline*0.25);
background: url('#{$static-path}/images/icon-sm-xseries-black.png') no-repeat; background: url('#{$static-path}/images/icon-sm-xseries-black.png') no-repeat;
background-color: transparent; background-color: transparent;
background-size: 100%;
width: ($baseline*1); width: ($baseline*0.7);
height: ($baseline*1); height: ($baseline*0.7);
} }
} }
} }
.title{ .title{
font-size:em(30); font-size: em(24);
color: $gray-l1; color: $gray-l1;
margin-bottom: 10px; margin-bottom: 10px;
line-height: 1.2;
}
}
}
@include media($bp-small) {
.program-card{
@include omega(n);
@include span-columns(4);
.card-link{
.banner-image-container{
height: 166px;
}
}
.text-section{
margin-top: 156px;
} }
} }
} }
...@@ -61,9 +94,16 @@ $card-height: 150px; ...@@ -61,9 +94,16 @@ $card-height: 150px;
@include media($bp-medium) { @include media($bp-medium) {
.program-card{ .program-card{
@include omega(2n); @include omega(n);
@include span-columns(4); @include span-columns(8);
display:inline; .card-link{
.banner-image-container{
height: 242px;
}
}
.text-section{
margin-top: 232px;
}
} }
} }
...@@ -73,7 +113,14 @@ $card-height: 150px; ...@@ -73,7 +113,14 @@ $card-height: 150px;
.program-card{ .program-card{
@include omega(2n); @include omega(2n);
@include span-columns(6); @include span-columns(6);
display:inline; .card-link{
.banner-image-container{
height: 116px;
}
}
.text-section{
margin-top: 106px;
}
} }
} }
...@@ -81,7 +128,14 @@ $card-height: 150px; ...@@ -81,7 +128,14 @@ $card-height: 150px;
.program-card{ .program-card{
@include omega(2n); @include omega(2n);
@include span-columns(6); @include span-columns(6);
display:inline; .card-link{
.banner-image-container{
height: 145px;
}
}
.text-section{
margin-top: 135px;
}
} }
} }
...@@ -3,6 +3,10 @@ ...@@ -3,6 +3,10 @@
@import '../base/grid-settings'; @import '../base/grid-settings';
@import 'neat/neat'; // lib - Neat @import 'neat/neat'; // lib - Neat
//Patern Library button colors
$pl-button-border-color: #065683;
$pl-button-color: #0079bc;
.program-list-wrapper{ .program-list-wrapper{
@include outer-container; @include outer-container;
padding: $baseline $baseline; padding: $baseline $baseline;
...@@ -20,20 +24,30 @@ ...@@ -20,20 +24,30 @@
background-color: $body-bg; background-color: $body-bg;
box-sizing: border-box; box-sizing: border-box;
border: 1px solid $border-color-l3; border: 1px solid $border-color-l3;
clear:both; clear: both;
.advertise-message{ .advertise-message{
font-size:em(12); font-size: em(12);
color: $gray-d4; color: $gray-d4;
margin-bottom: $baseline; margin-bottom: $baseline;
} }
.ad-link{ .ad-link{
padding:$baseline * 0.5; text-align:center;
border: 1px solid $blue-t1;
font-size: em(16);
a{ a{
padding: $baseline * 0.5;
border: 1px solid $pl-button-border-color;
color: $pl-button-color;
font-size: em(16);
text-decoration: none; text-decoration: none;
&:hover, &:focus, &:active{ display: block;
background-color: $button-bg-hover-color; line-height: 1.2;
&:hover,
&:focus,
&:active{
color: $white;
background-color: $pl-button-color;
}
span{
@include margin-left($baseline*0.25);
} }
} }
} }
...@@ -44,11 +58,10 @@ ...@@ -44,11 +58,10 @@
@include media($bp-medium) { @include media($bp-medium) {
.program-cards-container{ .program-cards-container{
@include span-columns(6); @include span-columns(8);
} }
.sidebar{ .sidebar{
@include omega(n); @include span-columns(8);
@include span-columns(2);
} }
} }
......
<div class="banner-image"> <a href="<%- marketingUrl %>" class="card-link">
<a href="<%- marketingUrl %>" class="card-link"> <div class="banner-image-container">
<img alt="<%- gettext(name)%>" src="" /> <picture>
</a> <source srcset="<%- smallBannerUrl %>" media="(max-width: <%- breakpoints.max.tiny %>)">
</div> <source srcset="<%- mediumBannerUrl %>" media="(max-width: <%- breakpoints.max.small %>)">
<source srcset="<%- largeBannerUrl %>" media="(max-width: <%- breakpoints.max.medium %>)">
<source srcset="<%- smallBannerUrl %>" media="(max-width: <%- breakpoints.max.large %>)">
<img class="banner-image" srcset="<%- mediumBannerUrl %>" alt="<%- gettext(name)%>">
</picture>
</div>
</a>
<div class="text-section"> <div class="text-section">
<div class="meta-info"> <div class="meta-info">
<div class="organization"> <div class="organization">
<% _.each(organizations, function(org){ %> <% _.each(organizations, function(org){ %>
<span><%- gettext(org.display_name) %></span> <%- gettext(org.display_name) %>
<% }); %> <% }); %>
</div> </div>
<div class="category"> <div class="category">
<span><%- gettext(category) %></span> <span class="category-text"><%- gettext(type) %></span>
<i class="xseries-icon" aria-hidden="true"></i> <i class="xseries-icon" aria-hidden="true"></i>
</div> </div>
</div> </div>
......
...@@ -18,6 +18,7 @@ from openedx.core.djangoapps.programs.utils import ( ...@@ -18,6 +18,7 @@ from openedx.core.djangoapps.programs.utils import (
get_programs_for_dashboard, get_programs_for_dashboard,
get_programs_for_credentials, get_programs_for_credentials,
get_engaged_programs, get_engaged_programs,
get_display_category
) )
from student.tests.factories import UserFactory, CourseEnrollmentFactory from student.tests.factories import UserFactory, CourseEnrollmentFactory
...@@ -109,6 +110,7 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, ...@@ -109,6 +110,7 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin,
actual = get_programs_for_dashboard(self.user, self.COURSE_KEYS) actual = get_programs_for_dashboard(self.user, self.COURSE_KEYS)
expected = {} expected = {}
for program in self.PROGRAMS_API_RESPONSE['results']: for program in self.PROGRAMS_API_RESPONSE['results']:
program['display_category'] = get_display_category(program)
for course_code in program['course_codes']: for course_code in program['course_codes']:
for run in course_code['run_modes']: for run in course_code['run_modes']:
course_key = run['course_key'] course_key = run['course_key']
...@@ -206,6 +208,8 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, ...@@ -206,6 +208,8 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin,
actual = get_engaged_programs(self.user, enrollments) actual = get_engaged_programs(self.user, enrollments)
programs = self.PROGRAMS_API_RESPONSE['results'] programs = self.PROGRAMS_API_RESPONSE['results']
for program in programs:
program['display_category'] = get_display_category(program)
# get_engaged_programs iterates across a list returned by the programs # get_engaged_programs iterates across a list returned by the programs
# API to create flattened lists keyed by course ID. These lists are # API to create flattened lists keyed by course ID. These lists are
# joined in order of enrollment creation time when constructing the # joined in order of enrollment creation time when constructing the
...@@ -234,6 +238,8 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, ...@@ -234,6 +238,8 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin,
actual = get_engaged_programs(self.user, enrollments) actual = get_engaged_programs(self.user, enrollments)
programs = self.PROGRAMS_API_RESPONSE['results'] programs = self.PROGRAMS_API_RESPONSE['results']
for program in programs:
program['display_category'] = get_display_category(program)
expected = [programs[0]] expected = [programs[0]]
self.assertEqual(expected, actual) self.assertEqual(expected, actual)
...@@ -251,6 +257,8 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, ...@@ -251,6 +257,8 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin,
actual = get_engaged_programs(self.user, enrollments) actual = get_engaged_programs(self.user, enrollments)
programs = self.PROGRAMS_API_RESPONSE['results'] programs = self.PROGRAMS_API_RESPONSE['results']
for program in programs:
program['display_category'] = get_display_category(program)
expected = programs[-2:] expected = programs[-2:]
self.assertEqual(expected, actual) self.assertEqual(expected, actual)
...@@ -277,3 +285,16 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin, ...@@ -277,3 +285,16 @@ class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin,
expected = [] expected = []
self.assertEqual(expected, actual) self.assertEqual(expected, actual)
@httpretty.activate
def test_get_display_category_success(self):
self.create_programs_config()
self.mock_programs_api()
actual_programs = get_programs(self.user)
for program in actual_programs:
expected = 'XSeries'
self.assertEqual(expected, get_display_category(program))
def test_get_display_category_none(self):
self.assertEqual('', get_display_category(None))
self.assertEqual('', get_display_category({"id": "test"}))
...@@ -46,6 +46,7 @@ def flatten_programs(programs, course_ids): ...@@ -46,6 +46,7 @@ def flatten_programs(programs, course_ids):
for run in course_code['run_modes']: for run in course_code['run_modes']:
run_id = run['course_key'] run_id = run['course_key']
if run_id in course_ids: if run_id in course_ids:
program['display_category'] = get_display_category(program)
flattened.setdefault(run_id, []).append(program) flattened.setdefault(run_id, []).append(program)
except KeyError: except KeyError:
log.exception('Unable to parse Programs API response: %r', program) log.exception('Unable to parse Programs API response: %r', program)
...@@ -113,6 +114,24 @@ def get_programs_for_credentials(user, programs_credentials): ...@@ -113,6 +114,24 @@ def get_programs_for_credentials(user, programs_credentials):
return certificate_programs return certificate_programs
def get_display_category(program):
""" Given the program, return the category of the program for display
Arguments:
program (Program): The program to get the display category string from
Returns:
string, the category for display to the user.
Empty string if the program has no category or is null.
"""
display_candidate = ''
if program and program.get('category'):
if program.get('category') == 'xseries':
display_candidate = 'XSeries'
else:
display_candidate = program.get('category', '').capitalize()
return display_candidate
def get_engaged_programs(user, enrollments): def get_engaged_programs(user, enrollments):
"""Derive a list of programs in which the given user is engaged. """Derive a list of programs in which the given user is engaged.
......
...@@ -8,7 +8,8 @@ ...@@ -8,7 +8,8 @@
"requirejs": "~2.1.22", "requirejs": "~2.1.22",
"uglify-js": "2.4.24", "uglify-js": "2.4.24",
"underscore": "~1.8.3", "underscore": "~1.8.3",
"underscore.string": "~3.3.4" "underscore.string": "~3.3.4",
"picturefill": "~3.0.2"
}, },
"devDependencies": { "devDependencies": {
"jshint": "^2.7.0", "jshint": "^2.7.0",
......
...@@ -42,7 +42,8 @@ COMMON_LOOKUP_DIRS = [ ...@@ -42,7 +42,8 @@ COMMON_LOOKUP_DIRS = [
# static directory. # static directory.
NPM_INSTALLED_LIBRARIES = [ NPM_INSTALLED_LIBRARIES = [
'underscore/underscore.js', 'underscore/underscore.js',
'underscore.string/dist/underscore.string.js' 'underscore.string/dist/underscore.string.js',
'picturefill/dist/picturefill.min.js'
] ]
# Directory to install static vendor files # Directory to install static vendor files
......
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