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
......
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()
......
...@@ -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