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)
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)
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>'), )
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>")
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)
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()
def username(self, obj): return HTML('<a href="{}">{}</a>').format( reverse("admin:auth_user_change", args=(obj.enrollment.user.id, )), obj.enrollment.user.username)
def test_ungettext(self): for i in [1, 2]: out = Text(ungettext(u"1 & {}", u"2 & {}", i)).format(HTML(u"<>")) self.assertEqual(out, u"{} & <>".format(i))
def _format_error_html(msg): """ Format an HTML error message """ return HTML(u'<p class="error_msg">{msg}</p>').format(msg=msg)
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))
def test_ungettext(self): for i in [1, 2]: out = ungettext("1 & {}", "2 & {}", i).format(HTML("<>")) self.assertEqual(out, "{} & <>".format(i))
def __str__(self): return HTML(u"<Badge '{slug}' for '{issuing_component}'>").format( slug=HTML(self.slug), issuing_component=HTML(self.issuing_component))
def __str__(self): return HTML(u"<CourseEventBadgesConfiguration ({})>").format( Text(u"Enabled") if self.enabled else Text(u"Disabled"))
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()
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)
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>'), )
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))
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 }
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)
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)
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))
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
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
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('<','<')) 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('<','<') 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('<','<') 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('<','<') # msg += "<p/>Expecting: %s" % str([type(x) for x in fexpect.atoms()]).replace('<','<') 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}
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}
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''))