Example #1
0
def get_first_purchase_offer_banner_fragment(user, course):
    """
    Return an HTML Fragment with First Purcahse Discount message,
    which has the discount_expiration_date, price,
    discount percentage and a link to upgrade.
    """
    if user and course:
        discount_expiration_date = get_discount_expiration_date(user, course)
        if (discount_expiration_date and
                can_receive_discount(user=user, course=course, discount_expiration_date=discount_expiration_date)):
            # Translator: xgettext:no-python-format
            offer_message = _(u'{banner_open} Upgrade by {discount_expiration_date} and save {percentage}% '
                              u'[{strikeout_price}]{span_close}{br}Discount will be automatically applied at checkout. '
                              u'{a_open}Upgrade Now{a_close}{div_close}')
            return Fragment(HTML(offer_message).format(
                a_open=HTML(u'<a href="{upgrade_link}">').format(
                    upgrade_link=verified_upgrade_deadline_link(user=user, course=course)
                ),
                a_close=HTML('</a>'),
                br=HTML('<br>'),
                banner_open=HTML(
                    '<div class="first-purchase-offer-banner"><span class="first-purchase-offer-banner-bold">'
                ),
                discount_expiration_date=discount_expiration_date.strftime(u'%B %d'),
                percentage=discount_percentage(course),
                span_close=HTML('</span>'),
                div_close=HTML('</div>'),
                strikeout_price=HTML(format_strikeout_price(user, course, check_for_discount=False)[0])
            ))
    return None
Example #2
0
def _get_discount_prices(user, course, assume_discount=False):
    """
    Return a tuple of (original, discounted, percentage)

    If assume_discount is True, we do not check if a discount applies and just go ahead with discount math anyway.

    Each returned price is a string with appropriate currency formatting added already.
    discounted and percentage will be returned as None if no discount is applicable.
    """
    base_price = get_course_prices(course, verified_only=True)[0]
    can_discount = assume_discount or can_receive_discount(user, course)

    if can_discount:
        percentage = discount_percentage(course)

        discounted_price = base_price * ((100.0 - percentage) / 100)
        if discounted_price:  # leave 0 prices alone, as format_course_price below will adjust to 'Free'
            if discounted_price == int(discounted_price):
                discounted_price = f'{discounted_price:0.0f}'
            else:
                discounted_price = f'{discounted_price:0.2f}'

        return format_course_price(base_price), format_course_price(
            discounted_price), percentage
    else:
        return format_course_price(base_price), None, None
Example #3
0
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.
    """
    if user and not user.is_anonymous and course:
        now = datetime.now(tz=pytz.UTC).strftime(u"%Y-%m-%d %H:%M:%S%z")
        saw_banner = ExperimentData.objects.filter(
            user=user, experiment_id=REV1008_EXPERIMENT_ID, key=str(course))
        if not saw_banner:
            ExperimentData.objects.create(user=user,
                                          experiment_id=REV1008_EXPERIMENT_ID,
                                          key=str(course),
                                          value=now)
        discount_expiration_date = get_discount_expiration_date(user, course)
        if (discount_expiration_date and can_receive_discount(
                user=user,
                course=course,
                discount_expiration_date=discount_expiration_date)):
            # Translator: xgettext:no-python-format
            offer_message = _(
                u'{banner_open} Upgrade by {discount_expiration_date} and save {percentage}% '
                u'[{strikeout_price}]{span_close}{br}Use code {b_open}{code}{b_close} at checkout! '
                u'{a_open}Upgrade Now{a_close}{div_close}')

            message_html = HTML(offer_message).format(
                a_open=HTML(u'<a id="welcome" href="{upgrade_link}">').format(
                    upgrade_link=verified_upgrade_deadline_link(
                        user=user, course=course)),
                a_close=HTML('</a>'),
                b_open=HTML('<b>'),
                code=Text('BIENVENIDOAEDX')
                if get_language() == 'es-419' else Text('EDXWELCOME'),
                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=discount_expiration_date.strftime(
                    u'%B %d'),
                percentage=discount_percentage(course),
                span_close=HTML('</b></span>'),
                div_close=HTML('</div>'),
                strikeout_price=HTML(
                    format_strikeout_price(user,
                                           course,
                                           check_for_discount=False)[0]))
            return message_html
    return None
Example #4
0
def format_strikeout_price(user,
                           course,
                           base_price=None,
                           check_for_discount=True):
    """
    Return a formatted price, including a struck-out original price if a discount applies, and also
        whether a discount was applied, as the tuple (formatted_price, has_discount).
    """
    if base_price is None:
        base_price = get_course_prices(course, verified_only=True)[0]

    original_price = format_course_price(base_price)

    if not check_for_discount or can_receive_discount(user, course):
        discount_price = base_price * (
            (100.0 - discount_percentage(course)) / 100)
        if discount_price == int(discount_price):
            discount_price = format_course_price(
                "{:0.0f}".format(discount_price))
        else:
            discount_price = format_course_price(
                "{:0.2f}".format(discount_price))

        # Separate out this string because it has a lot of syntax but no actual information for
        # translators to translate
        formatted_discount_price = HTML(
            u"{s_dp}{discount_price}{e_p} {s_st}{s_op}{original_price}{e_p}{e_st}"
        ).format(
            original_price=original_price,
            discount_price=discount_price,
            s_op=HTML("<span class='price original'>"),
            s_dp=HTML("<span class='price discount'>"),
            s_st=HTML("<del aria-hidden='true'>"),
            e_p=HTML("</span>"),
            e_st=HTML("</del>"),
        )

        return (HTML(
            _(u"{s_sr}Original price: {s_op}{original_price}{e_p}, discount price: {e_sr}{formatted_discount_price}"
              )).format(
                  original_price=original_price,
                  formatted_discount_price=formatted_discount_price,
                  s_sr=HTML("<span class='sr-only'>"),
                  s_op=HTML("<span class='price original'>"),
                  e_p=HTML("</span>"),
                  e_sr=HTML("</span>"),
              ), True)
    else:
        return (HTML(u"<span class='price'>{}</span>").format(original_price),
                False)
Example #5
0
def get_first_purchase_offer_banner_fragment(user, course):
    if user and course and can_receive_discount(user=user, course=course):
        # Translator: xgettext:no-python-format
        offer_message = _(
            u'{banner_open}{percentage}% off your first upgrade.{span_close}'
            u' Discount automatically applied.{div_close}')
        return Fragment(
            HTML(offer_message).format(banner_open=HTML(
                '<div class="first-purchase-offer-banner"><span class="first-purchase-offer-banner-bold">'
            ),
                                       percentage=discount_percentage(),
                                       span_close=HTML('</span>'),
                                       div_close=HTML('</div>')))
    return None
Example #6
0
def get_first_purchase_offer_banner_fragment(user, course):
    """
    Return an HTML Fragment with First Purcahse Discount message,
    which has the discount_expiration_date, price,
    discount percentage and a link to upgrade.
    """
    if user and not user.is_anonymous and course:
        now = datetime.now(tz=pytz.UTC)
        stop_bucketing_into_discount_experiment = datetime(
            2019, 11, 22, 0, 0, 0, 0, pytz.UTC)
        saw_banner = ExperimentData.objects.filter(
            user=user, experiment_id=REV1008_EXPERIMENT_ID, key=str(course))
        if not saw_banner and not now > stop_bucketing_into_discount_experiment:
            ExperimentData.objects.create(
                user=user,
                experiment_id=REV1008_EXPERIMENT_ID,
                key=str(course),
                value=now.strftime(u"%Y-%m-%d %H:%M:%S%z"))
        discount_expiration_date = get_discount_expiration_date(user, course)
        if (discount_expiration_date and can_receive_discount(
                user=user,
                course=course,
                discount_expiration_date=discount_expiration_date)):
            # Translator: xgettext:no-python-format
            offer_message = _(
                u'{banner_open} Upgrade by {discount_expiration_date} and save {percentage}% '
                u'[{strikeout_price}]{span_close}{br}Discount will be automatically applied at checkout. '
                u'{a_open}Upgrade Now{a_close}{div_close}')
            return Fragment(
                HTML(offer_message).format(
                    a_open=HTML(u'<a href="{upgrade_link}">').format(
                        upgrade_link=verified_upgrade_deadline_link(
                            user=user, course=course)),
                    a_close=HTML('</a>'),
                    br=HTML('<br>'),
                    banner_open=HTML(
                        '<div class="first-purchase-offer-banner" role="note">'
                        '<span class="first-purchase-offer-banner-bold">'),
                    discount_expiration_date=discount_expiration_date.strftime(
                        u'%B %d'),
                    percentage=discount_percentage(course),
                    span_close=HTML('</span>'),
                    div_close=HTML('</div>'),
                    strikeout_price=HTML(
                        format_strikeout_price(user,
                                               course,
                                               check_for_discount=False)[0])))
    return None
Example #7
0
def get_first_purchase_offer_banner_fragment(user, course):
    if (FIRST_PURCHASE_OFFER_BANNER_DISPLAY.is_enabled() and
            user and
            course and
            can_receive_discount(user=user, course_key_string=unicode(course.id))):
        # Translator: xgettext:no-python-format
        offer_message = _(u'{banner_open}{percentage}% off your first upgrade.{span_close}'
                          u' Discount automatically applied.{div_close}')
        return Fragment(HTML(offer_message).format(
            banner_open=HTML(
                '<div class="first-purchase-offer-banner"><span class="first-purchase-offer-banner-bold">'
            ),
            percentage=discount_percentage(),
            span_close=HTML('</span>'),
            div_close=HTML('</div>')
        ))
Example #8
0
def get_first_purchase_offer_banner_fragment(user, course):
    if (FIRST_PURCHASE_OFFER_BANNER_DISPLAY.is_enabled() and
            user and
            course and
            can_receive_discount(user=user, course=course)):
        # Translator: xgettext:no-python-format
        offer_message = _(u'{banner_open}{percentage}% off your first upgrade.{span_close}'
                          u' Discount automatically applied.{div_close}')
        return Fragment(HTML(offer_message).format(
            banner_open=HTML(
                '<div class="first-purchase-offer-banner"><span class="first-purchase-offer-banner-bold">'
            ),
            percentage=discount_percentage(),
            span_close=HTML('</span>'),
            div_close=HTML('</div>')
        ))
Example #9
0
    def get(self, request, course_id, error=None):
        """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_ip(request),
            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 non-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")
            verify_url = reverse('verify_student_start_flow', kwargs={'course_id': six.text_type(course_key)})
            redirect_url = "{url}?purchase_workflow={workflow}".format(url=verify_url, workflow=purchase_workflow)
            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 redirect(reverse('dashboard'))

        # 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]):
            # If the course has started redirect to course home instead
            if course.has_started():
                return redirect(reverse('openedx.course_experience.course_home', kwargs={'course_id': course_key}))
            return redirect(reverse('dashboard'))

        donation_for_course = request.session.get("donation_for_course", {})
        chosen_price = donation_for_course.get(six.text_type(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('{0}?{1}'.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 = text_type(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": ContentTypeGatingConfig.enabled_for_enrollment(
                user=request.user,
                course_key=course_key
            ),
            "course_duration_limit_enabled": CourseDurationLimitConfig.enabled_for_enrollment(
                user=request.user,
                course_key=course_key
            ),
        }
        context.update(
            get_experiment_user_metadata_context(
                course,
                request.user,
            )
        )

        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

            context["currency"] = verified_mode.currency.upper()
            context["min_price"] = price_before_discount
            context["verified_name"] = verified_mode.name
            context["verified_description"] = verified_mode.description

            offer_banner_fragment = get_first_purchase_offer_banner_fragment(
                request.user, course
            )
            if offer_banner_fragment:
                context['offer_banner_fragment'] = offer_banner_fragment
                discounted_price = "{:0.2f}".format(price_before_discount * ((100.0 - discount_percentage()) / 100))
                context["min_price"] = discounted_price
                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
        return render_to_response("course_modes/choose.html", context)