Skip to content
Snippets Groups Projects
Commit 67d0670b authored by Ned Batchelder's avatar Ned Batchelder
Browse files

Symbolic response no longer runs its checker in the Python sandbox.

parent 94f6e685
No related merge requests found
......@@ -927,19 +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, **kwargs):
code = [script_code, "kwargs = {}"]
for name, val in kwargs.iteritems():
if isinstance(val, (str, list, tuple, int, long, float, dict)):
code.append("kwargs[%r] = %r" % (name, val))
code.append("cfn_return = %s(expect, ans, **kwargs)" % cfn)
code.append("") # a final newline
def check_function(expect, ans):
code = (
script_code + "\n" +
"cfn_return = %s(expect, ans)\n" % cfn
)
globals_dict = {
'expect': expect,
'ans': ans,
}
locals_dict = {}
safe_exec.safe_exec("\n".join(code), globals_dict, locals_dict)
safe_exec.safe_exec(code, globals_dict, locals_dict)
return locals_dict['cfn_return']
return check_function
......@@ -958,8 +956,6 @@ class CustomResponse(LoncapaResponse):
else:
self.code = answer.text
self.cfn_kwargs_keys = []
def get_score(self, student_answers):
'''
student_answers is a dict with everything from request.POST, but with the first part
......@@ -1038,16 +1034,30 @@ class CustomResponse(LoncapaResponse):
# pass self.system.debug to cfn
self.context['debug'] = self.system.DEBUG
# Run the check function
self.execute_check_function(idset, submission)
# build map giving "correct"ness of the answer(s)
correct = self.context['correct']
messages = self.context['messages']
correct_map = CorrectMap()
overall_message = self.clean_message_html(self.context['overall_message']))
correct_map.set_overall_message(overall_message)
for k in range(len(idset)):
npoints = self.maxpoints[idset[k]] if correct[k] == 'correct' else 0
correct_map.set(idset[k], correct[k], msg=messages[k],
npoints=npoints)
return correct_map
def execute_check_function(self, idset, submission):
# exec the check function
if isinstance(self.code, basestring):
try:
locals_dict = {}
safe_exec.safe_exec(self.code, self.context, locals_dict)
self.context.update(locals_dict)
correct = self.context['correct']
messages = self.context['messages']
overall_message = self.context['overall_message']
except Exception as err:
self._handle_exec_exception(err)
......@@ -1056,33 +1066,27 @@ class CustomResponse(LoncapaResponse):
# this is an interface to the Tutor2 check functions
fn = self.code
ret = None
log.debug(" submission = %s" % submission)
try:
answer_given = submission[0] if (len(idset) == 1) else submission
kwargs = {n:self.context.get(n) for n in self.cfn_kwargs_keys}
ret = fn(self.expect, answer_given, **kwargs)
ret = fn(self.expect, answer_given)
except Exception as err:
self._handle_exec_exception(err)
if type(ret) == dict:
log.error("oops in customresponse (cfn) error %s" % err)
# print "context = ",self.context
log.error(traceback.format_exc())
raise Exception("oops in customresponse (cfn) error %s" % err)
log.debug(
"[courseware.capa.responsetypes.customresponse.get_score] ret = %s",
ret
)
if isinstance(ret, dict):
# One kind of dictionary the check function can return has the
# form {'ok': BOOLEAN, 'msg': STRING}
# If there are multiple inputs, they all get marked
# to the same correct/incorrect value
if 'ok' in ret:
correct = ['correct'] * len(idset) if ret[
'ok'] else ['incorrect'] * len(idset)
msg = ret.get('msg', None)
msg = self.clean_message_html(msg)
# If there is only one input, apply the message to that input
# Otherwise, apply the message to the whole problem
if len(idset) > 1:
overall_message = msg
else:
messages[0] = msg
correct = ['correct' if ret['ok'] else 'incorrect'] * len(idset)
self.context['messages'][0] = self.clean_message_html(ret['msg'])
# Another kind of dictionary the check function can return has
# the form:
......@@ -1104,6 +1108,7 @@ class CustomResponse(LoncapaResponse):
msg = (self.clean_message_html(input_dict['msg'])
if 'msg' in input_dict else None)
messages.append(msg)
self.context['messages'] = messages
# Otherwise, we do not recognize the dictionary
# Raise an exception
......@@ -1112,25 +1117,10 @@ class CustomResponse(LoncapaResponse):
raise ResponseError(
"CustomResponse: check function returned an invalid dict")
# The check function can return a boolean value,
# indicating whether all inputs should be marked
# correct or incorrect
else:
n = len(idset)
correct = ['correct'] * n if ret else ['incorrect'] * n
# build map giving "correct"ness of the answer(s)
correct_map = CorrectMap()
overall_message = self.clean_message_html(overall_message)
correct_map.set_overall_message(overall_message)
correct = ['correct' if ret else 'incorrect'] * len(idset)
for k in range(len(idset)):
npoints = (self.maxpoints[idset[k]]
if correct[k] == 'correct' else 0)
correct_map.set(idset[k], correct[k], msg=messages[k],
npoints=npoints)
return correct_map
self.context['correct'] = correct
def clean_message_html(self, msg):
......@@ -1205,12 +1195,23 @@ class SymbolicResponse(CustomResponse):
response_tag = 'symbolicresponse'
def setup_response(self):
# No, this is not pretty.
self.context['script_code'] += "from symmath import symmath_check\n"
self.xml.set('cfn', 'symmath_check')
CustomResponse.setup_response(self)
self.cfn_kwargs_keys.extend(['dynamath', 'options', 'debug'])
def execute_check_function(self, idset, submission):
from symmath import symmath_check
fn = self.code
try:
answer_given = submission[0] if (len(idset) == 1) else submission
ret = symmath_check(
self.expect, answer_given,
dynamath=self.context.get('dynamath'),
options=self.context.get('options'),
debug=self.context.get('debug'),
)
except Exception as err:
log.error("oops in symbolicresponse (cfn) error %s" % err)
log.error(traceback.format_exc())
raise Exception("oops in symbolicresponse (cfn) error %s" % err)
self.context['messages'][0] = self.clean_message_html(ret['msg'])
self.context['correct'] = ['correct' if ret['ok'] else 'incorrect'] * len(idset)
#-----------------------------------------------------------------------------
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment