diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 42156ede40..9699ec0145 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,6 +5,9 @@ These are notable changes in edx-platform. This is a rolling list of changes, in roughly chronological order, most recent first. Add your entries at or near the top. Include a label indicating the component affected. +Blades: Handle situation if no response were sent from XQueue to LMS in Matlab +problem after Run Code button press. BLD-994. + Blades: Set initial video quality to large instead of default to avoid automatic switch to HD when iframe resizes. BLD-981. Blades: Add an upload button for authors to provide students with an option to diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index a0631a12db..f66abf9134 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -39,6 +39,7 @@ graded status as'status' # makes sense, but a bunch of problems have markup that assumes block. Bigger TODO: figure out a # general css and layout strategy for capa, document it, then implement it. +import time import json import logging from lxml import etree @@ -53,6 +54,7 @@ from .registry import TagRegistry from chem import chemcalc from calc.preview import latex_preview import xqueue_interface +from xqueue_interface import XQUEUE_TIMEOUT from datetime import datetime from xmodule.stringify import stringify_children @@ -821,6 +823,15 @@ class MatlabInput(CodeInput): self.status = 'queued' self.queue_len = 1 self.msg = self.submitted_msg + # Handle situation if no response from xqueue arrived during specified time. + if ('queuetime' not in self.input_state or + time.time() - self.input_state['queuetime'] > XQUEUE_TIMEOUT): + self.queue_len = 0 + self.status = 'unsubmitted' + self.msg = _( + 'No response from Xqueue within {xqueue_timeout} seconds. Aborted.' + ).format(xqueue_timeout=XQUEUE_TIMEOUT) + def handle_ajax(self, dispatch, data): """ @@ -945,6 +956,7 @@ class MatlabInput(CodeInput): if error == 0: self.input_state['queuekey'] = queuekey self.input_state['queuestate'] = 'queued' + self.input_state['queuetime'] = time.time() return {'success': error == 0, 'message': msg} diff --git a/common/lib/capa/capa/tests/test_inputtypes.py b/common/lib/capa/capa/tests/test_inputtypes.py index cba14ff0f2..c1240c73bf 100644 --- a/common/lib/capa/capa/tests/test_inputtypes.py +++ b/common/lib/capa/capa/tests/test_inputtypes.py @@ -28,6 +28,8 @@ from capa import inputtypes from mock import ANY, patch from pyparsing import ParseException +from capa.xqueue_interface import XQUEUE_TIMEOUT + # just a handy shortcut lookup_tag = inputtypes.registry.get_class_for_tag @@ -524,10 +526,11 @@ class MatlabTest(unittest.TestCase): self.assertEqual(context, expected) - def test_rendering_while_queued(self): + @patch('capa.inputtypes.time.time', return_value=10) + def test_rendering_while_queued(self, time): state = {'value': 'print "good evening"', 'status': 'incomplete', - 'input_state': {'queuestate': 'queued'}, + 'input_state': {'queuestate': 'queued', 'queuetime': 5}, } elt = etree.fromstring(self.xml) @@ -576,9 +579,10 @@ class MatlabTest(unittest.TestCase): self.assertTrue('queuekey' not in self.the_input.input_state) self.assertTrue('queuestate' not in self.the_input.input_state) - def test_ungraded_response_success(self): + @patch('capa.inputtypes.time.time', return_value=10) + def test_ungraded_response_success(self, time): queuekey = 'abcd' - input_state = {'queuekey': queuekey, 'queuestate': 'queued'} + input_state = {'queuekey': queuekey, 'queuestate': 'queued', 'queuetime': 5} state = {'value': 'print "good evening"', 'status': 'incomplete', 'input_state': input_state, @@ -594,9 +598,10 @@ class MatlabTest(unittest.TestCase): self.assertTrue(input_state['queuestate'] is None) self.assertEqual(input_state['queue_msg'], inner_msg) - def test_ungraded_response_key_mismatch(self): + @patch('capa.inputtypes.time.time', return_value=10) + def test_ungraded_response_key_mismatch(self, time): queuekey = 'abcd' - input_state = {'queuekey': queuekey, 'queuestate': 'queued'} + input_state = {'queuekey': queuekey, 'queuestate': 'queued', 'queuetime': 5} state = {'value': 'print "good evening"', 'status': 'incomplete', 'input_state': input_state, @@ -612,6 +617,41 @@ class MatlabTest(unittest.TestCase): self.assertEqual(input_state['queuestate'], 'queued') self.assertFalse('queue_msg' in input_state) + @patch('capa.inputtypes.time.time', return_value=20) + def test_matlab_response_timeout_not_exceeded(self, time): + + state = {'input_state': {'queuestate': 'queued', 'queuetime': 5}} + elt = etree.fromstring(self.xml) + + the_input = self.input_class(test_capa_system(), elt, state) + context = the_input._get_render_context() + self.assertEqual(the_input.status, 'queued') + + + @patch('capa.inputtypes.time.time', return_value=45) + def test_matlab_response_timeout_exceeded(self, time): + + state = {'input_state': {'queuestate': 'queued', 'queuetime': 5}} + elt = etree.fromstring(self.xml) + + the_input = self.input_class(test_capa_system(), elt, state) + context = the_input._get_render_context() + self.assertEqual(the_input.status, 'unsubmitted') + self.assertEqual(the_input.msg, 'No response from Xqueue within {} seconds. Aborted.'.format(XQUEUE_TIMEOUT)) + + @patch('capa.inputtypes.time.time', return_value=20) + def test_matlab_response_migration_of_queuetime(self, time): + """ + Test if problem was saved before queuetime was introduced. + """ + state = {'input_state': {'queuestate': 'queued'}} + elt = etree.fromstring(self.xml) + + the_input = self.input_class(test_capa_system(), elt, state) + context = the_input._get_render_context() + self.assertEqual(the_input.status, 'unsubmitted') + + def test_get_html(self): # usual output output = self.the_input.get_html() @@ -651,7 +691,7 @@ class MatlabTest(unittest.TestCase): queue_msg = textwrap.dedent("""