diff --git a/cms/djangoapps/contentstore/features/problem-editor.py b/cms/djangoapps/contentstore/features/problem-editor.py index a57e5bda01..6d636e28b3 100644 --- a/cms/djangoapps/contentstore/features/problem-editor.py +++ b/cms/djangoapps/contentstore/features/problem-editor.py @@ -14,6 +14,7 @@ PROBLEM_WEIGHT = "Problem Weight" RANDOMIZATION = 'Randomization' SHOW_ANSWER = "Show Answer" TIMER_BETWEEN_ATTEMPTS = "Timer Between Attempts" +MATLAB_API_KEY = "Matlab API key" @step('I have created a Blank Common Problem$') def i_created_blank_common_problem(step): @@ -40,11 +41,12 @@ def i_see_advanced_settings_with_values(step): world.verify_all_setting_entries( [ [DISPLAY_NAME, "Blank Common Problem", True], + [MATLAB_API_KEY, "", False], [MAXIMUM_ATTEMPTS, "", False], [PROBLEM_WEIGHT, "", False], [RANDOMIZATION, "Never", False], [SHOW_ANSWER, "Finished", False], - [TIMER_BETWEEN_ATTEMPTS, "0", False] + [TIMER_BETWEEN_ATTEMPTS, "0", False], ]) diff --git a/common/lib/capa/capa/capa_problem.py b/common/lib/capa/capa/capa_problem.py index 00d3badd5e..e4997db1c6 100644 --- a/common/lib/capa/capa/capa_problem.py +++ b/common/lib/capa/capa/capa_problem.py @@ -92,6 +92,7 @@ class LoncapaSystem(object): seed, # Why do we do this if we have self.seed? STATIC_URL, # pylint: disable=invalid-name xqueue, + matlab_api_key=None ): self.ajax_url = ajax_url self.anonymous_student_id = anonymous_student_id @@ -105,6 +106,7 @@ class LoncapaSystem(object): self.seed = seed # Why do we do this if we have self.seed? self.STATIC_URL = STATIC_URL # pylint: disable=invalid-name self.xqueue = xqueue + self.matlab_api_key = matlab_api_key class LoncapaProblem(object): diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 1d5386b566..3106e4fa96 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -793,14 +793,10 @@ class MatlabInput(CodeInput): """ InputType for handling Matlab code input - TODO: API_KEY will go away once we have a way to specify it per-course Example: Initial Text - - %api_key=API_KEY - - + """ template = "matlabinput.html" tags = ['matlabinput'] @@ -817,8 +813,8 @@ class MatlabInput(CodeInput): self.setup_code_response_rendering() xml = self.xml - self.plot_payload = xml.findtext('./plot_payload') + self.plot_payload = xml.findtext('./plot_payload') # Check if problem has been queued self.queuename = 'matlab' self.queue_msg = '' @@ -967,7 +963,10 @@ class MatlabInput(CodeInput): contents = { 'grader_payload': self.plot_payload, 'student_info': json.dumps(student_info), - 'student_response': response + 'student_response': response, + 'token': getattr(self.capa_system, 'matlab_api_key', None), + 'endpoint_version': "2", + 'requestor_id': anonymous_student_id, } (error, msg) = qinterface.send_to_queue(header=xheader, diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py index 77227fab88..06be537057 100644 --- a/common/lib/capa/capa/responsetypes.py +++ b/common/lib/capa/capa/responsetypes.py @@ -1878,11 +1878,18 @@ class CodeResponse(LoncapaResponse): self.answer (an answer to display to the student in the LMS) self.payload """ - # Note that CodeResponse is agnostic to the specific contents of - # grader_payload grader_payload = codeparam.find('grader_payload') grader_payload = grader_payload.text if grader_payload is not None else '' - self.payload = {'grader_payload': grader_payload} + self.payload = { + 'grader_payload': grader_payload, + } + + # matlab api key can be defined in course settings. if so, add it to the grader payload + api_key = getattr(self.capa_system, 'matlab_api_key', None) + if self.xml.find('matlabinput') and api_key: + self.payload['token'] = api_key + self.payload['endpoint_version'] = "2" + self.payload['requestor_id'] = self.capa_system.anonymous_student_id self.initial_display = find_with_default( codeparam, 'initial_display', '') diff --git a/common/lib/capa/capa/tests/test_inputtypes.py b/common/lib/capa/capa/tests/test_inputtypes.py index 24ea807c19..c173e1f990 100644 --- a/common/lib/capa/capa/tests/test_inputtypes.py +++ b/common/lib/capa/capa/tests/test_inputtypes.py @@ -625,6 +625,22 @@ class MatlabTest(unittest.TestCase): the_input = self.input_class(test_capa_system(), elt, state) self.assertEqual(the_input.status, 'unsubmitted') + def test_matlab_api_key(self): + """ + Test that api_key ends up in the xqueue payload + """ + elt = etree.fromstring(self.xml) + system = test_capa_system() + system.matlab_api_key = 'test_api_key' + the_input = lookup_tag('matlabinput')(system, elt, {}) + + data = {'submission': 'x = 1234;'} + response = the_input.handle_ajax("plot", data) + + body = system.xqueue['interface'].send_to_queue.call_args[1]['body'] + payload = json.loads(body) + self.assertEqual('test_api_key', payload['token']) + self.assertEqual('2', payload['endpoint_version']) def test_get_html(self): # usual output diff --git a/common/lib/xmodule/xmodule/capa_base.py b/common/lib/xmodule/xmodule/capa_base.py index 45f79df373..6a95f571ab 100644 --- a/common/lib/xmodule/xmodule/capa_base.py +++ b/common/lib/xmodule/xmodule/capa_base.py @@ -189,6 +189,15 @@ class CapaFields(object): default=False, scope=Scope.settings ) + matlab_api_key = String( + display_name="Matlab API key", + help="Enter the API key provided by MathWorks for accessing the MATLAB Hosted Service. " + "This key is granted for exclusive use by this course for the specified duration. " + "Please do not share the API key with other courses and notify MathWorks immediately " + "if you believe the key is exposed or compromised. To obtain a key for your course, " + "or to report and issue, please contact moocsupport@mathworks.com", + scope=Scope.settings + ) class CapaMixin(CapaFields): @@ -292,6 +301,7 @@ class CapaMixin(CapaFields): seed=self.runtime.seed, # Why do we do this if we have self.seed? STATIC_URL=self.runtime.STATIC_URL, xqueue=self.runtime.xqueue, + matlab_api_key=self.matlab_api_key ) return LoncapaProblem( diff --git a/common/lib/xmodule/xmodule/modulestore/inheritance.py b/common/lib/xmodule/xmodule/modulestore/inheritance.py index 6555c94d29..459d008ebd 100644 --- a/common/lib/xmodule/xmodule/modulestore/inheritance.py +++ b/common/lib/xmodule/xmodule/modulestore/inheritance.py @@ -86,7 +86,15 @@ class InheritanceMixin(XBlockMixin): "If the value is not set, infinite attempts are allowed."), values={"min": 0}, scope=Scope.settings ) - + matlab_api_key = String( + display_name="Matlab API key", + help="Enter the API key provided by MathWorks for accessing the MATLAB Hosted Service. " + "This key is granted for exclusive use by this course for the specified duration. " + "Please do not share the API key with other courses and notify MathWorks immediately " + "if you believe the key is exposed or compromised. To obtain a key for your course, " + "or to report and issue, please contact moocsupport@mathworks.com", + scope=Scope.settings + ) def compute_inherited_metadata(descriptor):