Commit 2fab97c1 by Victor Shnayder

Refactor imageinput and crystallography.

- also add a test for chemicalequationinput
parent 9135d7b2
...@@ -647,9 +647,8 @@ _reg(solution) ...@@ -647,9 +647,8 @@ _reg(solution)
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
class ImageInput(InputTypeBase):
def imageinput(element, value, status, render_template, msg=''): """
'''
Clickable image as an input field. Element should specify the image source, height, Clickable image as an input field. Element should specify the image source, height,
and width, e.g. and width, e.g.
...@@ -657,130 +656,120 @@ def imageinput(element, value, status, render_template, msg=''): ...@@ -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 TODO: showanswer for imageimput does not work yet - need javascript to put rectangle
over acceptable area of image. 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)
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)
_reg(imageinput) template = "imageinput.html"
tags = ['imageinput']
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')
def crystallography(element, value, status, render_template, msg=''): # if value is of the form [x,y] then parse it and send along coordinates of previous answer
eid = element.get('id') m = re.match('\[([0-9]+),([0-9]+)]', self.value.strip().replace(' ', ''))
if eid is None: if m:
msg = 'cryst has no id: it probably appears outside of a known response type' # TODO (vshnayder): why is there a "-15" here??
msg += "\nSee problem XML source line %s" % getattr(element, 'sourceline', '<unavailable>') (self.gx, self.gy) = [int(x) - 15 for x in m.groups()]
raise Exception(msg) else:
height = element.get('height') (self.gx, self.gy) = (0, 0)
width = element.get('width')
display_file = element.get('display_file')
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 = {'"': '&quot;'}
value = saxutils.escape(value, escapedict)
context = {'id': eid,
'value': value, def _get_render_context(self):
'state': status,
'count': count, context = {'id': self.id,
'size': size, 'value': self.value,
'msg': msg, 'height': self.height,
'hidden': hidden, 'width': self.width,
'inline': element.get('inline', ''), 'src': self.src,
'width': width, 'gx': self.gx,
'height': height, 'gy': self.gy,
'display_file': display_file, '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=''): template = "crystallography.html"
eid = element.get('id') tags = ['crystallography']
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')
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 = {'"': '&quot;'}
value = saxutils.escape(value, escapedict)
molecules = element.get('molecules') def __init__(self, system, xml, state):
geometries = element.get('geometries') super(Crystallography, self).__init__(system, xml, state)
context = {'id': eid, self.height = xml.get('height')
'value': value, self.width = xml.get('width')
'state': status, self.size = xml.get('size')
'count': count,
'size': size, # if specified, then textline is hidden and id is stored in div of name given by hidden
'msg': msg, self.hidden = xml.get('hidden', '')
'hidden': hidden,
'inline': element.get('inline', ''), # Escape answers with quotes, so they don't crash the system!
'width': width, escapedict = {'"': '&quot;'}
'height': height, self.value = saxutils.escape(self.value, escapedict)
'display_file': display_file,
'molecules': molecules, def _get_render_context(self):
'geometries': geometries, 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 = {'"': '&quot;'}
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)
#-------------------------------------------------------------------------------- #--------------------------------------------------------------------------------
......
<% doinline = "inline" if inline else "" %> <section id="textinput_${id}" class="textinput" >
<section id="textinput_${id}" class="textinput ${doinline}" >
<div id="holder" style="width:${width};height:${height}"></div> <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> <div class="script_placeholder" data-src="/static/js/crystallography.js"></div>
% if state == 'unsubmitted': % if state == 'unsubmitted':
<div class="unanswered ${doinline}" id="status_${id}"> <div class="unanswered" id="status_${id}">
% elif state == 'correct': % elif state == 'correct':
<div class="correct ${doinline}" id="status_${id}"> <div class="correct" id="status_${id}">
% elif state == 'incorrect': % elif state == 'incorrect':
<div class="incorrect ${doinline}" id="status_${id}"> <div class="incorrect" id="status_${id}">
% elif state == 'incomplete': % elif state == 'incomplete':
<div class="incorrect ${doinline}" id="status_${id}"> <div class="incorrect" id="status_${id}">
% endif % endif
% if hidden: % if hidden:
<div style="display:none;" name="${hidden}" inputid="input_${id}" /> <div style="display:none;" name="${hidden}" inputid="input_${id}" />
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
% if msg: % if msg:
<span class="message">${msg|n}</span> <span class="message">${msg|n}</span>
% endif % endif
% if state in ['unsubmitted', 'correct', 'incorrect', 'incomplete'] or hidden: % if state in ['unsubmitted', 'correct', 'incorrect', 'incomplete']:
</div> </div>
% endif % endif
</section> </section>
<% doinline = "inline" if inline else "" %> <section id="vsepr_input_${id}" class="textinput" >
<section id="textinput_${id}" class="textinput ${doinline}" >
<table><tr><td height='600'> <table><tr><td height='600'>
<div id="vsepr_div_${id}" style="position:relative;" data-molecules="${molecules}" data-geometries="${geometries}"> <div id="vsepr_div_${id}" style="position:relative;" data-molecules="${molecules}" data-geometries="${geometries}">
<canvas id="vsepr_canvas_${id}" width="${width}" height="${height}"> <canvas id="vsepr_canvas_${id}" width="${width}" height="${height}">
...@@ -14,25 +12,17 @@ ...@@ -14,25 +12,17 @@
<div class="script_placeholder" data-src="/static/js/vsepr/vsepr.js"></div> <div class="script_placeholder" data-src="/static/js/vsepr/vsepr.js"></div>
% if state == 'unsubmitted': % if state == 'unsubmitted':
<div class="unanswered ${doinline}" id="status_${id}"> <div class="unanswered" id="status_${id}">
% elif state == 'correct': % elif state == 'correct':
<div class="correct ${doinline}" id="status_${id}"> <div class="correct" id="status_${id}">
% elif state == 'incorrect': % elif state == 'incorrect':
<div class="incorrect ${doinline}" id="status_${id}"> <div class="incorrect" id="status_${id}">
% elif state == 'incomplete': % 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}" />
% endif % endif
<input type="text" name="input_${id}" id="input_${id}" value="${value}" <input type="text" name="input_${id}" id="input_${id}" value="${value}"
% if size: style="display:none;"
size="${size}"
% endif
% if hidden:
style="display:none;"
% endif
/> />
<p class="status"> <p class="status">
...@@ -52,7 +42,7 @@ ...@@ -52,7 +42,7 @@
% if msg: % if msg:
<span class="message">${msg|n}</span> <span class="message">${msg|n}</span>
% endif % endif
% if state in ['unsubmitted', 'correct', 'incorrect', 'incomplete'] or hidden: % if state in ['unsubmitted', 'correct', 'incorrect', 'incomplete']:
</div> </div>
% endif % endif
</section> </section>
...@@ -4,6 +4,7 @@ Tests of input types (and actually responsetypes too). ...@@ -4,6 +4,7 @@ Tests of input types (and actually responsetypes too).
TODO: TODO:
- test unicode in values, parameters, etc. - test unicode in values, parameters, etc.
- test various html escapes - test various html escapes
- test funny xml chars -- should never get xml parse error if things are escaped properly.
""" """
from datetime import datetime from datetime import datetime
...@@ -351,8 +352,7 @@ class SchematicTest(unittest.TestCase): ...@@ -351,8 +352,7 @@ class SchematicTest(unittest.TestCase):
value = 'three resistors and an oscilating pendulum' value = 'three resistors and an oscilating pendulum'
state = {'value': value, state = {'value': value,
'status': 'unsubmitted', 'status': 'unsubmitted'}
'feedback' : {'message': '3'}, }
the_input = inputtypes.get_class_for_tag('schematic')(system, element, state) the_input = inputtypes.get_class_for_tag('schematic')(system, element, state)
...@@ -371,3 +371,120 @@ class SchematicTest(unittest.TestCase): ...@@ -371,3 +371,120 @@ class SchematicTest(unittest.TestCase):
self.assertEqual(context, expected) 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)
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