'
+ ]
+ for test in test_responses:
+ self.assertContains(response, test)
@attr(shard=1)
@@ -960,7 +950,8 @@ class BaseDueDateTests(ModuleStoreTestCase):
section = ItemFactory.create(
category='sequential',
parent_location=chapter.location,
- due=datetime(2013, 9, 18, 11, 30, 00)
+ due=datetime(2013, 9, 18, 11, 30, 00),
+ format='homework'
)
vertical = ItemFactory.create(category='vertical', parent_location=section.location)
ItemFactory.create(category='problem', parent_location=vertical.location)
@@ -975,8 +966,7 @@ class BaseDueDateTests(ModuleStoreTestCase):
self.user = UserFactory.create()
self.assertTrue(self.client.login(username=self.user.username, password='test'))
- self.time_with_tz = "due Sep 18, 2013 at 11:30 UTC"
- self.time_without_tz = "due Sep 18, 2013 at 11:30"
+ self.time_with_tz = "2013-09-18 11:30:00+00:00"
def test_backwards_compatability(self):
# The test course being used has show_timezone = False in the policy file
@@ -985,8 +975,7 @@ class BaseDueDateTests(ModuleStoreTestCase):
# remove the timezone.
course = self.set_up_course(due_date_display_format=None, show_timezone=False)
response = self.get_response(course)
- self.assertContains(response, self.time_without_tz)
- self.assertNotContains(response, self.time_with_tz)
+ self.assertContains(response, self.time_with_tz)
# Test that show_timezone has been cleared (which means you get the default value of True).
self.assertTrue(course.show_timezone)
@@ -1001,25 +990,11 @@ class BaseDueDateTests(ModuleStoreTestCase):
response = self.get_response(course)
self.assertContains(response, self.time_with_tz)
- def test_format_plain_text(self):
- # plain text due date
- course = self.set_up_course(due_date_display_format="foobar")
- response = self.get_response(course)
- self.assertNotContains(response, self.time_with_tz)
- self.assertContains(response, "due foobar")
-
def test_format_date(self):
# due date with no time
course = self.set_up_course(due_date_display_format=u"%b %d %y")
response = self.get_response(course)
- self.assertNotContains(response, self.time_with_tz)
- self.assertContains(response, "due Sep 18 13")
-
- def test_format_hidden(self):
- # hide due date completely
- course = self.set_up_course(due_date_display_format=u"")
- response = self.get_response(course)
- self.assertNotContains(response, "due ")
+ self.assertContains(response, self.time_with_tz)
def test_format_invalid(self):
# improperly formatted due_date_display_format falls through to default
@@ -1049,7 +1024,10 @@ class TestAccordionDueDate(BaseDueDateTests):
def get_response(self, course):
""" Returns the HTML for the accordion """
- return self.client.get(reverse('courseware', args=[unicode(course.id)]), follow=True)
+ return self.client.get(
+ reverse('courseware', args=[unicode(course.id)]),
+ follow=True
+ )
@attr(shard=1)
@@ -1089,14 +1067,17 @@ class StartDateTests(ModuleStoreTestCase):
course = self.set_up_course()
response = self.get_about_response(course.id)
# The start date is set in the set_up_course function above.
- self.assertContains(response, "2013-SEPTEMBER-16")
+ # This should return in the format '%Y-%m-%dT%H:%M:%S%z'
+ self.assertContains(response, "2013-09-16T07:17:28+0000")
- @patch('util.date_utils.pgettext', fake_pgettext(translations={
- ("abbreviated month name", "Jul"): "JULY",
- }))
- @patch('util.date_utils.ugettext', fake_ugettext(translations={
- "SHORT_DATE_FORMAT": "%Y-%b-%d",
- }))
+ @patch(
+ 'util.date_utils.pgettext',
+ fake_pgettext(translations={("abbreviated month name", "Jul"): "JULY", })
+ )
+ @patch(
+ 'util.date_utils.ugettext',
+ fake_ugettext(translations={"SHORT_DATE_FORMAT": "%Y-%b-%d", })
+ )
@unittest.skip
def test_format_localized_in_xml_course(self):
response = self.get_about_response(SlashSeparatedCourseKey('edX', 'toy', 'TT_2012_Fall'))
@@ -1345,17 +1326,17 @@ class ProgressPageTests(ModuleStoreTestCase):
"""Test that query counts remain the same for self-paced and instructor-paced courses."""
SelfPacedConfiguration(enabled=self_paced_enabled).save()
self.setup_course(self_paced=self_paced)
- with self.assertNumQueries(35), check_mongo_calls(4):
+ with self.assertNumQueries(38), check_mongo_calls(4):
self._get_progress_page()
def test_progress_queries(self):
self.setup_course()
- with self.assertNumQueries(35), check_mongo_calls(4):
+ with self.assertNumQueries(38), check_mongo_calls(4):
self._get_progress_page()
# subsequent accesses to the progress page require fewer queries.
for _ in range(2):
- with self.assertNumQueries(21), check_mongo_calls(4):
+ with self.assertNumQueries(24), check_mongo_calls(4):
self._get_progress_page()
@patch(
diff --git a/lms/djangoapps/courseware/views/index.py b/lms/djangoapps/courseware/views/index.py
index d3fe5938ef..337ebb57a5 100644
--- a/lms/djangoapps/courseware/views/index.py
+++ b/lms/djangoapps/courseware/views/index.py
@@ -24,7 +24,6 @@ import urllib
from xblock.fragment import Fragment
from opaque_keys.edx.keys import CourseKey
-from openedx.core.lib.time_zone_utils import get_user_time_zone
from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY
from openedx.core.djangoapps.user_api.preferences.api import get_user_preference
from shoppingcart.models import CourseRegistrationCode
@@ -402,7 +401,6 @@ class CoursewareIndex(View):
self.request,
self.course,
table_of_contents['chapters'],
- courseware_context['language_preference'],
)
# entrance exam data
@@ -499,7 +497,7 @@ class CoursewareIndex(View):
raise
-def render_accordion(request, course, table_of_contents, language_preference):
+def render_accordion(request, course, table_of_contents):
"""
Returns the HTML that renders the navigation for the given course.
Expects the table_of_contents to have data on each chapter and section,
@@ -511,9 +509,6 @@ def render_accordion(request, course, table_of_contents, language_preference):
('course_id', unicode(course.id)),
('csrf', csrf(request)['csrf_token']),
('due_date_display_format', course.due_date_display_format),
- ('time_zone', request.user.preferences.model.get_value(request.user, "time_zone", None)),
- ('language', language_preference),
-
] + TEMPLATE_IMPORTS.items()
)
return render_to_string('courseware/accordion.html', context)
diff --git a/lms/envs/common.py b/lms/envs/common.py
index 80cd61d865..70b40c8627 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -521,6 +521,9 @@ TEMPLATES = [
# Shoppingcart processor (detects if request.user has a cart)
'shoppingcart.context_processor.user_has_cart_context_processor',
+ # Timezone processor (sends language and time_zone preference)
+ 'courseware.context_processor.user_timezone_locale_prefs',
+
# Allows the open edX footer to be leveraged in Django Templates.
'edxmako.shortcuts.footer_context_processor',
diff --git a/lms/static/js/dateutil_factory.js b/lms/static/js/dateutil_factory.js
index 8638d3279a..d0b5b6c6a5 100644
--- a/lms/static/js/dateutil_factory.js
+++ b/lms/static/js/dateutil_factory.js
@@ -36,8 +36,8 @@
context = {
datetime: $(this).data('datetime'),
timezone: $(this).data('timezone'),
- language: $(this).attr('lang'),
- format: $(this).data('format')
+ language: $(this).data('language'),
+ format: DateUtils.dateFormatEnum[$(this).data('format')]
};
displayDatetime = stringHandler(
localizedTime(context),
@@ -45,6 +45,11 @@
$(this).data('datetoken')
);
$(this).text(displayDatetime);
+ } else {
+ displayDatetime = stringHandler(
+ $(this).data('string')
+ );
+ $(this).text(displayDatetime);
}
});
};
diff --git a/lms/static/js/spec/dateutil_factory_spec.js b/lms/static/js/spec/dateutil_factory_spec.js
index 852ee51a68..9e09e7bd30 100644
--- a/lms/static/js/spec/dateutil_factory_spec.js
+++ b/lms/static/js/spec/dateutil_factory_spec.js
@@ -35,7 +35,7 @@ define(['../dateutil_factory.js'], function(DateUtilIterator) {
'data-string="Due {date}">'
);
Object.keys(testLangs).forEach(function(key) {
- $form.attr('lang', String(key));
+ $form.attr('data-language', String(key));
$(document.body).append($form);
DateUtilIterator.transform(iterationKey);
diff --git a/lms/static/js/student_account/views/account_settings_factory.js b/lms/static/js/student_account/views/account_settings_factory.js
index 86fca0e190..c03ac83aba 100644
--- a/lms/static/js/student_account/views/account_settings_factory.js
+++ b/lms/static/js/student_account/views/account_settings_factory.js
@@ -116,9 +116,10 @@
title: gettext('Time Zone'),
valueAttribute: 'time_zone',
helpMessage: gettext(
- 'Select the time zone for displaying course dates. If you do not specify a ' +
- 'time zone here, course dates, including assignment deadlines, are displayed in ' +
- 'Coordinated Universal Time (UTC).'
+ 'Select the time zone for displaying course dates. ' +
+ 'If you do not specify a time zone, course dates, ' +
+ 'including assignment deadlines, will be displayed ' +
+ 'in your browser\'s local time zone.'
),
groupOptions: [{
groupTitle: gettext('All Time Zones'),
diff --git a/lms/templates/course.html b/lms/templates/course.html
index dfc53ef0ba..5ae2774d79 100644
--- a/lms/templates/course.html
+++ b/lms/templates/course.html
@@ -19,14 +19,31 @@ from django.core.urlresolvers import reverse
${course.display_number_with_default}${course.display_name_with_default}
-
${_("Starts")}: ${course.start_datetime_text()}
-
+ <%
+ if course.start is not None:
+ course_date_string = course.start.strftime('%Y-%m-%dT%H:%M:%S%z')
+ else:
+ course_date_string = ''
+ %>
+ % if isinstance(course_start_date, str):
+
${_("Starts")}: ${course_start_date}
+ % else:
+
+ % endif
+
${course.display_org_with_default}
${course.display_number_with_default}
-
${_("Starts")}:
-
+ %if isinstance(course_start_date, str):
+
${_("Starts")}:
+ %else:
+
${_("Starts")}:
+ %endif
+
+<%static:require_module_async module_name="js/dateutil_factory" class_name="DateUtilFactory">
+ DateUtilFactory.transform(iterationKey=".localized_datetime");
+%static:require_module_async>
diff --git a/lms/templates/courseware/accordion.html b/lms/templates/courseware/accordion.html
index 621ee43842..2327f028de 100644
--- a/lms/templates/courseware/accordion.html
+++ b/lms/templates/courseware/accordion.html
@@ -32,21 +32,19 @@ else:
span_start=HTML(''),
span_end=HTML(''),
) if 'active' in section and section['active'] else ''}
- <%
- if section.get('due') is None:
- due_date = ''
- else:
- formatted_string = get_time_display(section['due'], due_date_display_format, coerce_tz=time_zone)
- due_date = '' if len(formatted_string)==0 else _('due {date}').format(date=formatted_string)
- %>
- ## There is behavior differences between
+ ## There are behavior differences between
## rendering of sections which have proctoring/timed examinations
## and those that do not.
##
## Proctoring exposes a exam status message field as well as
## a status icon
-
+ <%
+ if section.get('due') is None:
+ data_string = ''
+ else:
+ data_string = _('due {date}')
+ %>
% if section['format'] or due_date or 'proctoring' in section:
% if 'proctoring' in section:
@@ -57,12 +55,12 @@ else:
## completed proctored exam statuses should not show the due date
## since the exam has already been submitted by the user
% if not section['proctoring'].get('in_completed_state', False):
- ${due_date}
+
% endif
% else:
## non-proctored section, we just show the exam format and the due date
## this is the standard case in edx-platform
- ${section['format']} ${due_date}
+
% if 'graded' in section and section['graded']:
@@ -87,4 +85,8 @@ else:
<%static:require_module_async module_name="js/courseware/accordion_events" class_name="AccordionEvents">
AccordionEvents();
%static:require_module_async>
+
+<%static:require_module_async module_name="js/dateutil_factory" class_name="DateUtilFactory">
+ DateUtilFactory.transform(iterationKey=".localized-datetime");
+%static:require_module_async>
% endif
diff --git a/lms/templates/courseware/course_about.html b/lms/templates/courseware/course_about.html
index bfa62e86ee..8d6b0ae5e8 100644
--- a/lms/templates/courseware/course_about.html
+++ b/lms/templates/courseware/course_about.html
@@ -223,21 +223,40 @@ from openedx.core.lib.courses import course_image_url
% endif
## We plan to ditch end_date (which is not stored in course metadata),
## but for backwards compatibility, show about/end_date blob if it exists.
% if get_course_about_section(request, course, "end_date") or course.end:
+ <%
+ course_end_date = course.end
+ %>
+
@@ -439,3 +455,7 @@ from student.helpers import (
}
});
+
+<%static:require_module_async module_name="js/dateutil_factory" class_name="DateUtilFactory">
+ DateUtilFactory.transform(iterationKey=".localized-datetime");
+%static:require_module_async>
diff --git a/openedx/core/djangoapps/content/course_overviews/models.py b/openedx/core/djangoapps/content/course_overviews/models.py
index 4b15b17125..d9cede9f58 100644
--- a/openedx/core/djangoapps/content/course_overviews/models.py
+++ b/openedx/core/djangoapps/content/course_overviews/models.py
@@ -357,7 +357,6 @@ class CourseOverview(TimeStampedModel):
"""
Returns True if the course starts with-in given number of days otherwise returns False.
"""
-
return course_metadata_utils.course_starts_within(self.start, days)
def start_datetime_text(self, format_string="SHORT_DATE", time_zone=utc):
@@ -389,6 +388,7 @@ class CourseOverview(TimeStampedModel):
def end_datetime_text(self, format_string="SHORT_DATE", time_zone=utc):
"""
Returns the end date or datetime for the course formatted as a string.
+
"""
return course_metadata_utils.course_end_datetime_text(
self.end,
diff --git a/openedx/core/lib/tests/test_time_zone_utils.py b/openedx/core/lib/tests/test_time_zone_utils.py
index f1ad4be79a..082b46cadd 100644
--- a/openedx/core/lib/tests/test_time_zone_utils.py
+++ b/openedx/core/lib/tests/test_time_zone_utils.py
@@ -1,12 +1,10 @@
"""Tests covering time zone utilities."""
from freezegun import freeze_time
from student.tests.factories import UserFactory
-from openedx.core.djangoapps.user_api.preferences.api import set_user_preference
from openedx.core.lib.time_zone_utils import (
get_display_time_zone,
get_time_zone_abbr,
get_time_zone_offset,
- get_user_time_zone,
)
from pytz import timezone, utc
from unittest import TestCase
@@ -25,20 +23,6 @@ class TestTimeZoneUtils(TestCase):
self.user = UserFactory.build()
self.user.save()
- def test_get_user_time_zone(self):
- """
- Test to ensure get_user_time_zone() returns the correct time zone
- or UTC if user has not specified time zone.
- """
- # User time zone should be UTC when no time zone has been chosen
- user_tz = get_user_time_zone(self.user)
- self.assertEqual(user_tz, utc)
-
- # User time zone should change when user specifies time zone
- set_user_preference(self.user, 'time_zone', 'Asia/Tokyo')
- user_tz = get_user_time_zone(self.user)
- self.assertEqual(user_tz, timezone('Asia/Tokyo'))
-
def _display_time_zone_helper(self, time_zone_string):
"""
Helper function to return all info from get_display_time_zone()
diff --git a/openedx/core/lib/time_zone_utils.py b/openedx/core/lib/time_zone_utils.py
index 442d5f7d60..27bad00626 100644
--- a/openedx/core/lib/time_zone_utils.py
+++ b/openedx/core/lib/time_zone_utils.py
@@ -2,19 +2,9 @@
Utilities related to timezones
"""
from datetime import datetime
-
from pytz import common_timezones, timezone, utc
-def get_user_time_zone(user):
- """
- Returns pytz time zone object of the user's time zone if available or UTC time zone if unavailable
- """
- #TODO: exception for unknown timezones?
- time_zone = user.preferences.model.get_value(user, "time_zone", 'utc')
- return timezone(time_zone)
-
-
def _format_time_zone_string(time_zone, date_time, format_string):
"""
Returns a string, specified by format string, of the current date/time of the time zone.