Commit 8d354302 by muhammad-ammar

address christina's feedback

parent e11a4f38
...@@ -737,7 +737,7 @@ class LoncapaProblem(object): ...@@ -737,7 +737,7 @@ class LoncapaProblem(object):
context['extra_files'] = extra_files or None context['extra_files'] = extra_files or None
return context return context
def _extract_html(self, problemtree, question_id=0): # private def _extract_html(self, problemtree): # private
""" """
Main (private) function which converts Problem XML tree to HTML. Main (private) function which converts Problem XML tree to HTML.
Calls itself recursively. Calls itself recursively.
...@@ -822,19 +822,14 @@ class LoncapaProblem(object): ...@@ -822,19 +822,14 @@ class LoncapaProblem(object):
# otherwise, render children recursively, and copy over attributes # otherwise, render children recursively, and copy over attributes
tree = etree.Element(problemtree.tag) tree = etree.Element(problemtree.tag)
for item in problemtree: for item in problemtree:
item_xhtml = self._extract_html(item, question_id) item_xhtml = self._extract_html(item)
if item.tag == 'question':
item_xhtml.set('class', 'question')
item_xhtml.set('id', 'question-{}'.format(question_id))
question_id += 1
if item_xhtml is not None: if item_xhtml is not None:
tree.append(item_xhtml) tree.append(item_xhtml)
if tree.tag in html_transforms: if tree.tag in html_transforms:
tree.tag = html_transforms[problemtree.tag]['tag'] tree.tag = html_transforms[problemtree.tag]['tag']
else:
# copy attributes over if not innocufying # copy attributes over if not innocufying
for (key, value) in problemtree.items(): for (key, value) in problemtree.items():
tree.set(key, value) tree.set(key, value)
...@@ -901,3 +896,11 @@ class LoncapaProblem(object): ...@@ -901,3 +896,11 @@ class LoncapaProblem(object):
for solution in tree.findall('.//solution'): for solution in tree.findall('.//solution'):
solution.attrib['id'] = "%s_solution_%i" % (self.problem_id, solution_id) solution.attrib['id'] = "%s_solution_%i" % (self.problem_id, solution_id)
solution_id += 1 solution_id += 1
# Assign unique ids to <question>...</question>
question_index = 0
for question in tree.findall('.//question'):
question.attrib['class'] = "question"
question.attrib['id'] = "%s_question_%i" % (self.problem_id, question_index)
question.attrib['question_index'] = str(question_index)
question_index += 1
<%page expression_filter="h"/>
<%! from django.utils.translation import ugettext as _ %> <%! from django.utils.translation import ugettext as _ %>
<div class="action demandhint"> <div class="action demandhint">
<div class="problem-hint" aria-live="polite"></div> <div class="problem-hint" aria-live="polite"></div>
......
...@@ -15,19 +15,30 @@ class CapaHtmlRenderTest(unittest.TestCase): ...@@ -15,19 +15,30 @@ class CapaHtmlRenderTest(unittest.TestCase):
super(CapaHtmlRenderTest, self).setUp() super(CapaHtmlRenderTest, self).setUp()
self.capa_system = test_capa_system() self.capa_system = test_capa_system()
def test_blank_problem(self): def problem_question(self, xml_str, capa_system=None):
"""
It's important that blank problems don't break, since that's
what you start with in studio.
""" """
xml_str = "<problem> </problem>" Create CAPA problem from `xml_str`.
Return question element and rendered HTML.
"""
# Create the problem # Create the problem
problem = new_loncapa_problem(xml_str) problem = new_loncapa_problem(xml_str, capa_system=capa_system or self.capa_system)
# Render the HTML # Render the HTML
etree.XML(problem.get_html()) rendered_html = etree.XML(problem.get_html())
# TODO: This test should inspect the rendered html and assert one or more things about it
# get question
question = rendered_html.find(".//div[@class='question']")
return question, rendered_html
def test_blank_problem(self):
"""
It's important that blank problems don't break, since that's
what you start with in studio.
"""
xml_str = "<problem><question></question></problem>"
self.assertIsNotNone(self.problem_question(xml_str)[0])
def test_include_html(self): def test_include_html(self):
# Create a test file to include # Create a test file to include
...@@ -43,14 +54,7 @@ class CapaHtmlRenderTest(unittest.TestCase): ...@@ -43,14 +54,7 @@ class CapaHtmlRenderTest(unittest.TestCase):
</problem> </problem>
""") """)
# Create the problem question, _ = self.problem_question(xml_str)
problem = new_loncapa_problem(xml_str, capa_system=self.capa_system)
# Render the HTML
rendered_html = etree.XML(problem.get_html())
# get question
question = rendered_html.find(".//div[@class='question']")
# Expect that the include file was embedded in the problem # Expect that the include file was embedded in the problem
test_element = question.find("test") test_element = question.find("test")
...@@ -65,14 +69,7 @@ class CapaHtmlRenderTest(unittest.TestCase): ...@@ -65,14 +69,7 @@ class CapaHtmlRenderTest(unittest.TestCase):
</problem> </problem>
""") """)
# Create the problem question, _ = self.problem_question(xml_str)
problem = new_loncapa_problem(xml_str)
# Render the HTML
rendered_html = etree.XML(problem.get_html())
# get question
question = rendered_html.find(".//div[@class='question']")
# Expect that the <startouttext /> and <endouttext /> # Expect that the <startouttext /> and <endouttext />
# were converted to <span></span> tags # were converted to <span></span> tags
...@@ -87,14 +84,7 @@ class CapaHtmlRenderTest(unittest.TestCase): ...@@ -87,14 +84,7 @@ class CapaHtmlRenderTest(unittest.TestCase):
</problem> </problem>
""") """)
# Create the problem question, _ = self.problem_question(xml_str)
problem = new_loncapa_problem(xml_str)
# Render the HTML
rendered_html = etree.XML(problem.get_html())
# get question
question = rendered_html.find(".//div[@class='question']")
# Expect that the anonymous_student_id was converted to "student" # Expect that the anonymous_student_id was converted to "student"
span_element = question.find('span') span_element = question.find('span')
...@@ -108,14 +98,10 @@ class CapaHtmlRenderTest(unittest.TestCase): ...@@ -108,14 +98,10 @@ class CapaHtmlRenderTest(unittest.TestCase):
</problem> </problem>
""") """)
# Create the problem question, _ = self.problem_question(xml_str)
problem = new_loncapa_problem(xml_str)
# Render the HTML
rendered_html = etree.XML(problem.get_html())
# Expect that the script element has been removed from the rendered HTML # Expect that the script element has been removed from the rendered HTML
script_element = rendered_html.find('script') script_element = question.find('script')
self.assertEqual(None, script_element) self.assertEqual(None, script_element)
def test_render_javascript(self): def test_render_javascript(self):
...@@ -126,11 +112,7 @@ class CapaHtmlRenderTest(unittest.TestCase): ...@@ -126,11 +112,7 @@ class CapaHtmlRenderTest(unittest.TestCase):
</problem> </problem>
""") """)
# Create the problem _, rendered_html = self.problem_question(xml_str)
problem = new_loncapa_problem(xml_str)
# Render the HTML
rendered_html = etree.XML(problem.get_html())
# expect the javascript is still present in the rendered html # expect the javascript is still present in the rendered html
self.assertIn( self.assertIn(
...@@ -153,16 +135,11 @@ class CapaHtmlRenderTest(unittest.TestCase): ...@@ -153,16 +135,11 @@ class CapaHtmlRenderTest(unittest.TestCase):
the_system.render_template = mock.Mock() the_system.render_template = mock.Mock()
the_system.render_template.return_value = "<div>Input Template Render</div>" the_system.render_template.return_value = "<div>Input Template Render</div>"
# Create the problem and render the HTML question, rendered_html = self.problem_question(xml_str, capa_system=the_system)
problem = new_loncapa_problem(xml_str, capa_system=the_system)
rendered_html = etree.XML(problem.get_html())
# Expect problem has been turned into a <div> # Expect problem has been turned into a <div>
self.assertEqual(rendered_html.tag, "div") self.assertEqual(rendered_html.tag, "div")
# get question
question = rendered_html.find(".//div[@class='question']")
# Expect question text is in a <p> child # Expect question text is in a <p> child
question_element = question.find("p") question_element = question.find("p")
self.assertEqual(question_element.text, "Test question") self.assertEqual(question_element.text, "Test question")
...@@ -259,12 +236,7 @@ class CapaHtmlRenderTest(unittest.TestCase): ...@@ -259,12 +236,7 @@ class CapaHtmlRenderTest(unittest.TestCase):
</problem> </problem>
""") """)
# Create the problem and render the HTML question, _ = self.problem_question(xml_str)
problem = new_loncapa_problem(xml_str)
rendered_html = etree.XML(problem.get_html())
# get question
question = rendered_html.find(".//div[@class='question']")
# Expect that the variable $test has been replaced with its value # Expect that the variable $test has been replaced with its value
span_element = question.find('span') span_element = question.find('span')
...@@ -288,7 +260,10 @@ class CapaHtmlRenderTest(unittest.TestCase): ...@@ -288,7 +260,10 @@ class CapaHtmlRenderTest(unittest.TestCase):
# Render the HTML # Render the HTML
the_html = problem.get_html() the_html = problem.get_html()
self.assertRegexpMatches(the_html, r"<div class=\"question\" id=\"question-0\">\s+</div>") self.assertRegexpMatches(
the_html,
r"<div class=\"question\" id=\"1_question_0\" question_index=\"0\">\s+</div>"
)
def _create_test_file(self, path, content_str): def _create_test_file(self, path, content_str):
test_fp = self.capa_system.filestore.open(path, "w") test_fp = self.capa_system.filestore.open(path, "w")
......
...@@ -155,7 +155,11 @@ class CapaFields(object): ...@@ -155,7 +155,11 @@ class CapaFields(object):
{"display_name": _("Per Student"), "value": RANDOMIZATION.PER_STUDENT} {"display_name": _("Per Student"), "value": RANDOMIZATION.PER_STUDENT}
] ]
) )
data = String(help=_("XML data for the problem"), scope=Scope.content, default="<problem></problem>") data = String(
help=_("XML data for the problem"),
scope=Scope.content,
default="<problem><question></question></problem>"
)
correct_map = Dict(help=_("Dictionary with the correctness of current student answers"), correct_map = Dict(help=_("Dictionary with the correctness of current student answers"),
scope=Scope.user_state, default={}) scope=Scope.user_state, default={})
input_state = Dict(help=_("Dictionary for maintaining the state of inputtypes"), scope=Scope.user_state) input_state = Dict(help=_("Dictionary for maintaining the state of inputtypes"), scope=Scope.user_state)
...@@ -661,7 +665,7 @@ class CapaMixin(CapaFields): ...@@ -661,7 +665,7 @@ class CapaMixin(CapaFields):
check_button_checking = False check_button_checking = False
content = { content = {
'name': self.display_name_with_default_escaped, 'name': self.display_name,
'html': html, 'html': html,
'weight': self.weight, 'weight': self.weight,
} }
......
...@@ -57,7 +57,6 @@ h2 { ...@@ -57,7 +57,6 @@ h2 {
&.problem-header { &.problem-header {
display: inline-block; display: inline-block;
margin-bottom: 0;
section.staff { section.staff {
margin-top: ($baseline*1.5); margin-top: ($baseline*1.5);
font-size: 80%; font-size: 80%;
...@@ -154,6 +153,10 @@ div.problem { ...@@ -154,6 +153,10 @@ div.problem {
div.question:not(:last-child) { div.question:not(:last-child) {
margin-bottom: $baseline; margin-bottom: $baseline;
} }
h3.problem-header {
margin-bottom: 0 !important; // needed to overrided the %hd-2 margin-bottom
}
} }
// +Problem - Choice Group // +Problem - Choice Group
......
...@@ -65,7 +65,7 @@ var options = { ...@@ -65,7 +65,7 @@ var options = {
specFiles: [ specFiles: [
{pattern: 'spec/helper.js', included: true, ignoreCoverage: true}, // Helper which depends on source files. {pattern: 'spec/helper.js', included: true, ignoreCoverage: true}, // Helper which depends on source files.
{pattern: 'spec/problem/*.js', included: true} {pattern: 'spec/**/*.js', included: true}
], ],
fixtureFiles: [ fixtureFiles: [
......
...@@ -5,6 +5,7 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -5,6 +5,7 @@ describe 'MarkdownEditingDescriptor', ->
return { return {
compare: (actual, expected) -> compare: (actual, expected) ->
{ {
# compare the actual and expected XMLs by removing all the whitespace characters.
pass: actual.replace(/\s+/g, '') == expected.replace(/\s+/g, '') pass: actual.replace(/\s+/g, '') == expected.replace(/\s+/g, '')
} }
} }
...@@ -108,6 +109,9 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -108,6 +109,9 @@ describe 'MarkdownEditingDescriptor', ->
expect(revisedSelection).toEqual('[explanation]\nmy text\n[explanation]') expect(revisedSelection).toEqual('[explanation]\nmy text\n[explanation]')
describe 'markdownToXml', -> describe 'markdownToXml', ->
it 'converts blank common problem to correct XML', ->
data = MarkdownEditingDescriptor.markdownToXml('')
expect(data).toXMLEqual('<problem><question></question></problem>')
it 'converts raw text to paragraph', -> it 'converts raw text to paragraph', ->
data = MarkdownEditingDescriptor.markdownToXml('foo') data = MarkdownEditingDescriptor.markdownToXml('foo')
expect(data).toEqual('<problem>\n<question>\n<p>foo</p>\n</question>\n</problem>') expect(data).toEqual('<problem>\n<question>\n<p>foo</p>\n</question>\n</problem>')
......
...@@ -25,8 +25,6 @@ class @Problem ...@@ -25,8 +25,6 @@ class @Problem
window.update_schematics() window.update_schematics()
debugger
problem_prefix = @element_id.replace(/problem_/,'') problem_prefix = @element_id.replace(/problem_/,'')
@inputs = @$("[id^='input_#{problem_prefix}_']") @inputs = @$("[id^='input_#{problem_prefix}_']")
@$('div.action button').click @refreshAnswers @$('div.action button').click @refreshAnswers
...@@ -228,7 +226,6 @@ class @Problem ...@@ -228,7 +226,6 @@ class @Problem
### ###
check_fd: => check_fd: =>
# If there are no file inputs in the problem, we can fall back on @check # If there are no file inputs in the problem, we can fall back on @check
debugger
if @el.find('input:file').length == 0 if @el.find('input:file').length == 0
@check() @check()
return return
...@@ -809,7 +806,7 @@ class @Problem ...@@ -809,7 +806,7 @@ class @Problem
hint_button: (event)=> hint_button: (event)=>
# Store the index of the currently shown hint as an attribute. # Store the index of the currently shown hint as an attribute.
# Use that to compute the next hint number when the button is clicked. # Use that to compute the next hint number when the button is clicked.
debugger
question = $(event.target).closest('div.question') question = $(event.target).closest('div.question')
hint_container = question.find('.problem-hint') hint_container = question.find('.problem-hint')
hint_index = hint_container.attr('hint_index') hint_index = hint_container.attr('hint_index')
...@@ -819,7 +816,7 @@ class @Problem ...@@ -819,7 +816,7 @@ class @Problem
next_index = parseInt(hint_index) + 1 next_index = parseInt(hint_index) + 1
data = data =
question_id: question.attr('id').split('-')[1] question_id: question.attr('question_index')
hint_index: next_index hint_index: next_index
input_id: @id input_id: @id
......
...@@ -580,11 +580,16 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor ...@@ -580,11 +580,16 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor
}` }`
questionsXML = [] questionsXML = []
# TODO! Should we change it to markdown.split('\n---\n'). What is the right choice? questionsMarkdown = markdown.split('\n---\n')
questionsMarkdown = markdown.split('---')
_.each questionsMarkdown, (questionMarkdown, index) -> _.each questionsMarkdown, (questionMarkdown, index) ->
if questionMarkdown.trim().length > 0 if questionMarkdown.trim().length > 0
questionsXML.push toXml(questionMarkdown) questionsXML.push toXml(questionMarkdown)
# add <question></question> when markdown is an empty string
# this is needed to handle blank_common.yaml where OLX is not
# in correct format after conversion
if markdown.trim().length == 0
questionsXML.push('<question></question>')
# make all questions descendants of a single problem element # make all questions descendants of a single problem element
return '<problem>\n' + questionsXML.join('\n\n') + '\n</problem>' return '<problem>\n' + questionsXML.join('\n\n') + '\n</problem>'
...@@ -2,4 +2,8 @@ ...@@ -2,4 +2,8 @@
metadata: metadata:
display_name: Blank Common Problem display_name: Blank Common Problem
markdown: "" markdown: ""
data: "<problem><question></question></problem>" data: |
<problem>
<question>
</question>
</problem>
...@@ -76,6 +76,6 @@ data: | ...@@ -76,6 +76,6 @@ data: |
who is unable to see the image."/> who is unable to see the image."/>
</div> </div>
</solution> </solution>
<question> </question>
</problem> </problem>
\ No newline at end of file
...@@ -12,9 +12,12 @@ data: | ...@@ -12,9 +12,12 @@ data: |
<p> <p>
For more information, see For more information, see
<a href="http://edx.readthedocs.io/projects/edx-partner-course-staff/en/latest/exercises_tools/drag_and_drop_deprecated.html" target="_blank"> <a href="http://edx.readthedocs.io/projects/edx-partner-course-staff/en/latest/exercises_tools/drag_and_drop_deprecated.html" target="_blank">
Drag and Drop Problem (Deprecated)</a> in <i>Building and Running an edX Course</i>.</p> Drag and Drop Problem (Deprecated)</a> in <i>Building and Running an edX Course</i>.
<p>When you add the problem, be sure to select <strong>Settings</strong> </p>
to specify a <strong>Display Name</strong> and other values that apply.</p> <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 problems as models.</p> <p>You can use the following example problems as models.</p>
<hr /> <hr />
<customresponse> <customresponse>
...@@ -52,7 +55,6 @@ data: | ...@@ -52,7 +55,6 @@ data: |
correct = ['incorrect'] correct = ['incorrect']
</answer> </answer>
</customresponse> </customresponse>
</question> </question>
<question> <question>
......
...@@ -14,12 +14,16 @@ data: | ...@@ -14,12 +14,16 @@ data: |
<a href="http://edx.readthedocs.io/projects/edx-partner-course-staff/en/latest/exercises_tools/image_mapped_input.html" target="_blank">Image Mapped Input <a href="http://edx.readthedocs.io/projects/edx-partner-course-staff/en/latest/exercises_tools/image_mapped_input.html" target="_blank">Image Mapped Input
Problem</a> in <i>Building and Running an edx Course</i>. Problem</a> in <i>Building and Running an edx Course</i>.
</p> </p>
<p>When you add the problem, be sure to select <strong>Settings</strong> <p>
to specify a <strong>Display Name</strong> and other values that apply.</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> <p>You can use the following example problem as a model.</p>
<p>What country is home to the Great Pyramid of Giza as well as the cities <p>
of Cairo and Memphis? Click the country on the map below.</p> What country is home to the Great Pyramid of Giza as well as the cities
of Cairo and Memphis? Click the country on the map below.
</p>
<imageresponse> <imageresponse>
<imageinput src="https://studio.edx.org/c4x/edX/DemoX/asset/Africa.png" <imageinput src="https://studio.edx.org/c4x/edX/DemoX/asset/Africa.png"
width="600" height="638" rectangle="(338,98)-(412,168)" alt="Map of width="600" height="638" rectangle="(338,98)-(412,168)" alt="Map of
...@@ -28,8 +32,10 @@ data: | ...@@ -28,8 +32,10 @@ data: |
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
<p>Explanation</p> <p>Explanation</p>
<p>Egypt is home to not only the Pyramids, Cairo, and Memphis, but also <p>
the Sphinx and the ancient Royal Library of Alexandria.</p> Egypt is home to not only the Pyramids, Cairo, and Memphis, but also
the Sphinx and the ancient Royal Library of Alexandria.
</p>
</div> </div>
</solution> </solution>
</question> </question>
......
...@@ -90,12 +90,16 @@ data: | ...@@ -90,12 +90,16 @@ data: |
<?xml version="1.0"?> <?xml version="1.0"?>
<problem showanswer="closed" rerandomize="never" weight="10" display_name="lec1_Q2"> <problem showanswer="closed" rerandomize="never" weight="10" display_name="lec1_Q2">
<question> <question>
<p>If you have a problem that is already written in LaTeX, you can use this problem type to <p>
If you have a problem that is already written in LaTeX, you can use this problem type to
easily convert your code into XML. After you paste your code into the LaTeX editor, easily convert your code into XML. After you paste your code into the LaTeX editor,
you only need to make a few minor adjustments.</p> you only need to make a few minor adjustments.
<p>For more information, see </p>
<p>
For more information, see
<a href="http://edx.readthedocs.io/projects/edx-partner-course-staff/en/latest/exercises_tools/problem_in_latex.html" target="_blank"> <a href="http://edx.readthedocs.io/projects/edx-partner-course-staff/en/latest/exercises_tools/problem_in_latex.html" target="_blank">
Problem Written in LaTeX</a> in <i>Building and Running an edX Course</i>.</p> Problem Written in LaTeX</a> in <i>Building and Running an edX Course</i>.
</p>
<p>You can use the following example problems as models.</p> <p>You can use the following example problems as models.</p>
<p><strong>Example Option Problem</strong></p> <p><strong>Example Option Problem</strong></p>
<p>Which of the following countries celebrates its independence on August 15?</p> <p>Which of the following countries celebrates its independence on August 15?</p>
...@@ -112,6 +116,7 @@ data: | ...@@ -112,6 +116,7 @@ data: |
</question> </question>
<br/> <br/>
<question> <question>
<p><strong>Example Multiple Choice Problem</strong></p> <p><strong>Example Multiple Choice Problem</strong></p>
<p>Which of the following countries has the largest population?</p> <p>Which of the following countries has the largest population?</p>
...@@ -136,6 +141,7 @@ data: | ...@@ -136,6 +141,7 @@ data: |
</question> </question>
<br/> <br/>
<question> <question>
<p><strong>Example Math Expression Problem</strong></p> <p><strong>Example Math Expression Problem</strong></p>
<p>What is Einstein's equation for the energy equivalent of a mass [mathjaxinline]m[/mathjaxinline]?</p> <p>What is Einstein's equation for the energy equivalent of a mass [mathjaxinline]m[/mathjaxinline]?</p>
...@@ -143,7 +149,9 @@ data: | ...@@ -143,7 +149,9 @@ data: |
<textline size="90" correct_answer="m*c^2" math="1"/> <textline size="90" correct_answer="m*c^2" math="1"/>
</symbolicresponse> </symbolicresponse>
</question> </question>
<br/> <br/>
<question> <question>
<p><strong>Example Numerical Problem</strong></p> <p><strong>Example Numerical Problem</strong></p>
<p>Estimate the energy savings (in J/y) if all the people ([mathjaxinline]3\times 10^8[/mathjaxinline]) in the U.&#xA0;S. switched from U.&#xA0;S. code to low-flow shower heads.</p> <p>Estimate the energy savings (in J/y) if all the people ([mathjaxinline]3\times 10^8[/mathjaxinline]) in the U.&#xA0;S. switched from U.&#xA0;S. code to low-flow shower heads.</p>
...@@ -155,7 +163,9 @@ data: | ...@@ -155,7 +163,9 @@ data: |
<p style="display:inline">&#xA0;EJ/year</p> <p style="display:inline">&#xA0;EJ/year</p>
</numericalresponse> </numericalresponse>
</question> </question>
<br/> <br/>
<question> <question>
<p><strong>Example Fill-in-the-Blank Problem</strong></p> <p><strong>Example Fill-in-the-Blank Problem</strong></p>
<p>What was the first post-secondary school in China to allow both male and female students?</p> <p>What was the first post-secondary school in China to allow both male and female students?</p>
...@@ -171,7 +181,9 @@ data: | ...@@ -171,7 +181,9 @@ data: |
</div> </div>
</solution> </solution>
</question> </question>
<br/> <br/>
<question> <question>
<p><strong>Example Custom Python-Evaluated Input Problem</strong></p> <p><strong>Example Custom Python-Evaluated Input Problem</strong></p>
<script type="loncapa/python"> <script type="loncapa/python">
...@@ -191,6 +203,17 @@ data: | ...@@ -191,6 +203,17 @@ data: |
<textline size="40" correct_answer="3" label="Integer #1"/><br/> <textline size="40" correct_answer="3" label="Integer #1"/><br/>
<textline size="40" correct_answer="7" label="Integer #2"/> <textline size="40" correct_answer="7" label="Integer #2"/>
</customresponse> </customresponse>
<solution>
<div class="detailed-solution">
<p>Explanation</p>
<p>Any set of integers on the line \(y = 10 - x\) satisfy these constraints.</p>
</div>
</solution>
</question>
<br/>
<question>
<p>Enter two integers that sum to 20.</p> <p>Enter two integers that sum to 20.</p>
<customresponse cfn="test_add" expect="20"> <customresponse cfn="test_add" expect="20">
<textline size="40" correct_answer="11" label="Integer #1"/><br/> <textline size="40" correct_answer="11" label="Integer #1"/><br/>
...@@ -199,17 +222,21 @@ data: | ...@@ -199,17 +222,21 @@ data: |
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
<p>Explanation</p> <p>Explanation</p>
<p>Any set of integers on the line \(y = 10 - x\) and \(y = 20 - x\) satisfy these constraints.</p> <p>Any set of integers on the line \(y = 20 - x\) satisfy these constraints.</p>
<p>To add an image to the solution, use an HTML "img" tag. Make sure to include alt text.</p> <p>To add an image to the solution, use an HTML "img" tag. Make sure to include alt text.</p>
<img src="/static/images/placeholder-image.png" width="400" alt="Description of image"/> <img src="/static/images/placeholder-image.png" width="400" alt="Description of image"/>
</div> </div>
</solution> </solution>
</question> </question>
<br/> <br/>
<question> <question>
<p><strong>Example Image Mapped Input Problem</strong></p> <p><strong>Example Image Mapped Input Problem</strong></p>
<p>What country is home to the Great Pyramid of Giza as well as the cities <p>
of Cairo and Memphis? Click the country on the map below.</p> What country is home to the Great Pyramid of Giza as well as the cities
of Cairo and Memphis? Click the country on the map below.
</p>
<imageresponse> <imageresponse>
<imageinput src="https://studio.edx.org/c4x/edX/DemoX/asset/Africa.png" <imageinput src="https://studio.edx.org/c4x/edX/DemoX/asset/Africa.png"
width="600" height="638" rectangle="(338,98)-(412,168)" alt="Map of width="600" height="638" rectangle="(338,98)-(412,168)" alt="Map of
...@@ -223,7 +250,9 @@ data: | ...@@ -223,7 +250,9 @@ data: |
</div> </div>
</solution> </solution>
</question> </question>
<br/> <br/>
<question> <question>
<p><strong>Example Hidden Explanation</strong></p> <p><strong>Example Hidden Explanation</strong></p>
<p>You can provide additional information that only appears at certain times by including a "showhide" flag. </p> <p>You can provide additional information that only appears at certain times by including a "showhide" flag. </p>
......
...@@ -25,10 +25,14 @@ metadata: ...@@ -25,10 +25,14 @@ metadata:
data: | data: |
<problem> <problem>
<question> <question>
<p>Multiple choice problems allow learners to select only one option. <p>
Learners can see all the options along with the problem text.</p> Multiple choice problems allow learners to select only one option.
<p>When you add the problem, be sure to select <strong>Settings</strong> Learners can see all the options along with the problem text.
to specify a <strong>Display Name</strong> and other values that apply.</p> </p>
<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> <p>You can use the following example problem as a model.</p>
<p>Which of the following countries has the largest population?</p> <p>Which of the following countries has the largest population?</p>
<multiplechoiceresponse> <multiplechoiceresponse>
......
...@@ -26,24 +26,27 @@ metadata: ...@@ -26,24 +26,27 @@ metadata:
data: | data: |
<problem> <problem>
<question> <question>
<p>In a numerical input problem, learners enter numbers or a specific and <p>
In a numerical input problem, learners enter numbers or a specific and
relatively simple mathematical expression. Learners enter the response in relatively simple mathematical expression. Learners enter the response in
plain text, and the system then converts the text to a symbolic expression plain text, and the system then converts the text to a symbolic expression
that learners can see below the response field.</p> that learners can see below the response field.
</p>
<p>The system can handle several types of characters, including basic <p>
The system can handle several types of characters, including basic
operators, fractions, exponents, and common constants such as i. You can operators, fractions, exponents, and common constants such as i. You can
refer learners to refer learners to
<a href="http://edx.readthedocs.io/projects/edx-guide-for-students/en/latest/completing_assignments/SFD_mathformatting.html#math-formatting" target="_blank">Entering Mathematical and Scientific Expressions</a> in the <i>EdX Learner's Guide</i> for information about how to enter text into the field.</p> <a href="http://edx.readthedocs.io/projects/edx-guide-for-students/en/latest/completing_assignments/SFD_mathformatting.html#math-formatting" target="_blank">Entering Mathematical and Scientific Expressions</a> in the <i>EdX Learner's Guide</i> for information about how to enter text into the field.</p>
<p>When you add the problem, be sure to select <strong>Settings</strong> <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> to specify a <strong>Display Name</strong> and other values that apply.
</p>
<p>You can use the following example problems as models.</p> <p>You can use the following example problems as models.</p>
<p>How many miles away from Earth is the sun? Use scientific notation to answer.</p> <p>How many miles away from Earth is the sun? Use scientific notation to answer.</p>
<numericalresponse answer="9.3*10^7"> <numericalresponse answer="9.3*10^7">
<formulaequationinput label="How many million miles are between Earth and the sun? Use scientific notation to answer." /> <formulaequationinput label="How many million miles are between Earth and the sun? Use scientific notation to answer." />
</numericalresponse> </numericalresponse>
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
<p>Explanation</p> <p>Explanation</p>
......
...@@ -51,5 +51,5 @@ data: | ...@@ -51,5 +51,5 @@ data: |
<hint>The mean is calculated by summing the set of numbers and dividing by n.</hint> <hint>The mean is calculated by summing the set of numbers and dividing by n.</hint>
<hint>n is the count of items in the set.</hint> <hint>n is the count of items in the set.</hint>
</demandhint> </demandhint>
<question> </question>
</problem> </problem>
\ No newline at end of file
...@@ -19,8 +19,10 @@ data: | ...@@ -19,8 +19,10 @@ data: |
<problem> <problem>
<question> <question>
<p>Dropdown problems allow learners to select only one option from a list of options.</p> <p>Dropdown problems allow learners to select only one option from a list of options.</p>
<p>When you add the problem, be sure to select <strong>Settings</strong> <p>
to specify a <strong>Display Name</strong> and other values that apply.</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> <p>You can use the following example problem as a model.</p>
<p>Which of the following countries celebrates its independence on August 15?</p> <p>Which of the following countries celebrates its independence on August 15?</p>
<br/> <br/>
......
...@@ -55,7 +55,8 @@ data: | ...@@ -55,7 +55,8 @@ data: |
<h4>Problem With Adaptive Hint</h4> <h4>Problem With Adaptive Hint</h4>
</p> </p>
<p> <p>
This problem demonstrates a question with hints, based on using the <tt class="tt">hintfn</tt> method. </p> This problem demonstrates a question with hints, based on using the <tt class="tt">hintfn</tt> method.
</p>
<script type="text/python" system_path="python_lib"> <script type="text/python" system_path="python_lib">
def test_str(expect, ans): def test_str(expect, ans):
print expect, ans print expect, ans
......
...@@ -21,11 +21,13 @@ metadata: ...@@ -21,11 +21,13 @@ metadata:
data: | data: |
<problem> <problem>
<question> <question>
<p>In text input problems, also known as "fill-in-the-blank" problems, <p>
In text input problems, also known as "fill-in-the-blank" problems,
learners enter text into a response field. The text that the learner enters learners enter text into a response field. The text that the learner enters
must match your specified answer text exactly. You can specify more than must match your specified answer text exactly. You can specify more than
one correct answer. Learners must enter a response that matches one of the one correct answer. Learners must enter a response that matches one of the
correct answers exactly.</p> correct answers exactly.
</p>
<p>When you add the problem, be sure to select <strong>Settings</strong> <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> 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> <p> You can use the following example problem as a model.</p>
......
...@@ -12,7 +12,7 @@ class ProblemPage(PageObject): ...@@ -12,7 +12,7 @@ class ProblemPage(PageObject):
url = None url = None
CSS_PROBLEM_HEADER = '.problem-header' CSS_PROBLEM_HEADER = '.problem-header'
# There can be multiple questions in a problem, so we need to make query selector specific to question # Used to make question specific css selector
question_id = 0 question_id = 0
def is_browser_on_page(self): def is_browser_on_page(self):
...@@ -28,7 +28,7 @@ class ProblemPage(PageObject): ...@@ -28,7 +28,7 @@ class ProblemPage(PageObject):
Returns: Returns:
str: Element selector specific to a question. str: Element selector specific to a question.
""" """
return 'div.problem #question-{}.question {}'.format(self.question_id, selector) return 'div.problem [question_index="{}"] {}'.format(self.question_id, selector)
@property @property
def problem_name(self): def problem_name(self):
......
...@@ -38,9 +38,12 @@ class ProblemsTest(UniqueCourseTest): ...@@ -38,9 +38,12 @@ class ProblemsTest(UniqueCourseTest):
) )
problem = self.get_problem() problem = self.get_problem()
problems = (problem,) * self.get_num_problems()
course_fixture.add_children( course_fixture.add_children(
XBlockFixtureDesc('chapter', 'Test Section').add_children( XBlockFixtureDesc('chapter', 'Test Section').add_children(
XBlockFixtureDesc('sequential', 'Test Subsection').add_children(problem) XBlockFixtureDesc('sequential', 'Test Subsection').add_children(
XBlockFixtureDesc('vertical', 'Test Unit').add_children(*problems)
)
) )
).install() ).install()
...@@ -58,6 +61,12 @@ class ProblemsTest(UniqueCourseTest): ...@@ -58,6 +61,12 @@ class ProblemsTest(UniqueCourseTest):
""" Subclasses should override this to complete the fixture """ """ Subclasses should override this to complete the fixture """
raise NotImplementedError() raise NotImplementedError()
def get_num_problems(self):
"""
Tells how many problems to create. Defaults to 1.
"""
return 1
class ProblemClarificationTest(ProblemsTest): class ProblemClarificationTest(ProblemsTest):
""" """
...@@ -470,3 +479,72 @@ class LogoutDuringAnswering(ProblemsTest): ...@@ -470,3 +479,72 @@ class LogoutDuringAnswering(ProblemsTest):
self.assertTrue(problem_page.is_browser_on_page()) self.assertTrue(problem_page.is_browser_on_page())
self.assertEqual(problem_page.problem_name, 'TEST PROBLEM') self.assertEqual(problem_page.problem_name, 'TEST PROBLEM')
# @attr('a11y')
class CAPAProblemQuestionA11yTest(ProblemsTest):
"""
TestCase Class for CAPA problem questions
"""
def get_problem(self):
"""
Problem structure.
"""
xml = dedent("""
<problem>
<question>
<p>question text</p>
<stringresponse answer="A">
<stringequalhint answer="C"><a href="#">aa bb</a> cc</stringequalhint>
<textline size="20"/>
</stringresponse>
<demandhint>
<hint>question 1 hint 1</hint>
<hint>question 1 hint 2</hint>
</demandhint>
</question>
<question>
<p>That is the question</p>
<multiplechoiceresponse>
<choicegroup type="MultipleChoice">
<choice correct="false">Alpha <choicehint>A hint</choicehint></choice>
<choice correct="true">Beta</choice>
</choicegroup>
</multiplechoiceresponse>
<demandhint>
<hint>question 2 hint 1</hint>
<hint>question 2 hint 2</hint>
</demandhint>
</question>
</problem>
""")
return XBlockFixtureDesc('problem', 'Problem Question TEST', data=xml)
def get_num_problems(self):
"""
Create two problems.
"""
return 2
def test_questions_have_unique_ids(self):
"""
Scenario: Verifies that each question has a unique id.
Given I am enrolled in a course.
And I visit a unit page with two CAPA problems where each problem has 2 questions
Then I check if questions have unique IDs
"""
self.courseware_page.visit()
problem_page = ProblemPage(self.browser)
# Set the scope to the problem question
problem_page.a11y_audit.config.set_scope(
include=['div.question']
)
# Check only for duplicate ids
problem_page.a11y_audit.config.set_rules({
"apply": ['duplicate-id'],
})
# Run the accessibility audit.
problem_page.a11y_audit.check_for_accessibility_errors()
<%! from django.utils.translation import ugettext as _ %> <%page expression_filter="h"/>
<%!
from django.utils.translation import ugettext as _
from openedx.core.djangolib.markup import HTML
%>
<%namespace name='static' file='static_content.html'/> <%namespace name='static' file='static_content.html'/>
<h3 class="hd hd-2 problem-header"> <h3 class="hd hd-2 problem-header">
...@@ -8,7 +12,7 @@ ...@@ -8,7 +12,7 @@
<div class="problem-progress"></div> <div class="problem-progress"></div>
<div class="problem"> <div class="problem">
${ problem['html'] } ${ HTML(problem['html']) }
<div class="action"> <div class="action">
<input type="hidden" name="problem_id" value="${ problem['name'] }" /> <input type="hidden" name="problem_id" value="${ problem['name'] }" />
% if check_button: % if check_button:
......
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