Skip to content
This repository has been archived by the owner on May 10, 2024. It is now read-only.

Commit

Permalink
delete datetext functions, implement dateutils
Browse files Browse the repository at this point in the history
  • Loading branch information
Gregory Martin committed Dec 20, 2016
1 parent ef55cc4 commit b0bf19a
Show file tree
Hide file tree
Showing 31 changed files with 143 additions and 524 deletions.
80 changes: 0 additions & 80 deletions common/lib/xmodule/xmodule/course_metadata_utils.py
Expand Up @@ -10,11 +10,8 @@
import dateutil.parser
from math import exp

from openedx.core.lib.time_zone_utils import get_time_zone_abbr
from pytz import utc

from .fields import Date

DEFAULT_START_DATE = datetime(2030, 1, 1, tzinfo=utc)


Expand Down Expand Up @@ -96,83 +93,6 @@ def course_start_date_is_default(start, advertised_start):
return advertised_start is None and start == DEFAULT_START_DATE


def _datetime_to_string(date_time, format_string, time_zone, strftime_localized):
"""
Formats the given datetime with the given function and format string.
Adds time zone abbreviation to the resulting string if the format is DATE_TIME or TIME.
Arguments:
date_time (datetime): the datetime to be formatted
format_string (str): the date format type, as passed to strftime
time_zone (pytz time zone): the time zone to convert to
strftime_localized ((datetime, str) -> str): a nm localized string
formatting function
"""
result = strftime_localized(date_time.astimezone(time_zone), format_string)
abbr = get_time_zone_abbr(time_zone, date_time)
return (
result + ' ' + abbr if format_string in ['DATE_TIME', 'TIME', 'DAY_AND_TIME']
else result
)


def course_start_datetime_text(start_date, advertised_start, format_string, time_zone, ugettext, strftime_localized):
"""
Calculates text to be shown to user regarding a course's start
datetime in specified time zone.
Prefers .advertised_start, then falls back to .start.
Arguments:
start_date (datetime): the course's start datetime
advertised_start (str): the course's advertised start date
format_string (str): the date format type, as passed to strftime
time_zone (pytz time zone): the time zone to convert to
ugettext ((str) -> str): a text localization function
strftime_localized ((datetime, str) -> str): a localized string
formatting function
"""
if advertised_start is not None:
# TODO: This will return an empty string if advertised_start == ""... consider changing this behavior?
try:
# from_json either returns a Date, returns None, or raises a ValueError
parsed_advertised_start = Date().from_json(advertised_start)
if parsed_advertised_start is not None:
# In the Django implementation of strftime_localized, if
# the year is <1900, _datetime_to_string will raise a ValueError.
return _datetime_to_string(parsed_advertised_start, format_string, time_zone, strftime_localized)
except ValueError:
pass
return advertised_start.title()
elif start_date != DEFAULT_START_DATE:
return _datetime_to_string(start_date, format_string, time_zone, strftime_localized)
else:
_ = ugettext
# Translators: TBD stands for 'To Be Determined' and is used when a course
# does not yet have an announced start date.
return _('TBD')


def course_end_datetime_text(end_date, format_string, time_zone, strftime_localized):
"""
Returns a formatted string for a course's end date or datetime.
If end_date is None, an empty string will be returned.
Arguments:
end_date (datetime): the end datetime of a course
format_string (str): the date format type, as passed to strftime
time_zone (pytz time zone): the time zone to convert to
strftime_localized ((datetime, str) -> str): a localized string
formatting function
"""
return (
_datetime_to_string(end_date, format_string, time_zone, strftime_localized) if end_date is not None
else ''
)


def may_certify_for_course(certificates_display_behavior, certificates_show_before_end, has_ended):
"""
Returns whether it is acceptable to show the student a certificate download
Expand Down
33 changes: 4 additions & 29 deletions common/lib/xmodule/xmodule/course_module.py
Expand Up @@ -197,10 +197,11 @@ class CourseFields(object):
scope=Scope.settings,
)
advertised_start = String(
display_name=_("Course Advertised Start Date"),
display_name=_("Course Advertised Start"),
help=_(
"Enter the date you want to advertise as the course start date, if this date is different from the set "
"start date. To advertise the set start date, enter null."
"Enter the text that you want to use as the advertised starting time frame for the course, "
"such as \"Winter 2018\". If you enter null for this value, the start date that you have set "
"for this course is used."
),
scope=Scope.settings
)
Expand Down Expand Up @@ -1211,21 +1212,6 @@ def id(self):
"""Return the course_id for this course"""
return self.location.course_key

def start_datetime_text(self, format_string="SHORT_DATE", time_zone=utc):
"""
Returns the desired text corresponding the course's start date and time in specified time zone, defaulted
to UTC. Prefers .advertised_start, then falls back to .start
"""
i18n = self.runtime.service(self, "i18n")
return course_metadata_utils.course_start_datetime_text(
self.start,
self.advertised_start,
format_string,
time_zone,
i18n.ugettext,
i18n.strftime
)

@property
def start_date_is_still_default(self):
"""
Expand All @@ -1237,17 +1223,6 @@ def start_date_is_still_default(self):
self.advertised_start
)

def end_datetime_text(self, format_string="SHORT_DATE", time_zone=utc):
"""
Returns the end date or date_time for the course formatted as a string.
"""
return course_metadata_utils.course_end_datetime_text(
self.end,
format_string,
time_zone,
self.runtime.service(self, "i18n").strftime
)

def get_discussion_blackout_datetimes(self):
"""
Get a list of dicts with start and end fields with datetime values from
Expand Down
84 changes: 1 addition & 83 deletions common/lib/xmodule/xmodule/tests/test_course_metadata_utils.py
Expand Up @@ -5,7 +5,7 @@
from datetime import timedelta, datetime
from unittest import TestCase

from pytz import timezone, utc
from pytz import utc
from xmodule.block_metadata_utils import (
url_name_for_block,
display_name_with_default,
Expand All @@ -18,11 +18,8 @@
has_course_ended,
DEFAULT_START_DATE,
course_start_date_is_default,
course_start_datetime_text,
course_end_datetime_text,
may_certify_for_course,
)
from xmodule.fields import Date
from xmodule.modulestore.tests.utils import (
MongoModulestoreBuilder,
VersioningModulestoreBuilder,
Expand Down Expand Up @@ -112,12 +109,6 @@ def noop_gettext(text):

test_datetime = datetime(1945, 2, 6, 4, 20, 00, tzinfo=utc)
advertised_start_parsable = "2038-01-19 03:14:07"
advertised_start_bad_date = "215-01-01 10:10:10"
advertised_start_unparsable = "This coming fall"
time_zone_normal_parsable = "2016-03-27 00:59:00"
time_zone_normal_datetime = datetime(2016, 3, 27, 00, 59, 00, tzinfo=utc)
time_zone_daylight_parsable = "2016-03-27 01:00:00"
time_zone_daylight_datetime = datetime(2016, 3, 27, 1, 00, 00, tzinfo=utc)

FunctionTest = namedtuple('FunctionTest', 'function scenarios') # pylint: disable=invalid-name
TestScenario = namedtuple('TestScenario', 'arguments expected_return') # pylint: disable=invalid-name
Expand Down Expand Up @@ -169,79 +160,6 @@ def noop_gettext(text):
TestScenario((DEFAULT_START_DATE, advertised_start_parsable), False),
TestScenario((DEFAULT_START_DATE, None), True),
]),
FunctionTest(course_start_datetime_text, [
# Test parsable advertised start date.
# Expect start datetime to be parsed and formatted back into a string.
TestScenario(
(DEFAULT_START_DATE, advertised_start_parsable, 'DATE_TIME',
utc, noop_gettext, mock_strftime_localized),
mock_strftime_localized(Date().from_json(advertised_start_parsable), 'DATE_TIME') + " UTC"
),
# Test un-parsable advertised start date.
# Expect date parsing to throw a ValueError, and the advertised
# start to be returned in Title Case.
TestScenario(
(test_datetime, advertised_start_unparsable, 'DATE_TIME',
utc, noop_gettext, mock_strftime_localized),
advertised_start_unparsable.title()
),
# Test parsable advertised start date from before January 1, 1900.
# Expect mock_strftime_localized to throw a ValueError, and the
# advertised start to be returned in Title Case.
TestScenario(
(test_datetime, advertised_start_bad_date, 'DATE_TIME',
utc, noop_gettext, mock_strftime_localized),
advertised_start_bad_date.title()
),
# Test without advertised start date, but with a set start datetime.
# Expect formatted datetime to be returned.
TestScenario(
(test_datetime, None, 'SHORT_DATE', utc, noop_gettext, mock_strftime_localized),
mock_strftime_localized(test_datetime, 'SHORT_DATE')
),
# Test without advertised start date and with default start datetime.
# Expect TBD to be returned.
TestScenario(
(DEFAULT_START_DATE, None, 'SHORT_DATE', utc, noop_gettext, mock_strftime_localized),
'TBD'
),
# Test correctly formatted start datetime is returned during normal daylight hours
TestScenario(
(DEFAULT_START_DATE, time_zone_normal_parsable, 'DATE_TIME',
timezone('Europe/Paris'), noop_gettext, mock_strftime_localized),
"DATE_TIME " + "2016-03-27 01:59:00 CET"
),
# Test correctly formatted start datetime is returned during daylight savings hours
TestScenario(
(DEFAULT_START_DATE, time_zone_daylight_parsable, 'DATE_TIME',
timezone('Europe/Paris'), noop_gettext, mock_strftime_localized),
"DATE_TIME " + "2016-03-27 03:00:00 CEST"
)
]),
FunctionTest(course_end_datetime_text, [
# Test with a set end datetime.
# Expect formatted datetime to be returned.
TestScenario(
(test_datetime, 'TIME', utc, mock_strftime_localized),
mock_strftime_localized(test_datetime, 'TIME') + " UTC"
),
# Test with default end datetime.
# Expect empty string to be returned.
TestScenario(
(None, 'TIME', utc, mock_strftime_localized),
""
),
# Test correctly formatted end datetime is returned during normal daylight hours
TestScenario(
(time_zone_normal_datetime, 'TIME', timezone('Europe/Paris'), mock_strftime_localized),
"TIME " + "2016-03-27 01:59:00 CET"
),
# Test correctly formatted end datetime is returned during daylight savings hours
TestScenario(
(time_zone_daylight_datetime, 'TIME', timezone('Europe/Paris'), mock_strftime_localized),
"TIME " + "2016-03-27 03:00:00 CEST"
)
]),
FunctionTest(may_certify_for_course, [
TestScenario(('early_with_info', True, True), True),
TestScenario(('early_no_info', False, False), True),
Expand Down
62 changes: 1 addition & 61 deletions common/lib/xmodule/xmodule/tests/test_course_module.py
Expand Up @@ -6,7 +6,7 @@
import itertools
from fs.memoryfs import MemoryFS
from mock import Mock, patch
from pytz import timezone, utc
from pytz import utc
from xblock.runtime import KvsFieldData, DictKeyValueStore

import xmodule.course_module
Expand Down Expand Up @@ -209,36 +209,6 @@ def test_sorting_score(self, gmtime_mock):
(xmodule.course_module.CourseFields.start.default, 'January 2014', 'January 2014', False, 'January 2014'),
]

@patch('xmodule.course_metadata_utils.datetime.now')
def test_start_date_text(self, gmtime_mock):
gmtime_mock.return_value = NOW
for s in self.start_advertised_settings:
d = get_dummy_course(start=s[0], advertised_start=s[1])
print "Checking start=%s advertised=%s" % (s[0], s[1])
self.assertEqual(d.start_datetime_text(), s[2])

@patch('xmodule.course_metadata_utils.datetime.now')
def test_start_date_time_text(self, gmtime_mock):
gmtime_mock.return_value = NOW
for setting in self.start_advertised_settings:
course = get_dummy_course(start=setting[0], advertised_start=setting[1])
print "Checking start=%s advertised=%s" % (setting[0], setting[1])
self.assertEqual(course.start_datetime_text("DATE_TIME"), setting[4])

@ddt.data(("2015-11-01T08:59", 'Nov 01, 2015', u'Nov 01, 2015 at 01:59 PDT'),
("2015-11-01T09:00", 'Nov 01, 2015', u'Nov 01, 2015 at 01:00 PST'))
@ddt.unpack
def test_start_date_time_zone(self, course_date, expected_short_date, expected_date_time):
"""
Test that start datetime text correctly formats datetimes
for normal daylight hours and daylight savings hours
"""
time_zone = timezone('America/Los_Angeles')

course = get_dummy_course(start=course_date, advertised_start=course_date)
self.assertEqual(course.start_datetime_text(time_zone=time_zone), expected_short_date)
self.assertEqual(course.start_datetime_text("DATE_TIME", time_zone), expected_date_time)

def test_start_date_is_default(self):
for s in self.start_advertised_settings:
d = get_dummy_course(start=s[0], advertised_start=s[1])
Expand Down Expand Up @@ -276,36 +246,6 @@ def test_is_newish(self):
descriptor = get_dummy_course(start='2012-12-31T12:00')
assert descriptor.is_newish is True

def test_end_date_text(self):
# No end date set, returns empty string.
d = get_dummy_course('2012-12-02T12:00')
self.assertEqual('', d.end_datetime_text())

d = get_dummy_course('2012-12-02T12:00', end='2014-9-04T12:00')
self.assertEqual('Sep 04, 2014', d.end_datetime_text())

def test_end_date_time_text(self):
# No end date set, returns empty string.
course = get_dummy_course('2012-12-02T12:00')
self.assertEqual('', course.end_datetime_text("DATE_TIME"))

course = get_dummy_course('2012-12-02T12:00', end='2014-9-04T12:00')
self.assertEqual('Sep 04, 2014 at 12:00 UTC', course.end_datetime_text("DATE_TIME"))

@ddt.data(("2015-11-01T08:59", 'Nov 01, 2015', u'Nov 01, 2015 at 01:59 PDT'),
("2015-11-01T09:00", 'Nov 01, 2015', u'Nov 01, 2015 at 01:00 PST'))
@ddt.unpack
def test_end_date_time_zone(self, course_date, expected_short_date, expected_date_time):
"""
Test that end datetime text correctly formats datetimes
for normal daylight hours and daylight savings hours
"""
time_zone = timezone('America/Los_Angeles')
course = get_dummy_course(course_date, end=course_date)

self.assertEqual(course.end_datetime_text(time_zone=time_zone), expected_short_date)
self.assertEqual(course.end_datetime_text("DATE_TIME", time_zone), expected_date_time)


class DiscussionTopicsTestCase(unittest.TestCase):
def test_default_discussion_topics(self):
Expand Down
31 changes: 0 additions & 31 deletions lms/djangoapps/ccx/models.py
Expand Up @@ -12,7 +12,6 @@
from lazy import lazy

from ccx_keys.locator import CCXLocator
from openedx.core.lib.time_zone_utils import get_time_zone_abbr
from openedx.core.djangoapps.xmodule_django.models import CourseKeyField, LocationKeyField
from xmodule.error_module import ErrorDescriptor
from xmodule.modulestore.django import modulestore
Expand Down Expand Up @@ -84,36 +83,6 @@ def has_ended(self):

return datetime.now(utc) > self.due

def start_datetime_text(self, format_string="SHORT_DATE", time_zone=utc):
"""Returns the desired text representation of the CCX start datetime
The returned value is in specified time zone, defaulted to UTC.
"""
i18n = self.course.runtime.service(self.course, "i18n")
strftime = i18n.strftime
value = strftime(self.start.astimezone(time_zone), format_string)
if format_string == 'DATE_TIME':
value += ' ' + get_time_zone_abbr(time_zone, self.start)
return value

def end_datetime_text(self, format_string="SHORT_DATE", time_zone=utc):
"""Returns the desired text representation of the CCX due datetime
If the due date for the CCX is not set, the value returned is the empty
string.
The returned value is in specified time zone, defaulted to UTC.
"""
if self.due is None:
return ''

i18n = self.course.runtime.service(self.course, "i18n")
strftime = i18n.strftime
value = strftime(self.due.astimezone(time_zone), format_string)
if format_string == 'DATE_TIME':
value += ' ' + get_time_zone_abbr(time_zone, self.due)
return value

@property
def structure(self):
"""
Expand Down

0 comments on commit b0bf19a

Please sign in to comment.