Example #1
0
    def _lint_output(linter, count, violations_list, is_html=False, limit=0):
        """
        Given a count & list of pylint violations, pretty-print the output.
        If `is_html`, will print out with HTML markup.
        """
        if is_html:
            lines = ['<body>\n']
            sep = '-------------<br/>\n'
            title = HTML("<h1>Quality Report: {}</h1>\n").format(linter)
            violations_bullets = ''.join(
                [HTML('<li>{violation}</li><br/>\n').format(violation=violation) for violation in violations_list]
            )
            violations_str = HTML('<ul>\n{bullets}</ul>\n').format(bullets=HTML(violations_bullets))
            violations_count_str = HTML("<b>Violations</b>: {count}<br/>\n")
            fail_line = HTML("<b>FAILURE</b>: {} count should be 0<br/>\n").format(linter)
        else:
            lines = []
            sep = '-------------\n'
            title = "Quality Report: {}\n".format(linter)
            violations_str = ''.join(violations_list)
            violations_count_str = "Violations: {count}\n"
            fail_line = "FAILURE: {} count should be {}\n".format(linter, limit)

        violations_count_str = violations_count_str.format(count=count)

        lines.extend([sep, title, sep, violations_str, sep, violations_count_str])

        if count > limit > -1:
            lines.append(fail_line)
        lines.append(sep + '\n')
        if is_html:
            lines.append('</body>')

        return ''.join(lines)
Example #2
0
    def _add_honor_code_field(self, form_desc, required=True):
        """Add an honor code field to a form description.
        Arguments:
            form_desc: A form description
        Keyword Arguments:
            required (bool): Whether this field is required; defaults to True
        """

        separate_honor_and_tos = self._is_field_visible("terms_of_service")
        # Separate terms of service and honor code checkboxes
        if separate_honor_and_tos:
            terms_label = _(u"Honor Code")
            terms_link = marketing_link("HONOR")

        # Combine terms of service and honor code checkboxes
        else:
            # Translators: This is a legal document users must agree to
            # in order to register a new account.
            terms_label = _(u"Terms of Service and Honor Code")
            terms_link = marketing_link("HONOR")

        # Translators: "Terms of Service" is a legal document users must agree to
        # in order to register a new account.
        label = Text(
            _(u"I agree to the {platform_name} {terms_of_service_link_start}{terms_of_service}{terms_of_service_link_end}"
              )).format(
                  platform_name=configuration_helpers.get_value(
                      "PLATFORM_NAME", settings.PLATFORM_NAME),
                  terms_of_service=terms_label,
                  terms_of_service_link_start=HTML(
                      u"<a href='{terms_link}' rel='noopener' target='_blank'>"
                  ).format(terms_link=terms_link),
                  terms_of_service_link_end=HTML("</a>"),
              )

        # Translators: "Terms of Service" is a legal document users must agree to
        # in order to register a new account.
        error_msg = _(
            u"You must agree to the {platform_name} {terms_of_service}"
        ).format(platform_name=configuration_helpers.get_value(
            "PLATFORM_NAME", settings.PLATFORM_NAME),
                 terms_of_service=terms_label)
        field_type = 'checkbox'

        if not separate_honor_and_tos:
            field_type = 'plaintext'

            pp_link = marketing_link("PRIVACY")
            label = Text(
                _(u"By creating an account, you agree to the \
                  {terms_of_service_link_start}{terms_of_service}{terms_of_service_link_end} \
                  and you acknowledge that {platform_name} and each Member process your personal data in accordance \
                  with the {privacy_policy_link_start}Privacy Policy{privacy_policy_link_end}."
                  )
            ).format(
                platform_name=configuration_helpers.get_value(
                    "PLATFORM_NAME", settings.PLATFORM_NAME),
                terms_of_service=terms_label,
                terms_of_service_link_start=HTML(
                    u"<a href='{terms_url}' rel='noopener' target='_blank'>").
                format(terms_url=terms_link),
                terms_of_service_link_end=HTML("</a>"),
                privacy_policy_link_start=HTML(
                    u"<a href='{pp_url}' rel='noopener' target='_blank'>").
                format(pp_url=pp_link),
                privacy_policy_link_end=HTML("</a>"),
            )

        form_desc.add_field(
            "honor_code",
            label=label,
            field_type=field_type,
            default=False,
            required=required,
            error_messages={"required": error_msg},
        )
def instructor_dashboard_2(request, course_id):  # lint-amnesty, pylint: disable=too-many-statements
    """ Display the instructor dashboard for a course. """
    try:
        course_key = CourseKey.from_string(course_id)
    except InvalidKeyError:
        log.error(
            "Unable to find course with course key %s while loading the Instructor Dashboard.",
            course_id)
        return HttpResponseServerError()

    course = get_course_by_id(course_key, depth=0)

    access = {
        'admin':
        request.user.is_staff,
        'instructor':
        bool(has_access(request.user, 'instructor', course)),
        'finance_admin':
        CourseFinanceAdminRole(course_key).has_user(request.user),
        'sales_admin':
        CourseSalesAdminRole(course_key).has_user(request.user),
        'staff':
        bool(has_access(request.user, 'staff', course)),
        'forum_admin':
        has_forum_access(request.user, course_key, FORUM_ROLE_ADMINISTRATOR),
        'data_researcher':
        request.user.has_perm(permissions.CAN_RESEARCH, course_key),
    }

    if not request.user.has_perm(permissions.VIEW_DASHBOARD, course_key):
        raise Http404()

    is_white_label = CourseMode.is_white_label(course_key)  # lint-amnesty, pylint: disable=unused-variable

    reports_enabled = configuration_helpers.get_value('SHOW_ECOMMERCE_REPORTS',
                                                      False)  # lint-amnesty, pylint: disable=unused-variable

    sections = []
    if access['staff']:
        sections.extend([
            _section_course_info(course, access),
            _section_membership(course, access),
            _section_cohort_management(course, access),
            _section_discussions_management(course, access),
            _section_student_admin(course, access),
        ])
    if access['data_researcher']:
        sections.append(_section_data_download(course, access))

    analytics_dashboard_message = None
    if show_analytics_dashboard_message(course_key) and (access['staff'] or
                                                         access['instructor']):
        # Construct a URL to the external analytics dashboard
        analytics_dashboard_url = '{}/courses/{}'.format(
            settings.ANALYTICS_DASHBOARD_URL, str(course_key))
        link_start = HTML("<a href=\"{}\" rel=\"noopener\" target=\"_blank\">"
                          ).format(analytics_dashboard_url)
        analytics_dashboard_message = _(
            "To gain insights into student enrollment and participation {link_start}"
            "visit {analytics_dashboard_name}, our new course analytics product{link_end}."
        )
        analytics_dashboard_message = Text(analytics_dashboard_message).format(
            link_start=link_start,
            link_end=HTML("</a>"),
            analytics_dashboard_name=settings.ANALYTICS_DASHBOARD_NAME)

        # Temporarily show the "Analytics" section until we have a better way of linking to Insights
        sections.append(_section_analytics(course, access))

    # Check if there is corresponding entry in the CourseMode Table related to the Instructor Dashboard course
    course_mode_has_price = False  # lint-amnesty, pylint: disable=unused-variable
    paid_modes = CourseMode.paid_modes_for_course(course_key)
    if len(paid_modes) == 1:
        course_mode_has_price = True
    elif len(paid_modes) > 1:
        log.error(
            "Course %s has %s course modes with payment options. Course must only have "
            "one paid course mode to enable eCommerce options.",
            str(course_key), len(paid_modes))

    if access['instructor'] and is_enabled_for_course(course_key):
        sections.insert(3, _section_extensions(course))

    # Gate access to course email by feature flag & by course-specific authorization
    if is_bulk_email_feature_enabled(course_key) and (access['staff']
                                                      or access['instructor']):
        sections.append(_section_send_email(course, access))

    # Gate access to Special Exam tab depending if either timed exams or proctored exams
    # are enabled in the course

    user_has_access = any([
        request.user.is_staff,
        CourseStaffRole(course_key).has_user(request.user),
        CourseInstructorRole(course_key).has_user(request.user)
    ])
    course_has_special_exams = course.enable_proctored_exams or course.enable_timed_exams
    can_see_special_exams = course_has_special_exams and user_has_access and settings.FEATURES.get(
        'ENABLE_SPECIAL_EXAMS', False)

    if can_see_special_exams:
        sections.append(_section_special_exams(course, access))
    # Certificates panel
    # This is used to generate example certificates
    # and enable self-generated certificates for a course.
    # Note: This is hidden for all CCXs
    certs_enabled = CertificateGenerationConfiguration.current(
    ).enabled and not hasattr(course_key, 'ccx')
    if certs_enabled and access['admin']:
        sections.append(_section_certificates(course))

    openassessment_blocks = modulestore().get_items(
        course_key, qualifiers={'category': 'openassessment'})
    # filter out orphaned openassessment blocks
    openassessment_blocks = [
        block for block in openassessment_blocks if block.parent is not None
    ]
    if len(openassessment_blocks) > 0 and access['staff']:
        sections.append(
            _section_open_response_assessment(request, course,
                                              openassessment_blocks, access))

    disable_buttons = not CourseEnrollment.objects.is_small_course(course_key)

    certificate_white_list = CertificateWhitelist.get_certificate_white_list(
        course_key)
    generate_certificate_exceptions_url = reverse(
        'generate_certificate_exceptions',
        kwargs={
            'course_id': str(course_key),
            'generate_for': ''
        })
    generate_bulk_certificate_exceptions_url = reverse(
        'generate_bulk_certificate_exceptions',
        kwargs={'course_id': str(course_key)})
    certificate_exception_view_url = reverse(
        'certificate_exception_view', kwargs={'course_id': str(course_key)})

    certificate_invalidation_view_url = reverse(
        'certificate_invalidation_view', kwargs={'course_id': str(course_key)})

    certificate_invalidations = CertificateInvalidation.get_certificate_invalidations(
        course_key)

    context = {
        'course':
        course,
        'studio_url':
        get_studio_url(course, 'course'),
        'sections':
        sections,
        'disable_buttons':
        disable_buttons,
        'analytics_dashboard_message':
        analytics_dashboard_message,
        'certificate_white_list':
        certificate_white_list,
        'certificate_invalidations':
        certificate_invalidations,
        'generate_certificate_exceptions_url':
        generate_certificate_exceptions_url,
        'generate_bulk_certificate_exceptions_url':
        generate_bulk_certificate_exceptions_url,
        'certificate_exception_view_url':
        certificate_exception_view_url,
        'certificate_invalidation_view_url':
        certificate_invalidation_view_url,
        'xqa_server':
        settings.FEATURES.get('XQA_SERVER', "http://your_xqa_server.com"),
    }

    return render_to_response(
        'instructor/instructor_dashboard_2/instructor_dashboard_2.html',
        context)
Example #4
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>'),
            )
Example #5
0
class CourseHomeFragmentViewTests(ModuleStoreTestCase):
    """
    Test Messages Displayed on the Course Home
    """
    CREATE_USER = False

    def setUp(self):
        super().setUp()
        CommerceConfiguration.objects.create(
            checkout_on_ecommerce_service=True)

        end = now() + timedelta(days=30)
        self.course = CourseFactory(
            start=now() - timedelta(days=30),
            end=end,
            self_paced=True,
        )
        self.url = course_home_url(self.course)

        CourseMode.objects.create(course_id=self.course.id,
                                  mode_slug=CourseMode.AUDIT)  # lint-amnesty, pylint: disable=no-member
        self.verified_mode = CourseMode.objects.create(
            course_id=self.course.id,  # lint-amnesty, pylint: disable=no-member
            mode_slug=CourseMode.VERIFIED,
            min_price=100,
            expiration_datetime=end,
            sku='test')

        self.user = UserFactory()
        self.client.login(username=self.user.username, password=TEST_PASSWORD)

        self.flag, __ = Flag.objects.update_or_create(
            name=SHOW_UPGRADE_MSG_ON_COURSE_HOME.name,
            defaults={'everyone': True})

    def assert_upgrade_message_not_displayed(self):
        response = self.client.get(self.url)
        self.assertNotContains(response, 'section-upgrade')

    def assert_upgrade_message_displayed(self):  # lint-amnesty, pylint: disable=missing-function-docstring
        response = self.client.get(self.url)
        self.assertContains(response, 'section-upgrade')
        url = EcommerceService().get_checkout_page_url(self.verified_mode.sku)
        self.assertContains(
            response, '<a id="green_upgrade" class="btn-brand btn-upgrade"')
        self.assertContains(response, url)
        self.assertContains(
            response,
            f"Upgrade (<span class='price'>${self.verified_mode.min_price}</span>)",
        )

    def test_no_upgrade_message_if_logged_out(self):
        self.client.logout()
        self.assert_upgrade_message_not_displayed()

    def test_no_upgrade_message_if_not_enrolled(self):
        assert len(CourseEnrollment.enrollments_for_user(self.user)) == 0
        self.assert_upgrade_message_not_displayed()

    def test_no_upgrade_message_if_verified_track(self):
        CourseEnrollment.enroll(self.user, self.course.id, CourseMode.VERIFIED)  # lint-amnesty, pylint: disable=no-member
        self.assert_upgrade_message_not_displayed()

    def test_no_upgrade_message_if_upgrade_deadline_passed(self):
        self.verified_mode.expiration_datetime = now() - timedelta(days=20)
        self.verified_mode.save()
        self.assert_upgrade_message_not_displayed()

    def test_no_upgrade_message_if_flag_disabled(self):
        self.flag.everyone = False
        self.flag.save()
        CourseEnrollment.enroll(self.user, self.course.id, CourseMode.AUDIT)  # lint-amnesty, pylint: disable=no-member
        self.assert_upgrade_message_not_displayed()

    def test_display_upgrade_message_if_audit_and_deadline_not_passed(self):
        CourseEnrollment.enroll(self.user, self.course.id, CourseMode.AUDIT)  # lint-amnesty, pylint: disable=no-member
        self.assert_upgrade_message_displayed()

    @mock.patch(
        'openedx.features.course_experience.views.course_home.format_strikeout_price',
        mock.Mock(return_value=(HTML("<span>DISCOUNT_PRICE</span>"), True)))
    def test_upgrade_message_discount(self):
        # pylint: disable=no-member
        CourseEnrollment.enroll(self.user, self.course.id, CourseMode.AUDIT)

        with override_waffle_flag(SHOW_UPGRADE_MSG_ON_COURSE_HOME, True):
            response = self.client.get(self.url)

        self.assertContains(response, "<span>DISCOUNT_PRICE</span>")
Example #6
0
def password_reset_confirm_wrapper(request, uidb36=None, token=None):
    """
    A wrapper around django.contrib.auth.views.password_reset_confirm.
    Needed because we want to set the user as active at this step.
    We also optionally do some additional password policy checks.
    """
    # convert old-style base36-encoded user id to base64
    uidb64 = uidb36_to_uidb64(uidb36)
    platform_name = {
        "platform_name": configuration_helpers.get_value('platform_name', settings.PLATFORM_NAME)
    }

    # User can not get this link unless account recovery feature is enabled.
    if 'is_account_recovery' in request.GET and not is_secondary_email_feature_enabled():
        raise Http404

    try:
        uid_int = base36_to_int(uidb36)
        user = User.objects.get(id=uid_int)
    except (ValueError, User.DoesNotExist):
        # if there's any error getting a user, just let django's
        # password_reset_confirm function handle it.
        return password_reset_confirm(
            request, uidb64=uidb64, token=token, extra_context=platform_name
        )

    if UserRetirementRequest.has_user_requested_retirement(user):
        # Refuse to reset the password of any user that has requested retirement.
        context = {
            'validlink': True,
            'form': None,
            'title': _('Password reset unsuccessful'),
            'err_msg': _('Error in resetting your password.'),
        }
        context.update(platform_name)
        return TemplateResponse(
            request, 'registration/password_reset_confirm.html', context
        )

    if waffle().is_enabled(PREVENT_AUTH_USER_WRITES):
        context = {
            'validlink': False,
            'form': None,
            'title': _('Password reset unsuccessful'),
            'err_msg': SYSTEM_MAINTENANCE_MSG,
        }
        context.update(platform_name)
        return TemplateResponse(
            request, 'registration/password_reset_confirm.html', context
        )

    if request.method == 'POST':
        # We have to make a copy of request.POST because it is a QueryDict object which is immutable until copied.
        # We have to use request.POST because the password_reset_confirm method takes in the request and a user's
        # password is set to the request.POST['new_password1'] field. We have to also normalize the new_password2
        # field so it passes the equivalence check that new_password1 == new_password2
        # In order to switch out of having to do this copy, we would want to move the normalize_password code into
        # a custom User model's set_password method to ensure it is always happening upon calling set_password.
        request.POST = request.POST.copy()
        request.POST['new_password1'] = normalize_password(request.POST['new_password1'])
        request.POST['new_password2'] = normalize_password(request.POST['new_password2'])

        password = request.POST['new_password1']

        try:
            validate_password(password, user=user)
        except ValidationError as err:
            # We have a password reset attempt which violates some security
            # policy, or any other validation. Use the existing Django template to communicate that
            # back to the user.
            context = {
                'validlink': True,
                'form': None,
                'title': _('Password reset unsuccessful'),
                'err_msg': ' '.join(err.messages),
            }
            context.update(platform_name)
            return TemplateResponse(
                request, 'registration/password_reset_confirm.html', context
            )

        # remember what the old password hash is before we call down
        old_password_hash = user.password

        if 'is_account_recovery' in request.GET:
            response = password_reset_confirm(
                request,
                uidb64=uidb64,
                token=token,
                extra_context=platform_name,
                template_name='registration/password_reset_confirm.html',
                post_reset_redirect='signin_user',
            )
        else:
            response = password_reset_confirm(
                request, uidb64=uidb64, token=token, extra_context=platform_name
            )

        # If password reset was unsuccessful a template response is returned (status_code 200).
        # Check if form is invalid then show an error to the user.
        # Note if password reset was successful we get response redirect (status_code 302).
        if response.status_code == 200:
            form_valid = response.context_data['form'].is_valid() if response.context_data['form'] else False
            if not form_valid:
                log.warning(
                    u'Unable to reset password for user [%s] because form is not valid. '
                    u'A possible cause is that the user had an invalid reset token',
                    user.username,
                )
                response.context_data['err_msg'] = _('Error in resetting your password. Please try again.')
                return response

        # get the updated user
        updated_user = User.objects.get(id=uid_int)
        if 'is_account_recovery' in request.GET:
            try:
                updated_user.email = updated_user.account_recovery.secondary_email
                updated_user.account_recovery.delete()
                # emit an event that the user changed their secondary email to the primary email
                tracker.emit(
                    SETTING_CHANGE_INITIATED,
                    {
                        "setting": "email",
                        "old": user.email,
                        "new": updated_user.email,
                        "user_id": updated_user.id,
                    }
                )
            except ObjectDoesNotExist:
                log.error(
                    'Account recovery process initiated without AccountRecovery instance for user {username}'.format(
                        username=updated_user.username
                    )
                )

        updated_user.save()

        if response.status_code == 302 and 'is_account_recovery' in request.GET:
            messages.success(
                request,
                HTML(_(
                    '{html_start}Password Creation Complete{html_end}'
                    'Your password has been created. {bold_start}{email}{bold_end} is now your primary login email.'
                )).format(
                    support_url=configuration_helpers.get_value('SUPPORT_SITE_LINK', settings.SUPPORT_SITE_LINK),
                    html_start=HTML('<p class="message-title">'),
                    html_end=HTML('</p>'),
                    bold_start=HTML('<b>'),
                    bold_end=HTML('</b>'),
                    email=updated_user.email,
                ),
                extra_tags='account-recovery aa-icon submission-success'
            )
    else:
        response = password_reset_confirm(
            request, uidb64=uidb64, token=token, extra_context=platform_name
        )

        response_was_successful = response.context_data.get('validlink')
        if response_was_successful and not user.is_active:
            user.is_active = True
            user.save()

    return response
class TestCourseSockView(SharedModuleStoreTestCase):
    """
    Tests for the course verification sock fragment view.
    """
    @classmethod
    def setUpClass(cls):
        super().setUpClass()

        # Create four courses
        cls.standard_course = CourseFactory.create()
        cls.verified_course = CourseFactory.create()
        cls.verified_course_update_expired = CourseFactory.create()
        cls.verified_course_already_enrolled = CourseFactory.create()

        # Assign each verifiable course an upgrade deadline
        add_course_mode(cls.verified_course, upgrade_deadline_expired=False)
        add_course_mode(cls.verified_course_update_expired, upgrade_deadline_expired=True)
        add_course_mode(cls.verified_course_already_enrolled, upgrade_deadline_expired=False)

    def setUp(self):
        super().setUp()
        self.user = UserFactory.create()

        # Enroll the user in the four courses
        CourseEnrollmentFactory.create(user=self.user, course_id=self.standard_course.id)
        CourseEnrollmentFactory.create(user=self.user, course_id=self.verified_course.id)
        CourseEnrollmentFactory.create(user=self.user, course_id=self.verified_course_update_expired.id)
        CourseEnrollmentFactory.create(
            user=self.user, course_id=self.verified_course_already_enrolled.id, mode=CourseMode.VERIFIED
        )

        CommerceConfiguration.objects.create(enabled=True, checkout_on_ecommerce_service=True)

        # Log the user in
        self.client.login(username=self.user.username, password=TEST_PASSWORD)

    def get_courseware(self, course):
        return self.client.get(reverse('courseware', kwargs={'course_id': str(course.id)}))

    @override_waffle_flag(DISPLAY_COURSE_SOCK_FLAG, active=True)
    def test_standard_course(self):
        """
        Ensure that a course that cannot be verified does
        not have a visible verification sock.
        """
        response = self.get_courseware(self.standard_course)
        self.assert_verified_sock_is_not_visible(self.standard_course, response)

    @override_waffle_flag(DISPLAY_COURSE_SOCK_FLAG, active=True)
    def test_verified_course(self):
        """
        Ensure that a course that can be verified has a
        visible verification sock.
        """
        response = self.get_courseware(self.verified_course)
        self.assert_verified_sock_is_visible(self.verified_course, response)

    @override_waffle_flag(DISPLAY_COURSE_SOCK_FLAG, active=True)
    def test_verified_course_updated_expired(self):
        """
        Ensure that a course that has an expired upgrade
        date does not display the verification sock.
        """
        response = self.get_courseware(self.verified_course_update_expired)
        self.assert_verified_sock_is_not_visible(self.verified_course_update_expired, response)

    @override_waffle_flag(DISPLAY_COURSE_SOCK_FLAG, active=True)
    def test_verified_course_user_already_upgraded(self):
        """
        Ensure that a user that has already upgraded to a
        verified status cannot see the verification sock.
        """
        response = self.get_courseware(self.verified_course_already_enrolled)
        self.assert_verified_sock_is_not_visible(self.verified_course_already_enrolled, response)

    @override_waffle_flag(DISPLAY_COURSE_SOCK_FLAG, active=True)
    @mock.patch(
        'openedx.features.course_experience.views.course_sock.format_strikeout_price',
        mock.Mock(return_value=(HTML("<span>DISCOUNT_PRICE</span>"), True))
    )
    def test_upgrade_message_discount(self):
        response = self.get_courseware(self.verified_course)
        self.assertContains(response, "<span>DISCOUNT_PRICE</span>")

    def assert_verified_sock_is_visible(self, course, response):  # lint-amnesty, pylint: disable=unused-argument
        return self.assertContains(response, TEST_VERIFICATION_SOCK_LOCATOR, html=False)

    def assert_verified_sock_is_not_visible(self, course, response):  # lint-amnesty, pylint: disable=unused-argument
        return self.assertNotContains(response, TEST_VERIFICATION_SOCK_LOCATOR, html=False)
Example #8
0
def render_body(context, **pageargs):
    __M_caller = context.caller_stack._push_frame()
    try:
        __M_locals = __M_dict_builtin(pageargs=pageargs)
        submit_button_submitting = context.get('submit_button_submitting',
                                               UNDEFINED)
        save_message = context.get('save_message', UNDEFINED)
        short_id = context.get('short_id', UNDEFINED)
        answer_notification_message = context.get(
            'answer_notification_message', UNDEFINED)
        should_enable_next_hint = context.get('should_enable_next_hint',
                                              UNDEFINED)
        id = context.get('id', UNDEFINED)
        reset_button = context.get('reset_button', UNDEFINED)
        has_saved_answers = context.get('has_saved_answers', UNDEFINED)
        demand_hint_possible = context.get('demand_hint_possible', UNDEFINED)
        should_enable_submit_button = context.get(
            'should_enable_submit_button', UNDEFINED)
        submit_button = context.get('submit_button', UNDEFINED)
        attempts_allowed = context.get('attempts_allowed', UNDEFINED)
        save_button = context.get('save_button', UNDEFINED)
        problem = context.get('problem', UNDEFINED)
        attempts_used = context.get('attempts_used', UNDEFINED)
        answer_available = context.get('answer_available', UNDEFINED)
        answer_notification_type = context.get('answer_notification_type',
                                               UNDEFINED)
        __M_writer = context.writer()
        __M_writer(u'\n')
        __M_writer(u'\n\n')
        __M_writer(u'\n<h3 class="hd hd-3 problem-header" id="')
        __M_writer(filters.html_escape(filters.decode.utf8(short_id)))
        __M_writer(u'-problem-title" aria-describedby="')
        __M_writer(filters.html_escape(filters.decode.utf8(id)))
        __M_writer(u'-problem-progress" tabindex="-1">\n  ')
        __M_writer(filters.html_escape(filters.decode.utf8(problem['name'])))
        __M_writer(u'\n</h3>\n\n<div class="problem-progress" id="')
        __M_writer(filters.html_escape(filters.decode.utf8(id)))
        __M_writer(u'-problem-progress"></div>\n\n<div class="problem">\n  ')
        __M_writer(
            filters.html_escape(filters.decode.utf8(HTML(problem['html']))))
        __M_writer(
            u'\n  <div class="action">\n    <input type="hidden" name="problem_id" value="'
        )
        __M_writer(filters.html_escape(filters.decode.utf8(problem['name'])))
        __M_writer(u'" />\n')
        if demand_hint_possible:
            __M_writer(u'      <div class="problem-hint">\n        ')
            runtime._include_file(context,
                                  u'problem_notifications.html',
                                  _template_uri,
                                  notification_name='hint',
                                  notification_type='problem-hint',
                                  notification_icon='fa-question',
                                  notification_message='')
            __M_writer(u'\n      </div>\n')
        __M_writer(
            u'\n    <div class="submit-attempt-container">\n      <button type="button" class="submit btn-brand" data-submitting="'
        )
        __M_writer(
            filters.html_escape(filters.decode.utf8(submit_button_submitting)))
        __M_writer(u'" data-value="')
        __M_writer(filters.html_escape(filters.decode.utf8(submit_button)))
        __M_writer(u'" data-should-enable-submit-button="')
        __M_writer(
            filters.html_escape(
                filters.decode.utf8(should_enable_submit_button)))
        __M_writer(u'" aria-describedby="submission_feedback_')
        __M_writer(filters.html_escape(filters.decode.utf8(short_id)))
        __M_writer(u'" ')
        __M_writer(
            filters.html_escape(
                filters.decode.utf8(
                    '' if should_enable_submit_button else 'disabled')))
        __M_writer(u'>\n          <span class="submit-label">')
        __M_writer(filters.html_escape(filters.decode.utf8(submit_button)))
        __M_writer(
            u'</span>\n      </button>\n      <div class="submission-feedback" id="submission_feedback_'
        )
        __M_writer(filters.html_escape(filters.decode.utf8(short_id)))
        __M_writer(u'">\n')
        if attempts_allowed:
            __M_writer(u'          ')
            __M_writer(
                filters.html_escape(
                    filters.decode.utf8(
                        ungettext(
                            "You have used {num_used} of {num_total} attempt",
                            "You have used {num_used} of {num_total} attempts",
                            attempts_allowed).format(
                                num_used=attempts_used,
                                num_total=attempts_allowed))))
            __M_writer(u'\n')
        __M_writer(u'        <span class="sr">')
        __M_writer(
            filters.html_escape(
                filters.decode.utf8(
                    _("Some problems have options such as save, reset, hints, or show answer. These options follow the Submit button."
                      ))))
        __M_writer(
            u'</span>\n      </div>\n    </div>\n    <div class="problem-action-buttons-wrapper">\n'
        )
        if demand_hint_possible:
            __M_writer(
                u'      <span class="problem-action-button-wrapper">\n          <button type="button" class="hint-button problem-action-btn btn-default btn-small" data-value="'
            )
            __M_writer(filters.html_escape(filters.decode.utf8(_('Hint'))))
            __M_writer(u'" ')
            __M_writer(
                filters.html_escape(
                    filters.decode.utf8(
                        '' if should_enable_next_hint else 'disabled')))
            __M_writer(
                u'><span class="icon fa fa-question" aria-hidden="true"></span>'
            )
            __M_writer(filters.html_escape(filters.decode.utf8(_('Hint'))))
            __M_writer(u'</button>\n      </span>\n')
        if save_button:
            __M_writer(
                u'      <span class="problem-action-button-wrapper">\n          <button type="button" class="save problem-action-btn btn-default btn-small" data-value="'
            )
            __M_writer(filters.html_escape(filters.decode.utf8(_('Save'))))
            __M_writer(
                u'">\n              <span class="icon fa fa-floppy-o" aria-hidden="true"></span>\n              <span aria-hidden="true">'
            )
            __M_writer(filters.html_escape(filters.decode.utf8(_('Save'))))
            __M_writer(u'</span>\n              <span class="sr">')
            __M_writer(
                filters.html_escape(filters.decode.utf8(
                    _("Save your answer"))))
            __M_writer(u'</span>\n          </button>\n      </span>\n')
        if reset_button:
            __M_writer(
                u'      <span class="problem-action-button-wrapper">\n          <button type="button" class="reset problem-action-btn btn-default btn-small" data-value="'
            )
            __M_writer(filters.html_escape(filters.decode.utf8(_('Reset'))))
            __M_writer(
                u'"><span class="icon fa fa-refresh" aria-hidden="true"></span><span aria-hidden="true">'
            )
            __M_writer(filters.html_escape(filters.decode.utf8(_('Reset'))))
            __M_writer(u'</span><span class="sr">')
            __M_writer(
                filters.html_escape(filters.decode.utf8(
                    _("Reset your answer"))))
            __M_writer(u'</span></button>\n      </span>\n')
        if answer_available:
            __M_writer(
                u'      <span class="problem-action-button-wrapper">\n          <button type="button" class="show problem-action-btn btn-default btn-small" aria-describedby="'
            )
            __M_writer(filters.html_escape(filters.decode.utf8(short_id)))
            __M_writer(
                u'-problem-title"><span class="icon fa fa-info-circle" aria-hidden="true"></span><span class="show-label">'
            )
            __M_writer(
                filters.html_escape(filters.decode.utf8(_('Show Answer'))))
            __M_writer(u'</span></button>\n      </span>\n')
        __M_writer(u'    </div>\n  </div>\n    ')
        runtime._include_file(context,
                              u'problem_notifications.html',
                              _template_uri,
                              notification_type='warning',
                              notification_icon='fa-exclamation-circle',
                              notification_name='gentle-alert',
                              notification_message='')
        __M_writer(u'\n')
        if answer_notification_type:
            if 'correct' == answer_notification_type:
                __M_writer(u'            ')
                runtime._include_file(
                    context,
                    u'problem_notifications.html',
                    _template_uri,
                    notification_type='success',
                    notification_icon='fa-check',
                    notification_name='submit',
                    is_hidden=False,
                    notification_message=answer_notification_message)
                __M_writer(u'\n')
            if 'incorrect' == answer_notification_type:
                __M_writer(u'            ')
                runtime._include_file(
                    context,
                    u'problem_notifications.html',
                    _template_uri,
                    notification_type='error',
                    notification_icon='fa-close',
                    notification_name='submit',
                    is_hidden=False,
                    notification_message=answer_notification_message)
                __M_writer(u'\n')
            if 'partially-correct' == answer_notification_type:
                __M_writer(u'            ')
                runtime._include_file(
                    context,
                    u'problem_notifications.html',
                    _template_uri,
                    notification_type='success',
                    notification_icon='fa-asterisk',
                    notification_name='submit',
                    is_hidden=False,
                    notification_message=answer_notification_message)
                __M_writer(u'\n')
            if 'submitted' == answer_notification_type:
                __M_writer(u'            ')
                runtime._include_file(
                    context,
                    u'problem_notifications.html',
                    _template_uri,
                    notification_type='general',
                    notification_icon='fa-info-circle',
                    notification_name='submit',
                    is_hidden=False,
                    notification_message=answer_notification_message)
                __M_writer(u'\n')
        __M_writer(u'    ')
        runtime._include_file(context,
                              u'problem_notifications.html',
                              _template_uri,
                              notification_type='warning',
                              notification_icon='fa-save',
                              notification_name='save',
                              notification_message=save_message,
                              is_hidden=not has_saved_answers)
        __M_writer(u'\n    ')

        notification_message = _('Answers are displayed within the problem')

        __M_locals_builtin_stored = __M_locals_builtin()
        __M_locals.update(
            __M_dict_builtin([(__M_key, __M_locals_builtin_stored[__M_key])
                              for __M_key in ['notification_message']
                              if __M_key in __M_locals_builtin_stored]))
        __M_writer(u'\n    ')
        runtime._include_file(context,
                              u'problem_notifications.html',
                              _template_uri,
                              notification_type='general',
                              notification_icon='fa-info-circle',
                              notification_name='show-answer',
                              notification_message=notification_message,
                              is_hidden=True)
        __M_writer(u'\n</div>\n')
        return ''
    finally:
        context.caller_stack._pop_frame()
Example #9
0
 def username(self, obj):
     return HTML('<a href="{}">{}</a>').format(
         reverse("admin:auth_user_change", args=(obj.enrollment.user.id, )),
         obj.enrollment.user.username)
Example #10
0
 def test_ungettext(self):
     for i in [1, 2]:
         out = Text(ungettext(u"1 & {}", u"2 & {}", i)).format(HTML(u"<>"))
         self.assertEqual(out, u"{} &amp; <>".format(i))
Example #11
0
def _format_error_html(msg):
    """ Format an HTML error message """
    return HTML(u'<p class="error_msg">{msg}</p>').format(msg=msg)
Example #12
0
def _get_processor_exception_html(exception):
    """
    Return HTML indicating that an error occurred.

    Args:
        exception (CCProcessorException): The exception that occurred.

    Returns:
        unicode: The rendered HTML.

    """
    payment_support_email = configuration_helpers.get_value(
        'payment_support_email', settings.PAYMENT_SUPPORT_EMAIL)
    if isinstance(exception, CCProcessorDataException):
        return _format_error_html(
            Text(
                _(u"Sorry! Our payment processor sent us back a payment confirmation that had inconsistent data! "
                  u"We apologize that we cannot verify whether the charge went through and take further action on your order. "
                  u"The specific error message is: {msg} "
                  u"Your credit card may possibly have been charged.  Contact us with payment-specific questions at {email}."
                  )).format(msg=HTML(
                      u'<span class="exception_msg">{msg}</span>').format(
                          msg=text_type(exception)),
                            email=payment_support_email))
    elif isinstance(exception, CCProcessorWrongAmountException):
        return _format_error_html(
            Text(
                _(u"Sorry! Due to an error your purchase was charged for a different amount than the order total! "
                  u"The specific error message is: {msg}. "
                  u"Your credit card has probably been charged. Contact us with payment-specific questions at {email}."
                  )).format(msg=HTML(
                      u'<span class="exception_msg">{msg}</span>').format(
                          msg=text_type(exception)),
                            email=payment_support_email))
    elif isinstance(exception, CCProcessorSignatureException):
        return _format_error_html(
            Text(
                _(u"Sorry! Our payment processor sent us back a corrupted message regarding your charge, so we are "
                  u"unable to validate that the message actually came from the payment processor. "
                  u"The specific error message is: {msg}. "
                  u"We apologize that we cannot verify whether the charge went through and take further action on your order. "
                  u"Your credit card may possibly have been charged. Contact us with payment-specific questions at {email}."
                  )).format(msg=HTML(
                      u'<span class="exception_msg">{msg}</span>').format(
                          msg=text_type(exception)),
                            email=payment_support_email))
    elif isinstance(exception, CCProcessorUserCancelled):
        return _format_error_html(
            _(u"Sorry! Our payment processor sent us back a message saying that you have cancelled this transaction. "
              u"The items in your shopping cart will exist for future purchase. "
              u"If you feel that this is in error, please contact us with payment-specific questions at {email}."
              ).format(email=payment_support_email))
    elif isinstance(exception, CCProcessorUserDeclined):
        return _format_error_html(
            _(u"We're sorry, but this payment was declined. The items in your shopping cart have been saved. "
              u"If you have any questions about this transaction, please contact us at {email}."
              ).format(email=payment_support_email))
    else:
        return _format_error_html(
            _(u"Sorry! Your payment could not be processed because an unexpected exception occurred. "
              u"Please contact us at {email} for assistance.").format(
                  email=payment_support_email))
Example #13
0
 def test_ungettext(self):
     for i in [1, 2]:
         out = ungettext("1 & {}", "2 & {}", i).format(HTML("<>"))
         self.assertEqual(out, "{} &amp; <>".format(i))
Example #14
0
 def __str__(self):
     return HTML(u"<Badge '{slug}' for '{issuing_component}'>").format(
         slug=HTML(self.slug),
         issuing_component=HTML(self.issuing_component))
Example #15
0
 def __str__(self):
     return HTML(u"<CourseEventBadgesConfiguration ({})>").format(
         Text(u"Enabled") if self.enabled else Text(u"Disabled"))
Example #16
0
def render_body(context,
                online_help_token,
                use_cookie_banner=False,
                **pageargs):
    __M_caller = context.caller_stack._push_frame()
    try:
        __M_locals = __M_dict_builtin(use_cookie_banner=use_cookie_banner,
                                      pageargs=pageargs,
                                      online_help_token=online_help_token)
        _import_ns = {}
        _mako_get_namespace(context, '__anon_0x7f4938752a50')._populate(
            _import_ns, [u'login_query'])
        csrf_token = _import_ns.get('csrf_token',
                                    context.get('csrf_token', UNDEFINED))
        settings = _import_ns.get('settings',
                                  context.get('settings', UNDEFINED))

        def navigation_top():
            return render_navigation_top(context._locals(__M_locals))

        len = _import_ns.get('len', context.get('len', UNDEFINED))
        course = _import_ns.get('course', context.get('course', UNDEFINED))

        def js_extra():
            return render_js_extra(context._locals(__M_locals))

        static = _mako_get_namespace(context, 'static')
        user = _import_ns.get('user', context.get('user', UNDEFINED))
        LANGUAGE_CODE = _import_ns.get('LANGUAGE_CODE',
                                       context.get('LANGUAGE_CODE', UNDEFINED))
        uses_pattern_library = _import_ns.get(
            'uses_pattern_library',
            context.get('uses_pattern_library', UNDEFINED))
        __M_writer = context.writer()
        __M_writer(u'\n')
        __M_writer(u'\n\n')
        __M_writer(u'\n')
        __M_writer(u'\n')
        __M_writer(u'\n\n')
        if 'parent' not in context._data or not hasattr(
                context._data['parent'], 'navigation_top'):
            context['self'].navigation_top(**pageargs)

        __M_writer(u'\n\n')
        if uses_pattern_library:
            __M_writer(u'    ')
            if 'parent' not in context._data or not hasattr(
                    context._data['parent'], 'js_extra'):
                context['self'].js_extra(**pageargs)

            __M_writer(u'\n')
        __M_writer(u'\n')

        unsupported_browser_alert_versions = configuration_helpers.get_value(
            'UNSUPPORTED_BROWSER_ALERT_VERSIONS',
            settings.FEATURES.get('UNSUPPORTED_BROWSER_ALERT_VERSIONS'))

        __M_locals_builtin_stored = __M_locals_builtin()
        __M_locals.update(
            __M_dict_builtin([
                (__M_key, __M_locals_builtin_stored[__M_key])
                for __M_key in ['unsupported_browser_alert_versions']
                if __M_key in __M_locals_builtin_stored
            ]))
        __M_writer(u'\n')
        if waffle.switch_is_active('enable_unsupported_browser_alert'):
            __M_writer(u'  <script>\n    var $buoop = {\n        notify:')
            __M_writer(filters.decode.utf8(unsupported_browser_alert_versions))
            __M_writer(
                u',\n        api:5,\n        reminder:0\n    };\n    function $buo_f() {\n        var e = document.createElement("script");\n        e.src = "//browser-update.org/update.min.js";\n        document.body.appendChild(e);\n    };\n    try {document.addEventListener("DOMContentLoaded", $buo_f,false)}\n    catch(e){window.attachEvent("onload", $buo_f)}\n  </script>\n'
            )
        __M_writer(u'\n<header class="global-header ')
        __M_writer(
            filters.html_escape(filters.decode.utf8('slim' if course else '')))
        __M_writer(u'">\n')
        if use_cookie_banner:
            __M_writer(u'        ')
            __M_writer(
                filters.html_escape(
                    filters.decode.utf8(
                        static.renderReact(component="CookiePolicyBanner",
                                           id="cookie-policy-banner",
                                           props={}))))
            __M_writer(u'\n')
        __M_writer(u'    <div class="main-header">\n        ')
        runtime._include_file(context,
                              u'navbar-logo-header.html',
                              _template_uri,
                              online_help_token=online_help_token)
        __M_writer(
            u'\n        <div class="hamburger-menu" role="button" aria-label=')
        __M_writer(filters.html_escape(filters.decode.utf8(_("Options Menu"))))
        __M_writer(
            u' aria-expanded="false" aria-controls="mobile-menu" tabindex="0">\n            <span class="line"></span>\n            <span class="line"></span>\n            <span class="line"></span>\n            <span class="line"></span>\n        </div>\n'
        )
        if user.is_authenticated:
            __M_writer(u'            ')
            runtime._include_file(context,
                                  u'navbar-authenticated.html',
                                  _template_uri,
                                  online_help_token=online_help_token)
            __M_writer(u'\n')
        else:
            __M_writer(u'            ')
            runtime._include_file(context,
                                  u'navbar-not-authenticated.html',
                                  _template_uri,
                                  online_help_token=online_help_token)
            __M_writer(u'\n')
        __M_writer(
            u'    </div>\n    <div class="mobile-menu hidden" aria-label=')
        __M_writer(filters.html_escape(filters.decode.utf8(_("More Options"))))
        __M_writer(u' role="menu" id="mobile-menu"></div>\n</header>\n\n')
        if course:
            __M_writer(
                u'<!--[if lte IE 9]>\n<div class="ie-banner" aria-hidden="true">'
            )
            __M_writer(
                filters.html_escape(
                    filters.decode.utf8(
                        Text(
                            _('{begin_strong}Warning:{end_strong} Your browser is not fully supported. We strongly recommend using {chrome_link} or {ff_link}.'
                              )).
                        format(
                            begin_strong=HTML('<strong>'),
                            end_strong=HTML('</strong>'),
                            chrome_link=HTML(
                                '<a href="https://www.google.com/chrome" target="_blank">Chrome</a>'
                            ),
                            ff_link=HTML(
                                '<a href="http://www.mozilla.org/firefox" target="_blank">Firefox</a>'
                            ),
                        ))))
            __M_writer(u'</div>\n<![endif]-->\n')
        __M_writer(u'\n')
        if settings.FEATURES.get('ENABLE_COOKIE_CONSENT', False):
            __M_writer(u'  ')
            runtime._include_file(context, u'../widgets/cookie-consent.html',
                                  _template_uri)
            __M_writer(u'\n')
        __M_writer(u'\n')
        if header_language_selector_is_enabled():
            __M_writer(u'    ')
            languages = released_languages()

            __M_locals_builtin_stored = __M_locals_builtin()
            __M_locals.update(
                __M_dict_builtin([(__M_key, __M_locals_builtin_stored[__M_key])
                                  for __M_key in ['languages']
                                  if __M_key in __M_locals_builtin_stored]))
            __M_writer(u'\n')
            if len(languages) > 1:
                __M_writer(
                    u'        <form action="/i18n/setlang/" method="post" class="settings-language-form" id="language-settings-form">\n            <input type="hidden" id="csrf_token" name="csrfmiddlewaretoken" value="'
                )
                __M_writer(filters.html_escape(
                    filters.decode.utf8(csrf_token)))
                __M_writer(u'">\n')
                if user.is_authenticated:
                    __M_writer(
                        u'                <input title="preference api" type="hidden" class="url-endpoint" value="'
                    )
                    __M_writer(
                        filters.html_escape(
                            filters.decode.utf8(
                                reverse('preferences_api',
                                        kwargs={'username': user.username}))))
                    __M_writer(u'" data-user-is-authenticated="true">\n')
                else:
                    __M_writer(
                        u'                <input title="session update url" type="hidden" class="url-endpoint" value="'
                    )
                    __M_writer(
                        filters.html_escape(
                            filters.decode.utf8(reverse('session_language'))))
                    __M_writer(u'" data-user-is-authenticated="false">\n')
                __M_writer(u'            <label><span class="sr">')
                __M_writer(
                    filters.html_escape(
                        filters.decode.utf8(_("Choose Language"))))
                __M_writer(
                    u'</span>\n            <select class="input select language-selector" id="settings-language-value" name="language">\n'
                )
                for language in languages:
                    if language[0] == LANGUAGE_CODE:
                        __M_writer(u'                        <option value="')
                        __M_writer(
                            filters.html_escape(
                                filters.decode.utf8(language[0])))
                        __M_writer(u'" selected="selected">')
                        __M_writer(
                            filters.html_escape(
                                filters.decode.utf8(language[1])))
                        __M_writer(u'</option>\n')
                    else:
                        __M_writer(u'                        <option value="')
                        __M_writer(
                            filters.html_escape(
                                filters.decode.utf8(language[0])))
                        __M_writer(u'" >')
                        __M_writer(
                            filters.html_escape(
                                filters.decode.utf8(language[1])))
                        __M_writer(u'</option>\n')
                __M_writer(
                    u'            </select>\n            </label>\n        </form>\n'
                )
        return ''
    finally:
        context.caller_stack._pop_frame()
Example #17
0
 def course_id(self, obj):
     return HTML('<a href="{}">{}</a>').format(
         reverse("admin:course_overviews_courseoverview_change",
                 args=(obj.enrollment.course_id, )),
         obj.enrollment.course_id)
Example #18
0
def _register_course_home_messages(request, course, user_access,
                                   course_start_data):
    """
    Register messages to be shown in the course home content page.
    """
    allow_anonymous = check_public_access(course, [COURSE_VISIBILITY_PUBLIC])

    if user_access['is_anonymous'] and not allow_anonymous:
        sign_in_or_register_text = (_(
            u'{sign_in_link} or {register_link} and then enroll in this course.'
        ) if not CourseMode.is_masters_only(course.id) else
                                    _(u'{sign_in_link} or {register_link}.'))
        CourseHomeMessages.register_info_message(
            request,
            Text(sign_in_or_register_text).format(
                sign_in_link=HTML(
                    u'<a href="/login?next={current_url}">{sign_in_label}</a>'
                ).format(
                    sign_in_label=_('Sign in'),
                    current_url=urlquote_plus(request.path),
                ),
                register_link=HTML(
                    u'<a href="/register?next={current_url}">{register_label}</a>'
                ).format(
                    register_label=_('register'),
                    current_url=urlquote_plus(request.path),
                )),
            title=Text(
                _('You must be enrolled in the course to see course content.')
            ))
    if not user_access['is_anonymous'] and not user_access['is_staff'] and \
            not user_access['is_enrolled']:

        title = Text(_(u'Welcome to {course_display_name}')).format(
            course_display_name=course.display_name)

        if CourseMode.is_masters_only(course.id):
            # if a course is a Master's only course, we will not offer user ability to self-enroll
            CourseHomeMessages.register_info_message(
                request,
                Text(
                    _('You must be enrolled in the course to see course content. '
                      'Please contact your degree administrator or edX Support if you have questions.'
                      )),
                title=title)
        elif not course.invitation_only:
            CourseHomeMessages.register_info_message(
                request,
                Text(
                    _(u'{open_enroll_link}Enroll now{close_enroll_link} to access the full course.'
                      )).format(open_enroll_link=HTML(
                          '<button class="enroll-btn btn-link">'),
                                close_enroll_link=HTML('</button>')),
                title=title)
        else:
            CourseHomeMessages.register_info_message(
                request,
                Text(
                    _('You must be enrolled in the course to see course content.'
                      )),
            )
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>'),
            )
Example #20
0
def activate_account(request, key):
    """
    When link in activation e-mail is clicked
    """
    # If request is in Studio call the appropriate view
    if theming_helpers.get_project_root_name().lower() == u'cms':
        return activate_account_studio(request, key)

    try:
        registration = Registration.objects.get(activation_key=key)
    except (Registration.DoesNotExist, Registration.MultipleObjectsReturned):
        messages.error(
            request,
            HTML(_(
                '{html_start}Your account could not be activated{html_end}'
                'Something went wrong, please <a href="{support_url}">contact support</a> to resolve this issue.'
            )).format(
                support_url=configuration_helpers.get_value('SUPPORT_SITE_LINK', settings.SUPPORT_SITE_LINK),
                html_start=HTML('<p class="message-title">'),
                html_end=HTML('</p>'),
            ),
            extra_tags='account-activation aa-icon'
        )
    else:
        if registration.user.is_active:
            messages.info(
                request,
                HTML(_('{html_start}This account has already been activated.{html_end}')).format(
                    html_start=HTML('<p class="message-title">'),
                    html_end=HTML('</p>'),
                ),
                extra_tags='account-activation aa-icon',
            )
        elif waffle().is_enabled(PREVENT_AUTH_USER_WRITES):
            messages.error(
                request,
                HTML(u'{html_start}{message}{html_end}').format(
                    message=Text(SYSTEM_MAINTENANCE_MSG),
                    html_start=HTML('<p class="message-title">'),
                    html_end=HTML('</p>'),
                ),
                extra_tags='account-activation aa-icon',
            )
        else:
            registration.activate()
            # Success message for logged in users.
            message = _('{html_start}Success{html_end} You have activated your account.')

            if not request.user.is_authenticated:
                # Success message for logged out users
                message = _(
                    '{html_start}Success! You have activated your account.{html_end}'
                    'You will now receive email updates and alerts from us related to'
                    ' the courses you are enrolled in. Sign In to continue.'
                )

            # Add message for later use.
            messages.success(
                request,
                HTML(message).format(
                    html_start=HTML('<p class="message-title">'),
                    html_end=HTML('</p>'),
                ),
                extra_tags='account-activation aa-icon',
            )

    return redirect('dashboard')
def generate_fragment_from_message(message):
    return Fragment(
        HTML('<div class="course-expiration-message">{}</div>').format(
            message))
Example #22
0
    def response_a11y_data(self, response, inputfields, responsetype_id,
                           problem_data):
        """
        Construct data to be used for a11y.

        Arguments:
            response (object): xml response object
            inputfields (list): list of inputfields in a responsetype
            responsetype_id (str): responsetype id
            problem_data (dict): dict to be filled with response data
        """
        # if there are no inputtypes then don't do anything
        if not inputfields:
            return

        element_to_be_deleted = None
        label = ''

        if len(inputfields) > 1:
            response.set('multiple_inputtypes', 'true')
            group_label_tag = response.find('label')
            group_description_tags = response.findall('description')
            group_label_tag_id = u'multiinput-group-label-{}'.format(
                responsetype_id)
            group_label_tag_text = ''
            if group_label_tag is not None:
                group_label_tag.tag = 'p'
                group_label_tag.set('id', group_label_tag_id)
                group_label_tag.set('class', 'multi-inputs-group-label')
                group_label_tag_text = stringify_children(group_label_tag)
                response.set('multiinput-group-label-id', group_label_tag_id)

            group_description_ids = []
            for index, group_description_tag in enumerate(
                    group_description_tags):
                group_description_tag_id = u'multiinput-group-description-{}-{}'.format(
                    responsetype_id, index)
                group_description_tag.tag = 'p'
                group_description_tag.set('id', group_description_tag_id)
                group_description_tag.set(
                    'class',
                    'multi-inputs-group-description question-description')
                group_description_ids.append(group_description_tag_id)

            if group_description_ids:
                response.set('multiinput-group_description_ids',
                             ' '.join(group_description_ids))

            for inputfield in inputfields:
                problem_data[inputfield.get('id')] = {
                    'group_label': group_label_tag_text,
                    'label': inputfield.attrib.get('label', ''),
                    'descriptions': {}
                }
        else:
            # Extract label value from <label> tag or label attribute from inside the responsetype
            responsetype_label_tag = response.find('label')
            if responsetype_label_tag is not None:
                label = stringify_children(responsetype_label_tag)
                # store <label> tag containing question text to delete
                # it later otherwise question will be rendered twice
                element_to_be_deleted = responsetype_label_tag
            elif 'label' in inputfields[0].attrib:
                # in this case we have old problems with label attribute and p tag having question in it
                # we will pick the first sibling of responsetype if its a p tag and match the text with
                # the label attribute text. if they are equal then we will use this text as question.
                # Get first <p> tag before responsetype, this <p> may contains the question text.
                p_tag = response.xpath('preceding-sibling::*[1][self::p]')

                if p_tag and p_tag[0].text == inputfields[0].attrib['label']:
                    label = stringify_children(p_tag[0])
                    element_to_be_deleted = p_tag[0]
            else:
                # In this case the problems don't have tag or label attribute inside the responsetype
                # so we will get the first preceding label tag w.r.t to this responsetype.
                # This will take care of those multi-question problems that are not using --- in their markdown.
                label_tag = response.xpath(
                    'preceding-sibling::*[1][self::label]')
                if label_tag:
                    label = stringify_children(label_tag[0])
                    element_to_be_deleted = label_tag[0]

            # delete label or p element only if inputtype is fully accessible
            if inputfields[
                    0].tag in ACCESSIBLE_CAPA_INPUT_TYPES and element_to_be_deleted is not None:
                element_to_be_deleted.getparent().remove(element_to_be_deleted)

            # Extract descriptions and set unique id on each description tag
            description_tags = response.findall('description')
            description_id = 1
            descriptions = OrderedDict()
            for description in description_tags:
                descriptions["description_%s_%i" %
                             (responsetype_id, description_id)] = HTML(
                                 stringify_children(description))
                response.remove(description)
                description_id += 1

            problem_data[inputfields[0].get('id')] = {
                'label': HTML(label.strip()) if label else '',
                'descriptions': descriptions
            }
Example #23
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)
def instructor_dashboard_2(request, course_id):
    """ Display the instructor dashboard for a course. """
    try:
        course_key = CourseKey.from_string(course_id)
    except InvalidKeyError:
        log.error(
            u"Unable to find course with course key %s while loading the Instructor Dashboard.",
            course_id)
        return HttpResponseServerError()

    course = get_course_by_id(course_key, depth=0)

    access = {
        'admin':
        request.user.is_staff,
        'instructor':
        bool(has_access(request.user, 'instructor', course)),
        'finance_admin':
        CourseFinanceAdminRole(course_key).has_user(request.user),
        'sales_admin':
        CourseSalesAdminRole(course_key).has_user(request.user),
        'staff':
        bool(has_access(request.user, 'staff', course)),
        'forum_admin':
        has_forum_access(request.user, course_key, FORUM_ROLE_ADMINISTRATOR),
    }

    if not access['staff']:
        raise Http404()

    is_white_label = CourseMode.is_white_label(course_key)

    sections = [
        _section_course_info(course, access),
        _section_membership(course, access, is_white_label),
        _section_cohort_management(course, access),
        _section_student_admin(course, access),
        _section_data_download(course, access),
    ]

    analytics_dashboard_message = None
    if settings.ANALYTICS_DASHBOARD_URL:
        # Construct a URL to the external analytics dashboard
        analytics_dashboard_url = '{0}/courses/{1}'.format(
            settings.ANALYTICS_DASHBOARD_URL, unicode(course_key))
        link_start = HTML("<a href=\"{}\" target=\"_blank\">").format(
            analytics_dashboard_url)
        analytics_dashboard_message = _(
            "To gain insights into student enrollment and participation {link_start}"
            "visit {analytics_dashboard_name}, our new course analytics product{link_end}."
        )
        analytics_dashboard_message = Text(analytics_dashboard_message).format(
            link_start=link_start,
            link_end=HTML("</a>"),
            analytics_dashboard_name=settings.ANALYTICS_DASHBOARD_NAME)

        # Temporarily show the "Analytics" section until we have a better way of linking to Insights
        sections.append(_section_analytics(course, access))

    # Check if there is corresponding entry in the CourseMode Table related to the Instructor Dashboard course
    course_mode_has_price = False
    paid_modes = CourseMode.paid_modes_for_course(course_key)
    if len(paid_modes) == 1:
        course_mode_has_price = True
    elif len(paid_modes) > 1:
        log.error(
            u"Course %s has %s course modes with payment options. Course must only have "
            u"one paid course mode to enable eCommerce options.",
            unicode(course_key), len(paid_modes))

    if settings.FEATURES.get('INDIVIDUAL_DUE_DATES') and access['instructor']:
        sections.insert(3, _section_extensions(course))

    # Gate access to course email by feature flag & by course-specific authorization
    if BulkEmailFlag.feature_enabled(course_key):
        sections.append(_section_send_email(course, access))

    # Gate access to Metrics tab by featue flag and staff authorization
    if settings.FEATURES['CLASS_DASHBOARD'] and access['staff']:
        sections.append(_section_metrics(course, access))

    # Gate access to Ecommerce tab
    if course_mode_has_price and (access['finance_admin']
                                  or access['sales_admin']):
        sections.append(
            _section_e_commerce(course, access, paid_modes[0], is_white_label,
                                is_white_label))

    # Gate access to Special Exam tab depending if either timed exams or proctored exams
    # are enabled in the course

    # NOTE: For now, if we only have procotred exams enabled, then only platform Staff
    # (user.is_staff) will be able to view the special exams tab. This may
    # change in the future
    can_see_special_exams = (
        ((course.enable_proctored_exams and request.user.is_staff)
         or course.enable_timed_exams)
        and settings.FEATURES.get('ENABLE_SPECIAL_EXAMS', False))
    if can_see_special_exams:
        sections.append(_section_special_exams(course, access))

    # Certificates panel
    # This is used to generate example certificates
    # and enable self-generated certificates for a course.
    certs_enabled = CertificateGenerationConfiguration.current().enabled
    if certs_enabled and access['admin']:
        sections.append(_section_certificates(course))

    disable_buttons = not _is_small_course(course_key)

    certificate_white_list = CertificateWhitelist.get_certificate_white_list(
        course_key)
    generate_certificate_exceptions_url = reverse(  # pylint: disable=invalid-name
        'generate_certificate_exceptions',
        kwargs={
            'course_id': unicode(course_key),
            'generate_for': ''
        })
    generate_bulk_certificate_exceptions_url = reverse(  # pylint: disable=invalid-name
        'generate_bulk_certificate_exceptions',
        kwargs={'course_id': unicode(course_key)})
    certificate_exception_view_url = reverse(
        'certificate_exception_view',
        kwargs={'course_id': unicode(course_key)})

    certificate_invalidation_view_url = reverse(  # pylint: disable=invalid-name
        'certificate_invalidation_view',
        kwargs={'course_id': unicode(course_key)})

    certificate_invalidations = CertificateInvalidation.get_certificate_invalidations(
        course_key)

    context = {
        'course': course,
        'studio_url': get_studio_url(course, 'course'),
        'sections': sections,
        'disable_buttons': disable_buttons,
        'analytics_dashboard_message': analytics_dashboard_message,
        'certificate_white_list': certificate_white_list,
        'certificate_invalidations': certificate_invalidations,
        'generate_certificate_exceptions_url':
        generate_certificate_exceptions_url,
        'generate_bulk_certificate_exceptions_url':
        generate_bulk_certificate_exceptions_url,
        'certificate_exception_view_url': certificate_exception_view_url,
        'certificate_invalidation_view_url': certificate_invalidation_view_url,
    }

    return render_to_response(
        'instructor/instructor_dashboard_2/instructor_dashboard_2.html',
        context)
Example #25
0
    def test_saml_auth_with_error(
        self,
        url_name,
        current_backend,
        current_provider,
        expected_enterprise_customer_mock_attrs,
        enterprise_customer_mock,
    ):
        params = []
        request = RequestFactory().get(reverse(url_name),
                                       params,
                                       HTTP_ACCEPT='text/html')
        SessionMiddleware().process_request(request)
        request.user = AnonymousUser()

        self.enable_saml()
        dummy_idp = 'testshib'
        self._configure_testshib_provider(current_provider, dummy_idp)
        expected_ec = mock.MagicMock(branding_configuration=mock.MagicMock(
            logo=mock.MagicMock(
                url=expected_enterprise_customer_mock_attrs['logo']),
            welcome_message=expected_enterprise_customer_mock_attrs[
                'welcome_msg']))
        expected_ec.name = expected_enterprise_customer_mock_attrs['name']
        enterprise_customer_data = {
            'uuid': '72416e52-8c77-4860-9584-15e5b06220fb',
            'name': 'Dummy Enterprise',
            'identity_provider': dummy_idp,
        }
        enterprise_customer_mock.return_value = enterprise_customer_data
        dummy_error_message = 'Authentication failed: SAML login failed ' \
                              '["invalid_response"] [SAML Response must contain 1 assertion]'

        # Add error message for error in auth pipeline
        MessageMiddleware().process_request(request)
        messages.error(request, dummy_error_message, extra_tags='social-auth')

        # Simulate a running pipeline
        pipeline_response = {'response': {'idp_name': dummy_idp}}
        pipeline_target = 'student_account.views.third_party_auth.pipeline'
        with simulate_running_pipeline(pipeline_target, current_backend,
                                       **pipeline_response):
            with mock.patch('edxmako.request_context.get_current_request',
                            return_value=request):
                response = login_and_registration_form(request)

        expected_error_message = Text(
            _(u'We are sorry, you are not authorized to access {platform_name} via this channel. '
              u'Please contact your {enterprise} administrator in order to access {platform_name} '
              u'or contact {edx_support_link}.{line_break}'
              u'{line_break}'
              u'Error Details:{line_break}{error_message}')
        ).format(
            platform_name=settings.PLATFORM_NAME,
            enterprise=enterprise_customer_data['name'],
            error_message=dummy_error_message,
            edx_support_link=HTML(
                '<a href="{edx_support_url}">{support_url_name}</a>').format(
                    edx_support_url=settings.SUPPORT_SITE_LINK,
                    support_url_name=_('edX Support'),
                ),
            line_break=HTML('<br/>'))
        self._assert_saml_auth_data_with_error(response, current_backend,
                                               current_provider,
                                               expected_error_message)
Example #26
0
def generate_course_expired_fragment(user, course):
    message = generate_course_expired_message(user, course)
    if message:
        return Fragment(HTML(u"""\
            <div class="course-expiration-message">{}</div>
        """).format(message))
Example #27
0
def make_error_message(msg):
    # msg = msg.replace('<p>','<p><span class="inline-error">').replace('</p>','</span></p>')
    msg = HTML('<div class="capa_alert">{msg}</div>').format(msg=msg)
    return msg
Example #28
0
def student_dashboard(request):
    """
    Provides the LMS dashboard view

    TODO: This is lms specific and does not belong in common code.

    Arguments:
        request: The request object.

    Returns:
        The dashboard response.

    """
    if settings.PROGS_URLS and settings.PROGS_URLS.get('DASHBOARD', None):
        return redirect(get_progs_url(settings.PROGS_URLS.get('DASHBOARD')))

    user = request.user
    if not UserProfile.objects.filter(user=user).exists():
        return redirect(reverse('account_settings'))

    platform_name = configuration_helpers.get_value("platform_name", settings.PLATFORM_NAME)

    enable_verified_certificates = configuration_helpers.get_value(
        'ENABLE_VERIFIED_CERTIFICATES',
        settings.FEATURES.get('ENABLE_VERIFIED_CERTIFICATES')
    )
    display_course_modes_on_dashboard = configuration_helpers.get_value(
        'DISPLAY_COURSE_MODES_ON_DASHBOARD',
        settings.FEATURES.get('DISPLAY_COURSE_MODES_ON_DASHBOARD', True)
    )
    activation_email_support_link = configuration_helpers.get_value(
        'ACTIVATION_EMAIL_SUPPORT_LINK', settings.ACTIVATION_EMAIL_SUPPORT_LINK
    ) or settings.SUPPORT_SITE_LINK
    hide_dashboard_courses_until_activated = configuration_helpers.get_value(
        'HIDE_DASHBOARD_COURSES_UNTIL_ACTIVATED',
        settings.FEATURES.get('HIDE_DASHBOARD_COURSES_UNTIL_ACTIVATED', False)
    )
    empty_dashboard_message = configuration_helpers.get_value(
        'EMPTY_DASHBOARD_MESSAGE', None
    )

    # Get the org whitelist or the org blacklist for the current site
    site_org_whitelist, site_org_blacklist = get_org_black_and_whitelist_for_site()
    course_enrollments = list(get_course_enrollments(user, site_org_whitelist, site_org_blacklist))

    # Get the entitlements for the user and a mapping to all available sessions for that entitlement
    # If an entitlement has no available sessions, pass through a mock course overview object
    (course_entitlements,
     course_entitlement_available_sessions,
     unfulfilled_entitlement_pseudo_sessions) = get_filtered_course_entitlements(
        user,
        site_org_whitelist,
        site_org_blacklist
    )

    # Record how many courses there are so that we can get a better
    # understanding of usage patterns on prod.
    monitoring_utils.accumulate('num_courses', len(course_enrollments))

    # Sort the enrollment pairs by the enrollment date
    course_enrollments.sort(key=lambda x: x.created, reverse=True)

    # Retrieve the course modes for each course
    enrolled_course_ids = [enrollment.course_id for enrollment in course_enrollments]
    __, unexpired_course_modes = CourseMode.all_and_unexpired_modes_for_courses(enrolled_course_ids)
    course_modes_by_course = {
        course_id: {
            mode.slug: mode
            for mode in modes
        }
        for course_id, modes in iteritems(unexpired_course_modes)
    }

    # Check to see if the student has recently enrolled in a course.
    # If so, display a notification message confirming the enrollment.
    enrollment_message = _create_recent_enrollment_message(
        course_enrollments, course_modes_by_course
    )
    course_optouts = Optout.objects.filter(user=user).values_list('course_id', flat=True)

    # Display activation message
    activate_account_message = ''
    if not user.is_active:
        activate_account_message = Text(_(
            "Check your {email_start}{email}{email_end} inbox for an account activation link from {platform_name}. "
            "If you need help, contact {link_start}{platform_name} Support{link_end}."
        )).format(
            platform_name=platform_name,
            email_start=HTML("<strong>"),
            email_end=HTML("</strong>"),
            email=user.email,
            link_start=HTML("<a target='_blank' href='{activation_email_support_link}'>").format(
                activation_email_support_link=activation_email_support_link,
            ),
            link_end=HTML("</a>"),
        )

    enterprise_message = get_dashboard_consent_notification(request, user, course_enrollments)

    # Disable lookup of Enterprise consent_required_course due to ENT-727
    # Will re-enable after fixing WL-1315
    consent_required_courses = set()
    enterprise_customer_name = None

    # Account activation message
    account_activation_messages = [
        message for message in messages.get_messages(request) if 'account-activation' in message.tags
    ]

    # Global staff can see what courses encountered an error on their dashboard
    staff_access = False
    errored_courses = {}
    if has_access(user, 'staff', 'global'):
        # Show any courses that encountered an error on load
        staff_access = True
        errored_courses = modulestore().get_errored_courses()

    show_courseware_links_for = frozenset(
        enrollment.course_id for enrollment in course_enrollments
        if has_access(request.user, 'load', enrollment.course_overview)
    )

    # Find programs associated with course runs being displayed. This information
    # is passed in the template context to allow rendering of program-related
    # information on the dashboard.
    meter = ProgramProgressMeter(request.site, user, enrollments=course_enrollments)
    ecommerce_service = EcommerceService()
    inverted_programs = meter.invert_programs()

    urls, programs_data = {}, {}
    bundles_on_dashboard_flag = WaffleFlag(WaffleFlagNamespace(name=u'student.experiments'), u'bundles_on_dashboard')

    # TODO: Delete this code and the relevant HTML code after testing LEARNER-3072 is complete
    if bundles_on_dashboard_flag.is_enabled() and inverted_programs and inverted_programs.items():
        if len(course_enrollments) < 4:
            for program in inverted_programs.values():
                try:
                    program_uuid = program[0]['uuid']
                    program_data = get_programs(request.site, uuid=program_uuid)
                    program_data = ProgramDataExtender(program_data, request.user).extend()
                    skus = program_data.get('skus')
                    checkout_page_url = ecommerce_service.get_checkout_page_url(*skus)
                    program_data['completeProgramURL'] = checkout_page_url + '&bundle=' + program_data.get('uuid')
                    programs_data[program_uuid] = program_data
                except:  # pylint: disable=bare-except
                    pass

    # Construct a dictionary of course mode information
    # used to render the course list.  We re-use the course modes dict
    # we loaded earlier to avoid hitting the database.
    course_mode_info = {
        enrollment.course_id: complete_course_mode_info(
            enrollment.course_id, enrollment,
            modes=course_modes_by_course[enrollment.course_id]
        )
        for enrollment in course_enrollments
    }

    # Determine the per-course verification status
    # This is a dictionary in which the keys are course locators
    # and the values are one of:
    #
    # VERIFY_STATUS_NEED_TO_VERIFY
    # VERIFY_STATUS_SUBMITTED
    # VERIFY_STATUS_APPROVED
    # VERIFY_STATUS_MISSED_DEADLINE
    #
    # Each of which correspond to a particular message to display
    # next to the course on the dashboard.
    #
    # If a course is not included in this dictionary,
    # there is no verification messaging to display.
    verify_status_by_course = check_verify_status_by_course(user, course_enrollments)
    cert_statuses = {
        enrollment.course_id: cert_info(request.user, enrollment.course_overview)
        for enrollment in course_enrollments
    }

    # only show email settings for Mongo course and when bulk email is turned on
    show_email_settings_for = frozenset(
        enrollment.course_id for enrollment in course_enrollments if (
            BulkEmailFlag.feature_enabled(enrollment.course_id)
        )
    )

    # Verification Attempts
    # Used to generate the "you must reverify for course x" banner
    verification_status = IDVerificationService.user_status(user)
    verification_errors = get_verification_error_reasons_for_display(verification_status['error'])

    # Gets data for midcourse reverifications, if any are necessary or have failed
    statuses = ["approved", "denied", "pending", "must_reverify"]
    reverifications = reverification_info(statuses)

    block_courses = frozenset(
        enrollment.course_id for enrollment in course_enrollments
        if is_course_blocked(
            request,
            CourseRegistrationCode.objects.filter(
                course_id=enrollment.course_id,
                registrationcoderedemption__redeemed_by=request.user
            ),
            enrollment.course_id
        )
    )

    enrolled_courses_either_paid = frozenset(
        enrollment.course_id for enrollment in course_enrollments
        if enrollment.is_paid_course()
    )

    # If there are *any* denied reverifications that have not been toggled off,
    # we'll display the banner
    denied_banner = any(item.display for item in reverifications["denied"])

    # Populate the Order History for the side-bar.
    order_history_list = order_history(
        user,
        course_org_filter=site_org_whitelist,
        org_filter_out_set=site_org_blacklist
    )

    # get list of courses having pre-requisites yet to be completed
    courses_having_prerequisites = frozenset(
        enrollment.course_id for enrollment in course_enrollments
        if enrollment.course_overview.pre_requisite_courses
    )
    courses_requirements_not_met = get_pre_requisite_courses_not_completed(user, courses_having_prerequisites)

    if 'notlive' in request.GET:
        redirect_message = _("The course you are looking for does not start until {date}.").format(
            date=request.GET['notlive']
        )
    elif 'course_closed' in request.GET:
        redirect_message = _("The course you are looking for is closed for enrollment as of {date}.").format(
            date=request.GET['course_closed']
        )
    else:
        redirect_message = ''

    valid_verification_statuses = ['approved', 'must_reverify', 'pending', 'expired']
    display_sidebar_on_dashboard = (len(order_history_list) or
                                    (verification_status['status'] in valid_verification_statuses and
                                    verification_status['should_display']))

    # Filter out any course enrollment course cards that are associated with fulfilled entitlements
    for entitlement in [e for e in course_entitlements if e.enrollment_course_run is not None]:
        course_enrollments = [
            enr for enr in course_enrollments if entitlement.enrollment_course_run.course_id != enr.course_id
        ]

    context = {
        'urls': urls,
        'programs_data': programs_data,
        'enterprise_message': enterprise_message,
        'consent_required_courses': consent_required_courses,
        'enterprise_customer_name': enterprise_customer_name,
        'enrollment_message': enrollment_message,
        'redirect_message': redirect_message,
        'account_activation_messages': account_activation_messages,
        'activate_account_message': activate_account_message,
        'course_enrollments': course_enrollments,
        'course_entitlements': course_entitlements,
        'course_entitlement_available_sessions': course_entitlement_available_sessions,
        'unfulfilled_entitlement_pseudo_sessions': unfulfilled_entitlement_pseudo_sessions,
        'course_optouts': course_optouts,
        'staff_access': staff_access,
        'errored_courses': errored_courses,
        'show_courseware_links_for': show_courseware_links_for,
        'all_course_modes': course_mode_info,
        'cert_statuses': cert_statuses,
        'credit_statuses': _credit_statuses(user, course_enrollments),
        'show_email_settings_for': show_email_settings_for,
        'reverifications': reverifications,
        'verification_display': verification_status['should_display'],
        'verification_status': verification_status['status'],
        'verification_status_by_course': verify_status_by_course,
        'verification_errors': verification_errors,
        'block_courses': block_courses,
        'denied_banner': denied_banner,
        'billing_email': settings.PAYMENT_SUPPORT_EMAIL,
        'user': user,
        'logout_url': reverse('logout'),
        'platform_name': platform_name,
        'enrolled_courses_either_paid': enrolled_courses_either_paid,
        'provider_states': [],
        'order_history_list': order_history_list,
        'courses_requirements_not_met': courses_requirements_not_met,
        'nav_hidden': True,
        'inverted_programs': inverted_programs,
        'show_program_listing': ProgramsApiConfig.is_enabled(),
        'show_dashboard_tabs': True,
        'disable_courseware_js': True,
        'display_course_modes_on_dashboard': enable_verified_certificates and display_course_modes_on_dashboard,
        'display_sidebar_on_dashboard': display_sidebar_on_dashboard,
        'display_sidebar_account_activation_message': not(user.is_active or hide_dashboard_courses_until_activated),
        'display_dashboard_courses': (user.is_active or not hide_dashboard_courses_until_activated),
        'empty_dashboard_message': empty_dashboard_message,
    }

    if ecommerce_service.is_enabled(request.user):
        context.update({
            'use_ecommerce_payment_flow': True,
            'ecommerce_payment_page': ecommerce_service.payment_page_url(),
        })

    # Gather urls for course card resume buttons.
    resume_button_urls = _get_urls_for_resume_buttons(user, course_enrollments)
    # There must be enough urls for dashboard.html. Template creates course
    # cards for "enrollments + entitlements".
    resume_button_urls += ['' for entitlement in course_entitlements]
    context.update({
        'resume_button_urls': resume_button_urls
    })

    response = render_to_response('dashboard.html', context)
    set_user_info_cookie(response, request)
    return response
Example #29
0
def symmath_check(expect,
                  ans,
                  dynamath=None,
                  options=None,
                  debug=None,
                  xml=None):
    """
    Check a symbolic mathematical expression using sympy.
    The input may be presentation MathML.  Uses formula.

    This is the default Symbolic Response checking function

    Desc of args:
    expect is a sympy string representing the correct answer. It is interpreted
     using my_sympify (from formula.py), which reads strings as sympy input
     (e.g. 'integrate(x^2, (x,1,2))' would be valid, and evaluate to give 1.5)

    ans is student-typed answer. It is expected to be ascii math, but the code
     below would support a sympy string.

    dynamath is the PMathML string converted by MathJax. It is used if
     evaluation with ans is not sufficient.

    options is a string with these possible substrings, set as an xml property
     of the problem:
     -matrix - make a sympy matrix, rather than a list of lists, if possible
     -qubit - passed to my_sympify
     -imaginary - used in formla, presumably to signal to use i as sqrt(-1)?
     -numerical - force numerical comparison.
    """

    msg = ''
    # msg += '<p/>abname=%s' % abname
    # msg += '<p/>adict=%s' % (repr(adict).replace('<','&lt;'))

    threshold = 1.0e-3  # for numerical comparison (also with matrices)
    DEBUG = debug

    if xml is not None:
        DEBUG = xml.get(
            'debug',
            False)  # override debug flag using attribute in symbolicmath xml
        if DEBUG in ['0', 'False']:
            DEBUG = False

    # options
    if options is None:
        options = ''
    do_matrix = 'matrix' in options
    do_qubit = 'qubit' in options
    do_numerical = 'numerical' in options

    # parse expected answer
    try:
        fexpect = my_sympify(str(expect), matrix=do_matrix, do_qubit=do_qubit)
    except Exception as err:
        msg += HTML(
            '<p>Error {err} in parsing OUR expected answer "{expect}"</p>'
        ).format(err=err, expect=expect)
        return {'ok': False, 'msg': make_error_message(msg)}

    ###### Sympy input #######
    # if expected answer is a number, try parsing provided answer as a number also
    try:
        fans = my_sympify(str(ans), matrix=do_matrix, do_qubit=do_qubit)
    except Exception as err:
        fans = None

    # do a numerical comparison if both expected and answer are numbers
    if hasattr(fexpect, 'is_number') and fexpect.is_number \
       and hasattr(fans, 'is_number') and fans.is_number:
        if is_within_tolerance(fexpect, fans, threshold):
            return {'ok': True, 'msg': msg}
        else:
            msg += HTML('<p>You entered: {fans}</p>').format(
                fans=to_latex(fans))
            return {'ok': False, 'msg': msg}

    if do_numerical:  # numerical answer expected - force numerical comparison
        if is_within_tolerance(fexpect, fans, threshold):
            return {'ok': True, 'msg': msg}
        else:
            msg += HTML('<p>You entered: {fans} (note that a numerical answer is expected)</p>').\
                format(fans=to_latex(fans))
            return {'ok': False, 'msg': msg}

    if fexpect == fans:
        msg += HTML('<p>You entered: {fans}</p>').format(fans=to_latex(fans))
        return {'ok': True, 'msg': msg}

    ###### PMathML input ######
    # convert mathml answer to formula
    try:
        mmlans = dynamath[0] if dynamath else None
    except Exception as err:
        mmlans = None
    if not mmlans:
        return {
            'ok':
            False,
            'msg':
            '[symmath_check] failed to get MathML for input; dynamath=%s' %
            dynamath
        }

    f = formula(mmlans, options=options)

    # get sympy representation of the formula
    # if DEBUG: msg += '<p/> mmlans=%s' % repr(mmlans).replace('<','&lt;')
    try:
        fsym = f.sympy
        msg += HTML('<p>You entered: {sympy}</p>').format(
            sympy=to_latex(f.sympy))
    except Exception as err:
        log.exception("Error evaluating expression '%s' as a valid equation",
                      ans)
        msg += HTML(
            "<p>Error in evaluating your expression '{ans}' as a valid equation</p>"
        ).format(ans=ans)
        if "Illegal math" in str(err):
            msg += HTML("<p>Illegal math expression</p>")
        if DEBUG:
            msg += HTML(
                'Error: {err}<hr><p><font color="blue">DEBUG messages:</p><p><pre>{format_exc}</pre></p>'
                '<p>cmathml=<pre>{cmathml}</pre></p><p>pmathml=<pre>{pmathml}</pre></p><hr>'
            ).format(err=escape(str(err)),
                     format_exc=traceback.format_exc(),
                     cmathml=escape(f.cmathml),
                     pmathml=escape(mmlans))
        return {'ok': False, 'msg': make_error_message(msg)}

    # do numerical comparison with expected
    if hasattr(fexpect, 'is_number') and fexpect.is_number:
        if hasattr(fsym, 'is_number') and fsym.is_number:
            if abs(abs(fsym - fexpect) / fexpect) < threshold:
                return {'ok': True, 'msg': msg}
            return {'ok': False, 'msg': msg}
        msg += HTML(
            "<p>Expecting a numerical answer!</p><p>given = {ans}</p><p>fsym = {fsym}</p>"
        ).format(ans=repr(ans), fsym=repr(fsym))
        # msg += "<p>cmathml = <pre>%s</pre></p>" % str(f.cmathml).replace('<','&lt;')
        return {'ok': False, 'msg': make_error_message(msg)}

    # Here is a good spot for adding calls to X.simplify() or X.expand(),
    #  allowing equivalence over binomial expansion or trig identities

    # exactly the same?
    if fexpect == fsym:
        return {'ok': True, 'msg': msg}

    if isinstance(fexpect, list):
        try:
            xgiven = my_evalf(fsym, chop=True)
            dm = my_evalf(sympy.Matrix(fexpect) - sympy.Matrix(xgiven),
                          chop=True)
            if abs(dm.vec().norm().evalf()) < threshold:
                return {'ok': True, 'msg': msg}
        except sympy.ShapeError:
            msg += HTML(
                "<p>Error - your input vector or matrix has the wrong dimensions"
            )
            return {'ok': False, 'msg': make_error_message(msg)}
        except Exception as err:
            msg += HTML(
                "<p>Error %s in comparing expected (a list) and your answer</p>"
            ).format(escape(str(err)))
            if DEBUG:
                msg += HTML("<p/><pre>{format_exc}</pre>").format(
                    format_exc=traceback.format_exc())
            return {'ok': False, 'msg': make_error_message(msg)}

    #diff = (fexpect-fsym).simplify()
    #fsym = fsym.simplify()
    #fexpect = fexpect.simplify()
    try:
        diff = (fexpect - fsym)
    except Exception as err:
        diff = None

    if DEBUG:
        msg += HTML('<hr><p><font color="blue">DEBUG messages:</p><p>Got: {fsym}</p><p>Expecting: {fexpect}</p>')\
            .format(fsym=repr(fsym), fexpect=repr(fexpect).replace('**', '^').replace('hat(I)', 'hat(i)'))
        # msg += "<p/>Got: %s" % str([type(x) for x in fsym.atoms()]).replace('<','&lt;')
        # msg += "<p/>Expecting: %s" % str([type(x) for x in fexpect.atoms()]).replace('<','&lt;')
        if diff:
            msg += HTML("<p>Difference: {diff}</p>").format(
                diff=to_latex(diff))
        msg += HTML('<hr>')

    # Used to return more keys: 'ex': fexpect, 'got': fsym
    return {'ok': False, 'msg': msg}
Example #30
0
def check(expect,
          given,
          numerical=False,
          matrix=False,
          normphase=False,
          abcsym=False,
          do_qubit=True,
          symtab=None,
          dosimplify=False):
    """
    Returns dict with

      'ok': True if check is good, False otherwise
      'msg': response message (in HTML)

    "expect" may have multiple possible acceptable answers, separated by "__OR__"

    """

    if "__or__" in expect:  # if multiple acceptable answers
        eset = expect.split('__or__')  # then see if any match
        for eone in eset:
            ret = check(eone, given, numerical, matrix, normphase, abcsym,
                        do_qubit, symtab, dosimplify)
            if ret['ok']:
                return ret
        return ret

    flags = {}
    if "__autonorm__" in expect:
        flags['autonorm'] = True
        expect = expect.replace('__autonorm__', '')
        matrix = True

    threshold = 1.0e-3
    if "__threshold__" in expect:
        (expect, st) = expect.split('__threshold__')
        threshold = float(st)
        numerical = True

    if str(given) == '' and not str(expect) == '':
        return {'ok': False, 'msg': ''}

    try:
        xgiven = my_sympify(given,
                            normphase,
                            matrix,
                            do_qubit=do_qubit,
                            abcsym=abcsym,
                            symtab=symtab)
    except Exception as err:
        return {
            'ok':
            False,
            'msg':
            HTML('Error {err}<br/> in evaluating your expression "{given}"').
            format(err=err, given=given)
        }

    try:
        xexpect = my_sympify(expect,
                             normphase,
                             matrix,
                             do_qubit=do_qubit,
                             abcsym=abcsym,
                             symtab=symtab)
    except Exception as err:
        return {
            'ok':
            False,
            'msg':
            HTML('Error {err}<br/> in evaluating OUR expression "{expect}"').
            format(err=err, expect=expect)
        }

    if 'autonorm' in flags:  # normalize trace of matrices
        try:
            xgiven /= xgiven.trace()
        except Exception as err:
            return {
                'ok':
                False,
                'msg':
                HTML(
                    'Error {err}<br/> in normalizing trace of your expression {xgiven}'
                ).format(err=err, xgiven=to_latex(xgiven))
            }
        try:
            xexpect /= xexpect.trace()
        except Exception as err:
            return {
                'ok':
                False,
                'msg':
                HTML(
                    'Error {err}<br/> in normalizing trace of OUR expression {xexpect}'
                ).format(err=err, xexpect=to_latex(xexpect))
            }

    msg = 'Your expression was evaluated as ' + to_latex(xgiven)
    # msg += '<br/>Expected ' + to_latex(xexpect)

    # msg += "<br/>flags=%s" % flags

    if matrix and numerical:
        xgiven = my_evalf(xgiven, chop=True)
        dm = my_evalf(sympy.Matrix(xexpect) - sympy.Matrix(xgiven), chop=True)
        msg += " = " + to_latex(xgiven)
        if abs(dm.vec().norm().evalf()) < threshold:
            return {'ok': True, 'msg': msg}
        else:
            pass
            #msg += "dm = " + to_latex(dm) + " diff = " + str(abs(dm.vec().norm().evalf()))
            #msg += "expect = " + to_latex(xexpect)
    elif dosimplify:
        if sympy.simplify(xexpect) == sympy.simplify(xgiven):
            return {'ok': True, 'msg': msg}
    elif numerical:
        if abs((xexpect - xgiven).evalf(chop=True)) < threshold:
            return {'ok': True, 'msg': msg}
    elif xexpect == xgiven:
        return {'ok': True, 'msg': msg}

    #msg += "<p/>expect='%s', given='%s'" % (expect,given)	# debugging
    # msg += "<p/> dot test " + to_latex(dot(sympy.Symbol('x'),sympy.Symbol('y')))
    return {'ok': False, 'msg': msg}
Example #31
0
 def __str__(self):
     return HTML(u"<CourseCompleteImageConfiguration for '{mode}'{default}>"
                 ).format(mode=HTML(self.mode),
                          default=HTML(u" (default)")
                          if self.default else HTML(u''))