diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py index 6676eeeb33..04c72540c2 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py @@ -196,20 +196,25 @@ class OpenEndedModule(openendedchild.OpenEndedChild): 'student_info': json.dumps(student_info), } - (error, msg) = qinterface.send_to_queue( + error, error_message = qinterface.send_to_queue( header=xheader, body=json.dumps(contents) ) # Convert error to a success value success = True + message = "Successfully saved your feedback." if error: success = False - - self.child_state = self.DONE + message = "Unable to save your feedback. Please try again later." + log.error("Unable to send feedback to grader. location: {0}, error_message: {1}".format( + self.location_string, error_message + )) + else: + self.child_state = self.DONE # This is a student_facing_message - return {'success': success, 'msg': "Successfully submitted your feedback."} + return {'success': success, 'msg': message} def send_to_grader(self, submission, system): """ @@ -257,7 +262,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild): }) # Submit request. When successful, 'msg' is the prior length of the queue - qinterface.send_to_queue( + error, error_message = qinterface.send_to_queue( header=xheader, body=json.dumps(contents) ) @@ -267,7 +272,17 @@ class OpenEndedModule(openendedchild.OpenEndedChild): 'key': queuekey, 'time': qtime, } - return True + + success = True + message = "Successfully saved your submission." + if error: + success = False + message = 'Unable to send your submission to grader. Please try again later.' + log.error("Unable to submit to grader. location: {0}, error_message: {1}".format( + self.location_string, error_message + )) + + return (success, message) def _update_score(self, score_msg, queuekey, system): """ @@ -671,17 +686,24 @@ class OpenEndedModule(openendedchild.OpenEndedChild): if self.child_state != self.INITIAL: return self.out_of_sync_error(data) + message = "Successfully saved your submission." + # add new history element with answer and empty score and hint. success, error_message, data = self.append_file_link_to_student_answer(data) - if success: + if not success: + message = error_message + else: data['student_answer'] = OpenEndedModule.sanitize_html(data['student_answer']) - self.new_history_entry(data['student_answer']) - self.send_to_grader(data['student_answer'], system) - self.change_state(self.ASSESSING) + success, error_message = self.send_to_grader(data['student_answer'], system) + if not success: + message = error_message + else: + self.new_history_entry(data['student_answer']) + self.change_state(self.ASSESSING) return { 'success': success, - 'error': error_message, + 'error': message, 'student_response': data['student_answer'].replace("\n", "
") } 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 857942864b..e0453963b5 100644 --- a/common/lib/xmodule/xmodule/tests/test_combined_open_ended.py +++ b/common/lib/xmodule/xmodule/tests/test_combined_open_ended.py @@ -254,7 +254,7 @@ class OpenEndedModuleTest(unittest.TestCase): self.test_system.open_ended_grading_interface = None self.test_system.location = self.location self.mock_xqueue = MagicMock() - self.mock_xqueue.send_to_queue.return_value = (None, "Message") + self.mock_xqueue.send_to_queue.return_value = (0, "Queued") def constructed_callback(dispatch="score_update"): return dispatch @@ -290,7 +290,32 @@ class OpenEndedModuleTest(unittest.TestCase): self.mock_xqueue.send_to_queue.assert_called_with(body=json.dumps(contents), header=ANY) state = json.loads(self.openendedmodule.get_instance_state()) - self.assertIsNotNone(state['child_state'], OpenEndedModule.DONE) + self.assertEqual(state['child_state'], OpenEndedModule.DONE) + + def test_message_post_fail(self): + """Test message_post() if unable to send feedback to xqueue.""" + + get = {'feedback': 'feedback text', + 'submission_id': '1', + 'grader_id': '1', + 'score': 3} + qtime = datetime.strftime(datetime.now(UTC), xqueue_interface.dateformat) + student_info = {'anonymous_student_id': self.test_system.anonymous_student_id, + 'submission_time': qtime} + contents = { + 'feedback': get['feedback'], + 'submission_id': int(get['submission_id']), + 'grader_id': int(get['grader_id']), + 'score': get['score'], + 'student_info': json.dumps(student_info) + } + + self.mock_xqueue.send_to_queue.return_value = (1, "Not Queued") + result = self.openendedmodule.message_post(get, self.test_system) + self.assertFalse(result['success']) + + state = json.loads(self.openendedmodule.get_instance_state()) + self.assertNotEqual(state['child_state'], OpenEndedModule.DONE) # Disabled 1/27/14 due to flakiness in master # Should not be comparing the submission time to the current time! @@ -306,10 +331,41 @@ class OpenEndedModuleTest(unittest.TestCase): 'student_response': submission, 'max_score': self.max_score }) - result = self.openendedmodule.send_to_grader(submission, self.test_system) + result, __ = self.openendedmodule.send_to_grader(submission, self.test_system) self.assertTrue(result) self.mock_xqueue.send_to_queue.assert_called_with(body=json.dumps(contents), header=ANY) + def test_send_to_grader_fail(self): + """Test send_to_grader() if unable to send submission to xqueue.""" + + submission = "This is a student submission" + qtime = datetime.strftime(datetime.now(UTC), xqueue_interface.dateformat) + student_info = {'anonymous_student_id': self.test_system.anonymous_student_id, + 'submission_time': qtime} + contents = self.openendedmodule.payload.copy() + contents.update({ + 'student_info': json.dumps(student_info), + 'student_response': submission, + 'max_score': self.max_score + }) + self.mock_xqueue.send_to_queue.return_value = (1, "Not Queued") + result, __ = self.openendedmodule.send_to_grader(submission, self.test_system) + self.assertFalse(result) + + def test_save_answer_fail(self): + """Test save_answer() if unable to send submission to grader.""" + + submission = "This is a student submission" + self.openendedmodule.send_to_grader = Mock(return_value=(False, "Failed")) + response = self.openendedmodule.save_answer( + {"student_answer": submission}, + get_test_system() + ) + self.assertFalse(response['success']) + self.assertNotEqual(self.openendedmodule.latest_answer(), submission) + state = json.loads(self.openendedmodule.get_instance_state()) + self.assertEqual(state['child_state'], OpenEndedModule.INITIAL) + def update_score_single(self): self.openendedmodule.new_history_entry("New Entry") get = {'queuekey': "abcd", @@ -384,7 +440,7 @@ class OpenEndedModuleTest(unittest.TestCase): self.assertEqual(test_module.get_display_answer(), saved_response) # Mock out the send_to_grader function so it doesn't try to connect to the xqueue. - test_module.send_to_grader = Mock(return_value=True) + test_module.send_to_grader = Mock(return_value=(True, "Success")) # Submit a student response to the question. test_module.handle_ajax( "save_answer", @@ -937,7 +993,7 @@ class OpenEndedModuleXmlTest(unittest.TestCase, DummyModulestore): test_system = get_test_system() test_system.open_ended_grading_interface = None test_system.xqueue['interface'] = Mock( - send_to_queue=Mock(side_effect=[1, "queued"]) + send_to_queue=Mock(return_value=(0, "Queued")) ) return test_system @@ -1098,7 +1154,7 @@ class OpenEndedModuleXmlAttemptTest(unittest.TestCase, DummyModulestore): test_system = get_test_system() test_system.open_ended_grading_interface = None test_system.xqueue['interface'] = Mock( - send_to_queue=Mock(side_effect=[1, "queued"]) + send_to_queue=Mock(return_value=(0, "Queued")) ) return test_system @@ -1172,7 +1228,7 @@ class OpenEndedModuleXmlImageUploadTest(unittest.TestCase, DummyModulestore): test_system.open_ended_grading_interface = None test_system.s3_interface = test_util_open_ended.S3_INTERFACE test_system.xqueue['interface'] = Mock( - send_to_queue=Mock(side_effect=[1, "queued"]) + send_to_queue=Mock(return_value=(0, "Queued")) ) return test_system