Commit 707371e1 by Gregory Martin

Refactor info.html page datetime rendering

parent 14c01e3a
...@@ -294,18 +294,7 @@ def get_course_info_section(request, user, course, section_key): ...@@ -294,18 +294,7 @@ def get_course_info_section(request, user, course, section_key):
return html return html
def get_course_date_summary(course, user): def get_course_date_blocks(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):
""" """
Return the list of blocks to display on the course info page, Return the list of blocks to display on the course info page,
sorted by date. sorted by date.
......
...@@ -11,14 +11,12 @@ from django.core.urlresolvers import reverse ...@@ -11,14 +11,12 @@ from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy from django.utils.translation import ugettext_lazy
from django.utils.translation import to_locale, get_language from django.utils.translation import to_locale, get_language
from edxmako.shortcuts import render_to_string
from lazy import lazy from lazy import lazy
from course_modes.models import CourseMode from course_modes.models import CourseMode
from lms.djangoapps.commerce.utils import EcommerceService from lms.djangoapps.commerce.utils import EcommerceService
from lms.djangoapps.verify_student.models import VerificationDeadline, SoftwareSecurePhotoVerification from lms.djangoapps.verify_student.models import VerificationDeadline, SoftwareSecurePhotoVerification
from student.models import CourseEnrollment from student.models import CourseEnrollment
from openedx.core.lib.time_zone_utils import get_time_zone_abbr
class DateSummary(object): class DateSummary(object):
...@@ -78,24 +76,8 @@ class DateSummary(object): ...@@ -78,24 +76,8 @@ class DateSummary(object):
self.course = course self.course = course
self.user = user self.user = user
def get_context(self): @property
"""Return the template context used to render this summary block.""" def relative_datestring(self):
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):
""" """
Return this block's date in a human-readable format. If the date Return this block's date in a human-readable format. If the date
is None, returns the empty string. is None, returns the empty string.
...@@ -121,7 +103,7 @@ class DateSummary(object): ...@@ -121,7 +103,7 @@ class DateSummary(object):
date_format = _(u"{relative} ago - {absolute}") if date_has_passed else _(u"in {relative} - {absolute}") date_format = _(u"{relative} ago - {absolute}") if date_has_passed else _(u"in {relative} - {absolute}")
return date_format.format( return date_format.format(
relative=relative_date, relative=relative_date,
absolute=self.date.astimezone(self.time_zone).strftime(self.date_format.encode('utf-8')).decode('utf-8'), absolute='{date}',
) )
@property @property
...@@ -151,10 +133,6 @@ class TodaysDate(DateSummary): ...@@ -151,10 +133,6 @@ class TodaysDate(DateSummary):
css_class = 'todays-date' css_class = 'todays-date'
is_enabled = True 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. # The date is shown in the title, no need to display it again.
def get_context(self): def get_context(self):
context = super(TodaysDate, self).get_context() context = super(TodaysDate, self).get_context()
...@@ -167,9 +145,7 @@ class TodaysDate(DateSummary): ...@@ -167,9 +145,7 @@ class TodaysDate(DateSummary):
@property @property
def title(self): def title(self):
return _(u'Today is {date}').format( return 'current_datetime'
date=self.date.astimezone(self.time_zone).strftime(self.date_format.encode('utf-8')).decode('utf-8')
)
class CourseStartDate(DateSummary): class CourseStartDate(DateSummary):
......
...@@ -11,7 +11,7 @@ from pytz import utc ...@@ -11,7 +11,7 @@ from pytz import utc
from commerce.models import CommerceConfiguration from commerce.models import CommerceConfiguration
from course_modes.tests.factories import CourseModeFactory from course_modes.tests.factories import CourseModeFactory
from course_modes.models import CourseMode 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 ( from courseware.date_summary import (
CourseEndDate, CourseEndDate,
CourseStartDate, CourseStartDate,
...@@ -55,7 +55,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase): ...@@ -55,7 +55,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
self.course = CourseFactory.create( # pylint: disable=attribute-defined-outside-init self.course = CourseFactory.create( # pylint: disable=attribute-defined-outside-init
start=now + timedelta(days=days_till_start) 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: if days_till_end is not None:
self.course.end = now + timedelta(days=days_till_end) self.course.end = now + timedelta(days=days_till_end)
...@@ -92,10 +92,9 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase): ...@@ -92,10 +92,9 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
self.assertNotIn('date-summary', response.content) self.assertNotIn('date-summary', response.content)
# Tests for which blocks are enabled # Tests for which blocks are enabled
def assert_block_types(self, expected_blocks): def assert_block_types(self, expected_blocks):
"""Assert that the enabled block types for this course are as expected.""" """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(len(blocks), len(expected_blocks))
self.assertEqual(set(type(b) for b in blocks), set(expected_blocks)) self.assertEqual(set(type(b) for b in blocks), set(expected_blocks))
...@@ -163,64 +162,58 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase): ...@@ -163,64 +162,58 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
self.setup_course_and_user(**course_options) self.setup_course_and_user(**course_options)
self.assert_block_types(expected_blocks) self.assert_block_types(expected_blocks)
# Specific block type tests @freeze_time('2015-01-02')
def test_todays_date_block(self):
## 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):
""" """
Helper function to test that today's date block renders correctly Helper function to test that today's date block renders correctly
and displays the correct time, accounting for daylight savings and displays the correct time, accounting for daylight savings
""" """
self.setup_course_and_user() self.setup_course_and_user()
set_user_preference(self.user, "time_zone", "America/Los_Angeles")
block = TodaysDate(self.course, self.user) block = TodaysDate(self.course, self.user)
self.assertTrue(block.is_enabled) self.assertTrue(block.is_enabled)
self.assertEqual(block.date, datetime.now(utc)) self.assertEqual(block.date, datetime.now(utc))
self.assertEqual(block.title, 'Today is {date}'.format(date=expected_display_date)) self.assertEqual(block.title, 'current_datetime')
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)')
@freeze_time('2015-01-02') @freeze_time('2015-01-02')
def test_todays_date_render(self): def test_todays_date_no_timezone(self):
self.setup_course_and_user() self.setup_course_and_user()
block = TodaysDate(self.course, self.user) self.client.login(username='mrrobot', password='test')
self.assertIn('Jan 02, 2015', block.render())
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') @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.setup_course_and_user()
self.client.login(username='mrrobot', password='test')
set_user_preference(self.user, "time_zone", "America/Los_Angeles") set_user_preference(self.user, "time_zone", "America/Los_Angeles")
block = TodaysDate(self.course, self.user) url = reverse('info', args=(self.course.id,))
# Today is 'Jan 01, 2015' because of time zone offset response = self.client.get(url)
self.assertIn('Jan 01, 2015', block.render())
## 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): def test_course_start_date(self):
self.setup_course_and_user() self.setup_course_and_user()
block = CourseStartDate(self.course, self.user) block = CourseStartDate(self.course, self.user)
...@@ -229,19 +222,32 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase): ...@@ -229,19 +222,32 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
@freeze_time('2015-01-02') @freeze_time('2015-01-02')
def test_start_date_render(self): def test_start_date_render(self):
self.setup_course_and_user() self.setup_course_and_user()
block = CourseStartDate(self.course, self.user) self.client.login(username='mrrobot', password='test')
self.assertIn('in 1 day - Jan 03, 2015', block.render()) 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') @freeze_time('2015-01-02')
def test_start_date_render_time_zone(self): def test_start_date_render_time_zone(self):
self.setup_course_and_user() self.setup_course_and_user()
self.client.login(username='mrrobot', password='test')
set_user_preference(self.user, "time_zone", "America/Los_Angeles") set_user_preference(self.user, "time_zone", "America/Los_Angeles")
block = CourseStartDate(self.course, self.user) url = reverse('info', args=(self.course.id,))
# Jan 02 is in 1 day because of time zone offset response = self.client.get(url)
self.assertIn('in 1 day - Jan 02, 2015', block.render()) html_elements = [
'data-string="in 1 day - {date}"',
## CourseEndDate '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): def test_course_end_date_for_certificate_eligible_mode(self):
self.setup_course_and_user(days_till_start=-1) self.setup_course_and_user(days_till_start=-1)
block = CourseEndDate(self.course, self.user) block = CourseEndDate(self.course, self.user)
...@@ -257,6 +263,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase): ...@@ -257,6 +263,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
block.description, block.description,
'After this date, course content will be archived.' 'After this date, course content will be archived.'
) )
self.assertEqual(block.title, 'Course End')
def test_course_end_date_after_course(self): def test_course_end_date_after_course(self):
self.setup_course_and_user(days_till_start=-2, days_till_end=-1) self.setup_course_and_user(days_till_start=-2, days_till_end=-1)
...@@ -265,9 +272,9 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase): ...@@ -265,9 +272,9 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
block.description, block.description,
'This course is archived, which means you can review course content but it is no longer active.' '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') @freeze_time('2015-01-02')
def test_verified_upgrade_deadline_date(self): def test_verified_upgrade_deadline_date(self):
self.setup_course_and_user(days_till_upgrade_deadline=1) self.setup_course_and_user(days_till_upgrade_deadline=1)
...@@ -293,7 +300,6 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase): ...@@ -293,7 +300,6 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
self.assertEqual(block.link, '{}?sku={}'.format(checkout_page, sku)) self.assertEqual(block.link, '{}?sku={}'.format(checkout_page, sku))
## VerificationDeadlineDate ## VerificationDeadlineDate
def test_no_verification_deadline(self): def test_no_verification_deadline(self):
self.setup_course_and_user(days_till_start=-1, days_till_verification_deadline=None) self.setup_course_and_user(days_till_start=-1, days_till_verification_deadline=None)
block = VerificationDeadlineDate(self.course, self.user) block = VerificationDeadlineDate(self.course, self.user)
...@@ -352,8 +358,8 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase): ...@@ -352,8 +358,8 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
@freeze_time('2015-01-02') @freeze_time('2015-01-02')
@ddt.data( @ddt.data(
(-1, '1 day ago - Jan 01, 2015'), (-1, '1 day ago - {date}'),
(1, 'in 1 day - Jan 03, 2015') (1, 'in 1 day - {date}')
) )
@ddt.unpack @ddt.unpack
def test_render_date_string_past(self, delta, expected_date_string): def test_render_date_string_past(self, delta, expected_date_string):
...@@ -363,21 +369,4 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase): ...@@ -363,21 +369,4 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
days_till_verification_deadline=delta, days_till_verification_deadline=delta,
) )
block = VerificationDeadlineDate(self.course, self.user) block = VerificationDeadlineDate(self.course, self.user)
self.assertEqual(block.get_context()['date'], expected_date_string) self.assertEqual(block.relative_datestring, 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)
<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 @@ ...@@ -3,10 +3,12 @@
<%def name="online_help_token()"><% return "courseinfo" %></%def> <%def name="online_help_token()"><% return "courseinfo" %></%def>
<%namespace name='static' file='../static_content.html'/> <%namespace name='static' file='../static_content.html'/>
<%! <%!
from django.utils.translation import ugettext as _ from datetime import datetime
from pytz import timezone, utc
from courseware.courses import get_course_info_section, get_course_date_summary from django.utils.translation import ugettext as _
from courseware.courses import get_course_info_section, get_course_date_blocks
from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
from openedx.core.djangolib.markup import HTML, Text from openedx.core.djangolib.markup import HTML, Text
%> %>
...@@ -89,11 +91,36 @@ from openedx.core.djangolib.markup import HTML, Text ...@@ -89,11 +91,36 @@ from openedx.core.djangolib.markup import HTML, Text
% endif % endif
</section> </section>
<section aria-label="${_('Handout Navigation')}" class="handouts"> <section aria-label="${_('Handout Navigation')}" class="handouts">
% if SelfPacedConfiguration.current().enable_course_home_improvements: % if SelfPacedConfiguration.current().enable_course_home_improvements:
<h1 class="handouts-header">${_("Important Course Dates")}</h1> <h1 class="handouts-header">${_("Important Course Dates")}</h1>
${HTML(get_course_date_summary(course, user))} ## Should be organized by date, last date appearing at the bottom
% endif
% 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> <h1 class="handouts-header">${_(course.info_sidebar_name)}</h1>
${HTML(get_course_info_section(request, masquerade_user, course, 'handouts'))} ${HTML(get_course_info_section(request, masquerade_user, course, 'handouts'))}
</section> </section>
...@@ -106,7 +133,12 @@ from openedx.core.djangolib.markup import HTML, Text ...@@ -106,7 +133,12 @@ from openedx.core.djangolib.markup import HTML, Text
<h1 class="handouts-header">${_("Course Handouts")}</h1> <h1 class="handouts-header">${_("Course Handouts")}</h1>
${HTML(get_course_info_section(request, masquerade_user, course, 'guest_handouts'))} ${HTML(get_course_info_section(request, masquerade_user, course, 'guest_handouts'))}
</section> </section>
% endif % endif
</div> </div>
</div> </div>
</main> </main>
<%static:require_module_async module_name="js/dateutil_factory" class_name="DateUtilFactory">
DateUtilFactory.transform(iterationKey=".localized-datetime");
</%static:require_module_async>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment