def get_expiration_banner_text(user, course, language='en'): # lint-amnesty, pylint: disable=unused-argument """ Get text for banner that messages user course expiration date for different tests that depend on it. """ upgrade_link = verified_upgrade_deadline_link(user=user, course=course) enrollment = CourseEnrollment.get_enrollment(user, course.id) expiration_date = enrollment.created + timedelta(weeks=4) upgrade_deadline = enrollment.upgrade_deadline if upgrade_deadline is None or now() < upgrade_deadline: upgrade_deadline = enrollment.course_upgrade_deadline formatted_expiration_date = strftime_localized_html(expiration_date, 'SHORT_DATE') if upgrade_deadline: formatted_upgrade_deadline = strftime_localized_html(upgrade_deadline, 'SHORT_DATE') bannerText = '<strong>Audit Access Expires {expiration_date}</strong><br>\ You lose all access to this course, including your progress, on {expiration_date}.\ <br>Upgrade by {upgrade_deadline} to get unlimited access to the course as long as it exists\ on the site. <a id="FBE_banner" href="{upgrade_link}">Upgrade now<span class="sr-only"> to retain access past\ {expiration_date}</span></a>'.format( expiration_date=formatted_expiration_date, upgrade_link=upgrade_link, upgrade_deadline=formatted_upgrade_deadline ) else: bannerText = '<strong>Audit Access Expires {expiration_date}</strong><br>\ You lose all access to this course, including your progress, on {expiration_date}.\ '.format( expiration_date=formatted_expiration_date ) return bannerText
def test_first_purchase_offer_banner_display(self, applicability, percentage, can_receive_discount_mock, discount_percentage_mock): """ Ensure first purchase offer banner displays correctly """ can_receive_discount_mock.return_value = applicability discount_percentage_mock.return_value = percentage user = self.create_user_for_course(self.course, CourseUserType.ENROLLED) now_time = datetime.now(tz=UTC).strftime(u"%Y-%m-%d %H:%M:%S%z") ExperimentData.objects.create( user=user, experiment_id=REV1008_EXPERIMENT_ID, key=str(self.course.id), value=now_time ) self.client.login(username=user.username, password=self.TEST_PASSWORD) url = course_home_url(self.course) response = self.client.get(url) expiration_date = strftime_localized_html(get_discount_expiration_date(user, self.course), 'SHORT_DATE') upgrade_link = verified_upgrade_deadline_link(user=user, course=self.course) bannerText = u'''<div class="first-purchase-offer-banner" role="note"> <span class="first-purchase-offer-banner-bold"><b> Upgrade by {discount_expiration_date} and save {percentage}% [{strikeout_price}]</b></span> <br>Use code <b>EDXWELCOME</b> at checkout! <a id="welcome" href="{upgrade_link}">Upgrade Now</a> </div>'''.format( discount_expiration_date=expiration_date, percentage=percentage, strikeout_price=HTML(format_strikeout_price(user, self.course)[0]), upgrade_link=upgrade_link ) if applicability: self.assertContains(response, bannerText, html=True) else: self.assertNotContains(response, bannerText, html=True)
def test_happy_path(self, timezone): dtime = datetime(2013, 2, 14, 16, 41, 17) with patch('common.djangoapps.util.date_utils.user_timezone_locale_prefs', return_value={'user_timezone': timezone}): html = strftime_localized_html(dtime, 'SHORT_DATE') assert isinstance(html, Markup) self.assertRegex(html, '<span class="localized-datetime" data-format="shortDate" data-timezone="%s" ' % timezone + '\\s*data-datetime="2013-02-14T16:41:17" data-language="en">Feb 14, 2013</span>')
def generate_offer_html(user, course): """ Create the actual HTML object with the offer text in it. Returns a openedx.core.djangolib.markup.HTML object, or None if the user should not be shown an offer message. """ data = generate_offer_data(user, course) if not data: return None # Translator: xgettext:no-python-format offer_message = _( '{banner_open} Upgrade by {discount_expiration_date} and save {percentage}% ' '[{strikeout_price}]{span_close}{br}Use code {b_open}{code}{b_close} at checkout! ' '{a_open}Upgrade Now{a_close}{div_close}') message_html = HTML(offer_message).format( a_open=HTML('<a id="welcome" href="{upgrade_link}">').format( upgrade_link=data['upgrade_url']), a_close=HTML('</a>'), b_open=HTML('<b>'), code=Text(data['code']), b_close=HTML('</b>'), br=HTML('<br>'), banner_open=HTML( '<div class="first-purchase-offer-banner" role="note">' '<span class="first-purchase-offer-banner-bold"><b>'), discount_expiration_date=strftime_localized_html( data['expiration_date'], 'SHORT_DATE'), percentage=data['percentage'], span_close=HTML('</b></span>'), div_close=HTML('</div>'), strikeout_price=_format_discounted_price(data['original_price'], data['discounted_price']), ) return message_html
def get(self, request, course_id, error=None): # lint-amnesty, pylint: disable=too-many-statements """Displays the course mode choice page. Args: request (`Request`): The Django Request object. course_id (unicode): The slash-separated course key. Keyword Args: error (unicode): If provided, display this error message on the page. Returns: Response """ course_key = CourseKey.from_string(course_id) # Check whether the user has access to this course # based on country access rules. embargo_redirect = embargo_api.redirect_if_blocked( course_key, user=request.user, ip_address=get_client_ip(request)[0], url=request.path) if embargo_redirect: return redirect(embargo_redirect) enrollment_mode, is_active = CourseEnrollment.enrollment_mode_for_user( request.user, course_key) increment('track-selection.{}.{}'.format( enrollment_mode, 'active' if is_active else 'inactive')) increment('track-selection.views') if enrollment_mode is None: LOG.info( 'Rendering track selection for unenrolled user, referred by %s', request.META.get('HTTP_REFERER')) modes = CourseMode.modes_for_course_dict(course_key) ecommerce_service = EcommerceService() # We assume that, if 'professional' is one of the modes, it should be the *only* mode. # If there are both modes, default to 'no-id-professional'. has_enrolled_professional = ( CourseMode.is_professional_slug(enrollment_mode) and is_active) if CourseMode.has_professional_mode( modes) and not has_enrolled_professional: purchase_workflow = request.GET.get("purchase_workflow", "single") redirect_url = IDVerificationService.get_verify_location( course_id=course_key) if ecommerce_service.is_enabled(request.user): professional_mode = modes.get( CourseMode.NO_ID_PROFESSIONAL_MODE) or modes.get( CourseMode.PROFESSIONAL) if purchase_workflow == "single" and professional_mode.sku: redirect_url = ecommerce_service.get_checkout_page_url( professional_mode.sku) if purchase_workflow == "bulk" and professional_mode.bulk_sku: redirect_url = ecommerce_service.get_checkout_page_url( professional_mode.bulk_sku) return redirect(redirect_url) course = modulestore().get_course(course_key) # If there isn't a verified mode available, then there's nothing # to do on this page. Send the user to the dashboard. if not CourseMode.has_verified_mode(modes): return self._redirect_to_course_or_dashboard( course, course_key, request.user) # If a user has already paid, redirect them to the dashboard. if is_active and (enrollment_mode in CourseMode.VERIFIED_MODES + [CourseMode.NO_ID_PROFESSIONAL_MODE]): return self._redirect_to_course_or_dashboard( course, course_key, request.user) donation_for_course = request.session.get("donation_for_course", {}) chosen_price = donation_for_course.get(str(course_key), None) if CourseEnrollment.is_enrollment_closed(request.user, course): locale = to_locale(get_language()) enrollment_end_date = format_datetime(course.enrollment_end, 'short', locale=locale) params = six.moves.urllib.parse.urlencode( {'course_closed': enrollment_end_date}) return redirect('{}?{}'.format(reverse('dashboard'), params)) # When a credit mode is available, students will be given the option # to upgrade from a verified mode to a credit mode at the end of the course. # This allows students who have completed photo verification to be eligible # for university credit. # Since credit isn't one of the selectable options on the track selection page, # we need to check *all* available course modes in order to determine whether # a credit mode is available. If so, then we show slightly different messaging # for the verified track. has_credit_upsell = any( CourseMode.is_credit_mode(mode) for mode in CourseMode.modes_for_course(course_key, only_selectable=False)) course_id = str(course_key) gated_content = ContentTypeGatingConfig.enabled_for_enrollment( user=request.user, course_key=course_key) context = { "course_modes_choose_url": reverse("course_modes_choose", kwargs={'course_id': course_id}), "modes": modes, "has_credit_upsell": has_credit_upsell, "course_name": course.display_name_with_default, "course_org": course.display_org_with_default, "course_num": course.display_number_with_default, "chosen_price": chosen_price, "error": error, "responsive": True, "nav_hidden": True, "content_gating_enabled": gated_content, "course_duration_limit_enabled": CourseDurationLimitConfig.enabled_for_enrollment( request.user, course), } context.update( get_experiment_user_metadata_context( course, request.user, )) title_content = '' if enrollment_mode: title_content = _( "Congratulations! You are now enrolled in {course_name}" ).format(course_name=course.display_name_with_default) context["title_content"] = title_content if "verified" in modes: verified_mode = modes["verified"] context["suggested_prices"] = [ decimal.Decimal(x.strip()) for x in verified_mode.suggested_prices.split(",") if x.strip() ] price_before_discount = verified_mode.min_price course_price = price_before_discount enterprise_customer = enterprise_customer_for_request(request) LOG.info( '[e-commerce calculate API] Going to hit the API for user [%s] linked to [%s] enterprise', request.user.username, enterprise_customer.get('name') if isinstance( enterprise_customer, dict) else None # Test Purpose ) if enterprise_customer and verified_mode.sku: course_price = get_course_final_price(request.user, verified_mode.sku, price_before_discount) context["currency"] = verified_mode.currency.upper() context["currency_symbol"] = get_currency_symbol( verified_mode.currency.upper()) context["min_price"] = course_price context["verified_name"] = verified_mode.name context["verified_description"] = verified_mode.description # if course_price is equal to price_before_discount then user doesn't entitle to any discount. if course_price != price_before_discount: context["price_before_discount"] = price_before_discount if verified_mode.sku: context[ "use_ecommerce_payment_flow"] = ecommerce_service.is_enabled( request.user) context[ "ecommerce_payment_page"] = ecommerce_service.payment_page_url( ) context["sku"] = verified_mode.sku context["bulk_sku"] = verified_mode.bulk_sku context['currency_data'] = [] if waffle.switch_is_active('local_currency'): if 'edx-price-l10n' not in request.COOKIES: currency_data = get_currency_data() try: context['currency_data'] = json.dumps(currency_data) except TypeError: pass language = get_language() context['track_links'] = get_verified_track_links(language) duration = get_user_course_duration(request.user, course) deadline = duration and get_user_course_expiration_date( request.user, course) if deadline: formatted_audit_access_date = strftime_localized_html( deadline, 'SHORT_DATE') context['audit_access_deadline'] = formatted_audit_access_date fbe_is_on = deadline and gated_content # Route to correct Track Selection page. # REV-2133 TODO Value Prop: remove waffle flag after testing is completed # and happy path version is ready to be rolled out to all users. if VALUE_PROP_TRACK_SELECTION_FLAG.is_enabled(): if not error: # TODO: Remove by executing REV-2355 if not enterprise_customer_for_request( request): # TODO: Remove by executing REV-2342 if fbe_is_on: return render_to_response("course_modes/fbe.html", context) else: return render_to_response("course_modes/unfbe.html", context) # If error or enterprise_customer, failover to old choose.html page return render_to_response("course_modes/choose.html", context)
def test_invalid_format_string(self): dtime = datetime(2013, 2, 14, 16, 41, 17) with self.assertRaisesRegex( AssertionError, 'format "NOPE" not yet supported in strftime_localized_html'): strftime_localized_html(dtime, 'NOPE')
def generate_course_expired_message(user, course): """ Generate the message for the user course expiration date if it exists. """ expiration_data = get_access_expiration_data(user, course) if not expiration_data: return expiration_date = expiration_data['expiration_date'] masquerading_expired_course = expiration_data[ 'masquerading_expired_course'] upgrade_deadline = expiration_data['upgrade_deadline'] upgrade_url = expiration_data['upgrade_url'] if masquerading_expired_course: upgrade_message = _( 'This learner does not have access to this course. ' 'Their access expired on {expiration_date}.') return HTML(upgrade_message).format( expiration_date=strftime_localized_html(expiration_date, 'SHORT_DATE')) else: expiration_message = _( '{strong_open}Audit Access Expires {expiration_date}{strong_close}' '{line_break}You lose all access to this course, including your progress, on ' '{expiration_date}.') upgrade_deadline_message = _( '{line_break}Upgrade by {upgrade_deadline} to get unlimited access to the course ' 'as long as it exists on the site. {a_open}Upgrade now{sronly_span_open} to ' 'retain access past {expiration_date}{span_close}{a_close}') full_message = expiration_message if upgrade_deadline and upgrade_url: full_message += upgrade_deadline_message using_upgrade_messaging = True else: using_upgrade_messaging = False formatted_expiration_date = strftime_localized_html( expiration_date, 'SHORT_DATE') if using_upgrade_messaging: formatted_upgrade_deadline = strftime_localized_html( upgrade_deadline, 'SHORT_DATE') return HTML(full_message).format( a_open=HTML('<a id="FBE_banner" href="{upgrade_link}">' ).format(upgrade_link=upgrade_url), sronly_span_open=HTML('<span class="sr-only">'), span_close=HTML('</span>'), a_close=HTML('</a>'), expiration_date=HTML(formatted_expiration_date), strong_open=HTML('<strong>'), strong_close=HTML('</strong>'), line_break=HTML('<br>'), upgrade_deadline=HTML(formatted_upgrade_deadline)) else: return HTML(full_message).format( span_close=HTML('</span>'), expiration_date=HTML(formatted_expiration_date), strong_open=HTML('<strong>'), strong_close=HTML('</strong>'), line_break=HTML('<br>'), )