From f9cca8befdb7794b79486ba2ef9c6132309c0894 Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Mon, 4 Feb 2013 11:51:05 -0500 Subject: [PATCH] Starting a framework for handling the closure of an open ended problem. Currently incomplete. --- .../xmodule/combined_open_ended_module.py | 20 ++++++++++++++----- .../lib/xmodule/xmodule/open_ended_module.py | 9 ++++----- common/lib/xmodule/xmodule/openendedchild.py | 7 ++++++- .../xmodule/xmodule/self_assessment_module.py | 9 +++------ .../xmodule/tests/test_combined_open_ended.py | 10 ++++++++-- .../xmodule/tests/test_self_assessment.py | 6 +++++- 6 files changed, 41 insertions(+), 20 deletions(-) diff --git a/common/lib/xmodule/xmodule/combined_open_ended_module.py b/common/lib/xmodule/xmodule/combined_open_ended_module.py index 14a59c9004..e9b3d1d4d0 100644 --- a/common/lib/xmodule/xmodule/combined_open_ended_module.py +++ b/common/lib/xmodule/xmodule/combined_open_ended_module.py @@ -188,6 +188,15 @@ class CombinedOpenEndedModule(XModule): self.task_xml = definition['task_xml'] 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): """ Gets the tag name of a given xml block. @@ -269,7 +278,7 @@ class CombinedOpenEndedModule(XModule): 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: 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.state = self.ASSESSING elif current_task_state is None and self.current_task_number > 0: @@ -285,7 +294,7 @@ class CombinedOpenEndedModule(XModule): }) self.current_task = child_task_module(self.system, self.location, 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.state = self.ASSESSING else: @@ -293,10 +302,11 @@ class CombinedOpenEndedModule(XModule): current_task_state = self.overwrite_state(current_task_state) self.current_task = child_task_module(self.system, self.location, 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 + def check_allow_reset(self): """ 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): 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, - self.static_data, instance_state=task_state) + self.static_data, self, instance_state=task_state) last_response = task.latest_answer() last_score = task.latest_score() last_post_assessment = task.latest_post_assessment(self.system) @@ -700,4 +710,4 @@ class CombinedOpenEndedDescriptor(XmlDescriptor, EditingDescriptor): for child in ['task']: add_child(child) - return elt \ No newline at end of file + return elt diff --git a/common/lib/xmodule/xmodule/open_ended_module.py b/common/lib/xmodule/xmodule/open_ended_module.py index 94d45d96e3..2d6970e162 100644 --- a/common/lib/xmodule/xmodule/open_ended_module.py +++ b/common/lib/xmodule/xmodule/open_ended_module.py @@ -548,13 +548,12 @@ class OpenEndedModule(openendedchild.OpenEndedChild): @param system: modulesystem @return: Success indicator """ - if self.attempts > self.max_attempts: - # 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. + # Once we close the problem, we should not allow students + # to save answers + if self.closed(): return { 'success': False, - 'error': 'Too many attempts.' + 'error': 'Problem is closed.' } if self.state != self.INITIAL: diff --git a/common/lib/xmodule/xmodule/openendedchild.py b/common/lib/xmodule/xmodule/openendedchild.py index 7151ac0723..8c35fb0cae 100644 --- a/common/lib/xmodule/xmodule/openendedchild.py +++ b/common/lib/xmodule/xmodule/openendedchild.py @@ -73,7 +73,7 @@ class OpenEndedChild(object): '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): # Load instance state if instance_state is not None: @@ -87,6 +87,8 @@ class OpenEndedChild(object): # Scores are on scale from 0 to max_score self.history = instance_state.get('history', []) + self.parent = parent + self.state = instance_state.get('state', self.INITIAL) self.created = instance_state.get('created', False) @@ -116,6 +118,9 @@ class OpenEndedChild(object): """ pass + def closed(self): + return self.parent.closed() + def latest_answer(self): """Empty string if not available""" if not self.history: diff --git a/common/lib/xmodule/xmodule/self_assessment_module.py b/common/lib/xmodule/xmodule/self_assessment_module.py index 38a60e11f5..bb8b46559d 100644 --- a/common/lib/xmodule/xmodule/self_assessment_module.py +++ b/common/lib/xmodule/xmodule/self_assessment_module.py @@ -189,14 +189,11 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): Dictionary with keys 'success' and either 'error' (if not success), or 'rubric_html' (if success). """ - # Check to see if attempts are less than max - if self.attempts > self.max_attempts: - # 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. + # Check to see if this problem is closed + if self.closed(): return { 'success': False, - 'error': 'Too many attempts.' + 'error': 'This problem is now closed.' } if self.state != self.INITIAL: diff --git a/common/lib/xmodule/xmodule/tests/test_combined_open_ended.py b/common/lib/xmodule/xmodule/tests/test_combined_open_ended.py index c89f5ee848..69c502cd5d 100644 --- a/common/lib/xmodule/xmodule/tests/test_combined_open_ended.py +++ b/common/lib/xmodule/xmodule/tests/test_combined_open_ended.py @@ -43,11 +43,14 @@ class OpenEndedChildTest(unittest.TestCase): 'accept_file_upload' : False, } definition = Mock() + parent = Mock() + parent.closed.return_value = False; descriptor = Mock() def setUp(self): 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): @@ -166,6 +169,8 @@ class OpenEndedModuleTest(unittest.TestCase): ''') definition = {'oeparam': oeparam} descriptor = Mock() + parent = Mock() + parent.closed.return_value = False; def setUp(self): test_system.location = self.location @@ -173,7 +178,8 @@ class OpenEndedModuleTest(unittest.TestCase): self.mock_xqueue.send_to_queue.return_value=(None, "Message") test_system.xqueue = {'interface':self.mock_xqueue, 'callback_url':'/', 'default_queuename': 'testqueue', 'waittime': 1} 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): get = {'feedback': 'feedback text', diff --git a/common/lib/xmodule/xmodule/tests/test_self_assessment.py b/common/lib/xmodule/xmodule/tests/test_self_assessment.py index c5fb82e412..74018cf101 100644 --- a/common/lib/xmodule/xmodule/tests/test_self_assessment.py +++ b/common/lib/xmodule/xmodule/tests/test_self_assessment.py @@ -30,6 +30,8 @@ class SelfAssessmentTest(unittest.TestCase): metadata = {'attempts': '10'} descriptor = Mock() + parent = Mock() + parent.closed.return_value = False def setUp(self): state = json.dumps({'student_answers': ["Answer 1", "answer 2", "answer 3"], @@ -49,7 +51,8 @@ class SelfAssessmentTest(unittest.TestCase): self.module = SelfAssessmentModule(test_system, self.location, self.definition, self.descriptor, - static_data, state, metadata=self.metadata) + static_data, self.parent, + state, metadata=self.metadata) def test_get_html(self): html = self.module.get_html(test_system) @@ -72,6 +75,7 @@ class SelfAssessmentTest(unittest.TestCase): # if we now assess as right, skip the REQUEST_HINT state 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.assertEqual(self.module.state, self.module.DONE)