Merge branch 'bug/dhm/date-parse' of github.com:MITx/mitx into bug/dhm/date-parse
Conflicts: cms/djangoapps/models/settings/course_metadata.py cms/urls.py
This commit is contained in:
@@ -1,17 +1,25 @@
|
||||
import time
|
||||
import datetime
|
||||
import re
|
||||
import calendar
|
||||
import dateutil.parser
|
||||
|
||||
|
||||
def time_to_date(time_obj):
|
||||
"""
|
||||
Convert a time.time_struct to a true universal time (can pass to js Date constructor)
|
||||
Convert a time.time_struct to a true universal time (can pass to js Date
|
||||
constructor)
|
||||
"""
|
||||
# TODO change to using the isoformat() function on datetime. js date can parse those
|
||||
return calendar.timegm(time_obj) * 1000
|
||||
|
||||
|
||||
def time_to_isodate(source):
|
||||
'''Convert to an iso date'''
|
||||
if isinstance(source, time.struct_time):
|
||||
return time.strftime('%Y-%m-%dT%H:%M:%SZ', source)
|
||||
elif isinstance(source, datetime):
|
||||
return source.isoformat() + 'Z'
|
||||
|
||||
|
||||
def jsdate_to_time(field):
|
||||
"""
|
||||
Convert a universal time (iso format) or msec since epoch to a time obj
|
||||
@@ -19,8 +27,7 @@ def jsdate_to_time(field):
|
||||
if field is None:
|
||||
return field
|
||||
elif isinstance(field, basestring):
|
||||
# ISO format but ignores time zone assuming it's Z.
|
||||
d = datetime.datetime(*map(int, re.split('[^\d]', field)[:6])) # stop after seconds. Debatable
|
||||
d = dateutil.parser.parse(field)
|
||||
return d.utctimetuple()
|
||||
elif isinstance(field, (int, long, float)):
|
||||
return time.gmtime(field / 1000)
|
||||
|
||||
@@ -12,14 +12,9 @@ from xmodule.seq_module import SequenceDescriptor, SequenceModule
|
||||
from xmodule.timeparse import parse_time
|
||||
from xmodule.util.decorators import lazyproperty
|
||||
from xmodule.graders import grader_from_conf
|
||||
from datetime import datetime
|
||||
import json
|
||||
import logging
|
||||
import requests
|
||||
import time
|
||||
import copy
|
||||
|
||||
from xblock.core import Scope, ModelType, List, String, Object, Boolean
|
||||
from xblock.core import Scope, List, String, Object, Boolean
|
||||
from .fields import Date
|
||||
|
||||
|
||||
@@ -33,26 +28,27 @@ class StringOrDate(Date):
|
||||
if it doesn't parse.
|
||||
Return None if not present or invalid.
|
||||
"""
|
||||
if value is None:
|
||||
return None
|
||||
|
||||
try:
|
||||
return time.strptime(value, self.time_format)
|
||||
result = super(StringOrDate, self).from_json(value)
|
||||
except ValueError:
|
||||
return value
|
||||
if result is None:
|
||||
return value
|
||||
else:
|
||||
return result
|
||||
|
||||
def to_json(self, value):
|
||||
"""
|
||||
Convert a time struct to a string
|
||||
"""
|
||||
if value is None:
|
||||
return None
|
||||
|
||||
try:
|
||||
return time.strftime(self.time_format, value)
|
||||
except (ValueError, TypeError):
|
||||
result = super(StringOrDate, self).to_json(value)
|
||||
except:
|
||||
return value
|
||||
|
||||
if result is None:
|
||||
return value
|
||||
else:
|
||||
return result
|
||||
|
||||
|
||||
edx_xml_parser = etree.XMLParser(dtd_validation=False, load_dtd=False,
|
||||
@@ -60,6 +56,7 @@ edx_xml_parser = etree.XMLParser(dtd_validation=False, load_dtd=False,
|
||||
|
||||
_cached_toc = {}
|
||||
|
||||
|
||||
class Textbook(object):
|
||||
def __init__(self, title, book_url):
|
||||
self.title = title
|
||||
@@ -367,7 +364,7 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
|
||||
textbooks.append((textbook.get('title'), textbook.get('book_url')))
|
||||
xml_object.remove(textbook)
|
||||
|
||||
#Load the wiki tag if it exists
|
||||
# Load the wiki tag if it exists
|
||||
wiki_slug = None
|
||||
wiki_tag = xml_object.find("wiki")
|
||||
if wiki_tag is not None:
|
||||
@@ -675,7 +672,7 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
|
||||
# *end* of the same day, not the same time. It's going to be used as the
|
||||
# end of the exam overall, so we don't want the exam to disappear too soon.
|
||||
# It's also used optionally as the registration end date, so time matters there too.
|
||||
self.last_eligible_appointment_date = self._try_parse_time('Last_Eligible_Appointment_Date') # or self.first_eligible_appointment_date
|
||||
self.last_eligible_appointment_date = self._try_parse_time('Last_Eligible_Appointment_Date') # or self.first_eligible_appointment_date
|
||||
if self.last_eligible_appointment_date is None:
|
||||
raise ValueError("Last appointment date must be specified")
|
||||
self.registration_start_date = self._try_parse_time('Registration_Start_Date') or time.gmtime(0)
|
||||
|
||||
@@ -4,27 +4,35 @@ import re
|
||||
|
||||
from datetime import timedelta
|
||||
from xblock.core import ModelType
|
||||
import datetime
|
||||
import dateutil.parser
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Date(ModelType):
|
||||
time_format = "%Y-%m-%dT%H:%M"
|
||||
|
||||
def from_json(self, value):
|
||||
'''
|
||||
Date fields know how to parse and produce json (iso) compatible formats.
|
||||
'''
|
||||
# NB: these are copies of util.converters.*
|
||||
def from_json(self, field):
|
||||
"""
|
||||
Parse an optional metadata key containing a time: if present, complain
|
||||
if it doesn't parse.
|
||||
Return None if not present or invalid.
|
||||
"""
|
||||
if value is None:
|
||||
return None
|
||||
|
||||
try:
|
||||
return time.strptime(value, self.time_format)
|
||||
except ValueError as e:
|
||||
msg = "Field {0} has bad value '{1}': '{2}'".format(
|
||||
self._name, value, e)
|
||||
if field is None:
|
||||
return field
|
||||
elif isinstance(field, basestring):
|
||||
d = dateutil.parser.parse(field)
|
||||
return d.utctimetuple()
|
||||
elif isinstance(field, (int, long, float)):
|
||||
return time.gmtime(field / 1000)
|
||||
elif isinstance(field, time.struct_time):
|
||||
return field
|
||||
else:
|
||||
msg = "Field {0} has bad value '{1}'".format(
|
||||
self._name, field)
|
||||
log.warning(msg)
|
||||
return None
|
||||
|
||||
@@ -34,8 +42,11 @@ class Date(ModelType):
|
||||
"""
|
||||
if value is None:
|
||||
return None
|
||||
|
||||
return time.strftime(self.time_format, value)
|
||||
if isinstance(value, time.struct_time):
|
||||
# struct_times are always utc
|
||||
return time.strftime('%Y-%m-%dT%H:%M:%SZ', value)
|
||||
elif isinstance(value, datetime.datetime):
|
||||
return value.isoformat() + 'Z'
|
||||
|
||||
|
||||
TIMEDELTA_REGEX = re.compile(r'^((?P<days>\d+?) day(?:s?))?(\s)?((?P<hours>\d+?) hour(?:s?))?(\s)?((?P<minutes>\d+?) minute(?:s)?)?(\s)?((?P<seconds>\d+?) second(?:s)?)?$')
|
||||
@@ -66,4 +77,4 @@ class Timedelta(ModelType):
|
||||
cur_value = getattr(value, attr, 0)
|
||||
if cur_value > 0:
|
||||
values.append("%d %s" % (cur_value, attr))
|
||||
return ' '.join(values)
|
||||
return ' '.join(values)
|
||||
|
||||
Reference in New Issue
Block a user