Commit 42c543b7 by Ned Batchelder

Merge pull request #2183 from edx/ned/add-i18n-for-inputtypes

Ned/add i18n for inputtypes
parents e74ddf81 fe5d2c74
...@@ -63,37 +63,79 @@ log = logging.getLogger(__name__) ...@@ -63,37 +63,79 @@ log = logging.getLogger(__name__)
# main class for this module # main class for this module
class LoncapaSystem(object):
"""
An encapsulation of resources needed from the outside.
These interfaces are collected here so that a caller of LoncapaProblem
can provide these resources however make sense for their environment, and
this code can remain independent.
Attributes:
i18n: an object implementing the `gettext.Translations` interface so
that we can use `.ugettext` to localize strings.
See :class:`ModuleSystem` for documentation of other attributes.
"""
def __init__( # pylint: disable=invalid-name
self,
ajax_url,
anonymous_student_id,
cache,
can_execute_unsafe_code,
DEBUG, # pylint: disable=invalid-name
filestore,
i18n,
node_path,
render_template,
seed, # Why do we do this if we have self.seed?
STATIC_URL, # pylint: disable=invalid-name
xqueue,
):
self.ajax_url = ajax_url
self.anonymous_student_id = anonymous_student_id
self.cache = cache
self.can_execute_unsafe_code = can_execute_unsafe_code
self.DEBUG = DEBUG # pylint: disable=invalid-name
self.filestore = filestore
self.i18n = i18n
self.node_path = node_path
self.render_template = render_template
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
class LoncapaProblem(object): class LoncapaProblem(object):
''' '''
Main class for capa Problems. Main class for capa Problems.
''' '''
def __init__(self, problem_text, id, state=None, seed=None, system=None): def __init__(self, problem_text, id, capa_system, state=None, seed=None):
''' """
Initializes capa Problem. Initializes capa Problem.
Arguments: Arguments:
- problem_text (string): xml defining the problem problem_text (string): xml defining the problem.
- id (string): identifier for this problem; often a filename (no spaces) id (string): identifier for this problem, often a filename (no spaces).
- seed (int): random number generator seed (int) capa_system (LoncapaSystem): LoncapaSystem instance which provides OS,
- state (dict): containing the following keys: rendering, user context, and other resources.
- 'seed' - (int) random number generator seed state (dict): containing the following keys:
- 'student_answers' - (dict) maps input id to the stored answer for that input - `seed` (int) random number generator seed
- 'correct_map' (CorrectMap) a map of each input to their 'correctness' - `student_answers` (dict) maps input id to the stored answer for that input
- 'done' - (bool) indicates whether or not this problem is considered done - `correct_map` (CorrectMap) a map of each input to their 'correctness'
- 'input_state' - (dict) maps input_id to a dictionary that holds the state for that input - `done` (bool) indicates whether or not this problem is considered done
- system (ModuleSystem): ModuleSystem instance which provides OS, - `input_state` (dict) maps input_id to a dictionary that holds the state for that input
rendering, and user context seed (int): random number generator seed.
''' """
## Initialize class variables from state ## Initialize class variables from state
self.do_reset() self.do_reset()
self.problem_id = id self.problem_id = id
self.system = system self.capa_system = capa_system
if self.system is None:
raise Exception()
state = state or {} state = state or {}
...@@ -412,8 +454,8 @@ class LoncapaProblem(object): ...@@ -412,8 +454,8 @@ class LoncapaProblem(object):
filename = inc.get('file') filename = inc.get('file')
if filename is not None: if filename is not None:
try: try:
# open using ModuleSystem OSFS filestore # open using LoncapaSystem OSFS filestore
ifp = self.system.filestore.open(filename) ifp = self.capa_system.filestore.open(filename)
except Exception as err: except Exception as err:
log.warning( log.warning(
'Error %s in problem xml include: %s' % ( 'Error %s in problem xml include: %s' % (
...@@ -422,12 +464,12 @@ class LoncapaProblem(object): ...@@ -422,12 +464,12 @@ class LoncapaProblem(object):
) )
log.warning( log.warning(
'Cannot find file %s in %s' % ( 'Cannot find file %s in %s' % (
filename, self.system.filestore filename, self.capa_system.filestore
) )
) )
# if debugging, don't fail - just log error # if debugging, don't fail - just log error
# TODO (vshnayder): need real error handling, display to users # TODO (vshnayder): need real error handling, display to users
if not self.system.get('DEBUG'): if not self.capa_system.DEBUG:
raise raise
else: else:
continue continue
...@@ -443,7 +485,7 @@ class LoncapaProblem(object): ...@@ -443,7 +485,7 @@ class LoncapaProblem(object):
log.warning('Cannot parse XML in %s' % (filename)) log.warning('Cannot parse XML in %s' % (filename))
# if debugging, don't fail - just log error # if debugging, don't fail - just log error
# TODO (vshnayder): same as above # TODO (vshnayder): same as above
if not self.system.get('DEBUG'): if not self.capa_system.DEBUG:
raise raise
else: else:
continue continue
...@@ -476,9 +518,9 @@ class LoncapaProblem(object): ...@@ -476,9 +518,9 @@ class LoncapaProblem(object):
continue continue
# path is an absolute path or a path relative to the data dir # path is an absolute path or a path relative to the data dir
dir = os.path.join(self.system.filestore.root_path, dir) dir = os.path.join(self.capa_system.filestore.root_path, dir)
# Check that we are within the filestore tree. # Check that we are within the filestore tree.
reldir = os.path.relpath(dir, self.system.filestore.root_path) reldir = os.path.relpath(dir, self.capa_system.filestore.root_path)
if ".." in reldir: if ".." in reldir:
log.warning("Ignoring Python directory outside of course: %r" % dir) log.warning("Ignoring Python directory outside of course: %r" % dir)
continue continue
...@@ -527,9 +569,9 @@ class LoncapaProblem(object): ...@@ -527,9 +569,9 @@ class LoncapaProblem(object):
context, context,
random_seed=self.seed, random_seed=self.seed,
python_path=python_path, python_path=python_path,
cache=self.system.cache, cache=self.capa_system.cache,
slug=self.problem_id, slug=self.problem_id,
unsafely=self.system.can_execute_unsafe_code(), unsafely=self.capa_system.can_execute_unsafe_code(),
) )
except Exception as err: except Exception as err:
log.exception("Error while execing script code: " + all_code) log.exception("Error while execing script code: " + all_code)
...@@ -600,7 +642,7 @@ class LoncapaProblem(object): ...@@ -600,7 +642,7 @@ class LoncapaProblem(object):
input_type_cls = inputtypes.registry.get_class_for_tag(problemtree.tag) input_type_cls = inputtypes.registry.get_class_for_tag(problemtree.tag)
# save the input type so that we can make ajax calls on it if we need to # save the input type so that we can make ajax calls on it if we need to
self.inputs[input_id] = input_type_cls(self.system, problemtree, state) self.inputs[input_id] = input_type_cls(self.capa_system, problemtree, state)
return self.inputs[input_id].get_html() return self.inputs[input_id].get_html()
# let each Response render itself # let each Response render itself
...@@ -613,7 +655,7 @@ class LoncapaProblem(object): ...@@ -613,7 +655,7 @@ class LoncapaProblem(object):
# let each custom renderer render itself: # let each custom renderer render itself:
if problemtree.tag in customrender.registry.registered_tags(): if problemtree.tag in customrender.registry.registered_tags():
renderer_class = customrender.registry.get_class_for_tag(problemtree.tag) renderer_class = customrender.registry.get_class_for_tag(problemtree.tag)
renderer = renderer_class(self.system, problemtree) renderer = renderer_class(self.capa_system, problemtree)
return renderer.get_html() return renderer.get_html()
# otherwise, render children recursively, and copy over attributes # otherwise, render children recursively, and copy over attributes
...@@ -670,7 +712,7 @@ class LoncapaProblem(object): ...@@ -670,7 +712,7 @@ class LoncapaProblem(object):
# instantiate capa Response # instantiate capa Response
responsetype_cls = responsetypes.registry.get_class_for_tag(response.tag) responsetype_cls = responsetypes.registry.get_class_for_tag(response.tag)
responder = responsetype_cls(response, inputfields, self.context, self.system) responder = responsetype_cls(response, inputfields, self.context, self.capa_system)
# save in list in self # save in list in self
self.responders[response] = responder self.responders[response] = responder
......
...@@ -128,7 +128,7 @@ class InputTypeBase(object): ...@@ -128,7 +128,7 @@ class InputTypeBase(object):
""" """
Instantiate an InputType class. Arguments: Instantiate an InputType class. Arguments:
- system : ModuleSystem instance which provides OS, rendering, and user context. - system : LoncapaModule instance which provides OS, rendering, and user context.
Specifically, must have a render_template function. Specifically, must have a render_template function.
- xml : Element tree of this Input element - xml : Element tree of this Input element
- state : a dictionary with optional keys: - state : a dictionary with optional keys:
...@@ -146,7 +146,7 @@ class InputTypeBase(object): ...@@ -146,7 +146,7 @@ class InputTypeBase(object):
self.xml = xml self.xml = xml
self.tag = xml.tag self.tag = xml.tag
self.system = system self.capa_system = system
# NOTE: ID should only come from one place. If it comes from multiple, # NOTE: ID should only come from one place. If it comes from multiple,
# we use state first, XML second (in case the xml changed, but we have # we use state first, XML second (in case the xml changed, but we have
...@@ -257,7 +257,7 @@ class InputTypeBase(object): ...@@ -257,7 +257,7 @@ class InputTypeBase(object):
'value': self.value, 'value': self.value,
'status': self.status, 'status': self.status,
'msg': self.msg, 'msg': self.msg,
'STATIC_URL': self.system.STATIC_URL, 'STATIC_URL': self.capa_system.STATIC_URL,
} }
context.update((a, v) for ( context.update((a, v) for (
a, v) in self.loaded_attributes.iteritems() if a in self.to_render) a, v) in self.loaded_attributes.iteritems() if a in self.to_render)
...@@ -282,7 +282,7 @@ class InputTypeBase(object): ...@@ -282,7 +282,7 @@ class InputTypeBase(object):
context = self._get_render_context() context = self._get_render_context()
html = self.system.render_template(self.template, context) html = self.capa_system.render_template(self.template, context)
return etree.XML(html) return etree.XML(html)
...@@ -505,9 +505,9 @@ class JSInput(InputTypeBase): ...@@ -505,9 +505,9 @@ class JSInput(InputTypeBase):
def _extra_context(self): def _extra_context(self):
context = { context = {
'jschannel_loader': '{static_url}js/capa/src/jschannel.js'.format( 'jschannel_loader': '{static_url}js/capa/src/jschannel.js'.format(
static_url=self.system.STATIC_URL), static_url=self.capa_system.STATIC_URL),
'jsinput_loader': '{static_url}js/capa/src/jsinput.js'.format( 'jsinput_loader': '{static_url}js/capa/src/jsinput.js'.format(
static_url=self.system.STATIC_URL), static_url=self.capa_system.STATIC_URL),
'saved_state': self.value 'saved_state': self.value
} }
...@@ -822,18 +822,19 @@ class MatlabInput(CodeInput): ...@@ -822,18 +822,19 @@ class MatlabInput(CodeInput):
- 'message' - message to be rendered in case of error - 'message' - message to be rendered in case of error
''' '''
# only send data if xqueue exists # only send data if xqueue exists
if self.system.xqueue is None: if self.capa_system.xqueue is None:
return {'success': False, 'message': 'Cannot connect to the queue'} return {'success': False, 'message': 'Cannot connect to the queue'}
# pull relevant info out of get # pull relevant info out of get
response = data['submission'] response = data['submission']
# construct xqueue headers # construct xqueue headers
qinterface = self.system.xqueue['interface'] qinterface = self.capa_system.xqueue['interface']
qtime = datetime.utcnow().strftime(xqueue_interface.dateformat) qtime = datetime.utcnow().strftime(xqueue_interface.dateformat)
callback_url = self.system.xqueue['construct_callback']('ungraded_response') callback_url = self.capa_system.xqueue['construct_callback']('ungraded_response')
anonymous_student_id = self.system.anonymous_student_id anonymous_student_id = self.capa_system.anonymous_student_id
queuekey = xqueue_interface.make_hashkey(str(self.system.seed) + qtime + # TODO: Why is this using self.capa_system.seed when we have self.seed???
queuekey = xqueue_interface.make_hashkey(str(self.capa_system.seed) + qtime +
anonymous_student_id + anonymous_student_id +
self.input_id) self.input_id)
xheader = xqueue_interface.make_xheader( xheader = xqueue_interface.make_xheader(
...@@ -1006,7 +1007,7 @@ class ChemicalEquationInput(InputTypeBase): ...@@ -1006,7 +1007,7 @@ class ChemicalEquationInput(InputTypeBase):
""" """
return { return {
'previewer': '{static_url}js/capa/chemical_equation_preview.js'.format( 'previewer': '{static_url}js/capa/chemical_equation_preview.js'.format(
static_url=self.system.STATIC_URL), static_url=self.capa_system.STATIC_URL),
} }
def handle_ajax(self, dispatch, data): def handle_ajax(self, dispatch, data):
...@@ -1091,7 +1092,7 @@ class FormulaEquationInput(InputTypeBase): ...@@ -1091,7 +1092,7 @@ class FormulaEquationInput(InputTypeBase):
return { return {
'previewer': '{static_url}js/capa/src/formula_equation_preview.js'.format( 'previewer': '{static_url}js/capa/src/formula_equation_preview.js'.format(
static_url=self.system.STATIC_URL), static_url=self.capa_system.STATIC_URL),
'reported_status': reported_status, 'reported_status': reported_status,
} }
...@@ -1274,7 +1275,7 @@ class EditAMoleculeInput(InputTypeBase): ...@@ -1274,7 +1275,7 @@ class EditAMoleculeInput(InputTypeBase):
""" """
context = { context = {
'applet_loader': '{static_url}js/capa/editamolecule.js'.format( 'applet_loader': '{static_url}js/capa/editamolecule.js'.format(
static_url=self.system.STATIC_URL), static_url=self.capa_system.STATIC_URL),
} }
return context return context
...@@ -1310,7 +1311,7 @@ class DesignProtein2dInput(InputTypeBase): ...@@ -1310,7 +1311,7 @@ class DesignProtein2dInput(InputTypeBase):
""" """
context = { context = {
'applet_loader': '{static_url}js/capa/design-protein-2d.js'.format( 'applet_loader': '{static_url}js/capa/design-protein-2d.js'.format(
static_url=self.system.STATIC_URL), static_url=self.capa_system.STATIC_URL),
} }
return context return context
...@@ -1346,7 +1347,7 @@ class EditAGeneInput(InputTypeBase): ...@@ -1346,7 +1347,7 @@ class EditAGeneInput(InputTypeBase):
""" """
context = { context = {
'applet_loader': '{static_url}js/capa/edit-a-gene.js'.format( 'applet_loader': '{static_url}js/capa/edit-a-gene.js'.format(
static_url=self.system.STATIC_URL), static_url=self.capa_system.STATIC_URL),
} }
return context return context
......
...@@ -129,20 +129,20 @@ class LoncapaResponse(object): ...@@ -129,20 +129,20 @@ class LoncapaResponse(object):
allowed_inputfields = [] allowed_inputfields = []
required_attributes = [] required_attributes = []
def __init__(self, xml, inputfields, context, system=None): def __init__(self, xml, inputfields, context, system):
''' '''
Init is passed the following arguments: Init is passed the following arguments:
- xml : ElementTree of this Response - xml : ElementTree of this Response
- inputfields : ordered list of ElementTrees for each input entry field in this Response - inputfields : ordered list of ElementTrees for each input entry field in this Response
- context : script processor context - context : script processor context
- system : ModuleSystem instance which provides OS, rendering, and user context - system : LoncapaSystem instance which provides OS, rendering, and user context
''' '''
self.xml = xml self.xml = xml
self.inputfields = inputfields self.inputfields = inputfields
self.context = context self.context = context
self.system = system self.capa_system = system
self.id = xml.get('id') self.id = xml.get('id')
...@@ -298,7 +298,7 @@ class LoncapaResponse(object): ...@@ -298,7 +298,7 @@ class LoncapaResponse(object):
python_path=self.context['python_path'], python_path=self.context['python_path'],
slug=self.id, slug=self.id,
random_seed=self.context['seed'], random_seed=self.context['seed'],
unsafely=self.system.can_execute_unsafe_code(), unsafely=self.capa_system.can_execute_unsafe_code(),
) )
except Exception as err: except Exception as err:
msg = 'Error %s in evaluating hint function %s' % (err, hintfn) msg = 'Error %s in evaluating hint function %s' % (err, hintfn)
...@@ -444,7 +444,7 @@ class JavascriptResponse(LoncapaResponse): ...@@ -444,7 +444,7 @@ class JavascriptResponse(LoncapaResponse):
# manually being compiled to DATA_DIR/js/compiled. # manually being compiled to DATA_DIR/js/compiled.
# latestTimestamp = 0 # latestTimestamp = 0
# basepath = self.system.filestore.root_path + '/js/' # basepath = self.capa_system.filestore.root_path + '/js/'
# for filename in (self.display_dependencies + [self.display]): # for filename in (self.display_dependencies + [self.display]):
# filepath = basepath + filename # filepath = basepath + filename
# timestamp = os.stat(filepath).st_mtime # timestamp = os.stat(filepath).st_mtime
...@@ -467,7 +467,7 @@ class JavascriptResponse(LoncapaResponse): ...@@ -467,7 +467,7 @@ class JavascriptResponse(LoncapaResponse):
# outfile.close() # outfile.close()
# TODO this should also be fixed when the above is fixed. # TODO this should also be fixed when the above is fixed.
filename = self.system.ajax_url.split('/')[-1] + '.js' filename = self.capa_system.ajax_url.split('/')[-1] + '.js'
self.display_filename = 'compiled/' + filename self.display_filename = 'compiled/' + filename
def parse_xml(self): def parse_xml(self):
...@@ -510,16 +510,16 @@ class JavascriptResponse(LoncapaResponse): ...@@ -510,16 +510,16 @@ class JavascriptResponse(LoncapaResponse):
def get_node_env(self): def get_node_env(self):
js_dir = os.path.join(self.system.filestore.root_path, 'js') js_dir = os.path.join(self.capa_system.filestore.root_path, 'js')
tmp_env = os.environ.copy() tmp_env = os.environ.copy()
node_path = self.system.node_path + ":" + os.path.normpath(js_dir) node_path = self.capa_system.node_path + ":" + os.path.normpath(js_dir)
tmp_env["NODE_PATH"] = node_path tmp_env["NODE_PATH"] = node_path
return tmp_env return tmp_env
def call_node(self, args): def call_node(self, args):
# Node.js code is un-sandboxed. If the XModuleSystem says we aren't # Node.js code is un-sandboxed. If the LoncapaSystem says we aren't
# allowed to run unsafe code, then stop now. # allowed to run unsafe code, then stop now.
if not self.system.can_execute_unsafe_code(): if not self.capa_system.can_execute_unsafe_code():
raise LoncapaProblemError("Execution of unsafe Javascript code is not allowed.") raise LoncapaProblemError("Execution of unsafe Javascript code is not allowed.")
subprocess_args = ["node"] subprocess_args = ["node"]
...@@ -875,8 +875,9 @@ class NumericalResponse(LoncapaResponse): ...@@ -875,8 +875,9 @@ class NumericalResponse(LoncapaResponse):
correct_ans = evaluator({}, {}, self.correct_answer) correct_ans = evaluator({}, {}, self.correct_answer)
except Exception: except Exception:
log.debug("Content error--answer '%s' is not a valid number", self.correct_answer) log.debug("Content error--answer '%s' is not a valid number", self.correct_answer)
_ = self.capa_system.i18n.ugettext
raise StudentInputError( raise StudentInputError(
"There was a problem with the staff answer to this problem" _("There was a problem with the staff answer to this problem")
) )
return correct_ans return correct_ans
...@@ -1154,7 +1155,7 @@ class CustomResponse(LoncapaResponse): ...@@ -1154,7 +1155,7 @@ class CustomResponse(LoncapaResponse):
python_path=self.context['python_path'], python_path=self.context['python_path'],
slug=self.id, slug=self.id,
random_seed=self.context['seed'], random_seed=self.context['seed'],
unsafely=self.system.can_execute_unsafe_code(), unsafely=self.capa_system.can_execute_unsafe_code(),
) )
return globals_dict['cfn_return'] return globals_dict['cfn_return']
return check_function return check_function
...@@ -1169,8 +1170,8 @@ class CustomResponse(LoncapaResponse): ...@@ -1169,8 +1170,8 @@ class CustomResponse(LoncapaResponse):
else: else:
answer_src = answer.get('src') answer_src = answer.get('src')
if answer_src is not None: if answer_src is not None:
self.code = self.system.filesystem.open( # TODO: this code seems not to be used any more since self.capa_system.filesystem doesn't exist.
'src/' + answer_src).read() self.code = self.capa_system.filesystem.open('src/' + answer_src).read()
else: else:
self.code = answer.text self.code = answer.text
...@@ -1249,8 +1250,8 @@ class CustomResponse(LoncapaResponse): ...@@ -1249,8 +1250,8 @@ class CustomResponse(LoncapaResponse):
'testdat': 'hello world', 'testdat': 'hello world',
}) })
# pass self.system.debug to cfn # Pass DEBUG to the check function.
self.context['debug'] = self.system.DEBUG self.context['debug'] = self.capa_system.DEBUG
# Run the check function # Run the check function
self.execute_check_function(idset, submission) self.execute_check_function(idset, submission)
...@@ -1275,10 +1276,10 @@ class CustomResponse(LoncapaResponse): ...@@ -1275,10 +1276,10 @@ class CustomResponse(LoncapaResponse):
safe_exec.safe_exec( safe_exec.safe_exec(
self.code, self.code,
self.context, self.context,
cache=self.system.cache, cache=self.capa_system.cache,
slug=self.id, slug=self.id,
random_seed=self.context['seed'], random_seed=self.context['seed'],
unsafely=self.system.can_execute_unsafe_code(), unsafely=self.capa_system.can_execute_unsafe_code(),
) )
except Exception as err: except Exception as err:
self._handle_exec_exception(err) self._handle_exec_exception(err)
...@@ -1470,18 +1471,21 @@ ScoreMessage = namedtuple('ScoreMessage', ['valid', 'correct', 'points', 'msg']) ...@@ -1470,18 +1471,21 @@ ScoreMessage = namedtuple('ScoreMessage', ['valid', 'correct', 'points', 'msg'])
@registry.register @registry.register
class CodeResponse(LoncapaResponse): class CodeResponse(LoncapaResponse):
""" """
Grade student code using an external queueing server, called 'xqueue' Grade student code using an external queueing server, called 'xqueue'.
Expects 'xqueue' dict in ModuleSystem with the following keys that are needed by CodeResponse: Expects 'xqueue' dict in LoncapaSystem with the following keys that are
system.xqueue = { 'interface': XqueueInterface object, needed by CodeResponse::
'construct_callback': Per-StudentModule callback URL
constructor, defaults to using 'score_update' capa_system.xqueue = {
as the correct dispatch (function), 'interface': XQueueInterface object.
'default_queuename': Default queuename to submit request (string) 'construct_callback': Per-StudentModule callback URL constructor,
defaults to using 'score_update' as the correct dispatch (function).
'default_queuename': Default queue name to submit request (string).
} }
External requests are only submitted for student submission grading External requests are only submitted for student submission grading, not
(i.e. and not for getting reference answers) for getting reference answers.
""" """
tags = ['coderesponse'] tags = ['coderesponse']
...@@ -1504,8 +1508,8 @@ class CodeResponse(LoncapaResponse): ...@@ -1504,8 +1508,8 @@ class CodeResponse(LoncapaResponse):
self.url = xml.get('url', None) self.url = xml.get('url', None)
# We do not support xqueue within Studio. # We do not support xqueue within Studio.
if self.system.xqueue is not None: if self.capa_system.xqueue is not None:
default_queuename = self.system.xqueue['default_queuename'] default_queuename = self.capa_system.xqueue['default_queuename']
else: else:
default_queuename = None default_queuename = None
self.queue_name = xml.get('queuename', default_queuename) self.queue_name = xml.get('queuename', default_queuename)
...@@ -1548,7 +1552,7 @@ class CodeResponse(LoncapaResponse): ...@@ -1548,7 +1552,7 @@ class CodeResponse(LoncapaResponse):
raise Exception(err) raise Exception(err)
# We do not support xqueue within Studio. # We do not support xqueue within Studio.
if self.system.xqueue is None: if self.capa_system.xqueue is None:
cmap = CorrectMap() cmap = CorrectMap()
cmap.set(self.answer_id, queuestate=None, cmap.set(self.answer_id, queuestate=None,
msg='Error checking problem: no external queueing server is configured.') msg='Error checking problem: no external queueing server is configured.')
...@@ -1557,16 +1561,16 @@ class CodeResponse(LoncapaResponse): ...@@ -1557,16 +1561,16 @@ class CodeResponse(LoncapaResponse):
# Prepare xqueue request # Prepare xqueue request
#------------------------------------------------------------ #------------------------------------------------------------
qinterface = self.system.xqueue['interface'] qinterface = self.capa_system.xqueue['interface']
qtime = datetime.strftime(datetime.now(UTC), xqueue_interface.dateformat) qtime = datetime.strftime(datetime.now(UTC), xqueue_interface.dateformat)
anonymous_student_id = self.system.anonymous_student_id anonymous_student_id = self.capa_system.anonymous_student_id
# Generate header # Generate header
queuekey = xqueue_interface.make_hashkey( queuekey = xqueue_interface.make_hashkey(
str(self.system.seed) + qtime + anonymous_student_id + self.answer_id str(self.capa_system.seed) + qtime + anonymous_student_id + self.answer_id
) )
callback_url = self.system.xqueue['construct_callback']() callback_url = self.capa_system.xqueue['construct_callback']()
xheader = xqueue_interface.make_xheader( xheader = xqueue_interface.make_xheader(
lms_callback_url=callback_url, lms_callback_url=callback_url,
lms_key=queuekey, lms_key=queuekey,
...@@ -1748,8 +1752,8 @@ class ExternalResponse(LoncapaResponse): ...@@ -1748,8 +1752,8 @@ class ExternalResponse(LoncapaResponse):
if answer is not None: if answer is not None:
answer_src = answer.get('src') answer_src = answer.get('src')
if answer_src is not None: if answer_src is not None:
self.code = self.system.filesystem.open( # TODO: this code seems not to be used any more since self.capa_system.filesystem doesn't exist.
'src/' + answer_src).read() self.code = self.capa_system.filesystem.open('src/' + answer_src).read()
else: else:
self.code = answer.text self.code = answer.text
else: else:
...@@ -1791,7 +1795,7 @@ class ExternalResponse(LoncapaResponse): ...@@ -1791,7 +1795,7 @@ class ExternalResponse(LoncapaResponse):
log.error(msg) log.error(msg)
raise Exception(msg) raise Exception(msg)
if self.system.DEBUG: if self.capa_system.DEBUG:
log.info('response = %s', req.text) log.info('response = %s', req.text)
if (not req.text) or (not req.text.strip()): if (not req.text) or (not req.text.strip()):
...@@ -1830,7 +1834,7 @@ class ExternalResponse(LoncapaResponse): ...@@ -1830,7 +1834,7 @@ class ExternalResponse(LoncapaResponse):
rxml = self.do_external_request('get_score', extra_payload) rxml = self.do_external_request('get_score', extra_payload)
except Exception as err: # pylint: disable=W0703 except Exception as err: # pylint: disable=W0703
log.error('Error %s', err) log.error('Error %s', err)
if self.system.DEBUG: if self.capa_system.DEBUG:
cmap.set_dict(dict(zip(sorted( cmap.set_dict(dict(zip(sorted(
self.answer_ids), ['incorrect'] * len(idset)))) self.answer_ids), ['incorrect'] * len(idset))))
cmap.set_property( cmap.set_property(
...@@ -1862,7 +1866,7 @@ class ExternalResponse(LoncapaResponse): ...@@ -1862,7 +1866,7 @@ class ExternalResponse(LoncapaResponse):
exans = json.loads(rxml.find('expected').text) exans = json.loads(rxml.find('expected').text)
except Exception as err: # pylint: disable=W0703 except Exception as err: # pylint: disable=W0703
log.error('Error %s', err) log.error('Error %s', err)
if self.system.DEBUG: if self.capa_system.DEBUG:
msg = '<span class="inline-error">%s</span>' % str( msg = '<span class="inline-error">%s</span>' % str(
err).replace('<', '&lt;') err).replace('<', '&lt;')
exans = [''] * len(self.answer_ids) exans = [''] * len(self.answer_ids)
...@@ -2100,7 +2104,7 @@ class SchematicResponse(LoncapaResponse): ...@@ -2100,7 +2104,7 @@ class SchematicResponse(LoncapaResponse):
answer_src = answer.get('src') answer_src = answer.get('src')
if answer_src is not None: if answer_src is not None:
# Untested; never used # Untested; never used
self.code = self.system.filestore.open('src/' + answer_src).read() self.code = self.capa_system.filestore.open('src/' + answer_src).read()
else: else:
self.code = answer.text self.code = answer.text
...@@ -2114,10 +2118,10 @@ class SchematicResponse(LoncapaResponse): ...@@ -2114,10 +2118,10 @@ class SchematicResponse(LoncapaResponse):
safe_exec.safe_exec( safe_exec.safe_exec(
self.code, self.code,
self.context, self.context,
cache=self.system.cache, cache=self.capa_system.cache,
slug=self.id, slug=self.id,
random_seed=self.context['seed'], random_seed=self.context['seed'],
unsafely=self.system.can_execute_unsafe_code(), unsafely=self.capa_system.can_execute_unsafe_code(),
) )
except Exception as err: except Exception as err:
msg = 'Error %s in evaluating SchematicResponse' % err msg = 'Error %s in evaluating SchematicResponse' % err
......
import fs.osfs """Tools for helping with testing capa."""
import gettext
import os import os
import os.path import os.path
from capa.capa_problem import LoncapaProblem import fs.osfs
from xmodule.x_module import ModuleSystem
from capa.capa_problem import LoncapaProblem, LoncapaSystem
from mock import Mock, MagicMock from mock import Mock, MagicMock
import xml.sax.saxutils as saxutils import xml.sax.saxutils as saxutils
...@@ -26,34 +29,29 @@ xqueue_interface = MagicMock() ...@@ -26,34 +29,29 @@ xqueue_interface = MagicMock()
xqueue_interface.send_to_queue.return_value = (0, 'Success!') xqueue_interface.send_to_queue.return_value = (0, 'Success!')
def test_system(): def test_capa_system():
""" """
Construct a mock ModuleSystem instance. Construct a mock LoncapaSystem instance.
""" """
the_system = Mock( the_system = Mock(
spec=ModuleSystem, spec=LoncapaSystem,
ajax_url='/dummy-ajax-url', ajax_url='/dummy-ajax-url',
STATIC_URL='/dummy-static/', anonymous_student_id='student',
cache=None,
can_execute_unsafe_code=lambda: False,
DEBUG=True, DEBUG=True,
track_function=Mock(), filestore=fs.osfs.OSFS(os.path.join(TEST_DIR, "test_files")),
get_module=Mock(), i18n=gettext.NullTranslations(),
node_path=os.environ.get("NODE_PATH", "/usr/local/lib/node_modules"),
render_template=tst_render_template, render_template=tst_render_template,
replace_urls=Mock(),
user=Mock(),
seed=0, seed=0,
filestore=fs.osfs.OSFS(os.path.join(TEST_DIR, "test_files")), STATIC_URL='/dummy-static/',
debug=True,
hostname="edx.org",
xqueue={'interface': xqueue_interface, 'construct_callback': calledback_url, 'default_queuename': 'testqueue', 'waittime': 10}, xqueue={'interface': xqueue_interface, 'construct_callback': calledback_url, 'default_queuename': 'testqueue', 'waittime': 10},
node_path=os.environ.get("NODE_PATH", "/usr/local/lib/node_modules"),
anonymous_student_id='student',
cache=None,
can_execute_unsafe_code=lambda: False,
) )
return the_system return the_system
def new_loncapa_problem(xml, system=None): def new_loncapa_problem(xml, capa_system=None):
"""Construct a `LoncapaProblem` suitable for unit tests.""" """Construct a `LoncapaProblem` suitable for unit tests."""
return LoncapaProblem(xml, id='1', seed=723, system=system or test_system()) return LoncapaProblem(xml, id='1', seed=723, capa_system=capa_system or test_capa_system())
...@@ -2,7 +2,7 @@ from lxml import etree ...@@ -2,7 +2,7 @@ from lxml import etree
import unittest import unittest
import xml.sax.saxutils as saxutils import xml.sax.saxutils as saxutils
from . import test_system from . import test_capa_system
from capa import customrender from capa import customrender
# just a handy shortcut # just a handy shortcut
...@@ -11,7 +11,7 @@ lookup_tag = customrender.registry.get_class_for_tag ...@@ -11,7 +11,7 @@ lookup_tag = customrender.registry.get_class_for_tag
def extract_context(xml): def extract_context(xml):
""" """
Given an xml element corresponding to the output of test_system.render_template, get back the Given an xml element corresponding to the output of test_capa_system.render_template, get back the
original context original context
""" """
return eval(xml.text) return eval(xml.text)
...@@ -26,7 +26,7 @@ class HelperTest(unittest.TestCase): ...@@ -26,7 +26,7 @@ class HelperTest(unittest.TestCase):
Make sure that our helper function works! Make sure that our helper function works!
''' '''
def check(self, d): def check(self, d):
xml = etree.XML(test_system().render_template('blah', d)) xml = etree.XML(test_capa_system().render_template('blah', d))
self.assertEqual(d, extract_context(xml)) self.assertEqual(d, extract_context(xml))
def test_extract_context(self): def test_extract_context(self):
...@@ -46,11 +46,11 @@ class SolutionRenderTest(unittest.TestCase): ...@@ -46,11 +46,11 @@ class SolutionRenderTest(unittest.TestCase):
xml_str = """<solution id="solution_12">{s}</solution>""".format(s=solution) xml_str = """<solution id="solution_12">{s}</solution>""".format(s=solution)
element = etree.fromstring(xml_str) element = etree.fromstring(xml_str)
renderer = lookup_tag('solution')(test_system(), element) renderer = lookup_tag('solution')(test_capa_system(), element)
self.assertEqual(renderer.id, 'solution_12') self.assertEqual(renderer.id, 'solution_12')
# Our test_system "renders" templates to a div with the repr of the context. # Our test_capa_system "renders" templates to a div with the repr of the context.
xml = renderer.get_html() xml = renderer.get_html()
context = extract_context(xml) context = extract_context(xml)
self.assertEqual(context, {'id': 'solution_12'}) self.assertEqual(context, {'id': 'solution_12'})
...@@ -65,7 +65,7 @@ class MathRenderTest(unittest.TestCase): ...@@ -65,7 +65,7 @@ class MathRenderTest(unittest.TestCase):
xml_str = """<math>{tex}</math>""".format(tex=latex_in) xml_str = """<math>{tex}</math>""".format(tex=latex_in)
element = etree.fromstring(xml_str) element = etree.fromstring(xml_str)
renderer = lookup_tag('math')(test_system(), element) renderer = lookup_tag('math')(test_capa_system(), element)
self.assertEqual(renderer.mathstr, mathjax_out) self.assertEqual(renderer.mathstr, mathjax_out)
......
...@@ -6,14 +6,14 @@ import textwrap ...@@ -6,14 +6,14 @@ import textwrap
import mock import mock
from .response_xml_factory import StringResponseXMLFactory, CustomResponseXMLFactory from .response_xml_factory import StringResponseXMLFactory, CustomResponseXMLFactory
from . import test_system, new_loncapa_problem from . import test_capa_system, new_loncapa_problem
class CapaHtmlRenderTest(unittest.TestCase): class CapaHtmlRenderTest(unittest.TestCase):
def setUp(self): def setUp(self):
super(CapaHtmlRenderTest, self).setUp() super(CapaHtmlRenderTest, self).setUp()
self.system = test_system() self.capa_system = test_capa_system()
def test_blank_problem(self): def test_blank_problem(self):
""" """
...@@ -44,7 +44,7 @@ class CapaHtmlRenderTest(unittest.TestCase): ...@@ -44,7 +44,7 @@ class CapaHtmlRenderTest(unittest.TestCase):
""") """)
# Create the problem # Create the problem
problem = new_loncapa_problem(xml_str, system=self.system) problem = new_loncapa_problem(xml_str, capa_system=self.capa_system)
# Render the HTML # Render the HTML
rendered_html = etree.XML(problem.get_html()) rendered_html = etree.XML(problem.get_html())
...@@ -119,12 +119,12 @@ class CapaHtmlRenderTest(unittest.TestCase): ...@@ -119,12 +119,12 @@ class CapaHtmlRenderTest(unittest.TestCase):
xml_str = StringResponseXMLFactory().build_xml(**kwargs) xml_str = StringResponseXMLFactory().build_xml(**kwargs)
# Mock out the template renderer # Mock out the template renderer
the_system = test_system() the_system = test_capa_system()
the_system.render_template = mock.Mock() the_system.render_template = mock.Mock()
the_system.render_template.return_value = "<div>Input Template Render</div>" the_system.render_template.return_value = "<div>Input Template Render</div>"
# Create the problem and render the HTML # Create the problem and render the HTML
problem = new_loncapa_problem(xml_str, system=the_system) problem = new_loncapa_problem(xml_str, capa_system=the_system)
rendered_html = etree.XML(problem.get_html()) rendered_html = etree.XML(problem.get_html())
# Expect problem has been turned into a <div> # Expect problem has been turned into a <div>
...@@ -253,7 +253,7 @@ class CapaHtmlRenderTest(unittest.TestCase): ...@@ -253,7 +253,7 @@ class CapaHtmlRenderTest(unittest.TestCase):
self.assertRegexpMatches(the_html, r"<div>\s+</div>") self.assertRegexpMatches(the_html, r"<div>\s+</div>")
def _create_test_file(self, path, content_str): def _create_test_file(self, path, content_str):
test_fp = self.system.filestore.open(path, "w") test_fp = self.capa_system.filestore.open(path, "w")
test_fp.write(content_str) test_fp.write(content_str)
test_fp.close() test_fp.close()
......
...@@ -22,7 +22,7 @@ from lxml import etree ...@@ -22,7 +22,7 @@ from lxml import etree
import unittest import unittest
import xml.sax.saxutils as saxutils import xml.sax.saxutils as saxutils
from . import test_system from . import test_capa_system
from capa import inputtypes from capa import inputtypes
from mock import ANY, patch from mock import ANY, patch
from pyparsing import ParseException from pyparsing import ParseException
...@@ -47,7 +47,7 @@ class OptionInputTest(unittest.TestCase): ...@@ -47,7 +47,7 @@ class OptionInputTest(unittest.TestCase):
state = {'value': 'Down', state = {'value': 'Down',
'id': 'sky_input', 'id': 'sky_input',
'status': 'answered'} 'status': 'answered'}
option_input = lookup_tag('optioninput')(test_system(), element, state) option_input = lookup_tag('optioninput')(test_capa_system(), element, state)
context = option_input._get_render_context() # pylint: disable=W0212 context = option_input._get_render_context() # pylint: disable=W0212
...@@ -106,7 +106,7 @@ class ChoiceGroupTest(unittest.TestCase): ...@@ -106,7 +106,7 @@ class ChoiceGroupTest(unittest.TestCase):
'id': 'sky_input', 'id': 'sky_input',
'status': 'answered'} 'status': 'answered'}
the_input = lookup_tag(tag)(test_system(), element, state) the_input = lookup_tag(tag)(test_capa_system(), element, state)
context = the_input._get_render_context() # pylint: disable=W0212 context = the_input._get_render_context() # pylint: disable=W0212
...@@ -159,7 +159,7 @@ class JavascriptInputTest(unittest.TestCase): ...@@ -159,7 +159,7 @@ class JavascriptInputTest(unittest.TestCase):
element = etree.fromstring(xml_str) element = etree.fromstring(xml_str)
state = {'value': '3', } state = {'value': '3', }
the_input = lookup_tag('javascriptinput')(test_system(), element, state) the_input = lookup_tag('javascriptinput')(test_capa_system(), element, state)
context = the_input._get_render_context() # pylint: disable=W0212 context = the_input._get_render_context() # pylint: disable=W0212
...@@ -190,7 +190,7 @@ class TextLineTest(unittest.TestCase): ...@@ -190,7 +190,7 @@ class TextLineTest(unittest.TestCase):
element = etree.fromstring(xml_str) element = etree.fromstring(xml_str)
state = {'value': 'BumbleBee', } state = {'value': 'BumbleBee', }
the_input = lookup_tag('textline')(test_system(), element, state) the_input = lookup_tag('textline')(test_capa_system(), element, state)
context = the_input._get_render_context() # pylint: disable=W0212 context = the_input._get_render_context() # pylint: disable=W0212
...@@ -221,7 +221,7 @@ class TextLineTest(unittest.TestCase): ...@@ -221,7 +221,7 @@ class TextLineTest(unittest.TestCase):
element = etree.fromstring(xml_str) element = etree.fromstring(xml_str)
state = {'value': 'BumbleBee', } state = {'value': 'BumbleBee', }
the_input = lookup_tag('textline')(test_system(), element, state) the_input = lookup_tag('textline')(test_capa_system(), element, state)
context = the_input._get_render_context() # pylint: disable=W0212 context = the_input._get_render_context() # pylint: disable=W0212
...@@ -264,7 +264,7 @@ class TextLineTest(unittest.TestCase): ...@@ -264,7 +264,7 @@ class TextLineTest(unittest.TestCase):
element = etree.fromstring(xml_str) element = etree.fromstring(xml_str)
state = {'value': 'BumbleBee', } state = {'value': 'BumbleBee', }
the_input = lookup_tag('textline')(test_system(), element, state) the_input = lookup_tag('textline')(test_capa_system(), element, state)
context = the_input._get_render_context() # pylint: disable=W0212 context = the_input._get_render_context() # pylint: disable=W0212
...@@ -305,7 +305,7 @@ class FileSubmissionTest(unittest.TestCase): ...@@ -305,7 +305,7 @@ class FileSubmissionTest(unittest.TestCase):
'status': 'incomplete', 'status': 'incomplete',
'feedback': {'message': '3'}, } 'feedback': {'message': '3'}, }
input_class = lookup_tag('filesubmission') input_class = lookup_tag('filesubmission')
the_input = input_class(test_system(), element, state) the_input = input_class(test_capa_system(), element, state)
context = the_input._get_render_context() # pylint: disable=W0212 context = the_input._get_render_context() # pylint: disable=W0212
...@@ -353,7 +353,7 @@ class CodeInputTest(unittest.TestCase): ...@@ -353,7 +353,7 @@ class CodeInputTest(unittest.TestCase):
'feedback': {'message': '3'}, } 'feedback': {'message': '3'}, }
input_class = lookup_tag('codeinput') input_class = lookup_tag('codeinput')
the_input = input_class(test_system(), element, state) the_input = input_class(test_capa_system(), element, state)
context = the_input._get_render_context() # pylint: disable=W0212 context = the_input._get_render_context() # pylint: disable=W0212
...@@ -405,7 +405,7 @@ class MatlabTest(unittest.TestCase): ...@@ -405,7 +405,7 @@ class MatlabTest(unittest.TestCase):
'feedback': {'message': '3'}, } 'feedback': {'message': '3'}, }
self.input_class = lookup_tag('matlabinput') self.input_class = lookup_tag('matlabinput')
self.the_input = self.input_class(test_system(), elt, state) self.the_input = self.input_class(test_capa_system(), elt, state)
def test_rendering(self): def test_rendering(self):
context = self.the_input._get_render_context() # pylint: disable=W0212 context = self.the_input._get_render_context() # pylint: disable=W0212
...@@ -436,7 +436,7 @@ class MatlabTest(unittest.TestCase): ...@@ -436,7 +436,7 @@ class MatlabTest(unittest.TestCase):
'feedback': {'message': '3'}, } 'feedback': {'message': '3'}, }
elt = etree.fromstring(self.xml) elt = etree.fromstring(self.xml)
the_input = self.input_class(test_system(), elt, state) the_input = self.input_class(test_capa_system(), elt, state)
context = the_input._get_render_context() # pylint: disable=W0212 context = the_input._get_render_context() # pylint: disable=W0212
expected = { expected = {
...@@ -466,7 +466,7 @@ class MatlabTest(unittest.TestCase): ...@@ -466,7 +466,7 @@ class MatlabTest(unittest.TestCase):
} }
elt = etree.fromstring(self.xml) elt = etree.fromstring(self.xml)
the_input = self.input_class(test_system(), elt, state) the_input = self.input_class(test_capa_system(), elt, state)
context = the_input._get_render_context() # pylint: disable=W0212 context = the_input._get_render_context() # pylint: disable=W0212
expected = { expected = {
'STATIC_URL': '/dummy-static/', 'STATIC_URL': '/dummy-static/',
...@@ -494,7 +494,7 @@ class MatlabTest(unittest.TestCase): ...@@ -494,7 +494,7 @@ class MatlabTest(unittest.TestCase):
} }
elt = etree.fromstring(self.xml) elt = etree.fromstring(self.xml)
the_input = self.input_class(test_system(), elt, state) the_input = self.input_class(test_capa_system(), elt, state)
context = the_input._get_render_context() # pylint: disable=W0212 context = the_input._get_render_context() # pylint: disable=W0212
expected = { expected = {
'STATIC_URL': '/dummy-static/', 'STATIC_URL': '/dummy-static/',
...@@ -519,7 +519,7 @@ class MatlabTest(unittest.TestCase): ...@@ -519,7 +519,7 @@ class MatlabTest(unittest.TestCase):
data = {'submission': 'x = 1234;'} data = {'submission': 'x = 1234;'}
response = self.the_input.handle_ajax("plot", data) response = self.the_input.handle_ajax("plot", data)
test_system().xqueue['interface'].send_to_queue.assert_called_with(header=ANY, body=ANY) test_capa_system().xqueue['interface'].send_to_queue.assert_called_with(header=ANY, body=ANY)
self.assertTrue(response['success']) self.assertTrue(response['success'])
self.assertTrue(self.the_input.input_state['queuekey'] is not None) self.assertTrue(self.the_input.input_state['queuekey'] is not None)
...@@ -528,7 +528,7 @@ class MatlabTest(unittest.TestCase): ...@@ -528,7 +528,7 @@ class MatlabTest(unittest.TestCase):
def test_plot_data_failure(self): def test_plot_data_failure(self):
data = {'submission': 'x = 1234;'} data = {'submission': 'x = 1234;'}
error_message = 'Error message!' error_message = 'Error message!'
test_system().xqueue['interface'].send_to_queue.return_value = (1, error_message) test_capa_system().xqueue['interface'].send_to_queue.return_value = (1, error_message)
response = self.the_input.handle_ajax("plot", data) response = self.the_input.handle_ajax("plot", data)
self.assertFalse(response['success']) self.assertFalse(response['success'])
self.assertEqual(response['message'], error_message) self.assertEqual(response['message'], error_message)
...@@ -544,7 +544,7 @@ class MatlabTest(unittest.TestCase): ...@@ -544,7 +544,7 @@ class MatlabTest(unittest.TestCase):
'feedback': {'message': '3'}, } 'feedback': {'message': '3'}, }
elt = etree.fromstring(self.xml) elt = etree.fromstring(self.xml)
the_input = self.input_class(test_system(), elt, state) the_input = self.input_class(test_capa_system(), elt, state)
inner_msg = 'hello!' inner_msg = 'hello!'
queue_msg = json.dumps({'msg': inner_msg}) queue_msg = json.dumps({'msg': inner_msg})
...@@ -562,7 +562,7 @@ class MatlabTest(unittest.TestCase): ...@@ -562,7 +562,7 @@ class MatlabTest(unittest.TestCase):
'feedback': {'message': '3'}, } 'feedback': {'message': '3'}, }
elt = etree.fromstring(self.xml) elt = etree.fromstring(self.xml)
the_input = self.input_class(test_system(), elt, state) the_input = self.input_class(test_capa_system(), elt, state)
inner_msg = 'hello!' inner_msg = 'hello!'
queue_msg = json.dumps({'msg': inner_msg}) queue_msg = json.dumps({'msg': inner_msg})
...@@ -601,7 +601,7 @@ class SchematicTest(unittest.TestCase): ...@@ -601,7 +601,7 @@ class SchematicTest(unittest.TestCase):
state = {'value': value, state = {'value': value,
'status': 'unsubmitted'} 'status': 'unsubmitted'}
the_input = lookup_tag('schematic')(test_system(), element, state) the_input = lookup_tag('schematic')(test_capa_system(), element, state)
context = the_input._get_render_context() # pylint: disable=W0212 context = the_input._get_render_context() # pylint: disable=W0212
...@@ -643,7 +643,7 @@ class ImageInputTest(unittest.TestCase): ...@@ -643,7 +643,7 @@ class ImageInputTest(unittest.TestCase):
state = {'value': value, state = {'value': value,
'status': 'unsubmitted'} 'status': 'unsubmitted'}
the_input = lookup_tag('imageinput')(test_system(), element, state) the_input = lookup_tag('imageinput')(test_capa_system(), element, state)
context = the_input._get_render_context() # pylint: disable=W0212 context = the_input._get_render_context() # pylint: disable=W0212
...@@ -697,7 +697,7 @@ class CrystallographyTest(unittest.TestCase): ...@@ -697,7 +697,7 @@ class CrystallographyTest(unittest.TestCase):
state = {'value': value, state = {'value': value,
'status': 'unsubmitted'} 'status': 'unsubmitted'}
the_input = lookup_tag('crystallography')(test_system(), element, state) the_input = lookup_tag('crystallography')(test_capa_system(), element, state)
context = the_input._get_render_context() # pylint: disable=W0212 context = the_input._get_render_context() # pylint: disable=W0212
...@@ -738,7 +738,7 @@ class VseprTest(unittest.TestCase): ...@@ -738,7 +738,7 @@ class VseprTest(unittest.TestCase):
state = {'value': value, state = {'value': value,
'status': 'unsubmitted'} 'status': 'unsubmitted'}
the_input = lookup_tag('vsepr_input')(test_system(), element, state) the_input = lookup_tag('vsepr_input')(test_capa_system(), element, state)
context = the_input._get_render_context() # pylint: disable=W0212 context = the_input._get_render_context() # pylint: disable=W0212
...@@ -768,7 +768,7 @@ class ChemicalEquationTest(unittest.TestCase): ...@@ -768,7 +768,7 @@ class ChemicalEquationTest(unittest.TestCase):
element = etree.fromstring(xml_str) element = etree.fromstring(xml_str)
state = {'value': 'H2OYeah', } state = {'value': 'H2OYeah', }
self.the_input = lookup_tag('chemicalequationinput')(test_system(), element, state) self.the_input = lookup_tag('chemicalequationinput')(test_capa_system(), element, state)
def test_rendering(self): def test_rendering(self):
''' Verify that the render context matches the expected render context''' ''' Verify that the render context matches the expected render context'''
...@@ -853,7 +853,7 @@ class FormulaEquationTest(unittest.TestCase): ...@@ -853,7 +853,7 @@ class FormulaEquationTest(unittest.TestCase):
element = etree.fromstring(xml_str) element = etree.fromstring(xml_str)
state = {'value': 'x^2+1/2'} state = {'value': 'x^2+1/2'}
self.the_input = lookup_tag('formulaequationinput')(test_system(), element, state) self.the_input = lookup_tag('formulaequationinput')(test_capa_system(), element, state)
def test_rendering(self): def test_rendering(self):
""" """
...@@ -1005,7 +1005,7 @@ class DragAndDropTest(unittest.TestCase): ...@@ -1005,7 +1005,7 @@ class DragAndDropTest(unittest.TestCase):
] ]
} }
the_input = lookup_tag('drag_and_drop_input')(test_system(), element, state) the_input = lookup_tag('drag_and_drop_input')(test_capa_system(), element, state)
context = the_input._get_render_context() # pylint: disable=W0212 context = the_input._get_render_context() # pylint: disable=W0212
expected = { expected = {
...@@ -1056,7 +1056,7 @@ class AnnotationInputTest(unittest.TestCase): ...@@ -1056,7 +1056,7 @@ class AnnotationInputTest(unittest.TestCase):
tag = 'annotationinput' tag = 'annotationinput'
the_input = lookup_tag(tag)(test_system(), element, state) the_input = lookup_tag(tag)(test_capa_system(), element, state)
context = the_input._get_render_context() # pylint: disable=W0212 context = the_input._get_render_context() # pylint: disable=W0212
...@@ -1146,7 +1146,7 @@ class TestChoiceText(unittest.TestCase): ...@@ -1146,7 +1146,7 @@ class TestChoiceText(unittest.TestCase):
'submitted_message': 'Answer received.' 'submitted_message': 'Answer received.'
} }
expected.update(state) expected.update(state)
the_input = lookup_tag(tag)(test_system(), element, state) the_input = lookup_tag(tag)(test_capa_system(), element, state)
context = the_input._get_render_context() # pylint: disable=W0212 context = the_input._get_render_context() # pylint: disable=W0212
self.assertEqual(context, expected) self.assertEqual(context, expected)
......
...@@ -13,7 +13,7 @@ import textwrap ...@@ -13,7 +13,7 @@ import textwrap
import requests import requests
import mock import mock
from . import new_loncapa_problem, test_system from . import new_loncapa_problem, test_capa_system
import calc import calc
from capa.responsetypes import LoncapaProblemError, \ from capa.responsetypes import LoncapaProblemError, \
...@@ -37,9 +37,9 @@ class ResponseTest(unittest.TestCase): ...@@ -37,9 +37,9 @@ class ResponseTest(unittest.TestCase):
if self.xml_factory_class: if self.xml_factory_class:
self.xml_factory = self.xml_factory_class() self.xml_factory = self.xml_factory_class()
def build_problem(self, system=None, **kwargs): def build_problem(self, capa_system=None, **kwargs):
xml = self.xml_factory.build_xml(**kwargs) xml = self.xml_factory.build_xml(**kwargs)
return new_loncapa_problem(xml, system=system) return new_loncapa_problem(xml, capa_system=capa_system)
def assert_grade(self, problem, submission, expected_correctness, msg=None): def assert_grade(self, problem, submission, expected_correctness, msg=None):
input_dict = {'1_2_1': submission} input_dict = {'1_2_1': submission}
...@@ -1022,10 +1022,10 @@ class JavascriptResponseTest(ResponseTest): ...@@ -1022,10 +1022,10 @@ class JavascriptResponseTest(ResponseTest):
coffee_file_path = os.path.dirname(__file__) + "/test_files/js/*.coffee" coffee_file_path = os.path.dirname(__file__) + "/test_files/js/*.coffee"
os.system("node_modules/.bin/coffee -c %s" % (coffee_file_path)) os.system("node_modules/.bin/coffee -c %s" % (coffee_file_path))
system = test_system() capa_system = test_capa_system()
system.can_execute_unsafe_code = lambda: True capa_system.can_execute_unsafe_code = lambda: True
problem = self.build_problem( problem = self.build_problem(
system=system, capa_system=capa_system,
generator_src="test_problem_generator.js", generator_src="test_problem_generator.js",
grader_src="test_problem_grader.js", grader_src="test_problem_grader.js",
display_class="TestProblemDisplay", display_class="TestProblemDisplay",
...@@ -1040,12 +1040,12 @@ class JavascriptResponseTest(ResponseTest): ...@@ -1040,12 +1040,12 @@ class JavascriptResponseTest(ResponseTest):
def test_cant_execute_javascript(self): def test_cant_execute_javascript(self):
# If the system says to disallow unsafe code execution, then making # If the system says to disallow unsafe code execution, then making
# this problem will raise an exception. # this problem will raise an exception.
system = test_system() capa_system = test_capa_system()
system.can_execute_unsafe_code = lambda: False capa_system.can_execute_unsafe_code = lambda: False
with self.assertRaises(LoncapaProblemError): with self.assertRaises(LoncapaProblemError):
self.build_problem( self.build_problem(
system=system, capa_system=capa_system,
generator_src="test_problem_generator.js", generator_src="test_problem_generator.js",
grader_src="test_problem_grader.js", grader_src="test_problem_grader.js",
display_class="TestProblemDisplay", display_class="TestProblemDisplay",
...@@ -1140,6 +1140,24 @@ class NumericalResponseTest(ResponseTest): ...@@ -1140,6 +1140,24 @@ class NumericalResponseTest(ResponseTest):
"Content error--answer '%s' is not a valid number", staff_ans "Content error--answer '%s' is not a valid number", staff_ans
) )
@mock.patch('capa.responsetypes.log')
def test_responsetype_i18n(self, mock_log):
"""Test that LoncapaSystem has an i18n that works."""
staff_ans = "clearly bad syntax )[+1e"
problem = self.build_problem(answer=staff_ans, tolerance=1e-3)
class FakeTranslations(object):
"""A fake gettext.Translations object."""
def ugettext(self, text):
"""Return the 'translation' of `text`."""
if text == "There was a problem with the staff answer to this problem":
text = "TRANSLATED!"
return text
problem.capa_system.i18n = FakeTranslations()
with self.assertRaisesRegexp(StudentInputError, "TRANSLATED!"):
self.assert_grade(problem, '1+j', 'correct')
def test_grade_infinity(self): def test_grade_infinity(self):
""" """
Check that infinity doesn't automatically get marked correct. Check that infinity doesn't automatically get marked correct.
......
...@@ -11,7 +11,7 @@ import sys ...@@ -11,7 +11,7 @@ import sys
from pkg_resources import resource_string from pkg_resources import resource_string
from capa.capa_problem import LoncapaProblem from capa.capa_problem import LoncapaProblem, LoncapaSystem
from capa.responsetypes import StudentInputError, \ from capa.responsetypes import StudentInputError, \
ResponseError, LoncapaProblemError ResponseError, LoncapaProblemError
from capa.util import convert_files_to_filenames from capa.util import convert_files_to_filenames
...@@ -260,12 +260,27 @@ class CapaMixin(CapaFields): ...@@ -260,12 +260,27 @@ class CapaMixin(CapaFields):
if text is None: if text is None:
text = self.data text = self.data
capa_system = LoncapaSystem(
ajax_url=self.runtime.ajax_url,
anonymous_student_id=self.runtime.anonymous_student_id,
cache=self.runtime.cache,
can_execute_unsafe_code=self.runtime.can_execute_unsafe_code,
DEBUG=self.runtime.DEBUG,
filestore=self.runtime.filestore,
i18n=self.runtime.service(self, "i18n"),
node_path=self.runtime.node_path,
render_template=self.runtime.render_template,
seed=self.runtime.seed, # Why do we do this if we have self.seed?
STATIC_URL=self.runtime.STATIC_URL,
xqueue=self.runtime.xqueue,
)
return LoncapaProblem( return LoncapaProblem(
problem_text=text, problem_text=text,
id=self.location.html_id(), id=self.location.html_id(),
state=state, state=state,
seed=self.seed, seed=self.seed,
system=self.runtime, capa_system=capa_system,
) )
def get_state_for_lcp(self): def get_state_for_lcp(self):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment