Commit 4fa1f116 by Dave St.Germain

Added labels to CAPA input types, with markdown syntax for adding them

in studio.
LMS-1895 and others
parent 30aede6f
...@@ -100,6 +100,12 @@ or= mouse</code></pre> ...@@ -100,6 +100,12 @@ or= mouse</code></pre>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<h6>${_("Label")}</h6>
<div class="col">
<pre><code>&gt;&gt;What is the capital of Argentina?&lt;&lt;</code></pre>
</div>
</div>
<div class="row">
<h6>${_("Explanation")}</h6> <h6>${_("Explanation")}</h6>
<div class="col sample explanation"> <div class="col sample explanation">
<img src="${static.url("img/explanation-example.png")}" /> <img src="${static.url("img/explanation-example.png")}" />
......
...@@ -299,7 +299,7 @@ class OptionInput(InputTypeBase): ...@@ -299,7 +299,7 @@ class OptionInput(InputTypeBase):
Example: Example:
<optioninput options="('Up','Down')" correct="Up"/><text>The location of the sky</text> <optioninput options="('Up','Down')" label="Where is the sky?" correct="Up"/><text>The location of the sky</text>
# TODO: allow ordering to be randomized # TODO: allow ordering to be randomized
""" """
...@@ -335,6 +335,7 @@ class OptionInput(InputTypeBase): ...@@ -335,6 +335,7 @@ class OptionInput(InputTypeBase):
Convert options to a convenient format. Convert options to a convenient format.
""" """
return [Attribute('options', transform=cls.parse_options), return [Attribute('options', transform=cls.parse_options),
Attribute('label', ''),
Attribute('inline', False)] Attribute('inline', False)]
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
...@@ -353,7 +354,7 @@ class ChoiceGroup(InputTypeBase): ...@@ -353,7 +354,7 @@ class ChoiceGroup(InputTypeBase):
Example: Example:
<choicegroup> <choicegroup label="Which foil?">
<choice correct="false" name="foil1"> <choice correct="false" name="foil1">
<text>This is foil One.</text> <text>This is foil One.</text>
</choice> </choice>
...@@ -388,6 +389,7 @@ class ChoiceGroup(InputTypeBase): ...@@ -388,6 +389,7 @@ class ChoiceGroup(InputTypeBase):
@classmethod @classmethod
def get_attributes(cls): def get_attributes(cls):
return [Attribute("show_correctness", "always"), return [Attribute("show_correctness", "always"),
Attribute('label', ''),
Attribute("submitted_message", "Answer received.")] Attribute("submitted_message", "Answer received.")]
def _extra_context(self): def _extra_context(self):
...@@ -535,7 +537,7 @@ class TextLine(InputTypeBase): ...@@ -535,7 +537,7 @@ class TextLine(InputTypeBase):
is used e.g. for embedding simulations turned into questions. is used e.g. for embedding simulations turned into questions.
Example: Example:
<textline math="1" trailing_text="m/s" /> <textline math="1" trailing_text="m/s" label="How fast is a cheetah?" />
This example will render out a text line with a math preview and the text 'm/s' This example will render out a text line with a math preview and the text 'm/s'
after the end of the text line. after the end of the text line.
...@@ -551,7 +553,7 @@ class TextLine(InputTypeBase): ...@@ -551,7 +553,7 @@ class TextLine(InputTypeBase):
""" """
return [ return [
Attribute('size', None), Attribute('size', None),
Attribute('label', ''),
Attribute('hidden', False), Attribute('hidden', False),
Attribute('inline', False), Attribute('inline', False),
...@@ -612,6 +614,7 @@ class FileSubmission(InputTypeBase): ...@@ -612,6 +614,7 @@ class FileSubmission(InputTypeBase):
Convert the list of allowed files to a convenient format. Convert the list of allowed files to a convenient format.
""" """
return [Attribute('allowed_files', '[]', transform=cls.parse_files), return [Attribute('allowed_files', '[]', transform=cls.parse_files),
Attribute('label', ''),
Attribute('required_files', '[]', transform=cls.parse_files), ] Attribute('required_files', '[]', transform=cls.parse_files), ]
def setup(self): def setup(self):
...@@ -898,6 +901,7 @@ class Schematic(InputTypeBase): ...@@ -898,6 +901,7 @@ class Schematic(InputTypeBase):
Attribute('analyses', None), Attribute('analyses', None),
Attribute('initial_value', None), Attribute('initial_value', None),
Attribute('submit_analyses', None), Attribute('submit_analyses', None),
Attribute('label', ''),
] ]
...@@ -926,6 +930,7 @@ class ImageInput(InputTypeBase): ...@@ -926,6 +930,7 @@ class ImageInput(InputTypeBase):
""" """
return [Attribute('src'), return [Attribute('src'),
Attribute('height'), Attribute('height'),
Attribute('label', ''),
Attribute('width'), ] Attribute('width'), ]
def setup(self): def setup(self):
...@@ -1016,7 +1021,8 @@ class ChemicalEquationInput(InputTypeBase): ...@@ -1016,7 +1021,8 @@ class ChemicalEquationInput(InputTypeBase):
""" """
Can set size of text field. Can set size of text field.
""" """
return [Attribute('size', '20'), ] return [Attribute('size', '20'),
Attribute('label', ''),]
def _extra_context(self): def _extra_context(self):
""" """
...@@ -1078,7 +1084,7 @@ class FormulaEquationInput(InputTypeBase): ...@@ -1078,7 +1084,7 @@ class FormulaEquationInput(InputTypeBase):
Example: Example:
<formulaequationinput size="50"/> <formulaequationinput size="50" label="Enter the equation for motion"/>
options: size -- width of the textbox. options: size -- width of the textbox.
""" """
...@@ -1094,6 +1100,7 @@ class FormulaEquationInput(InputTypeBase): ...@@ -1094,6 +1100,7 @@ class FormulaEquationInput(InputTypeBase):
return [ return [
Attribute('size', '20'), Attribute('size', '20'),
Attribute('inline', False), Attribute('inline', False),
Attribute('label', ''),
] ]
def _extra_context(self): def _extra_context(self):
...@@ -1494,7 +1501,7 @@ class ChoiceTextGroup(InputTypeBase): ...@@ -1494,7 +1501,7 @@ class ChoiceTextGroup(InputTypeBase):
select the correct choice and fill in numbers to make it accurate. select the correct choice and fill in numbers to make it accurate.
<endouttext/> <endouttext/>
<choicetextresponse> <choicetextresponse>
<radiotextgroup> <radiotextgroup label="What is the correct choice?">
<choice correct="false">The lowest number rolled was: <choice correct="false">The lowest number rolled was:
<decoy_input/> and the highest number rolled was: <decoy_input/> and the highest number rolled was:
<decoy_input/> .</choice> <decoy_input/> .</choice>
...@@ -1517,7 +1524,7 @@ class ChoiceTextGroup(InputTypeBase): ...@@ -1517,7 +1524,7 @@ class ChoiceTextGroup(InputTypeBase):
select the correct choices and fill in numbers to make them accurate. select the correct choices and fill in numbers to make them accurate.
<endouttext/> <endouttext/>
<choicetextresponse> <choicetextresponse>
<checkboxtextgroup> <checkboxtextgroup label="What is the answer?">
<choice correct="true"> <choice correct="true">
The lowest number selected was <numtolerance_input answer="1.4142" tolerance="0.01"/> The lowest number selected was <numtolerance_input answer="1.4142" tolerance="0.01"/>
</choice> </choice>
...@@ -1575,7 +1582,8 @@ class ChoiceTextGroup(InputTypeBase): ...@@ -1575,7 +1582,8 @@ class ChoiceTextGroup(InputTypeBase):
""" """
return [ return [
Attribute("show_correctness", "always"), Attribute("show_correctness", "always"),
Attribute("submitted_message", "Answer received.") Attribute("submitted_message", "Answer received."),
Attribute("label", ""),
] ]
def _extra_context(self): def _extra_context(self):
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
<div class="incorrect" id="status_${id}"> <div class="incorrect" id="status_${id}">
% endif % endif
<input type="text" name="input_${id}" id="input_${id}" aria-describedby="answer_${id}" data-input-id="${id}" value="${value|h}" <input type="text" name="input_${id}" id="input_${id}" aria-label="${label}" aria-describedby="answer_${id}" data-input-id="${id}" value="${value|h}"
% if size: % if size:
size="${size}" size="${size}"
% endif % endif
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
% endif % endif
</div> </div>
<fieldset> <fieldset role="radiogroup" aria-label="${label}">
% for choice_id, choice_description in choices: % for choice_id, choice_description in choices:
<label for="input_${id}_${choice_id}" <label for="input_${id}_${choice_id}"
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
% endif % endif
% endif % endif
> >
<input type="${input_type}" name="input_${id}${name_array_suffix}" id="input_${id}_${choice_id}" aria-describedby="answer_${id}" value="${choice_id}" <input type="${input_type}" name="input_${id}${name_array_suffix}" id="input_${id}_${choice_id}" aria-role="radio" aria-describedby="answer_${id}" value="${choice_id}"
## If the student selected this choice... ## If the student selected this choice...
% if input_type == 'radio' and ( (isinstance(value, basestring) and (choice_id == value)) or (not isinstance(value, basestring) and choice_id in value) ): % if input_type == 'radio' and ( (isinstance(value, basestring) and (choice_id == value)) or (not isinstance(value, basestring) and choice_id in value) ):
checked="true" checked="true"
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
% endif % endif
</div> </div>
<fieldset> <fieldset aria-label="${label}">
% for choice_id, choice_description in choices: % for choice_id, choice_description in choices:
<%choice_id= choice_id %> <%choice_id= choice_id %>
<section id="forinput${choice_id}" <section id="forinput${choice_id}"
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
% endif % endif
<p class="debug">${status}</p> <p class="debug">${status}</p>
<input type="file" name="input_${id}" id="input_${id}" value="${value}" multiple="multiple" data-required_files="${required_files|h}" data-allowed_files="${allowed_files|h}"/> <input type="file" name="input_${id}" id="input_${id}" value="${value}" multiple="multiple" data-required_files="${required_files|h}" data-allowed_files="${allowed_files|h}" aria-label="${label}" />
</div> </div>
<div class="message">${msg|n}</div> <div class="message">${msg|n}</div>
</section> </section>
...@@ -2,11 +2,12 @@ ...@@ -2,11 +2,12 @@
<section id="formulaequationinput_${id}" class="inputtype formulaequationinput" ${doinline}> <section id="formulaequationinput_${id}" class="inputtype formulaequationinput" ${doinline}>
<div class="${reported_status}" id="status_${id}"> <div class="${reported_status}" id="status_${id}">
<input type="text" name="input_${id}" id="input_${id}" <input type="text" name="input_${id}" id="input_${id}"
data-input-id="${id}" value="${value|h}" data-input-id="${id}" value="${value|h}"
% if size: aria-label="${label}"
size="${size}" % if size:
% endif size="${size}"
/> % endif
/>
<p class="status">${reported_status}</p> <p class="status">${reported_status}</p>
......
<% doinline = "inline" if inline else "" %> <% doinline = "inline" if inline else "" %>
<form class="inputtype option-input ${doinline}"> <form class="inputtype option-input ${doinline}">
<select name="input_${id}" id="input_${id}" aria-describedby="answer_${id}"> <select name="input_${id}" id="input_${id}" aria-label="${label}" aria-describedby="answer_${id}">
<option value="option_${id}_dummy_default"> </option> <option value="option_${id}_dummy_default"> </option>
% for option_id, option_description in options: % for option_id, option_description in options:
<option value="${option_id}" <option value="${option_id}"
......
<span> <span>
<input type="hidden" class="schematic" height="${height}" width="${width}" parts="${parts}" analyses="${analyses}" name="input_${id}" id="input_${id}" aria-describedby="answer_${id}" value="" initial_value=""/> <input type="hidden" class="schematic" height="${height}" width="${width}" parts="${parts}" analyses="${analyses}" name="input_${id}" id="input_${id}" aria-label="${label}" aria-describedby="answer_${id}" value="" initial_value=""/>
<div id="value_${id}" style="display:none">${value}</div> <div id="value_${id}" style="display:none">${value}</div>
<div id="initial_value_${id}" style="display:none">${initial_value}</div> <div id="initial_value_${id}" style="display:none">${initial_value}</div>
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
<div style="display:none;" name="${hidden}" inputid="input_${id}" /> <div style="display:none;" name="${hidden}" inputid="input_${id}" />
% endif % endif
<input type="text" name="input_${id}" id="input_${id}" aria-describedby="answer_${id}" value="${value|h}" <input type="text" name="input_${id}" id="input_${id}" aria-label="${label}" aria-describedby="answer_${id}" value="${value|h}"
% if do_math: % if do_math:
class="math" class="math"
% endif % endif
......
...@@ -154,6 +154,7 @@ class CapaHtmlRenderTest(unittest.TestCase): ...@@ -154,6 +154,7 @@ class CapaHtmlRenderTest(unittest.TestCase):
expected_textline_context = { expected_textline_context = {
'STATIC_URL': '/dummy-static/', 'STATIC_URL': '/dummy-static/',
'status': 'unsubmitted', 'status': 'unsubmitted',
'label': '',
'value': '', 'value': '',
'preprocessor': None, 'preprocessor': None,
'msg': '', 'msg': '',
......
...@@ -124,6 +124,7 @@ class ChoiceGroupTemplateTest(TemplateTestCase): ...@@ -124,6 +124,7 @@ class ChoiceGroupTemplateTest(TemplateTestCase):
self.context = {'id': '1', self.context = {'id': '1',
'choices': choices, 'choices': choices,
'status': 'correct', 'status': 'correct',
'label': 'test',
'input_type': 'checkbox', 'input_type': 'checkbox',
'name_array_suffix': '1', 'name_array_suffix': '1',
'value': '3'} 'value': '3'}
...@@ -336,6 +337,11 @@ class ChoiceGroupTemplateTest(TemplateTestCase): ...@@ -336,6 +337,11 @@ class ChoiceGroupTemplateTest(TemplateTestCase):
# Expect that we do NOT see the message yet # Expect that we do NOT see the message yet
self.assert_no_xpath(xml, "//div[@class='capa_alert']", self.context) self.assert_no_xpath(xml, "//div[@class='capa_alert']", self.context)
def test_label(self):
xml = self.render_to_xml(self.context)
xpath = "//fieldset[@aria-label='%s']" % self.context['label']
self.assert_has_xpath(xml, xpath, self.context)
class TextlineTemplateTest(TemplateTestCase): class TextlineTemplateTest(TemplateTestCase):
""" """
...@@ -347,6 +353,7 @@ class TextlineTemplateTest(TemplateTestCase): ...@@ -347,6 +353,7 @@ class TextlineTemplateTest(TemplateTestCase):
def setUp(self): def setUp(self):
self.context = {'id': '1', self.context = {'id': '1',
'status': 'correct', 'status': 'correct',
'label': 'test',
'value': '3', 'value': '3',
'preprocessor': None, 'preprocessor': None,
'trailing_text': None} 'trailing_text': None}
...@@ -384,6 +391,11 @@ class TextlineTemplateTest(TemplateTestCase): ...@@ -384,6 +391,11 @@ class TextlineTemplateTest(TemplateTestCase):
self.assert_has_text(xml, "//p[@class='status']", self.assert_has_text(xml, "//p[@class='status']",
status_mark, exact=False) status_mark, exact=False)
def test_label(self):
xml = self.render_to_xml(self.context)
xpath = "//input[@aria-label='%s']" % self.context['label']
self.assert_has_xpath(xml, xpath, self.context)
def test_hidden(self): def test_hidden(self):
self.context['hidden'] = True self.context['hidden'] = True
xml = self.render_to_xml(self.context) xml = self.render_to_xml(self.context)
...@@ -460,6 +472,7 @@ class FormulaEquationInputTemplateTest(TemplateTestCase): ...@@ -460,6 +472,7 @@ class FormulaEquationInputTemplateTest(TemplateTestCase):
'id': 2, 'id': 2,
'value': 'PREFILLED_VALUE', 'value': 'PREFILLED_VALUE',
'status': 'unsubmitted', 'status': 'unsubmitted',
'label': 'test',
'previewer': 'file.js', 'previewer': 'file.js',
'reported_status': 'REPORTED_STATUS', 'reported_status': 'REPORTED_STATUS',
} }
...@@ -651,7 +664,7 @@ class OptionInputTemplateTest(TemplateTestCase): ...@@ -651,7 +664,7 @@ class OptionInputTemplateTest(TemplateTestCase):
TEMPLATE_NAME = 'optioninput.html' TEMPLATE_NAME = 'optioninput.html'
def setUp(self): def setUp(self):
self.context = {'id': 2, 'options': [], 'status': 'unsubmitted', 'value': 0} self.context = {'id': 2, 'options': [], 'status': 'unsubmitted', 'label': 'test', 'value': 0}
super(OptionInputTemplateTest, self).setUp() super(OptionInputTemplateTest, self).setUp()
def test_select_options(self): def test_select_options(self):
...@@ -694,6 +707,11 @@ class OptionInputTemplateTest(TemplateTestCase): ...@@ -694,6 +707,11 @@ class OptionInputTemplateTest(TemplateTestCase):
xpath = "//span[@class='{0}']".format(expected_css_class) xpath = "//span[@class='{0}']".format(expected_css_class)
self.assert_has_xpath(xml, xpath, self.context) self.assert_has_xpath(xml, xpath, self.context)
def test_label(self):
xml = self.render_to_xml(self.context)
xpath = "//select[@aria-label='%s']" % self.context['label']
self.assert_has_xpath(xml, xpath, self.context)
class DragAndDropTemplateTest(TemplateTestCase): class DragAndDropTemplateTest(TemplateTestCase):
""" """
...@@ -769,6 +787,7 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase): ...@@ -769,6 +787,7 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase):
'choices': choices, 'choices': choices,
'status': 'correct', 'status': 'correct',
'input_type': 'radio', 'input_type': 'radio',
'label': 'choicetext label',
'value': self.VALUE_DICT} 'value': self.VALUE_DICT}
super(ChoiceTextGroupTemplateTest, self).setUp() super(ChoiceTextGroupTemplateTest, self).setUp()
...@@ -908,3 +927,8 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase): ...@@ -908,3 +927,8 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase):
# Should NOT mark the whole problem # Should NOT mark the whole problem
xpath = "//div[@class='indicator_container']/span" xpath = "//div[@class='indicator_container']/span"
self.assert_no_xpath(xml, xpath, self.context) self.assert_no_xpath(xml, xpath, self.context)
def test_label(self):
xml = self.render_to_xml(self.context)
xpath = "//fieldset[@aria-label='%s']" % self.context['label']
self.assert_has_xpath(xml, xpath, self.context)
...@@ -56,6 +56,7 @@ class OptionInputTest(unittest.TestCase): ...@@ -56,6 +56,7 @@ class OptionInputTest(unittest.TestCase):
'value': 'Down', 'value': 'Down',
'options': [('Up', 'Up'), ('Down', 'Down'), ('Don\'t know', 'Don\'t know')], 'options': [('Up', 'Up'), ('Down', 'Down'), ('Don\'t know', 'Don\'t know')],
'status': 'answered', 'status': 'answered',
'label': '',
'msg': '', 'msg': '',
'inline': False, 'inline': False,
'id': 'sky_input', 'id': 'sky_input',
...@@ -115,6 +116,7 @@ class ChoiceGroupTest(unittest.TestCase): ...@@ -115,6 +116,7 @@ class ChoiceGroupTest(unittest.TestCase):
'id': 'sky_input', 'id': 'sky_input',
'value': 'foil3', 'value': 'foil3',
'status': 'answered', 'status': 'answered',
'label': '',
'msg': '', 'msg': '',
'input_type': expected_input_type, 'input_type': expected_input_type,
'choices': [('foil1', '<text>This is foil One.</text>'), 'choices': [('foil1', '<text>This is foil One.</text>'),
...@@ -167,6 +169,7 @@ class JavascriptInputTest(unittest.TestCase): ...@@ -167,6 +169,7 @@ class JavascriptInputTest(unittest.TestCase):
'STATIC_URL': '/dummy-static/', 'STATIC_URL': '/dummy-static/',
'id': 'prob_1_2', 'id': 'prob_1_2',
'status': 'unanswered', 'status': 'unanswered',
# 'label': '',
'msg': '', 'msg': '',
'value': '3', 'value': '3',
'params': params, 'params': params,
...@@ -185,7 +188,7 @@ class TextLineTest(unittest.TestCase): ...@@ -185,7 +188,7 @@ class TextLineTest(unittest.TestCase):
def test_rendering(self): def test_rendering(self):
size = "42" size = "42"
xml_str = """<textline id="prob_1_2" size="{size}"/>""".format(size=size) xml_str = """<textline id="prob_1_2" label="testing 123" size="{size}"/>""".format(size=size)
element = etree.fromstring(xml_str) element = etree.fromstring(xml_str)
...@@ -199,6 +202,7 @@ class TextLineTest(unittest.TestCase): ...@@ -199,6 +202,7 @@ class TextLineTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': 'BumbleBee', 'value': 'BumbleBee',
'status': 'unanswered', 'status': 'unanswered',
'label': 'testing 123',
'size': size, 'size': size,
'msg': '', 'msg': '',
'hidden': False, 'hidden': False,
...@@ -230,6 +234,7 @@ class TextLineTest(unittest.TestCase): ...@@ -230,6 +234,7 @@ class TextLineTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': 'BumbleBee', 'value': 'BumbleBee',
'status': 'unanswered', 'status': 'unanswered',
'label': '',
'size': size, 'size': size,
'msg': '', 'msg': '',
'hidden': False, 'hidden': False,
...@@ -273,6 +278,7 @@ class TextLineTest(unittest.TestCase): ...@@ -273,6 +278,7 @@ class TextLineTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': 'BumbleBee', 'value': 'BumbleBee',
'status': 'unanswered', 'status': 'unanswered',
'label': '',
'size': size, 'size': size,
'msg': '', 'msg': '',
'hidden': False, 'hidden': False,
...@@ -313,6 +319,7 @@ class FileSubmissionTest(unittest.TestCase): ...@@ -313,6 +319,7 @@ class FileSubmissionTest(unittest.TestCase):
'STATIC_URL': '/dummy-static/', 'STATIC_URL': '/dummy-static/',
'id': 'prob_1_2', 'id': 'prob_1_2',
'status': 'queued', 'status': 'queued',
'label': '',
'msg': the_input.submitted_msg, 'msg': the_input.submitted_msg,
'value': 'BumbleBee.py', 'value': 'BumbleBee.py',
'queue_len': '3', 'queue_len': '3',
...@@ -362,6 +369,7 @@ class CodeInputTest(unittest.TestCase): ...@@ -362,6 +369,7 @@ class CodeInputTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': 'print "good evening"', 'value': 'print "good evening"',
'status': 'queued', 'status': 'queued',
# 'label': '',
'msg': the_input.submitted_msg, 'msg': the_input.submitted_msg,
'mode': mode, 'mode': mode,
'linenumbers': linenumbers, 'linenumbers': linenumbers,
...@@ -415,6 +423,7 @@ class MatlabTest(unittest.TestCase): ...@@ -415,6 +423,7 @@ class MatlabTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': 'print "good evening"', 'value': 'print "good evening"',
'status': 'queued', 'status': 'queued',
# 'label': '',
'msg': self.the_input.submitted_msg, 'msg': self.the_input.submitted_msg,
'mode': self.mode, 'mode': self.mode,
'rows': self.rows, 'rows': self.rows,
...@@ -444,6 +453,7 @@ class MatlabTest(unittest.TestCase): ...@@ -444,6 +453,7 @@ class MatlabTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': 'print "good evening"', 'value': 'print "good evening"',
'status': 'queued', 'status': 'queued',
# 'label': '',
'msg': the_input.submitted_msg, 'msg': the_input.submitted_msg,
'mode': self.mode, 'mode': self.mode,
'rows': self.rows, 'rows': self.rows,
...@@ -473,6 +483,7 @@ class MatlabTest(unittest.TestCase): ...@@ -473,6 +483,7 @@ class MatlabTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': 'print "good evening"', 'value': 'print "good evening"',
'status': status, 'status': status,
# 'label': '',
'msg': '', 'msg': '',
'mode': self.mode, 'mode': self.mode,
'rows': self.rows, 'rows': self.rows,
...@@ -501,6 +512,7 @@ class MatlabTest(unittest.TestCase): ...@@ -501,6 +512,7 @@ class MatlabTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': 'print "good evening"', 'value': 'print "good evening"',
'status': 'queued', 'status': 'queued',
# 'label': '',
'msg': the_input.submitted_msg, 'msg': the_input.submitted_msg,
'mode': self.mode, 'mode': self.mode,
'rows': self.rows, 'rows': self.rows,
...@@ -610,6 +622,7 @@ class SchematicTest(unittest.TestCase): ...@@ -610,6 +622,7 @@ class SchematicTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': value, 'value': value,
'status': 'unsubmitted', 'status': 'unsubmitted',
'label': '',
'msg': '', 'msg': '',
'initial_value': initial_value, 'initial_value': initial_value,
'width': width, 'width': width,
...@@ -652,6 +665,7 @@ class ImageInputTest(unittest.TestCase): ...@@ -652,6 +665,7 @@ class ImageInputTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': value, 'value': value,
'status': 'unsubmitted', 'status': 'unsubmitted',
'label': '',
'width': width, 'width': width,
'height': height, 'height': height,
'src': src, 'src': src,
...@@ -706,6 +720,7 @@ class CrystallographyTest(unittest.TestCase): ...@@ -706,6 +720,7 @@ class CrystallographyTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': value, 'value': value,
'status': 'unsubmitted', 'status': 'unsubmitted',
# 'label': '',
'msg': '', 'msg': '',
'width': width, 'width': width,
'height': height, 'height': height,
...@@ -779,6 +794,7 @@ class ChemicalEquationTest(unittest.TestCase): ...@@ -779,6 +794,7 @@ class ChemicalEquationTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': 'H2OYeah', 'value': 'H2OYeah',
'status': 'unanswered', 'status': 'unanswered',
'label': '',
'msg': '', 'msg': '',
'size': self.size, 'size': self.size,
'previewer': '/dummy-static/js/capa/chemical_equation_preview.js', 'previewer': '/dummy-static/js/capa/chemical_equation_preview.js',
...@@ -866,6 +882,7 @@ class FormulaEquationTest(unittest.TestCase): ...@@ -866,6 +882,7 @@ class FormulaEquationTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': 'x^2+1/2', 'value': 'x^2+1/2',
'status': 'unanswered', 'status': 'unanswered',
'label': '',
'reported_status': '', 'reported_status': '',
'msg': '', 'msg': '',
'size': self.size, 'size': self.size,
...@@ -1013,6 +1030,7 @@ class DragAndDropTest(unittest.TestCase): ...@@ -1013,6 +1030,7 @@ class DragAndDropTest(unittest.TestCase):
'id': 'prob_1_2', 'id': 'prob_1_2',
'value': value, 'value': value,
'status': 'unsubmitted', 'status': 'unsubmitted',
# 'label': '',
'msg': '', 'msg': '',
'drag_and_drop_json': json.dumps(user_input) 'drag_and_drop_json': json.dumps(user_input)
} }
...@@ -1065,6 +1083,7 @@ class AnnotationInputTest(unittest.TestCase): ...@@ -1065,6 +1083,7 @@ class AnnotationInputTest(unittest.TestCase):
'id': 'annotation_input', 'id': 'annotation_input',
'value': value, 'value': value,
'status': 'answered', 'status': 'answered',
# 'label': '',
'msg': '', 'msg': '',
'title': 'foo', 'title': 'foo',
'text': 'bar', 'text': 'bar',
...@@ -1140,6 +1159,7 @@ class TestChoiceText(unittest.TestCase): ...@@ -1140,6 +1159,7 @@ class TestChoiceText(unittest.TestCase):
expected = { expected = {
'STATIC_URL': '/dummy-static/', 'STATIC_URL': '/dummy-static/',
'msg': '', 'msg': '',
'label': '',
'input_type': expected_input_type, 'input_type': expected_input_type,
'choices': choices, 'choices': choices,
'show_correctness': 'always', 'show_correctness': 'always',
......
...@@ -388,6 +388,164 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -388,6 +388,164 @@ describe 'MarkdownEditingDescriptor', ->
</div> </div>
</solution> </solution>
</problem>""") </problem>""")
# test labels
it 'converts markdown labels to label attributes', ->
data = MarkdownEditingDescriptor.markdownToXml(""">>Who lead the civil right movement in the United States of America?<<
= | \w*\.?\s*Luther King\s*.*
[Explanation]
Test Explanation.
[Explanation]
""")
expect(data).toEqual("""<problem>
<p>Who lead the civil right movement in the United States of America?</p>
<stringresponse answer="w*.?s*Luther Kings*.*" type="ci regexp" >
<textline label="Who lead the civil right movement in the United States of America?" size="20"/>
</stringresponse>
<solution>
<div class="detailed-solution">
<p>Explanation</p>
<p>Test Explanation.</p>
</div>
</solution>
</problem>""")
it 'handles multiple questions with labels', ->
data = MarkdownEditingDescriptor.markdownToXml("""
France is a country in Europe.
>>What is the capital of France?<<
= Paris
Germany is a country in Europe, too.
>>What is the capital of Germany?<<
( ) Bonn
( ) Hamburg
(x) Berlin
( ) Donut
""")
expect(data).toEqual("""<problem>
<p>France is a country in Europe.</p>
<p>What is the capital of France?</p>
<stringresponse answer="Paris" type="ci" >
<textline label="What is the capital of France?" size="20"/>
</stringresponse>
<p>Germany is a country in Europe, too.</p>
<p>What is the capital of Germany?</p>
<multiplechoiceresponse>
<choicegroup type="MultipleChoice">
<choice correct="false">Bonn</choice>
<choice correct="false">Hamburg</choice>
<choice correct="true">Berlin</choice>
<choice correct="false">Donut</choice>
</choicegroup>
</multiplechoiceresponse>
</problem>""")
it 'tests multiple questions with only one label', ->
data = MarkdownEditingDescriptor.markdownToXml("""
France is a country in Europe.
>>What is the capital of France?<<
= Paris
Germany is a country in Europe, too.
What is the capital of Germany?
( ) Bonn
( ) Hamburg
(x) Berlin
( ) Donut
""")
expect(data).toEqual("""<problem>
<p>France is a country in Europe.</p>
<p>What is the capital of France?</p>
<stringresponse answer="Paris" type="ci" >
<textline label="What is the capital of France?" size="20"/>
</stringresponse>
<p>Germany is a country in Europe, too.</p>
<p>What is the capital of Germany?</p>
<multiplechoiceresponse>
<choicegroup type="MultipleChoice">
<choice correct="false">Bonn</choice>
<choice correct="false">Hamburg</choice>
<choice correct="true">Berlin</choice>
<choice correct="false">Donut</choice>
</choicegroup>
</multiplechoiceresponse>
</problem>""")
it 'tests malformed labels', ->
data = MarkdownEditingDescriptor.markdownToXml("""
France is a country in Europe.
>>What is the capital of France?<
= Paris
blah>>What is the capital of <<Germany?<<
( ) Bonn
( ) Hamburg
(x) Berlin
( ) Donut
""")
expect(data).toEqual("""<problem>
<p>France is a country in Europe.</p>
<p>>>What is the capital of France?<</p>
<stringresponse answer="Paris" type="ci" >
<textline size="20"/>
</stringresponse>
<p>blahWhat is the capital of Germany?</p>
<multiplechoiceresponse>
<choicegroup label="What is the capital of &lt;&lt;Germany?" type="MultipleChoice">
<choice correct="false">Bonn</choice>
<choice correct="false">Hamburg</choice>
<choice correct="true">Berlin</choice>
<choice correct="false">Donut</choice>
</choicegroup>
</multiplechoiceresponse>
</problem>""")
it 'adds labels to formulae', ->
data = MarkdownEditingDescriptor.markdownToXml("""
>>Enter the numerical value of Pi:<<
= 3.14159 +- .02
""")
expect(data).toEqual("""<problem>
<p>Enter the numerical value of Pi:</p>
<numericalresponse answer="3.14159">
<responseparam type="tolerance" default=".02" />
<formulaequationinput label="Enter the numerical value of Pi:" />
</numericalresponse>
</problem>""")
it 'escapes entities in labels', ->
data = MarkdownEditingDescriptor.markdownToXml("""
>>What is the "capital" of France & the 'best' > place < to live"?<<
= Paris
""")
expect(data).toEqual("""<problem>
<p>What is the "capital" of France & the 'best' > place < to live"?</p>
<stringresponse answer="Paris" type="ci" >
<textline label="What is the &quot;capital&quot; of France &amp; the &apos;best&apos; &gt; place &lt; to live&quot;?" size="20"/>
</stringresponse>
</problem>""")
# test oddities # test oddities
it 'converts headers and oddities to xml', -> it 'converts headers and oddities to xml', ->
data = MarkdownEditingDescriptor.markdownToXml("""Not a header data = MarkdownEditingDescriptor.markdownToXml("""Not a header
......
...@@ -329,6 +329,33 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor ...@@ -329,6 +329,33 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor
return selectString; return selectString;
}); });
// replace labels
// looks for >>arbitrary text<< and inserts it into the label attribute of the input type directly below the text.
var split = xml.split('\n');
var new_xml = [];
var line, i, curlabel = '';
var didinput = false;
for (i = 0; i < split.length; i++) {
line = split[i];
if (match = line.match(/>>(.*)<</)) {
curlabel = match[1].replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&apos;');
line = line.replace(/>>|<</g, '');
} else if (line.match(/<\w+response/) && didinput) {
// reset label to prevent gobbling up previous one (if multiple questions)
curlabel = '';
didinput = false;
} else if (line.match(/<(textline|optioninput|formulaequationinput|choicegroup|checkboxgroup)/) && curlabel != '') {
line = line.replace(/<(textline|optioninput|formulaequationinput|choicegroup|checkboxgroup)/, '<$1 label="' + curlabel + '"');
didinput = true;
}
new_xml.push(line);
}
xml = new_xml.join('\n');
// replace code blocks // replace code blocks
xml = xml.replace(/\[code\]\n?([^\]]*)\[\/?code\]/gmi, function(match, p1) { xml = xml.replace(/\[code\]\n?([^\]]*)\[\/?code\]/gmi, function(match, p1) {
......
...@@ -13,7 +13,7 @@ data: | ...@@ -13,7 +13,7 @@ data: |
<problem> <problem>
<p>A checkboxes problem presents checkbox buttons for student input. Students can select more than one option presented.</p> <p>A checkboxes problem presents checkbox buttons for student input. Students can select more than one option presented.</p>
<choiceresponse> <choiceresponse>
<checkboxgroup direction="vertical"> <checkboxgroup direction="vertical" label="Select the answer that matches">
<choice correct="true">correct</choice> <choice correct="true">correct</choice>
<choice correct="false">incorrect</choice> <choice correct="false">incorrect</choice>
<choice correct="true">correct</choice> <choice correct="true">correct</choice>
......
...@@ -27,14 +27,14 @@ data: | ...@@ -27,14 +27,14 @@ data: |
<p>Enter two integers which sum to 10: </p> <p>Enter two integers which sum to 10: </p>
<customresponse cfn="test_add_to_ten"> <customresponse cfn="test_add_to_ten">
<textline size="40" correct_answer="3"/><br/> <textline size="40" correct_answer="3" label="Integer #1"/><br/>
<textline size="40" correct_answer="7"/> <textline size="40" correct_answer="7" label="Integer #2"/>
</customresponse> </customresponse>
<p>Enter two integers which sum to 20: </p> <p>Enter two integers which sum to 20: </p>
<customresponse cfn="test_add" expect="20"> <customresponse cfn="test_add" expect="20">
<textline size="40" correct_answer="11"/><br/> <textline size="40" correct_answer="11" label="Integer #1"/><br/>
<textline size="40" correct_answer="9"/> <textline size="40" correct_answer="9" label="Integer #2"/>
</customresponse> </customresponse>
<solution> <solution>
......
...@@ -32,7 +32,7 @@ data: | ...@@ -32,7 +32,7 @@ data: |
<formularesponse type="ci" samples="R_1,R_2,R_3@1,2,3:3,4,5#10" answer="$VoVi"> <formularesponse type="ci" samples="R_1,R_2,R_3@1,2,3:3,4,5#10" answer="$VoVi">
<responseparam type="tolerance" default="0.00001"/> <responseparam type="tolerance" default="0.00001"/>
<formulaequationinput size="40" /> <formulaequationinput size="40" label="Enter the equation"/>
</formularesponse> </formularesponse>
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
......
...@@ -98,7 +98,7 @@ data: | ...@@ -98,7 +98,7 @@ data: |
Where is the earth? </p> Where is the earth? </p>
<p> <p>
<optionresponse> <optionresponse>
<optioninput options="('up','down')" correct="down"/> <optioninput options="('up','down')" correct="down" label="Where is the earth?"/>
</optionresponse> </optionresponse>
</p> </p>
<p> <p>
...@@ -108,7 +108,7 @@ data: | ...@@ -108,7 +108,7 @@ data: |
What is Einstein's equation for the energy equivalent of a mass [mathjaxinline]m[/mathjaxinline]? </p> What is Einstein's equation for the energy equivalent of a mass [mathjaxinline]m[/mathjaxinline]? </p>
<p> <p>
<symbolicresponse expect="m*c^2"> <symbolicresponse expect="m*c^2">
<textline size="90" correct_answer="m*c^2" math="1"/> <textline size="90" correct_answer="m*c^2" math="1" label="Enter Einstein's equation"/>
</symbolicresponse> </symbolicresponse>
</p> </p>
<p> <p>
...@@ -120,7 +120,7 @@ data: | ...@@ -120,7 +120,7 @@ data: |
<p style="display:inline">Energy saved = </p> <p style="display:inline">Energy saved = </p>
<numericalresponse inline="1" answer="0.52"> <numericalresponse inline="1" answer="0.52">
<responseparam description="Numerical Tolerance" type="tolerance" default="0.02" name="tol"/> <responseparam description="Numerical Tolerance" type="tolerance" default="0.02" name="tol"/>
<formulaequationinput/> <formulaequationinput label="Enter the equation"/>
</numericalresponse> </numericalresponse>
<p style="display:inline">&#xA0;EJ/year</p> <p style="display:inline">&#xA0;EJ/year</p>
</p> </p>
...@@ -131,7 +131,7 @@ data: | ...@@ -131,7 +131,7 @@ data: |
What color is a banana? </p> What color is a banana? </p>
<p> <p>
<choiceresponse> <choiceresponse>
<checkboxgroup> <checkboxgroup label="What color is a banana?">
<choice correct="false" name="1"> <choice correct="false" name="1">
<text>Red</text> <text>Red</text>
</choice> </choice>
...@@ -154,7 +154,7 @@ data: | ...@@ -154,7 +154,7 @@ data: |
In what U.S. state is Detroit located? </p> In what U.S. state is Detroit located? </p>
<p> <p>
<stringresponse answer="Michigan"> <stringresponse answer="Michigan">
<textline/> <textline label="What state contains Michigan?"/>
</stringresponse> </stringresponse>
</p> </p>
<p> <p>
...@@ -179,7 +179,7 @@ data: | ...@@ -179,7 +179,7 @@ data: |
Enter a python list of two numbers which sum to 10, eg [9,1]: </p> Enter a python list of two numbers which sum to 10, eg [9,1]: </p>
<p> <p>
<customresponse cfn="sumtest" expect="[1,9]"> <customresponse cfn="sumtest" expect="[1,9]">
<textline correct_answer="[1,9]"/> <textline correct_answer="[1,9]" label="Enter the python list"/>
</customresponse> </customresponse>
</p> </p>
<p> <p>
......
...@@ -10,7 +10,7 @@ metadata: ...@@ -10,7 +10,7 @@ metadata:
That is, each of the alternate responses presented to the student should be the result of a plausible mistake That is, each of the alternate responses presented to the student should be the result of a plausible mistake
that a student might make. that a student might make.
What Apple device competed with the portable CD player? >>What Apple device competed with the portable CD player?<<
( ) The iPad ( ) The iPad
( ) Napster ( ) Napster
(x) The iPod (x) The iPod
...@@ -30,7 +30,7 @@ data: | ...@@ -30,7 +30,7 @@ data: |
<p>What Apple device competed with the portable CD player?</p> <p>What Apple device competed with the portable CD player?</p>
<multiplechoiceresponse> <multiplechoiceresponse>
<choicegroup type="MultipleChoice"> <choicegroup type="MultipleChoice" label="What Apple device competed with the portable CD player?">
<choice correct="false" name="ipad">The iPad</choice> <choice correct="false" name="ipad">The iPad</choice>
<choice correct="false" name="beatles">Napster</choice> <choice correct="false" name="beatles">Napster</choice>
<choice correct="true" name="ipod">The iPod</choice> <choice correct="true" name="ipod">The iPod</choice>
......
...@@ -9,13 +9,13 @@ metadata: ...@@ -9,13 +9,13 @@ metadata:
The answer is correct if it is within a specified numerical tolerance The answer is correct if it is within a specified numerical tolerance
of the expected answer. of the expected answer.
Enter the numerical value of Pi: >>Enter the numerical value of Pi:<<
= 3.14159 +- .02 = 3.14159 +- .02
Enter the approximate value of 502*9: >>Enter the approximate value of 502*9:<<
= 4518 +- 15% = 4518 +- 15%
Enter the number of fingers on a human hand: >>Enter the number of fingers on a human hand<<
= 5 = 5
[explanation] [explanation]
...@@ -47,19 +47,19 @@ data: | ...@@ -47,19 +47,19 @@ data: |
<p>Enter the numerical value of Pi: <p>Enter the numerical value of Pi:
<numericalresponse answer="3.14159"> <numericalresponse answer="3.14159">
<responseparam type="tolerance" default=".02" /> <responseparam type="tolerance" default=".02" />
<formulaequationinput /> <formulaequationinput label="Enter the numerical value of Pi" />
</numericalresponse> </numericalresponse>
</p> </p>
<p>Enter the approximate value of 502*9: <p>Enter the approximate value of 502*9:
<numericalresponse answer="$computed_response"> <numericalresponse answer="$computed_response">
<responseparam type="tolerance" default="15%"/> <responseparam type="tolerance" default="15%"/>
<formulaequationinput /> <formulaequationinput label="Enter the approximate value of 502 times 9"/>
</numericalresponse> </numericalresponse>
</p> </p>
<p>Enter the number of fingers on a human hand: <p>Enter the number of fingers on a human hand:
<numericalresponse answer="5"> <numericalresponse answer="5">
<formulaequationinput /> <formulaequationinput label="Enter the number of fingers on a human hand"/>
</numericalresponse> </numericalresponse>
</p> </p>
<solution> <solution>
......
...@@ -8,7 +8,7 @@ metadata: ...@@ -8,7 +8,7 @@ metadata:
The answer options and the identification of the correct answer is defined in the <b>optioninput</b> tag. The answer options and the identification of the correct answer is defined in the <b>optioninput</b> tag.
Translation between Dropdown and __________ is extremely straightforward: >>Translation between Dropdown and __________ is extremely straightforward:<<
[[(Multiple Choice), Text Input, Numerical Input, External Response, Image Response]] [[(Multiple Choice), Text Input, Numerical Input, External Response, Image Response]]
...@@ -29,7 +29,7 @@ data: | ...@@ -29,7 +29,7 @@ data: |
<p>Translation between Dropdown and __________ is extremely straightforward: <p>Translation between Dropdown and __________ is extremely straightforward:
<optionresponse> <optionresponse>
<optioninput options="('Multiple Choice','Text Input','Numerical Input','External Response','Image Response')" correct="Multiple Choice"></optioninput> <optioninput options="('Multiple Choice','Text Input','Numerical Input','External Response','Image Response')" correct="Multiple Choice" label="Translation between Dropdown and __________ is extremely straightforward"></optioninput>
</optionresponse> </optionresponse>
</p> </p>
<solution> <solution>
......
...@@ -40,7 +40,7 @@ data: | ...@@ -40,7 +40,7 @@ data: |
What is the best programming language that exists today? You may enter your answer in upper or lower case, with or without quotes. </p> What is the best programming language that exists today? You may enter your answer in upper or lower case, with or without quotes. </p>
<p> <p>
<customresponse cfn="test_str" expect="python"> <customresponse cfn="test_str" expect="python">
<textline correct_answer="python"/> <textline correct_answer="python" label="What is the best programming language that exists today?"/>
<hintgroup hintfn="hint_fn"/> <hintgroup hintfn="hint_fn"/>
</customresponse> </customresponse>
</p> </p>
......
...@@ -85,7 +85,7 @@ data: | ...@@ -85,7 +85,7 @@ data: |
What is the best programming language that exists today? You may enter your answer in upper or lower case, with or without quotes. </p> What is the best programming language that exists today? You may enter your answer in upper or lower case, with or without quotes. </p>
<p> <p>
<customresponse cfn="test_str" expect="python"> <customresponse cfn="test_str" expect="python">
<textline correct_answer="python"/> <textline correct_answer="python" label="What is the best programming language that exists today?"/>
<hintgroup hintfn="hint_fn"/> <hintgroup hintfn="hint_fn"/>
</customresponse> </customresponse>
</p> </p>
......
...@@ -9,7 +9,7 @@ metadata: ...@@ -9,7 +9,7 @@ metadata:
The answer is correct if it matches every character of the expected answer. This can be a problem with The answer is correct if it matches every character of the expected answer. This can be a problem with
international spelling, dates, or anything where the format of the answer is not clear. international spelling, dates, or anything where the format of the answer is not clear.
Which US state has Lansing as its capital? >>Which US state has Lansing as its capital?<<
= Michigan = Michigan
...@@ -29,9 +29,9 @@ data: | ...@@ -29,9 +29,9 @@ data: |
The answer is correct if it matches every character of the expected answer. This can be a problem with international spelling, dates, or anything where the format of the answer is not clear. The answer is correct if it matches every character of the expected answer. This can be a problem with international spelling, dates, or anything where the format of the answer is not clear.
</p> </p>
<p >Which US state has Lansing as its capital? </p> <p>Which US state has Lansing as its capital? </p>
<stringresponse answer="Michigan" type="ci"> <stringresponse answer="Michigan" type="ci">
<textline size="20"/> <textline size="20" label="Which US state has Lansing as its capital?"/>
</stringresponse> </stringresponse>
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
......
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