Commit c2682273 by Victor Shnayder

Convert textline input into class.

Unify math and non-math code and templates.

Add tests.
parent 0c6f6f87
......@@ -16,7 +16,7 @@ class ClassName(InputTypeBase):
def _get_render_context(self):
context = {'id': self.id,
}
return context
......@@ -394,92 +394,58 @@ register_input_class(JavascriptInput)
#-----------------------------------------------------------------------------
def textline(element, value, status, render_template, msg=""):
'''
Simple text line input, with optional size specification.
'''
# TODO: 'dojs' flag is temporary, for backwards compatibility with 8.02x
if element.get('math') or element.get('dojs'):
return textline_dynamath(element, value, status, render_template, msg)
eid = element.get('id')
if eid is None:
msg = 'textline has no id: it probably appears outside of a known response type'
msg += "\nSee problem XML source line %s" % getattr(element, 'sourceline', '<unavailable>')
raise Exception(msg)
count = int(eid.split('_')[-2]) - 1 # HACK
size = element.get('size')
# if specified, then textline is hidden and id is stored in div of name given by hidden
hidden = element.get('hidden', '')
class TextLine(InputTypeBase):
"""
# Escape answers with quotes, so they don't crash the system!
escapedict = {'"': '&quot;'}
value = saxutils.escape(value, escapedict)
"""
context = {'id': eid,
'value': value,
'state': status,
'count': count,
'size': size,
'msg': msg,
'hidden': hidden,
'inline': element.get('inline',''),
}
template = "textinput.html"
tags = ['textline']
html = render_template("textinput.html", context)
try:
xhtml = etree.XML(html)
except Exception as err:
# TODO: needs to be self.system.DEBUG - but can't access system
if True:
log.debug('[inputtypes.textline] failed to parse XML for:\n%s' % html)
raise
return xhtml
def __init__(self, system, xml, state):
super(TextLine, self).__init__(system, xml, state)
self.size = self.xml.get('size')
_reg(textline)
# if specified, then textline is hidden and input id is stored
# in div with name=self.hidden.
self.hidden = self.xml.get('hidden', False)
#-----------------------------------------------------------------------------
# TODO (vshnayder): can we get rid of inline? Was it one of
# the styling hacks early this semester?
self.inline = self.xml.get('inline', False)
# TODO: 'dojs' flag is temporary, for backwards compatibility with 8.02x
self.do_math = bool(self.xml.get('math') or self.xml.get('dojs'))
# TODO: do math checking using ajax instead of using js, so
# that we only have one math parser.
self.preprocessor = None
if self.do_math:
# Preprocessor to insert between raw input and Mathjax
self.preprocessor = {'class_name': self.xml.get('preprocessorClassName',''),
'script_src': self.xml.get('preprocessorSrc','')}
if '' in self.preprocessor.values():
self.preprocessor = None
def textline_dynamath(element, value, status, render_template, msg=''):
'''
Text line input with dynamic math display (equation rendered on client in real time
during input).
'''
# TODO: Make a wrapper for <formulainput>
# TODO: Make an AJAX loop to confirm equation is okay in real-time as user types
'''
textline is used for simple one-line inputs, like formularesponse and symbolicresponse.
uses a <span id=display_eid>`{::}`</span>
and a hidden textarea with id=input_eid_fromjs for the mathjax rendering and return.
'''
eid = element.get('id')
count = int(eid.split('_')[-2]) - 1 # HACK
size = element.get('size')
# if specified, then textline is hidden and id is stored in div of name given by hidden
hidden = element.get('hidden', '')
# Preprocessor to insert between raw input and Mathjax
preprocessor = {'class_name': element.get('preprocessorClassName',''),
'script_src': element.get('preprocessorSrc','')}
if '' in preprocessor.values():
preprocessor = None
# Escape characters in student input for safe XML parsing
escapedict = {'"': '&quot;'}
value = saxutils.escape(value, escapedict)
def _get_render_context(self):
# Escape answers with quotes, so they don't crash the system!
escapedict = {'"': '&quot;'}
value = saxutils.escape(self.value, escapedict)
context = {'id': eid,
'value': value,
'state': status,
'count': count,
'size': size,
'msg': msg,
'hidden': hidden,
'preprocessor': preprocessor,}
html = render_template("textinput_dynamath.html", context)
return etree.XML(html)
context = {'id': self.id,
'value': value,
'state': self.status,
'size': self.size,
'msg': self.msg,
'hidden': self.hidden,
'inline': self.inline,
'do_math': self.do_math,
'preprocessor': self.preprocessor,
}
return context
register_input_class(TextLine)
#-----------------------------------------------------------------------------
def filesubmission(element, value, status, render_template, msg=''):
......
<% doinline = "inline" if inline else "" %>
<section id="textinput_${id}" class="textinput ${doinline}" >
<%
# TODO:
# Is id inputtype_${id} vs textinput_${id} important?
# Is class capa_inputtype vs textinput important?
# should really just use one.
%>
<section id="${'inputtype' if do_math else 'textinput'}_${id}" class="${'text-input-dynamath capa_inputtype' if do_math else 'textinput'} ${doinline}" >
% if preprocessor is not None:
<div class="text-input-dynamath_data" data-preprocessor="${preprocessor['class_name']}"/>
<div class="script_placeholder" data-src="${preprocessor['script_src']}"/>
% endif
% if state == 'unsubmitted':
<div class="unanswered ${doinline}" id="status_${id}">
% elif state == 'correct':
......@@ -15,12 +27,15 @@
% endif
<input type="text" name="input_${id}" id="input_${id}" value="${value}"
% if size:
size="${size}"
% endif
% if hidden:
style="display:none;"
% endif
% if do_math:
class="math"
% endif
% if size:
size="${size}"
% endif
% if hidden:
style="display:none;"
% endif
/>
<p class="status">
......@@ -35,12 +50,18 @@
% endif
</p>
<p id="answer_${id}" class="answer"></p>
<p id="answer_${id}" class="answer"></p>
% if do_math:
<div id="display_${id}" class="equation">`{::}`</div>
% endif
% if state in ['unsubmitted', 'correct', 'incorrect', 'incomplete']:
</div>
% endif
% if msg:
<span class="message">${msg|n}</span>
% endif
% if state in ['unsubmitted', 'correct', 'incorrect', 'incomplete'] or hidden:
</div>
% endif
</section>
###
### version of textline.html which does dynamic math
###
<section class="text-input-dynamath capa_inputtype" id="inputtype_${id}">
% if preprocessor is not None:
<div class="text-input-dynamath_data" data-preprocessor="${preprocessor['class_name']}"/>
<div class="script_placeholder" data-src="${preprocessor['script_src']}"/>
% endif
% if state == 'unsubmitted':
<div class="unanswered" id="status_${id}">
% elif state == 'correct':
<div class="correct" id="status_${id}">
% elif state == 'incorrect':
<div class="incorrect" id="status_${id}">
% elif state == 'incomplete':
<div class="incorrect" id="status_${id}">
% endif
% if hidden:
<div style="display:none;" name="${hidden}" inputid="input_${id}" />
% endif
<input type="text" name="input_${id}" id="input_${id}" value="${value}" class="math" size="${size if size else ''}"
% if hidden:
style="display:none;"
% endif
/>
<p class="status">
% if state == 'unsubmitted':
unanswered
% elif state == 'correct':
correct
% elif state == 'incorrect':
incorrect
% elif state == 'incomplete':
incomplete
% endif
</p>
<p id="answer_${id}" class="answer"></p>
<div id="display_${id}" class="equation">`{::}`</div>
</div>
<textarea style="display:none" id="input_${id}_dynamath" name="input_${id}_dynamath"> </textarea>
% if msg:
<span class="message">${msg|n}</span>
% endif
</section>
......@@ -169,3 +169,60 @@ class JavascriptInputTest(unittest.TestCase):
'evaluation': '',}
self.assertEqual(context, expected)
class TextLineTest(unittest.TestCase):
'''
Check that textline inputs work, with and without math.
'''
def test_rendering(self):
size = "42"
xml_str = """<textline id="prob_1_2" size="{size}"/>""".format(size=size)
element = etree.fromstring(xml_str)
state = {'value': 'BumbleBee',}
the_input = inputtypes.get_class_for_tag('textline')(system, element, state)
context = the_input._get_render_context()
expected = {'id': 'prob_1_2',
'value': 'BumbleBee',
'state': 'unanswered',
'size': size,
'msg': '',
'hidden': False,
'inline': False,
'do_math': False,
'preprocessor': None}
self.assertEqual(context, expected)
def test_math_rendering(self):
size = "42"
preprocessorClass = "preParty"
script = "foo/party.js"
xml_str = """<textline math="True" id="prob_1_2" size="{size}"
preprocessorClassName="{pp}"
preprocessorSrc="{sc}"/>""".format(size=size, pp=preprocessorClass, sc=script)
element = etree.fromstring(xml_str)
state = {'value': 'BumbleBee',}
the_input = inputtypes.get_class_for_tag('textline')(system, element, state)
context = the_input._get_render_context()
expected = {'id': 'prob_1_2',
'value': 'BumbleBee',
'state': 'unanswered',
'size': size,
'msg': '',
'hidden': False,
'inline': False,
'do_math': True,
'preprocessor': {'class_name': preprocessorClass,
'script_src': script}}
self.assertEqual(context, expected)
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