From 964e73f0532c7cb9ed22975467b0797fb6926bac Mon Sep 17 00:00:00 2001 From: "Dave St.Germain" Date: Mon, 28 Apr 2014 15:47:49 -0400 Subject: [PATCH 1/2] Allow courses to set Matlab API key globally, in advanced settings. --- common/lib/capa/capa/capa_problem.py | 2 ++ common/lib/capa/capa/inputtypes.py | 21 +++++++++++++------ common/lib/capa/capa/responsetypes.py | 8 +++++-- common/lib/capa/capa/tests/test_inputtypes.py | 15 +++++++++++++ common/lib/xmodule/xmodule/capa_base.py | 2 ++ .../xmodule/modulestore/inheritance.py | 5 ++++- 6 files changed, 44 insertions(+), 9 deletions(-) diff --git a/common/lib/capa/capa/capa_problem.py b/common/lib/capa/capa/capa_problem.py index 00d3badd5e65..e4997db1c658 100644 --- a/common/lib/capa/capa/capa_problem.py +++ b/common/lib/capa/capa/capa_problem.py @@ -92,6 +92,7 @@ def __init__( # pylint: disable=invalid-na 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 @@ def __init__( # pylint: disable=invalid-na 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 13627ae0f686..5b906d71ccef 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -783,14 +783,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'] @@ -807,8 +803,21 @@ def setup(self): self.setup_code_response_rendering() xml = self.xml - self.plot_payload = xml.findtext('./plot_payload') + # the new way to define the api key is to set it in the course advanced settings + api_key = getattr(self.capa_system, 'matlab_api_key', None) + if api_key: + plot_payload = '%api_key={}'.format(api_key) + else: + plot_payload = '' + + # the old way to define api_key is to add it to the plot_payload xml. + # are there other things that go in the payload? + xml_payload = xml.findtext('./plot_payload') + if xml_payload: + plot_payload += '\n{}'.format(xml_payload) + + self.plot_payload = plot_payload # Check if problem has been queued self.queuename = 'matlab' self.queue_msg = '' diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py index 5b65fb3870e7..0adbed7eb3cd 100644 --- a/common/lib/capa/capa/responsetypes.py +++ b/common/lib/capa/capa/responsetypes.py @@ -1876,10 +1876,14 @@ def _parse_coderesponse_xml(self, codeparam): 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 '' + # matlab api key can be defined in course settings. if so, add it to the grader payload + # only if the problem didn't have an api key already defined. + api_key = getattr(self.capa_system, 'matlab_api_key', None) + if self.xml.find('matlabinput') and api_key and 'api_key' not in grader_payload: + grader_payload += '\n%api_key={}'.format(api_key) + self.payload = {'grader_payload': grader_payload} self.initial_display = find_with_default( diff --git a/common/lib/capa/capa/tests/test_inputtypes.py b/common/lib/capa/capa/tests/test_inputtypes.py index 81acd89afc30..ebf8506281b5 100644 --- a/common/lib/capa/capa/tests/test_inputtypes.py +++ b/common/lib/capa/capa/tests/test_inputtypes.py @@ -628,6 +628,21 @@ def test_matlab_response_migration_of_queuetime(self, time): context = the_input._get_render_context() 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.assertIn('%api_key=test_api_key', payload['grader_payload']) 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 45f79df3730c..a2e73469e6ba 100644 --- a/common/lib/xmodule/xmodule/capa_base.py +++ b/common/lib/xmodule/xmodule/capa_base.py @@ -189,6 +189,7 @@ class CapaFields(object): default=False, scope=Scope.settings ) + matlab_api_key = String(help="API key for Matlab problems", scope=Scope.settings) class CapaMixin(CapaFields): @@ -292,6 +293,7 @@ def new_lcp(self, state, text=None): 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 6555c94d29dd..0fb8a63915f2 100644 --- a/common/lib/xmodule/xmodule/modulestore/inheritance.py +++ b/common/lib/xmodule/xmodule/modulestore/inheritance.py @@ -86,7 +86,10 @@ class InheritanceMixin(XBlockMixin): "If the value is not set, infinite attempts are allowed."), values={"min": 0}, scope=Scope.settings ) - + matlab_api_key = String( + help="API key for Matlab problems", + scope=Scope.settings + ) def compute_inherited_metadata(descriptor): From 0173eaf9d2f1369099a036e447208ee308535ff2 Mon Sep 17 00:00:00 2001 From: "Dave St.Germain" Date: Thu, 15 May 2014 14:46:16 -0400 Subject: [PATCH 2/2] Updated payload for new matlab endpoint. --- .../contentstore/features/problem-editor.py | 4 +++- common/lib/capa/capa/inputtypes.py | 20 +++++-------------- common/lib/capa/capa/responsetypes.py | 13 +++++++----- common/lib/capa/capa/tests/test_inputtypes.py | 3 ++- common/lib/xmodule/xmodule/capa_base.py | 10 +++++++++- .../xmodule/modulestore/inheritance.py | 7 ++++++- 6 files changed, 33 insertions(+), 24 deletions(-) diff --git a/cms/djangoapps/contentstore/features/problem-editor.py b/cms/djangoapps/contentstore/features/problem-editor.py index a57e5bda01c3..6d636e28b35a 100644 --- a/cms/djangoapps/contentstore/features/problem-editor.py +++ b/cms/djangoapps/contentstore/features/problem-editor.py @@ -14,6 +14,7 @@ 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/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 5b906d71ccef..5087763d61d9 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -804,20 +804,7 @@ def setup(self): xml = self.xml - # the new way to define the api key is to set it in the course advanced settings - api_key = getattr(self.capa_system, 'matlab_api_key', None) - if api_key: - plot_payload = '%api_key={}'.format(api_key) - else: - plot_payload = '' - - # the old way to define api_key is to add it to the plot_payload xml. - # are there other things that go in the payload? - xml_payload = xml.findtext('./plot_payload') - if xml_payload: - plot_payload += '\n{}'.format(xml_payload) - - self.plot_payload = plot_payload + self.plot_payload = xml.findtext('./plot_payload') # Check if problem has been queued self.queuename = 'matlab' self.queue_msg = '' @@ -966,7 +953,10 @@ def _plot_data(self, data): 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 0adbed7eb3cd..963c01cfae82 100644 --- a/common/lib/capa/capa/responsetypes.py +++ b/common/lib/capa/capa/responsetypes.py @@ -1878,13 +1878,16 @@ def _parse_coderesponse_xml(self, codeparam): """ grader_payload = codeparam.find('grader_payload') grader_payload = grader_payload.text if grader_payload is not None else '' + self.payload = { + 'grader_payload': grader_payload, + } + # matlab api key can be defined in course settings. if so, add it to the grader payload - # only if the problem didn't have an api key already defined. api_key = getattr(self.capa_system, 'matlab_api_key', None) - if self.xml.find('matlabinput') and api_key and 'api_key' not in grader_payload: - grader_payload += '\n%api_key={}'.format(api_key) - - self.payload = {'grader_payload': grader_payload} + 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 ebf8506281b5..4816b0943f85 100644 --- a/common/lib/capa/capa/tests/test_inputtypes.py +++ b/common/lib/capa/capa/tests/test_inputtypes.py @@ -642,7 +642,8 @@ def test_matlab_api_key(self): body = system.xqueue['interface'].send_to_queue.call_args[1]['body'] payload = json.loads(body) - self.assertIn('%api_key=test_api_key', payload['grader_payload']) + 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 a2e73469e6ba..6a95f571ab08 100644 --- a/common/lib/xmodule/xmodule/capa_base.py +++ b/common/lib/xmodule/xmodule/capa_base.py @@ -189,7 +189,15 @@ class CapaFields(object): default=False, scope=Scope.settings ) - matlab_api_key = String(help="API key for Matlab problems", 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): diff --git a/common/lib/xmodule/xmodule/modulestore/inheritance.py b/common/lib/xmodule/xmodule/modulestore/inheritance.py index 0fb8a63915f2..459d008ebda0 100644 --- a/common/lib/xmodule/xmodule/modulestore/inheritance.py +++ b/common/lib/xmodule/xmodule/modulestore/inheritance.py @@ -87,7 +87,12 @@ class InheritanceMixin(XBlockMixin): values={"min": 0}, scope=Scope.settings ) matlab_api_key = String( - help="API key for Matlab problems", + 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 )