From 75dc4459cde25e95204dcbcaf6d3793af0993ea3 Mon Sep 17 00:00:00 2001 From: Matthew Piatetsky Date: Mon, 29 Apr 2019 16:57:41 -0400 Subject: [PATCH] add first purchase discount banner on course home --- .../sass/features/_course-experience.scss | 17 +++++++++++++++++ openedx/features/course_experience/__init__.py | 4 ++++ .../course-home-fragment.html | 3 +++ .../tests/views/test_course_home.py | 15 +++++++++++++++ openedx/features/course_experience/utils.py | 18 ++++++++++++++++++ .../course_experience/views/course_home.py | 10 +++++++++- 6 files changed, 66 insertions(+), 1 deletion(-) diff --git a/lms/static/sass/features/_course-experience.scss b/lms/static/sass/features/_course-experience.scss index 70b7789846..0533e5308e 100644 --- a/lms/static/sass/features/_course-experience.scss +++ b/lms/static/sass/features/_course-experience.scss @@ -20,6 +20,23 @@ } } +// First purchase offer banner +.first-purchase-offer-banner { + background-color: #DEE3F1; + font-size: 16px; + border-radius: 7px; + padding: 20px; + margin: 20px auto; + + .first-purchase-offer-banner-bold { + font-weight: bold; + color: #23419F; + margin-right: 3px; + margin-left: 5px; + } + +} + // Course call to action message .course-message { display: flex; diff --git a/openedx/features/course_experience/__init__.py b/openedx/features/course_experience/__init__.py index 807bc6153f..33202a2bae 100644 --- a/openedx/features/course_experience/__init__.py +++ b/openedx/features/course_experience/__init__.py @@ -47,6 +47,10 @@ USE_BOOTSTRAP_FLAG = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'use_bootstrap', fl SEO_WAFFLE_FLAG_NAMESPACE = WaffleFlagNamespace(name='seo') COURSE_ENABLE_UNENROLLED_ACCESS_FLAG = CourseWaffleFlag(SEO_WAFFLE_FLAG_NAMESPACE, 'enable_anonymous_courseware_access') +# Flag to control display of first purchase offer banner +FIRST_PURCHASE_OFFER_BANNER = WaffleFlagNamespace(name='first_purchase_offer_banner') +FIRST_PURCHASE_OFFER_BANNER_DISPLAY = WaffleFlag(FIRST_PURCHASE_OFFER_BANNER, 'display', flag_undefined_default=False) + def course_home_page_title(course): # pylint: disable=unused-argument """ diff --git a/openedx/features/course_experience/templates/course_experience/course-home-fragment.html b/openedx/features/course_experience/templates/course_experience/course-home-fragment.html index c6dee98b27..4512cf6cb1 100644 --- a/openedx/features/course_experience/templates/course_experience/course-home-fragment.html +++ b/openedx/features/course_experience/templates/course_experience/course-home-fragment.html @@ -81,6 +81,9 @@ from openedx.features.portfolio_project import INCLUDE_PORTFOLIO_UPSELL_MODAL
+ % if offer_banner_fragment: + ${HTML(offer_banner_fragment.content)} + % endif % if course_expiration_fragment: ${HTML(course_expiration_fragment.content)} % endif diff --git a/openedx/features/course_experience/tests/views/test_course_home.py b/openedx/features/course_experience/tests/views/test_course_home.py index 3b179f894c..639b6756de 100644 --- a/openedx/features/course_experience/tests/views/test_course_home.py +++ b/openedx/features/course_experience/tests/views/test_course_home.py @@ -21,6 +21,7 @@ from waffle.testutils import override_flag from course_modes.models import CourseMode from course_modes.tests.factories import CourseModeFactory from courseware.tests.helpers import get_expiration_banner_text + from django_comment_client.tests.factories import RoleFactory from django_comment_common.models import ( FORUM_ROLE_ADMINISTRATOR, @@ -48,6 +49,7 @@ from openedx.features.course_duration_limits.config import EXPERIMENT_DATA_HOLDB from openedx.features.course_duration_limits.models import CourseDurationLimitConfig from openedx.features.course_experience import ( COURSE_ENABLE_UNENROLLED_ACCESS_FLAG, + FIRST_PURCHASE_OFFER_BANNER_DISPLAY, SHOW_REVIEWS_TOOL_FLAG, SHOW_UPGRADE_MSG_ON_COURSE_HOME, UNIFIED_COURSE_TAB_FLAG @@ -409,6 +411,19 @@ class TestCourseHomePageAccess(CourseHomePageTestCase): ) self.assertRedirects(response, expected_url) + @override_waffle_flag(FIRST_PURCHASE_OFFER_BANNER_DISPLAY, active=True) + def test_first_purchase_offer_banner(self): + """ + Ensure first purchase offer banner displays correctly + """ + user = self.create_user_for_course(self.course, CourseUserType.ENROLLED) + self.client.login(username=user.username, password=self.TEST_PASSWORD) + url = course_home_url(self.course) + response = self.client.get(url) + bannerText = u'''
+ 15% off your first upgrade. Discount automatically applied.
''' + self.assertContains(response, bannerText, html=True) + @mock.patch.dict(settings.FEATURES, {'DISABLE_START_DATES': False}) def test_course_does_not_expire_for_verified_user(self): """ diff --git a/openedx/features/course_experience/utils.py b/openedx/features/course_experience/utils.py index 3637b4731b..f52a6479d1 100644 --- a/openedx/features/course_experience/utils.py +++ b/openedx/features/course_experience/utils.py @@ -2,11 +2,15 @@ Common utilities for the course experience, including course outline. """ from completion.models import BlockCompletion +from django.utils.translation import ugettext as _ from lms.djangoapps.course_api.blocks.api import get_blocks from lms.djangoapps.course_blocks.utils import get_student_module_as_dict from opaque_keys.edx.keys import CourseKey +from openedx.core.djangolib.markup import HTML from openedx.core.lib.cache_utils import request_cached +from openedx.features.course_experience import FIRST_PURCHASE_OFFER_BANNER_DISPLAY +from web_fragments.fragment import Fragment from xmodule.modulestore.django import modulestore @@ -182,3 +186,17 @@ def get_resume_block(block): if resume_block: return resume_block return block + + +def get_first_purchase_offer_banner_fragment(user, course): + if FIRST_PURCHASE_OFFER_BANNER_DISPLAY.is_enabled() and user and course: + # Translator: xgettext:no-python-format + offer_message = _(u'{banner_open}15% off your first upgrade.{span_close}' + u' Discount automatically applied.{div_close}') + return Fragment(HTML(offer_message).format( + banner_open=HTML( + '
' + ), + span_close=HTML(''), + div_close=HTML('
') + )) diff --git a/openedx/features/course_experience/views/course_home.py b/openedx/features/course_experience/views/course_home.py index e740fd8cc5..8b4796ed90 100644 --- a/openedx/features/course_experience/views/course_home.py +++ b/openedx/features/course_experience/views/course_home.py @@ -31,6 +31,7 @@ from openedx.core.djangoapps.plugin_api.views import EdxFragmentView from openedx.core.djangoapps.util.maintenance_banner import add_maintenance_banner from openedx.features.course_duration_limits.access import generate_course_expired_fragment from openedx.features.course_experience.course_tools import CourseToolsPluginManager +from openedx.features.course_experience.utils import get_first_purchase_offer_banner_fragment from student.models import CourseEnrollment from util.views import ensure_valid_course_key from xmodule.course_module import COURSE_VISIBILITY_PUBLIC, COURSE_VISIBILITY_PUBLIC_OUTLINE @@ -139,6 +140,7 @@ class CourseHomeFragmentView(EdxFragmentView): outline_fragment = None update_message_fragment = None course_sock_fragment = None + offer_banner_fragment = None course_expiration_fragment = None has_visited_course = None resume_course_url = None @@ -159,9 +161,14 @@ class CourseHomeFragmentView(EdxFragmentView): course_sock_fragment = CourseSockFragmentView().render_to_fragment(request, course=course, **kwargs) has_visited_course, resume_course_url = self._get_resume_course_info(request, course_id) handouts_html = self._get_course_handouts(request, course) + course_overview = CourseOverview.get_from_id(course.id) + offer_banner_fragment = get_first_purchase_offer_banner_fragment( + request.user, + course_overview + ) course_expiration_fragment = generate_course_expired_fragment( request.user, - CourseOverview.get_from_id(course.id) + course_overview ) elif allow_public_outline or allow_public: outline_fragment = CourseOutlineFragmentView().render_to_fragment( @@ -212,6 +219,7 @@ class CourseHomeFragmentView(EdxFragmentView): 'outline_fragment': outline_fragment, 'handouts_html': handouts_html, 'course_home_message_fragment': course_home_message_fragment, + 'offer_banner_fragment': offer_banner_fragment, 'course_expiration_fragment': course_expiration_fragment, 'has_visited_course': has_visited_course, 'resume_course_url': resume_course_url,