diff --git a/cms/djangoapps/models/settings/course_metadata.py b/cms/djangoapps/models/settings/course_metadata.py index f186fbf116..422b357c13 100644 --- a/cms/djangoapps/models/settings/course_metadata.py +++ b/cms/djangoapps/models/settings/course_metadata.py @@ -20,7 +20,8 @@ class CourseMetadata(object): 'enrollment_end', 'tabs', 'graceperiod', - 'checklists'] + 'checklists', + 'show_timezone'] @classmethod def fetch(cls, course_location): diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py index 1f671a2670..24e6995ae1 100644 --- a/common/lib/xmodule/xmodule/course_module.py +++ b/common/lib/xmodule/xmodule/course_module.py @@ -337,7 +337,10 @@ class CourseFields(object): "action_external": False}]} ]) info_sidebar_name = String(scope=Scope.settings, default='Course Handouts') - show_timezone = Boolean(help="True if timezones should be shown on dates in the courseware", scope=Scope.settings, default=True) + show_timezone = Boolean( + help="True if timezones should be shown on dates in the courseware. Deprecated in favor of due_date_display_format.", + scope=Scope.settings, default=True + ) due_date_display_format = String( help="Format supported by strftime for displaying due dates. Takes precedence over show_timezone.", scope=Scope.settings, default=None @@ -395,7 +398,13 @@ class CourseDescriptor(CourseFields, SequenceDescriptor): elif isinstance(self.location, CourseLocator): self.wiki_slug = self.location.course_id or self.display_name - msg = None + if self.due_date_display_format is None and self.show_timezone is False: + # For existing courses with show_timezone set to False (and no due_date_display_format specified), + # set the due_date_display_format to what would have been shown previously (with no timezone). + # Then remove show_timezone so that if the user clears out the due_date_display_format, + # they get the default date display. + self.due_date_display_format = u"%b %d, %Y at %H:%M" + delattr(self, 'show_timezone') # NOTE: relies on the modulestore to call set_grading_policy() right after # init. (Modulestore is in charge of figuring out where to load the policy from) diff --git a/common/lib/xmodule/xmodule/tests/test_date_utils.py b/common/lib/xmodule/xmodule/tests/test_date_utils.py index f6c1097ea6..d7efba6010 100644 --- a/common/lib/xmodule/xmodule/tests/test_date_utils.py +++ b/common/lib/xmodule/xmodule/tests/test_date_utils.py @@ -12,12 +12,6 @@ def test_get_default_time_display(): assert_equals( "Mar 12, 1992 at 15:03 UTC", get_default_time_display(test_time)) - assert_equals( - "Mar 12, 1992 at 15:03 UTC", - get_default_time_display(test_time, True)) - assert_equals( - "Mar 12, 1992 at 15:03", - get_default_time_display(test_time, False)) def test_get_default_time_display_notz(): @@ -25,12 +19,6 @@ def test_get_default_time_display_notz(): assert_equals( "Mar 12, 1992 at 15:03 UTC", get_default_time_display(test_time)) - assert_equals( - "Mar 12, 1992 at 15:03 UTC", - get_default_time_display(test_time, True)) - assert_equals( - "Mar 12, 1992 at 15:03", - get_default_time_display(test_time, False)) def test_get_time_display_return_empty(): @@ -42,17 +30,16 @@ def test_get_time_display_return_empty(): def test_get_time_display(): test_time = datetime(1992, 3, 12, 15, 3, 30, tzinfo=UTC) assert_equals("dummy text", get_time_display(test_time, 'dummy text')) - assert_equals("Mar 12 1992", get_time_display(test_time, '%b %d %Y', True)) - assert_equals("Mar 12 1992 UTC", get_time_display(test_time, '%b %d %Y %Z', False)) - assert_equals("Mar 12 15:03", get_time_display(test_time, '%b %d %H:%M', False)) + assert_equals("Mar 12 1992", get_time_display(test_time, '%b %d %Y')) + assert_equals("Mar 12 1992 UTC", get_time_display(test_time, '%b %d %Y %Z')) + assert_equals("Mar 12 15:03", get_time_display(test_time, '%b %d %H:%M')) def test_get_time_pass_through(): test_time = datetime(1992, 3, 12, 15, 3, 30, tzinfo=UTC) assert_equals("Mar 12, 1992 at 15:03 UTC", get_time_display(test_time)) - assert_equals("Mar 12, 1992 at 15:03", get_time_display(test_time, None, False)) - assert_equals("Mar 12, 1992 at 15:03", get_time_display(test_time, "%", False)) - assert_equals("Mar 12, 1992 at 15:03 UTC", get_time_display(test_time, "%", True)) + assert_equals("Mar 12, 1992 at 15:03 UTC", get_time_display(test_time, None)) + assert_equals("Mar 12, 1992 at 15:03 UTC", get_time_display(test_time, "%")) # pylint: disable=W0232 @@ -72,12 +59,6 @@ def test_get_default_time_display_no_tzname(): assert_equals( "Mar 12, 1992 at 15:03-0300", get_default_time_display(test_time)) - assert_equals( - "Mar 12, 1992 at 15:03-0300", - get_default_time_display(test_time, True)) - assert_equals( - "Mar 12, 1992 at 15:03", - get_default_time_display(test_time, False)) def test_almost_same_datetime(): diff --git a/common/lib/xmodule/xmodule/util/date_utils.py b/common/lib/xmodule/xmodule/util/date_utils.py index e032bd1685..43012e17ab 100644 --- a/common/lib/xmodule/xmodule/util/date_utils.py +++ b/common/lib/xmodule/xmodule/util/date_utils.py @@ -2,51 +2,46 @@ Convenience methods for working with datetime objects """ from datetime import timedelta -from django.utils.translation import ugettext as _ -def get_default_time_display(dt, show_timezone=True): +def get_default_time_display(dt): """ Converts a datetime to a string representation. This is the default representation used in Studio and LMS. - It is of the form "Apr 09, 2013 at 16:00" or "Apr 09, 2013 at 16:00 UTC", - depending on the value of show_timezone. + It is of the form "Apr 09, 2013 at 16:00 UTC". If None is passed in for dt, an empty string will be returned. - The default value of show_timezone is True. """ if dt is None: return u"" - timezone = u"" - if show_timezone: - if dt.tzinfo is not None: - try: - timezone = u" " + dt.tzinfo.tzname(dt) - except NotImplementedError: - timezone = dt.strftime('%z') - else: - timezone = u" UTC" - return unicode(dt.strftime(u"%b %d, %Y {at} %H:%M{tz}")).format( - at=_(u"at"), tz=timezone).strip() + if dt.tzinfo is not None: + try: + timezone = u" " + dt.tzinfo.tzname(dt) + except NotImplementedError: + timezone = dt.strftime('%z') + else: + timezone = u" UTC" + return unicode(dt.strftime(u"%b %d, %Y at %H:%M{tz}")).format( + tz=timezone).strip() -def get_time_display(dt, format_string=None, show_timezone=True): +def get_time_display(dt, format_string=None): """ Converts a datetime to a string representation. If None is passed in for dt, an empty string will be returned. + If the format_string is None, or if format_string is improperly - formatted, this method will return the value from `get_default_time_display` - (passing in the show_timezone argument). - If the format_string is specified, show_timezone is ignored. + formatted, this method will return the value from `get_default_time_display`. + format_string should be a unicode string that is a valid argument for datetime's strftime method. """ if dt is None or format_string is None: - return get_default_time_display(dt, show_timezone) + return get_default_time_display(dt) try: return unicode(dt.strftime(format_string)) except ValueError: - return get_default_time_display(dt, show_timezone) + return get_default_time_display(dt) def almost_same_datetime(dt1, dt2, allowed_delta=timedelta(minutes=1)): diff --git a/common/test/data/due_date/policies/2013_fall/policy.json b/common/test/data/due_date/policies/2013_fall/policy.json index c85f8b35fa..42ba75ae29 100644 --- a/common/test/data/due_date/policies/2013_fall/policy.json +++ b/common/test/data/due_date/policies/2013_fall/policy.json @@ -1 +1 @@ -{"course/2013_fall": {"tabs": [{"type": "courseware"}, {"type": "course_info", "name": "Course Info"}, {"type": "textbooks"}, {"type": "discussion", "name": "Discussion"}, {"type": "wiki", "name": "Wiki"}, {"type": "progress", "name": "Progress"}], "display_name": "due_date", "discussion_topics": {"General": {"id": "i4x-edX-due_date-course-2013_fall"}}}} \ No newline at end of file +{"course/2013_fall": {"tabs": [{"type": "courseware"}, {"type": "course_info", "name": "Course Info"}, {"type": "textbooks"}, {"type": "discussion", "name": "Discussion"}, {"type": "wiki", "name": "Wiki"}, {"type": "progress", "name": "Progress"}], "display_name": "due_date", "discussion_topics": {"General": {"id": "i4x-edX-due_date-course-2013_fall"}}, "show_timezone": "false"}} \ No newline at end of file diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index eeb24635d5..72a63196de 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -14,7 +14,7 @@ from student.models import CourseEnrollment from student.tests.factories import AdminFactory from mitxmako.middleware import MakoMiddleware -from xmodule.modulestore.django import modulestore +from xmodule.modulestore.django import modulestore, clear_existing_modulestores import courseware.views as views from xmodule.modulestore import Location @@ -254,6 +254,8 @@ class ViewsTestCase(TestCase): def set_show_timezone(show_timezone): """ Sets the show_timezone property and returns value from get_text function. + + Note that show_timezone is deprecated and cannot be set by the user. """ course.show_timezone = show_timezone course.save() @@ -268,38 +270,51 @@ class ViewsTestCase(TestCase): return get_text() request.user = self.user + # Clear out the modulestores, so we start with the test course in its default state. + clear_existing_modulestores() course = modulestore().get_course("edX/due_date/2013_fall") - # Default case show_timezone = True - text = set_show_timezone(True) - self.assertIn("due Sep 18, 2013 at 11:30 UTC", text) + time_with_utc = "due Sep 18, 2013 at 11:30 UTC" + time_without_utc = "due Sep 18, 2013 at 11:30" - # show_timezone = False - text = set_show_timezone(False) - self.assertNotIn("due Sep 18, 2013 at 11:30 UTC", text) - self.assertIn("due Sep 18, 2013 at 11:30", text) + # The test course being used has show_timezone = False in the policy file + # (and no due_date_display_format set). This is to test our backwards compatibility-- + # in course_module's init method, the date_display_format will be set accordingly to + # remove the timezone. + text = get_text() + self.assertIn(time_without_utc, text) + self.assertNotIn(time_with_utc, text) + # Test that show_timezone has been cleared (which means you get the default value of True). + self.assertTrue(course.show_timezone) + + # Clear out the due date format and verify you get the default (with timezone). + delattr(course, 'due_date_display_format') + course.save() + text = get_text() + self.assertIn(time_with_utc, text) + + # Same for setting the due date to None + text = set_due_date_format(None) + self.assertIn(time_with_utc, text) # plain text due date text = set_due_date_format("foobar") - self.assertNotIn("due Sep 18, 2013 at 11:30", text) + self.assertNotIn(time_with_utc, text) self.assertIn("due foobar", text) # due date with no time - text = set_due_date_format(u"%b %d %Y") - self.assertNotIn("due Sep 18, 2013 at 11:30", text) - self.assertIn("due Sep 18 2013", text) + text = set_due_date_format(u"%b %d %y") + self.assertNotIn(time_with_utc, text) + self.assertIn("due Sep 18 13", text) # hide due date completely text = set_due_date_format(u"") self.assertNotIn("due ", text) - # improperly formatted due_date_display_format falls through to default with show_timezone arg + # improperly formatted due_date_display_format falls through to default + # (value of show_timezone does not matter-- setting to False to make that clear). + set_show_timezone(False) text = set_due_date_format(u"%%%") self.assertNotIn("%%%", text) - self.assertIn("due Sep 18, 2013 at 11:30", text) - self.assertNotIn("due Sep 18, 2013 at 11:30 UTC", text) + self.assertIn(time_with_utc, text) - set_show_timezone(True) - text = set_due_date_format(u"%%%") - self.assertNotIn("%%%", text) - self.assertIn("due Sep 18, 2013 at 11:30 UTC", text) diff --git a/lms/djangoapps/courseware/views.py b/lms/djangoapps/courseware/views.py index f156ce69f0..500d818672 100644 --- a/lms/djangoapps/courseware/views.py +++ b/lms/djangoapps/courseware/views.py @@ -98,7 +98,6 @@ def render_accordion(request, course, chapter, section, field_data_cache): context = dict([('toc', toc), ('course_id', course.id), ('csrf', csrf(request)['csrf_token']), - ('show_timezone', course.show_timezone), ('due_date_display_format', course.due_date_display_format)] + template_imports.items()) return render_to_string('courseware/accordion.html', context) diff --git a/lms/templates/courseware/accordion.html b/lms/templates/courseware/accordion.html index cb7ac99f6e..8784b27d71 100644 --- a/lms/templates/courseware/accordion.html +++ b/lms/templates/courseware/accordion.html @@ -29,7 +29,7 @@ if section.get('due') is None: due_date = '' else: - formatted_string = get_time_display(section['due'], due_date_display_format, show_timezone) + formatted_string = get_time_display(section['due'], due_date_display_format) due_date = '' if len(formatted_string)==0 else _('due {date}'.format(date=formatted_string)) %>

${section['format']} ${due_date}

diff --git a/lms/templates/courseware/progress.html b/lms/templates/courseware/progress.html index 629e396235..530ab39cd7 100644 --- a/lms/templates/courseware/progress.html +++ b/lms/templates/courseware/progress.html @@ -70,7 +70,7 @@ ${progress_graph.body(grade_summary, course.grade_cutoffs, "grade-detail-graph", %if section.get('due') is not None: <% - formatted_string = get_time_display(section['due'], course.due_date_display_format, course.show_timezone) + formatted_string = get_time_display(section['due'], course.due_date_display_format) due_date = '' if len(formatted_string)==0 else _('due {date}'.format(date=formatted_string)) %>