Refactor imageinput and crystallography.
- also add a test for chemicalequationinput
This commit is contained in:
@@ -647,9 +647,8 @@ _reg(solution)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def imageinput(element, value, status, render_template, msg=''):
|
||||
'''
|
||||
class ImageInput(InputTypeBase):
|
||||
"""
|
||||
Clickable image as an input field. Element should specify the image source, height,
|
||||
and width, e.g.
|
||||
|
||||
@@ -657,130 +656,120 @@ def imageinput(element, value, status, render_template, msg=''):
|
||||
|
||||
TODO: showanswer for imageimput does not work yet - need javascript to put rectangle
|
||||
over acceptable area of image.
|
||||
'''
|
||||
eid = element.get('id')
|
||||
src = element.get('src')
|
||||
height = element.get('height')
|
||||
width = element.get('width')
|
||||
"""
|
||||
|
||||
# if value is of the form [x,y] then parse it and send along coordinates of previous answer
|
||||
m = re.match('\[([0-9]+),([0-9]+)]', value.strip().replace(' ', ''))
|
||||
if m:
|
||||
(gx, gy) = [int(x) - 15 for x in m.groups()]
|
||||
else:
|
||||
(gx, gy) = (0, 0)
|
||||
template = "imageinput.html"
|
||||
tags = ['imageinput']
|
||||
|
||||
context = {
|
||||
'id': eid,
|
||||
'value': value,
|
||||
'height': height,
|
||||
'width': width,
|
||||
'src': src,
|
||||
'gx': gx,
|
||||
'gy': gy,
|
||||
'state': status, # to change
|
||||
'msg': msg, # to change
|
||||
}
|
||||
html = render_template("imageinput.html", context)
|
||||
return etree.XML(html)
|
||||
def __init__(self, system, xml, state):
|
||||
super(ImageInput, self).__init__(system, xml, state)
|
||||
self.src = xml.get('src')
|
||||
self.height = xml.get('height')
|
||||
self.width = xml.get('width')
|
||||
|
||||
_reg(imageinput)
|
||||
# if value is of the form [x,y] then parse it and send along coordinates of previous answer
|
||||
m = re.match('\[([0-9]+),([0-9]+)]', self.value.strip().replace(' ', ''))
|
||||
if m:
|
||||
# TODO (vshnayder): why is there a "-15" here??
|
||||
(self.gx, self.gy) = [int(x) - 15 for x in m.groups()]
|
||||
else:
|
||||
(self.gx, self.gy) = (0, 0)
|
||||
|
||||
|
||||
def crystallography(element, value, status, render_template, msg=''):
|
||||
eid = element.get('id')
|
||||
if eid is None:
|
||||
msg = 'cryst 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)
|
||||
height = element.get('height')
|
||||
width = element.get('width')
|
||||
display_file = element.get('display_file')
|
||||
def _get_render_context(self):
|
||||
|
||||
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', '')
|
||||
# Escape answers with quotes, so they don't crash the system!
|
||||
escapedict = {'"': '"'}
|
||||
value = saxutils.escape(value, escapedict)
|
||||
|
||||
context = {'id': eid,
|
||||
'value': value,
|
||||
'state': status,
|
||||
'count': count,
|
||||
'size': size,
|
||||
'msg': msg,
|
||||
'hidden': hidden,
|
||||
'inline': element.get('inline', ''),
|
||||
'width': width,
|
||||
'height': height,
|
||||
'display_file': display_file,
|
||||
context = {'id': self.id,
|
||||
'value': self.value,
|
||||
'height': self.height,
|
||||
'width': self.width,
|
||||
'src': self.src,
|
||||
'gx': self.gx,
|
||||
'gy': self.gy,
|
||||
'state': self.status, # to change (VS: to what??)
|
||||
'msg': self.msg, # to change
|
||||
}
|
||||
return context
|
||||
|
||||
html = render_template("crystallography.html", context)
|
||||
register_input_class(ImageInput)
|
||||
|
||||
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.crystallography] failed to parse XML for:\n%s' % html)
|
||||
raise
|
||||
return xhtml
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
_reg(crystallography)
|
||||
class Crystallography(InputTypeBase):
|
||||
"""
|
||||
An input for crystallography -- user selects 3 points on the axes, and we get a plane.
|
||||
|
||||
TODO: what's the actual value format?
|
||||
"""
|
||||
|
||||
def vsepr_input(element, value, status, render_template, msg=''):
|
||||
eid = element.get('id')
|
||||
if eid is None:
|
||||
msg = 'cryst 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)
|
||||
height = element.get('height')
|
||||
width = element.get('width')
|
||||
display_file = element.get('display_file')
|
||||
template = "crystallography.html"
|
||||
tags = ['crystallography']
|
||||
|
||||
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', '')
|
||||
# Escape answers with quotes, so they don't crash the system!
|
||||
escapedict = {'"': '"'}
|
||||
value = saxutils.escape(value, escapedict)
|
||||
def __init__(self, system, xml, state):
|
||||
super(Crystallography, self).__init__(system, xml, state)
|
||||
|
||||
molecules = element.get('molecules')
|
||||
geometries = element.get('geometries')
|
||||
self.height = xml.get('height')
|
||||
self.width = xml.get('width')
|
||||
self.size = xml.get('size')
|
||||
|
||||
context = {'id': eid,
|
||||
'value': value,
|
||||
'state': status,
|
||||
'count': count,
|
||||
'size': size,
|
||||
'msg': msg,
|
||||
'hidden': hidden,
|
||||
'inline': element.get('inline', ''),
|
||||
'width': width,
|
||||
'height': height,
|
||||
'display_file': display_file,
|
||||
'molecules': molecules,
|
||||
'geometries': geometries,
|
||||
# if specified, then textline is hidden and id is stored in div of name given by hidden
|
||||
self.hidden = xml.get('hidden', '')
|
||||
|
||||
# Escape answers with quotes, so they don't crash the system!
|
||||
escapedict = {'"': '"'}
|
||||
self.value = saxutils.escape(self.value, escapedict)
|
||||
|
||||
def _get_render_context(self):
|
||||
context = {'id': self.id,
|
||||
'value': self.value,
|
||||
'state': self.status,
|
||||
'size': self.size,
|
||||
'msg': self.msg,
|
||||
'hidden': self.hidden,
|
||||
'width': self.width,
|
||||
'height': self.height,
|
||||
}
|
||||
return context
|
||||
|
||||
html = render_template("vsepr_input.html", context)
|
||||
register_input_class(Crystallography)
|
||||
|
||||
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.vsepr_input] failed to parse XML for:\n%s' % html)
|
||||
raise
|
||||
return xhtml
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
_reg(vsepr_input)
|
||||
class VseprInput(InputTypeBase):
|
||||
"""
|
||||
Input for molecular geometry--show possible structures, let student
|
||||
pick structure and label positions with atoms or electron pairs.
|
||||
"""
|
||||
|
||||
template = 'vsepr_input.html'
|
||||
tags = ['vsepr_input']
|
||||
|
||||
def __init__(self, system, xml, state):
|
||||
super(ImageInput, self).__init__(system, xml, state)
|
||||
|
||||
self.height = xml.get('height')
|
||||
self.width = xml.get('width')
|
||||
|
||||
# Escape answers with quotes, so they don't crash the system!
|
||||
escapedict = {'"': '"'}
|
||||
self.value = saxutils.escape(self.value, escapedict)
|
||||
|
||||
self.molecules = xml.get('molecules')
|
||||
self.geometries = xml.get('geometries')
|
||||
|
||||
def _get_render_context(self):
|
||||
|
||||
context = {'id': self.id,
|
||||
'value': self.value,
|
||||
'state': self.status,
|
||||
'msg': self.msg,
|
||||
'width': self.width,
|
||||
'height': self.height,
|
||||
'molecules': self.molecules,
|
||||
'geometries': self.geometries,
|
||||
}
|
||||
return context
|
||||
|
||||
register_input_class(VseprInput)
|
||||
|
||||
#--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
<% doinline = "inline" if inline else "" %>
|
||||
|
||||
<section id="textinput_${id}" class="textinput ${doinline}" >
|
||||
<section id="textinput_${id}" class="textinput" >
|
||||
<div id="holder" style="width:${width};height:${height}"></div>
|
||||
<div class="script_placeholder" data-src="/static/js/raphael.js"></div><div class="script_placeholder" data-src="/static/js/sylvester.js"></div><div class="script_placeholder" data-src="/static/js/underscore-min.js"></div>
|
||||
<div class="script_placeholder" data-src="/static/js/raphael.js"></div>
|
||||
<div class="script_placeholder" data-src="/static/js/sylvester.js"></div>
|
||||
<div class="script_placeholder" data-src="/static/js/underscore-min.js"></div>
|
||||
<div class="script_placeholder" data-src="/static/js/crystallography.js"></div>
|
||||
|
||||
|
||||
% if state == 'unsubmitted':
|
||||
<div class="unanswered ${doinline}" id="status_${id}">
|
||||
<div class="unanswered" id="status_${id}">
|
||||
% elif state == 'correct':
|
||||
<div class="correct ${doinline}" id="status_${id}">
|
||||
<div class="correct" id="status_${id}">
|
||||
% elif state == 'incorrect':
|
||||
<div class="incorrect ${doinline}" id="status_${id}">
|
||||
<div class="incorrect" id="status_${id}">
|
||||
% elif state == 'incomplete':
|
||||
<div class="incorrect ${doinline}" id="status_${id}">
|
||||
<div class="incorrect" id="status_${id}">
|
||||
% endif
|
||||
% if hidden:
|
||||
<div style="display:none;" name="${hidden}" inputid="input_${id}" />
|
||||
@@ -45,7 +45,7 @@
|
||||
% if msg:
|
||||
<span class="message">${msg|n}</span>
|
||||
% endif
|
||||
% if state in ['unsubmitted', 'correct', 'incorrect', 'incomplete'] or hidden:
|
||||
% if state in ['unsubmitted', 'correct', 'incorrect', 'incomplete']:
|
||||
</div>
|
||||
% endif
|
||||
</section>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
<% doinline = "inline" if inline else "" %>
|
||||
|
||||
<section id="textinput_${id}" class="textinput ${doinline}" >
|
||||
<section id="vsepr_input_${id}" class="textinput" >
|
||||
<table><tr><td height='600'>
|
||||
<div id="vsepr_div_${id}" style="position:relative;" data-molecules="${molecules}" data-geometries="${geometries}">
|
||||
<canvas id="vsepr_canvas_${id}" width="${width}" height="${height}">
|
||||
@@ -14,25 +12,17 @@
|
||||
<div class="script_placeholder" data-src="/static/js/vsepr/vsepr.js"></div>
|
||||
|
||||
% if state == 'unsubmitted':
|
||||
<div class="unanswered ${doinline}" id="status_${id}">
|
||||
<div class="unanswered" id="status_${id}">
|
||||
% elif state == 'correct':
|
||||
<div class="correct ${doinline}" id="status_${id}">
|
||||
<div class="correct" id="status_${id}">
|
||||
% elif state == 'incorrect':
|
||||
<div class="incorrect ${doinline}" id="status_${id}">
|
||||
<div class="incorrect" id="status_${id}">
|
||||
% elif state == 'incomplete':
|
||||
<div class="incorrect ${doinline}" id="status_${id}">
|
||||
% endif
|
||||
% if hidden:
|
||||
<div style="display:none;" name="${hidden}" inputid="input_${id}" />
|
||||
<div class="incorrect" id="status_${id}">
|
||||
% endif
|
||||
|
||||
<input type="text" name="input_${id}" id="input_${id}" value="${value}"
|
||||
% if size:
|
||||
size="${size}"
|
||||
% endif
|
||||
% if hidden:
|
||||
style="display:none;"
|
||||
% endif
|
||||
style="display:none;"
|
||||
/>
|
||||
|
||||
<p class="status">
|
||||
@@ -52,7 +42,7 @@
|
||||
% if msg:
|
||||
<span class="message">${msg|n}</span>
|
||||
% endif
|
||||
% if state in ['unsubmitted', 'correct', 'incorrect', 'incomplete'] or hidden:
|
||||
% if state in ['unsubmitted', 'correct', 'incorrect', 'incomplete']:
|
||||
</div>
|
||||
% endif
|
||||
</section>
|
||||
|
||||
@@ -4,6 +4,7 @@ Tests of input types (and actually responsetypes too).
|
||||
TODO:
|
||||
- test unicode in values, parameters, etc.
|
||||
- test various html escapes
|
||||
- test funny xml chars -- should never get xml parse error if things are escaped properly.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
@@ -351,8 +352,7 @@ class SchematicTest(unittest.TestCase):
|
||||
|
||||
value = 'three resistors and an oscilating pendulum'
|
||||
state = {'value': value,
|
||||
'status': 'unsubmitted',
|
||||
'feedback' : {'message': '3'}, }
|
||||
'status': 'unsubmitted'}
|
||||
|
||||
the_input = inputtypes.get_class_for_tag('schematic')(system, element, state)
|
||||
|
||||
@@ -371,3 +371,120 @@ class SchematicTest(unittest.TestCase):
|
||||
|
||||
self.assertEqual(context, expected)
|
||||
|
||||
|
||||
class ImageInputTest(unittest.TestCase):
|
||||
'''
|
||||
Check that image inputs work
|
||||
'''
|
||||
|
||||
def check(self, value, egx, egy):
|
||||
height = '78'
|
||||
width = '427'
|
||||
src = 'http://www.edx.org/cowclicker.jpg'
|
||||
|
||||
xml_str = """<imageinput id="prob_1_2"
|
||||
src="{s}"
|
||||
height="{h}"
|
||||
width="{w}"
|
||||
/>""".format(s=src, h=height, w=width)
|
||||
|
||||
element = etree.fromstring(xml_str)
|
||||
|
||||
state = {'value': value,
|
||||
'status': 'unsubmitted'}
|
||||
|
||||
the_input = inputtypes.get_class_for_tag('imageinput')(system, element, state)
|
||||
|
||||
context = the_input._get_render_context()
|
||||
|
||||
expected = {'id': 'prob_1_2',
|
||||
'value': value,
|
||||
'state': 'unsubmitted',
|
||||
'width': width,
|
||||
'height': height,
|
||||
'src': src,
|
||||
'gx': egx,
|
||||
'gy': egy,
|
||||
'state': 'unsubmitted',
|
||||
'msg': ''}
|
||||
|
||||
self.assertEqual(context, expected)
|
||||
|
||||
def test_with_value(self):
|
||||
self.check('[50,40]', 35, 25)
|
||||
|
||||
def test_without_value(self):
|
||||
self.check('', 0, 0)
|
||||
|
||||
def test_corrupt_values(self):
|
||||
self.check('[12', 0, 0)
|
||||
self.check('[12, a]', 0, 0)
|
||||
self.check('[12 10]', 0, 0)
|
||||
self.check('[12]', 0, 0)
|
||||
self.check('[12 13 14]', 0, 0)
|
||||
|
||||
|
||||
|
||||
class CrystallographyTest(unittest.TestCase):
|
||||
'''
|
||||
Check that crystallography inputs work
|
||||
'''
|
||||
|
||||
def test_rendering(self):
|
||||
height = '12'
|
||||
width = '33'
|
||||
size = '10'
|
||||
|
||||
xml_str = """<crystallography id="prob_1_2"
|
||||
height="{h}"
|
||||
width="{w}"
|
||||
size="{s}"
|
||||
/>""".format(h=height, w=width, s=size)
|
||||
|
||||
element = etree.fromstring(xml_str)
|
||||
|
||||
value = 'abc'
|
||||
state = {'value': value,
|
||||
'status': 'unsubmitted'}
|
||||
|
||||
the_input = inputtypes.get_class_for_tag('crystallography')(system, element, state)
|
||||
|
||||
context = the_input._get_render_context()
|
||||
|
||||
expected = {'id': 'prob_1_2',
|
||||
'value': value,
|
||||
'state': 'unsubmitted',
|
||||
'size': size,
|
||||
'msg': '',
|
||||
'hidden': '',
|
||||
'width': width,
|
||||
'height': height,
|
||||
}
|
||||
|
||||
self.assertEqual(context, expected)
|
||||
|
||||
|
||||
class ChemicalEquationTest(unittest.TestCase):
|
||||
'''
|
||||
Check that chemical equation inputs work.
|
||||
'''
|
||||
|
||||
def test_rendering(self):
|
||||
size = "42"
|
||||
xml_str = """<chemicalequationinput id="prob_1_2" size="{size}"/>""".format(size=size)
|
||||
|
||||
element = etree.fromstring(xml_str)
|
||||
|
||||
state = {'value': 'H2OYeah',}
|
||||
the_input = inputtypes.get_class_for_tag('chemicalequationinput')(system, element, state)
|
||||
|
||||
context = the_input._get_render_context()
|
||||
|
||||
expected = {'id': 'prob_1_2',
|
||||
'value': 'H2OYeah',
|
||||
'status': 'unanswered',
|
||||
'size': size,
|
||||
'previewer': '/static/js/capa/chemical_equation_preview.js',
|
||||
}
|
||||
self.assertEqual(context, expected)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user