Upsell Courses in Courseware, CourseOutline, Discussions, Progress
This commit is contained in:
committed by
Calen Pennington
parent
4e9d41f222
commit
fdafd53a66
@@ -231,18 +231,18 @@ class TestFieldOverrideMongoPerformance(FieldOverridePerformanceTestCase):
|
||||
# # of sql queries to default,
|
||||
# # of mongo queries,
|
||||
# )
|
||||
('no_overrides', 1, True, False): (25, 1),
|
||||
('no_overrides', 2, True, False): (25, 1),
|
||||
('no_overrides', 3, True, False): (25, 1),
|
||||
('ccx', 1, True, False): (25, 1),
|
||||
('ccx', 2, True, False): (25, 1),
|
||||
('ccx', 3, True, False): (25, 1),
|
||||
('no_overrides', 1, False, False): (25, 1),
|
||||
('no_overrides', 2, False, False): (25, 1),
|
||||
('no_overrides', 3, False, False): (25, 1),
|
||||
('ccx', 1, False, False): (25, 1),
|
||||
('ccx', 2, False, False): (25, 1),
|
||||
('ccx', 3, False, False): (25, 1),
|
||||
('no_overrides', 1, True, False): (27, 1),
|
||||
('no_overrides', 2, True, False): (27, 1),
|
||||
('no_overrides', 3, True, False): (27, 1),
|
||||
('ccx', 1, True, False): (27, 1),
|
||||
('ccx', 2, True, False): (27, 1),
|
||||
('ccx', 3, True, False): (27, 1),
|
||||
('no_overrides', 1, False, False): (27, 1),
|
||||
('no_overrides', 2, False, False): (27, 1),
|
||||
('no_overrides', 3, False, False): (27, 1),
|
||||
('ccx', 1, False, False): (27, 1),
|
||||
('ccx', 2, False, False): (27, 1),
|
||||
('ccx', 3, False, False): (27, 1),
|
||||
}
|
||||
|
||||
|
||||
@@ -254,19 +254,19 @@ class TestFieldOverrideSplitPerformance(FieldOverridePerformanceTestCase):
|
||||
__test__ = True
|
||||
|
||||
TEST_DATA = {
|
||||
('no_overrides', 1, True, False): (25, 3),
|
||||
('no_overrides', 2, True, False): (25, 3),
|
||||
('no_overrides', 3, True, False): (25, 3),
|
||||
('ccx', 1, True, False): (25, 3),
|
||||
('ccx', 2, True, False): (25, 3),
|
||||
('ccx', 3, True, False): (25, 3),
|
||||
('ccx', 1, True, True): (26, 3),
|
||||
('ccx', 2, True, True): (26, 3),
|
||||
('ccx', 3, True, True): (26, 3),
|
||||
('no_overrides', 1, False, False): (25, 3),
|
||||
('no_overrides', 2, False, False): (25, 3),
|
||||
('no_overrides', 3, False, False): (25, 3),
|
||||
('ccx', 1, False, False): (25, 3),
|
||||
('ccx', 2, False, False): (25, 3),
|
||||
('ccx', 3, False, False): (25, 3),
|
||||
('no_overrides', 1, True, False): (27, 3),
|
||||
('no_overrides', 2, True, False): (27, 3),
|
||||
('no_overrides', 3, True, False): (27, 3),
|
||||
('ccx', 1, True, False): (27, 3),
|
||||
('ccx', 2, True, False): (27, 3),
|
||||
('ccx', 3, True, False): (27, 3),
|
||||
('ccx', 1, True, True): (28, 3),
|
||||
('ccx', 2, True, True): (28, 3),
|
||||
('ccx', 3, True, True): (28, 3),
|
||||
('no_overrides', 1, False, False): (27, 3),
|
||||
('no_overrides', 2, False, False): (27, 3),
|
||||
('no_overrides', 3, False, False): (27, 3),
|
||||
('ccx', 1, False, False): (27, 3),
|
||||
('ccx', 2, False, False): (27, 3),
|
||||
('ccx', 3, False, False): (27, 3),
|
||||
}
|
||||
|
||||
@@ -72,9 +72,10 @@ class DateSummary(object):
|
||||
self.user.preferences.model.get_value(self.user, "time_zone", "UTC")
|
||||
)
|
||||
|
||||
def __init__(self, course, user):
|
||||
def __init__(self, course, user, course_id=None):
|
||||
self.course = course
|
||||
self.user = user
|
||||
self.course_id = course_id or self.course.id
|
||||
|
||||
@property
|
||||
def relative_datestring(self):
|
||||
@@ -174,7 +175,7 @@ class CourseEndDate(DateSummary):
|
||||
@property
|
||||
def description(self):
|
||||
if datetime.now(utc) <= self.date:
|
||||
mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
|
||||
mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course_id)
|
||||
if is_active and CourseMode.is_eligible_for_certificate(mode):
|
||||
return _('To earn a certificate, you must complete all requirements before this date.')
|
||||
else:
|
||||
@@ -204,10 +205,10 @@ class VerifiedUpgradeDeadlineDate(DateSummary):
|
||||
ecommerce_service = EcommerceService()
|
||||
if ecommerce_service.is_enabled(self.user):
|
||||
course_mode = CourseMode.objects.get(
|
||||
course_id=self.course.id, mode_slug=CourseMode.VERIFIED
|
||||
course_id=self.course_id, mode_slug=CourseMode.VERIFIED
|
||||
)
|
||||
return ecommerce_service.checkout_page_url(course_mode.sku)
|
||||
return reverse('verify_student_upgrade_and_verify', args=(self.course.id,))
|
||||
return reverse('verify_student_upgrade_and_verify', args=(self.course_id,))
|
||||
|
||||
@property
|
||||
def is_enabled(self):
|
||||
@@ -221,7 +222,7 @@ class VerifiedUpgradeDeadlineDate(DateSummary):
|
||||
if not is_enabled:
|
||||
return False
|
||||
|
||||
enrollment_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
|
||||
enrollment_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course_id)
|
||||
|
||||
# Return `true` if user is not enrolled in course
|
||||
if enrollment_mode is None and is_active is None:
|
||||
@@ -234,7 +235,7 @@ class VerifiedUpgradeDeadlineDate(DateSummary):
|
||||
def date(self):
|
||||
try:
|
||||
verified_mode = CourseMode.objects.get(
|
||||
course_id=self.course.id, mode_slug=CourseMode.VERIFIED
|
||||
course_id=self.course_id, mode_slug=CourseMode.VERIFIED
|
||||
)
|
||||
return verified_mode.expiration_datetime
|
||||
except CourseMode.DoesNotExist:
|
||||
@@ -273,7 +274,7 @@ class VerificationDeadlineDate(DateSummary):
|
||||
'verification-deadline-retry': (_('Retry Verification'), reverse('verify_student_reverify')),
|
||||
'verification-deadline-upcoming': (
|
||||
_('Verify My Identity'),
|
||||
reverse('verify_student_verify_now', args=(self.course.id,))
|
||||
reverse('verify_student_verify_now', args=(self.course_id,))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -297,13 +298,13 @@ class VerificationDeadlineDate(DateSummary):
|
||||
|
||||
@lazy
|
||||
def date(self):
|
||||
return VerificationDeadline.deadline_for_course(self.course.id)
|
||||
return VerificationDeadline.deadline_for_course(self.course_id)
|
||||
|
||||
@lazy
|
||||
def is_enabled(self):
|
||||
if self.date is None:
|
||||
return False
|
||||
(mode, is_active) = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
|
||||
(mode, is_active) = CourseEnrollment.enrollment_mode_for_user(self.user, self.course_id)
|
||||
if is_active and mode == 'verified':
|
||||
return self.verification_status in ('expired', 'none', 'must_reverify')
|
||||
return False
|
||||
|
||||
@@ -367,7 +367,7 @@ class SelfPacedCourseInfoTestCase(LoginEnrollmentTestCase, SharedModuleStoreTest
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
def test_num_queries_instructor_paced(self):
|
||||
self.fetch_course_info_with_queries(self.instructor_paced_course, 21, 4)
|
||||
self.fetch_course_info_with_queries(self.instructor_paced_course, 23, 4)
|
||||
|
||||
def test_num_queries_self_paced(self):
|
||||
self.fetch_course_info_with_queries(self.self_paced_course, 21, 4)
|
||||
self.fetch_course_info_with_queries(self.self_paced_course, 23, 4)
|
||||
|
||||
@@ -310,89 +310,6 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
|
||||
)
|
||||
self.assertEqual(block.title, 'Course End')
|
||||
|
||||
# Tests Verified Upgrade Deadline Date Block
|
||||
|
||||
def check_upgrade_banner(
|
||||
self,
|
||||
banner_expected=True,
|
||||
include_url_parameter=True,
|
||||
expected_cookie_value=None
|
||||
):
|
||||
"""
|
||||
Helper method to check for the presence of the Upgrade Banner
|
||||
"""
|
||||
url = reverse('info', args=[self.course.id.to_deprecated_string()])
|
||||
if include_url_parameter:
|
||||
url += '?upgrade=true'
|
||||
resp = self.client.get(url)
|
||||
upgrade_cookie_name = 'show_upgrade_notification'
|
||||
expected_banner_text = "Give yourself an additional incentive to complete"
|
||||
if banner_expected:
|
||||
self.assertIn(expected_banner_text, resp.content)
|
||||
self.assertIn(str(self.course.id), self.client.cookies[upgrade_cookie_name].value)
|
||||
else:
|
||||
self.assertNotIn(expected_banner_text, resp.content)
|
||||
if upgrade_cookie_name in self.client.cookies:
|
||||
self.assertNotIn(str(self.course.id), self.client.cookies[upgrade_cookie_name].value)
|
||||
if expected_cookie_value is not None:
|
||||
self.assertIn(str(expected_cookie_value), self.client.cookies[upgrade_cookie_name].value)
|
||||
|
||||
def test_verified_upgrade_deadline_date(self):
|
||||
with freeze_time('2015-01-02'):
|
||||
self.setup_course_and_user(days_till_upgrade_deadline=1, user_enrollment_mode=CourseMode.AUDIT)
|
||||
self.client.login(username='mrrobot', password='test')
|
||||
block = VerifiedUpgradeDeadlineDate(self.course, self.user)
|
||||
self.assertEqual(block.date, datetime.now(utc) + timedelta(days=1))
|
||||
self.assertTrue(block.is_enabled)
|
||||
self.assertEqual(block.link, reverse('verify_student_upgrade_and_verify', args=(self.course.id,)))
|
||||
self.check_upgrade_banner()
|
||||
|
||||
def test_without_upgrade_deadline(self):
|
||||
self.setup_course_and_user(enrollment_mode=None)
|
||||
self.client.login(username='mrrobot', password='test')
|
||||
block = VerifiedUpgradeDeadlineDate(self.course, self.user)
|
||||
self.assertFalse(block.is_enabled)
|
||||
self.assertIsNone(block.date)
|
||||
self.check_upgrade_banner(banner_expected=False)
|
||||
|
||||
def test_verified_upgrade_banner_not_present_past_deadline(self):
|
||||
with freeze_time('2015-01-02'):
|
||||
self.setup_course_and_user(days_till_upgrade_deadline=-1, user_enrollment_mode=CourseMode.AUDIT)
|
||||
self.client.login(username='mrrobot', password='test')
|
||||
block = VerifiedUpgradeDeadlineDate(self.course, self.user)
|
||||
self.assertFalse(block.is_enabled)
|
||||
self.check_upgrade_banner(banner_expected=False)
|
||||
|
||||
def test_verified_upgrade_banner_cookie(self):
|
||||
with freeze_time('2015-01-02'):
|
||||
self.setup_course_and_user(days_till_upgrade_deadline=1, user_enrollment_mode=CourseMode.AUDIT)
|
||||
self.client.login(username='mrrobot', password='test')
|
||||
|
||||
# No URL parameter or cookie present, notification should not be shown.
|
||||
self.check_upgrade_banner(include_url_parameter=False, banner_expected=False)
|
||||
|
||||
# Now pass URL parameter-- notification should be shown.
|
||||
self.check_upgrade_banner(include_url_parameter=True)
|
||||
|
||||
# A cookie should be set in the previous call, so it is no longer necessary to pass
|
||||
# the URL parameter in order to see the notification.
|
||||
self.check_upgrade_banner(include_url_parameter=False)
|
||||
|
||||
# Store the current course_id for testing
|
||||
old_course_id = self.course.id
|
||||
|
||||
# Change to another course
|
||||
self.setup_course_and_user(days_till_upgrade_deadline=1,
|
||||
user_enrollment_mode=CourseMode.AUDIT,
|
||||
create_user=False)
|
||||
|
||||
# Banner should not be present in the newly created course
|
||||
self.check_upgrade_banner(include_url_parameter=False,
|
||||
banner_expected=False,
|
||||
expected_cookie_value=old_course_id)
|
||||
|
||||
# Unfortunately (according to django doc), it is not possible to test expiration of the cookie.
|
||||
|
||||
def test_ecommerce_checkout_redirect(self):
|
||||
"""Verify the block link redirects to ecommerce checkout if it's enabled."""
|
||||
sku = 'TESTSKU'
|
||||
|
||||
@@ -210,8 +210,8 @@ class IndexQueryTestCase(ModuleStoreTestCase):
|
||||
NUM_PROBLEMS = 20
|
||||
|
||||
@ddt.data(
|
||||
(ModuleStoreEnum.Type.mongo, 10, 145),
|
||||
(ModuleStoreEnum.Type.split, 4, 145),
|
||||
(ModuleStoreEnum.Type.mongo, 10, 147),
|
||||
(ModuleStoreEnum.Type.split, 4, 147),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_index_query_counts(self, store_type, expected_mongo_query_count, expected_mysql_query_count):
|
||||
@@ -572,16 +572,18 @@ class ViewsTestCase(ModuleStoreTestCase):
|
||||
"""
|
||||
registration_price = 99
|
||||
self.course.cosmetic_display_price = 10
|
||||
# Since registration_price is set, it overrides the cosmetic_display_price and should be returned
|
||||
self.assertEqual(views.get_cosmetic_display_price(self.course, registration_price), "$99")
|
||||
with patch('course_modes.models.CourseMode.min_course_price_for_currency', return_value=registration_price):
|
||||
# Since registration_price is set, it overrides the cosmetic_display_price and should be returned
|
||||
self.assertEqual(views.get_cosmetic_display_price(self.course), "$99")
|
||||
|
||||
registration_price = 0
|
||||
# Since registration_price is not set, cosmetic_display_price should be returned
|
||||
self.assertEqual(views.get_cosmetic_display_price(self.course, registration_price), "$10")
|
||||
with patch('course_modes.models.CourseMode.min_course_price_for_currency', return_value=registration_price):
|
||||
# Since registration_price is not set, cosmetic_display_price should be returned
|
||||
self.assertEqual(views.get_cosmetic_display_price(self.course), "$10")
|
||||
|
||||
self.course.cosmetic_display_price = 0
|
||||
# Since both prices are not set, there is no price, thus "Free"
|
||||
self.assertEqual(views.get_cosmetic_display_price(self.course, registration_price), "Free")
|
||||
self.assertEqual(views.get_cosmetic_display_price(self.course), "Free")
|
||||
|
||||
def test_jump_to_invalid(self):
|
||||
# TODO add a test for invalid location
|
||||
@@ -1420,12 +1422,12 @@ class ProgressPageTests(ProgressPageBaseTests):
|
||||
"""Test that query counts remain the same for self-paced and instructor-paced courses."""
|
||||
SelfPacedConfiguration(enabled=self_paced_enabled).save()
|
||||
self.setup_course(self_paced=self_paced)
|
||||
with self.assertNumQueries(41), check_mongo_calls(1):
|
||||
with self.assertNumQueries(43), check_mongo_calls(1):
|
||||
self._get_progress_page()
|
||||
|
||||
@ddt.data(
|
||||
(False, 41, 27),
|
||||
(True, 34, 23)
|
||||
(False, 43, 29),
|
||||
(True, 36, 25)
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_progress_queries(self, enable_waffle, initial, subsequent):
|
||||
|
||||
@@ -55,7 +55,10 @@ from ..entrance_exams import (
|
||||
from ..masquerade import setup_masquerade
|
||||
from ..model_data import FieldDataCache
|
||||
from ..module_render import toc_for_course, get_module_for_descriptor
|
||||
from .views import CourseTabView, check_access_to_course
|
||||
from .views import (
|
||||
CourseTabView, check_access_to_course, check_and_get_upgrade_link,
|
||||
get_cosmetic_verified_display_price
|
||||
)
|
||||
|
||||
|
||||
TEMPLATE_IMPORTS = {'urllib': urllib}
|
||||
@@ -149,7 +152,7 @@ class CoursewareIndex(View):
|
||||
self._save_positions()
|
||||
self._prefetch_and_bind_section()
|
||||
|
||||
return render_to_response('courseware/courseware.html', self._create_courseware_context())
|
||||
return render_to_response('courseware/courseware.html', self._create_courseware_context(request))
|
||||
|
||||
def _redirect_if_not_requested_section(self):
|
||||
"""
|
||||
@@ -319,12 +322,11 @@ class CoursewareIndex(View):
|
||||
save_child_position(self.course, self.chapter_url_name)
|
||||
save_child_position(self.chapter, self.section_url_name)
|
||||
|
||||
def _create_courseware_context(self):
|
||||
def _create_courseware_context(self, request):
|
||||
"""
|
||||
Returns and creates the rendering context for the courseware.
|
||||
Also returns the table of contents for the courseware.
|
||||
"""
|
||||
request = RequestCache.get_current_request()
|
||||
course_url_name = default_course_url_name(request)
|
||||
course_url = reverse(course_url_name, kwargs={'course_id': unicode(self.course.id)})
|
||||
courseware_context = {
|
||||
@@ -346,6 +348,8 @@ class CoursewareIndex(View):
|
||||
'section_title': None,
|
||||
'sequence_title': None,
|
||||
'disable_accordion': waffle.flag_is_active(request, UNIFIED_COURSE_VIEW_FLAG),
|
||||
'upgrade_link': check_and_get_upgrade_link(request, self.effective_user, self.course.id),
|
||||
'upgrade_price': get_cosmetic_verified_display_price(self.course),
|
||||
}
|
||||
table_of_contents = toc_for_course(
|
||||
self.effective_user,
|
||||
|
||||
@@ -320,30 +320,12 @@ def course_info(request, course_id):
|
||||
if settings.FEATURES.get('ENABLE_MKTG_SITE'):
|
||||
url_to_enroll = marketing_link('COURSES')
|
||||
|
||||
store_upgrade_cookie = False
|
||||
upgrade_cookie_name = 'show_upgrade_notification'
|
||||
upgrade_link = None
|
||||
|
||||
# Construct the dates fragment
|
||||
dates_fragment = None
|
||||
|
||||
if request.user.is_authenticated():
|
||||
if SelfPacedConfiguration.current().enable_course_home_improvements:
|
||||
dates_fragment = CourseDatesFragmentView().render_to_fragment(request, course_id=course_id)
|
||||
show_upgrade_notification = False
|
||||
if request.GET.get('upgrade', 'false') == 'true':
|
||||
store_upgrade_cookie = True
|
||||
show_upgrade_notification = True
|
||||
elif upgrade_cookie_name in request.COOKIES and course_id in request.COOKIES[upgrade_cookie_name]:
|
||||
show_upgrade_notification = True
|
||||
|
||||
if show_upgrade_notification:
|
||||
upgrade_data = VerifiedUpgradeDeadlineDate(course, user)
|
||||
if upgrade_data.is_enabled:
|
||||
upgrade_link = upgrade_data.link
|
||||
else:
|
||||
# The upgrade is not enabled so the cookie does not need to be stored
|
||||
store_upgrade_cookie = False
|
||||
|
||||
context = {
|
||||
'request': request,
|
||||
@@ -358,7 +340,8 @@ def course_info(request, course_id):
|
||||
'show_enroll_banner': show_enroll_banner,
|
||||
'dates_fragment': dates_fragment,
|
||||
'url_to_enroll': url_to_enroll,
|
||||
'upgrade_link': upgrade_link,
|
||||
'upgrade_link': check_and_get_upgrade_link(request, user, course.id),
|
||||
'upgrade_price': get_cosmetic_verified_display_price(course),
|
||||
}
|
||||
|
||||
# Get the URL of the user's last position in order to display the 'where you were last' message
|
||||
@@ -375,25 +358,22 @@ def course_info(request, course_id):
|
||||
if CourseEnrollment.is_enrolled(request.user, course.id):
|
||||
inject_coursetalk_keys_into_context(context, course_key)
|
||||
|
||||
response = render_to_response('courseware/info.html', context)
|
||||
if store_upgrade_cookie:
|
||||
if upgrade_cookie_name in request.COOKIES and str(course_id) not in request.COOKIES[upgrade_cookie_name]:
|
||||
cookie_value = '%s,%s' % (course_id, request.COOKIES[upgrade_cookie_name])
|
||||
elif upgrade_cookie_name in request.COOKIES and str(course_id) in request.COOKIES[upgrade_cookie_name]:
|
||||
cookie_value = request.COOKIES[upgrade_cookie_name]
|
||||
else:
|
||||
cookie_value = course_id
|
||||
return render_to_response('courseware/info.html', context)
|
||||
|
||||
if cookie_value is not None:
|
||||
response.set_cookie(
|
||||
upgrade_cookie_name,
|
||||
cookie_value,
|
||||
max_age=10 * 24 * 60 * 60, # set for 10 days
|
||||
domain=settings.SESSION_COOKIE_DOMAIN,
|
||||
httponly=True # no use case for accessing from JavaScript
|
||||
)
|
||||
|
||||
return response
|
||||
UPGRADE_COOKIE_NAME = 'show_upgrade_notification'
|
||||
|
||||
|
||||
def check_and_get_upgrade_link(request, user, course_id):
|
||||
upgrade_link = None
|
||||
|
||||
if request.user.is_authenticated():
|
||||
upgrade_data = VerifiedUpgradeDeadlineDate(None, user, course_id=course_id)
|
||||
if upgrade_data.is_enabled:
|
||||
upgrade_link = upgrade_data.link
|
||||
request.need_to_set_upgrade_cookie = True
|
||||
|
||||
return upgrade_link
|
||||
|
||||
|
||||
class StaticCourseTabView(EdxFragmentView):
|
||||
@@ -518,6 +498,8 @@ class CourseTabView(EdxFragmentView):
|
||||
'supports_preview_menu': supports_preview_menu,
|
||||
'uses_pattern_library': True,
|
||||
'disable_courseware_js': True,
|
||||
'upgrade_link': check_and_get_upgrade_link(request, request.user, course.id),
|
||||
'upgrade_price': get_cosmetic_verified_display_price(course),
|
||||
}
|
||||
|
||||
def render_to_fragment(self, request, course=None, page_context=None, **kwargs):
|
||||
@@ -569,23 +551,57 @@ def registered_for_course(course, user):
|
||||
return False
|
||||
|
||||
|
||||
def get_cosmetic_display_price(course, registration_price):
|
||||
def get_cosmetic_verified_display_price(course):
|
||||
"""
|
||||
Return Course Price as a string preceded by correct currency, or 'Free'
|
||||
Returns the minimum verified cert course price as a string preceded by correct currency, or 'Free'.
|
||||
"""
|
||||
return get_course_prices(course, verified_only=True)[1]
|
||||
|
||||
|
||||
def get_cosmetic_display_price(course):
|
||||
"""
|
||||
Returns the course price as a string preceded by correct currency, or 'Free'.
|
||||
"""
|
||||
return get_course_prices(course)[1]
|
||||
|
||||
|
||||
def get_course_prices(course, verified_only=False):
|
||||
"""
|
||||
Return registration_price and cosmetic_display_prices.
|
||||
registration_price is the minimum price for the course across all course modes.
|
||||
cosmetic_display_prices is the course price as a string preceded by correct currency, or 'Free'.
|
||||
"""
|
||||
# Find the
|
||||
if verified_only:
|
||||
registration_price = CourseMode.min_course_price_for_verified_for_currency(
|
||||
course.id,
|
||||
settings.PAID_COURSE_REGISTRATION_CURRENCY[0]
|
||||
)
|
||||
else:
|
||||
registration_price = CourseMode.min_course_price_for_currency(
|
||||
course.id,
|
||||
settings.PAID_COURSE_REGISTRATION_CURRENCY[0]
|
||||
)
|
||||
|
||||
currency_symbol = settings.PAID_COURSE_REGISTRATION_CURRENCY[1]
|
||||
|
||||
price = course.cosmetic_display_price
|
||||
if registration_price > 0:
|
||||
price = registration_price
|
||||
# Handle course overview objects which have no cosmetic_display_price
|
||||
elif hasattr(course, 'cosmetic_display_price'):
|
||||
price = course.cosmetic_display_price
|
||||
else:
|
||||
price = None
|
||||
|
||||
if price:
|
||||
# Translators: This will look like '$50', where {currency_symbol} is a symbol such as '$' and {price} is a
|
||||
# numerical amount in that currency. Adjust this display as needed for your language.
|
||||
return _("{currency_symbol}{price}").format(currency_symbol=currency_symbol, price=price)
|
||||
cosmetic_display_price = _("{currency_symbol}{price}").format(currency_symbol=currency_symbol, price=price)
|
||||
else:
|
||||
# Translators: This refers to the cost of the course. In this case, the course costs nothing so it is free.
|
||||
return _('Free')
|
||||
cosmetic_display_price = _('Free')
|
||||
|
||||
return registration_price, cosmetic_display_price
|
||||
|
||||
|
||||
class EnrollStaffView(View):
|
||||
@@ -720,12 +736,7 @@ def course_about(request, course_id):
|
||||
if professional_mode.bulk_sku:
|
||||
ecommerce_bulk_checkout_link = ecomm_service.checkout_page_url(professional_mode.bulk_sku)
|
||||
|
||||
# Find the minimum price for the course across all course modes
|
||||
registration_price = CourseMode.min_course_price_for_currency(
|
||||
course_key,
|
||||
settings.PAID_COURSE_REGISTRATION_CURRENCY[0]
|
||||
)
|
||||
course_price = get_cosmetic_display_price(course, registration_price)
|
||||
registration_price, course_price = get_course_prices(course)
|
||||
|
||||
# Determine which checkout workflow to use -- LMS shoppingcart or Otto basket
|
||||
can_add_course_to_cart = _is_shopping_cart_enabled and registration_price and not ecommerce_checkout_link
|
||||
@@ -889,6 +900,8 @@ def _progress(request, course_key, student_id):
|
||||
'passed': is_course_passed(course, grade_summary),
|
||||
'credit_course_requirements': _credit_course_requirements(course_key, student),
|
||||
'certificate_data': _get_cert_data(student, course, course_key, is_active, enrollment_mode),
|
||||
'upgrade_link': check_and_get_upgrade_link(request, student, course.id),
|
||||
'upgrade_price': get_cosmetic_verified_display_price(course),
|
||||
}
|
||||
|
||||
with outer_atomic():
|
||||
|
||||
@@ -39,6 +39,8 @@ from django_comment_client.utils import (
|
||||
strip_none
|
||||
)
|
||||
from django_comment_common.utils import ThreadContext, get_course_discussion_settings, set_course_discussion_settings
|
||||
from lms.djangoapps.courseware.views.views import check_and_get_upgrade_link, get_cosmetic_verified_display_price
|
||||
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from openedx.core.djangoapps.plugin_api.views import EdxFragmentView
|
||||
from rest_framework import status
|
||||
@@ -440,7 +442,9 @@ def _create_discussion_board_context(request, course_key, discussion_id=None, th
|
||||
'sort_preference': cc_user.default_sort_key,
|
||||
'category_map': course_settings["category_map"],
|
||||
'course_settings': course_settings,
|
||||
'is_commentable_divided': is_commentable_divided(course_key, discussion_id, course_discussion_settings)
|
||||
'is_commentable_divided': is_commentable_divided(course_key, discussion_id, course_discussion_settings),
|
||||
'upgrade_link': check_and_get_upgrade_link(request, user, course.id),
|
||||
'upgrade_price': get_cosmetic_verified_display_price(course),
|
||||
})
|
||||
return context
|
||||
|
||||
|
||||
@@ -82,26 +82,6 @@ from openedx.core.djangolib.markup import HTML, Text
|
||||
</div>
|
||||
% endif
|
||||
|
||||
% if upgrade_link:
|
||||
<div class="upgrade-banner">
|
||||
<div class="notification-color-border"></div>
|
||||
<div class="notification-content">
|
||||
<div class="upgrade-icon">
|
||||
<img src="${STATIC_URL}images/edx-verified-mini-cert.png">
|
||||
</div>
|
||||
<div class="upgrade-msg">
|
||||
<h3 class="msg-title">${_("Give yourself an additional incentive to complete")}</h3>
|
||||
<p class="view-verified-info">${_("Earn a verified certificate.")}
|
||||
<a href="https://www.edx.org/verified-certificate" target="_blank">${_("Learn More")}</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="upgrade-banner-button">
|
||||
<a href="${upgrade_link}" class="btn-upgrade">${_("Upgrade Now")}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
|
||||
<h3 class="hd hd-3">${_("Course Updates and News")}</h3>
|
||||
${HTML(get_course_info_section(request, masquerade_user, course, 'updates'))}
|
||||
|
||||
|
||||
7
lms/templates/courseware/upgrade.html
Normal file
7
lms/templates/courseware/upgrade.html
Normal file
@@ -0,0 +1,7 @@
|
||||
% if upgrade_link:
|
||||
<script type="text/plain"
|
||||
id="upgrade_user"
|
||||
data-link="${upgrade_link}"
|
||||
data-price="${upgrade_price}">
|
||||
</script>
|
||||
% endif
|
||||
@@ -98,6 +98,7 @@ from pipeline_mako import render_require_js_path_overrides
|
||||
<%block name="headextra"/>
|
||||
<%block name="head_extra"/>
|
||||
|
||||
<%include file="/courseware/upgrade.html"/>
|
||||
<%static:optional_include_mako file="head-extra.html" is_theming_enabled="True" />
|
||||
|
||||
<%include file="widgets/optimizely.html" />
|
||||
|
||||
@@ -89,7 +89,7 @@ class TestCourseHomePage(SharedModuleStoreTestCase):
|
||||
course_home_url(self.course)
|
||||
|
||||
# Fetch the view and verify the query counts
|
||||
with self.assertNumQueries(42):
|
||||
with self.assertNumQueries(45):
|
||||
with check_mongo_calls(5):
|
||||
url = course_home_url(self.course)
|
||||
self.client.get(url)
|
||||
|
||||
@@ -124,7 +124,7 @@ class TestCourseUpdatesPage(SharedModuleStoreTestCase):
|
||||
course_updates_url(self.course)
|
||||
|
||||
# Fetch the view and verify that the query counts haven't changed
|
||||
with self.assertNumQueries(32):
|
||||
with self.assertNumQueries(34):
|
||||
with check_mongo_calls(4):
|
||||
url = course_updates_url(self.course)
|
||||
self.client.get(url)
|
||||
|
||||
@@ -8,6 +8,7 @@ from opaque_keys.edx.keys import CourseKey
|
||||
from web_fragments.fragment import Fragment
|
||||
|
||||
from courseware.courses import get_course_overview_with_access
|
||||
from lms.djangoapps.courseware.views.views import check_and_get_upgrade_link, get_cosmetic_verified_display_price
|
||||
from openedx.core.djangoapps.plugin_api.views import EdxFragmentView
|
||||
|
||||
from ..utils import get_course_outline_block_tree
|
||||
@@ -30,7 +31,9 @@ class CourseOutlineFragmentView(EdxFragmentView):
|
||||
context = {
|
||||
'csrf': csrf(request)['csrf_token'],
|
||||
'course': course_overview,
|
||||
'blocks': course_block_tree
|
||||
'blocks': course_block_tree,
|
||||
'upgrade_link': check_and_get_upgrade_link(request, request.user, course_key),
|
||||
'upgrade_price': get_cosmetic_verified_display_price(course_overview),
|
||||
}
|
||||
html = render_to_string('course_experience/course-outline-fragment.html', context)
|
||||
return Fragment(html)
|
||||
|
||||
Reference in New Issue
Block a user