Commit 16a248b9 by Ahsan Ulhaq

Fieldset and legend solution is lost when edit Problem

when user try to update a problem the fieldset and legend solution for that
problem is lost because legend and fieldset are not added in the markdownToXml.
So added new markups where fieldset is to be added and used ```>><<``` markup for
legend.

AC-103
parent b1227c1d
......@@ -139,6 +139,7 @@ class LoncapaProblem(object):
self.do_reset()
self.problem_id = id
self.capa_system = capa_system
self.question_label = ""
state = state or {}
......@@ -732,6 +733,7 @@ class LoncapaProblem(object):
'status': status,
'id': input_id,
'input_state': self.input_state[input_id],
'question_label': self.question_label if self.question_label else None,
'answervariable': answervariable,
'feedback': {
'message': msg,
......@@ -762,8 +764,12 @@ class LoncapaProblem(object):
tree = etree.Element(problemtree.tag)
for item in problemtree:
item_xhtml = self._extract_html(item)
item_sibling = self.sibling_for_item(problemtree, item)
if item_xhtml is not None:
tree.append(item_xhtml)
if item_xhtml.tag == "legend" and item_sibling is not None and item_sibling in self.responders:
self.question_label = item_xhtml.text
else:
tree.append(item_xhtml)
if tree.tag in html_transforms:
tree.tag = html_transforms[problemtree.tag]['tag']
......@@ -834,3 +840,18 @@ class LoncapaProblem(object):
for solution in tree.findall('.//solution'):
solution.attrib['id'] = "%s_solution_%i" % (self.problem_id, solution_id)
solution_id += 1
def sibling_for_item(self, tree, node):
"""
Check if node exist in problem tree and return next sibling if exist
else return None
"""
problem_tree = tree.xpath('//problem/*')
if node in problem_tree:
node_index = problem_tree.index(node)
try:
return problem_tree[node_index + 1]
except IndexError:
return None
else:
return None
......@@ -214,6 +214,7 @@ class InputTypeBase(object):
self.hintmode = feedback.get('hintmode', None)
self.input_state = state.get('input_state', {})
self.answervariable = state.get("answervariable", None)
self.question_label = state.get("question_label", None)
# put hint above msg if it should be displayed
if self.hintmode == 'always':
......@@ -313,6 +314,7 @@ class InputTypeBase(object):
context.update(self._extra_context())
if self.answervariable:
context.update({'answervariable': self.answervariable})
context.update({'question_label': self.question_label if self.question_label is not None else ""})
return context
def _extra_context(self):
......
......@@ -17,8 +17,8 @@
% endif
</div>
<fieldset role="${input_type}group" aria-label="${label}">
<fieldset>
<legend>${question_label}</legend>
% for choice_id, choice_description in choices:
<label for="input_${id}_${choice_id}"
## If the student has selected this choice...
......
......@@ -175,6 +175,7 @@ class CapaHtmlRenderTest(unittest.TestCase):
'label': '',
'value': '',
'preprocessor': None,
'question_label': '',
'msg': '',
'inline': False,
'hidden': False,
......
......@@ -126,6 +126,7 @@ class ChoiceGroupTemplateTest(TemplateTestCase):
self.context = {'id': '1',
'choices': choices,
'status': Status('correct'),
'question_label': '',
'label': 'test',
'input_type': 'checkbox',
'name_array_suffix': '1',
......@@ -141,6 +142,7 @@ class ChoiceGroupTemplateTest(TemplateTestCase):
self.context['status'] = Status('correct')
self.context['input_type'] = 'checkbox'
self.context['value'] = ['1', '2']
self.context['question_label'] = ['']
# Should mark the entire problem correct
xml = self.render_to_xml(self.context)
......@@ -222,8 +224,8 @@ class ChoiceGroupTemplateTest(TemplateTestCase):
(not the entire problem) is marked correct.
"""
conditions = [
{'input_type': 'radio', 'value': '2'},
{'input_type': 'radio', 'value': ['2']}]
{'input_type': 'radio', 'question_label': '', 'value': '2'},
{'input_type': 'radio', 'question_label': '', 'value': ['2']}]
self.context['status'] = Status('correct')
......@@ -270,16 +272,16 @@ class ChoiceGroupTemplateTest(TemplateTestCase):
"""
conditions = [
{'input_type': 'radio', 'status': Status('correct'), 'value': ''},
{'input_type': 'radio', 'status': Status('correct'), 'value': '2'},
{'input_type': 'radio', 'status': Status('correct'), 'value': ['2']},
{'input_type': 'radio', 'status': Status('incorrect'), 'value': '2'},
{'input_type': 'radio', 'status': Status('incorrect'), 'value': []},
{'input_type': 'radio', 'status': Status('incorrect'), 'value': ['2']},
{'input_type': 'checkbox', 'status': Status('correct'), 'value': []},
{'input_type': 'checkbox', 'status': Status('correct'), 'value': ['2']},
{'input_type': 'checkbox', 'status': Status('incorrect'), 'value': []},
{'input_type': 'checkbox', 'status': Status('incorrect'), 'value': ['2']}]
{'input_type': 'radio', 'status': Status('correct'), 'question_label': '', 'value': ''},
{'input_type': 'radio', 'status': Status('correct'), 'question_label': '', 'value': '2'},
{'input_type': 'radio', 'status': Status('correct'), 'question_label': '', 'value': ['2']},
{'input_type': 'radio', 'status': Status('incorrect'), 'question_label': '', 'value': '2'},
{'input_type': 'radio', 'status': Status('incorrect'), 'question_label': '', 'value': []},
{'input_type': 'radio', 'status': Status('incorrect'), 'question_label': '', 'value': ['2']},
{'input_type': 'checkbox', 'status': Status('correct'), 'question_label': '', 'value': []},
{'input_type': 'checkbox', 'status': Status('correct'), 'question_label': '', 'value': ['2']},
{'input_type': 'checkbox', 'status': Status('incorrect'), 'question_label': '', 'value': []},
{'input_type': 'checkbox', 'status': Status('incorrect'), 'question_label': '', 'value': ['2']}]
self.context['show_correctness'] = 'never'
self.context['submitted_message'] = 'Test message'
......@@ -315,9 +317,9 @@ class ChoiceGroupTemplateTest(TemplateTestCase):
"""
conditions = [
{'input_type': 'radio', 'status': Status('unsubmitted'), 'value': ''},
{'input_type': 'radio', 'status': Status('unsubmitted'), 'value': []},
{'input_type': 'checkbox', 'status': Status('unsubmitted'), 'value': []},
{'input_type': 'radio', 'status': Status('unsubmitted'), 'question_label': '', 'value': ''},
{'input_type': 'radio', 'status': Status('unsubmitted'), 'question_label': '', 'value': []},
{'input_type': 'checkbox', 'status': Status('unsubmitted'), 'question_label': '', 'value': []},
# These tests expose bug #365
# When the bug is fixed, uncomment these cases.
......@@ -925,7 +927,7 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase):
(not the entire problem) is marked correct."""
conditions = [
{'input_type': 'radio', 'value': self.VALUE_DICT}]
{'input_type': 'radio', 'question_label': '', 'value': self.VALUE_DICT}]
self.context['status'] = 'correct'
......@@ -945,7 +947,7 @@ class ChoiceTextGroupTemplateTest(TemplateTestCase):
(not the entire problem) is marked incorrect."""
conditions = [
{'input_type': 'radio', 'value': self.VALUE_DICT}]
{'input_type': 'radio', 'question_label': '', 'value': self.VALUE_DICT}]
self.context['status'] = 'incorrect'
......
......@@ -61,6 +61,7 @@ class OptionInputTest(unittest.TestCase):
'value': 'Down',
'options': [('Up', 'Up'), ('Down', 'Down'), ('Don\'t know', 'Don\'t know')],
'status': inputtypes.Status('answered'),
'question_label': '',
'label': '',
'msg': '',
'inline': False,
......@@ -121,6 +122,7 @@ class ChoiceGroupTest(unittest.TestCase):
'id': 'sky_input',
'value': 'foil3',
'status': inputtypes.Status('answered'),
'question_label': '',
'label': '',
'msg': '',
'input_type': expected_input_type,
......@@ -174,6 +176,7 @@ class JavascriptInputTest(unittest.TestCase):
'STATIC_URL': '/dummy-static/',
'id': 'prob_1_2',
'status': inputtypes.Status('unanswered'),
'question_label': '',
# 'label': '',
'msg': '',
'value': '3',
......@@ -207,6 +210,7 @@ class TextLineTest(unittest.TestCase):
'id': 'prob_1_2',
'value': 'BumbleBee',
'status': inputtypes.Status('unanswered'),
'question_label': '',
'label': 'testing 123',
'size': size,
'msg': '',
......@@ -239,6 +243,7 @@ class TextLineTest(unittest.TestCase):
'id': 'prob_1_2',
'value': 'BumbleBee',
'status': inputtypes.Status('unanswered'),
'question_label': '',
'label': '',
'size': size,
'msg': '',
......@@ -283,6 +288,7 @@ class TextLineTest(unittest.TestCase):
'id': 'prob_1_2',
'value': 'BumbleBee',
'status': inputtypes.Status('unanswered'),
'question_label': '',
'label': '',
'size': size,
'msg': '',
......@@ -324,6 +330,7 @@ class FileSubmissionTest(unittest.TestCase):
'STATIC_URL': '/dummy-static/',
'id': 'prob_1_2',
'status': inputtypes.Status('queued'),
'question_label': '',
'label': '',
'msg': the_input.submitted_msg,
'value': 'BumbleBee.py',
......@@ -374,6 +381,7 @@ class CodeInputTest(unittest.TestCase):
'id': 'prob_1_2',
'value': 'print "good evening"',
'status': inputtypes.Status('queued'),
'question_label': '',
# 'label': '',
'msg': the_input.submitted_msg,
'mode': mode,
......@@ -429,6 +437,7 @@ class MatlabTest(unittest.TestCase):
'id': 'prob_1_2',
'value': 'print "good evening"',
'status': inputtypes.Status('queued'),
'question_label': '',
# 'label': '',
'msg': self.the_input.submitted_msg,
'mode': self.mode,
......@@ -460,6 +469,7 @@ class MatlabTest(unittest.TestCase):
'id': 'prob_1_2',
'value': 'print "good evening"',
'status': inputtypes.Status('queued'),
'question_label': '',
# 'label': '',
'msg': the_input.submitted_msg,
'mode': self.mode,
......@@ -491,6 +501,7 @@ class MatlabTest(unittest.TestCase):
'id': 'prob_1_2',
'value': 'print "good evening"',
'status': inputtypes.Status(status),
'question_label': '',
# 'label': '',
'msg': '',
'mode': self.mode,
......@@ -522,6 +533,7 @@ class MatlabTest(unittest.TestCase):
'id': 'prob_1_2',
'value': 'print "good evening"',
'status': inputtypes.Status('queued'),
'question_label': '',
# 'label': '',
'msg': the_input.submitted_msg,
'mode': self.mode,
......@@ -648,7 +660,14 @@ class MatlabTest(unittest.TestCase):
output = self.the_input.get_html()
self.assertEqual(
etree.tostring(output),
"""<div>{\'status\': Status(\'queued\'), \'button_enabled\': True, \'rows\': \'10\', \'queue_len\': \'3\', \'mode\': \'\', \'cols\': \'80\', \'STATIC_URL\': \'/dummy-static/\', \'linenumbers\': \'true\', \'queue_msg\': \'\', \'value\': \'print "good evening"\', \'msg\': u\'Submitted. As soon as a response is returned, this message will be replaced by that feedback.\', \'matlab_editor_js\': \'/dummy-static/js/vendor/CodeMirror/octave.js\', \'hidden\': \'\', \'id\': \'prob_1_2\', \'tabsize\': 4}</div>"""
" ".join(textwrap.dedent("""
<div>{\'status\': Status(\'queued\'), \'button_enabled\': True, \'rows\': \'10\', \'queue_len\': \'3\',
\'question_label\': \'\', \'mode\': \'\', \'cols\': \'80\', \'STATIC_URL\': \'/dummy-static/\',
\'linenumbers\': \'true\', \'queue_msg\': \'\', \'value\': \'print "good evening"\',
\'msg\': u\'Submitted. As soon as a response is returned, this message will be replaced by that feedback.\',
\'matlab_editor_js\': \'/dummy-static/js/vendor/CodeMirror/octave.js\', \'hidden\': \'\',
\'id\': \'prob_1_2\', \'tabsize\': 4}</div>
""").replace('\n', ' ').split())
)
# test html, that is correct HTML5 html, but is not parsable by XML parser.
......@@ -739,6 +758,7 @@ class MatlabTest(unittest.TestCase):
'id': 'prob_1_2',
'value': 'print "good evening"',
'status': inputtypes.Status('queued'),
'question_label': '',
'msg': self.the_input.submitted_msg,
'mode': self.mode,
'rows': self.rows,
......@@ -848,6 +868,7 @@ class SchematicTest(unittest.TestCase):
'id': 'prob_1_2',
'value': value,
'status': inputtypes.Status('unsubmitted'),
'question_label': '',
'label': '',
'msg': '',
'initial_value': initial_value,
......@@ -892,6 +913,7 @@ class ImageInputTest(unittest.TestCase):
'id': 'prob_1_2',
'value': value,
'status': inputtypes.Status('unsubmitted'),
'question_label': '',
'label': '',
'width': width,
'height': height,
......@@ -947,6 +969,7 @@ class CrystallographyTest(unittest.TestCase):
'id': 'prob_1_2',
'value': value,
'status': inputtypes.Status('unsubmitted'),
'question_label': '',
# 'label': '',
'msg': '',
'width': width,
......@@ -989,6 +1012,7 @@ class VseprTest(unittest.TestCase):
'id': 'prob_1_2',
'value': value,
'status': inputtypes.Status('unsubmitted'),
'question_label': '',
'msg': '',
'width': width,
'height': height,
......@@ -1022,6 +1046,7 @@ class ChemicalEquationTest(unittest.TestCase):
'id': 'prob_1_2',
'value': 'H2OYeah',
'status': inputtypes.Status('unanswered'),
'question_label': '',
'label': '',
'msg': '',
'size': self.size,
......@@ -1111,6 +1136,7 @@ class FormulaEquationTest(unittest.TestCase):
'id': 'prob_1_2',
'value': 'x^2+1/2',
'status': inputtypes.Status('unanswered'),
'question_label': '',
'label': '',
'msg': '',
'size': self.size,
......@@ -1240,6 +1266,7 @@ class DragAndDropTest(unittest.TestCase):
'id': 'prob_1_2',
'value': value,
'status': inputtypes.Status('unsubmitted'),
'question_label': '',
# 'label': '',
'msg': '',
'drag_and_drop_json': json.dumps(user_input)
......@@ -1292,6 +1319,7 @@ class AnnotationInputTest(unittest.TestCase):
'STATIC_URL': '/dummy-static/',
'id': 'annotation_input',
'status': inputtypes.Status('answered'),
'question_label': '',
# 'label': '',
'msg': '',
'title': 'foo',
......@@ -1368,6 +1396,7 @@ class TestChoiceText(unittest.TestCase):
expected = {
'STATIC_URL': '/dummy-static/',
'msg': '',
'question_label': '',
'label': '',
'input_type': expected_input_type,
'choices': choices,
......
......@@ -509,7 +509,7 @@ describe 'MarkdownEditingDescriptor', ->
[Explanation]
""")
expect(data).toEqual("""<problem>
<p>Who lead the civil right movement in the United States of America?</p>
<legend>Who lead the civil right movement in the United States of America?</legend>
<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>
......@@ -541,14 +541,14 @@ describe 'MarkdownEditingDescriptor', ->
expect(data).toEqual("""<problem>
<p>France is a country in Europe.</p>
<p>What is the capital of France?</p>
<legend>What is the capital of France?</legend>
<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>
<legend>What is the capital of Germany?</legend>
<multiplechoiceresponse>
<choicegroup label="What is the capital of Germany?" type="MultipleChoice">
<choice correct="false">Bonn</choice>
......@@ -574,11 +574,12 @@ describe 'MarkdownEditingDescriptor', ->
( ) Hamburg
(x) Berlin
( ) Donut
""")
expect(data).toEqual("""<problem>
<p>France is a country in Europe.</p>
<p>What is the capital of France?</p>
<legend>What is the capital of France?</legend>
<stringresponse answer="Paris" type="ci" >
<textline label="What is the capital of France?" size="20"/>
</stringresponse>
......@@ -594,8 +595,8 @@ describe 'MarkdownEditingDescriptor', ->
<choice correct="false">Donut</choice>
</choicegroup>
</multiplechoiceresponse>
</problem>""")
it 'tests malformed labels', ->
data = MarkdownEditingDescriptor.markdownToXml("""
......@@ -604,7 +605,7 @@ describe 'MarkdownEditingDescriptor', ->
>>What is the capital of France?<
= Paris
blah>>What is the capital of <<Germany?<<
>>What is the capital of <<Germany?<<
( ) Bonn
( ) Hamburg
(x) Berlin
......@@ -618,7 +619,7 @@ describe 'MarkdownEditingDescriptor', ->
<textline size="20"/>
</stringresponse>
<p>blahWhat is the capital of Germany?</p>
<legend>What is the capital of Germany?</legend>
<multiplechoiceresponse>
<choicegroup label="What is the capital of &lt;&lt;Germany?" type="MultipleChoice">
<choice correct="false">Bonn</choice>
......@@ -636,7 +637,7 @@ describe 'MarkdownEditingDescriptor', ->
= 3.14159 +- .02
""")
expect(data).toEqual("""<problem>
<p>Enter the numerical value of Pi:</p>
<legend>Enter the numerical value of Pi:</legend>
<numericalresponse answer="3.14159">
<responseparam type="tolerance" default=".02" />
<formulaequationinput label="Enter the numerical value of Pi:" />
......@@ -650,7 +651,7 @@ describe 'MarkdownEditingDescriptor', ->
= Paris
""")
expect(data).toEqual("""<problem>
<p>What is the "capital" of France & the 'best' > place < to live"?</p>
<legend>What is the "capital" of France & the 'best' > place < to live"?</legend>
<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>
......
......@@ -371,7 +371,9 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&apos;');
line = line.replace(/>>|<</g, '');
line = line.replace(/>>/g, '<legend>');
line = line.replace(/<+$/g, '</legend>');
line = line.replace(/<</g, '');
} else if (line.match(/<\w+response/) && didinput && curlabel == prevlabel) {
// reset label to prevent gobbling up previous one (if multiple questions)
curlabel = '';
......
......@@ -16,6 +16,7 @@ metadata:
[x] French
[ ] Hungarian
Note: Make sure you select all of the correct options—there may be more than one!
[explanation]
......@@ -28,18 +29,16 @@ data: |
<p>When you add the component, be sure to select <strong>Settings</strong>
to specify a <strong>Display Name</strong> and other values that apply.</p>
<p>You can use the following example problem as a model.</p>
<fieldset>
<legend>The following languages are in the Indo-European family:</legend>
<choiceresponse>
<checkboxgroup direction="vertical">
<choice correct="true" name="urdu">Urdu</choice>
<choice correct="false" name="finnish">Finnish</choice>
<choice correct="true" name="marathi">Marathi</choice>
<choice correct="true" name="french">French</choice>
<choice correct="false" name="hungarian">Hungarian</choice>
</checkboxgroup>
</choiceresponse>
</fieldset>
<legend>The following languages are in the Indo-European family:</legend>
<choiceresponse>
<checkboxgroup direction="vertical">
<choice correct="true" name="urdu">Urdu</choice>
<choice correct="false" name="finnish">Finnish</choice>
<choice correct="true" name="marathi">Marathi</choice>
<choice correct="true" name="french">French</choice>
<choice correct="false" name="hungarian">Hungarian</choice>
</checkboxgroup>
</choiceresponse>
<p><strong>Note</strong>: Make sure you select all of the correct options—there may be more than one!</p>
<solution>
<div class="detailed-solution">
......
......@@ -15,6 +15,7 @@ metadata:
(x) Indonesia
( ) Russia
[explanation]
According to September 2014 estimates:
The population of Indonesia is approximately 250 million.
......@@ -30,7 +31,6 @@ data: |
<p>When you add the problem, be sure to select <strong>Settings</strong>
to specify a <strong>Display Name</strong> and other values that apply.</p>
<p>You can use the following example problem as a model.</p>
<fieldset>
<legend>Which of the following countries has the largest population?</legend>
<multiplechoiceresponse>
<choicegroup type="MultipleChoice">
......@@ -40,7 +40,6 @@ data: |
<choice correct="false" name="russia">Russia</choice>
</choicegroup>
</multiplechoiceresponse>
</fieldset>
<solution>
<div class="detailed-solution">
<p>Explanation</p>
......
......@@ -14,11 +14,12 @@ metadata:
>>How many miles away from Earth is the sun? Use scientific notation to answer.<<
= 9.3*10^6
or= 9.296*10^6
or= 9.296*10^6
>>The square of what number is -100?<<
= 10*i
= 10*i
[explanation]
The sun is 93,000,000, or 9.3*10^6, miles away from Earth.
......
......@@ -14,6 +14,7 @@ metadata:
[[(India), Spain, China, Bermuda]]
[explanation]
India became an independent nation on August 15, 1947.
[explanation]
......
......@@ -14,7 +14,7 @@ metadata:
= Nanjing Higher Normal Institute
or= National Central University
or= Nanjing University
[explanation]
Nanjing Higher Normal Institute first admitted female students in 1920.
[explanation]
......
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