Merge pull request #1554 from edx/sarina/fix-violations
Clean up pep8/pylint
This commit is contained in:
@@ -35,17 +35,17 @@ from calc import evaluator, UndefinedVariable
|
||||
from . import correctmap
|
||||
from datetime import datetime
|
||||
from pytz import UTC
|
||||
from .util import *
|
||||
from .util import compare_with_tolerance, contextualize_text, convert_files_to_filenames, is_list_of_files, find_with_default
|
||||
from lxml import etree
|
||||
from lxml.html.soupparser import fromstring as fromstring_bs # uses Beautiful Soup!!! FIXME?
|
||||
import capa.xqueue_interface as xqueue_interface
|
||||
|
||||
import safe_exec
|
||||
import capa.safe_exec as safe_exec
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
CorrectMap = correctmap.CorrectMap
|
||||
CorrectMap = correctmap.CorrectMap # pylint: disable=C0103
|
||||
CORRECTMAP_PY = None
|
||||
|
||||
|
||||
@@ -1181,7 +1181,7 @@ class CustomResponse(LoncapaResponse):
|
||||
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}
|
||||
kwargs = {n: self.context.get(n) for n in kwnames}
|
||||
log.debug(" submission = %s" % submission)
|
||||
try:
|
||||
ret = fn(self.expect, answer_given, **kwargs)
|
||||
@@ -1304,7 +1304,7 @@ class CustomResponse(LoncapaResponse):
|
||||
|
||||
# Notify student with a student input error
|
||||
_, _, traceback_obj = sys.exc_info()
|
||||
raise ResponseError, err.message, traceback_obj
|
||||
raise ResponseError(err.message, traceback_obj)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
@@ -1597,15 +1597,25 @@ class CodeResponse(LoncapaResponse):
|
||||
|
||||
|
||||
class ExternalResponse(LoncapaResponse):
|
||||
'''
|
||||
"""
|
||||
Grade the students input using an external server.
|
||||
|
||||
Typically used by coding problems.
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
response_tag = 'externalresponse'
|
||||
allowed_inputfields = ['textline', 'textbox']
|
||||
awdmap = {
|
||||
'EXACT_ANS': 'correct', # TODO: handle other loncapa responses
|
||||
'WRONG_FORMAT': 'incorrect',
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.url = ''
|
||||
self.tests = []
|
||||
self.code = ''
|
||||
super(ExternalResponse, self).__init__(*args, **kwargs)
|
||||
|
||||
def setup_response(self):
|
||||
xml = self.xml
|
||||
@@ -1633,45 +1643,44 @@ class ExternalResponse(LoncapaResponse):
|
||||
self.tests = xml.get('tests')
|
||||
|
||||
def do_external_request(self, cmd, extra_payload):
|
||||
'''
|
||||
"""
|
||||
Perform HTTP request / post to external server.
|
||||
|
||||
cmd = remote command to perform (str)
|
||||
extra_payload = dict of extra stuff to post.
|
||||
|
||||
Return XML tree of response (from response body)
|
||||
'''
|
||||
"""
|
||||
xmlstr = etree.tostring(self.xml, pretty_print=True)
|
||||
payload = {'xml': xmlstr,
|
||||
'edX_cmd': cmd,
|
||||
'edX_tests': self.tests,
|
||||
'processor': self.code,
|
||||
}
|
||||
payload = {
|
||||
'xml': xmlstr,
|
||||
'edX_cmd': cmd,
|
||||
'edX_tests': self.tests,
|
||||
'processor': self.code,
|
||||
}
|
||||
payload.update(extra_payload)
|
||||
|
||||
try:
|
||||
# call external server. TODO: synchronous call, can block for a
|
||||
# long time
|
||||
r = requests.post(self.url, data=payload)
|
||||
req = requests.post(self.url, data=payload)
|
||||
except Exception as err:
|
||||
msg = 'Error %s - cannot connect to external server url=%s' % (
|
||||
err, self.url)
|
||||
msg = 'Error {0} - cannot connect to external server url={1}'.format(err, self.url)
|
||||
log.error(msg)
|
||||
raise Exception(msg)
|
||||
|
||||
if self.system.DEBUG:
|
||||
log.info('response = %s' % r.text)
|
||||
log.info('response = %s', req.text)
|
||||
|
||||
if (not r.text) or (not r.text.strip()):
|
||||
if (not req.text) or (not req.text.strip()):
|
||||
raise Exception(
|
||||
'Error: no response from external server url=%s' % self.url)
|
||||
|
||||
try:
|
||||
# response is XML; parse it
|
||||
rxml = etree.fromstring(r.text)
|
||||
rxml = etree.fromstring(req.text)
|
||||
except Exception as err:
|
||||
msg = 'Error %s - cannot parse response from external server r.text=%s' % (
|
||||
err, r.text)
|
||||
msg = 'Error {0} - cannot parse response from external server req.text={1}'.format(err, req.text)
|
||||
log.error(msg)
|
||||
raise Exception(msg)
|
||||
|
||||
@@ -1682,9 +1691,13 @@ class ExternalResponse(LoncapaResponse):
|
||||
cmap = CorrectMap()
|
||||
try:
|
||||
submission = [student_answers[k] for k in idset]
|
||||
except Exception as err:
|
||||
log.error('Error %s: cannot get student answer for %s; student_answers=%s' %
|
||||
(err, self.answer_ids, student_answers))
|
||||
except Exception as err: # pylint: disable=W0703
|
||||
log.error(
|
||||
'Error %s: cannot get student answer for %s; student_answers=%s',
|
||||
err,
|
||||
self.answer_ids,
|
||||
student_answers
|
||||
)
|
||||
raise Exception(err)
|
||||
|
||||
self.context.update({'submission': submission})
|
||||
@@ -1693,8 +1706,8 @@ class ExternalResponse(LoncapaResponse):
|
||||
|
||||
try:
|
||||
rxml = self.do_external_request('get_score', extra_payload)
|
||||
except Exception as err:
|
||||
log.error('Error %s' % err)
|
||||
except Exception as err: # pylint: disable=W0703
|
||||
log.error('Error %s', err)
|
||||
if self.system.DEBUG:
|
||||
cmap.set_dict(dict(zip(sorted(
|
||||
self.answer_ids), ['incorrect'] * len(idset))))
|
||||
@@ -1703,13 +1716,11 @@ class ExternalResponse(LoncapaResponse):
|
||||
'<span class="inline-error">%s</span>' % str(err).replace('<', '<'))
|
||||
return cmap
|
||||
|
||||
ad = rxml.find('awarddetail').text
|
||||
admap = {'EXACT_ANS': 'correct', # TODO: handle other loncapa responses
|
||||
'WRONG_FORMAT': 'incorrect',
|
||||
}
|
||||
awd = rxml.find('awarddetail').text
|
||||
|
||||
self.context['correct'] = ['correct']
|
||||
if ad in admap:
|
||||
self.context['correct'][0] = admap[ad]
|
||||
if awd in self.awdmap:
|
||||
self.context['correct'][0] = self.awdmap[awd]
|
||||
|
||||
# create CorrectMap
|
||||
for key in idset:
|
||||
@@ -1721,14 +1732,14 @@ class ExternalResponse(LoncapaResponse):
|
||||
return cmap
|
||||
|
||||
def get_answers(self):
|
||||
'''
|
||||
"""
|
||||
Use external server to get expected answers
|
||||
'''
|
||||
"""
|
||||
try:
|
||||
rxml = self.do_external_request('get_answers', {})
|
||||
exans = json.loads(rxml.find('expected').text)
|
||||
except Exception as err:
|
||||
log.error('Error %s' % err)
|
||||
except Exception as err: # pylint: disable=W0703
|
||||
log.error('Error %s', err)
|
||||
if self.system.DEBUG:
|
||||
msg = '<span class="inline-error">%s</span>' % str(
|
||||
err).replace('<', '<')
|
||||
@@ -1736,8 +1747,8 @@ class ExternalResponse(LoncapaResponse):
|
||||
exans[0] = msg
|
||||
|
||||
if not (len(exans) == len(self.answer_ids)):
|
||||
log.error('Expected %d answers from external server, only got %d!' %
|
||||
(len(self.answer_ids), len(exans)))
|
||||
log.error('Expected %s answers from external server, only got %s!',
|
||||
len(self.answer_ids), len(exans))
|
||||
raise Exception('Short response from external server')
|
||||
return dict(zip(self.answer_ids, exans))
|
||||
|
||||
@@ -1745,9 +1756,9 @@ class ExternalResponse(LoncapaResponse):
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
class FormulaResponse(LoncapaResponse):
|
||||
'''
|
||||
"""
|
||||
Checking of symbolic math response using numerical sampling.
|
||||
'''
|
||||
"""
|
||||
|
||||
response_tag = 'formularesponse'
|
||||
hint_tag = 'formulahint'
|
||||
@@ -1776,11 +1787,11 @@ class FormulaResponse(LoncapaResponse):
|
||||
if tolerance_xml: # If it isn't an empty list...
|
||||
self.tolerance = contextualize_text(tolerance_xml[0], context)
|
||||
|
||||
ts = xml.get('type')
|
||||
if ts is None:
|
||||
types = xml.get('type')
|
||||
if types is None:
|
||||
typeslist = []
|
||||
else:
|
||||
typeslist = ts.split(',')
|
||||
typeslist = types.split(',')
|
||||
if 'ci' in typeslist:
|
||||
# Case insensitive
|
||||
self.case_sensitive = False
|
||||
@@ -1812,30 +1823,33 @@ class FormulaResponse(LoncapaResponse):
|
||||
answer,
|
||||
case_sensitive=self.case_sensitive,
|
||||
))
|
||||
except UndefinedVariable as uv:
|
||||
except UndefinedVariable as err:
|
||||
log.debug(
|
||||
'formularesponse: undefined variable in formula=%s' % answer)
|
||||
raise StudentInputError(
|
||||
"Invalid input: " + uv.message + " not permitted in answer"
|
||||
'formularesponse: undefined variable in formula=%s',
|
||||
cgi.escape(answer)
|
||||
)
|
||||
except ValueError as ve:
|
||||
if 'factorial' in ve.message:
|
||||
raise StudentInputError(
|
||||
"Invalid input: " + err.message + " not permitted in answer"
|
||||
)
|
||||
except ValueError as err:
|
||||
if 'factorial' in err.message:
|
||||
# This is thrown when fact() or factorial() is used in a formularesponse answer
|
||||
# that tests on negative and/or non-integer inputs
|
||||
# ve.message will be: `factorial() only accepts integral values` or
|
||||
# err.message will be: `factorial() only accepts integral values` or
|
||||
# `factorial() not defined for negative values`
|
||||
log.debug(
|
||||
('formularesponse: factorial function used in response '
|
||||
'that tests negative and/or non-integer inputs. '
|
||||
'given={0}').format(given)
|
||||
'Provided answer was: %s'),
|
||||
cgi.escape(answer)
|
||||
)
|
||||
raise StudentInputError(
|
||||
("factorial function not permitted in answer "
|
||||
"for this problem. Provided answer was: "
|
||||
"{0}").format(cgi.escape(given))
|
||||
"{0}").format(cgi.escape(answer))
|
||||
)
|
||||
# If non-factorial related ValueError thrown, handle it the same as any other Exception
|
||||
log.debug('formularesponse: error {0} in formula'.format(ve))
|
||||
log.debug('formularesponse: error %s in formula', err)
|
||||
raise StudentInputError("Invalid input: Could not parse '%s' as a formula" %
|
||||
cgi.escape(answer))
|
||||
except Exception as err:
|
||||
@@ -1857,7 +1871,7 @@ class FormulaResponse(LoncapaResponse):
|
||||
ranges = dict(zip(variables, sranges))
|
||||
|
||||
out = []
|
||||
for i in range(numsamples):
|
||||
for _ in range(numsamples):
|
||||
var_dict = {}
|
||||
# ranges give numerical ranges for testing
|
||||
for var in ranges:
|
||||
@@ -1902,15 +1916,17 @@ class FormulaResponse(LoncapaResponse):
|
||||
except StudentInputError:
|
||||
return False
|
||||
|
||||
def strip_dict(self, d):
|
||||
''' Takes a dict. Returns an identical dict, with all non-word
|
||||
def strip_dict(self, inp_d):
|
||||
"""
|
||||
Takes a dict. Returns an identical dict, with all non-word
|
||||
keys and all non-numeric values stripped out. All values also
|
||||
converted to float. Used so we can safely use Python contexts.
|
||||
'''
|
||||
d = dict([(k, numpy.complex(d[k])) for k in d if type(k) == str and
|
||||
k.isalnum() and
|
||||
isinstance(d[k], numbers.Number)])
|
||||
return d
|
||||
"""
|
||||
inp_d = dict([(k, numpy.complex(inp_d[k]))
|
||||
for k in inp_d if type(k) == str and
|
||||
k.isalnum() and
|
||||
isinstance(inp_d[k], numbers.Number)])
|
||||
return inp_d
|
||||
|
||||
def check_hint_condition(self, hxml_set, student_answers):
|
||||
given = student_answers[self.answer_id]
|
||||
@@ -1920,14 +1936,18 @@ class FormulaResponse(LoncapaResponse):
|
||||
name = hxml.get('name')
|
||||
correct_answer = contextualize_text(
|
||||
hxml.get('answer'), self.context)
|
||||
# pylint: disable=W0703
|
||||
try:
|
||||
correctness = self.check_formula(
|
||||
correct_answer, given, samples)
|
||||
correct_answer,
|
||||
given,
|
||||
samples
|
||||
)
|
||||
except Exception:
|
||||
correctness = 'incorrect'
|
||||
if correctness == 'correct':
|
||||
hints_to_show.append(name)
|
||||
log.debug('hints_to_show = %s' % hints_to_show)
|
||||
log.debug('hints_to_show = %s', hints_to_show)
|
||||
return hints_to_show
|
||||
|
||||
def get_answers(self):
|
||||
@@ -1937,10 +1957,16 @@ class FormulaResponse(LoncapaResponse):
|
||||
|
||||
|
||||
class SchematicResponse(LoncapaResponse):
|
||||
|
||||
"""
|
||||
Circuit schematic response type.
|
||||
"""
|
||||
response_tag = 'schematicresponse'
|
||||
allowed_inputfields = ['schematic']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.code = ''
|
||||
super(SchematicResponse, self).__init__(*args, **kwargs)
|
||||
|
||||
def setup_response(self):
|
||||
xml = self.xml
|
||||
answer = xml.xpath('//*[@id=$id]//answer', id=xml.get('id'))[0]
|
||||
@@ -2010,6 +2036,10 @@ class ImageResponse(LoncapaResponse):
|
||||
response_tag = 'imageresponse'
|
||||
allowed_inputfields = ['imageinput']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.ielements = []
|
||||
super(ImageResponse, self).__init__(*args, **kwargs)
|
||||
|
||||
def setup_response(self):
|
||||
self.ielements = self.inputfields
|
||||
self.answer_ids = [ie.get('id') for ie in self.ielements]
|
||||
@@ -2018,40 +2048,39 @@ class ImageResponse(LoncapaResponse):
|
||||
correct_map = CorrectMap()
|
||||
expectedset = self.get_mapped_answers()
|
||||
for aid in self.answer_ids: # loop through IDs of <imageinput>
|
||||
# fields in our stanza
|
||||
given = student_answers[
|
||||
aid] # this should be a string of the form '[x,y]'
|
||||
# Fields in our stanza
|
||||
given = student_answers[aid] # This should be a string of the form '[x,y]'
|
||||
correct_map.set(aid, 'incorrect')
|
||||
if not given: # No answer to parse. Mark as incorrect and move on
|
||||
continue
|
||||
# parse given answer
|
||||
m = re.match(r'\[([0-9]+),([0-9]+)]', given.strip().replace(' ', ''))
|
||||
if not m:
|
||||
# Parse given answer
|
||||
acoords = re.match(r'\[([0-9]+),([0-9]+)]', given.strip().replace(' ', ''))
|
||||
if not acoords:
|
||||
raise Exception('[capamodule.capa.responsetypes.imageinput] '
|
||||
'error grading %s (input=%s)' % (aid, given))
|
||||
(gx, gy) = [int(x) for x in m.groups()]
|
||||
'error grading {0} (input={1})'.format(aid, given))
|
||||
(ans_x, ans_y) = [int(x) for x in acoords.groups()]
|
||||
|
||||
rectangles, regions = expectedset
|
||||
if rectangles[aid]: # rectangles part - for backward compatibility
|
||||
if rectangles[aid]: # Rectangles part - for backward compatibility
|
||||
# Check whether given point lies in any of the solution
|
||||
# rectangles
|
||||
solution_rectangles = rectangles[aid].split(';')
|
||||
for solution_rectangle in solution_rectangles:
|
||||
# parse expected answer
|
||||
# TODO: Compile regexp on file load
|
||||
m = re.match(
|
||||
sr_coords = re.match(
|
||||
r'[\(\[]([0-9]+),([0-9]+)[\)\]]-[\(\[]([0-9]+),([0-9]+)[\)\]]',
|
||||
solution_rectangle.strip().replace(' ', ''))
|
||||
if not m:
|
||||
if not sr_coords:
|
||||
msg = 'Error in problem specification! cannot parse rectangle in %s' % (
|
||||
etree.tostring(self.ielements[aid], pretty_print=True))
|
||||
raise Exception(
|
||||
'[capamodule.capa.responsetypes.imageinput] ' + msg)
|
||||
(llx, lly, urx, ury) = [int(x) for x in m.groups()]
|
||||
(llx, lly, urx, ury) = [int(x) for x in sr_coords.groups()]
|
||||
|
||||
# answer is correct if (x,y) is within the specified
|
||||
# rectangle
|
||||
if (llx <= gx <= urx) and (lly <= gy <= ury):
|
||||
if (llx <= ans_x <= urx) and (lly <= ans_y <= ury):
|
||||
correct_map.set(aid, 'correct')
|
||||
break
|
||||
if correct_map[aid]['correctness'] != 'correct' and regions[aid]:
|
||||
@@ -2065,13 +2094,13 @@ class ImageResponse(LoncapaResponse):
|
||||
for region in parsed_region:
|
||||
polygon = MultiPoint(region).convex_hull
|
||||
if (polygon.type == 'Polygon' and
|
||||
polygon.contains(Point(gx, gy))):
|
||||
polygon.contains(Point(ans_x, ans_y))):
|
||||
correct_map.set(aid, 'correct')
|
||||
break
|
||||
return correct_map
|
||||
|
||||
def get_mapped_answers(self):
|
||||
'''
|
||||
"""
|
||||
Returns the internal representation of the answers
|
||||
|
||||
Input:
|
||||
@@ -2080,7 +2109,7 @@ class ImageResponse(LoncapaResponse):
|
||||
tuple (dict, dict) -
|
||||
rectangles (dict) - a map of inputs to the defined rectangle for that input
|
||||
regions (dict) - a map of inputs to the defined region for that input
|
||||
'''
|
||||
"""
|
||||
answers = (
|
||||
dict([(ie.get('id'), ie.get(
|
||||
'rectangle')) for ie in self.ielements]),
|
||||
@@ -2088,7 +2117,7 @@ class ImageResponse(LoncapaResponse):
|
||||
return answers
|
||||
|
||||
def get_answers(self):
|
||||
'''
|
||||
"""
|
||||
Returns the external representation of the answers
|
||||
|
||||
Input:
|
||||
@@ -2096,11 +2125,11 @@ class ImageResponse(LoncapaResponse):
|
||||
Returns:
|
||||
dict (str, (str, str)) - a map of inputs to a tuple of their rectange
|
||||
and their regions
|
||||
'''
|
||||
"""
|
||||
answers = {}
|
||||
for ie in self.ielements:
|
||||
ie_id = ie.get('id')
|
||||
answers[ie_id] = (ie.get('rectangle'), ie.get('regions'))
|
||||
for ielt in self.ielements:
|
||||
ie_id = ielt.get('id')
|
||||
answers[ie_id] = (ielt.get('rectangle'), ielt.get('regions'))
|
||||
|
||||
return answers
|
||||
|
||||
@@ -2108,26 +2137,32 @@ class ImageResponse(LoncapaResponse):
|
||||
|
||||
|
||||
class AnnotationResponse(LoncapaResponse):
|
||||
'''
|
||||
"""
|
||||
Checking of annotation responses.
|
||||
|
||||
The response contains both a comment (student commentary) and an option (student tag).
|
||||
Only the tag is currently graded. Answers may be incorrect, partially correct, or correct.
|
||||
'''
|
||||
"""
|
||||
response_tag = 'annotationresponse'
|
||||
allowed_inputfields = ['annotationinput']
|
||||
max_inputfields = 1
|
||||
default_scoring = {'incorrect': 0, 'partially-correct': 1, 'correct': 2}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.scoring_map = {}
|
||||
self.answer_map = {}
|
||||
super(AnnotationResponse, self).__init__(*args, **kwargs)
|
||||
|
||||
def setup_response(self):
|
||||
xml = self.xml
|
||||
self.scoring_map = self._get_scoring_map()
|
||||
self.answer_map = self._get_answer_map()
|
||||
self.maxpoints = self._get_max_points()
|
||||
|
||||
def get_score(self, student_answers):
|
||||
''' Returns a CorrectMap for the student answer, which may include
|
||||
partially correct answers.'''
|
||||
"""
|
||||
Returns a CorrectMap for the student answer, which may include
|
||||
partially correct answers.
|
||||
"""
|
||||
student_answer = student_answers[self.answer_id]
|
||||
student_option = self._get_submitted_option_id(student_answer)
|
||||
|
||||
@@ -2146,23 +2181,26 @@ class AnnotationResponse(LoncapaResponse):
|
||||
return self.answer_map
|
||||
|
||||
def _get_scoring_map(self):
|
||||
''' Returns a dict of option->scoring for each input. '''
|
||||
"""Returns a dict of option->scoring for each input."""
|
||||
scoring = self.default_scoring
|
||||
choices = dict([(choice, choice) for choice in scoring])
|
||||
scoring_map = {}
|
||||
|
||||
for inputfield in self.inputfields:
|
||||
option_scoring = dict([(option['id'], {
|
||||
option_scoring = dict([(
|
||||
option['id'],
|
||||
{
|
||||
'correctness': choices.get(option['choice']),
|
||||
'points': scoring.get(option['choice'])
|
||||
}) for option in self._find_options(inputfield)])
|
||||
}
|
||||
) for option in self._find_options(inputfield)])
|
||||
|
||||
scoring_map[inputfield.get('id')] = option_scoring
|
||||
|
||||
return scoring_map
|
||||
|
||||
def _get_answer_map(self):
|
||||
''' Returns a dict of answers for each input.'''
|
||||
"""Returns a dict of answers for each input."""
|
||||
answer_map = {}
|
||||
for inputfield in self.inputfields:
|
||||
correct_option = self._find_option_with_choice(
|
||||
@@ -2173,13 +2211,13 @@ class AnnotationResponse(LoncapaResponse):
|
||||
return answer_map
|
||||
|
||||
def _get_max_points(self):
|
||||
''' Returns a dict of the max points for each input: input id -> maxpoints. '''
|
||||
"""Returns a dict of the max points for each input: input id -> maxpoints."""
|
||||
scoring = self.default_scoring
|
||||
correct_points = scoring.get('correct')
|
||||
return dict([(inputfield.get('id'), correct_points) for inputfield in self.inputfields])
|
||||
|
||||
def _find_options(self, inputfield):
|
||||
''' Returns an array of dicts where each dict represents an option. '''
|
||||
"""Returns an array of dicts where each dict represents an option. """
|
||||
elements = inputfield.findall('./options/option')
|
||||
return [{
|
||||
'id': index,
|
||||
@@ -2188,22 +2226,22 @@ class AnnotationResponse(LoncapaResponse):
|
||||
} for (index, option) in enumerate(elements)]
|
||||
|
||||
def _find_option_with_choice(self, inputfield, choice):
|
||||
''' Returns the option with the given choice value, otherwise None. '''
|
||||
"""Returns the option with the given choice value, otherwise None. """
|
||||
for option in self._find_options(inputfield):
|
||||
if option['choice'] == choice:
|
||||
return option
|
||||
|
||||
def _unpack(self, json_value):
|
||||
''' Unpacks a student response value submitted as JSON.'''
|
||||
d = json.loads(json_value)
|
||||
if type(d) != dict:
|
||||
d = {}
|
||||
"""Unpacks a student response value submitted as JSON."""
|
||||
json_d = json.loads(json_value)
|
||||
if type(json_d) != dict:
|
||||
json_d = {}
|
||||
|
||||
comment_value = d.get('comment', '')
|
||||
if not isinstance(d, basestring):
|
||||
comment_value = json_d.get('comment', '')
|
||||
if not isinstance(json_d, basestring):
|
||||
comment_value = ''
|
||||
|
||||
options_value = d.get('options', [])
|
||||
options_value = json_d.get('options', [])
|
||||
if not isinstance(options_value, list):
|
||||
options_value = []
|
||||
|
||||
@@ -2213,7 +2251,7 @@ class AnnotationResponse(LoncapaResponse):
|
||||
}
|
||||
|
||||
def _get_submitted_option_id(self, student_answer):
|
||||
''' Return the single option that was selected, otherwise None.'''
|
||||
"""Return the single option that was selected, otherwise None."""
|
||||
submitted = self._unpack(student_answer)
|
||||
option_ids = submitted['options_value']
|
||||
if len(option_ids) == 1:
|
||||
@@ -2235,6 +2273,12 @@ class ChoiceTextResponse(LoncapaResponse):
|
||||
'radiotextgroup'
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.correct_inputs = {}
|
||||
self.answer_values = {}
|
||||
self.correct_choices = {}
|
||||
super(ChoiceTextResponse, self).__init__(*args, **kwargs)
|
||||
|
||||
def setup_response(self):
|
||||
"""
|
||||
Sets up three dictionaries for use later:
|
||||
@@ -2250,10 +2294,8 @@ class ChoiceTextResponse(LoncapaResponse):
|
||||
|
||||
"""
|
||||
context = self.context
|
||||
self.correct_choices = {}
|
||||
self.assign_choice_names()
|
||||
self.correct_inputs = {}
|
||||
self.answer_values = {self.answer_id: []}
|
||||
self.assign_choice_names()
|
||||
correct_xml = self.xml.xpath('//*[@id=$id]//choice[@correct="true"]',
|
||||
id=self.xml.get('id'))
|
||||
for node in correct_xml:
|
||||
@@ -2552,6 +2594,7 @@ class ChoiceTextResponse(LoncapaResponse):
|
||||
# TEMPORARY: List of all response subclasses
|
||||
# FIXME: To be replaced by auto-registration
|
||||
|
||||
# pylint: disable=E0604
|
||||
__all__ = [CodeResponse,
|
||||
NumericalResponse,
|
||||
FormulaResponse,
|
||||
|
||||
Reference in New Issue
Block a user