From db57dc30ebe6683f3ef800a0462da6ae46688189 Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Sat, 4 Feb 2012 11:53:30 -0500 Subject: [PATCH] Changed due date to be display_due_date and close_date. Controlled with due and graceperiod attributes --- courseware/content_parser.py | 25 +++++++++++++++++++++---- courseware/modules/capa_module.py | 24 ++++++++++++++++-------- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/courseware/content_parser.py b/courseware/content_parser.py index ff61f5cdc0..db14618067 100644 --- a/courseware/content_parser.py +++ b/courseware/content_parser.py @@ -1,7 +1,9 @@ -import json import hashlib +import json import logging +import re +from datetime import timedelta from lxml import etree from mako.template import Template from mako.lookup import TemplateLookup @@ -20,6 +22,20 @@ TODO: Shift everything from xml.dom.minidom to XPath (or XQuery) log = logging.getLogger("mitx.courseware") + +timedelta_regex = re.compile(r'^((?P\d+?) day(?:s?))?(\s)?((?P\d+?) hour(?:s?))?(\s)?((?P\d+?) minute(?:s)?)?(\s)?((?P\d+?) second(?:s)?)?$') + +def parse_timedelta(time_str): + parts = timedelta_regex.match(time_str) + if not parts: + return + parts = parts.groupdict() + time_params = {} + for (name, param) in parts.iteritems(): + if param: + time_params[name] = int(param) + return timedelta(**time_params) + def fasthash(string): m = hashlib.new("md4") m.update(string) @@ -98,7 +114,7 @@ def propogate_downward_tag(element, attribute_name, parent_attribute = None): child (A) already has that attribute, A will keep the same attribute and all of A's children will inherit A's attribute. This is a recursive call.''' - if (parent_attribute == None): #This is the entry call. Select all due elements + if (parent_attribute == None): #This is the entry call. Select all elements with this attribute all_attributed_elements = element.xpath("//*[@" + attribute_name +"]") for attributed_element in all_attributed_elements: attribute_value = attributed_element.get(attribute_name) @@ -106,7 +122,7 @@ def propogate_downward_tag(element, attribute_name, parent_attribute = None): propogate_downward_tag(child_element, attribute_name, attribute_value) else: '''The hack below is because we would get _ContentOnlyELements from the - iterator that can't have due dates set. We can't find API for it. If we + iterator that can't have attributes set. We can't find API for it. If we ever have an element which subclasses BaseElement, we will not tag it''' if not element.get(attribute_name) and type(element) == etree._Element: element.set(attribute_name, parent_attribute) @@ -133,6 +149,7 @@ def course_file(user): id_tag(tree) propogate_downward_tag(tree, "due") propogate_downward_tag(tree, "graded") + propogate_downward_tag(tree, "graceperiod") return tree def module_xml(coursefile, module, id_tag, module_id): @@ -170,7 +187,7 @@ def toc_from_xml(dom, active_chapter, active_section): sections.append({'name':s.get("name") or "", 'time':s.get("time") or "", 'format':s.get("format") or "", - 'due':s.get("due") or "", + 'due':s.get("display_due_date") or "", 'active':(c.get("name")==active_chapter and \ s.get("name")==active_section)}) ch.append({'name':c.get("name"), diff --git a/courseware/modules/capa_module.py b/courseware/modules/capa_module.py index 380117d9a0..830a915142 100644 --- a/courseware/modules/capa_module.py +++ b/courseware/modules/capa_module.py @@ -119,18 +119,26 @@ class LoncapaModule(XModule): self.attempts = 0 self.max_attempts = None - self.due_date = None - + dom2 = etree.fromstring(xml) self.explanation=content_parser.item(dom2.xpath('/problem/@explain'), default="closed") self.explain_available=content_parser.item(dom2.xpath('/problem/@explain_available')) - self.due_date=content_parser.item(dom2.xpath('/problem/@due')) - if len(self.due_date)>0: - self.due_date=dateutil.parser.parse(self.due_date) + display_due_date_string=content_parser.item(dom2.xpath('/problem/@due')) + if len(display_due_date_string)>0: + self.display_due_date=dateutil.parser.parse(display_due_date_string) else: - self.due_date=None + self.display_due_date=None + + + grace_period_string = content_parser.item(dom2.xpath('/problem/@graceperiod')) + if len(grace_period_string)>0 and self.display_due_date: + self.grace_period = content_parser.parse_timedelta(grace_period_string) + self.close_date = self.display_due_date + self.grace_period + else: + self.grace_period = None + self.close_date = self.display_due_date self.max_attempts=content_parser.item(dom2.xpath('/problem/@attempts')) if len(self.max_attempts)>0: @@ -166,7 +174,7 @@ class LoncapaModule(XModule): def handle_ajax(self, dispatch, get): if dispatch=='problem_get': response = self.get_problem(get) - elif False: #self.due_date > + elif False: #self.close_date > return json.dumps({"error":"Past due date"}) elif dispatch=='problem_check': response = self.check_problem(get) @@ -184,7 +192,7 @@ class LoncapaModule(XModule): ''' Is the student still allowed to submit answers? ''' if self.attempts == self.max_attempts: return True - if self.due_date != None and datetime.datetime.utcnow() > self.due_date: + if self.close_date != None and datetime.datetime.utcnow() > self.close_date: return True return False