Commit cbe97d6f by Alexander Kryklia

Merge pull request #1948 from edx/anton/allow-arbitrary-regex-in-stringresponse

Anton/allow arbitrary regex in stringresponse
parents de6eb99b ee1953b3
......@@ -5,6 +5,8 @@ These are notable changes in edx-platform. This is a rolling list of changes,
in roughly chronological order, most recent first. Add your entries at or near
the top. Include a label indicating the component affected.
Blades: Allow regexp strings as the correct answer to a string response question. BLD-475.
Common: Add feature flags to allow developer use of pure XBlocks
- ALLOW_ALL_ADVANCED_COMPONENTS disables the hard-coded list of advanced
components in Studio, and allows any xblock to be added as an
......
......@@ -329,8 +329,8 @@ class LoncapaResponse(object):
rephints = hintgroup.findall(self.hint_tag)
hints_to_show = self.check_hint_condition(
rephints, student_answers)
# can be 'on_request' or 'always' (default)
hintmode = hintgroup.get('mode', 'always')
for hintpart in hintgroup.findall('hintpart'):
if hintpart.get('on') in hints_to_show:
......@@ -947,21 +947,35 @@ class NumericalResponse(LoncapaResponse):
class StringResponse(LoncapaResponse):
'''
This response type allows one or more answers. Use `_or_` separator to set
more than 1 answer.
This response type allows one or more answers.
Example:
Additional answers are added by `additional_answer` tag.
If `regexp` is in `type` attribute, than answers and hints are treated as regular expressions.
# One answer
Examples:
<stringresponse answer="Michigan">
<textline size="20" />
</stringresponse >
# Multiple answers
<stringresponse answer="Martin Luther King_or_Dr. Martin Luther King Jr.">
<textline size="20" />
<textline size="20" />
</stringresponse >
<stringresponse answer="a1" type="ci regexp">
<additional_answer>\d5</additional_answer>
<additional_answer>a3</additional_answer>
<textline size="20"/>
<hintgroup>
<stringhint answer="a0" type="ci" name="ha0" />
<stringhint answer="a4" type="ci" name="ha4" />
<stringhint answer="^\d" type="ci" name="re1" />
<hintpart on="ha0">
<startouttext />+1<endouttext />
</hintpart >
<hintpart on="ha4">
<startouttext />-1<endouttext />
</hintpart >
<hintpart on="re1">
<startouttext />Any number+5<endouttext />
</hintpart >
</hintgroup>
</stringresponse>
'''
response_tag = 'stringresponse'
hint_tag = 'stringhint'
......@@ -969,11 +983,30 @@ class StringResponse(LoncapaResponse):
required_attributes = ['answer']
max_inputfields = 1
correct_answer = []
SEPARATOR = '_or_'
def setup_response_backward(self):
self.correct_answer = [
contextualize_text(answer, self.context).strip() for answer in self.xml.get('answer').split('_or_')
]
def setup_response(self):
self.correct_answer = [contextualize_text(answer, self.context).strip()
for answer in self.xml.get('answer').split(self.SEPARATOR)]
self.backward = '_or_' in self.xml.get('answer').lower()
self.regexp = 'regexp' in self.xml.get('type').lower().split(' ')
self.case_insensitive = 'ci' in self.xml.get('type').lower().split(' ')
# backward compatibility, can be removed in future, it is up to @Lyla Fisher.
if self.backward:
self.setup_response_backward()
return
# end of backward compatibility
correct_answers = [self.xml.get('answer')] + [el.text for el in self.xml.findall('additional_answer')]
self.correct_answer = [contextualize_text(answer, self.context).strip() for answer in correct_answers]
# remove additional_answer from xml, otherwise they will be displayed
for el in self.xml.findall('additional_answer'):
self.xml.remove(el)
def get_score(self, student_answers):
'''Grade a string response '''
......@@ -981,21 +1014,61 @@ class StringResponse(LoncapaResponse):
correct = self.check_string(self.correct_answer, student_answer)
return CorrectMap(self.answer_id, 'correct' if correct else 'incorrect')
def check_string(self, expected, given):
if self.xml.get('type') == 'ci':
def check_string_backward(self, expected, given):
if self.case_insensitive:
return given.lower() in [i.lower() for i in expected]
return given in expected
def check_string(self, expected, given):
"""
Find given in expected.
If self.regexp is true, regular expression search is used.
if self.case_insensitive is true, case insensitive search is used, otherwise case sensitive search is used.
Spaces around values of attributes are stripped in XML parsing step.
Args:
expected: list.
given: str.
Returns: bool
Raises: `ResponseError` if it fails to compile regular expression.
Note: for old code, which supports _or_ separator, we add some backward compatibility handling.
Should be removed soon. When to remove it, is up to Lyla Fisher.
"""
# backward compatibility, should be removed in future.
if self.backward:
return self.check_string_backward(expected, given)
# end of backward compatibility
if self.regexp: # regexp match
flags = re.IGNORECASE if self.case_insensitive else 0
try:
regexp = re.compile('^'+ '|'.join(expected) + '$', flags=flags | re.UNICODE)
result = re.search(regexp, given)
except Exception as err:
msg = '[courseware.capa.responsetypes.stringresponse] error: {}'.format(err.message)
log.error(msg, exc_info=True)
raise ResponseError(msg)
return bool(result)
else: # string match
if self.case_insensitive:
return given.lower() in [i.lower() for i in expected]
else:
return given in expected
def check_hint_condition(self, hxml_set, student_answers):
given = student_answers[self.answer_id].strip()
hints_to_show = []
for hxml in hxml_set:
name = hxml.get('name')
correct_answer = [contextualize_text(answer, self.context).strip()
for answer in hxml.get('answer').split(self.SEPARATOR)]
hinted_answer = contextualize_text(hxml.get('answer'), self.context).strip()
if self.check_string(correct_answer, given):
if self.check_string([hinted_answer], given):
hints_to_show.append(name)
log.debug('hints_to_show = %s', hints_to_show)
return hints_to_show
......
......@@ -690,22 +690,30 @@ class StringResponseXMLFactory(ResponseXMLFactory):
*hintfn*: The name of a function in the script to use for hints.
*regexp*: Whether the response is regexp
*additional_answers*: list of additional asnwers.
"""
# Retrieve the **kwargs
answer = kwargs.get("answer", None)
case_sensitive = kwargs.get("case_sensitive", True)
hint_list = kwargs.get('hints', None)
hint_fn = kwargs.get('hintfn', None)
regexp = kwargs.get('regexp', None)
additional_answers = kwargs.get('additional_answers', [])
assert answer
# Create the <stringresponse> element
response_element = etree.Element("stringresponse")
# Set the answer attribute
response_element.set("answer", str(answer))
response_element.set("answer", unicode(answer))
# Set the case sensitivity
response_element.set("type", "cs" if case_sensitive else "ci")
# Set the case sensitivity and regexp:
type_value = "cs" if case_sensitive else "ci"
type_value += ' regexp' if regexp else ''
response_element.set("type", type_value)
# Add the hints if specified
if hint_list or hint_fn:
......@@ -727,6 +735,9 @@ class StringResponseXMLFactory(ResponseXMLFactory):
assert not hint_list
hintgroup_element.set("hintfn", hint_fn)
for additional_answer in additional_answers:
etree.SubElement(response_element, "additional_answer").text = additional_answer
return response_element
def create_input_element(self, **kwargs):
......
# -*- coding: utf-8 -*-
"""
Tests of responsetypes
"""
......@@ -502,6 +503,135 @@ class StringResponseTest(ResponseTest):
from capa.tests.response_xml_factory import StringResponseXMLFactory
xml_factory_class = StringResponseXMLFactory
def test_backward_compatibility_for_multiple_answers(self):
"""
Remove this test, once support for _or_ separator will be removed.
"""
answers = ["Second", "Third", "Fourth"]
problem = self.build_problem(answer="_or_".join(answers), case_sensitive=True)
for answer in answers:
# Exact string should be correct
self.assert_grade(problem, answer, "correct")
# Other strings and the lowercase version of the string are incorrect
self.assert_grade(problem, "Other String", "incorrect")
problem = self.build_problem(answer="_or_".join(answers), case_sensitive=False)
for answer in answers:
# Exact string should be correct
self.assert_grade(problem, answer, "correct")
self.assert_grade(problem, answer.lower(), "correct")
self.assert_grade(problem, "Other String", "incorrect")
def test_regexp(self):
problem = self.build_problem(answer="Second", case_sensitive=False, regexp=True)
self.assert_grade(problem, "Second", "correct")
problem = self.build_problem(answer="sec", case_sensitive=False, regexp=True)
self.assert_grade(problem, "Second", "incorrect")
problem = self.build_problem(answer="sec.*", case_sensitive=False, regexp=True)
self.assert_grade(problem, "Second", "correct")
problem = self.build_problem(answer="sec.*", case_sensitive=True, regexp=True)
self.assert_grade(problem, "Second", "incorrect")
problem = self.build_problem(answer="Sec.*$", case_sensitive=False, regexp=True)
self.assert_grade(problem, "Second", "correct")
problem = self.build_problem(answer="^sec$", case_sensitive=False, regexp=True)
self.assert_grade(problem, "Second", "incorrect")
problem = self.build_problem(answer="^Sec(ond)?$", case_sensitive=False, regexp=True)
self.assert_grade(problem, "Second", "correct")
problem = self.build_problem(answer="^Sec(ond)?$", case_sensitive=False, regexp=True)
self.assert_grade(problem, "Sec", "correct")
problem = self.build_problem(answer="tre+", case_sensitive=False, regexp=True)
self.assert_grade(problem, "There is a tree", "incorrect")
problem = self.build_problem(answer=".*tre+", case_sensitive=False, regexp=True)
self.assert_grade(problem, "There is a tree", "correct")
answers = [
"Martin Luther King Junior",
"Doctor Martin Luther King Junior",
"Dr. Martin Luther King Jr.",
"Martin Luther King"
]
problem = self.build_problem(answer="\w*\.?.*Luther King\s*.*", case_sensitive=True, regexp=True)
for answer in answers:
self.assert_grade(problem, answer, "correct")
problem = self.build_problem(answer="^(-\|){2,5}$", case_sensitive=False, regexp=True)
self.assert_grade(problem, "-|-|-|", "correct")
self.assert_grade(problem, "-|", "incorrect")
self.assert_grade(problem, "-|-|-|-|-|-|", "incorrect")
regexps = [
"^One$",
"two",
"^thre+",
"^4|Four$",
]
problem = self.build_problem(
answer="just_sample",
case_sensitive=False,
regexp=True,
additional_answers=regexps
)
self.assert_grade(problem, "One", "correct")
self.assert_grade(problem, "two", "correct")
self.assert_grade(problem, "!!two!!", "correct")
self.assert_grade(problem, "threeeee", "correct")
self.assert_grade(problem, "three", "correct")
self.assert_grade(problem, "4", "correct")
self.assert_grade(problem, "Four", "correct")
self.assert_grade(problem, "Five", "incorrect")
self.assert_grade(problem, "|", "incorrect")
# test unicode
problem = self.build_problem(answer=u"æ", case_sensitive=False, regexp=True, additional_answers=[u'ö'])
self.assert_grade(problem, u"æ", "correct")
self.assert_grade(problem, u"ö", "correct")
self.assert_grade(problem, u"î", "incorrect")
self.assert_grade(problem, u"o", "incorrect")
def test_backslash_and_unicode_regexps(self):
"""
Test some special cases of [unicode] regexps.
One needs to use either r'' strings or write real `repr` of unicode strings, because of the following
(from python docs, http://docs.python.org/2/library/re.html):
'for example, to match a literal backslash, one might have to write '\\\\' as the pattern string,
because the regular expression must be \\,
and each backslash must be expressed as \\ inside a regular Python string literal.'
Example of real use case in Studio:
a) user inputs regexp in usual regexp language,
b) regexp is saved to xml and is read in python as repr of that string
So a\d in front-end editor will become a\\\\d in xml, so it will match a1 as student answer.
"""
problem = self.build_problem(answer=ur"5\\æ", case_sensitive=False, regexp=True)
self.assert_grade(problem, u"5\æ", "correct")
problem = self.build_problem(answer=u"5\\\\æ", case_sensitive=False, regexp=True)
self.assert_grade(problem, u"5\æ", "correct")
def test_backslash(self):
problem = self.build_problem(answer=u"a\\\\c1", case_sensitive=False, regexp=True)
self.assert_grade(problem, u"a\c1", "correct")
def test_special_chars(self):
problem = self.build_problem(answer=ur"a \s1", case_sensitive=False, regexp=True)
self.assert_grade(problem, u"a 1", "correct")
def test_case_sensitive(self):
# Test single answer
problem = self.build_problem(answer="Second", case_sensitive=True)
......@@ -515,7 +645,7 @@ class StringResponseTest(ResponseTest):
# Test multiple answers
answers = ["Second", "Third", "Fourth"]
problem = self.build_problem(answer="_or_".join(answers), case_sensitive=True)
problem = self.build_problem(answer="sample_answer", case_sensitive=True, additional_answers=answers)
for answer in answers:
# Exact string should be correct
......@@ -525,6 +655,18 @@ class StringResponseTest(ResponseTest):
self.assert_grade(problem, "Other String", "incorrect")
self.assert_grade(problem, "second", "incorrect")
def test_bogus_escape_not_raised(self):
"""
We now adding ^ and $ around regexp, so no bogus escape error will be raised.
"""
problem = self.build_problem(answer=u"\\", case_sensitive=False, regexp=True)
self.assert_grade(problem, u"\\", "incorrect")
# right way to search for \
problem = self.build_problem(answer=u"\\\\", case_sensitive=False, regexp=True)
self.assert_grade(problem, u"\\", "correct")
def test_case_insensitive(self):
# Test single answer
problem = self.build_problem(answer="Second", case_sensitive=False)
......@@ -539,7 +681,7 @@ class StringResponseTest(ResponseTest):
# Test multiple answers
answers = ["Second", "Third", "Fourth"]
problem = self.build_problem(answer="_or_".join(answers), case_sensitive=False)
problem = self.build_problem(answer="sample_answer", case_sensitive=False, additional_answers=answers)
for answer in answers:
# Exact string should be correct
......@@ -549,20 +691,77 @@ class StringResponseTest(ResponseTest):
# Other strings and the lowercase version of the string are incorrect
self.assert_grade(problem, "Other String", "incorrect")
def test_partial_matching(self):
problem = self.build_problem(answer="a2", case_sensitive=False, regexp=True, additional_answers=['.?\\d.?'])
self.assert_grade(problem, "a3", "correct")
self.assert_grade(problem, "3a", "correct")
def test_exception(self):
problem = self.build_problem(answer="a2", case_sensitive=False, regexp=True, additional_answers=['?\\d?'])
with self.assertRaises(Exception) as cm:
self.assert_grade(problem, "a3", "correct")
exception_message = cm.exception.message
self.assertIn("nothing to repeat", exception_message)
def test_hints(self):
multiple_answers = [
"Martin Luther King Junior",
"Doctor Martin Luther King Junior",
"Dr. Martin Luther King Jr.",
"Martin Luther King"
hints = [
("wisconsin", "wisc", "The state capital of Wisconsin is Madison"),
("minnesota", "minn", "The state capital of Minnesota is St. Paul"),
]
hints = [("wisconsin", "wisc", "The state capital of Wisconsin is Madison"),
("minnesota", "minn", "The state capital of Minnesota is St. Paul"),
("_or_".join(multiple_answers), "mlk", "He lead the civil right movement in the United States of America.")]
problem = self.build_problem(
answer="Michigan",
case_sensitive=False,
hints=hints,
)
# We should get a hint for Wisconsin
input_dict = {'1_2_1': 'Wisconsin'}
correct_map = problem.grade_answers(input_dict)
self.assertEquals(correct_map.get_hint('1_2_1'),
"The state capital of Wisconsin is Madison")
problem = self.build_problem(answer="Michigan",
case_sensitive=False,
hints=hints)
# We should get a hint for Minnesota
input_dict = {'1_2_1': 'Minnesota'}
correct_map = problem.grade_answers(input_dict)
self.assertEquals(correct_map.get_hint('1_2_1'),
"The state capital of Minnesota is St. Paul")
# We should NOT get a hint for Michigan (the correct answer)
input_dict = {'1_2_1': 'Michigan'}
correct_map = problem.grade_answers(input_dict)
self.assertEquals(correct_map.get_hint('1_2_1'), "")
# We should NOT get a hint for any other string
input_dict = {'1_2_1': 'California'}
correct_map = problem.grade_answers(input_dict)
self.assertEquals(correct_map.get_hint('1_2_1'), "")
def test_hints_regexp_and_answer_regexp(self):
different_student_answers = [
"May be it is Boston",
"Boston, really?",
"Boston",
"OK, I see, this is Boston",
]
# if problem has regexp = true, it will accept hints written in regexp
hints = [
("wisconsin", "wisc", "The state capital of Wisconsin is Madison"),
("minnesota", "minn", "The state capital of Minnesota is St. Paul"),
(".*Boston.*", "bst", "First letter of correct answer is M."),
('^\\d9$', "numbers", "Should not end with 9."),
]
additional_answers = [
'^\\d[0-8]$',
]
problem = self.build_problem(
answer="Michigan",
case_sensitive=False,
hints=hints,
additional_answers=additional_answers,
regexp=True
)
# We should get a hint for Wisconsin
input_dict = {'1_2_1': 'Wisconsin'}
......@@ -587,12 +786,18 @@ class StringResponseTest(ResponseTest):
self.assertEquals(correct_map.get_hint('1_2_1'), "")
# We should get the same hint for each answer
for answer in multiple_answers:
for answer in different_student_answers:
input_dict = {'1_2_1': answer}
correct_map = problem.grade_answers(input_dict)
self.assertEquals(correct_map.get_hint('1_2_1'),
"He lead the civil right movement in the United States of America.")
self.assertEquals(correct_map.get_hint('1_2_1'), "First letter of correct answer is M.")
input_dict = {'1_2_1': '59'}
correct_map = problem.grade_answers(input_dict)
self.assertEquals(correct_map.get_hint('1_2_1'), "Should not end with 9.")
input_dict = {'1_2_1': '57'}
correct_map = problem.grade_answers(input_dict)
self.assertEquals(correct_map.get_hint('1_2_1'), "")
def test_computed_hints(self):
problem = self.build_problem(
......
......@@ -270,7 +270,7 @@ describe 'MarkdownEditingDescriptor', ->
<p>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>Which US state has Lansing as its capital?</p>
<stringresponse answer="Michigan" type="ci">
<stringresponse answer="Michigan" type="ci" >
<textline size="20"/>
</stringresponse>
......@@ -283,6 +283,29 @@ describe 'MarkdownEditingDescriptor', ->
</div>
</solution>
</problem>""")
it 'converts StringResponse with regular expression to xml', ->
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 King\s*.*" type="ci regexp" >
<textline size="20"/>
</stringresponse>
<solution>
<div class="detailed-solution">
<p>Explanation</p>
<p>Test Explanation.</p>
</div>
</solution>
</problem>""")
it 'converts StringResponse with multiple answers to xml', ->
data = MarkdownEditingDescriptor.markdownToXml("""Who lead the civil right movement in the United States of America?
= Dr. Martin Luther King Jr.
......@@ -296,7 +319,39 @@ describe 'MarkdownEditingDescriptor', ->
""")
expect(data).toEqual("""<problem>
<p>Who lead the civil right movement in the United States of America?</p>
<stringresponse answer="Dr. Martin Luther King Jr._or_Doctor Martin Luther King Junior_or_Martin Luther King_or_Martin Luther King Junior" type="ci">
<stringresponse answer="Dr. Martin Luther King Jr." type="ci" >
<additional_answer>Doctor Martin Luther King Junior</additional_answer>
<additional_answer>Martin Luther King</additional_answer>
<additional_answer>Martin Luther King Junior</additional_answer>
<textline size="20"/>
</stringresponse>
<solution>
<div class="detailed-solution">
<p>Explanation</p>
<p>Test Explanation.</p>
</div>
</solution>
</problem>""")
it 'converts StringResponse with multiple answers and regular expressions to xml', ->
data = MarkdownEditingDescriptor.markdownToXml("""Write a number from 1 to 4.
=| ^One$
or= two
or= ^thre+
or= ^4|Four$
[Explanation]
Test Explanation.
[Explanation]
""")
expect(data).toEqual("""<problem>
<p>Write a number from 1 to 4.</p>
<stringresponse answer="^One$" type="ci regexp" >
<additional_answer>two</additional_answer>
<additional_answer>^thre+</additional_answer>
<additional_answer>^4|Four$</additional_answer>
<textline size="20"/>
</stringresponse>
......
......@@ -247,13 +247,17 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor
string += ' <formulaequationinput />\n';
string += '</numericalresponse>\n\n';
} else {
var answers = [];
var firstAnswer = answersList.shift();
if (firstAnswer[0] === '|') { // this is regexp case
string = '<stringresponse answer="' + firstAnswer.slice(1).trim() + '" type="ci regexp" >\n'
}
else {
string = '<stringresponse answer="' + firstAnswer + '" type="ci" >\n'
}
for(var i = 0; i < answersList.length; i++) {
answers.push(answersList[i])
string += ' <additional_answer>' + answersList[i] + '</additional_answer>\n'
}
string = '<stringresponse answer="' + answers.join('_or_') + '" type="ci">\n <textline size="20"/>\n</stringresponse>\n\n';
string += ' <textline size="20"/>\n</stringresponse>\n\n';
}
return string;
});
......
.. raw:: latex
\newpage %
.. _Appendix E:
......@@ -25,7 +25,7 @@ differences between the two include the following.
Sample Problem:
.. image:: ../Images/image287.png
:width: 600
:width: 600
**Problem Code:**
......@@ -70,7 +70,7 @@ Sample Problem:
<div class="detailed-solution">
</div>
</solution>
</problem>
</problem>
......@@ -88,11 +88,11 @@ Sample Problem:
.. raw:: latex
\newpage %
Multiple Choice
Multiple Choice
===============
......@@ -110,14 +110,14 @@ differences between the two include the following.
• The Option Response drop-down input format makes it more likely for students to think of an answer and then search for it, rather than relying purely on recognition to answer the question.
• The Multiple Choice format is more explicit and visual. This makes it a more appropriate choice for presenting tricky or complicated answer options which areintended to get the student to pause and think.
• The Multiple Choice format is more explicit and visual. This makes it a more appropriate choice for presenting tricky or complicated answer options which are intended to get the student to pause and think.
Sample Problem:
.. image:: ../Images/image289.png
:width: 600
:width: 600
**Problem Code:**
**Problem Code:**
.. code-block:: xml
......@@ -137,7 +137,7 @@ Sample Problem:
<solution>
<div class="detailed-solution">
<p>Explanation</p>
<p>It depends on how many choices are marked as correct in the underlying XML.</p>
<p>It depends on how many choices are marked as correct in the underlying XML.</p>
<p>Note that if all choices are marked as incorrect, there is no
correct response.</p>
</div>
......@@ -145,7 +145,7 @@ Sample Problem:
</problem>
**Template**
**Template**
.. code-block:: xml
......@@ -185,7 +185,7 @@ Sample Problem:
.. raw:: latex
\newpage %
......@@ -206,7 +206,7 @@ have zero correct responses.
Sample Problem:
.. image:: ../Images/image290.png
:width: 600
:width: 600
**Problem Code:**
......@@ -244,7 +244,7 @@ Sample Problem:
</problem>
.. raw:: latex
\newpage %
......@@ -266,7 +266,7 @@ clear.
Sample Problem:
.. image:: ../Images/image291.png
:width: 600
:width: 600
**Problem Code:**
......@@ -301,19 +301,104 @@ Sample Problem:
</solution>
</problem>
This response type allows to add more than one answer. Use `additional_answer` tag to add more answers.
You can add `regexp` to value of `type` attribute, for example: `type="ci regexp"` or `type="regexp"` or `type="regexp cs"`.
In this case, any answer and hint will be treated as regular expressions.
Regular expression has to match whole answer, for answer to be correct.
Student answers "foobar", "o foo" or " ==foo==", will be correct if teacher has set answer=".*foo.*" with type="regexp".
**Template**
.. code-block:: xml
<problem>
<stringresponse answer="a1" type="ci regexp">
<additional_answer>\d5</additional_answer>
<additional_answer>a3</additional_answer>
<textline size="20"/>
<hintgroup>
<stringhint answer="a0" type="ci" name="ha0" />
<stringhint answer="a4" type="ci" name="ha4" />
<stringhint answer="^\d" type="ci" name="re1" />
<hintpart on="ha0">
<startouttext />+1<endouttext />
</hintpart >
<hintpart on="ha4">
<startouttext />-1<endouttext />
</hintpart >
<hintpart on="re1">
<startouttext />Any number+5<endouttext />
</hintpart >
</hintgroup>
</stringresponse>
</problem>
**XML Attribute Information**
<stringresponse>
.. image:: ../Images/stringresponse.png
.. raw:: html
<table border="1" class="docutils" width="60%">
<colgroup>
<col width="15%">
<col width="75%">
<col width="10%">
</colgroup>
<thead valign="bottom">
<tr class="row-odd"><th class="head">Attribute</th>
<th class="head">Description</th>
<th class="head">Notes</th>
</tr>
</thead>
<tbody valign="top">
<tr class="row-even"><td>type</td>
<td>(optional) “[ci] [regex]”. Add “ci” if the student response should be graded case-insensitively. The default is to take case into consideration when grading. Add “regexp” for correct answer to be treated as regular expression.</td>
<td>&nbsp;</td>
</tr>
<tr class="row-odd"><td>answer</td>
<td>The string that is used to compare with student answer. If "regexp" is not presented in value of <em>type</em> attribute, student should enter value equal to exact value of this attribute in order to get credit. If "regexp" is presented in value of <em>type</em> attribute, value of <em>answer</em> is treated as regular expression and exact match of this expression and student answer will be done. If search is successful, student will get credit.</td>
<td>&nbsp;</td>
</tr>
</tbody>
</table>
<table border="1" class="docutils" width="60%">
<colgroup>
<col width="15%">
<col width="75%">
<col width="10%">
</colgroup>
<thead valign="bottom">
<tr class="row-odd"><th class="head">Children</th>
<th class="head">Description</th>
<th class="head">Notes</th>
</tr>
</thead>
<tbody valign="top">
<tr class="row-even"><td>textline</td>
<td>used to accept student input. See description below.</td>
<td>&nbsp;</td>
</tr>
<tr class="row-odd"><td>additional_answer</td>
<td>todo</td>
<td>&nbsp;</td>
</tr>
</tbody>
</table>
<textline>
.. image:: ../Images/stringresponse2.png
<additional_answer> - Can be unlimited number of this tags. Any tag adds one more additional answer for matching.
.. raw:: latex
\newpage %
......@@ -339,7 +424,7 @@ only, and the examples below show its use.
Sample Problem:
.. image:: ../Images/image292.png
:width: 600
:width: 600
**Problem Code**:
......@@ -366,7 +451,7 @@ Sample Problem:
<script type="loncapa/python">
computed_response = math.sqrt(math.fsum([math.pow(math.pi,2), math.pow(math.e,2)]))
</script>
<p>What is the distance in the plane between the points (pi, 0) and (0, e)? You can type math.
<numericalresponse answer="$computed_response">
<responseparam type="tolerance" default="0.0001" />
......@@ -383,7 +468,7 @@ Sample Problem:
the square root of the sum of the squares of the differences of each coordinate.
Even though an exact numerical value is checked in this case, the
easiest way to enter this answer is to type
<code>sqrt(pi^2+e^2)</code> into the editor.
<code>sqrt(pi^2+e^2)</code> into the editor.
Other answers like <code>sqrt((pi-0)^2+(0-e)^2)</code> also work.
</p>
</div>
......@@ -589,7 +674,7 @@ default. These include:
Operators and Functions
~~~~~~~~~~~~~~~~~~~~~~~
As expected, the normal operators apply (with normal order of operations):
As expected, the normal operators apply (with normal order of operations):
``+ - * / ^``. Also provided is a special "parallel resistors" operator given
by ``||``. For example, an input of ``1 || 2`` would represent the resistance
of a pair of parallel resistors (of resistance 1 and 2 ohms), evaluating to 2/3
......@@ -610,7 +695,7 @@ The default included functions are the following:
coth, arcsinh, arccosh, arctanh, arcsech, arccsch, arccoth
.. raw:: latex
\newpage %
......@@ -651,7 +736,7 @@ backward 3.
Sample Problem:
.. image:: ../Images/image293.png
:width: 600
:width: 600
**Problem Code**:
......@@ -663,7 +748,7 @@ Sample Problem:
<p>Write an expression for the product of R_1, R_2, and the inverse of R_3.</p>
<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" />
</formularesponse>
......@@ -672,7 +757,7 @@ Sample Problem:
VoVi = "(R_1*R_2)/R_3"
</script>
<formularesponse type="cs" samples="m,c@1,2:3,4#10" answer="m*c^2">
<responseparam type="tolerance" default="0.00001"/>
<responseparam type="tolerance" default="0.00001"/>
<text><i>E</i> =</text> <formulaequationinput size="40"/>
</formularesponse>
......@@ -681,13 +766,13 @@ Sample Problem:
derivative = "n*x^(n-1)"
</script>
<formularesponse type="ci" samples="x,n@1,2:3,4#10" answer="$derivative">
<responseparam type="tolerance" default="0.00001"/>
<responseparam type="tolerance" default="0.00001"/>
<formulaequationinput size="40" />
</formularesponse>
<!-- Example problem specifying only one variable -->
<formularesponse type="ci" samples="x@1,9#10" answer="x**2 - x + 4">
<responseparam type="tolerance" default="0.00001"/>
<responseparam type="tolerance" default="0.00001"/>
<formulaequationinput size="40" />
</formularesponse>
......@@ -736,7 +821,7 @@ size (optional) defines the size (i.e. the width)
========= ============================================= =====
.. raw:: latex
\newpage %
......@@ -751,7 +836,7 @@ Images have to be uploaded to the courseware Assets directory. Response clicks a
Sample Problem:
.. image:: ../Images/image294.png
:width: 600
:width: 600
**Problem Code**:
......@@ -771,7 +856,7 @@ Sample Problem:
<problem>
<imageresponse>
<imageinput src="Path_to_Image_File.png" width="220" height="150" rectangle="(80,40)-(130,90)" />
</imageresponse>
</imageresponse>
</problem>
XML Attribute Information
......@@ -786,7 +871,7 @@ XML Attribute Information
.. image:: ../Images/imageresponse2.png
.. raw:: latex
\newpage %
.. _Custom Response:
......@@ -799,7 +884,7 @@ A Custom Response input type accepts one or more lines of text input from the st
Sample Problem:
.. image:: ../Images/image295.png
:width: 600
:width: 600
**Problem Code**:
......@@ -846,7 +931,7 @@ Sample Problem:
<div class="detailed-solution">
<p>Explanation</p>
<p>For the first part, any two numbers of the form <i>n</i>
and <i>10-n</i>, where <i>n</i> is any integer, will work.
and <i>10-n</i>, where <i>n</i> is any integer, will work.
One possible answer would be the pair 0 and 10.
</p>
<p>For the second part, any pair <i>x</i> and <i>20-x</i> will work, where <i>x</i> is any real number with a finite decimal representation. Both inputs have to be entered either in standard decimal notation or in scientific exponential notation. One possible answer would be the pair 0.5 and 19.5. Another way to write this would be 5e-1 and 1.95e1.
......@@ -915,7 +1000,7 @@ Sample Problem:
.. raw:: latex
\newpage %
.. _Chemical Equation Response:
......@@ -924,12 +1009,12 @@ Chemical Equation Response
==========================
The Chemical Equation Response input type is a special type of Custom Response
that allows the student to enter chemical equations as answers.
that allows the student to enter chemical equations as answers.
Sample Problem:
.. image:: ../Images/image296.png
:width: 600
:width: 600
**Problem Code**:
......@@ -945,7 +1030,7 @@ Sample Problem:
<chemicalequationinput size="50"/>
<answer type="loncapa/python">
if chemcalc.chemical_equations_equal(submission[0], 'H2SO4 -> H^+ + HSO4^-'):
if chemcalc.chemical_equations_equal(submission[0], 'H2SO4 -> H^+ + HSO4^-'):
correct = ['correct']
else:
correct = ['incorrect']
......@@ -955,28 +1040,28 @@ Sample Problem:
<p> Some tips:<ul><li>Only real element symbols are permitted.</li><li>Subscripts are entered with plain text.</li><li>Superscripts are indicated with a caret (^).</li><li>The reaction arrow (\(\longrightarrow\)) is indicated with "->".</li></ul>
So, you can enter "H2SO4 -> H^+ + HSO4^-".</p>
<endouttext/>
</problem>
</problem>
.. raw:: latex
\newpage %
Schematic Response
==================
The Schematic Response input type provides an interactive grid on which the
student can construct a schematic answer, such as a circuit.
student can construct a schematic answer, such as a circuit.
Sample Problem:
.. image:: ../Images/image297.png
:width: 600
:width: 600
.. image:: ../Images/image298.png
:width: 600
:width: 600
.. image:: ../Images/image299.png
:width: 600
:width: 600
**Problem Code**:
......
......@@ -17,7 +17,7 @@
# Our libraries:
-e git+https://github.com/edx/XBlock.git@a1a3e76b269d15b7bbd11976d8aef63e1db6c4c2#egg=XBlock
-e git+https://github.com/edx/codejail.git@e3d98f9455#egg=codejail
-e git+https://github.com/edx/diff-cover.git@v0.2.6#egg=diff_cover
-e git+https://github.com/edx/diff-cover.git@v0.2.9#egg=diff_cover
-e git+https://github.com/edx/js-test-tool.git@v0.1.5#egg=js_test_tool
-e git+https://github.com/edx/django-waffle.git@823a102e48#egg=django-waffle
-e git+https://github.com/edx/event-tracking.git@f0211d702d#egg=event-tracking
......
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