Commit 16f4a3f9 by cahrens

Convert due date to a Date (vs. a string with no year).

parent b2bda104
......@@ -31,7 +31,7 @@ Feature: Create Subsection
Then I see the correct dates
And I reload the page
Then I see the correct dates
@skip-phantom
Scenario: Delete a subsection
Given I have opened a new course section in Studio
......
......@@ -25,7 +25,7 @@ def i_have_added_a_new_subsection(step):
def i_have_opened_a_new_subsection(step):
step.given('I have opened a new course section in Studio')
step.given('I have added a new subsection')
css_click('span.subsection-name-value')
world.css_click('span.subsection-name-value')
@step('I click the New Subsection link')
......@@ -58,16 +58,16 @@ def i_see_complete_subsection_name_with_quote_in_editor(step):
@step('I have set a release date and due date in different years$')
def test_have_set_dates_in_different_years(step):
set_date_and_time('input#start_date', '12/25/2013', 'input#start_time', '3:00am')
css_click('.set-date')
set_date_and_time('input#due_date', '1/2/2014', 'input#due_time', '4:00am')
world.css_click('.set-date')
set_date_and_time('input#due_date', '01/02/2014', 'input#due_time', '4:00am')
@step('I see the correct dates$')
def i_see_the_correct_dates(step):
assert_equal('12/25/2013', css_find('input#start_date').first.value)
assert_equal('3:00am', css_find('input#start_time').first.value)
assert_equal('1/2/2014', css_find('input#due_date').first.value)
assert_equal('4:00am', css_find('input#due_time').first.value)
assert_equal('12/25/2013', world.css_find('input#start_date').first.value)
assert_equal('3:00am', world.css_find('input#start_time').first.value)
assert_equal('01/02/2014', world.css_find('input#due_date').first.value)
assert_equal('4:00am', world.css_find('input#due_time').first.value)
@step('I mark it as Homework$')
......
......@@ -147,10 +147,6 @@ def compute_unit_state(unit):
return UnitState.public
def get_date_display(date):
return date.strftime("%d %B, %Y at %I:%M %p")
def update_item(location, value):
"""
If value is None, delete the db entry. Otherwise, update it using the correct modulestore.
......
......@@ -6,7 +6,6 @@ import sys
import time
import tarfile
import shutil
from datetime import datetime
from collections import defaultdict
from uuid import uuid4
from path import path
......@@ -47,12 +46,13 @@ from functools import partial
from xmodule.contentstore.django import contentstore
from xmodule.contentstore.content import StaticContent
from xmodule.util.date_utils import get_default_time_display
from auth.authz import is_user_in_course_group_role, get_users_in_course_group_by_role
from auth.authz import get_user_by_email, add_user_to_course_group, remove_user_from_course_group
from auth.authz import INSTRUCTOR_ROLE_NAME, STAFF_ROLE_NAME, create_all_course_groups
from .utils import get_course_location_for_item, get_lms_link_for_item, compute_unit_state, \
get_date_display, UnitState, get_course_for_item, get_url_reverse
UnitState, get_course_for_item, get_url_reverse
from xmodule.modulestore.xml_importer import import_from_xml
from contentstore.course_info_model import get_course_updates, \
......@@ -253,6 +253,12 @@ def edit_subsection(request, location):
can_view_live = True
break
# item.lms.start is a struct_time using GMT
# item.lms.due is a String, 'March 20 17:00'
# edit_subsection.html, due is converted to dateutil.parser.parse(item.lms.due) = {datetime} 2013-03-20 17:00:00
#parsed_due_date = dateutil.parser.parse(item.lms.due)
return render_to_response('edit_subsection.html',
{'subsection': item,
'context_course': course,
......@@ -374,7 +380,7 @@ def edit_unit(request, location):
'draft_preview_link': preview_lms_link,
'published_preview_link': lms_link,
'subsection': containing_subsection,
'release_date': get_date_display(datetime.fromtimestamp(time.mktime(containing_subsection.lms.start))) if containing_subsection.lms.start is not None else None,
'release_date': get_default_time_display(containing_subsection.lms.start) if containing_subsection.lms.start is not None else None,
'section': containing_section,
'create_new_unit_template': Location('i4x', 'edx', 'templates', 'vertical', 'Empty'),
'unit_state': unit_state,
......@@ -830,7 +836,7 @@ def upload_asset(request, org, course, coursename):
readback = contentstore().find(content.location)
response_payload = {'displayname': content.name,
'uploadDate': get_date_display(readback.last_modified_at),
'uploadDate': get_default_time_display(readback.last_modified_at.timetuple()),
'url': StaticContent.get_url_path_from_location(content.location),
'thumb_url': StaticContent.get_url_path_from_location(thumbnail_location) if thumbnail_content is not None else None,
'msg': 'Upload completed'
......@@ -1402,7 +1408,7 @@ def asset_index(request, org, course, name):
id = asset['_id']
display_info = {}
display_info['displayname'] = asset['displayname']
display_info['uploadDate'] = get_date_display(asset['uploadDate'])
display_info['uploadDate'] = get_default_time_display(asset['uploadDate'].timetuple())
asset_location = StaticContent.compute_location(id['org'], id['course'], id['name'])
display_info['url'] = StaticContent.get_url_path_from_location(asset_location)
......
......@@ -4,6 +4,9 @@ var $modalCover;
var $newComponentItem;
var $changedInput;
var $spinner;
var $newComponentTypePicker;
var $newComponentTemplatePickers;
var $newComponentButton;
$(document).ready(function () {
$body = $('body');
......@@ -228,7 +231,7 @@ function syncReleaseDate(e) {
$("#start_time").val("");
}
function getEdxTimeFromDateTimeVals(date_val, time_val, format) {
function getEdxTimeFromDateTimeVals(date_val, time_val) {
var edxTimeStr = null;
if (date_val != '') {
......@@ -237,20 +240,17 @@ function getEdxTimeFromDateTimeVals(date_val, time_val, format) {
// Note, we are using date.js utility which has better parsing abilities than the built in JS date parsing
var date = Date.parse(date_val + " " + time_val);
if (format == null)
format = 'yyyy-MM-ddTHH:mm';
edxTimeStr = date.toString(format);
edxTimeStr = date.toString('yyyy-MM-ddTHH:mm');
}
return edxTimeStr;
}
function getEdxTimeFromDateTimeInputs(date_id, time_id, format) {
function getEdxTimeFromDateTimeInputs(date_id, time_id) {
var input_date = $('#' + date_id).val();
var input_time = $('#' + time_id).val();
return getEdxTimeFromDateTimeVals(input_date, input_time, format);
return getEdxTimeFromDateTimeVals(input_date, input_time);
}
function autosaveInput(e) {
......@@ -291,10 +291,8 @@ function saveSubsection() {
}
// Piece back together the date/time UI elements into one date/time string
// NOTE: our various "date/time" metadata elements don't always utilize the same formatting string
// so make sure we're passing back the correct format
metadata['start'] = getEdxTimeFromDateTimeInputs('start_date', 'start_time');
metadata['due'] = getEdxTimeFromDateTimeInputs('due_date', 'due_time', 'MMMM dd HH:mm');
metadata['due'] = getEdxTimeFromDateTimeInputs('due_date', 'due_time');
$.ajax({
url: "/save_item",
......@@ -316,8 +314,8 @@ function saveSubsection() {
function createNewUnit(e) {
e.preventDefault();
parent = $(this).data('parent');
template = $(this).data('template');
var parent = $(this).data('parent');
var template = $(this).data('template');
$.post('/clone_item',
{'parent_location': parent,
......
......@@ -4,6 +4,7 @@
import dateutil.parser
import logging
from datetime import datetime
from xmodule.util.date_utils import get_time_struct_display
%>
<%! from django.core.urlresolvers import reverse %>
......@@ -66,12 +67,8 @@
<a href="#" class="set-date">Set a due date</a>
<div class="datepair date-setter">
<p class="date-description">
<%
# due date uses it own formatting for stringifying the date. As with capa_module.py, there's a utility module available for us to use
due_date = dateutil.parser.parse(subsection.lms.due) if subsection.lms.due else None
%>
<input type="text" id="due_date" name="due_date" value="${due_date.strftime('%m/%d/%Y') if due_date is not None else ''}" placeholder="MM/DD/YYYY" class="date" size='15' autocomplete="off"/>
<input type="text" id="due_time" name="due_time" value="${due_date.strftime('%H:%M') if due_date is not None else ''}" placeholder="HH:MM" class="time" size='10' autocomplete="off"/>
<input type="text" id="due_date" name="due_date" value="${get_time_struct_display(subsection.lms.due, '%m/%d/%Y')}" placeholder="MM/DD/YYYY" class="date" size='15' autocomplete="off"/>
<input type="text" id="due_time" name="due_time" value="${get_time_struct_display(subsection.lms.due, '%H:%M')}" placeholder="HH:MM" class="time" size='10' autocomplete="off"/>
<a href="#" class="remove-date">Remove due date</a>
</p>
</div>
......
import cgi
import datetime
import dateutil
import dateutil.parser
import hashlib
import json
import logging
import traceback
import sys
from lxml import etree
from pkg_resources import resource_string
from capa.capa_problem import LoncapaProblem
......@@ -18,8 +15,9 @@ from .progress import Progress
from xmodule.x_module import XModule
from xmodule.raw_module import RawDescriptor
from xmodule.exceptions import NotFoundError
from xblock.core import Integer, Scope, BlockScope, ModelType, String, Boolean, Object, Float
from .fields import Timedelta
from xblock.core import Integer, Scope, String, Boolean, Object, Float
from .fields import Timedelta, Date
from xmodule.util.date_utils import time_to_datetime
log = logging.getLogger("mitx.courseware")
......@@ -86,7 +84,7 @@ class ComplexEncoder(json.JSONEncoder):
class CapaFields(object):
attempts = StringyInteger(help="Number of attempts taken by the student on this problem", default=0, scope=Scope.student_state)
max_attempts = StringyInteger(help="Maximum number of attempts that a student is allowed", scope=Scope.settings)
due = String(help="Date that this problem is due by", scope=Scope.settings)
due = Date(help="Date that this problem is due by", scope=Scope.settings)
graceperiod = Timedelta(help="Amount of time after the due date that submissions will be accepted", scope=Scope.settings)
showanswer = String(help="When to show the problem answer to the student", scope=Scope.settings, default="closed")
force_save_button = Boolean(help="Whether to force the save button to appear on the page", scope=Scope.settings, default=False)
......@@ -124,10 +122,7 @@ class CapaModule(CapaFields, XModule):
def __init__(self, system, location, descriptor, model_data):
XModule.__init__(self, system, location, descriptor, model_data)
if self.due:
due_date = dateutil.parser.parse(self.due)
else:
due_date = None
due_date = time_to_datetime(self.due)
if self.graceperiod is not None and due_date:
self.close_date = due_date + self.graceperiod
......
import json
import logging
from lxml import etree
......@@ -6,9 +5,10 @@ from pkg_resources import resource_string
from xmodule.raw_module import RawDescriptor
from .x_module import XModule
from xblock.core import Integer, Scope, BlockScope, ModelType, String, Boolean, Object, Float, List
from xblock.core import Integer, Scope, String, Boolean, List
from xmodule.open_ended_grading_classes.combined_open_ended_modulev1 import CombinedOpenEndedV1Module, CombinedOpenEndedV1Descriptor
from collections import namedtuple
from .fields import Date
log = logging.getLogger("mitx.courseware")
......@@ -63,7 +63,7 @@ class CombinedOpenEndedFields(object):
scope=Scope.settings)
skip_spelling_checks = Boolean(help="Whether or not to skip initial spelling checks.", default=True,
scope=Scope.settings)
due = String(help="Date that this problem is due by", default=None, scope=Scope.settings)
due = Date(help="Date that this problem is due by", default=None, scope=Scope.settings)
graceperiod = String(help="Amount of time after the due date that submissions will be accepted", default=None,
scope=Scope.settings)
max_score = Integer(help="Maximum score for the problem.", default=1, scope=Scope.settings)
......
......@@ -14,6 +14,7 @@ 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 xmodule.util.date_utils import time_to_datetime
import json
from xblock.core import Scope, List, String, Object, Boolean
......@@ -533,19 +534,17 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
def _sorting_dates(self):
# utility function to get datetime objects for dates used to
# compute the is_new flag and the sorting_score
def to_datetime(timestamp):
return datetime(*timestamp[:6])
announcement = self.announcement
if announcement is not None:
announcement = to_datetime(announcement)
announcement = time_to_datetime(announcement)
try:
start = dateutil.parser.parse(self.advertised_start)
except (ValueError, AttributeError):
start = to_datetime(self.start)
start = time_to_datetime(self.start)
now = to_datetime(time.gmtime())
now = time_to_datetime(time.gmtime())
return announcement, start, now
......
import logging
from lxml import etree
from dateutil import parser
from pkg_resources import resource_string
......@@ -8,6 +7,9 @@ from xmodule.editing_module import EditingDescriptor
from xmodule.x_module import XModule
from xmodule.xml_module import XmlDescriptor
from xblock.core import Scope, Integer, String
from .fields import Date
from xmodule.util.date_utils import time_to_datetime
log = logging.getLogger(__name__)
......@@ -16,7 +18,7 @@ class FolditFields(object):
# default to what Spring_7012x uses
required_level = Integer(default=4, scope=Scope.settings)
required_sublevel = Integer(default=5, scope=Scope.settings)
due = String(help="Date that this problem is due by", scope=Scope.settings, default='')
due = Date(help="Date that this problem is due by", scope=Scope.settings, default='')
show_basic_score = String(scope=Scope.settings, default='false')
show_leaderboard = String(scope=Scope.settings, default='false')
......@@ -36,17 +38,8 @@ class FolditModule(FolditFields, XModule):
required_sublevel="3"
show_leaderboard="false"/>
"""
def parse_due_date():
"""
Pull out the date, or None
"""
s = self.due
if s:
return parser.parse(s)
else:
return None
self.due_time = parse_due_date()
self.due_time = time_to_datetime(self.due)
def is_complete(self):
"""
......@@ -178,8 +171,8 @@ class FolditDescriptor(FolditFields, XmlDescriptor, EditingDescriptor):
@classmethod
def definition_from_xml(cls, xml_object, system):
return ({}, [])
return {}, []
def definition_to_xml(self):
def definition_to_xml(self, resource_fs):
xml_object = etree.Element('foldit')
return xml_object
......@@ -143,11 +143,11 @@ class CombinedOpenEndedV1Module():
self.accept_file_upload = self.instance_state.get('accept_file_upload', ACCEPT_FILE_UPLOAD) in TRUE_DICT
self.skip_basic_checks = self.instance_state.get('skip_spelling_checks', SKIP_BASIC_CHECKS) in TRUE_DICT
display_due_date_string = self.instance_state.get('due', None)
due_date = self.instance_state.get('due', None)
grace_period_string = self.instance_state.get('graceperiod', None)
try:
self.timeinfo = TimeInfo(display_due_date_string, grace_period_string)
self.timeinfo = TimeInfo(due_date, grace_period_string)
except:
log.error("Error parsing due date information in location {0}".format(location))
raise
......
......@@ -6,13 +6,12 @@ from lxml import etree
from datetime import datetime
from pkg_resources import resource_string
from .capa_module import ComplexEncoder
from .stringify import stringify_children
from .x_module import XModule
from xmodule.raw_module import RawDescriptor
from xmodule.modulestore import Location
from xmodule.modulestore.django import modulestore
from .timeinfo import TimeInfo
from xblock.core import Object, Integer, Boolean, String, Scope
from .fields import Date
from xmodule.open_ended_grading_classes.peer_grading_service import PeerGradingService, GradingServiceError, MockPeerGradingService
......@@ -31,7 +30,7 @@ class PeerGradingFields(object):
use_for_single_location = Boolean(help="Whether to use this for a single location or as a panel.", default=USE_FOR_SINGLE_LOCATION, scope=Scope.settings)
link_to_location = String(help="The location this problem is linked to.", default=LINK_TO_LOCATION, scope=Scope.settings)
is_graded = Boolean(help="Whether or not this module is scored.",default=IS_GRADED, scope=Scope.settings)
display_due_date_string = String(help="Due date that should be displayed.", default=None, scope=Scope.settings)
due_date = Date(help="Due date that should be displayed.", default=None, scope=Scope.settings)
grace_period_string = String(help="Amount of grace to give on the due date.", default=None, scope=Scope.settings)
max_grade = Integer(help="The maximum grade that a student can receieve for this problem.", default=MAX_SCORE, scope=Scope.settings)
student_data_for_location = Object(help="Student data for a given peer grading problem.", default=json.dumps({}),scope=Scope.student_state)
......@@ -72,7 +71,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
self._model_data['due'] = due_date
try:
self.timeinfo = TimeInfo(self.display_due_date_string, self.grace_period_string)
self.timeinfo = TimeInfo(self.due_date, self.grace_period_string)
except:
log.error("Error parsing due date information in location {0}".format(location))
raise
......
# Tests for xmodule.util.date_utils
from nose.tools import assert_equals
from xmodule.util import date_utils
import datetime
import time
def test_get_time_struct_display():
assert_equals("", date_utils.get_time_struct_display(None, ""))
test_time = time.struct_time((1992, 3, 12, 15, 3, 30, 1, 71, 0))
assert_equals("03/12/1992", date_utils.get_time_struct_display(test_time, '%m/%d/%Y'))
assert_equals("15:03", date_utils.get_time_struct_display(test_time, '%H:%M'))
def test_get_default_time_display():
assert_equals("", date_utils.get_default_time_display(None))
test_time = time.struct_time((1992, 3, 12, 15, 3, 30, 1, 71, 0))
assert_equals("Mar 12, 1992 at 03:03 PM",
date_utils.get_default_time_display(test_time))
def test_time_to_datetime():
assert_equals(None, date_utils.time_to_datetime(None))
test_time = time.struct_time((1992, 3, 12, 15, 3, 30, 1, 71, 0))
assert_equals(datetime.datetime(1992, 3, 12, 15, 3, 30),
date_utils.time_to_datetime(test_time))
# -*- coding: utf-8 -*-
from path import path
import unittest
from fs.memoryfs import MemoryFS
from lxml import etree
from mock import Mock, patch
from collections import defaultdict
from xmodule.x_module import XMLParsingSystem, XModuleDescriptor
from xmodule.xml_module import is_pointer_tag
from xmodule.errortracker import make_error_tracker
from xmodule.modulestore import Location
from xmodule.modulestore.xml import ImportSystem, XMLModuleStore
from xmodule.modulestore.exceptions import ItemNotFoundError
from xmodule.modulestore.inheritance import compute_inherited_metadata
from xmodule.fields import Date
from .test_export import DATA_DIR
......@@ -137,7 +133,7 @@ class ImportTestCase(BaseCourseTestCase):
- inherited metadata doesn't leak to children.
"""
system = self.get_system()
v = '1 hour'
v = 'March 20 17:00'
url_name = 'test1'
start_xml = '''
<course org="{org}" course="{course}"
......@@ -150,11 +146,11 @@ class ImportTestCase(BaseCourseTestCase):
compute_inherited_metadata(descriptor)
print descriptor, descriptor._model_data
self.assertEqual(descriptor.lms.due, v)
self.assertEqual(descriptor.lms.due, Date().from_json(v))
# Check that the child inherits due correctly
child = descriptor.get_children()[0]
self.assertEqual(child.lms.due, v)
self.assertEqual(child.lms.due, Date().from_json(v))
# Now export and check things
resource_fs = MemoryFS()
......
import dateutil
import dateutil.parser
import datetime
from .timeparse import parse_timedelta
from xmodule.util.date_utils import time_to_datetime
import logging
log = logging.getLogger(__name__)
......@@ -9,7 +7,7 @@ log = logging.getLogger(__name__)
class TimeInfo(object):
"""
This is a simple object that calculates and stores datetime information for an XModule
based on the due date string and the grace period string
based on the due date and the grace period string
So far it parses out three different pieces of time information:
self.display_due_date - the 'official' due date that gets displayed to students
......@@ -17,13 +15,10 @@ class TimeInfo(object):
self.close_date - the real due date
"""
def __init__(self, display_due_date_string, grace_period_string):
if display_due_date_string is not None:
try:
self.display_due_date = dateutil.parser.parse(display_due_date_string)
except ValueError:
log.error("Could not parse due date {0}".format(display_due_date_string))
raise
def __init__(self, due_date, grace_period_string):
if due_date is not None:
self.display_due_date = time_to_datetime(due_date)
else:
self.display_due_date = None
......
import time
import datetime
def get_default_time_display(time_struct):
"""
Converts a time struct to a string representation. This is the default
representation used in Studio and LMS.
It is of the form "Apr 09, 2013 at 04:00 PM".
If None is passed in, an empty string will be returned.
"""
return get_time_struct_display(time_struct, "%b %d, %Y at %I:%M %p")
def get_time_struct_display(time_struct, format):
"""
Converts a time struct to a string based on the given format.
If None is passed in, an empty string will be returned.
"""
return '' if time_struct is None else time.strftime(format, time_struct)
def time_to_datetime(time_struct):
"""
Convert a time struct to a datetime.
If None is passed in, None will be returned.
"""
return datetime.datetime(*time_struct[:6]) if time_struct else None
......@@ -124,17 +124,17 @@ class TestTOC(TestCase):
expected = ([{'active': True, 'sections':
[{'url_name': 'Toy_Videos', 'display_name': u'Toy Videos', 'graded': True,
'format': u'Lecture Sequence', 'due': '', 'active': False},
'format': u'Lecture Sequence', 'due': None, 'active': False},
{'url_name': 'Welcome', 'display_name': u'Welcome', 'graded': True,
'format': '', 'due': '', 'active': False},
'format': '', 'due': None, 'active': False},
{'url_name': 'video_123456789012', 'display_name': 'video 123456789012', 'graded': True,
'format': '', 'due': '', 'active': False},
'format': '', 'due': None, 'active': False},
{'url_name': 'video_4f66f493ac8f', 'display_name': 'video 4f66f493ac8f', 'graded': True,
'format': '', 'due': '', 'active': False}],
'format': '', 'due': None, 'active': False}],
'url_name': 'Overview', 'display_name': u'Overview'},
{'active': False, 'sections':
[{'url_name': 'toyvideo', 'display_name': 'toyvideo', 'graded': True,
'format': '', 'due': '', 'active': False}],
'format': '', 'due': None, 'active': False}],
'url_name': 'secret:magic', 'display_name': 'secret:magic'}])
actual = render.toc_for_course(self.portal_user, request, self.toy_course, chapter, None, model_data_cache)
......@@ -151,17 +151,17 @@ class TestTOC(TestCase):
expected = ([{'active': True, 'sections':
[{'url_name': 'Toy_Videos', 'display_name': u'Toy Videos', 'graded': True,
'format': u'Lecture Sequence', 'due': '', 'active': False},
'format': u'Lecture Sequence', 'due': None, 'active': False},
{'url_name': 'Welcome', 'display_name': u'Welcome', 'graded': True,
'format': '', 'due': '', 'active': True},
'format': '', 'due': None, 'active': True},
{'url_name': 'video_123456789012', 'display_name': 'video 123456789012', 'graded': True,
'format': '', 'due': '', 'active': False},
'format': '', 'due': None, 'active': False},
{'url_name': 'video_4f66f493ac8f', 'display_name': 'video 4f66f493ac8f', 'graded': True,
'format': '', 'due': '', 'active': False}],
'format': '', 'due': None, 'active': False}],
'url_name': 'Overview', 'display_name': u'Overview'},
{'active': False, 'sections':
[{'url_name': 'toyvideo', 'display_name': 'toyvideo', 'graded': True,
'format': '', 'due': '', 'active': False}],
'format': '', 'due': None, 'active': False}],
'url_name': 'secret:magic', 'display_name': 'secret:magic'}])
actual = render.toc_for_course(self.portal_user, request, self.toy_course, chapter, section, model_data_cache)
......
<%! from django.core.urlresolvers import reverse %>
<%! from xmodule.util.date_utils import get_default_time_display %>
<%def name="make_chapter(chapter)">
<div class="chapter">
......@@ -10,7 +11,7 @@
<li class="${'active' if 'active' in section and section['active'] else ''} ${'graded' if 'graded' in section and section['graded'] else ''}">
<a href="${reverse('courseware_section', args=[course_id, chapter['url_name'], section['url_name']])}">
<p>${section['display_name']}</p>
<p class="subtitle">${section['format']} ${"due " + section['due'] if 'due' in section and section['due'] != '' else ''}</p>
<p class="subtitle">${section['format']} ${"due " + get_default_time_display(section['due']) if 'due' in section else ''}</p>
</a>
</li>
% endfor
......
......@@ -13,6 +13,8 @@
from django.core.urlresolvers import reverse
%>
<%! from xmodule.util.date_utils import get_default_time_display %>
<%block name="js_extra">
<script type="text/javascript" src="${static.url('js/vendor/flot/jquery.flot.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/flot/jquery.flot.stack.js')}"></script>
......@@ -62,7 +64,7 @@ ${progress_graph.body(grade_summary, course.grade_cutoffs, "grade-detail-graph",
%if 'due' in section and section['due']!="":
<em>
due ${section['due']}
due ${get_default_time_display(section['due'])}
</em>
%endif
</p>
......
......@@ -51,7 +51,7 @@ class LmsNamespace(Namespace):
)
start = Date(help="Start time when this module is visible", scope=Scope.settings)
due = String(help="Date that this problem is due by", scope=Scope.settings, default='')
due = Date(help="Date that this problem is due by", scope=Scope.settings)
source_file = String(help="DO NOT USE", scope=Scope.settings)
xqa_key = String(help="DO NOT USE", scope=Scope.settings)
ispublic = Boolean(help="Whether this course is open to the public, or only to admins", scope=Scope.settings)
......
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