Allows ChoiceResponse CAPA problems to use script-computed variables to
determine choice correctness, as MultipleChoiceResponse problems already do. Adds a unit test for this change, and for the existing MultipleChoiceResponse computed correctness capability.
This commit is contained in:
@@ -851,31 +851,30 @@ class ChoiceResponse(LoncapaResponse):
|
||||
def setup_response(self):
|
||||
self.assign_choice_names()
|
||||
|
||||
correct_xml = self.xml.xpath(
|
||||
'//*[@id=$id]//choice[@correct="true"]',
|
||||
id=self.xml.get('id')
|
||||
)
|
||||
self.correct_choices = set()
|
||||
self.incorrect_choices = set()
|
||||
for choice in self.get_choices():
|
||||
|
||||
self.correct_choices = set([
|
||||
choice.get('name') for choice in correct_xml
|
||||
])
|
||||
# contextualize the name and correct attributes
|
||||
name = contextualize_text(choice.get('name'), self.context)
|
||||
correct = contextualize_text(choice.get('correct'), self.context).upper()
|
||||
|
||||
incorrect_xml = self.xml.xpath(
|
||||
'//*[@id=$id]//choice[@correct="false"]',
|
||||
id=self.xml.get('id')
|
||||
)
|
||||
# divide choices into correct and incorrect
|
||||
if correct == 'TRUE':
|
||||
self.correct_choices.add(name)
|
||||
elif correct == 'FALSE':
|
||||
self.incorrect_choices.add(name)
|
||||
|
||||
self.incorrect_choices = set([
|
||||
choice.get('name') for choice in incorrect_xml
|
||||
])
|
||||
def get_choices(self):
|
||||
"""Returns this response's XML choice elements."""
|
||||
return self.xml.xpath('//*[@id=$id]//choice', id=self.xml.get('id'))
|
||||
|
||||
def assign_choice_names(self):
|
||||
"""
|
||||
Initialize name attributes in <choice> tags for this response.
|
||||
"""
|
||||
|
||||
for index, choice in enumerate(self.xml.xpath('//*[@id=$id]//choice',
|
||||
id=self.xml.get('id'))):
|
||||
for index, choice in enumerate(self.get_choices()):
|
||||
choice.set("name", "choice_" + str(index))
|
||||
# If a choice does not have an id, assign 'A' 'B', .. used by CompoundHint
|
||||
if not choice.get('id'):
|
||||
|
||||
@@ -172,6 +172,8 @@ class ResponseXMLFactory(object):
|
||||
correctness = 'false'
|
||||
elif 'partial' in correct_val:
|
||||
correctness = 'partial'
|
||||
else:
|
||||
correctness = correct_val
|
||||
|
||||
choice_element.set('correct', correctness)
|
||||
|
||||
|
||||
@@ -169,6 +169,35 @@ class MultiChoiceResponseTest(ResponseTest): # pylint: disable=missing-docstrin
|
||||
correct_map = problem.grade_answers({'1_2_1': 'choice_2'})
|
||||
self.assertAlmostEqual(correct_map.get_npoints('1_2_1'), 0)
|
||||
|
||||
def test_contextualized_choices(self):
|
||||
script = textwrap.dedent("""
|
||||
a = 2
|
||||
b = 9
|
||||
c = a + b
|
||||
|
||||
ok0 = c % 2 == 0 # check remainder modulo 2
|
||||
text0 = "$a + $b is even"
|
||||
|
||||
ok1 = c % 2 == 1 # check remainder modulo 2
|
||||
text1 = "$a + $b is odd"
|
||||
|
||||
ok2 = "partial"
|
||||
text2 = "infinity may be both"
|
||||
""")
|
||||
choices = ["$ok0", "$ok1", "$ok2"]
|
||||
choice_names = ["$text0 ... (should be $ok0)",
|
||||
"$text1 ... (should be $ok1)",
|
||||
"$text2 ... (should be $ok2)"]
|
||||
problem = self.build_problem(script=script,
|
||||
choices=choices,
|
||||
choice_names=choice_names,
|
||||
credit_type='points')
|
||||
|
||||
# Ensure the expected correctness and choice names
|
||||
self.assert_grade(problem, 'choice_2 + 9 is even ... (should be False)', 'incorrect')
|
||||
self.assert_grade(problem, 'choice_2 + 9 is odd ... (should be True)', 'correct')
|
||||
self.assert_grade(problem, 'choice_infinity may be both ... (should be partial)', 'partially-correct')
|
||||
|
||||
|
||||
class TrueFalseResponseTest(ResponseTest): # pylint: disable=missing-docstring
|
||||
xml_factory_class = TrueFalseResponseXMLFactory
|
||||
@@ -1292,6 +1321,26 @@ class ChoiceResponseTest(ResponseTest): # pylint: disable=missing-docstring
|
||||
correct_map = problem.grade_answers({})
|
||||
self.assertEqual(correct_map.get_correctness('1_2_1'), 'incorrect')
|
||||
|
||||
def test_contextualized_choices(self):
|
||||
script = textwrap.dedent("""
|
||||
a = 6
|
||||
b = 4
|
||||
c = a + b
|
||||
|
||||
ok0 = c % 2 == 0 # check remainder modulo 2
|
||||
ok1 = c % 3 == 0 # check remainder modulo 3
|
||||
ok2 = c % 5 == 0 # check remainder modulo 5
|
||||
ok3 = not any([ok0, ok1, ok2])
|
||||
""")
|
||||
choices = ["$ok0", "$ok1", "$ok2", "$ok3"]
|
||||
problem = self.build_problem(script=script,
|
||||
choice_type='checkbox',
|
||||
choices=choices)
|
||||
|
||||
# Ensure the expected correctness
|
||||
self.assert_grade(problem, ['choice_0', 'choice_2'], 'correct')
|
||||
self.assert_grade(problem, ['choice_1', 'choice_3'], 'incorrect')
|
||||
|
||||
|
||||
class JavascriptResponseTest(ResponseTest): # pylint: disable=missing-docstring
|
||||
xml_factory_class = JavascriptResponseXMLFactory
|
||||
|
||||
Reference in New Issue
Block a user