Make it possible for customresponse check functions to get extra arguments, though they need to be declared in the XML.
This commit is contained in:
@@ -927,15 +927,17 @@ class CustomResponse(LoncapaResponse):
|
||||
# actual function that will re-execute the original script,
|
||||
# and invoke the function with the data needed.
|
||||
def make_check_function(script_code, cfn):
|
||||
def check_function(expect, ans):
|
||||
def check_function(expect, ans, **kwargs):
|
||||
extra_args = "".join(", {0}={0}".format(k) for k in kwargs)
|
||||
code = (
|
||||
script_code + "\n" +
|
||||
"cfn_return = %s(expect, ans)\n" % cfn
|
||||
"cfn_return = %s(expect, ans%s)\n" % (cfn, extra_args)
|
||||
)
|
||||
globals_dict = {
|
||||
'expect': expect,
|
||||
'ans': ans,
|
||||
}
|
||||
globals_dict.update(kwargs)
|
||||
safe_exec.safe_exec(code, globals_dict)
|
||||
return globals_dict['cfn_return']
|
||||
return check_function
|
||||
@@ -1063,10 +1065,12 @@ class CustomResponse(LoncapaResponse):
|
||||
|
||||
# this is an interface to the Tutor2 check functions
|
||||
fn = self.code
|
||||
answer_given = submission[0] if (len(idset) == 1) else submission
|
||||
kwnames = self.xml.get("cfn_extra_args", "").split()
|
||||
kwargs = {n:self.context.get(n) for n in kwnames}
|
||||
log.debug(" submission = %s" % submission)
|
||||
try:
|
||||
answer_given = submission[0] if (len(idset) == 1) else submission
|
||||
ret = fn(self.expect, answer_given)
|
||||
ret = fn(self.expect, answer_given, **kwargs)
|
||||
except Exception as err:
|
||||
log.error("oops in customresponse (cfn) error %s" % err)
|
||||
# print "context = ",self.context
|
||||
|
||||
@@ -221,6 +221,8 @@ class CustomResponseXMLFactory(ResponseXMLFactory):
|
||||
cfn = kwargs.get('cfn', None)
|
||||
expect = kwargs.get('expect', None)
|
||||
answer = kwargs.get('answer', None)
|
||||
options = kwargs.get('options', None)
|
||||
cfn_extra_args = kwargs.get('cfn_extra_args', None)
|
||||
|
||||
# Create the response element
|
||||
response_element = etree.Element("customresponse")
|
||||
@@ -235,6 +237,12 @@ class CustomResponseXMLFactory(ResponseXMLFactory):
|
||||
answer_element = etree.SubElement(response_element, "answer")
|
||||
answer_element.text = str(answer)
|
||||
|
||||
if options:
|
||||
response_element.set('options', str(options))
|
||||
|
||||
if cfn_extra_args:
|
||||
response_element.set('cfn_extra_args', str(cfn_extra_args))
|
||||
|
||||
return response_element
|
||||
|
||||
def create_input_element(self, **kwargs):
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
Tests of responsetypes
|
||||
"""
|
||||
|
||||
|
||||
from datetime import datetime
|
||||
import json
|
||||
from nose.plugins.skip import SkipTest
|
||||
@@ -806,9 +805,8 @@ class CustomResponseTest(ResponseTest):
|
||||
#
|
||||
# 'answer_given' is the answer the student gave (if there is just one input)
|
||||
# or an ordered list of answers (if there are multiple inputs)
|
||||
#
|
||||
#
|
||||
# The function should return a dict of the form
|
||||
#
|
||||
# The function should return a dict of the form
|
||||
# { 'ok': BOOL, 'msg': STRING }
|
||||
#
|
||||
script = textwrap.dedent("""
|
||||
@@ -917,6 +915,35 @@ class CustomResponseTest(ResponseTest):
|
||||
self.assertEqual(correct_map.get_msg('1_2_2'), 'Feedback 2')
|
||||
self.assertEqual(correct_map.get_msg('1_2_3'), 'Feedback 3')
|
||||
|
||||
def test_function_code_with_extra_args(self):
|
||||
script = textwrap.dedent("""\
|
||||
def check_func(expect, answer_given, options, dynamath):
|
||||
assert options == "xyzzy", "Options was %r" % options
|
||||
return {'ok': answer_given == expect, 'msg': 'Message text'}
|
||||
""")
|
||||
|
||||
problem = self.build_problem(script=script, cfn="check_func", expect="42", options="xyzzy", cfn_extra_args="options dynamath")
|
||||
|
||||
# Correct answer
|
||||
input_dict = {'1_2_1': '42'}
|
||||
correct_map = problem.grade_answers(input_dict)
|
||||
|
||||
correctness = correct_map.get_correctness('1_2_1')
|
||||
msg = correct_map.get_msg('1_2_1')
|
||||
|
||||
self.assertEqual(correctness, 'correct')
|
||||
self.assertEqual(msg, "Message text\n")
|
||||
|
||||
# Incorrect answer
|
||||
input_dict = {'1_2_1': '0'}
|
||||
correct_map = problem.grade_answers(input_dict)
|
||||
|
||||
correctness = correct_map.get_correctness('1_2_1')
|
||||
msg = correct_map.get_msg('1_2_1')
|
||||
|
||||
self.assertEqual(correctness, 'incorrect')
|
||||
self.assertEqual(msg, "Message text\n")
|
||||
|
||||
def test_multiple_inputs_return_one_status(self):
|
||||
# When given multiple inputs, the 'answer_given' argument
|
||||
# to the check_func() is a list of inputs
|
||||
|
||||
Reference in New Issue
Block a user