Commit 7bab863c by polesye

Merge pull request #1698 from edx/anton/multiple-answers-string-response

Allow multiple answers for string response.
parents 99cf0ec5 77c208a1
...@@ -5,6 +5,8 @@ These are notable changes in edx-platform. This is a rolling list of changes, ...@@ -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 in roughly chronological order, most recent first. Add your entries at or near
the top. Include a label indicating the component affected. the top. Include a label indicating the component affected.
Blades: Allow multiple strings as the correct answer to a string response question. BLD-474.
Blades: a11y - Videos will alert screenreaders when the video is over. Blades: a11y - Videos will alert screenreaders when the video is over.
LMS: Trap focus on the loading element when a user loads more threads LMS: Trap focus on the loading element when a user loads more threads
......
...@@ -75,7 +75,9 @@ ...@@ -75,7 +75,9 @@
<img src="${static.url("img/string-example.png")}" /> <img src="${static.url("img/string-example.png")}" />
</div> </div>
<div class="col"> <div class="col">
<pre><code>= dog</code></pre> <pre><code>= dog
or= cat
or= mouse</code></pre>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
......
...@@ -946,17 +946,34 @@ class NumericalResponse(LoncapaResponse): ...@@ -946,17 +946,34 @@ class NumericalResponse(LoncapaResponse):
class StringResponse(LoncapaResponse): class StringResponse(LoncapaResponse):
'''
This response type allows one or more answers. Use `_or_` separator to set
more than 1 answer.
Example:
# One answer
<stringresponse answer="Michigan">
<textline size="20" />
</stringresponse >
# Multiple answers
<stringresponse answer="Martin Luther King_or_Dr. Martin Luther King Jr.">
<textline size="20" />
</stringresponse >
'''
response_tag = 'stringresponse' response_tag = 'stringresponse'
hint_tag = 'stringhint' hint_tag = 'stringhint'
allowed_inputfields = ['textline'] allowed_inputfields = ['textline']
required_attributes = ['answer'] required_attributes = ['answer']
max_inputfields = 1 max_inputfields = 1
correct_answer = None correct_answer = []
SEPARATOR = '_or_'
def setup_response(self): def setup_response(self):
self.correct_answer = contextualize_text( self.correct_answer = [contextualize_text(answer, self.context).strip()
self.xml.get('answer'), self.context).strip() for answer in self.xml.get('answer').split(self.SEPARATOR)]
def get_score(self, student_answers): def get_score(self, student_answers):
'''Grade a string response ''' '''Grade a string response '''
...@@ -966,23 +983,25 @@ class StringResponse(LoncapaResponse): ...@@ -966,23 +983,25 @@ class StringResponse(LoncapaResponse):
def check_string(self, expected, given): def check_string(self, expected, given):
if self.xml.get('type') == 'ci': if self.xml.get('type') == 'ci':
return given.lower() == expected.lower() return given.lower() in [i.lower() for i in expected]
return given == expected return given in expected
def check_hint_condition(self, hxml_set, student_answers): def check_hint_condition(self, hxml_set, student_answers):
given = student_answers[self.answer_id].strip() given = student_answers[self.answer_id].strip()
hints_to_show = [] hints_to_show = []
for hxml in hxml_set: for hxml in hxml_set:
name = hxml.get('name') name = hxml.get('name')
correct_answer = contextualize_text(
hxml.get('answer'), self.context).strip() correct_answer = [contextualize_text(answer, self.context).strip()
for answer in hxml.get('answer').split(self.SEPARATOR)]
if self.check_string(correct_answer, given): if self.check_string(correct_answer, given):
hints_to_show.append(name) hints_to_show.append(name)
log.debug('hints_to_show = %s', hints_to_show) log.debug('hints_to_show = %s', hints_to_show)
return hints_to_show return hints_to_show
def get_answers(self): def get_answers(self):
return {self.answer_id: self.correct_answer} return {self.answer_id: ' <b>or</b> '.join(self.correct_answer)}
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
......
...@@ -500,6 +500,7 @@ class StringResponseTest(ResponseTest): ...@@ -500,6 +500,7 @@ class StringResponseTest(ResponseTest):
xml_factory_class = StringResponseXMLFactory xml_factory_class = StringResponseXMLFactory
def test_case_sensitive(self): def test_case_sensitive(self):
# Test single answer
problem = self.build_problem(answer="Second", case_sensitive=True) problem = self.build_problem(answer="Second", case_sensitive=True)
# Exact string should be correct # Exact string should be correct
...@@ -509,7 +510,20 @@ class StringResponseTest(ResponseTest): ...@@ -509,7 +510,20 @@ class StringResponseTest(ResponseTest):
self.assert_grade(problem, "Other String", "incorrect") self.assert_grade(problem, "Other String", "incorrect")
self.assert_grade(problem, "second", "incorrect") self.assert_grade(problem, "second", "incorrect")
# Test multiple answers
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")
self.assert_grade(problem, "second", "incorrect")
def test_case_insensitive(self): def test_case_insensitive(self):
# Test single answer
problem = self.build_problem(answer="Second", case_sensitive=False) problem = self.build_problem(answer="Second", case_sensitive=False)
# Both versions of the string should be allowed, regardless # Both versions of the string should be allowed, regardless
...@@ -520,9 +534,28 @@ class StringResponseTest(ResponseTest): ...@@ -520,9 +534,28 @@ class StringResponseTest(ResponseTest):
# Other strings are not allowed # Other strings are not allowed
self.assert_grade(problem, "Other String", "incorrect") self.assert_grade(problem, "Other String", "incorrect")
# Test multiple answers
answers = ["Second", "Third", "Fourth"]
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")
# Other strings and the lowercase version of the string are incorrect
self.assert_grade(problem, "Other String", "incorrect")
def test_hints(self): 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"), hints = [("wisconsin", "wisc", "The state capital of Wisconsin is Madison"),
("minnesota", "minn", "The state capital of Minnesota is St. Paul")] ("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", problem = self.build_problem(answer="Michigan",
case_sensitive=False, case_sensitive=False,
...@@ -550,6 +583,14 @@ class StringResponseTest(ResponseTest): ...@@ -550,6 +583,14 @@ class StringResponseTest(ResponseTest):
correct_map = problem.grade_answers(input_dict) correct_map = problem.grade_answers(input_dict)
self.assertEquals(correct_map.get_hint('1_2_1'), "") self.assertEquals(correct_map.get_hint('1_2_1'), "")
# We should get the same hint for each answer
for answer in multiple_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.")
def test_computed_hints(self): def test_computed_hints(self):
problem = self.build_problem( problem = self.build_problem(
answer="Michigan", answer="Michigan",
......
...@@ -104,45 +104,45 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -104,45 +104,45 @@ describe 'MarkdownEditingDescriptor', ->
Enter the number of fingers on a human hand: Enter the number of fingers on a human hand:
= 5 = 5
[Explanation] [Explanation]
Pi, or the the ratio between a circle's circumference to its diameter, is an irrational number known to extreme precision. It is value is approximately equal to 3.14. Pi, or the the ratio between a circle's circumference to its diameter, is an irrational number known to extreme precision. It is value is approximately equal to 3.14.
Although you can get an exact value by typing 502*9 into a calculator, the result will be close to 500*10, or 5,000. The grader accepts any response within 15% of the true value, 4518, so that you can use any estimation technique that you like. Although you can get an exact value by typing 502*9 into a calculator, the result will be close to 500*10, or 5,000. The grader accepts any response within 15% of the true value, 4518, so that you can use any estimation technique that you like.
If you look at your hand, you can count that you have five fingers. If you look at your hand, you can count that you have five fingers.
[Explanation] [Explanation]
""") """)
expect(data).toEqual("""<problem> expect(data).toEqual("""<problem>
<p>A numerical response problem accepts a line of text input from the student, and evaluates the input for correctness based on its numerical value.</p> <p>A numerical response problem accepts a line of text input from the student, and evaluates the input for correctness based on its numerical value.</p>
<p>The answer is correct if it is within a specified numerical tolerance of the expected answer.</p> <p>The answer is correct if it is within a specified numerical tolerance of the expected answer.</p>
<p>Enter the numerical value of Pi:</p> <p>Enter the numerical value of Pi:</p>
<numericalresponse answer="3.14159"> <numericalresponse answer="3.14159">
<responseparam type="tolerance" default=".02" /> <responseparam type="tolerance" default=".02" />
<formulaequationinput /> <formulaequationinput />
</numericalresponse> </numericalresponse>
<p>Enter the approximate value of 502*9:</p> <p>Enter the approximate value of 502*9:</p>
<numericalresponse answer="4518"> <numericalresponse answer="4518">
<responseparam type="tolerance" default="15%" /> <responseparam type="tolerance" default="15%" />
<formulaequationinput /> <formulaequationinput />
</numericalresponse> </numericalresponse>
<p>Enter the number of fingers on a human hand:</p> <p>Enter the number of fingers on a human hand:</p>
<numericalresponse answer="5"> <numericalresponse answer="5">
<formulaequationinput /> <formulaequationinput />
</numericalresponse> </numericalresponse>
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
<p>Explanation</p> <p>Explanation</p>
<p>Pi, or the the ratio between a circle's circumference to its diameter, is an irrational number known to extreme precision. It is value is approximately equal to 3.14.</p> <p>Pi, or the the ratio between a circle's circumference to its diameter, is an irrational number known to extreme precision. It is value is approximately equal to 3.14.</p>
<p>Although you can get an exact value by typing 502*9 into a calculator, the result will be close to 500*10, or 5,000. The grader accepts any response within 15% of the true value, 4518, so that you can use any estimation technique that you like.</p> <p>Although you can get an exact value by typing 502*9 into a calculator, the result will be close to 500*10, or 5,000. The grader accepts any response within 15% of the true value, 4518, so that you can use any estimation technique that you like.</p>
<p>If you look at your hand, you can count that you have five fingers.</p> <p>If you look at your hand, you can count that you have five fingers.</p>
</div> </div>
...@@ -162,11 +162,26 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -162,11 +162,26 @@ describe 'MarkdownEditingDescriptor', ->
</problem>""") </problem>""")
it 'markup with multiple answers doesn\'t break numerical response', ->
data = MarkdownEditingDescriptor.markdownToXml("""
Enter 1 with a tolerance:
= 1 +- .02
or= 2 +- 5%
""")
expect(data).toEqual("""<problem>
<p>Enter 1 with a tolerance:</p>
<numericalresponse answer="1">
<responseparam type="tolerance" default=".02" />
<formulaequationinput />
</numericalresponse>
</problem>""")
it 'converts multiple choice to xml', -> it 'converts multiple choice to xml', ->
data = MarkdownEditingDescriptor.markdownToXml("""A multiple choice problem presents radio buttons for student input. Students can only select a single option presented. Multiple Choice questions have been the subject of many areas of research due to the early invention and adoption of bubble sheets. data = MarkdownEditingDescriptor.markdownToXml("""A multiple choice problem presents radio buttons for student input. Students can only select a single option presented. Multiple Choice questions have been the subject of many areas of research due to the early invention and adoption of bubble sheets.
One of the main elements that goes into a good multiple choice question is the existence of good distractors. That is, each of the alternate responses presented to the student should be the result of a plausible mistake that a student might make. One of the main elements that goes into a good multiple choice question is the existence of good distractors. That is, each of the alternate responses presented to the student should be the result of a plausible mistake that a student might make.
What Apple device competed with the portable CD player? What Apple device competed with the portable CD player?
( ) The iPad ( ) The iPad
( ) Napster ( ) Napster
...@@ -174,16 +189,16 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -174,16 +189,16 @@ describe 'MarkdownEditingDescriptor', ->
( ) The vegetable peeler ( ) The vegetable peeler
( ) Android ( ) Android
( ) The Beatles ( ) The Beatles
[Explanation] [Explanation]
The release of the iPod allowed consumers to carry their entire music library with them in a format that did not rely on fragile and energy-intensive spinning disks. The release of the iPod allowed consumers to carry their entire music library with them in a format that did not rely on fragile and energy-intensive spinning disks.
[Explanation] [Explanation]
""") """)
expect(data).toEqual("""<problem> expect(data).toEqual("""<problem>
<p>A multiple choice problem presents radio buttons for student input. Students can only select a single option presented. Multiple Choice questions have been the subject of many areas of research due to the early invention and adoption of bubble sheets.</p> <p>A multiple choice problem presents radio buttons for student input. Students can only select a single option presented. Multiple Choice questions have been the subject of many areas of research due to the early invention and adoption of bubble sheets.</p>
<p>One of the main elements that goes into a good multiple choice question is the existence of good distractors. That is, each of the alternate responses presented to the student should be the result of a plausible mistake that a student might make.</p> <p>One of the main elements that goes into a good multiple choice question is the existence of good distractors. That is, each of the alternate responses presented to the student should be the result of a plausible mistake that a student might make.</p>
<p>What Apple device competed with the portable CD player?</p> <p>What Apple device competed with the portable CD player?</p>
<multiplechoiceresponse> <multiplechoiceresponse>
<choicegroup type="MultipleChoice"> <choicegroup type="MultipleChoice">
...@@ -195,104 +210,130 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -195,104 +210,130 @@ describe 'MarkdownEditingDescriptor', ->
<choice correct="false">The Beatles</choice> <choice correct="false">The Beatles</choice>
</choicegroup> </choicegroup>
</multiplechoiceresponse> </multiplechoiceresponse>
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
<p>Explanation</p> <p>Explanation</p>
<p>The release of the iPod allowed consumers to carry their entire music library with them in a format that did not rely on fragile and energy-intensive spinning disks.</p> <p>The release of the iPod allowed consumers to carry their entire music library with them in a format that did not rely on fragile and energy-intensive spinning disks.</p>
</div> </div>
</solution> </solution>
</problem>""") </problem>""")
it 'converts OptionResponse to xml', -> it 'converts OptionResponse to xml', ->
data = MarkdownEditingDescriptor.markdownToXml("""OptionResponse gives a limited set of options for students to respond with, and presents those options in a format that encourages them to search for a specific answer rather than being immediately presented with options from which to recognize the correct answer. data = MarkdownEditingDescriptor.markdownToXml("""OptionResponse gives a limited set of options for students to respond with, and presents those options in a format that encourages them to search for a specific answer rather than being immediately presented with options from which to recognize the correct answer.
The answer options and the identification of the correct answer is defined in the <b>optioninput</b> tag. The answer options and the identification of the correct answer is defined in the <b>optioninput</b> tag.
Translation between Option Response and __________ is extremely straightforward: Translation between Option Response and __________ is extremely straightforward:
[[(Multiple Choice), String Response, Numerical Response, External Response, Image Response]] [[(Multiple Choice), String Response, Numerical Response, External Response, Image Response]]
[Explanation] [Explanation]
Multiple Choice also allows students to select from a variety of pre-written responses, although the format makes it easier for students to read very long response options. Optionresponse also differs slightly because students are more likely to think of an answer and then search for it rather than relying purely on recognition to answer the question. Multiple Choice also allows students to select from a variety of pre-written responses, although the format makes it easier for students to read very long response options. Optionresponse also differs slightly because students are more likely to think of an answer and then search for it rather than relying purely on recognition to answer the question.
[Explanation] [Explanation]
""") """)
expect(data).toEqual("""<problem> expect(data).toEqual("""<problem>
<p>OptionResponse gives a limited set of options for students to respond with, and presents those options in a format that encourages them to search for a specific answer rather than being immediately presented with options from which to recognize the correct answer.</p> <p>OptionResponse gives a limited set of options for students to respond with, and presents those options in a format that encourages them to search for a specific answer rather than being immediately presented with options from which to recognize the correct answer.</p>
<p>The answer options and the identification of the correct answer is defined in the <b>optioninput</b> tag.</p> <p>The answer options and the identification of the correct answer is defined in the <b>optioninput</b> tag.</p>
<p>Translation between Option Response and __________ is extremely straightforward:</p> <p>Translation between Option Response and __________ is extremely straightforward:</p>
<optionresponse> <optionresponse>
<optioninput options="('Multiple Choice','String Response','Numerical Response','External Response','Image Response')" correct="Multiple Choice"></optioninput> <optioninput options="('Multiple Choice','String Response','Numerical Response','External Response','Image Response')" correct="Multiple Choice"></optioninput>
</optionresponse> </optionresponse>
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
<p>Explanation</p> <p>Explanation</p>
<p>Multiple Choice also allows students to select from a variety of pre-written responses, although the format makes it easier for students to read very long response options. Optionresponse also differs slightly because students are more likely to think of an answer and then search for it rather than relying purely on recognition to answer the question.</p> <p>Multiple Choice also allows students to select from a variety of pre-written responses, although the format makes it easier for students to read very long response options. Optionresponse also differs slightly because students are more likely to think of an answer and then search for it rather than relying purely on recognition to answer the question.</p>
</div> </div>
</solution> </solution>
</problem>""") </problem>""")
it 'converts StringResponse to xml', -> it 'converts StringResponse to xml', ->
data = MarkdownEditingDescriptor.markdownToXml("""A string response problem accepts a line of text input from the student, and evaluates the input for correctness based on an expected answer within each input box. data = MarkdownEditingDescriptor.markdownToXml("""A string response problem accepts a line of text input from the student, and evaluates the input for correctness based on an expected answer within each input box.
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. 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.
Which US state has Lansing as its capital? Which US state has Lansing as its capital?
= Michigan = Michigan
[Explanation] [Explanation]
Lansing is the capital of Michigan, although it is not Michgan's largest city, or even the seat of the county in which it resides. Lansing is the capital of Michigan, although it is not Michgan's largest city, or even the seat of the county in which it resides.
[Explanation] [Explanation]
""") """)
expect(data).toEqual("""<problem> expect(data).toEqual("""<problem>
<p>A string response problem accepts a line of text input from the student, and evaluates the input for correctness based on an expected answer within each input box.</p> <p>A string response problem accepts a line of text input from the student, and evaluates the input for correctness based on an expected answer within each input box.</p>
<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>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> <p>Which US state has Lansing as its capital?</p>
<stringresponse answer="Michigan" type="ci"> <stringresponse answer="Michigan" type="ci">
<textline size="20"/> <textline size="20"/>
</stringresponse> </stringresponse>
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
<p>Explanation</p> <p>Explanation</p>
<p>Lansing is the capital of Michigan, although it is not Michgan's largest city, or even the seat of the county in which it resides.</p> <p>Lansing is the capital of Michigan, although it is not Michgan's largest city, or even the seat of the county in which it resides.</p>
</div> </div>
</solution> </solution>
</problem>""") </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.
or= Doctor Martin Luther King Junior
or= Martin Luther King
or= Martin Luther King Junior
[Explanation]
Test Explanation.
[Explanation]
""")
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">
<textline size="20"/>
</stringresponse>
<solution>
<div class="detailed-solution">
<p>Explanation</p>
<p>Test Explanation.</p>
</div>
</solution>
</problem>""")
# test oddities # test oddities
it 'converts headers and oddities to xml', -> it 'converts headers and oddities to xml', ->
data = MarkdownEditingDescriptor.markdownToXml("""Not a header data = MarkdownEditingDescriptor.markdownToXml("""Not a header
A header A header
============== ==============
Multiple choice w/ parentheticals Multiple choice w/ parentheticals
( ) option (with parens) ( ) option (with parens)
( ) xd option (x) ( ) xd option (x)
()) parentheses inside ()) parentheses inside
() no space b4 close paren () no space b4 close paren
Choice checks Choice checks
[ ] option1 [x] [ ] option1 [x]
[x] correct [x] correct
[x] redundant [x] redundant
[(] distractor [(] distractor
[] no space [] no space
Option with multiple correct ones Option with multiple correct ones
[[one option, (correct one), (should not be correct)]] [[one option, (correct one), (should not be correct)]]
Option with embedded parens Option with embedded parens
[[My (heart), another, (correct)]] [[My (heart), another, (correct)]]
What happens w/ empty correct options? What happens w/ empty correct options?
[[()]] [[()]]
...@@ -300,21 +341,21 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -300,21 +341,21 @@ describe 'MarkdownEditingDescriptor', ->
[explanation] [explanation]
orphaned start orphaned start
No p tags in the below No p tags in the below
<script type='javascript'> <script type='javascript'>
var two = 2; var two = 2;
console.log(two * 2); console.log(two * 2);
</script> </script>
But in this there should be But in this there should be
<div> <div>
Great ideas require offsetting. Great ideas require offsetting.
bad tests require drivel bad tests require drivel
</div> </div>
[code] [code]
Code should be nicely monospaced. Code should be nicely monospaced.
[/code] [/code]
...@@ -322,7 +363,7 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -322,7 +363,7 @@ describe 'MarkdownEditingDescriptor', ->
expect(data).toEqual("""<problem> expect(data).toEqual("""<problem>
<p>Not a header</p> <p>Not a header</p>
<h1>A header</h1> <h1>A header</h1>
<p>Multiple choice w/ parentheticals</p> <p>Multiple choice w/ parentheticals</p>
<multiplechoiceresponse> <multiplechoiceresponse>
<choicegroup type="MultipleChoice"> <choicegroup type="MultipleChoice">
...@@ -332,7 +373,7 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -332,7 +373,7 @@ describe 'MarkdownEditingDescriptor', ->
<choice correct="false">no space b4 close paren</choice> <choice correct="false">no space b4 close paren</choice>
</choicegroup> </choicegroup>
</multiplechoiceresponse> </multiplechoiceresponse>
<p>Choice checks</p> <p>Choice checks</p>
<choiceresponse> <choiceresponse>
<checkboxgroup direction="vertical"> <checkboxgroup direction="vertical">
...@@ -343,25 +384,25 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -343,25 +384,25 @@ describe 'MarkdownEditingDescriptor', ->
<choice correct="false">no space</choice> <choice correct="false">no space</choice>
</checkboxgroup> </checkboxgroup>
</choiceresponse> </choiceresponse>
<p>Option with multiple correct ones</p> <p>Option with multiple correct ones</p>
<optionresponse> <optionresponse>
<optioninput options="('one option','correct one','should not be correct')" correct="correct one"></optioninput> <optioninput options="('one option','correct one','should not be correct')" correct="correct one"></optioninput>
</optionresponse> </optionresponse>
<p>Option with embedded parens</p> <p>Option with embedded parens</p>
<optionresponse> <optionresponse>
<optioninput options="('My (heart)','another','correct')" correct="correct"></optioninput> <optioninput options="('My (heart)','another','correct')" correct="correct"></optioninput>
</optionresponse> </optionresponse>
<p>What happens w/ empty correct options?</p> <p>What happens w/ empty correct options?</p>
<optionresponse> <optionresponse>
<optioninput options="('')" correct=""></optioninput> <optioninput options="('')" correct=""></optioninput>
</optionresponse> </optionresponse>
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
<p>Explanation</p> <p>Explanation</p>
...@@ -379,14 +420,14 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -379,14 +420,14 @@ describe 'MarkdownEditingDescriptor', ->
console.log(two * 2); console.log(two * 2);
</script> </script>
<p>But in this there should be</p> <p>But in this there should be</p>
<div> <div>
<p>Great ideas require offsetting.</p> <p>Great ideas require offsetting.</p>
<p>bad tests require drivel</p> <p>bad tests require drivel</p>
</div> </div>
<pre><code> <pre><code>
Code should be nicely monospaced. Code should be nicely monospaced.
</code></pre> </code></pre>
......
...@@ -228,11 +228,13 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor ...@@ -228,11 +228,13 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor
}); });
// replace string and numerical // replace string and numerical
xml = xml.replace(/^\=\s*(.*?$)/gm, function(match, p) { xml = xml.replace(/(^\=\s*(.*?$)(\n*or\=\s*(.*?$))*)+/gm, function(match, p) {
var string; var string,
var floatValue = parseFloat(p); answersList = p.replace(/^(or)?=\s*/gm, '').split('\n'),
floatValue = parseFloat(answersList[0]);
if(!isNaN(floatValue)) { if(!isNaN(floatValue)) {
var params = /(.*?)\+\-\s*(.*?$)/.exec(p); var params = /(.*?)\+\-\s*(.*?$)/.exec(answersList[0]);
if(params) { if(params) {
string = '<numericalresponse answer="' + floatValue + '">\n'; string = '<numericalresponse answer="' + floatValue + '">\n';
string += ' <responseparam type="tolerance" default="' + params[2] + '" />\n'; string += ' <responseparam type="tolerance" default="' + params[2] + '" />\n';
...@@ -242,10 +244,16 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor ...@@ -242,10 +244,16 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor
string += ' <formulaequationinput />\n'; string += ' <formulaequationinput />\n';
string += '</numericalresponse>\n\n'; string += '</numericalresponse>\n\n';
} else { } else {
string = '<stringresponse answer="' + p + '" type="ci">\n <textline size="20"/>\n</stringresponse>\n\n'; var answers = [];
for(var i = 0; i < answersList.length; i++) {
answers.push(answersList[i])
}
string = '<stringresponse answer="' + answers.join('_or_') + '" type="ci">\n <textline size="20"/>\n</stringresponse>\n\n';
} }
return string; return string;
}); });
// replace selects // replace selects
xml = xml.replace(/\[\[(.+?)\]\]/g, function(match, p) { xml = xml.replace(/\[\[(.+?)\]\]/g, function(match, p) {
...@@ -262,13 +270,13 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor ...@@ -262,13 +270,13 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor
selectString += '</optionresponse>\n\n'; selectString += '</optionresponse>\n\n';
return selectString; return selectString;
}); });
// replace explanations // replace explanations
xml = xml.replace(/\[explanation\]\n?([^\]]*)\[\/?explanation\]/gmi, function(match, p1) { xml = xml.replace(/\[explanation\]\n?([^\]]*)\[\/?explanation\]/gmi, function(match, p1) {
var selectString = '<solution>\n<div class="detailed-solution">\nExplanation\n\n' + p1 + '\n</div>\n</solution>'; var selectString = '<solution>\n<div class="detailed-solution">\nExplanation\n\n' + p1 + '\n</div>\n</solution>';
return selectString; return selectString;
}); });
// replace code blocks // replace code blocks
xml = xml.replace(/\[code\]\n?([^\]]*)\[\/?code\]/gmi, function(match, p1) { xml = xml.replace(/\[code\]\n?([^\]]*)\[\/?code\]/gmi, function(match, p1) {
var selectString = '<pre><code>\n' + p1 + '</code></pre>'; var selectString = '<pre><code>\n' + p1 + '</code></pre>';
...@@ -293,7 +301,7 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor ...@@ -293,7 +301,7 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor
// rid white space // rid white space
xml = xml.replace(/\n\n\n/g, '\n'); xml = xml.replace(/\n\n\n/g, '\n');
// surround w/ problem tag // surround w/ problem tag
xml = '<problem>\n' + xml + '\n</problem>'; xml = '<problem>\n' + xml + '\n</problem>';
......
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