Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions cms/djangoapps/models/settings/course_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand All @@ -20,7 +19,9 @@ class CourseMetadata(object):
'enrollment_end',
'tabs',
'graceperiod',
'checklists']
'checklists',
'show_timezone'
]

@classmethod
def fetch(cls, course_location):
Expand Down
17 changes: 15 additions & 2 deletions common/lib/xmodule/xmodule/course_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,14 @@ 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
)
enrollment_domain = String(help="External login method associated with user accounts allowed to register in course",
scope=Scope.settings)
course_image = String(
Expand Down Expand Up @@ -391,7 +398,13 @@ def __init__(self, *args, **kwargs):
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)
Expand Down
43 changes: 23 additions & 20 deletions common/lib/xmodule/xmodule/tests/test_date_utils.py
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -12,25 +12,34 @@ 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():
def test_get_dflt_time_disp_notz():
test_time = datetime(1992, 3, 12, 15, 3, 30)
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_disp_ret_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'))
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 UTC", get_time_display(test_time, None))
assert_equals("Mar 12, 1992 at 15:03 UTC", get_time_display(test_time, "%"))


# pylint: disable=W0232
Expand All @@ -50,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():
Expand Down
48 changes: 31 additions & 17 deletions common/lib/xmodule/xmodule/util/date_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +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(dtime):
"""
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:
if dtime 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 dtime.tzinfo is not None:
try:
timezone = u" " + dtime.tzinfo.tzname(dtime)
except NotImplementedError:
timezone = dtime.strftime('%z')
else:
timezone = u" UTC"
return unicode(dtime.strftime(u"%b %d, %Y at %H:%M{tz}")).format(
tz=timezone).strip()


def get_time_display(dtime, 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`.

format_string should be a unicode string that is a valid argument for datetime's strftime method.
"""
if dtime is None or format_string is None:
return get_default_time_display(dtime)
try:
return unicode(dtime.strftime(format_string))
except ValueError:
return get_default_time_display(dtime)


def almost_same_datetime(dt1, dt2, allowed_delta=timedelta(minutes=1)):
Expand Down
47 changes: 47 additions & 0 deletions common/test/data/due_date/about/overview.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<section class="about">
<h2>About This Course</h2>
<p>Include your long course description here. The long course description should contain 150-400 words.</p>

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

<section class="prerequisites">
<h2>Prerequisites</h2>
<p>Add information about course prerequisites here.</p>
</section>

<section class="course-staff">
<h2>Course Staff</h2>
<article class="teacher">
<div class="teacher-image">
<img src="/static/images/pl-faculty.png" align="left" style="margin:0 20 px 0">
</div>

<h3>Staff Member #1</h3>
<p>Biography of instructor/staff member #1</p>
</article>

<article class="teacher">
<div class="teacher-image">
<img src="/static/images/pl-faculty.png" align="left" style="margin:0 20 px 0">
</div>

<h3>Staff Member #2</h3>
<p>Biography of instructor/staff member #2</p>
</article>
</section>

<section class="faq">
<section class="responses">
<h2>Frequently Asked Questions</h2>
<article class="response">
<h3>Do I need to buy a textbook?</h3>
<p>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.</p>
</article>

<article class="response">
<h3>Question #2</h3>
<p>Your answer would be displayed here.</p>
</article>
</section>
</section>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<chapter display_name="Section">
<sequential url_name="c804fa32227142a1bd9d5bc183d4a20d"/>
</chapter>
1 change: 1 addition & 0 deletions common/test/data/due_date/course.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<course url_name="2013_fall" org="edX" course="due_date"/>
3 changes: 3 additions & 0 deletions common/test/data/due_date/course/2013_fall.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<course display_name="due_date">
<chapter url_name="c8ee0db7e5a84c85bac80b7013cf6512"/>
</course>
Original file line number Diff line number Diff line change
@@ -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}}
1 change: 1 addition & 0 deletions common/test/data/due_date/policies/2013_fall/policy.json
Original file line number Diff line number Diff line change
@@ -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"}}, "show_timezone": "false"}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<problem display_name="Multiple Choice" markdown="A multiple choice problem presents radio buttons for student input. Students can only select a single &#10;option presented. Multiple Choice questions have been the subject of many areas of research due to the early &#10;invention and adoption of bubble sheets.&#10;&#10;One of the main elements that goes into a good multiple choice question is the existence of good distractors. &#10;That is, each of the alternate responses presented to the student should be the result of a plausible mistake &#10;that a student might make.&#10;&#10;What Apple device competed with the portable CD player?&#10; ( ) The iPad&#10; ( ) Napster&#10; (x) The iPod&#10; ( ) The vegetable peeler&#10; &#10;[explanation]&#10;The release of the iPod allowed consumers to carry their entire music library with them in a &#10;format that did not rely on fragile and energy-intensive spinning disks.&#10;[explanation]&#10;">
<p>
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.</p>
<p> 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.
</p>

<p>What Apple device competed with the portable CD player?</p>
<multiplechoiceresponse>
<choicegroup type="MultipleChoice">
<choice correct="false" name="ipad">The iPad</choice>
<choice correct="false" name="beatles">Napster</choice>
<choice correct="true" name="ipod">The iPod</choice>
<choice correct="false" name="peeler">The vegetable peeler</choice>
</choicegroup>
</multiplechoiceresponse>
<solution>
<div class="detailed-solution">
<p>Explanation</p>
<p>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. </p>
</div>
</solution>
</problem>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<sequential display_name="Subsection" due="2013-09-18T11:30:00Z" start="1970-01-01T00:00:00Z">
<vertical url_name="45640305a210424ebcc6f8e045fad0be"/>
</sequential>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<vertical display_name="New Unit">
<problem url_name="d392c80f5c044e45a4a5f2d62f94efc5"/>
</vertical>
1 change: 1 addition & 0 deletions lms/djangoapps/courseware/tests/modulestore_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Loading