Merge pull request #19728 from edx/unicode1
Revert "Revert "change banner date localization to use dateutilfactory""
This commit is contained in:
@@ -172,13 +172,13 @@ class LoginEnrollmentTestCase(TestCase):
|
||||
self.user = self.activate_user(self.email)
|
||||
self.login(self.email, self.password)
|
||||
|
||||
def assert_request_status_code(self, status_code, url, method="GET", **kwargs):
|
||||
def assert_request_status_code(self, status_code, url, method="GET", **kwargs): # pylint: disable=unicode-format-string
|
||||
make_request = getattr(self.client, method.lower())
|
||||
response = make_request(url, **kwargs)
|
||||
self.assertEqual(
|
||||
response.status_code, status_code,
|
||||
"{method} request to {url} returned status code {actual}, "
|
||||
"expected status code {expected}".format(
|
||||
u"{method} request to {url} returned status code {actual}, "
|
||||
u"expected status code {expected}".format(
|
||||
method=method, url=url,
|
||||
actual=response.status_code, expected=status_code
|
||||
)
|
||||
@@ -355,7 +355,7 @@ def _create_mock_json_request(user, data, method='POST'):
|
||||
return request
|
||||
|
||||
|
||||
def get_expiration_banner_text(user, course, language='en-us'):
|
||||
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.
|
||||
@@ -367,19 +367,21 @@ def get_expiration_banner_text(user, course, language='en-us'):
|
||||
if upgrade_deadline is None or now() < upgrade_deadline:
|
||||
upgrade_deadline = enrollment.course_upgrade_deadline
|
||||
|
||||
language_is_es = language and language.split('-')[0].lower() == 'es'
|
||||
if language_is_es:
|
||||
formatted_expiration_date = strftime_localized(expiration_date, '%-d de %b. de %Y').lower()
|
||||
else:
|
||||
formatted_expiration_date = strftime_localized(expiration_date, '%b. %-d, %Y')
|
||||
|
||||
date_string = u'<span class="localized-datetime" data-format="shortDate" \
|
||||
data-datetime="{formatted_date}" data-language="{language}">{formatted_date_localized}</span>'
|
||||
formatted_expiration_date = date_string.format(
|
||||
language=language,
|
||||
formatted_date=expiration_date.strftime(u'%b %-d, %Y'),
|
||||
formatted_date_localized=strftime_localized(expiration_date, u'%b %-d, %Y')
|
||||
)
|
||||
if upgrade_deadline:
|
||||
if language_is_es:
|
||||
formatted_upgrade_deadline = strftime_localized(upgrade_deadline, '%-d de %b. de %Y').lower()
|
||||
else:
|
||||
formatted_upgrade_deadline = strftime_localized(upgrade_deadline, '%b. %-d, %Y')
|
||||
formatted_upgrade_deadline = date_string.format(
|
||||
language=language,
|
||||
formatted_date=upgrade_deadline.strftime(u'%b %-d, %Y'),
|
||||
formatted_date_localized=strftime_localized(upgrade_deadline, u'%b %-d, %Y')
|
||||
)
|
||||
|
||||
bannerText = '<strong>Audit Access Expires {expiration_date}</strong><br>\
|
||||
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 href="{upgrade_link}">Upgrade now<span class="sr-only"> to retain access past\
|
||||
@@ -389,7 +391,7 @@ def get_expiration_banner_text(user, course, language='en-us'):
|
||||
upgrade_deadline=formatted_upgrade_deadline
|
||||
)
|
||||
else:
|
||||
bannerText = '<strong>Audit Access Expires {expiration_date}</strong><br>\
|
||||
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
|
||||
|
||||
@@ -32,22 +32,19 @@ class AuditExpiredError(AccessError):
|
||||
"""
|
||||
def __init__(self, user, course, expiration_date):
|
||||
error_code = "audit_expired"
|
||||
developer_message = "User {} had access to {} until {}".format(user, course, expiration_date)
|
||||
developer_message = u"User {} had access to {} until {}".format(user, course, expiration_date)
|
||||
language = get_language()
|
||||
if language and language.split('-')[0].lower() == 'es':
|
||||
expiration_date = strftime_localized(expiration_date, '%-d de %b. de %Y').lower()
|
||||
else:
|
||||
expiration_date = strftime_localized(expiration_date, '%b. %-d, %Y')
|
||||
user_message = _("Access expired on {expiration_date}").format(expiration_date=expiration_date)
|
||||
expiration_date = strftime_localized(expiration_date, u'%b. %-d, %Y')
|
||||
user_message = _(u"Access expired on {expiration_date}").format(expiration_date=expiration_date)
|
||||
try:
|
||||
course_name = CourseOverview.get_from_id(course.id).display_name_with_default
|
||||
additional_context_user_message = _("Access to {course_name} expired on {expiration_date}").format(
|
||||
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 = _("Access to the course you were looking"
|
||||
" for expired on {expiration_date}").format(
|
||||
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,
|
||||
@@ -63,7 +60,6 @@ def get_user_course_expiration_date(user, course):
|
||||
- Course access duration is bounded by the min and max duration.
|
||||
- If course fields are missing, default course access duration to MIN_DURATION.
|
||||
"""
|
||||
|
||||
access_duration = MIN_DURATION
|
||||
|
||||
if not CourseMode.verified_mode_for_course(course.id, include_expired=True):
|
||||
@@ -112,6 +108,12 @@ def check_course_expired(user, course):
|
||||
return ACCESS_GRANTED
|
||||
|
||||
|
||||
def get_date_string():
|
||||
# Creating this method to allow unit testing an issue where this string was missing the unicode prefix
|
||||
return u'<span class="localized-datetime" data-format="shortDate" \
|
||||
data-datetime="{formatted_date}" data-language="{language}">{formatted_date_localized}</span>'
|
||||
|
||||
|
||||
def generate_course_expired_message(user, course):
|
||||
"""
|
||||
Generate the message for the user course expiration date if it exists.
|
||||
@@ -125,9 +127,9 @@ def generate_course_expired_message(user, course):
|
||||
|
||||
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. '
|
||||
'Their access expired on {expiration_date}.')
|
||||
u'Their access expired on {expiration_date}.')
|
||||
return HTML(upgrade_message).format(
|
||||
expiration_date=strftime_localized(expiration_date, '%b. %-d, %Y')
|
||||
expiration_date=strftime_localized(expiration_date, u'%b. %-d, %Y')
|
||||
)
|
||||
else:
|
||||
enrollment = CourseEnrollment.get_enrollment(user, course.id)
|
||||
@@ -140,12 +142,12 @@ def generate_course_expired_message(user, course):
|
||||
if (not upgrade_deadline) or (upgrade_deadline < now):
|
||||
upgrade_deadline = course_upgrade_deadline
|
||||
|
||||
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}')
|
||||
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
|
||||
@@ -154,36 +156,37 @@ def generate_course_expired_message(user, course):
|
||||
using_upgrade_messaging = False
|
||||
|
||||
language = get_language()
|
||||
language_is_es = language and language.split('-')[0].lower() == 'es'
|
||||
if language_is_es:
|
||||
formatted_expiration_date = strftime_localized(expiration_date, '%-d de %b. de %Y').lower()
|
||||
else:
|
||||
formatted_expiration_date = strftime_localized(expiration_date, '%b. %-d, %Y')
|
||||
|
||||
date_string = get_date_string()
|
||||
formatted_expiration_date = date_string.format(
|
||||
language=language,
|
||||
formatted_date=expiration_date.strftime(u'%b %-d, %Y'),
|
||||
formatted_date_localized=strftime_localized(expiration_date, u'%b %-d, %Y')
|
||||
)
|
||||
if using_upgrade_messaging:
|
||||
if language_is_es:
|
||||
formatted_upgrade_deadline = strftime_localized(upgrade_deadline, '%-d de %b. de %Y').lower()
|
||||
else:
|
||||
formatted_upgrade_deadline = strftime_localized(upgrade_deadline, '%b. %-d, %Y')
|
||||
formatted_upgrade_deadline = date_string.format(
|
||||
language=language,
|
||||
formatted_date=upgrade_deadline.strftime(u'%b %-d, %Y'),
|
||||
formatted_date_localized=strftime_localized(upgrade_deadline, u'%b %-d, %Y')
|
||||
)
|
||||
|
||||
return HTML(full_message).format(
|
||||
a_open=HTML('<a href="{upgrade_link}">').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=formatted_expiration_date,
|
||||
expiration_date=HTML(formatted_expiration_date),
|
||||
strong_open=HTML('<strong>'),
|
||||
strong_close=HTML('</strong>'),
|
||||
line_break=HTML('<br>'),
|
||||
upgrade_deadline=formatted_upgrade_deadline
|
||||
upgrade_deadline=HTML(formatted_upgrade_deadline)
|
||||
)
|
||||
|
||||
else:
|
||||
return HTML(full_message).format(
|
||||
span_close=HTML('</span>'),
|
||||
expiration_date=formatted_expiration_date,
|
||||
expiration_date=HTML(formatted_expiration_date),
|
||||
strong_open=HTML('<strong>'),
|
||||
strong_close=HTML('</strong>'),
|
||||
line_break=HTML('<br>'),
|
||||
|
||||
@@ -5,10 +5,8 @@ import itertools
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from course_modes.tests.factories import CourseModeFactory
|
||||
from django.test import RequestFactory
|
||||
from django.utils import timezone
|
||||
from courseware.models import DynamicUpgradeDeadlineConfiguration
|
||||
from mock import patch
|
||||
from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
|
||||
from openedx.features.course_duration_limits.access import (
|
||||
@@ -33,12 +31,11 @@ class TestAccess(CacheIsolationTestCase):
|
||||
|
||||
@ddt.data(
|
||||
*itertools.product(
|
||||
['en-us', 'es-419'],
|
||||
itertools.product([None, -2, -1, 1, 2], repeat=2),
|
||||
)
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_generate_course_expired_message(self, language, offsets):
|
||||
def test_generate_course_expired_message(self, offsets):
|
||||
now = timezone.now()
|
||||
schedule_offset, course_offset = offsets
|
||||
|
||||
@@ -53,45 +50,40 @@ class TestAccess(CacheIsolationTestCase):
|
||||
course_upgrade_deadline = None
|
||||
|
||||
def format_date(date):
|
||||
if language.startswith('es-'):
|
||||
return strftime_localized(date, '%-d de %b. de %Y').lower()
|
||||
else:
|
||||
return strftime_localized(date, '%b. %-d, %Y')
|
||||
return strftime_localized(date, u'%b %-d, %Y')
|
||||
|
||||
patch_lang = patch('openedx.features.course_duration_limits.access.get_language', return_value=language)
|
||||
with patch_lang:
|
||||
enrollment = CourseEnrollmentFactory.create(
|
||||
course__start=datetime(2018, 1, 1, tzinfo=UTC),
|
||||
course__self_paced=True,
|
||||
)
|
||||
CourseModeFactory.create(
|
||||
course_id=enrollment.course.id,
|
||||
mode_slug=CourseMode.VERIFIED,
|
||||
expiration_datetime=course_upgrade_deadline,
|
||||
)
|
||||
CourseModeFactory.create(
|
||||
course_id=enrollment.course.id,
|
||||
mode_slug=CourseMode.AUDIT,
|
||||
)
|
||||
ScheduleFactory.create(
|
||||
enrollment=enrollment,
|
||||
upgrade_deadline=schedule_upgrade_deadline,
|
||||
)
|
||||
enrollment = CourseEnrollmentFactory.create(
|
||||
course__start=datetime(2018, 1, 1, tzinfo=UTC),
|
||||
course__self_paced=True,
|
||||
)
|
||||
CourseModeFactory.create(
|
||||
course_id=enrollment.course.id,
|
||||
mode_slug=CourseMode.VERIFIED,
|
||||
expiration_datetime=course_upgrade_deadline,
|
||||
)
|
||||
CourseModeFactory.create(
|
||||
course_id=enrollment.course.id,
|
||||
mode_slug=CourseMode.AUDIT,
|
||||
)
|
||||
ScheduleFactory.create(
|
||||
enrollment=enrollment,
|
||||
upgrade_deadline=schedule_upgrade_deadline,
|
||||
)
|
||||
|
||||
duration_limit_upgrade_deadline = get_user_course_expiration_date(enrollment.user, enrollment.course)
|
||||
self.assertIsNotNone(duration_limit_upgrade_deadline)
|
||||
duration_limit_upgrade_deadline = get_user_course_expiration_date(enrollment.user, enrollment.course)
|
||||
self.assertIsNotNone(duration_limit_upgrade_deadline)
|
||||
|
||||
message = generate_course_expired_message(enrollment.user, enrollment.course)
|
||||
message = generate_course_expired_message(enrollment.user, enrollment.course)
|
||||
|
||||
self.assertIn(format_date(duration_limit_upgrade_deadline), message)
|
||||
self.assertIn(format_date(duration_limit_upgrade_deadline), message)
|
||||
|
||||
soft_upgradeable = schedule_upgrade_deadline is not None and now < schedule_upgrade_deadline
|
||||
upgradeable = course_upgrade_deadline is None or now < course_upgrade_deadline
|
||||
has_upgrade_deadline = course_upgrade_deadline is not None
|
||||
soft_upgradeable = schedule_upgrade_deadline is not None and now < schedule_upgrade_deadline
|
||||
upgradeable = course_upgrade_deadline is None or now < course_upgrade_deadline
|
||||
has_upgrade_deadline = course_upgrade_deadline is not None
|
||||
|
||||
if upgradeable and soft_upgradeable:
|
||||
self.assertIn(format_date(schedule_upgrade_deadline), message)
|
||||
elif upgradeable and has_upgrade_deadline:
|
||||
self.assertIn(format_date(course_upgrade_deadline), message)
|
||||
else:
|
||||
self.assertNotIn("Upgrade by", message)
|
||||
if upgradeable and soft_upgradeable:
|
||||
self.assertIn(format_date(schedule_upgrade_deadline), message)
|
||||
elif upgradeable and has_upgrade_deadline:
|
||||
self.assertIn(format_date(course_upgrade_deadline), message)
|
||||
else:
|
||||
self.assertNotIn("Upgrade by", message)
|
||||
|
||||
@@ -38,6 +38,7 @@ from lms.djangoapps.courseware.tests.factories import (
|
||||
GlobalStaffFactory,
|
||||
)
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
from openedx.core.djangoapps.dark_lang.models import DarkLangConfig
|
||||
from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
|
||||
from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES, override_waffle_flag
|
||||
from openedx.features.course_duration_limits.config import EXPERIMENT_ID
|
||||
@@ -525,10 +526,10 @@ class TestCourseHomePageAccess(CourseHomePageTestCase):
|
||||
|
||||
response = self.client.get(url)
|
||||
|
||||
expiration_date = strftime_localized(course.start + timedelta(weeks=4), '%b. %-d, %Y')
|
||||
expiration_date = strftime_localized(course.start + timedelta(weeks=4), u'%b. %-d, %Y')
|
||||
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(
|
||||
expected_params['access_response_error'] = u'Access to {run} expired on {expiration_date}'.format(
|
||||
run=course_name,
|
||||
expiration_date=expiration_date
|
||||
)
|
||||
@@ -722,6 +723,46 @@ class TestCourseHomePageAccess(CourseHomePageTestCase):
|
||||
bannerText = get_expiration_banner_text(self.staff_user, self.course)
|
||||
self.assertNotContains(response, bannerText, html=True)
|
||||
|
||||
@mock.patch("util.date_utils.strftime_localized")
|
||||
@mock.patch("openedx.features.course_duration_limits.access.get_date_string")
|
||||
def test_course_expiration_banner_with_unicode(self, mock_strftime_localized, mock_get_date_string):
|
||||
"""
|
||||
Ensure that switching to other languages that have unicode in their
|
||||
date representations will not cause the course home page to 404.
|
||||
"""
|
||||
fake_unicode_start_time = u"üñîçø∂é_ßtå®t_tîµé"
|
||||
mock_strftime_localized.return_value = fake_unicode_start_time
|
||||
date_string = u'<span class="localized-datetime" data-format="shortDate" \
|
||||
data-datetime="{formatted_date}" data-language="{language}">{formatted_date_localized}</span>'
|
||||
mock_get_date_string.return_value = date_string
|
||||
|
||||
config = CourseDurationLimitConfig(
|
||||
course=CourseOverview.get_from_id(self.course.id),
|
||||
enabled=True,
|
||||
enabled_as_of=datetime(2018, 1, 1)
|
||||
)
|
||||
config.save()
|
||||
url = course_home_url(self.course)
|
||||
user = self.create_user_for_course(self.course, CourseUserType.UNENROLLED)
|
||||
CourseEnrollment.enroll(user, self.course.id)
|
||||
|
||||
language = 'zh-cn'
|
||||
DarkLangConfig(
|
||||
released_languages=language,
|
||||
changed_by=user,
|
||||
enabled=True
|
||||
).save()
|
||||
|
||||
response = self.client.get(url, HTTP_ACCEPT_LANGUAGE=language)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response['Content-Language'], language)
|
||||
|
||||
# Check that if the string is incorrectly not marked as unicode we still get the error
|
||||
with mock.patch("openedx.features.course_duration_limits.access.get_date_string",
|
||||
return_value=str(date_string)):
|
||||
response = self.client.get(url, HTTP_ACCEPT_LANGUAGE=language)
|
||||
self.assertEqual(response.status_code, 500)
|
||||
|
||||
@override_waffle_flag(COURSE_PRE_START_ACCESS_FLAG, active=True)
|
||||
@override_waffle_flag(ENABLE_COURSE_GOALS, active=True)
|
||||
def test_course_goals(self):
|
||||
@@ -846,7 +887,10 @@ class CourseHomeFragmentViewTests(ModuleStoreTestCase):
|
||||
url = EcommerceService().get_checkout_page_url(self.verified_mode.sku)
|
||||
self.assertIn('<a class="btn-brand btn-upgrade"', response.content)
|
||||
self.assertIn(url, response.content)
|
||||
self.assertIn('Upgrade (${price})'.format(price=self.verified_mode.min_price), response.content)
|
||||
self.assertIn(
|
||||
u'Upgrade (${price})'.format(price=self.verified_mode.min_price),
|
||||
response.content.decode(response.charset)
|
||||
)
|
||||
|
||||
def test_no_upgrade_message_if_logged_out(self):
|
||||
self.client.logout()
|
||||
|
||||
Reference in New Issue
Block a user