Commit 09d2cf4d by Filippo Valsorda Committed by Xavier Antoviaque

Add string decoding support and tests to xblock.mixin.DateTuple

parent 30a96ddf
...@@ -3,17 +3,50 @@ Mixin defining common Studio functionality ...@@ -3,17 +3,50 @@ Mixin defining common Studio functionality
""" """
import datetime import datetime
import dateutil.parser
import logging
import time import time
from pytz import UTC
from xblock.fields import Scope, Field, Integer, XBlockMixin from xblock.fields import Scope, Field, Integer, XBlockMixin
log = logging.getLogger(__name__)
class DateTuple(Field): class DateTuple(Field):
""" """
Field that stores datetime objects as time tuples Field that stores datetime objects as time tuples
""" """
# See note below about not defaulting these
CURRENT_YEAR = datetime.datetime.now(UTC).year
PREVENT_DEFAULT_DAY_MON_SEED1 = datetime.datetime(CURRENT_YEAR, 1, 1, tzinfo=UTC)
PREVENT_DEFAULT_DAY_MON_SEED2 = datetime.datetime(CURRENT_YEAR, 2, 2, tzinfo=UTC)
MUTABLE = False
def _parse_date_wo_default_month_day(self, field):
"""
Parse the field as an iso string but prevent dateutils from defaulting the day or month while
allowing it to default the other fields.
"""
# It's not trivial to replace dateutil b/c parsing timezones as Z, +03:30, -400 is hard in python
# however, we don't want dateutil to default the month or day (but some tests at least expect
# us to default year); so, we'll see if dateutil uses the defaults for these the hard way
result = dateutil.parser.parse(field, default=self.PREVENT_DEFAULT_DAY_MON_SEED1)
result_other = dateutil.parser.parse(field, default=self.PREVENT_DEFAULT_DAY_MON_SEED2)
if result != result_other:
log.warning("Field {0} is missing month or day".format(self._name, field))
return None
if result.tzinfo is None:
result = result.replace(tzinfo=UTC)
return result
def from_json(self, value): def from_json(self, value):
return datetime.datetime(*value[0:6]) if value is None:
return None
return datetime.datetime(*value[0:6]).replace(tzinfo=UTC)
def to_json(self, value): def to_json(self, value):
if value is None: if value is None:
...@@ -22,13 +55,24 @@ class DateTuple(Field): ...@@ -22,13 +55,24 @@ class DateTuple(Field):
return list(value.timetuple()) return list(value.timetuple())
def enforce_type(self, value): def enforce_type(self, value):
if isinstance(value, datetime.datetime) or value is None: if value == "" or value is None:
return None
if isinstance(value, datetime.datetime):
if value.tzinfo is None:
value = value.replace(tzinfo=UTC)
return value return value
if isinstance(value, (tuple, time.struct_time)): if isinstance(value, tuple):
return datetime.datetime(*value[0:6]) return datetime.datetime(*value[0:6]).replace(tzinfo=UTC)
if isinstance(value, time.struct_time):
return datetime.datetime.fromtimestamp(time.mktime(value), UTC)
if isinstance(value, basestring):
return self._parse_date_wo_default_month_day(value)
raise TypeError("Value should be datetime, a timetuple or None, not {}".format(type(value))) raise TypeError("Value should be datetime, timetuple, str or None, not {}".format(type(value)))
class CmsBlockMixin(XBlockMixin): class CmsBlockMixin(XBlockMixin):
......
import unittest
import datetime
import time
from pytz import UTC
from cms.lib.xblock.mixin import DateTuple
class DateTupleTest(unittest.TestCase):
datetuple = DateTuple()
def test_from_json(self):
'''Test conversion from iso compatible date strings to struct_time'''
self.assertEqual(
DateTupleTest.datetuple.from_json((2014, 5, 9, 21, 1, 27)),
datetime.datetime(2014, 5, 9, 21, 1, 27, tzinfo=UTC))
self.assertIsNone(DateTupleTest.datetuple.from_json(None))
def test_enforce_type(self):
self.assertEqual(DateTupleTest.datetuple.enforce_type(
(2014, 5, 9, 21, 1, 27)),
datetime.datetime(2014, 5, 9, 21, 1, 27, tzinfo=UTC))
self.assertEqual(DateTupleTest.datetuple.enforce_type(
time.struct_time((2014, 5, 9, 21, 1, 27, 0, 0, 0))),
datetime.datetime(2014, 5, 9, 21, 1, 27, tzinfo=UTC))
self.assertEqual(DateTupleTest.datetuple.enforce_type(
datetime.datetime(2014, 5, 9, 21, 1, 27)),
datetime.datetime(2014, 5, 9, 21, 1, 27, tzinfo=UTC))
self.assertEqual(DateTupleTest.datetuple.enforce_type(
"2013-01-01T00:00:00Z"),
datetime.datetime(2013, 1, 1, 00, 00, 00, tzinfo=UTC))
self.assertEqual(DateTupleTest.datetuple.enforce_type(
"2013-01-01 11:44:22"),
datetime.datetime(2013, 1, 1, 11, 44, 22, tzinfo=UTC))
self.assertEqual(DateTupleTest.datetuple.enforce_type(
"2012-12-31T23:59"),
datetime.datetime(2012, 12, 31, 23, 59, 00, tzinfo=UTC))
self.assertIsNone(DateTupleTest.datetuple.enforce_type(None))
self.assertIsNone(DateTupleTest.datetuple.enforce_type(""))
def test_to_json(self):
self.assertEqual(DateTupleTest.datetuple.to_json(
datetime.datetime(2014, 5, 9, 21, 1, 27, tzinfo=UTC))[:6],
[2014, 5, 9, 21, 1, 27])
self.assertIsNone(DateTupleTest.datetuple.to_json(None))
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment