Beispiel #1
0
def generate_offer_data(user, course):
    """
    Create a dictionary of information about the current discount offer.

    Used by serializers to pass onto frontends and by the LMS locally to generate HTML for template rendering.

    Returns a dictionary of data, or None if no offer is applicable.
    """
    if not user or not course or user.is_anonymous:
        return None

    ExperimentData.objects.get_or_create(
        user=user, experiment_id=REV1008_EXPERIMENT_ID, key=str(course),
        defaults={
            'value': datetime.now(tz=pytz.UTC).strftime('%Y-%m-%d %H:%M:%S%z'),
        },
    )

    expiration_date = get_discount_expiration_date(user, course)
    if not expiration_date:
        return None

    if not can_receive_discount(user, course, discount_expiration_date=expiration_date):
        return None

    original, discounted, percentage = _get_discount_prices(user, course, assume_discount=True)

    return {
        'code': 'BIENVENIDOAEDX' if get_language() == 'es-419' else 'EDXWELCOME',
        'expiration_date': expiration_date,
        'original_price': original,
        'discounted_price': discounted,
        'percentage': percentage,
        'upgrade_url': verified_upgrade_deadline_link(user, course=course),
    }
Beispiel #2
0
 def get_dates_banner_info(self, _):
     """
     Serializer mixin for returning date banner info.  Gets its input from
     the views course_key_string url parameter and the request's user object.
     """
     info = {
         'missed_deadlines': False,
         'content_type_gating_enabled': False,
     }
     course_key_string = self.context['view'].kwargs.get(
         'course_key_string')
     if course_key_string:
         course_key = CourseKey.from_string(course_key_string)
         request = self.context['request']
         missed_deadlines, missed_gated_content = dates_banner_should_display(
             course_key, request.user)
         info['missed_deadlines'] = missed_deadlines
         info['missed_gated_content'] = missed_gated_content
         info[
             'content_type_gating_enabled'] = ContentTypeGatingConfig.enabled_for_enrollment(
                 user=request.user,
                 course_key=course_key,
             )
         info['verified_upgrade_link'] = verified_upgrade_deadline_link(
             request.user, course_id=course_key)
     return info
Beispiel #3
0
 def url(cls, course_key):
     """
     Returns the URL for this tool for the specified course key.
     """
     request = get_current_request()
     return verified_upgrade_deadline_link(request.user,
                                           course_id=course_key)
Beispiel #4
0
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
Beispiel #5
0
    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), value=now_time
        )
        self.client.login(username=user.username, password=self.TEST_PASSWORD)
        url = course_home_url(self.course)
        response = self.client.get(url)
        discount_expiration_date = get_discount_expiration_date(user, self.course).strftime(u'%B %d')
        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">
             Upgrade by {discount_expiration_date} and save {percentage}% [{strikeout_price}]</span>
             <br>Use code <b>EDXWELCOME</b> at checkout! <a href="{upgrade_link}">Upgrade Now</a>
             </div>'''.format(
            discount_expiration_date=discount_expiration_date,
            percentage=percentage,
            strikeout_price=HTML(format_strikeout_price(user, self.course, check_for_discount=False)[0]),
            upgrade_link=upgrade_link
        )

        if applicability:
            self.assertContains(response, bannerText, html=True)
        else:
            self.assertNotContains(response, bannerText, html=True)
Beispiel #6
0
def check_and_get_upgrade_link_and_date(user, enrollment=None, course=None):
    """
    For an authenticated user, return a link to allow them to upgrade
    in the specified course.

    Returns the upgrade link and upgrade deadline for a user in a given course given
    that the user is within the window to upgrade defined by our dynamic pacing feature;
    otherwise, returns None for both the link and date.
    """
    if enrollment is None and course is None:
        logger.warning(u'Must specify either an enrollment or a course')
        return (None, None, None)

    if enrollment:
        if course and enrollment.course_id != course.id:
            logger.warning(u'{} refers to a different course than {} which was supplied. Enrollment course id={}, '
                           u'repr={!r}, deprecated={}. Course id={}, repr={!r}, deprecated={}.'
                           .format(enrollment,
                                   course,
                                   enrollment.course_id,
                                   enrollment.course_id,
                                   enrollment.course_id.deprecated,
                                   course.id,
                                   course.id,
                                   course.id.deprecated
                                   )
                           )
            return (None, None, None)

        if enrollment.user_id != user.id:
            logger.warning(u'{} refers to a different user than {} which was supplied. '
                           u'Enrollment user id={}, repr={!r}. '
                           u'User id={}, repr={!r}.'.format(enrollment,
                                                            user,
                                                            enrollment.user_id,
                                                            enrollment.user_id,
                                                            user.id,
                                                            user.id,
                                                            )
                           )
            return (None, None, None)

    if enrollment is None:
        enrollment = CourseEnrollment.get_enrollment(user, course.id)
        if enrollment is None:
            return (None, None, None)

    if user.is_authenticated and can_show_verified_upgrade(user, enrollment, course):
        return (
            verified_upgrade_deadline_link(user, enrollment.course),
            enrollment.upgrade_deadline,
            enrollment.course_upgrade_deadline,
        )

    return (None, None, enrollment.course_upgrade_deadline)
Beispiel #7
0
 def verified_mode(self):
     """
     Return verified mode information, or None.
     """
     mode = CourseMode.verified_mode_for_course(self.course_key)
     if mode:
         return {
             'price': mode.min_price,
             'currency': mode.currency.upper(),
             'currency_symbol': get_currency_symbol(mode.currency.upper()),
             'sku': mode.sku,
             'upgrade_url': verified_upgrade_deadline_link(self.effective_user, self.overview),
         }
Beispiel #8
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
Beispiel #9
0
def serialize_upgrade_info(user, course_overview, enrollment):
    """
    Return verified mode upgrade information, or None.

    This is used in a few API views to provide consistent upgrade info to frontends.
    """
    if not can_show_verified_upgrade(user, enrollment):
        return None

    mode = CourseMode.verified_mode_for_course(course=course_overview)
    return {
        'access_expiration_date': get_user_course_expiration_date(user, course_overview),
        'currency': mode.currency.upper(),
        'currency_symbol': get_currency_symbol(mode.currency.upper()),
        'price': mode.min_price,
        'sku': mode.sku,
        'upgrade_url': verified_upgrade_deadline_link(user, course_overview),
    }
Beispiel #10
0
    def get_verification_context(request, course):
        enrollment = CourseEnrollment.get_enrollment(request.user, course.id)
        show_course_sock = can_show_verified_upgrade(request.user, enrollment, course)
        if show_course_sock:
            upgrade_url = verified_upgrade_deadline_link(request.user, course=course)
            course_price, _ = format_strikeout_price(request.user, course)
        else:
            upgrade_url = ''
            course_price = ''

        context = {
            'show_course_sock': show_course_sock,
            'course_price': course_price,
            'course_id': course.id,
            'upgrade_url': upgrade_url,
        }

        return context
Beispiel #11
0
def get_expiration_banner_text(user, course, language='en'):
    """
    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

    date_string = u'<span class="localized-datetime" data-format="shortDate" data-timezone="None" \
        data-datetime="{formatted_date}" data-language="{language}">{formatted_date_localized}</span>'
    formatted_expiration_date = date_string.format(
        language=language,
        formatted_date=expiration_date.isoformat(),
        formatted_date_localized=strftime_localized(expiration_date, EXPIRATION_DATE_FORMAT_STR)
    )
    if upgrade_deadline:
        formatted_upgrade_deadline = date_string.format(
            language=language,
            formatted_date=upgrade_deadline.isoformat(),
            formatted_date_localized=strftime_localized(upgrade_deadline, EXPIRATION_DATE_FORMAT_STR)
        )

        bannerText = u'<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 = u'<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 get_access_expiration_data(user, course):
    """
    Create a dictionary of information about the access expiration for this user & course.

    Used by serializers to pass onto frontends and by the LMS locally to generate HTML for template rendering.

    Returns a dictionary of data, or None if no expiration is applicable.
    """
    expiration_date = get_user_course_expiration_date(user, course)
    if not expiration_date:
        return None

    enrollment = CourseEnrollment.get_enrollment(user, course.id)
    if enrollment is None:
        return None

    now = timezone.now()
    upgrade_deadline = enrollment.upgrade_deadline
    if not upgrade_deadline or upgrade_deadline < now:
        upgrade_deadline = enrollment.course_upgrade_deadline
    if upgrade_deadline and upgrade_deadline < now:
        upgrade_deadline = None

    masquerading_expired_course = is_masquerading_as_specific_student(
        user, course.id) and expiration_date < now

    return {
        'expiration_date':
        expiration_date,
        'masquerading_expired_course':
        masquerading_expired_course,
        'upgrade_deadline':
        upgrade_deadline,
        'upgrade_url':
        verified_upgrade_deadline_link(user, course=course)
        if upgrade_deadline else None,
    }
Beispiel #13
0
    def verified_mode(self):
        """
        Return verified mode information, or None.
        """
        if not can_show_verified_upgrade(self.effective_user,
                                         self.enrollment_object):
            return None

        mode = CourseMode.verified_mode_for_course(self.course_key)
        return {
            'access_expiration_date':
            get_user_course_expiration_date(self.effective_user,
                                            self.overview),
            'price':
            mode.min_price,
            'currency':
            mode.currency.upper(),
            'currency_symbol':
            get_currency_symbol(mode.currency.upper()),
            'sku':
            mode.sku,
            'upgrade_url':
            verified_upgrade_deadline_link(self.effective_user, self.overview),
        }
Beispiel #14
0
 def link(self):
     return verified_upgrade_deadline_link(self.user, self.course,
                                           self.course_id)
Beispiel #15
0
def generate_course_expired_message(user, course):
    """
    Generate the message for the user course expiration date if it exists.
    """
    if not CourseDurationLimitConfig.enabled_for_enrollment(
            user=user, course_key=course.id):
        return

    expiration_date = get_user_course_expiration_date(user, course)
    if not expiration_date:
        return

    if is_masquerading_as_specific_student(
            user, course.id) and timezone.now() > expiration_date:
        upgrade_message = _(
            'This learner does not have access to this course. '
            u'Their access expired on {expiration_date}.')
        return HTML(upgrade_message).format(expiration_date=strftime_localized(
            expiration_date, EXPIRATION_DATE_FORMAT_STR))
    else:
        enrollment = CourseEnrollment.get_enrollment(user, course.id)
        if enrollment is None:
            return

        upgrade_deadline = enrollment.upgrade_deadline
        now = timezone.now()
        course_upgrade_deadline = enrollment.course_upgrade_deadline
        if (not upgrade_deadline) or (upgrade_deadline < now):
            upgrade_deadline = course_upgrade_deadline

        expiration_message = _(
            u'{strong_open}Audit Access Expires {expiration_date}{strong_close}'
            u'{line_break}You lose all access to this course, including your progress, on '
            u'{expiration_date}.')
        upgrade_deadline_message = _(
            u'{line_break}Upgrade by {upgrade_deadline} to get unlimited access to the course '
            u'as long as it exists on the site. {a_open}Upgrade now{sronly_span_open} to '
            u'retain access past {expiration_date}{span_close}{a_close}')
        full_message = expiration_message
        if upgrade_deadline and now < upgrade_deadline:
            full_message += upgrade_deadline_message
            using_upgrade_messaging = True
        else:
            using_upgrade_messaging = False

        language = get_language()
        date_string = get_date_string()
        formatted_expiration_date = date_string.format(
            language=language,
            formatted_date=expiration_date.strftime("%Y-%m-%d"),
            formatted_date_localized=strftime_localized(
                expiration_date, EXPIRATION_DATE_FORMAT_STR))
        if using_upgrade_messaging:
            formatted_upgrade_deadline = date_string.format(
                language=language,
                formatted_date=upgrade_deadline.strftime("%Y-%m-%d"),
                formatted_date_localized=strftime_localized(
                    upgrade_deadline, EXPIRATION_DATE_FORMAT_STR))

            return HTML(full_message).format(
                a_open=HTML(u'<a href="{upgrade_link}">').format(
                    upgrade_link=verified_upgrade_deadline_link(
                        user=user, course=course)),
                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>'),
            )
Beispiel #16
0
def _get_verified_upgrade_link(user, schedule):
    enrollment = schedule.enrollment
    if enrollment.dynamic_upgrade_deadline is not None and can_show_verified_upgrade(user, enrollment):
        return verified_upgrade_deadline_link(user, enrollment.course)
Beispiel #17
0
    def render_to_fragment(self, request, course_id=None, **kwargs):  # lint-amnesty, pylint: disable=arguments-differ, too-many-statements
        """
        Renders the course's home page as a fragment.
        """
        course_key = CourseKey.from_string(course_id)
        course = get_course_with_access(request.user, 'load', course_key)

        # Render the course dates as a fragment
        dates_fragment = CourseDatesFragmentView().render_to_fragment(
            request, course_id=course_id, **kwargs)

        # Render the full content to enrolled users, as well as to course and global staff.
        # Unenrolled users who are not course or global staff are given only a subset.
        enrollment = CourseEnrollment.get_enrollment(request.user, course_key)
        user_access = {
            'is_anonymous': request.user.is_anonymous,
            'is_enrolled': enrollment and enrollment.is_active,
            'is_staff': has_access(request.user, 'staff', course_key),
        }

        allow_anonymous = COURSE_ENABLE_UNENROLLED_ACCESS_FLAG.is_enabled(
            course_key)
        allow_public = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC
        allow_public_outline = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC_OUTLINE

        # Set all the fragments
        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
        handouts_html = None

        course_overview = CourseOverview.get_from_id(course.id)
        if user_access['is_enrolled'] or user_access['is_staff']:
            outline_fragment = CourseOutlineFragmentView().render_to_fragment(
                request, course_id=course_id, **kwargs)
            if LATEST_UPDATE_FLAG.is_enabled(course_key):
                update_message_fragment = LatestUpdateFragmentView(
                ).render_to_fragment(request, course_id=course_id, **kwargs)
            else:
                update_message_fragment = WelcomeMessageFragmentView(
                ).render_to_fragment(request, course_id=course_id, **kwargs)
            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)
            offer_banner_fragment = get_first_purchase_offer_banner_fragment(
                request.user, course_overview)
            course_expiration_fragment = generate_course_expired_fragment(
                request.user, course_overview)
        elif allow_public_outline or allow_public:
            outline_fragment = CourseOutlineFragmentView().render_to_fragment(
                request, course_id=course_id, user_is_enrolled=False, **kwargs)
            course_sock_fragment = CourseSockFragmentView().render_to_fragment(
                request, course=course, **kwargs)
            if allow_public:
                handouts_html = self._get_course_handouts(request, course)
        else:
            # Redirect the user to the dashboard if they are not enrolled and
            # this is a course that does not support direct enrollment.
            if not can_self_enroll_in_course(course_key):
                raise CourseAccessRedirect(reverse('dashboard'))

        # Get the course tools enabled for this user and course
        course_tools = CourseToolsPluginManager.get_enabled_course_tools(
            request, course_key)

        # Check if the user can access the course goal functionality
        has_goal_permission = has_course_goal_permission(
            request, course_id, user_access)

        # Grab the current course goal and the acceptable course goal keys mapped to translated values
        current_goal = get_course_goal(request.user, course_key)
        goal_options = get_course_goal_options()

        # Get the course goals api endpoint
        goal_api_url = get_goal_api_url(request)

        # Grab the course home messages fragment to render any relevant django messages
        course_home_message_fragment = CourseHomeMessageFragmentView(
        ).render_to_fragment(request,
                             course_id=course_id,
                             user_access=user_access,
                             **kwargs)

        # Get info for upgrade messaging
        upgrade_price = None
        upgrade_url = None
        has_discount = False

        # TODO Add switch to control deployment
        if SHOW_UPGRADE_MSG_ON_COURSE_HOME.is_enabled(
                course_key) and can_show_verified_upgrade(
                    request.user, enrollment, course):
            upgrade_url = verified_upgrade_deadline_link(request.user,
                                                         course_id=course_key)
            upgrade_price, has_discount = format_strikeout_price(
                request.user, course_overview)

        show_search = (
            settings.FEATURES.get('ENABLE_COURSEWARE_SEARCH') or
            (settings.FEATURES.get('ENABLE_COURSEWARE_SEARCH_FOR_COURSE_STAFF')
             and user_access['is_staff']))
        # Render the course home fragment
        context = {
            'request': request,
            'csrf': csrf(request)['csrf_token'],
            'course': course,
            'course_key': course_key,
            '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,
            'course_tools': course_tools,
            'dates_fragment': dates_fragment,
            'username': request.user.username,
            'goal_api_url': goal_api_url,
            'has_goal_permission': has_goal_permission,
            'goal_options': goal_options,
            'current_goal': current_goal,
            'update_message_fragment': update_message_fragment,
            'course_sock_fragment': course_sock_fragment,
            'disable_courseware_js': True,
            'uses_bootstrap': True,
            'upgrade_price': upgrade_price,
            'upgrade_url': upgrade_url,
            'has_discount': has_discount,
            'show_search': show_search,
        }
        html = render_to_string('course_experience/course-home-fragment.html',
                                context)
        return Fragment(html)