def to_dict(self): """ Convert entitlement to dictionary representation including relevant policy information. Returns: The entitlement UUID The associated course's UUID The date at which the entitlement expired. None if it is still active. The localized string representing the date at which the entitlement expires. """ expiration_date = None if self.get_days_until_expiration( ) < settings.ENTITLEMENT_EXPIRED_ALERT_PERIOD: expiration_date = strftime_localized( now() + timedelta(days=self.get_days_until_expiration()), 'SHORT_DATE') expired_at = strftime_localized( self.expired_at_datetime, 'SHORT_DATE') if self.expired_at_datetime else None return { 'uuid': str(self.uuid), 'course_uuid': str(self.course_uuid), 'expired_at': expired_at, 'expiration_date': expiration_date }
def get_user_orders(user): """Given a user, get the detail of all the orders from the Ecommerce service. Args: user (User): The user to authenticate as when requesting ecommerce. Returns: list of dict, representing orders returned by the Ecommerce service. """ user_orders = [] commerce_configuration = CommerceConfiguration.current() user_query = {'username': user.username} use_cache = commerce_configuration.is_cache_enabled cache_key = commerce_configuration.CACHE_KEY + '.' + str(user.id) if use_cache else None api = ecommerce_api_client(user) commerce_user_orders = get_edx_api_data( commerce_configuration, 'orders', api=api, querystring=user_query, cache_key=cache_key ) for order in commerce_user_orders: if order['status'].lower() == 'complete': date_placed = datetime.strptime(order['date_placed'], "%Y-%m-%dT%H:%M:%SZ") order_data = { 'number': order['number'], 'price': order['total_excl_tax'], 'order_date': strftime_localized(date_placed, 'SHORT_DATE'), 'receipt_url': EcommerceService().get_receipt_page_url(order['number']), 'lines': order['lines'], } user_orders.append(order_data) return user_orders
def test_expired_course(self): """ Ensure that a user accessing an expired course sees a redirect to the student dashboard, not a 404. """ CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=datetime(2010, 1, 1, tzinfo=UTC)) course = CourseFactory.create(start=THREE_YEARS_AGO) url = course_home_url(course) for mode in [CourseMode.AUDIT, CourseMode.VERIFIED]: CourseModeFactory.create(course_id=course.id, mode_slug=mode) # assert that an if an expired audit user tries to access the course they are redirected to the dashboard audit_user = UserFactory(password=self.TEST_PASSWORD) self.client.login(username=audit_user.username, password=self.TEST_PASSWORD) audit_enrollment = CourseEnrollment.enroll(audit_user, course.id, mode=CourseMode.AUDIT) audit_enrollment.created = THREE_YEARS_AGO + timedelta(days=1) audit_enrollment.save() response = self.client.get(url) expiration_date = strftime_localized(course.start + timedelta(weeks=4) + timedelta(days=1), 'SHORT_DATE') expected_params = QueryDict(mutable=True) course_name = CourseOverview.get_from_id(course.id).display_name_with_default expected_params['access_response_error'] = 'Access to {run} expired on {expiration_date}'.format( run=course_name, expiration_date=expiration_date ) expected_url = '{url}?{params}'.format( url=reverse('dashboard'), params=expected_params.urlencode() ) self.assertRedirects(response, expected_url)
def check_course_access_with_redirect(course, user, action, check_if_enrolled=False, check_survey_complete=True, check_if_authenticated=False): # lint-amnesty, pylint: disable=line-too-long """ Check that the user has the access to perform the specified action on the course (CourseBlock|CourseOverview). check_if_enrolled: If true, additionally verifies that the user is enrolled. check_survey_complete: If true, additionally verifies that the user has completed the survey. """ request = get_current_request() check_content_start_date_for_masquerade_user(course.id, user, request, course.start) access_response = check_course_access(course, user, action, check_if_enrolled, check_survey_complete, check_if_authenticated) # lint-amnesty, pylint: disable=line-too-long if not access_response: # Redirect if StartDateError if isinstance(access_response, StartDateError): start_date = strftime_localized(course.start, 'SHORT_DATE') params = QueryDict(mutable=True) params['notlive'] = start_date raise CourseAccessRedirect( '{dashboard_url}?{params}'.format( dashboard_url=reverse('dashboard'), params=params.urlencode()), access_response) # Redirect if AuditExpiredError if isinstance(access_response, AuditExpiredError): params = QueryDict(mutable=True) params[ 'access_response_error'] = access_response.additional_context_user_message raise CourseAccessRedirect( '{dashboard_url}?{params}'.format( dashboard_url=reverse('dashboard'), params=params.urlencode()), access_response) # Redirect if the user must answer a survey before entering the course. if isinstance(access_response, MilestoneAccessError): raise CourseAccessRedirect( '{dashboard_url}'.format(dashboard_url=reverse('dashboard'), ), access_response) # Redirect if the user is not enrolled and must be to see content if isinstance(access_response, EnrollmentRequiredAccessError): raise CourseAccessRedirect( reverse('about_course', args=[str(course.id)])) # Redirect if user must be authenticated to view the content if isinstance(access_response, AuthenticationRequiredAccessError): raise CourseAccessRedirect( reverse('about_course', args=[str(course.id)])) # Redirect if the user must answer a survey before entering the course. if isinstance(access_response, SurveyRequiredAccessError): raise CourseAccessRedirect( reverse('course_survey', args=[str(course.id)])) # Deliberately return a non-specific error message to avoid # leaking info about access control settings raise CoursewareAccessException(access_response)
def test_usual_strftime_behavior(self, fmt_expected): (fmt, expected) = fmt_expected dtime = datetime(2013, 2, 14, 16, 41, 17) self.assertEqual(expected, strftime_localized(dtime, fmt)) # strftime doesn't like Unicode, so do the work in UTF8. self.assertEqual( expected.encode('utf-8') if six.PY2 else expected, dtime.strftime(fmt.encode('utf-8') if six.PY2 else fmt))
def _update_certificate_context(context, course, user_certificate, platform_name): """ Build up the certificate web view context using the provided values (Helper method to keep the view clean) """ # Populate dynamic output values using the course/certificate data loaded above certificate_type = context.get('certificate_type') # Override the defaults with any mode-specific static values context['certificate_id_number'] = user_certificate.verify_uuid context['certificate_verify_url'] = u"{prefix}{uuid}{suffix}".format( prefix=context.get('certificate_verify_url_prefix'), uuid=user_certificate.verify_uuid, suffix=context.get('certificate_verify_url_suffix') ) # Translators: The format of the date includes the full name of the month date = display_date_for_certificate(course, user_certificate) context['certificate_date_issued'] = _(u'{month} {day}, {year}').format( month=strftime_localized(date, "%B"), day=date.day, year=date.year ) # Translators: This text represents the verification of the certificate context['document_meta_description'] = _(u'This is a valid {platform_name} certificate for {user_name}, ' u'who participated in {partner_short_name} {course_number}').format( platform_name=platform_name, user_name=context['accomplishment_copy_name'], partner_short_name=context['organization_short_name'], course_number=context['course_number'] ) # Translators: This text is bound to the HTML 'title' element of the page and appears in the browser title bar context['document_title'] = _(u"{partner_short_name} {course_number} Certificate | {platform_name}").format( partner_short_name=context['organization_short_name'], course_number=context['course_number'], platform_name=platform_name ) # Translators: This text fragment appears after the student's name (displayed in a large font) on the certificate # screen. The text describes the accomplishment represented by the certificate information displayed to the user context['accomplishment_copy_description_full'] = _(u"successfully completed, received a passing grade, and was " u"awarded this {platform_name} {certificate_type} " u"Certificate of Completion in ").format( platform_name=platform_name, certificate_type=context.get("certificate_type")) certificate_type_description = get_certificate_description(user_certificate.mode, certificate_type, platform_name) if certificate_type_description: context['certificate_type_description'] = certificate_type_description # Translators: This text describes the purpose (and therefore, value) of a course certificate context['certificate_info_description'] = _(u"{platform_name} acknowledges achievements through " u"certificates, which are awarded for course activities " u"that {platform_name} students complete.").format( platform_name=platform_name, )
def enforce_compliance_on_login(user, password): """ Verify that the user's password is compliant with password policy rules and determine what should be done if it is not. Raises NonCompliantPasswordException when the password is found to be non-compliant and the compliance deadline for the user has been reached. In this case, login should be prevented. Raises NonCompliantPasswordWarning when the password is found to be non-compliant and the compliance deadline for the user is in the future. Returns None when the password is found to be compliant, or when no deadline for compliance has been set for the user. Important: This method should only be called AFTER the user has been authenticated. """ is_compliant = _check_user_compliance(user, password) if is_compliant: return deadline = _get_compliance_deadline_for_user(user) if deadline is None: return now = datetime.now(pytz.UTC) if now >= deadline: # lint-amnesty, pylint: disable=no-else-raise raise NonCompliantPasswordException( HTML(_( '{strong_tag_open}We recently changed our password requirements{strong_tag_close}{break_line_tag}' 'Your current password does not meet the new security requirements. We just sent a password-reset ' 'message to the email address associated with this account. Thank you for helping us keep your data ' 'safe.' )).format( strong_tag_open=HTML('<strong>'), strong_tag_close=HTML('</strong>'), break_line_tag=HTML('<br/>'), ) ) else: raise NonCompliantPasswordWarning( HTML(_( '{strong_tag_open}Required Action: Please update your password{strong_tag_close}{break_line_tag}' 'As of {deadline}, {platform_name} will require all learners to have complex passwords. Your current ' 'password does not meet these requirements. To reset your password, go to to ' '{anchor_tag_open}Account Settings{anchor_tag_close}.' )).format( strong_tag_open=HTML('<strong>'), strong_tag_close=HTML('</strong>'), break_line_tag=HTML('<br/>'), platform_name=settings.PLATFORM_NAME, deadline=strftime_localized(deadline, DEFAULT_SHORT_DATE_FORMAT), anchor_tag_open=HTML('<a href="{account_settings_url}">').format( account_settings_url=settings.LMS_ROOT_URL + "/account/settings" ), anchor_tag_close=HTML('</a>') ) )
def get_expiration_banner_text(user, course, language='en'): """ Get text for banner that messages user course expiration date for different tests that depend on it. """ upgrade_link = verified_upgrade_deadline_link(user=user, course=course) enrollment = CourseEnrollment.get_enrollment(user, course.id) expiration_date = enrollment.created + timedelta(weeks=4) upgrade_deadline = enrollment.upgrade_deadline if upgrade_deadline is None or now() < upgrade_deadline: upgrade_deadline = enrollment.course_upgrade_deadline date_string = u'<span class="localized-datetime" data-format="shortDate" data-timezone="None" \ data-datetime="{formatted_date}" data-language="{language}">{formatted_date_localized}</span>' formatted_expiration_date = date_string.format( language=language, formatted_date=expiration_date.isoformat(), formatted_date_localized=strftime_localized(expiration_date, EXPIRATION_DATE_FORMAT_STR) ) if upgrade_deadline: formatted_upgrade_deadline = date_string.format( language=language, formatted_date=upgrade_deadline.isoformat(), formatted_date_localized=strftime_localized(upgrade_deadline, EXPIRATION_DATE_FORMAT_STR) ) bannerText = u'<strong>Audit Access Expires {expiration_date}</strong><br>\ You lose all access to this course, including your progress, on {expiration_date}.\ <br>Upgrade by {upgrade_deadline} to get unlimited access to the course as long as it exists\ on the site. <a id="FBE_banner" href="{upgrade_link}">Upgrade now<span class="sr-only"> to retain access past\ {expiration_date}</span></a>'.format( expiration_date=formatted_expiration_date, upgrade_link=upgrade_link, upgrade_deadline=formatted_upgrade_deadline ) else: bannerText = u'<strong>Audit Access Expires {expiration_date}</strong><br>\ You lose all access to this course, including your progress, on {expiration_date}.\ '.format( expiration_date=formatted_expiration_date ) return bannerText
def get_formatted_date(date_to_format, formatting): """ Takes a date and a format to return a string in the desired format Arguments: date_to_format (datetime.datetime): Date that we want to format formatting (str): The format we want the result to be in Returns: str: Formatted date """ return strftime_localized(date_to_format, formatting or '') if date_to_format else ''
def convert_date_time_zone_and_format(date_time_object, time_zone, time_format): """ Change time zone of datetime object and return a formatted string Args: date_time_object (datetime): datetime object time_zone (string): a time zone string time_format (string): a time format string Returns: DateTime(string) """ date_in_desired_timezone = convert_timezone(date_time_object, time_zone) return strftime_localized(date_in_desired_timezone, time_format)
def test_non_live_course(self): """Ensure that a user accessing a non-live course sees a redirect to the student dashboard, not a 404. """ self.setup_user() self.enroll(self.course) url = reverse('info', args=[str(self.course.id)]) response = self.client.get(url) start_date = strftime_localized(self.course.start, 'SHORT_DATE') expected_params = QueryDict(mutable=True) expected_params['notlive'] = start_date expected_url = '{url}?{params}'.format( url=reverse('dashboard'), params=expected_params.urlencode()) self.assertRedirects(response, expected_url)
def strftime(self, *args, **kwargs): """ A locale-aware implementation of strftime. """ # This is the wrong place to import this function. I'm putting it here # because the xmodule test suite can't import this module, because # Django is not available in that suite. This function isn't called in # that suite, so this hides the import so the test won't fail. # # As I said, this is wrong. But Cale says this code will soon be # refactored to a place that will be right, and the code can be made # right there. If you are reading this comment after April 1, 2014, # then Cale was a liar. from common.djangoapps.util.date_utils import strftime_localized return strftime_localized(*args, **kwargs)
def test_non_live_course(self): """ Ensure that a user accessing a non-live course sees a redirect to the student dashboard, not a 404. """ future_course = self.create_future_course() self.create_user_for_course(future_course, CourseUserType.ENROLLED) url = course_home_url(future_course) response = self.client.get(url) start_date = strftime_localized(future_course.start, 'SHORT_DATE') expected_params = QueryDict(mutable=True) expected_params['notlive'] = start_date expected_url = '{url}?{params}'.format( url=reverse('dashboard'), params=expected_params.urlencode()) self.assertRedirects(response, expected_url)
def construct_arabic_datetime_string(date_time_object, output_format): """ Create the correct Arabic language localized date string based on the string format provided Args: date_time_object (datetime): Datetime date, time or datetime object output_format (str): Output formatting for the datetime object Returns: str: Localized arabic output string in the specified format """ date_in_AST = convert_timezone(date_time_object, WEBINAR_DEFAULT_TIME_ZONE) with override(ARABIC_LANGUAGE_CODE): if output_format == WEBINAR_DATE_TIME_FORMAT: return '{week_day}, {month} {day}, {year} {hours}:{minutes} {time_format} AST'.format( week_day=strftime_localized(date_in_AST, '%A'), month=strftime_localized(date_in_AST, '%B'), day=date_in_AST.day, year=date_in_AST.year, hours=strftime_localized(date_in_AST, '%I'), minutes=strftime_localized(date_in_AST, '%M'), time_format=strftime_localized(date_in_AST, '%p'), ) elif output_format == WEBINAR_DATE_FORMAT: return '{month} {day}, {year}'.format(month=strftime_localized( date_in_AST, '%B'), day=date_in_AST.day, year=date_in_AST.year) elif output_format == WEBINAR_TIME_FORMAT: return '{hours}:{minutes}, {time_format} AST'.format( hours=strftime_localized(date_in_AST, '%I'), minutes=strftime_localized(date_in_AST, '%M'), time_format=strftime_localized(date_in_AST, '%p'), )
def get_arabic_certificate_extra_context(request, course_id): """ Compute `date_issued` and `gender` for the Arabic Course Certificates. This method is used directly from arabic certificate template. This is done to avoid core changes and customise the Arabic certificates. To display gender specific text, we need to send gender in context. Date formatting is also custom for Arabic certificates so this method returns date in the required format as well. Args: request (WSGIRequest): request object course_id (str): Course Id Returns: tuple: Gender of the learner and Date issued. """ certificate = None certificate_uuid = request.resolver_match.kwargs.get('certificate_uuid', None) if certificate_uuid: certificate = GeneratedCertificate.eligible_certificates.filter( verify_uuid=certificate_uuid, status=CertificateStatuses.downloadable ).first() user = certificate.user if certificate else request.user course_key = CourseKey.from_string(course_id) course = get_course_by_id(course_key) if not certificate: preview_mode = request.GET.get('preview', None) certificate = _get_user_certificate(request, user, course_key, course, preview_mode) date_format = u'{day} {month} {year}' if course.language == ARABIC_LANGUAGE_CODE else u'{month} {day}, {year}' display_date = display_date_for_certificate(course, certificate) with translation.override(ARABIC_LANGUAGE_CODE): date_issued = _(date_format).format( # pylint: disable=translation-of-non-string month=strftime_localized(display_date, '%B'), day=display_date.day, year=display_date.year ) return user.profile.gender, date_issued
def __init__(self, user, course, expiration_date): error_code = 'audit_expired' developer_message = f'User {user} had access to {course} until {expiration_date}' expiration_date = strftime_localized(expiration_date, 'SHORT_DATE') user_message = _('Access expired on {expiration_date}').format( expiration_date=expiration_date) try: course_name = course.display_name_with_default additional_context_user_message = _( 'Access to {course_name} expired on {expiration_date}').format( course_name=course_name, expiration_date=expiration_date) except CourseOverview.DoesNotExist: additional_context_user_message = _( 'Access to the course you were looking' ' for expired on {expiration_date}').format( expiration_date=expiration_date) # lint-amnesty, pylint: disable=super-with-arguments super().__init__(error_code, developer_message, user_message, additional_context_user_message)
def __init__(self, user, course, expiration_date): error_code = 'audit_expired' developer_message = 'User {} had access to {} until {}'.format( user, course, expiration_date) expiration_date = strftime_localized(expiration_date, 'SHORT_DATE') user_message = _('Access expired on {expiration_date}').format( expiration_date=expiration_date) try: course_name = course.display_name_with_default additional_context_user_message = _( 'Access to {course_name} expired on {expiration_date}').format( course_name=course_name, expiration_date=expiration_date) except CourseOverview.DoesNotExist: additional_context_user_message = _( 'Access to the course you were looking' ' for expired on {expiration_date}').format( expiration_date=expiration_date) super(AuditExpiredError, self).__init__(error_code, developer_message, user_message, additional_context_user_message)
def __init__(self, user, course, expiration_date): error_code = "audit_expired" developer_message = u"User {} had access to {} until {}".format( user, course, expiration_date) expiration_date = strftime_localized(expiration_date, EXPIRATION_DATE_FORMAT_STR) user_message = _(u"Access expired on {expiration_date}").format( expiration_date=expiration_date) try: course_name = course.display_name_with_default additional_context_user_message = _( u"Access to {course_name} expired on {expiration_date}" ).format(course_name=course_name, expiration_date=expiration_date) except CourseOverview.DoesNotExist: additional_context_user_message = _( u"Access to the course you were looking" u" for expired on {expiration_date}").format( expiration_date=expiration_date) super(AuditExpiredError, self).__init__(error_code, developer_message, user_message, additional_context_user_message)
def get_weekly_application_submissions(submitted_applications, number_of_weeks): """ Calculates the number of submitted applications for the past few weeks, sent as argument, including the current week. Arguments: submitted_applications (QuerySet): queryset containing submitted applications number_of_weeks (int): number of weeks for which weekly submissions need to be calculated Returns: JSON: a json object containing two lists: one for dates of the past 12 weeks and the other for number of application submissions in those weeks """ today = datetime.today() start_of_week = today - timedelta(days=today.weekday()) submissions_list = [0] * number_of_weeks date_list = [''] * number_of_weeks start_date = start_of_week end_date = today for index in range(number_of_weeks - 1, -1, -1): filtered_applications_count = submitted_applications.get_submitted_applications_count_in_date_range( start_date, end_date) submissions_list[index] = filtered_applications_count date_list[index] = strftime_localized( start_date, WEEKLY_SUBMISSIONS_GRAPH_DATE_FORMAT) end_date = start_date - timedelta(days=1) start_date = start_date - timedelta(days=7) return json.dumps({ 'submissions_list': submissions_list, 'date_list': date_list })
def get_certificates(self): """ Get certificates data for user. Returns: list: Contains dicts which has data for all the courses. """ user = self.request.user certificates = GeneratedCertificate.eligible_certificates.filter( user=user).order_by('course_id') certificate_data = [] for cert in certificates: formatted_cert = format_certificate_for_user(user.username, cert) if formatted_cert: course = get_course_by_id(cert.course_id) formatted_cert['course_display_name'] = course.display_name formatted_cert['date_created'] = strftime_localized( display_date_for_certificate(course, cert), '%b %d, %Y') certificate_data.append(formatted_cert) return certificate_data
def _attach_course_run_enrollment_open_date(self, run_mode): run_mode['enrollment_open_date'] = strftime_localized( self.enrollment_start, 'SHORT_DATE')
def test_invalid_format_strings(self, fmt): dtime = datetime(2013, 2, 14, 16, 41, 17) with self.assertRaises(ValueError): strftime_localized(dtime, fmt)
def test_recursion_protection(self, fmt_expected): (fmt, expected) = fmt_expected dtime = datetime(2013, 2, 14, 16, 41, 17) self.assertEqual(expected, strftime_localized(dtime, fmt))
def test_usual_strftime_behavior(self, fmt_expected): (fmt, expected) = fmt_expected dtime = datetime(2013, 2, 14, 16, 41, 17) self.assertEqual(expected, strftime_localized(dtime, fmt)) self.assertEqual(expected, dtime.strftime(fmt))
def test_shortcuts(self, fmt_expected): (fmt, expected) = fmt_expected dtime = datetime(2013, 2, 14, 16, 41, 17) assert expected == strftime_localized(dtime, fmt)
def test_translated_words(self, fmt_expected): (fmt, expected) = fmt_expected dtime = datetime(2013, 2, 14, 16, 41, 17) assert expected == strftime_localized(dtime, fmt)
def _update_certificate_context(context, course, course_overview, user_certificate, platform_name): """ Build up the certificate web view context using the provided values (Helper method to keep the view clean) """ # Populate dynamic output values using the course/certificate data loaded above certificate_type = context.get('certificate_type') # Override the defaults with any mode-specific static values context['certificate_id_number'] = user_certificate.verify_uuid context['certificate_verify_url'] = "{prefix}{uuid}{suffix}".format( prefix=context.get('certificate_verify_url_prefix'), uuid=user_certificate.verify_uuid, suffix=context.get('certificate_verify_url_suffix')) # We prefer a CourseOverview for this function because it validates and corrects certificate_available_date # and certificates_display_behavior values. However, not all certificates are guaranteed to have a CourseOverview # associated with them, so we fall back on the course in that case. This shouldn't cause a problem because courses # that are missing CourseOverviews are generally old courses, and thus their display values are no longer relevant if course_overview: date = display_date_for_certificate(course_overview, user_certificate) else: date = display_date_for_certificate(course, user_certificate) # Translators: The format of the date includes the full name of the month context['certificate_date_issued'] = strftime_localized( date, settings.CERTIFICATE_DATE_FORMAT) # Translators: This text represents the verification of the certificate context['document_meta_description'] = _( 'This is a valid {platform_name} certificate for {user_name}, ' 'who participated in {partner_short_name} {course_number}').format( platform_name=platform_name, user_name=context['accomplishment_copy_name'], partner_short_name=context['organization_short_name'], course_number=context['course_number']) # Translators: This text is bound to the HTML 'title' element of the page and appears in the browser title bar context['document_title'] = _( "{partner_short_name} {course_number} Certificate | {platform_name}" ).format(partner_short_name=context['organization_short_name'], course_number=context['course_number'], platform_name=platform_name) # Translators: This text fragment appears after the student's name (displayed in a large font) on the certificate # screen. The text describes the accomplishment represented by the certificate information displayed to the user context['accomplishment_copy_description_full'] = _( "successfully completed, received a passing grade, and was " "awarded this {platform_name} {certificate_type} " "Certificate of Completion in ").format( platform_name=platform_name, certificate_type=context.get("certificate_type")) certificate_type_description = get_certificate_description( user_certificate.mode, certificate_type, platform_name, course.location.course_key) if certificate_type_description: context['certificate_type_description'] = certificate_type_description # Translators: This text describes the purpose (and therefore, value) of a course certificate context['certificate_info_description'] = _( "{platform_name} acknowledges achievements through " "certificates, which are awarded for course activities " "that {platform_name} students complete.").format( platform_name=platform_name, )
def assertDateInMessage(self, date, message): # lint-amnesty, pylint: disable=missing-function-docstring # First, check that the formatted version is in there assert strftime_localized(date, 'SHORT_DATE') in message # But also that the machine-readable version is in there assert 'data-datetime="%s"' % date.isoformat() in message
def test_translated_formats(self, fmt_expected): (fmt, expected) = fmt_expected dtime = datetime(2013, 2, 14, 16, 41, 17) self.assertEqual(expected, strftime_localized(dtime, fmt))
def generate_course_expired_message(user, course): """ Generate the message for the user course expiration date if it exists. """ expiration_date = get_user_course_expiration_date(user, course) if not expiration_date: return user_timezone_locale = user_timezone_locale_prefs( crum.get_current_request()) user_timezone = user_timezone_locale['user_timezone'] now = timezone.now() if is_masquerading_as_specific_student( user, course.id) and 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 if (not upgrade_deadline) or (upgrade_deadline < now): upgrade_deadline = enrollment.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, user_timezone=user_timezone, formatted_date=expiration_date.isoformat(), formatted_date_localized=strftime_localized( expiration_date, EXPIRATION_DATE_FORMAT_STR)) if using_upgrade_messaging: formatted_upgrade_deadline = date_string.format( language=language, user_timezone=user_timezone, formatted_date=upgrade_deadline.isoformat(), formatted_date_localized=strftime_localized( upgrade_deadline, EXPIRATION_DATE_FORMAT_STR)) return HTML(full_message).format( a_open=HTML(u'<a id="FBE_banner" 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>'), )