Merge pull request #22954 from edx/dsheraz/PROD-347
add multiple correct values check for Dropdown problems
This commit is contained in:
@@ -245,6 +245,22 @@ class LoncapaProblem(object):
|
||||
This translation takes in the new format and synthesizes the old option= attribute
|
||||
so all downstream logic works unchanged with the new <option> tag format.
|
||||
"""
|
||||
def is_optioninput_valid(optioninput):
|
||||
"""
|
||||
Verifies if a given optioninput xml is valid or not.
|
||||
|
||||
A given optioninput(Dropdown) problem is invalid if it has more than one correct answer.
|
||||
|
||||
Argument:
|
||||
optioninput: dropdown specification tree
|
||||
Returns:
|
||||
boolean: signifying if the optioninput is valid or not.
|
||||
"""
|
||||
correct_options = [
|
||||
option.get('correct').upper() == 'TRUE' for option in optioninput.findall('./option')
|
||||
]
|
||||
return correct_options.count(True) in (0, 1)
|
||||
|
||||
additionals = tree.xpath('//stringresponse/additional_answer')
|
||||
for additional in additionals:
|
||||
answer = additional.get('answer')
|
||||
@@ -252,8 +268,9 @@ class LoncapaProblem(object):
|
||||
if not answer and text: # trigger of old->new conversion
|
||||
additional.set('answer', text)
|
||||
additional.text = ''
|
||||
|
||||
for optioninput in tree.xpath('//optioninput'):
|
||||
if not is_optioninput_valid(optioninput):
|
||||
raise responsetypes.LoncapaProblemError("Dropdown questions can only have one correct answer.")
|
||||
correct_option = None
|
||||
child_options = []
|
||||
for option_element in optioninput.findall('./option'):
|
||||
|
||||
@@ -12,6 +12,7 @@ from lxml import etree
|
||||
from markupsafe import Markup
|
||||
from mock import patch
|
||||
|
||||
from capa.responsetypes import LoncapaProblemError
|
||||
from capa.tests.helpers import new_loncapa_problem
|
||||
from openedx.core.djangolib.markup import HTML
|
||||
|
||||
@@ -460,6 +461,37 @@ class CAPAProblemTest(unittest.TestCase):
|
||||
self.assert_question_tag(question1, question2, tag='label', label_attr=False)
|
||||
self.assert_question_tag(question1, question2, tag='p', label_attr=True)
|
||||
|
||||
def test_optionresponse_xml_compatibility(self):
|
||||
"""
|
||||
Verify that an optionresponse problem with multiple correct answers is not instantiated.
|
||||
|
||||
Scenario:
|
||||
Given an optionresponse/Dropdown problem
|
||||
If there are multiple correct answers
|
||||
Then the problem is not instantiated
|
||||
And Loncapa problem error exception is raised
|
||||
If the problem is corrected by including only one correct answer
|
||||
Then the problem is created successfully
|
||||
"""
|
||||
xml = """
|
||||
<problem>
|
||||
<optionresponse>
|
||||
<p>You can use this template as a guide to the simple editor markdown and OLX markup to use for dropdown problems. Edit this component to replace this template with your own assessment.</p>
|
||||
<label>Add the question text, or prompt, here. This text is required.</label>
|
||||
<description>You can add an optional tip or note related to the prompt like this. </description>
|
||||
<optioninput>
|
||||
<option correct="False">an incorrect answer</option>
|
||||
<option correct="True">the correct answer</option>
|
||||
<option correct="{correctness}">an incorrect answer</option>
|
||||
</optioninput>
|
||||
</optionresponse>
|
||||
</problem>
|
||||
"""
|
||||
with self.assertRaises(LoncapaProblemError):
|
||||
new_loncapa_problem(xml.format(correctness=True))
|
||||
problem = new_loncapa_problem(xml.format(correctness=False))
|
||||
self.assertIsNotNone(problem)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class CAPAMultiInputProblemTest(unittest.TestCase):
|
||||
|
||||
@@ -344,7 +344,7 @@ class ProblemBlock(
|
||||
|
||||
def max_score(self):
|
||||
"""
|
||||
Return the problem's max score
|
||||
Return the problem's max score if problem is instantiated successfully, else return max score of 0.
|
||||
"""
|
||||
from capa.capa_problem import LoncapaProblem, LoncapaSystem
|
||||
capa_system = LoncapaSystem(
|
||||
@@ -363,16 +363,22 @@ class ProblemBlock(
|
||||
xqueue=None,
|
||||
matlab_api_key=None,
|
||||
)
|
||||
lcp = LoncapaProblem(
|
||||
problem_text=self.data,
|
||||
id=self.location.html_id(),
|
||||
capa_system=capa_system,
|
||||
capa_module=self,
|
||||
state={},
|
||||
seed=1,
|
||||
minimal_init=True,
|
||||
)
|
||||
return lcp.get_max_score()
|
||||
try:
|
||||
lcp = LoncapaProblem(
|
||||
problem_text=self.data,
|
||||
id=self.location.html_id(),
|
||||
capa_system=capa_system,
|
||||
capa_module=self,
|
||||
state={},
|
||||
seed=1,
|
||||
minimal_init=True,
|
||||
)
|
||||
except responsetypes.LoncapaProblemError:
|
||||
log.exception(u"LcpFatalError for block {} while getting max score".format(str(self.location)))
|
||||
maximum_score = 0
|
||||
else:
|
||||
maximum_score = lcp.get_max_score()
|
||||
return maximum_score
|
||||
|
||||
def generate_report_data(self, user_state_iterator, limit_responses=None):
|
||||
"""
|
||||
|
||||
@@ -2871,6 +2871,27 @@ class ProblemBlockXMLTest(unittest.TestCase):
|
||||
with self.assertRaises(etree.XMLSyntaxError):
|
||||
self._create_descriptor(sample_invalid_xml, name="Invalid XML")
|
||||
|
||||
def test_invalid_dropdown_xml(self):
|
||||
"""
|
||||
Verify the capa problem cannot be created from dropdown xml with multiple correct answers.
|
||||
"""
|
||||
problem_xml = textwrap.dedent("""
|
||||
<problem>
|
||||
<optionresponse>
|
||||
<p>You can use this template as a guide to the simple editor markdown and OLX markup to use for dropdown problems. Edit this component to replace this template with your own assessment.</p>
|
||||
<label>Add the question text, or prompt, here. This text is required.</label>
|
||||
<description>You can add an optional tip or note related to the prompt like this. </description>
|
||||
<optioninput>
|
||||
<option correct="False">an incorrect answer</option>
|
||||
<option correct="True">the correct answer</option>
|
||||
<option correct="True">an incorrect answer</option>
|
||||
</optioninput>
|
||||
</optionresponse>
|
||||
</problem>
|
||||
""")
|
||||
with self.assertRaises(Exception):
|
||||
CapaFactory.create(xml=problem_xml)
|
||||
|
||||
|
||||
class ComplexEncoderTest(unittest.TestCase):
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ Test for lms courseware app, module render unit
|
||||
|
||||
import itertools
|
||||
import json
|
||||
import textwrap
|
||||
from datetime import datetime
|
||||
from functools import partial
|
||||
|
||||
@@ -1880,6 +1881,47 @@ class TestStaffDebugInfo(SharedModuleStoreTestCase):
|
||||
result_fragment = module.render(STUDENT_VIEW)
|
||||
self.assertIn('Staff Debug', result_fragment.content)
|
||||
|
||||
def test_staff_debug_info_score_for_invalid_dropdown(self):
|
||||
"""
|
||||
Verifies that for an invalid drop down problem, the max score is set
|
||||
to zero in the html.
|
||||
"""
|
||||
problem_xml = """
|
||||
<problem>
|
||||
<optionresponse>
|
||||
<p>You can use this template as a guide to the simple editor markdown and OLX markup to use for dropdown problems. Edit this component to replace this template with your own assessment.</p>
|
||||
<label>Add the question text, or prompt, here. This text is required.</label>
|
||||
<description>You can add an optional tip or note related to the prompt like this. </description>
|
||||
<optioninput>
|
||||
<option correct="False">an incorrect answer</option>
|
||||
<option correct="True">the correct answer</option>
|
||||
<option correct="True">an incorrect answer</option>
|
||||
</optioninput>
|
||||
</optionresponse>
|
||||
</problem>
|
||||
"""
|
||||
problem_descriptor = ItemFactory.create(
|
||||
category='problem',
|
||||
data=problem_xml
|
||||
)
|
||||
module = render.get_module(
|
||||
self.user,
|
||||
self.request,
|
||||
problem_descriptor.location,
|
||||
self.field_data_cache
|
||||
)
|
||||
html_fragment = module.render(STUDENT_VIEW)
|
||||
expected_score_override_html = textwrap.dedent("""<div>
|
||||
<label for="sd_fs_{block_id}">Score (for override only):</label>
|
||||
<input type="text" tabindex="0" id="sd_fs_{block_id}" placeholder="0"/>
|
||||
<label for="sd_fs_{block_id}"> / 0</label>
|
||||
</div>""")
|
||||
|
||||
self.assertIn(
|
||||
expected_score_override_html.format(block_id=problem_descriptor.location.block_id),
|
||||
html_fragment.content
|
||||
)
|
||||
|
||||
@XBlock.register_temp_plugin(DetachedXBlock, identifier='detached-block')
|
||||
def test_staff_debug_info_disabled_for_detached_blocks(self):
|
||||
"""Staff markup should not be present on detached blocks."""
|
||||
|
||||
@@ -347,6 +347,35 @@ class GradesTransformerTestCase(CourseStructureTestCase):
|
||||
max_score=2,
|
||||
)
|
||||
|
||||
def test_max_score_for_invalid_dropdown_problem(self):
|
||||
"""
|
||||
Verify that for an invalid dropdown problem, the max score is set to zero.
|
||||
"""
|
||||
problem_data = u'''
|
||||
<problem>
|
||||
<optionresponse>
|
||||
<p>You can use this template as a guide to the simple editor markdown and OLX markup to use for dropdown problems. Edit this component to replace this template with your own assessment.</p>
|
||||
<label>Add the question text, or prompt, here. This text is required.</label>
|
||||
<description>You can add an optional tip or note related to the prompt like this. </description>
|
||||
<optioninput>
|
||||
<option correct="False">an incorrect answer</option>
|
||||
<option correct="True">the correct answer</option>
|
||||
<option correct="True">an incorrect answer</option>
|
||||
</optioninput>
|
||||
</optionresponse>
|
||||
</problem>
|
||||
'''
|
||||
|
||||
blocks = self.build_course_with_problems(problem_data)
|
||||
block_structure = get_course_blocks(self.student, blocks['course'].location, self.transformers)
|
||||
|
||||
self.assert_collected_transformer_block_fields(
|
||||
block_structure,
|
||||
blocks['problem'].location,
|
||||
self.TRANSFORMER_CLASS_TO_TEST,
|
||||
max_score=0,
|
||||
)
|
||||
|
||||
def test_course_version_not_collected_in_old_mongo(self):
|
||||
blocks = self.build_course_with_problems()
|
||||
block_structure = get_course_blocks(self.student, blocks[u'course'].location, self.transformers)
|
||||
|
||||
Reference in New Issue
Block a user