From 08d9942993150dfa0da12e447471290926a263c7 Mon Sep 17 00:00:00 2001 From: cahrens Date: Tue, 10 Sep 2013 13:52:23 -0400 Subject: [PATCH 1/5] Allow custom formatting of due date strings. STUD-724 Test course for allowing custom formatting of due date strings. STUD-724 Try making the name of the course match the folder name. More cleanup. More cleanup. updates. --- common/lib/xmodule/xmodule/course_module.py | 4 + .../xmodule/xmodule/tests/test_date_utils.py | 24 ++++- common/lib/xmodule/xmodule/util/date_utils.py | 19 ++++ common/test/data/due_date/about/overview.html | 47 ++++++++++ .../c8ee0db7e5a84c85bac80b7013cf6512.xml | 3 + common/test/data/due_date/course.xml | 1 + .../test/data/due_date/course/2013_fall.xml | 3 + .../policies/2013_fall/grading_policy.json | 1 + .../due_date/policies/2013_fall/policy.json | 1 + .../d392c80f5c044e45a4a5f2d62f94efc5.xml | 23 +++++ .../c804fa32227142a1bd9d5bc183d4a20d.xml | 3 + .../45640305a210424ebcc6f8e045fad0be.xml | 3 + .../courseware/tests/modulestore_config.py | 1 + lms/djangoapps/courseware/tests/test_views.py | 89 ++++++++++++++++++- lms/djangoapps/courseware/views.py | 3 +- lms/templates/courseware/accordion.html | 11 ++- lms/templates/courseware/progress.html | 8 +- 17 files changed, 234 insertions(+), 10 deletions(-) create mode 100644 common/test/data/due_date/about/overview.html create mode 100644 common/test/data/due_date/chapter/c8ee0db7e5a84c85bac80b7013cf6512.xml create mode 100644 common/test/data/due_date/course.xml create mode 100644 common/test/data/due_date/course/2013_fall.xml create mode 100644 common/test/data/due_date/policies/2013_fall/grading_policy.json create mode 100644 common/test/data/due_date/policies/2013_fall/policy.json create mode 100644 common/test/data/due_date/problem/d392c80f5c044e45a4a5f2d62f94efc5.xml create mode 100644 common/test/data/due_date/sequential/c804fa32227142a1bd9d5bc183d4a20d.xml create mode 100644 common/test/data/due_date/vertical/45640305a210424ebcc6f8e045fad0be.xml diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py index 658a095d1480..1f671a267030 100644 --- a/common/lib/xmodule/xmodule/course_module.py +++ b/common/lib/xmodule/xmodule/course_module.py @@ -338,6 +338,10 @@ class CourseFields(object): ]) 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) + due_date_display_format = String( + help="Format supported by strftime for displaying due dates. Takes precedence over show_timezone.", + scope=Scope.settings, default=None + ) enrollment_domain = String(help="External login method associated with user accounts allowed to register in course", scope=Scope.settings) course_image = String( diff --git a/common/lib/xmodule/xmodule/tests/test_date_utils.py b/common/lib/xmodule/xmodule/tests/test_date_utils.py index 37f30e1b566d..f6c1097ea634 100644 --- a/common/lib/xmodule/xmodule/tests/test_date_utils.py +++ b/common/lib/xmodule/xmodule/tests/test_date_utils.py @@ -1,7 +1,7 @@ """Tests for xmodule.util.date_utils""" from nose.tools import assert_equals, assert_false # pylint: disable=E0611 -from xmodule.util.date_utils import get_default_time_display, almost_same_datetime +from xmodule.util.date_utils import get_default_time_display, get_time_display, almost_same_datetime from datetime import datetime, timedelta, tzinfo from pytz import UTC @@ -33,6 +33,28 @@ def test_get_default_time_display_notz(): get_default_time_display(test_time, False)) +def test_get_time_display_return_empty(): + assert_equals("", get_time_display(None)) + test_time = datetime(1992, 3, 12, 15, 3, 30, tzinfo=UTC) + assert_equals("", get_time_display(test_time, "")) + + +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)) + + +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)) + + # pylint: disable=W0232 class NamelessTZ(tzinfo): """Static timezone for testing""" diff --git a/common/lib/xmodule/xmodule/util/date_utils.py b/common/lib/xmodule/xmodule/util/date_utils.py index 8baa59558b3e..e032bd168539 100644 --- a/common/lib/xmodule/xmodule/util/date_utils.py +++ b/common/lib/xmodule/xmodule/util/date_utils.py @@ -30,6 +30,25 @@ def get_default_time_display(dt, show_timezone=True): at=_(u"at"), tz=timezone).strip() +def get_time_display(dt, format_string=None, show_timezone=True): + """ + 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. + 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) + try: + return unicode(dt.strftime(format_string)) + except ValueError: + return get_default_time_display(dt, show_timezone) + + def almost_same_datetime(dt1, dt2, allowed_delta=timedelta(minutes=1)): """ Returns true if these are w/in a minute of each other. (in case secs saved to db diff --git a/common/test/data/due_date/about/overview.html b/common/test/data/due_date/about/overview.html new file mode 100644 index 000000000000..961786b8f407 --- /dev/null +++ b/common/test/data/due_date/about/overview.html @@ -0,0 +1,47 @@ +
+

About This Course

+

Include your long course description here. The long course description should contain 150-400 words.

+ +

This is paragraph 2 of the long course description. Add more paragraphs as needed. Make sure to enclose them in paragraph tags.

+
+ +
+

Prerequisites

+

Add information about course prerequisites here.

+
+ +
+

Course Staff

+
+
+ +
+ +

Staff Member #1

+

Biography of instructor/staff member #1

+
+ +
+
+ +
+ +

Staff Member #2

+

Biography of instructor/staff member #2

+
+
+ +
+
+

Frequently Asked Questions

+
+

Do I need to buy a textbook?

+

No, a free online version of Chemistry: Principles, Patterns, and Applications, First Edition by Bruce Averill and Patricia Eldredge will be available, though you can purchase a printed version (published by FlatWorld Knowledge) if you’d like.

+
+ +
+

Question #2

+

Your answer would be displayed here.

+
+
+
diff --git a/common/test/data/due_date/chapter/c8ee0db7e5a84c85bac80b7013cf6512.xml b/common/test/data/due_date/chapter/c8ee0db7e5a84c85bac80b7013cf6512.xml new file mode 100644 index 000000000000..cdc5d294f8cf --- /dev/null +++ b/common/test/data/due_date/chapter/c8ee0db7e5a84c85bac80b7013cf6512.xml @@ -0,0 +1,3 @@ + + + diff --git a/common/test/data/due_date/course.xml b/common/test/data/due_date/course.xml new file mode 100644 index 000000000000..e355f924b440 --- /dev/null +++ b/common/test/data/due_date/course.xml @@ -0,0 +1 @@ + diff --git a/common/test/data/due_date/course/2013_fall.xml b/common/test/data/due_date/course/2013_fall.xml new file mode 100644 index 000000000000..97ad3760ae03 --- /dev/null +++ b/common/test/data/due_date/course/2013_fall.xml @@ -0,0 +1,3 @@ + + + diff --git a/common/test/data/due_date/policies/2013_fall/grading_policy.json b/common/test/data/due_date/policies/2013_fall/grading_policy.json new file mode 100644 index 000000000000..272cb4fec6f4 --- /dev/null +++ b/common/test/data/due_date/policies/2013_fall/grading_policy.json @@ -0,0 +1 @@ +{"GRADER": [{"short_label": "HW", "min_count": 12, "type": "Homework", "drop_count": 2, "weight": 0.15}, {"min_count": 12, "type": "Lab", "drop_count": 2, "weight": 0.15}, {"short_label": "Midterm", "min_count": 1, "type": "Midterm Exam", "drop_count": 0, "weight": 0.3}, {"short_label": "Final", "min_count": 1, "type": "Final Exam", "drop_count": 0, "weight": 0.4}], "GRADE_CUTOFFS": {"Pass": 0.5}} \ No newline at end of file diff --git a/common/test/data/due_date/policies/2013_fall/policy.json b/common/test/data/due_date/policies/2013_fall/policy.json new file mode 100644 index 000000000000..c85f8b35faec --- /dev/null +++ b/common/test/data/due_date/policies/2013_fall/policy.json @@ -0,0 +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 diff --git a/common/test/data/due_date/problem/d392c80f5c044e45a4a5f2d62f94efc5.xml b/common/test/data/due_date/problem/d392c80f5c044e45a4a5f2d62f94efc5.xml new file mode 100644 index 000000000000..4e50ac396b77 --- /dev/null +++ b/common/test/data/due_date/problem/d392c80f5c044e45a4a5f2d62f94efc5.xml @@ -0,0 +1,23 @@ + +

+A multiple choice problem presents radio buttons for student +input. Students can only select a single option presented. Multiple Choice questions have been the subject of many areas of research due to the early invention and adoption of bubble sheets.

+

One of the main elements that goes into a good multiple choice question is the existence of good distractors. That is, each of the alternate responses presented to the student should be the result of a plausible mistake that a student might make. +

+ +

What Apple device competed with the portable CD player?

+ + + The iPad + Napster + The iPod + The vegetable peeler + + + +
+

Explanation

+

The release of the iPod allowed consumers to carry their entire music library with them in a format that did not rely on fragile and energy-intensive spinning disks.

+
+
+
diff --git a/common/test/data/due_date/sequential/c804fa32227142a1bd9d5bc183d4a20d.xml b/common/test/data/due_date/sequential/c804fa32227142a1bd9d5bc183d4a20d.xml new file mode 100644 index 000000000000..a26ed3789fc6 --- /dev/null +++ b/common/test/data/due_date/sequential/c804fa32227142a1bd9d5bc183d4a20d.xml @@ -0,0 +1,3 @@ + + + diff --git a/common/test/data/due_date/vertical/45640305a210424ebcc6f8e045fad0be.xml b/common/test/data/due_date/vertical/45640305a210424ebcc6f8e045fad0be.xml new file mode 100644 index 000000000000..66b6fc546b04 --- /dev/null +++ b/common/test/data/due_date/vertical/45640305a210424ebcc6f8e045fad0be.xml @@ -0,0 +1,3 @@ + + + diff --git a/lms/djangoapps/courseware/tests/modulestore_config.py b/lms/djangoapps/courseware/tests/modulestore_config.py index 74fd3da57fe8..9e887855c38c 100644 --- a/lms/djangoapps/courseware/tests/modulestore_config.py +++ b/lms/djangoapps/courseware/tests/modulestore_config.py @@ -22,5 +22,6 @@ 'edX/test_about_blob_end_date/2012_Fall': 'xml', 'edX/graded/2012_Fall': 'xml', 'edX/open_ended/2012_Fall': 'xml', + 'edX/due_date/2013_fall': 'xml' } TEST_DATA_MIXED_MODULESTORE = mixed_store_config(TEST_DATA_DIR, MAPPINGS) diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index b4e835d2d978..eeb24635d565 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -31,9 +31,8 @@ class TestJumpTo(TestCase): def setUp(self): - # Load toy course from XML + # Use toy course from XML self.course_name = 'edX/toy/2012_Fall' - self.toy_course = modulestore().get_course(self.course_name) def test_jumpto_invalid_location(self): location = Location('i4x', 'edX', 'toy', 'NoSuchPlace', None) @@ -62,7 +61,9 @@ def test_jumpto_id_invalid_location(self): self.assertEqual(response.status_code, 404) +@override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE) class ViewsTestCase(TestCase): + """ Tests for views.py methods. """ def setUp(self): self.user = User.objects.create(username='dummy', password='123456', email='test@mit.edu') @@ -73,8 +74,6 @@ def setUp(self): self.enrollment.save() self.location = ['tag', 'org', 'course', 'category', 'name'] - # This is a CourseDescriptor object - self.toy_course = modulestore().get_course('edX/toy/2012_Fall') self.request_factory = RequestFactory() chapter = 'Overview' self.chapter_url = '%s/%s/%s' % ('/courses', self.course_id, chapter) @@ -222,3 +221,85 @@ def test_submission_history_xss(self): }) response = self.client.get(url) self.assertFalse(' @@ -69,8 +69,12 @@

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 629e3962354f..530ab39cd74f 100644 --- a/lms/templates/courseware/progress.html +++ b/lms/templates/courseware/progress.html @@ -70,7 +70,7 @@

Date: Thu, 12 Sep 2013 13:28:36 -0400 Subject: [PATCH 4/5] Style cleanup. --- cms/djangoapps/models/settings/course_metadata.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cms/djangoapps/models/settings/course_metadata.py b/cms/djangoapps/models/settings/course_metadata.py index 422b357c1332..5708cf665b93 100644 --- a/cms/djangoapps/models/settings/course_metadata.py +++ b/cms/djangoapps/models/settings/course_metadata.py @@ -21,7 +21,8 @@ class CourseMetadata(object): 'tabs', 'graceperiod', 'checklists', - 'show_timezone'] + 'show_timezone' + ] @classmethod def fetch(cls, course_location): From 2c9fc59d79a4e45fe31fa7e8f5e92e4d7c569146 Mon Sep 17 00:00:00 2001 From: cahrens Date: Thu, 12 Sep 2013 13:35:37 -0400 Subject: [PATCH 5/5] Unused import. --- cms/djangoapps/models/settings/course_metadata.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cms/djangoapps/models/settings/course_metadata.py b/cms/djangoapps/models/settings/course_metadata.py index 5708cf665b93..603865b8846b 100644 --- a/cms/djangoapps/models/settings/course_metadata.py +++ b/cms/djangoapps/models/settings/course_metadata.py @@ -2,7 +2,6 @@ from contentstore.utils import get_modulestore from xmodule.modulestore.inheritance import own_metadata from xblock.fields import Scope -from xmodule.course_module import CourseDescriptor from cms.xmodule_namespace import CmsBlockMixin