Refactor info.html page datetime rendering
This commit is contained in:
@@ -294,18 +294,7 @@ def get_course_info_section(request, user, course, section_key):
|
||||
return html
|
||||
|
||||
|
||||
def get_course_date_summary(course, user):
|
||||
"""
|
||||
Return the snippet of HTML to be included on the course info page
|
||||
in the 'Date Summary' section.
|
||||
"""
|
||||
blocks = _get_course_date_summary_blocks(course, user)
|
||||
return '\n'.join(
|
||||
b.render() for b in blocks
|
||||
)
|
||||
|
||||
|
||||
def _get_course_date_summary_blocks(course, user):
|
||||
def get_course_date_blocks(course, user):
|
||||
"""
|
||||
Return the list of blocks to display on the course info page,
|
||||
sorted by date.
|
||||
|
||||
@@ -11,14 +11,12 @@ from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy
|
||||
from django.utils.translation import to_locale, get_language
|
||||
from edxmako.shortcuts import render_to_string
|
||||
from lazy import lazy
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from lms.djangoapps.commerce.utils import EcommerceService
|
||||
from lms.djangoapps.verify_student.models import VerificationDeadline, SoftwareSecurePhotoVerification
|
||||
from student.models import CourseEnrollment
|
||||
from openedx.core.lib.time_zone_utils import get_time_zone_abbr
|
||||
|
||||
|
||||
class DateSummary(object):
|
||||
@@ -78,24 +76,8 @@ class DateSummary(object):
|
||||
self.course = course
|
||||
self.user = user
|
||||
|
||||
def get_context(self):
|
||||
"""Return the template context used to render this summary block."""
|
||||
return {
|
||||
'title': self.title,
|
||||
'date': self._format_date(),
|
||||
'description': self.description,
|
||||
'css_class': self.css_class,
|
||||
'link': self.link,
|
||||
'link_text': self.link_text,
|
||||
}
|
||||
|
||||
def render(self):
|
||||
"""
|
||||
Return an HTML representation of this summary block.
|
||||
"""
|
||||
return render_to_string('courseware/date_summary.html', self.get_context())
|
||||
|
||||
def _format_date(self):
|
||||
@property
|
||||
def relative_datestring(self):
|
||||
"""
|
||||
Return this block's date in a human-readable format. If the date
|
||||
is None, returns the empty string.
|
||||
@@ -121,7 +103,7 @@ class DateSummary(object):
|
||||
date_format = _(u"{relative} ago - {absolute}") if date_has_passed else _(u"in {relative} - {absolute}")
|
||||
return date_format.format(
|
||||
relative=relative_date,
|
||||
absolute=self.date.astimezone(self.time_zone).strftime(self.date_format.encode('utf-8')).decode('utf-8'),
|
||||
absolute='{date}',
|
||||
)
|
||||
|
||||
@property
|
||||
@@ -151,10 +133,6 @@ class TodaysDate(DateSummary):
|
||||
css_class = 'todays-date'
|
||||
is_enabled = True
|
||||
|
||||
@property
|
||||
def date_format(self):
|
||||
return u'%b %d, %Y (%H:%M {tz_abbr})'.format(tz_abbr=get_time_zone_abbr(self.time_zone))
|
||||
|
||||
# The date is shown in the title, no need to display it again.
|
||||
def get_context(self):
|
||||
context = super(TodaysDate, self).get_context()
|
||||
@@ -167,9 +145,7 @@ class TodaysDate(DateSummary):
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
return _(u'Today is {date}').format(
|
||||
date=self.date.astimezone(self.time_zone).strftime(self.date_format.encode('utf-8')).decode('utf-8')
|
||||
)
|
||||
return 'current_datetime'
|
||||
|
||||
|
||||
class CourseStartDate(DateSummary):
|
||||
|
||||
@@ -11,7 +11,7 @@ from pytz import utc
|
||||
from commerce.models import CommerceConfiguration
|
||||
from course_modes.tests.factories import CourseModeFactory
|
||||
from course_modes.models import CourseMode
|
||||
from courseware.courses import _get_course_date_summary_blocks
|
||||
from courseware.courses import get_course_date_blocks
|
||||
from courseware.date_summary import (
|
||||
CourseEndDate,
|
||||
CourseStartDate,
|
||||
@@ -55,7 +55,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
|
||||
self.course = CourseFactory.create( # pylint: disable=attribute-defined-outside-init
|
||||
start=now + timedelta(days=days_till_start)
|
||||
)
|
||||
self.user = UserFactory.create() # pylint: disable=attribute-defined-outside-init
|
||||
self.user = UserFactory.create(username='mrrobot', password='test') # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
if days_till_end is not None:
|
||||
self.course.end = now + timedelta(days=days_till_end)
|
||||
@@ -92,10 +92,9 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
|
||||
self.assertNotIn('date-summary', response.content)
|
||||
|
||||
# Tests for which blocks are enabled
|
||||
|
||||
def assert_block_types(self, expected_blocks):
|
||||
"""Assert that the enabled block types for this course are as expected."""
|
||||
blocks = _get_course_date_summary_blocks(self.course, self.user)
|
||||
blocks = get_course_date_blocks(self.course, self.user)
|
||||
self.assertEqual(len(blocks), len(expected_blocks))
|
||||
self.assertEqual(set(type(b) for b in blocks), set(expected_blocks))
|
||||
|
||||
@@ -163,64 +162,58 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
|
||||
self.setup_course_and_user(**course_options)
|
||||
self.assert_block_types(expected_blocks)
|
||||
|
||||
# Specific block type tests
|
||||
|
||||
## Base DateSummary -- test empty defaults
|
||||
|
||||
def test_date_summary(self):
|
||||
self.setup_course_and_user()
|
||||
block = DateSummary(self.course, self.user)
|
||||
html = '<div class="date-summary-container"><div class="date-summary date-summary-"></div></div>'
|
||||
self.assertHTMLEqual(block.render(), html)
|
||||
self.assertFalse(block.is_enabled)
|
||||
|
||||
## TodaysDate
|
||||
|
||||
def _today_date_helper(self, expected_display_date):
|
||||
@freeze_time('2015-01-02')
|
||||
def test_todays_date_block(self):
|
||||
"""
|
||||
Helper function to test that today's date block renders correctly
|
||||
and displays the correct time, accounting for daylight savings
|
||||
"""
|
||||
self.setup_course_and_user()
|
||||
set_user_preference(self.user, "time_zone", "America/Los_Angeles")
|
||||
block = TodaysDate(self.course, self.user)
|
||||
self.assertTrue(block.is_enabled)
|
||||
self.assertEqual(block.date, datetime.now(utc))
|
||||
self.assertEqual(block.title, 'Today is {date}'.format(date=expected_display_date))
|
||||
self.assertNotIn('date-summary-date', block.render())
|
||||
|
||||
@freeze_time('2015-11-01 08:59:00')
|
||||
def test_todays_date_time_zone_daylight(self):
|
||||
"""
|
||||
Test today's date block displays correctly during
|
||||
daylight savings hours
|
||||
"""
|
||||
self._today_date_helper('Nov 01, 2015 (01:59 PDT)')
|
||||
|
||||
@freeze_time('2015-11-01 09:00:00')
|
||||
def test_todays_date_time_zone_normal(self):
|
||||
"""
|
||||
Test today's date block displays correctly during
|
||||
normal daylight hours
|
||||
"""
|
||||
self._today_date_helper('Nov 01, 2015 (01:00 PST)')
|
||||
self.assertEqual(block.title, 'current_datetime')
|
||||
|
||||
@freeze_time('2015-01-02')
|
||||
def test_todays_date_render(self):
|
||||
def test_todays_date_no_timezone(self):
|
||||
self.setup_course_and_user()
|
||||
block = TodaysDate(self.course, self.user)
|
||||
self.assertIn('Jan 02, 2015', block.render())
|
||||
self.client.login(username='mrrobot', password='test')
|
||||
|
||||
html_elements = [
|
||||
'<h1 class="handouts-header">Important Course Dates</h1>',
|
||||
'<div class="date-summary-container">',
|
||||
'<div class="date-summary date-summary-todays-date">',
|
||||
'<h3 class="heading localized-datetime"',
|
||||
'data-datetime="2015-01-02 00:00:00+00:00"',
|
||||
'data-string="Today is {date}"',
|
||||
'data-timezone="None"'
|
||||
]
|
||||
url = reverse('info', args=(self.course.id, ))
|
||||
response = self.client.get(url)
|
||||
for html in html_elements:
|
||||
self.assertContains(response, html)
|
||||
|
||||
@freeze_time('2015-01-02')
|
||||
def test_todays_date_render_time_zone(self):
|
||||
def test_todays_date_timezone(self):
|
||||
self.setup_course_and_user()
|
||||
self.client.login(username='mrrobot', password='test')
|
||||
set_user_preference(self.user, "time_zone", "America/Los_Angeles")
|
||||
block = TodaysDate(self.course, self.user)
|
||||
# Today is 'Jan 01, 2015' because of time zone offset
|
||||
self.assertIn('Jan 01, 2015', block.render())
|
||||
url = reverse('info', args=(self.course.id,))
|
||||
response = self.client.get(url)
|
||||
|
||||
## CourseStartDate
|
||||
html_elements = [
|
||||
'<h1 class="handouts-header">Important Course Dates</h1>',
|
||||
'<div class="date-summary-container">',
|
||||
'<div class="date-summary date-summary-todays-date">',
|
||||
'<h3 class="heading localized-datetime"',
|
||||
'data-datetime="2015-01-02 00:00:00+00:00"',
|
||||
'data-string="Today is {date}"',
|
||||
'data-timezone="America/Los_Angeles"'
|
||||
]
|
||||
for html in html_elements:
|
||||
self.assertContains(response, html)
|
||||
|
||||
## Tests Course Start Date
|
||||
def test_course_start_date(self):
|
||||
self.setup_course_and_user()
|
||||
block = CourseStartDate(self.course, self.user)
|
||||
@@ -229,19 +222,32 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
|
||||
@freeze_time('2015-01-02')
|
||||
def test_start_date_render(self):
|
||||
self.setup_course_and_user()
|
||||
block = CourseStartDate(self.course, self.user)
|
||||
self.assertIn('in 1 day - Jan 03, 2015', block.render())
|
||||
self.client.login(username='mrrobot', password='test')
|
||||
url = reverse('info', args=(self.course.id,))
|
||||
response = self.client.get(url)
|
||||
html_elements = [
|
||||
'data-string="in 1 day - {date}"',
|
||||
'data-datetime="2015-01-03 00:00:00+00:00"'
|
||||
]
|
||||
for html in html_elements:
|
||||
self.assertContains(response, html)
|
||||
|
||||
@freeze_time('2015-01-02')
|
||||
def test_start_date_render_time_zone(self):
|
||||
self.setup_course_and_user()
|
||||
self.client.login(username='mrrobot', password='test')
|
||||
set_user_preference(self.user, "time_zone", "America/Los_Angeles")
|
||||
block = CourseStartDate(self.course, self.user)
|
||||
# Jan 02 is in 1 day because of time zone offset
|
||||
self.assertIn('in 1 day - Jan 02, 2015', block.render())
|
||||
|
||||
## CourseEndDate
|
||||
url = reverse('info', args=(self.course.id,))
|
||||
response = self.client.get(url)
|
||||
html_elements = [
|
||||
'data-string="in 1 day - {date}"',
|
||||
'data-datetime="2015-01-03 00:00:00+00:00"',
|
||||
'data-timezone="America/Los_Angeles"'
|
||||
]
|
||||
for html in html_elements:
|
||||
self.assertContains(response, html)
|
||||
|
||||
## Tests Course End Date Block
|
||||
def test_course_end_date_for_certificate_eligible_mode(self):
|
||||
self.setup_course_and_user(days_till_start=-1)
|
||||
block = CourseEndDate(self.course, self.user)
|
||||
@@ -257,6 +263,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
|
||||
block.description,
|
||||
'After this date, course content will be archived.'
|
||||
)
|
||||
self.assertEqual(block.title, 'Course End')
|
||||
|
||||
def test_course_end_date_after_course(self):
|
||||
self.setup_course_and_user(days_till_start=-2, days_till_end=-1)
|
||||
@@ -265,9 +272,9 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
|
||||
block.description,
|
||||
'This course is archived, which means you can review course content but it is no longer active.'
|
||||
)
|
||||
self.assertEqual(block.title, 'Course End')
|
||||
|
||||
## VerifiedUpgradeDeadlineDate
|
||||
|
||||
## Tests Verified Upgrade Deadline Date Block
|
||||
@freeze_time('2015-01-02')
|
||||
def test_verified_upgrade_deadline_date(self):
|
||||
self.setup_course_and_user(days_till_upgrade_deadline=1)
|
||||
@@ -293,7 +300,6 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
|
||||
self.assertEqual(block.link, '{}?sku={}'.format(checkout_page, sku))
|
||||
|
||||
## VerificationDeadlineDate
|
||||
|
||||
def test_no_verification_deadline(self):
|
||||
self.setup_course_and_user(days_till_start=-1, days_till_verification_deadline=None)
|
||||
block = VerificationDeadlineDate(self.course, self.user)
|
||||
@@ -352,8 +358,8 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
|
||||
|
||||
@freeze_time('2015-01-02')
|
||||
@ddt.data(
|
||||
(-1, '1 day ago - Jan 01, 2015'),
|
||||
(1, 'in 1 day - Jan 03, 2015')
|
||||
(-1, '1 day ago - {date}'),
|
||||
(1, 'in 1 day - {date}')
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_render_date_string_past(self, delta, expected_date_string):
|
||||
@@ -363,21 +369,4 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
|
||||
days_till_verification_deadline=delta,
|
||||
)
|
||||
block = VerificationDeadlineDate(self.course, self.user)
|
||||
self.assertEqual(block.get_context()['date'], expected_date_string)
|
||||
|
||||
@freeze_time('2015-01-02')
|
||||
@ddt.data(
|
||||
# dates reflected from Jan 01, 2015 because of time zone offset
|
||||
(-1, '1 day ago - Dec 31, 2014'),
|
||||
(1, 'in 1 day - Jan 02, 2015')
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_render_date_string_time_zone(self, delta, expected_date_string):
|
||||
self.setup_course_and_user(
|
||||
days_till_start=-10,
|
||||
verification_status='denied',
|
||||
days_till_verification_deadline=delta,
|
||||
)
|
||||
set_user_preference(self.user, "time_zone", "America/Los_Angeles")
|
||||
block = VerificationDeadlineDate(self.course, self.user)
|
||||
self.assertEqual(block.get_context()['date'], expected_date_string)
|
||||
self.assertEqual(block.relative_datestring, expected_date_string)
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<div class="date-summary-container">
|
||||
<div class="date-summary date-summary-${css_class}">
|
||||
% if title:
|
||||
<h3 class="heading">${title}</h3>
|
||||
% endif
|
||||
% if date:
|
||||
<h4 class="date">${date}</h4>
|
||||
% endif
|
||||
% if description:
|
||||
<p class="description">${description}</p>
|
||||
% endif
|
||||
% if link and link_text:
|
||||
<span class="date-summary-link">
|
||||
<a href="${link}">${link_text}</a>
|
||||
</span>
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
@@ -3,10 +3,12 @@
|
||||
<%def name="online_help_token()"><% return "courseinfo" %></%def>
|
||||
<%namespace name='static' file='../static_content.html'/>
|
||||
<%!
|
||||
from datetime import datetime
|
||||
from pytz import timezone, utc
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from courseware.courses import get_course_info_section, get_course_date_summary
|
||||
|
||||
from courseware.courses import get_course_info_section, get_course_date_blocks
|
||||
from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
|
||||
from openedx.core.djangolib.markup import HTML, Text
|
||||
%>
|
||||
@@ -89,15 +91,40 @@ from openedx.core.djangolib.markup import HTML, Text
|
||||
% endif
|
||||
</section>
|
||||
<section aria-label="${_('Handout Navigation')}" class="handouts">
|
||||
% if SelfPacedConfiguration.current().enable_course_home_improvements:
|
||||
<h1 class="handouts-header">${_("Important Course Dates")}</h1>
|
||||
${HTML(get_course_date_summary(course, user))}
|
||||
% endif
|
||||
|
||||
<h1 class="handouts-header">${_(course.info_sidebar_name)}</h1>
|
||||
${HTML(get_course_info_section(request, masquerade_user, course, 'handouts'))}
|
||||
% if SelfPacedConfiguration.current().enable_course_home_improvements:
|
||||
<h1 class="handouts-header">${_("Important Course Dates")}</h1>
|
||||
## Should be organized by date, last date appearing at the bottom
|
||||
|
||||
% for course_date in get_course_date_blocks(course, user):
|
||||
<div class="date-summary-container">
|
||||
<div class="date-summary date-summary-${course_date.css_class}">
|
||||
% if course_date.title:
|
||||
% if course_date.title == 'current_datetime':
|
||||
<h3 class="heading localized-datetime" data-datetime="${course_date.date}" data-string="${_(u'Today is {date}')}" data-timezone="${user_timezone}" data-language="${user_language}"></h3>
|
||||
% else:
|
||||
<h3 class="heading">${course_date.title}</h3>
|
||||
% endif
|
||||
% endif
|
||||
% if course_date.date and course_date.title != 'current_datetime':
|
||||
<h4 class="date localized-datetime" data-format="shortDate" data-datetime="${course_date.date}" data-timezone="${user_timezone}" data-language="${user_language}" data-string="${_(course_date.relative_datestring)}"></h4>
|
||||
% endif
|
||||
% if course_date.description:
|
||||
<p class="description">${course_date.description}</p>
|
||||
% endif
|
||||
% if course_date.link and course_date.link_text:
|
||||
<span class="date-summary-link">
|
||||
<a href="${course_date.link}">${course_date.link_text}</a>
|
||||
</span>
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
% endfor
|
||||
% endif
|
||||
<h1 class="handouts-header">${_(course.info_sidebar_name)}</h1>
|
||||
${HTML(get_course_info_section(request, masquerade_user, course, 'handouts'))}
|
||||
</section>
|
||||
% else:
|
||||
% else:
|
||||
<section class="updates">
|
||||
<h1 class="handouts-header">${_("Course Updates and News")}</h1>
|
||||
${HTML(get_course_info_section(request, masquerade_user, course, 'guest_updates'))}
|
||||
@@ -106,7 +133,12 @@ from openedx.core.djangolib.markup import HTML, Text
|
||||
<h1 class="handouts-header">${_("Course Handouts")}</h1>
|
||||
${HTML(get_course_info_section(request, masquerade_user, course, 'guest_handouts'))}
|
||||
</section>
|
||||
% endif
|
||||
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<%static:require_module_async module_name="js/dateutil_factory" class_name="DateUtilFactory">
|
||||
DateUtilFactory.transform(iterationKey=".localized-datetime");
|
||||
</%static:require_module_async>
|
||||
|
||||
Reference in New Issue
Block a user