Commit f9cca8be by Diana Huang

Starting a framework for handling the closure of an open ended problem.

Currently incomplete.
parent 01fab2ec
...@@ -188,6 +188,15 @@ class CombinedOpenEndedModule(XModule): ...@@ -188,6 +188,15 @@ class CombinedOpenEndedModule(XModule):
self.task_xml = definition['task_xml'] self.task_xml = definition['task_xml']
self.setup_next_task() self.setup_next_task()
def closed(self):
return True
#''' Is the student still allowed to submit answers? '''
#if self.attempts == self.max_attempts:
# return True
#if self.close_date is not None and datetime.datetime.utcnow() > self.close_date:
# return True
def get_tag_name(self, xml): def get_tag_name(self, xml):
""" """
Gets the tag name of a given xml block. Gets the tag name of a given xml block.
...@@ -269,7 +278,7 @@ class CombinedOpenEndedModule(XModule): ...@@ -269,7 +278,7 @@ class CombinedOpenEndedModule(XModule):
self.current_task_parsed_xml = self.current_task_descriptor.definition_from_xml(etree_xml, self.system) self.current_task_parsed_xml = self.current_task_descriptor.definition_from_xml(etree_xml, self.system)
if current_task_state is None and self.current_task_number == 0: if current_task_state is None and self.current_task_number == 0:
self.current_task = child_task_module(self.system, self.location, self.current_task = child_task_module(self.system, self.location,
self.current_task_parsed_xml, self.current_task_descriptor, self.static_data) self.current_task_parsed_xml, self.current_task_descriptor, self.static_data, self)
self.task_states.append(self.current_task.get_instance_state()) self.task_states.append(self.current_task.get_instance_state())
self.state = self.ASSESSING self.state = self.ASSESSING
elif current_task_state is None and self.current_task_number > 0: elif current_task_state is None and self.current_task_number > 0:
...@@ -285,7 +294,7 @@ class CombinedOpenEndedModule(XModule): ...@@ -285,7 +294,7 @@ class CombinedOpenEndedModule(XModule):
}) })
self.current_task = child_task_module(self.system, self.location, self.current_task = child_task_module(self.system, self.location,
self.current_task_parsed_xml, self.current_task_descriptor, self.static_data, self.current_task_parsed_xml, self.current_task_descriptor, self.static_data,
instance_state=current_task_state) self, instance_state=current_task_state)
self.task_states.append(self.current_task.get_instance_state()) self.task_states.append(self.current_task.get_instance_state())
self.state = self.ASSESSING self.state = self.ASSESSING
else: else:
...@@ -293,10 +302,11 @@ class CombinedOpenEndedModule(XModule): ...@@ -293,10 +302,11 @@ class CombinedOpenEndedModule(XModule):
current_task_state = self.overwrite_state(current_task_state) current_task_state = self.overwrite_state(current_task_state)
self.current_task = child_task_module(self.system, self.location, self.current_task = child_task_module(self.system, self.location,
self.current_task_parsed_xml, self.current_task_descriptor, self.static_data, self.current_task_parsed_xml, self.current_task_descriptor, self.static_data,
instance_state=current_task_state) self, instance_state=current_task_state)
return True return True
def check_allow_reset(self): def check_allow_reset(self):
""" """
Checks to see if the student has passed the criteria to move to the next module. If not, sets Checks to see if the student has passed the criteria to move to the next module. If not, sets
...@@ -404,7 +414,7 @@ class CombinedOpenEndedModule(XModule): ...@@ -404,7 +414,7 @@ class CombinedOpenEndedModule(XModule):
task_parsed_xml = task_descriptor.definition_from_xml(etree_xml, self.system) task_parsed_xml = task_descriptor.definition_from_xml(etree_xml, self.system)
task = children['modules'][task_type](self.system, self.location, task_parsed_xml, task_descriptor, task = children['modules'][task_type](self.system, self.location, task_parsed_xml, task_descriptor,
self.static_data, instance_state=task_state) self.static_data, self, instance_state=task_state)
last_response = task.latest_answer() last_response = task.latest_answer()
last_score = task.latest_score() last_score = task.latest_score()
last_post_assessment = task.latest_post_assessment(self.system) last_post_assessment = task.latest_post_assessment(self.system)
...@@ -700,4 +710,4 @@ class CombinedOpenEndedDescriptor(XmlDescriptor, EditingDescriptor): ...@@ -700,4 +710,4 @@ class CombinedOpenEndedDescriptor(XmlDescriptor, EditingDescriptor):
for child in ['task']: for child in ['task']:
add_child(child) add_child(child)
return elt return elt
\ No newline at end of file
...@@ -548,13 +548,12 @@ class OpenEndedModule(openendedchild.OpenEndedChild): ...@@ -548,13 +548,12 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
@param system: modulesystem @param system: modulesystem
@return: Success indicator @return: Success indicator
""" """
if self.attempts > self.max_attempts: # Once we close the problem, we should not allow students
# If too many attempts, prevent student from saving answer and # to save answers
# seeing rubric. In normal use, students shouldn't see this because if self.closed():
# they won't see the reset button once they're out of attempts.
return { return {
'success': False, 'success': False,
'error': 'Too many attempts.' 'error': 'Problem is closed.'
} }
if self.state != self.INITIAL: if self.state != self.INITIAL:
......
...@@ -73,7 +73,7 @@ class OpenEndedChild(object): ...@@ -73,7 +73,7 @@ class OpenEndedChild(object):
'done': 'Problem complete', 'done': 'Problem complete',
} }
def __init__(self, system, location, definition, descriptor, static_data, def __init__(self, system, location, definition, descriptor, static_data, parent,
instance_state=None, shared_state=None, **kwargs): instance_state=None, shared_state=None, **kwargs):
# Load instance state # Load instance state
if instance_state is not None: if instance_state is not None:
...@@ -87,6 +87,8 @@ class OpenEndedChild(object): ...@@ -87,6 +87,8 @@ class OpenEndedChild(object):
# Scores are on scale from 0 to max_score # Scores are on scale from 0 to max_score
self.history = instance_state.get('history', []) self.history = instance_state.get('history', [])
self.parent = parent
self.state = instance_state.get('state', self.INITIAL) self.state = instance_state.get('state', self.INITIAL)
self.created = instance_state.get('created', False) self.created = instance_state.get('created', False)
...@@ -116,6 +118,9 @@ class OpenEndedChild(object): ...@@ -116,6 +118,9 @@ class OpenEndedChild(object):
""" """
pass pass
def closed(self):
return self.parent.closed()
def latest_answer(self): def latest_answer(self):
"""Empty string if not available""" """Empty string if not available"""
if not self.history: if not self.history:
......
...@@ -189,14 +189,11 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): ...@@ -189,14 +189,11 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
Dictionary with keys 'success' and either 'error' (if not success), Dictionary with keys 'success' and either 'error' (if not success),
or 'rubric_html' (if success). or 'rubric_html' (if success).
""" """
# Check to see if attempts are less than max # Check to see if this problem is closed
if self.attempts > self.max_attempts: if self.closed():
# If too many attempts, prevent student from saving answer and
# seeing rubric. In normal use, students shouldn't see this because
# they won't see the reset button once they're out of attempts.
return { return {
'success': False, 'success': False,
'error': 'Too many attempts.' 'error': 'This problem is now closed.'
} }
if self.state != self.INITIAL: if self.state != self.INITIAL:
......
...@@ -43,11 +43,14 @@ class OpenEndedChildTest(unittest.TestCase): ...@@ -43,11 +43,14 @@ class OpenEndedChildTest(unittest.TestCase):
'accept_file_upload' : False, 'accept_file_upload' : False,
} }
definition = Mock() definition = Mock()
parent = Mock()
parent.closed.return_value = False;
descriptor = Mock() descriptor = Mock()
def setUp(self): def setUp(self):
self.openendedchild = OpenEndedChild(test_system, self.location, self.openendedchild = OpenEndedChild(test_system, self.location,
self.definition, self.descriptor, self.static_data, self.metadata) self.definition, self.descriptor, self.static_data,
self.parent, self.metadata)
def test_latest_answer_empty(self): def test_latest_answer_empty(self):
...@@ -166,6 +169,8 @@ class OpenEndedModuleTest(unittest.TestCase): ...@@ -166,6 +169,8 @@ class OpenEndedModuleTest(unittest.TestCase):
''') ''')
definition = {'oeparam': oeparam} definition = {'oeparam': oeparam}
descriptor = Mock() descriptor = Mock()
parent = Mock()
parent.closed.return_value = False;
def setUp(self): def setUp(self):
test_system.location = self.location test_system.location = self.location
...@@ -173,7 +178,8 @@ class OpenEndedModuleTest(unittest.TestCase): ...@@ -173,7 +178,8 @@ class OpenEndedModuleTest(unittest.TestCase):
self.mock_xqueue.send_to_queue.return_value=(None, "Message") self.mock_xqueue.send_to_queue.return_value=(None, "Message")
test_system.xqueue = {'interface':self.mock_xqueue, 'callback_url':'/', 'default_queuename': 'testqueue', 'waittime': 1} test_system.xqueue = {'interface':self.mock_xqueue, 'callback_url':'/', 'default_queuename': 'testqueue', 'waittime': 1}
self.openendedmodule = OpenEndedModule(test_system, self.location, self.openendedmodule = OpenEndedModule(test_system, self.location,
self.definition, self.descriptor, self.static_data, self.metadata) self.definition, self.descriptor, self.static_data,
self.parent, self.metadata)
def test_message_post(self): def test_message_post(self):
get = {'feedback': 'feedback text', get = {'feedback': 'feedback text',
......
...@@ -30,6 +30,8 @@ class SelfAssessmentTest(unittest.TestCase): ...@@ -30,6 +30,8 @@ class SelfAssessmentTest(unittest.TestCase):
metadata = {'attempts': '10'} metadata = {'attempts': '10'}
descriptor = Mock() descriptor = Mock()
parent = Mock()
parent.closed.return_value = False
def setUp(self): def setUp(self):
state = json.dumps({'student_answers': ["Answer 1", "answer 2", "answer 3"], state = json.dumps({'student_answers': ["Answer 1", "answer 2", "answer 3"],
...@@ -49,7 +51,8 @@ class SelfAssessmentTest(unittest.TestCase): ...@@ -49,7 +51,8 @@ class SelfAssessmentTest(unittest.TestCase):
self.module = SelfAssessmentModule(test_system, self.location, self.module = SelfAssessmentModule(test_system, self.location,
self.definition, self.descriptor, self.definition, self.descriptor,
static_data, state, metadata=self.metadata) static_data, self.parent,
state, metadata=self.metadata)
def test_get_html(self): def test_get_html(self):
html = self.module.get_html(test_system) html = self.module.get_html(test_system)
...@@ -72,6 +75,7 @@ class SelfAssessmentTest(unittest.TestCase): ...@@ -72,6 +75,7 @@ class SelfAssessmentTest(unittest.TestCase):
# if we now assess as right, skip the REQUEST_HINT state # if we now assess as right, skip the REQUEST_HINT state
self.module.save_answer({'student_answer': 'answer 4'}, test_system) self.module.save_answer({'student_answer': 'answer 4'}, test_system)
self.parent.closed.assert_called_with()
self.module.save_assessment({'assessment': '1'}, test_system) self.module.save_assessment({'assessment': '1'}, test_system)
self.assertEqual(self.module.state, self.module.DONE) self.assertEqual(self.module.state, self.module.DONE)
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