From 88c7acdfe87ba89afa10428c2e18e8979870f3e3 Mon Sep 17 00:00:00 2001 From: Usman Khalid Date: Fri, 6 Dec 2013 18:40:23 +0500 Subject: [PATCH] If unable to send submission/feedback to xqueue return an error instead of silently failing. ORA-197 --- .../open_ended_module.py | 44 +++++++++--- .../xmodule/tests/test_combined_open_ended.py | 70 +++++++++++++++++-- 2 files changed, 96 insertions(+), 18 deletions(-) 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 68fad9cefaf8..e23283551072 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 @@ def message_post(self, data, system): '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 @@ def send_to_grader(self, submission, system): }) # 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 @@ def send_to_grader(self, submission, system): '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): """ @@ -668,17 +683,24 @@ def save_answer(self, data, system): 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 6498c82c4ab3..e30c59a6d1a5 100644 --- a/common/lib/xmodule/xmodule/tests/test_combined_open_ended.py +++ b/common/lib/xmodule/xmodule/tests/test_combined_open_ended.py @@ -253,7 +253,7 @@ def setUp(self): 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 @@ -286,7 +286,32 @@ def test_message_post(self): 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) def test_send_to_grader(self): submission = "This is a student submission" @@ -299,10 +324,41 @@ def test_send_to_grader(self): '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", @@ -377,7 +433,7 @@ def test_open_ended_display(self): 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", @@ -901,7 +957,7 @@ def get_module_system(self, descriptor): 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 @@ -1062,7 +1118,7 @@ def get_module_system(self, descriptor): 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 @@ -1136,7 +1192,7 @@ def get_module_system(self, descriptor): 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