Update program detail fragment.

LEARNER-3755
This commit is contained in:
Waheed Ahmed
2018-01-11 17:11:56 +05:00
parent 6215945b6f
commit c68b50d964
9 changed files with 108 additions and 30 deletions

View File

@@ -32,7 +32,11 @@ class ProgramsFragmentView(EdxFragmentView):
Render the program listing fragment.
"""
user = request.user
mobile_only = json.loads(request.GET.get('mobile_only', 'false'))
try:
mobile_only = json.loads(request.GET.get('mobile_only', 'false'))
except ValueError:
mobile_only = False
programs_config = kwargs.get('programs_config') or ProgramsApiConfig.current()
if not programs_config.enabled or not user.is_authenticated():
raise Http404
@@ -80,7 +84,12 @@ class ProgramDetailsFragmentView(EdxFragmentView):
if not program_data:
raise Http404
program_data = ProgramDataExtender(program_data, request.user).extend()
try:
mobile_only = json.loads(request.GET.get('mobile_only', 'false'))
except ValueError:
mobile_only = False
program_data = ProgramDataExtender(program_data, request.user, mobile_only=mobile_only).extend()
course_data = meter.progress(programs=[program_data], count_only=False)[0]
certificate_data = get_certificates(request.user, program_data)

View File

@@ -56,7 +56,9 @@
$.extend(unselectedRun, {
marketing_url: courseRun.marketing_url,
is_enrollment_open: courseRun.is_enrollment_open
is_enrollment_open: courseRun.is_enrollment_open,
key: courseRun.key || '',
is_mobile_only: courseRun.is_mobile_only || false
});
}
@@ -196,7 +198,7 @@
if (isEnrolled && courseRun.course_url) {
courseTitleLink = courseRun.course_url;
} else if (!isEnrolled && courseRun.marketing_url) {
courseTitleLink = courseRun.marketing_url;
courseTitleLink = this.updateMarketingUrl(courseRun);
}
this.set({
certificate_url: courseRun.certificate_url,
@@ -217,7 +219,8 @@
upcoming_course_runs: this.getUpcomingCourseRuns(),
upgrade_url: courseRun.upgrade_url,
price: this.getCertificatePriceString(courseRun),
course_title_link: courseTitleLink
course_title_link: courseTitleLink,
is_mobile_only: courseRun.is_mobile_only || false
});
// This is used to render the date for completed and in progress courses
@@ -240,6 +243,30 @@
});
this.setActiveCourseRun(selectedCourseRun);
}
},
// update marketing url for deep linking if is_mobile_only true
updateMarketingUrl: function(courseRun) {
if (courseRun.is_mobile_only === true) {
var marketingUrl = courseRun.marketing_url, // eslint-disable-line vars-on-top
href = marketingUrl,
path,
start;
if (marketingUrl.indexOf('course_info?path_id') < 0) {
start = marketingUrl.indexOf('course/');
if (start > -1) {
path = marketingUrl.substr(start);
}
href = 'edxapp://course_info?path_id=' + path;
}
return href;
} else {
return courseRun.marketing_url;
}
}
});
});

View File

@@ -22,7 +22,8 @@
tpl: HtmlUtils.template(pageTpl),
events: {
'click .enroll-button': 'handleEnroll'
'click .enroll-button': 'handleEnroll',
'change .run-select': 'updateEnrollUrl'
},
initialize: function(options) {
@@ -55,16 +56,18 @@
handleEnroll: function() {
// Enrollment click event handled here
var courseRunKey = $('.run-select').val() || this.model.get('course_run_key');
this.model.updateCourseRun(courseRunKey);
if (this.model.get('is_enrolled')) {
// Create the enrollment.
this.enrollModel.save({
course_id: courseRunKey
}, {
success: _.bind(this.enrollSuccess, this),
error: _.bind(this.enrollError, this)
});
if (this.model.get('is_mobile_only') !== true) {
var courseRunKey = $('.run-select').val() || this.model.get('course_run_key'); // eslint-disable-line vars-on-top, max-len
this.model.updateCourseRun(courseRunKey);
if (this.model.get('is_enrolled')) {
// Create the enrollment.
this.enrollModel.save({
course_id: courseRunKey
}, {
success: _.bind(this.enrollSuccess, this),
error: _.bind(this.enrollError, this)
});
}
}
},
@@ -100,6 +103,14 @@
}
},
updateEnrollUrl: function() {
if (this.model.get('is_mobile_only') === true) {
var courseRunKey = $('.run-select').val(), // eslint-disable-line vars-on-top
href = 'edxapp://enroll?course_id=' + courseRunKey + '&email_opt_in=true';
$('.enroll-course-button').attr('href', href);
}
},
redirect: function(url) {
window.location.href = url;
}

View File

@@ -352,7 +352,7 @@
margin-bottom: 0;
}
button {
button, .enroll-course-button {
background-color: palette(primary, dark);
height: 37px;
width: 128px;
@@ -371,6 +371,10 @@
@include float(right);
}
}
.enroll-course-button {
padding: 7px 18.5px 0 18.5px;
}
}
.select-choice {

View File

@@ -33,9 +33,15 @@
</div>
<% } %>
<div class="enroll-button">
<% if (typeof is_mobile_only != 'undefined' && is_mobile_only === true) { %>
<a href="edxapp://enroll?course_id=<%- course_run_key %>&email_opt_in=true" class="enroll-course-button btn-brand btn cta-primary">
<%- gettext('Enroll Now') %>
</a>
<% } else { %>
<button type="button" class="btn-brand btn cta-primary">
<%- gettext('Enroll Now') %>
</button>
<% } %>
</div>
<% } else if (upcoming_course_runs.length > 0) {%>
<div class="no-action-message">

View File

@@ -20,7 +20,7 @@
<div><%- gettext('To complete the program, you must earn a verified certificate for each course.') %></div>
</div>
<% } %>
<% if (is_learner_eligible_for_one_click_purchase) { %>
<% if (is_learner_eligible_for_one_click_purchase && (typeof is_mobile_only === 'undefined' || is_mobile_only === false)) { %>
<a href="<%- completeProgramURL %>" class="btn-brand btn cta-primary upgrade-button complete-program">
<%- gettext('Upgrade All Remaining Courses (')%>
<% if (discount_data.is_discounted) { %>

View File

@@ -3,8 +3,10 @@
<span><%- gettext('Needs verified certificate ') %></span>
<span class="price"> <%- price %></span>
</div>
<% if (typeof is_mobile_only === 'undefined' || is_mobile_only === false) { %>
<div class="action col-12 md-col-4">
<a href="<%- upgrade_url %>" class="btn-brand btn cta-primary upgrade-button single-course-run">
<%- gettext('Upgrade to Verified') %>
<a>
</div>
<% } %>

View File

@@ -3,6 +3,7 @@
import datetime
import json
import uuid
from copy import deepcopy
import ddt
import httpretty
@@ -823,7 +824,7 @@ class TestProgramDataExtender(ModuleStoreTestCase):
"""Tests of the program data extender utility class."""
maxDiff = None
sku = 'abc123'
checkout_path = '/basket'
checkout_path = '/basket/add'
instructors = {
'instructors': [
{
@@ -852,7 +853,11 @@ class TestProgramDataExtender(ModuleStoreTestCase):
def _assert_supplemented(self, actual, **kwargs):
"""DRY helper used to verify that program data is extended correctly."""
self.course_run.update(
program = deepcopy(self.program)
course_run = deepcopy(self.course_run)
course = deepcopy(self.catalog_course)
course_run.update(
dict(
{
'certificate_url': None,
@@ -868,10 +873,10 @@ class TestProgramDataExtender(ModuleStoreTestCase):
)
)
self.catalog_course['course_runs'] = [self.course_run]
self.program['courses'] = [self.catalog_course]
course['course_runs'] = [course_run]
program['courses'] = [course]
self.assertEqual(actual, self.program)
self.assertEqual(actual, program)
@ddt.data(-1, 0, 1)
def test_is_enrollment_open(self, days_offset):
@@ -887,15 +892,13 @@ class TestProgramDataExtender(ModuleStoreTestCase):
self._assert_supplemented(data)
@ddt.data(
(False, None, False),
(True, MODES.audit, True),
(True, MODES.verified, False),
)
@ddt.unpack
@mock.patch(UTILS_MODULE + '.CourseMode.mode_for_course')
def test_student_enrollment_status(self, is_enrolled, enrolled_mode, is_upgrade_required, mock_get_mode):
"""Verify that program data is supplemented with the student's enrollment status."""
expected_upgrade_url = '{root}/{path}?sku={sku}'.format(
expected_upgrade_url = '{root}/{path}/?sku={sku}'.format(
root=ECOMMERCE_URL_ROOT,
path=self.checkout_path.strip('/'),
sku=self.sku,
@@ -1275,6 +1278,14 @@ class TestProgramDataExtender(ModuleStoreTestCase):
self.assertTrue(data['is_learner_eligible_for_one_click_purchase'])
self.assertEqual(set(data['skus']), expected_skus)
def test_course_url_with_mobile_only(self):
"""
Verify that correct course url is returned for mobile.
"""
data = ProgramDataExtender(self.program, self.user, mobile_only=True).extend()
expected_course_url = 'edxapp://enrolled_course_info?course_id={}'.format(self.course.id)
self._assert_supplemented(data, course_url=expected_course_url)
@skip_unless_lms
@mock.patch(UTILS_MODULE + '.get_credentials')

View File

@@ -82,6 +82,7 @@ class ProgramProgressMeter(object):
def __init__(self, site, user, enrollments=None, uuid=None, mobile_only=False):
self.site = site
self.user = user
self.mobile_only = mobile_only
self.enrollments = enrollments or list(CourseEnrollment.enrollments_for_user(self.user))
self.enrollments.sort(key=lambda e: e.created, reverse=True)
@@ -106,7 +107,7 @@ class ProgramProgressMeter(object):
if uuid:
self.programs = [get_programs(self.site, uuid=uuid)]
else:
self.programs = attach_program_detail_url(get_programs(self.site), mobile_only)
self.programs = attach_program_detail_url(get_programs(self.site), self.mobile_only)
def invert_programs(self):
"""Intersect programs and enrollments.
@@ -407,9 +408,11 @@ class ProgramDataExtender(object):
program_data (dict): Representation of a program.
user (User): The user whose enrollments to inspect.
"""
def __init__(self, program_data, user):
def __init__(self, program_data, user, mobile_only=False):
self.data = program_data
self.user = user
self.mobile_only = mobile_only
self.data.update({'is_mobile_only': self.mobile_only})
self.course_run_key = None
self.course_overview = None
@@ -452,7 +455,10 @@ class ProgramDataExtender(object):
) if certificate_uuid else None
def _attach_course_run_course_url(self, run_mode):
run_mode['course_url'] = reverse('course_root', args=[self.course_run_key])
if self.mobile_only:
run_mode['course_url'] = 'edxapp://enrolled_course_info?course_id={}'.format(run_mode.get('key'))
else:
run_mode['course_url'] = reverse('course_root', args=[self.course_run_key])
def _attach_course_run_enrollment_open_date(self, run_mode):
run_mode['enrollment_open_date'] = strftime_localized(self.enrollment_start, 'SHORT_DATE')
@@ -498,6 +504,9 @@ class ProgramDataExtender(object):
def _attach_course_run_may_certify(self, run_mode):
run_mode['may_certify'] = self.course_overview.may_certify()
def _attach_course_run_is_mobile_only(self, run_mode):
run_mode['is_mobile_only'] = self.mobile_only
def _filter_out_courses_with_entitlements(self, courses):
"""
Removes courses for which the current user already holds an applicable entitlement.
@@ -670,7 +679,6 @@ def get_certificates(user, extended_program):
return certificates
# pylint: disable=missing-docstring
class ProgramMarketingDataExtender(ProgramDataExtender):
"""
Utility for extending program data meant for the program marketing page which lives in the