def test_no_answer_pool(self):
        xml_str = textwrap.dedent("""
            <problem>

            <p>What is the correct answer?</p>
            <multiplechoiceresponse>
              <choicegroup type="MultipleChoice">
                <choice correct="false">wrong-1</choice>
                <choice correct="false">wrong-2</choice>
                <choice correct="true">correct-1</choice>
                <choice correct="false">wrong-3</choice>
                <choice correct="false">wrong-4</choice>
              </choicegroup>
            </multiplechoiceresponse>

        </problem>
        """)

        problem = new_loncapa_problem(xml_str, seed=723)
        the_html = problem.get_html()

        str1 = r"<div>.*\[.*'wrong-1'.*'wrong-2'.*'correct-1'.*'wrong-3'.*'wrong-4'.*\].*</div>"

        self.assertRegexpMatches(the_html, str1)
        # attributes *not* present
        response = problem.responders.values()[0]
        self.assertFalse(response.has_mask())
        self.assertFalse(response.has_answerpool())
    def test_shuffle_fixed_both_ends(self):
        xml_str = textwrap.dedent(
            """
            <problem>
            <multiplechoiceresponse>
              <choicegroup type="MultipleChoice" shuffle="true">
                <choice correct="false" fixed="true">Alpha</choice>
                <choice correct="false" fixed="true">Beta</choice>
                <choice correct="false">A</choice>
                <choice correct="false">B</choice>
                <choice correct="false">C</choice>
                <choice correct ="true">D</choice>
                <choice correct="false" fixed="true">Psi</choice>
                <choice correct="false" fixed="true">Omega</choice>

              </choicegroup>
            </multiplechoiceresponse>
            </problem>
        """
        )
        problem = new_loncapa_problem(xml_str, seed=0)
        the_html = problem.get_html()
        self.assertRegexpMatches(
            the_html, r"<div>.*\[.*'Alpha'.*'Beta'.*'B'.*'A'.*'C'.*'D'.*'Psi'.*'Omega'.*\].*</div>"
        )
 def test_label_is_empty_if_no_label_attribute(self):
     """
     Verify that label in response_data is empty string when label
     attribute is missing and responsetype is not fully accessible.
     """
     question = "Click the country which is home to the Pyramids."
     xml = """
     <problem>
         <p>{}</p>
         <imageresponse>
             <imageinput
             src="/static/Africa.png" width="600" height="638" rectangle="(338,98)-(412,168)"/>
         </imageresponse>
     </problem>
     """.format(question)
     problem = new_loncapa_problem(xml)
     self.assertEqual(
         problem.problem_data,
         {
             '1_2_1':
             {
                 'label': '',
                 'descriptions': {}
             }
         }
     )
 def test_find_correct_answer_text_choices(self, answer_id, answer_text):
     """
     Verify that ``find_correct_answer_text`` can find the correct answer for
     ChoiceResponse, MultipleChoiceResponse and OptionResponse problems.
     """
     problem = new_loncapa_problem(
         """
         <problem>
             <choiceresponse>
                 <checkboxgroup label="Select the correct synonym of paranoid?">
                     <choice correct="true">over-suspicious</choice>
                     <choice correct="false">funny</choice>
                 </checkboxgroup>
             </choiceresponse>
             <multiplechoiceresponse>
                 <choicegroup type="MultipleChoice">
                     <choice correct="true">The iPad</choice>
                     <choice correct="true">Napster</choice>
                     <choice correct="false">The iPod</choice>
                 </choicegroup>
             </multiplechoiceresponse>
             <optionresponse>
                 <optioninput options="('yellow','blue','green')" correct="blue" label="Color_1"/>
             </optionresponse>
         </problem>
         """
     )
     self.assertEquals(problem.find_correct_answer_text(answer_id), answer_text)
    def test_label_and_description_inside_responsetype(self, question):
        """
        Verify that
        * label is extracted
        * <label> tag is removed to avoid duplication

        This is the case when we have a problem with single question or
        problem with multiple-questions separated as per the new format.
        """
        xml = """
        <problem>
            <choiceresponse>
                <label>{question}</label>
                <description>Only the paranoid survive.</description>
                <checkboxgroup>
                    <choice correct="true">over-suspicious</choice>
                    <choice correct="false">funny</choice>
                </checkboxgroup>
            </choiceresponse>
        </problem>
        """.format(question=question)
        problem = new_loncapa_problem(xml)
        self.assertEqual(
            problem.problem_data,
            {
                '1_2_1':
                {
                    'label': question,
                    'descriptions': {'description_1_1_1': 'Only the paranoid survive.'}
                }
            }
        )
        self.assertEqual(len(problem.tree.xpath('//label')), 0)
 def test_invalid_answer_pool_all_correct(self):
     xml_str = textwrap.dedent("""
         <problem>
         <p>What is the correct answer?</p>
         <multiplechoiceresponse>
           <choicegroup type="MultipleChoice" answer-pool="4">
             <choice correct="true">!wrong-1</choice>
             <choice correct="true">!wrong-2</choice>
             <choice correct="true">!wrong-3</choice>
             <choice correct="true">!wrong-4</choice>
           </choicegroup>
         </multiplechoiceresponse>
     </problem>
     """)
     with self.assertRaisesRegexp(LoncapaProblemError, "1 correct.*1 incorrect"):
         new_loncapa_problem(xml_str)
    def test_render_response_with_overall_msg(self):
        # CustomResponse script that sets an overall_message
        script = textwrap.dedent("""
            def check_func(*args):
                msg = '<p>Test message 1<br /></p><p>Test message 2</p>'
                return {'overall_message': msg,
                        'input_list': [ {'ok': True, 'msg': '' } ] }
        """)

        # Generate some XML for a CustomResponse
        kwargs = {'script': script, 'cfn': 'check_func'}
        xml_str = CustomResponseXMLFactory().build_xml(**kwargs)

        # Create the problem and render the html
        problem = new_loncapa_problem(xml_str)

        # Grade the problem
        problem.grade_answers({'1_2_1': 'test'})

        # Render the html
        rendered_html = etree.XML(problem.get_html())

        # Expect that there is a <div> within the response <div>
        # with css class response_message
        msg_div_element = rendered_html.find(".//div[@class='response_message']")
        self.assertEqual(msg_div_element.tag, "div")
        self.assertEqual(msg_div_element.get('class'), "response_message")

        # Expect that the <div> contains our message (as part of the XML tree)
        msg_p_elements = msg_div_element.findall('p')
        self.assertEqual(msg_p_elements[0].tag, "p")
        self.assertEqual(msg_p_elements[0].text, "Test message 1")

        self.assertEqual(msg_p_elements[1].tag, "p")
        self.assertEqual(msg_p_elements[1].text, "Test message 2")
 def test_multiple_descriptions(self):
     """
     Verify that multiple descriptions are handled correctly.
     """
     desc1 = "The problem with trying to be the <em>bad guy</em>, there's always someone <strong>worse</strong>."
     desc2 = "Anyone who looks the world as if it was a game of chess deserves to lose."
     xml = """
     <problem>
         <p>Be sure to check your spelling.</p>
         <stringresponse answer="War" type="ci">
             <label>___ requires sacrifices.</label>
             <description>{}</description>
             <description>{}</description>
             <textline size="40"/>
         </stringresponse>
     </problem>
     """.format(desc1, desc2)
     problem = new_loncapa_problem(xml)
     self.assertEqual(
         problem.problem_data,
         {
             '1_2_1':
             {
                 'label': '___ requires sacrifices.',
                 'descriptions': {
                     'description_1_1_1': desc1,
                     'description_1_1_2': desc2
                 }
             }
         }
     )
 def test_non_accessible_inputtype(self):
     """
     Verify that tag with question text is not removed when inputtype is not fully accessible.
     """
     question = "Click the country which is home to the Pyramids."
     xml = """
     <problem>
         <p>{}</p>
         <imageresponse>
             <imageinput label="{}"
             src="/static/Africa.png" width="600" height="638" rectangle="(338,98)-(412,168)"/>
         </imageresponse>
     </problem>
     """.format(question, question)
     problem = new_loncapa_problem(xml)
     self.assertEqual(
         problem.problem_data,
         {
             '1_2_1':
             {
                 'label': question,
                 'descriptions': {}
             }
         }
     )
     # <p> tag with question text should not be deleted
     self.assertEqual(problem.tree.xpath("string(p[text()='{}'])".format(question)), question)
 def test_shuffle_4_choices(self):
     xml_str = textwrap.dedent(
         """
         <problem>
         <multiplechoiceresponse>
           <choicegroup type="MultipleChoice" shuffle="true">
             <choice correct="false">Apple</choice>
             <choice correct="false">Banana</choice>
             <choice correct="false">Chocolate</choice>
             <choice correct ="true">Donut</choice>
           </choicegroup>
         </multiplechoiceresponse>
         </problem>
     """
     )
     problem = new_loncapa_problem(xml_str, seed=0)
     # shuffling 4 things with seed of 0 yields: B A C D
     # Check that the choices are shuffled
     the_html = problem.get_html()
     self.assertRegexpMatches(the_html, r"<div>.*\[.*'Banana'.*'Apple'.*'Chocolate'.*'Donut'.*\].*</div>")
     # Check that choice name masking is enabled and that unmasking works
     response = problem.responders.values()[0]
     self.assertFalse(response.has_mask())
     self.assertEqual(response.unmask_order(), ["choice_1", "choice_0", "choice_2", "choice_3"])
     self.assertEqual(the_html, problem.get_html(), "should be able to call get_html() twice")
    def test_answer_pool_without_solutionset(self):
        xml_str = textwrap.dedent("""
            <problem>

            <p>What is the correct answer?</p>
            <multiplechoiceresponse>
              <choicegroup type="MultipleChoice" answer-pool="4">
                <choice correct="false">wrong-1</choice>
                <choice correct="false">wrong-2</choice>
                <choice correct="true">correct-1</choice>
                <choice correct="false">wrong-3</choice>
                <choice correct="false">wrong-4</choice>
                <choice correct="true">correct-2</choice>
              </choicegroup>
            </multiplechoiceresponse>

            <solution>
            <div class="detailed-solution">
                <p>Explanation</p>
                <p>This is the solution</p>
                <p>Not much to explain here, sorry!</p>
            </div>
            </solution>

        </problem>
        """)

        problem = new_loncapa_problem(xml_str, seed=723)
        the_html = problem.get_html()

        self.assertRegexpMatches(the_html, r"<div>.*\[.*'wrong-3'.*'correct-2'.*'wrong-2'.*'wrong-4'.*\].*</div>")
        self.assertRegexpMatches(the_html, r"<div>\{.*'1_solution_1'.*\}</div>")
    def test_targeted_feedback_no_solution_element(self):
        xml_str = textwrap.dedent("""
            <problem>
            <p>What is the correct answer?</p>
            <multiplechoiceresponse targeted-feedback="">
              <choicegroup type="MultipleChoice">
                <choice correct="false">wrong-1</choice>
                <choice correct="false">wrong-2</choice>
                <choice correct="true"  explanation-id="feedbackC">correct-1</choice>
                <choice correct="false">wrong-3</choice>
              </choicegroup>
            </multiplechoiceresponse>

            <targetedfeedbackset>
                <targetedfeedback explanation-id="feedbackC">
                <div class="detailed-targeted-feedback">
                    <p>Targeted Feedback</p>
                </div>
                </targetedfeedback>
            </targetedfeedbackset>
            </problem>
        """)

        # Solution element not found
        problem = new_loncapa_problem(xml_str)
        problem.done = True
        problem.student_answers = {'1_2_1': 'choice_2'}
        the_html = problem.get_html()
        without_new_lines = the_html.replace("\n", "")
        # </div> right after </targetedfeedbackset>
        self.assertRegexpMatches(
            without_new_lines,
            r"<div>.*<targetedfeedbackset>.*</targetedfeedbackset>\s*</div>"
        )
    def test_single_inputtypes(self):
        """
        Verify that HTML is correctly rendered when there is single inputtype.
        """
        question = 'Enter sum of 1+2'
        xml = textwrap.dedent("""
        <problem>
            <customresponse cfn="test_sum" expect="3">
        <script type="loncapa/python">
        def test_sum(expect, ans):
            return int(expect) == int(ans)
        </script>
                <label>{}</label>
                <textline size="20" correct_answer="3" />
            </customresponse>
        </problem>
        """.format(question))
        problem = new_loncapa_problem(xml, use_capa_render_template=True)
        problem_html = etree.XML(problem.get_html())

        # verify that only no multi input group div is present
        multi_inputs_group = problem_html.xpath('//div[@class="multi-inputs-group"]')
        self.assertEqual(len(multi_inputs_group), 0)

        # verify that question is rendered only once
        question = problem_html.xpath("//*[normalize-space(text())='{}']".format(question))
        self.assertEqual(len(question), 1)
    def test_multiple_inputtypes(self, group_label):
        """
        Verify that group label and labels for individual inputtypes are extracted correctly.
        """
        input1_label = 'What color is the sky?'
        input2_label = 'What color are pine needles?'
        xml = """
        <problem>
            <optionresponse>
                <label>{}</label>
                <optioninput options="('yellow','blue','green')" correct="blue" label="{}"/>
                <optioninput options="('yellow','blue','green')" correct="green" label="{}"/>
            </optionresponse>
        </problem>
        """.format(group_label, input1_label, input2_label)

        problem = new_loncapa_problem(xml)
        self.assertEqual(
            problem.problem_data,
            {
                '1_2_1':
                {
                    'group_label': group_label,
                    'label': input1_label,
                    'descriptions': {}
                },
                '1_2_2':
                {
                    'group_label': group_label,
                    'label': input2_label,
                    'descriptions': {}
                }
            }
        )
 def test_legacy_problem(self, question, label_attr):
     """
     Verify that legacy problem is handled correctly.
     """
     xml = """
     <problem>
         <p>Be sure to check your spelling.</p>
         <p>{}</p>
         <stringresponse answer="vulnerable" type="ci">
             <textline label="{}" size="40"/>
         </stringresponse>
     </problem>
     """.format(question, label_attr)
     problem = new_loncapa_problem(xml)
     self.assertEqual(
         problem.problem_data,
         {
             '1_2_1':
             {
                 'label': question,
                 'descriptions': {}
             }
         }
     )
     self.assertEqual(
         len(problem.tree.xpath("//*[normalize-space(text())='{}']".format(question))),
         0
     )
    def test_targeted_feedback_not_finished(self):
        problem = new_loncapa_problem(load_fixture('targeted_feedback.xml'))
        the_html = problem.get_html()
        without_new_lines = the_html.replace("\n", "")

        self.assertRegexpMatches(without_new_lines, r"<div>.*'wrong-1'.*'wrong-2'.*'correct-1'.*'wrong-3'.*</div>")
        self.assertNotRegexpMatches(without_new_lines, r"feedback1|feedback2|feedback3|feedbackC")
        self.assertEquals(the_html, problem.get_html(), "Should be able to call get_html() twice")
Beispiel #17
0
    def test_shuffle_not_with_answerpool(self):
        """Raise error if shuffle and answer-pool are both used."""
        xml_str = textwrap.dedent("""
            <problem>
            <multiplechoiceresponse>
              <choicegroup type="MultipleChoice" shuffle="true" answer-pool="4">
                <choice correct="false" fixed="true">A</choice>
                <choice correct="false">Mid</choice>
                <choice correct="true" fixed="true">C</choice>
                <choice correct="False">Mid</choice>
                <choice correct="false" fixed="true">D</choice>
              </choicegroup>
            </multiplechoiceresponse>
            </problem>
        """)

        with self.assertRaisesRegexp(LoncapaProblemError, "shuffle and answer-pool"):
            new_loncapa_problem(xml_str)
 def test_targeted_feedback_multiple_not_answered(self):
     # Not answered -> empty targeted feedback
     problem = new_loncapa_problem(load_fixture('targeted_feedback_multiple.xml'))
     the_html = problem.get_html()
     without_new_lines = the_html.replace("\n", "")
     # Q1 and Q2 have no feedback
     self.assertRegexpMatches(
         without_new_lines,
         r'<targetedfeedbackset.*?/>.*<targetedfeedbackset.*?/>'
     )
    def test_targeted_feedback_student_answer2(self):
        problem = new_loncapa_problem(load_fixture('targeted_feedback.xml'))
        problem.done = True
        problem.student_answers = {'1_2_1': 'choice_0'}

        the_html = problem.get_html()
        without_new_lines = the_html.replace("\n", "")
        # pylint: disable=line-too-long
        self.assertRegexpMatches(without_new_lines, r"<targetedfeedback explanation-id=\"feedback1\" role=\"group\" aria-describedby=\"1_2_1-legend\">\s*<span class=\"sr\">Incorrect</span>.*1st WRONG solution")
        self.assertRegexpMatches(without_new_lines, r"<div>\{.*'1_solution_1'.*\}</div>")
        self.assertNotRegexpMatches(without_new_lines, r"feedback2|feedback3|feedbackC")
 def test_answer_pool_4_choices_1_multiplechoiceresponse_seed2(self):
     problem = new_loncapa_problem(self.common_question_xml, seed=9)
     the_html = problem.get_html()
     # [('choice_0', u'wrong-1'), ('choice_4', u'wrong-4'), ('choice_3', u'wrong-3'), ('choice_2', u'correct-1')]
     self.assertRegexpMatches(the_html, r"<div>.*\[.*'wrong-1'.*'wrong-4'.*'wrong-3'.*'correct-1'.*\].*</div>")
     self.assertRegexpMatches(the_html, r"<div>\{.*'1_solution_1'.*\}</div>")
     # Check about masking
     response = problem.responders.values()[0]
     self.assertFalse(response.has_mask())
     self.assertTrue(hasattr(response, 'has_answerpool'))
     self.assertEqual(response.unmask_order(), ['choice_0', 'choice_4', 'choice_3', 'choice_2'])
 def test_find_answer_text_textinput(self):
     problem = new_loncapa_problem(
         """
         <problem>
             <stringresponse answer="hide" type="ci">
                 <textline size="40"/>
             </stringresponse>
         </problem>
         """
     )
     self.assertEquals(problem.find_answer_text('1_2_1', 'hide'), 'hide')
    def test_targeted_feedback_student_answer2(self):
        problem = new_loncapa_problem(load_fixture('targeted_feedback.xml'))
        problem.done = True
        problem.student_answers = {'1_2_1': 'choice_0'}

        the_html = problem.get_html()
        without_new_lines = the_html.replace("\n", "")

        self.assertRegexpMatches(without_new_lines, r"<targetedfeedback explanation-id=\"feedback1\">.*1st WRONG solution")
        self.assertRegexpMatches(without_new_lines, r"<div>\{.*'1_solution_1'.*\}</div>")
        self.assertNotRegexpMatches(without_new_lines, r"feedback2|feedback3|feedbackC")
 def test_find_question_label(self, answer_id, label, stripped_label):
     problem = new_loncapa_problem(
         '<problem><some-problem id="{}"/></problem>'.format(answer_id)
     )
     mock_problem_data = {
         answer_id: {
             'label': HTML(label) if label else ''
         }
     }
     with patch.object(problem, 'problem_data', mock_problem_data):
         self.assertEqual(problem.find_question_label(answer_id), stripped_label)
 def test_targeted_feedback_multiple_answer_2(self):
     problem = new_loncapa_problem(load_fixture('targeted_feedback_multiple.xml'))
     problem.done = True
     problem.student_answers = {'1_2_1': 'choice_0', '1_3_1': 'choice_2'}  # Q1 wrong, Q2 correct
     the_html = problem.get_html()
     without_new_lines = the_html.replace("\n", "")
     # Q1 has feedback1 and Q2 has feedbackC
     self.assertRegexpMatches(
         without_new_lines,
         r'<targetedfeedbackset.*?>.*?explanation-id="feedback1".*?</targetedfeedbackset>.*' +
         r'<targetedfeedbackset.*?>.*explanation-id="feedbackC".*?</targetedfeedbackset>'
     )
    def test_targeted_feedback_correct_answer(self):
        """ Test the case of targeted feedback for a correct answer. """
        problem = new_loncapa_problem(load_fixture('targeted_feedback.xml'))
        problem.done = True
        problem.student_answers = {'1_2_1': 'choice_2'}

        the_html = problem.get_html()
        without_new_lines = the_html.replace("\n", "")
        # pylint: disable=line-too-long
        self.assertRegexpMatches(without_new_lines,
                                 r"<targetedfeedback explanation-id=\"feedbackC\" role=\"group\" aria-describedby=\"1_2_1-legend\">\s*<span class=\"sr\">Correct</span>.*Feedback on your correct solution...")
        self.assertNotRegexpMatches(without_new_lines, r"feedback1|feedback2|feedback3")
    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> </problem>"

        # Create the problem
        problem = new_loncapa_problem(xml_str)

        # Render the HTML
        etree.XML(problem.get_html())
 def test_answer_pool_4_choices_1_multiplechoiceresponse_seed1(self):
     problem = new_loncapa_problem(self.common_question_xml, seed=723)
     the_html = problem.get_html()
     # [('choice_3', u'wrong-3'), ('choice_5', u'correct-2'), ('choice_1', u'wrong-2'), ('choice_4', u'wrong-4')]
     self.assertRegexpMatches(the_html, r"<div>.*\[.*'wrong-3'.*'correct-2'.*'wrong-2'.*'wrong-4'.*\].*</div>")
     self.assertRegexpMatches(the_html, r"<div>\{.*'1_solution_2'.*\}</div>")
     self.assertEqual(the_html, problem.get_html(), 'should be able to call get_html() twice')
     # Check about masking
     response = problem.responders.values()[0]
     self.assertFalse(response.has_mask())
     self.assertTrue(response.has_answerpool())
     self.assertEqual(response.unmask_order(), ['choice_3', 'choice_5', 'choice_1', 'choice_4'])
    def test_targeted_feedback_student_answer1(self):
        problem = new_loncapa_problem(load_fixture('targeted_feedback.xml'))
        problem.done = True
        problem.student_answers = {'1_2_1': 'choice_3'}

        the_html = problem.get_html()
        without_new_lines = the_html.replace("\n", "")

        self.assertRegexpMatches(without_new_lines, r"<targetedfeedback explanation-id=\"feedback3\">.*3rd WRONG solution")
        self.assertNotRegexpMatches(without_new_lines, r"feedback1|feedback2|feedbackC")
        # Check that calling it multiple times yields the same thing
        the_html2 = problem.get_html()
        self.assertEquals(the_html, the_html2)
    def test_targeted_feedback_student_answer1(self):
        problem = new_loncapa_problem(load_fixture('targeted_feedback.xml'))
        problem.done = True
        problem.student_answers = {'1_2_1': 'choice_3'}

        the_html = problem.get_html()
        without_new_lines = the_html.replace("\n", "")
        # pylint: disable=line-too-long
        self.assertRegexpMatches(without_new_lines, r"<targetedfeedback explanation-id=\"feedback3\" role=\"group\" aria-describedby=\"1_2_1-legend\">\s*<span class=\"sr\">Incorrect</span>.*3rd WRONG solution")
        self.assertNotRegexpMatches(without_new_lines, r"feedback1|feedback2|feedbackC")
        # Check that calling it multiple times yields the same thing
        the_html2 = problem.get_html()
        self.assertEquals(the_html, the_html2)
    def test_invalid_answer_pool_value(self):
        xml_str = textwrap.dedent("""
            <problem>

            <p>What is the correct answer?</p>
            <multiplechoiceresponse>
              <choicegroup type="MultipleChoice" answer-pool="2.3">
                <choice correct="false">wrong-1</choice>
                <choice correct="false">wrong-2</choice>
                <choice correct="true" explanation-id="solution1">correct-1</choice>
                <choice correct="false">wrong-3</choice>
                <choice correct="false">wrong-4</choice>
                <choice correct="true" explanation-id="solution2">correct-2</choice>
              </choicegroup>
            </multiplechoiceresponse>

            <solutionset>
                <solution explanation-id="solution1">
                <div class="detailed-solution">
                    <p>Explanation</p>
                    <p>This is the 1st solution</p>
                    <p>Not much to explain here, sorry!</p>
                </div>
                </solution>

                <solution explanation-id="solution2">
                <div class="detailed-solution">
                    <p>Explanation</p>
                    <p>This is the 2nd solution</p>
                </div>
                </solution>
            </solutionset>

        </problem>
        """)

        with self.assertRaisesRegexp(LoncapaProblemError, "answer-pool"):
            new_loncapa_problem(xml_str)
    def test_targeted_feedback_no_feedback_for_selected_choice2(self):
        xml_str = textwrap.dedent("""
            <problem>
            <p>What is the correct answer?</p>
            <multiplechoiceresponse targeted-feedback="">
              <choicegroup type="MultipleChoice">
                <choice correct="false" explanation-id="feedback1">wrong-1</choice>
                <choice correct="false" explanation-id="feedback2">wrong-2</choice>
                <choice correct="true" explanation-id="feedbackC">correct-1</choice>
                <choice correct="false" explanation-id="feedback3">wrong-3</choice>
              </choicegroup>
            </multiplechoiceresponse>

            <targetedfeedbackset>
                <targetedfeedback explanation-id="feedback1">
                <div class="detailed-targeted-feedback">
                    <p>Targeted Feedback</p>
                    <p>This is the 1st WRONG solution</p>
                </div>
                </targetedfeedback>

                <targetedfeedback explanation-id="feedback3">
                <div class="detailed-targeted-feedback">
                    <p>Targeted Feedback</p>
                    <p>This is the 3rd WRONG solution</p>
                </div>
                </targetedfeedback>

                <targetedfeedback explanation-id="feedbackC">
                <div class="detailed-targeted-feedback-correct">
                    <p>Targeted Feedback</p>
                    <p>Feedback on your correct solution...</p>
                </div>
                </targetedfeedback>

            </targetedfeedbackset>

            <solutionset>
                <solution explanation-id="feedbackC">
                <div class="detailed-solution">
                    <p>Explanation</p>
                    <p>This is the solution explanation</p>
                    <p>Not much to explain here, sorry!</p>
                </div>
                </solution>
            </solutionset>
        </problem>

        """)

        # The student chooses one with no feedback set, so we check that there's no feedback.
        problem = new_loncapa_problem(xml_str)
        problem.done = True
        problem.student_answers = {'1_2_1': 'choice_1'}

        the_html = problem.get_html()
        without_new_lines = the_html.replace("\n", "")

        self.assertNotRegex(
            without_new_lines,
            r"<targetedfeedback explanation-id=\"feedbackC\".*solution explanation"
        )
        self.assertRegex(without_new_lines,
                         r"<div>\{.*'1_solution_1'.*\}</div>")
        self.assertNotRegex(without_new_lines,
                            r"feedback1|feedback3|feedbackC")
    def test_render_response_xml(self):
        # Generate some XML for a string response
        kwargs = {
            'question_text': "Test question",
            'explanation_text': "Test explanation",
            'answer': 'Test answer',
            'hints': [('test prompt', 'test_hint', 'test hint text')]
        }
        xml_str = StringResponseXMLFactory().build_xml(**kwargs)

        # Mock out the template renderer
        the_system = test_capa_system()
        the_system.render_template = mock.Mock()
        the_system.render_template.return_value = "<div class='input-template-render'>Input Template Render</div>"

        # Create the problem and render the HTML
        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>
        assert rendered_html.tag == 'div'

        # Expect that the response has been turned into a <div> with correct attributes
        response_element = rendered_html.find('div')

        assert response_element.tag == 'div'
        assert response_element.attrib['aria-label'] == 'Question 1'

        # Expect that the response div.wrapper-problem-response
        # that contains a <div> for the textline
        textline_element = response_element.find('div')
        assert textline_element.text == 'Input Template Render'

        # Expect a child <div> for the solution
        # with the rendered template
        solution_element = rendered_html.xpath('//div[@class="input-template-render"]')[0]
        assert solution_element.text == 'Input Template Render'

        # Expect that the template renderer was called with the correct
        # arguments, once for the textline input and once for
        # the solution
        expected_textline_context = {
            'STATIC_URL': '/dummy-static/',
            'status': the_system.STATUS_CLASS('unsubmitted'),
            'value': '',
            'preprocessor': None,
            'msg': '',
            'inline': False,
            'hidden': False,
            'do_math': False,
            'id': '1_2_1',
            'trailing_text': '',
            'size': None,
            'response_data': {'label': 'Test question', 'descriptions': {}},
            'describedby_html': HTML('aria-describedby="status_1_2_1"')
        }

        expected_solution_context = {'id': '1_solution_1'}

        expected_calls = [
            mock.call('textline.html', expected_textline_context),
            mock.call('solutionspan.html', expected_solution_context),
            mock.call('textline.html', expected_textline_context),
            mock.call('solutionspan.html', expected_solution_context)
        ]

        assert the_system.render_template.call_args_list == expected_calls
Beispiel #33
0
 def test_find_answer_test_not_implemented(self, current_answer):
     problem = new_loncapa_problem('<problem/>')
     self.assertRaises(NotImplementedError, problem.find_answer_text, '', current_answer)
Beispiel #34
0
 def capa_problem(self, xml):
     """
     Create capa problem.
     """
     return new_loncapa_problem(xml, use_capa_render_template=True)
    def test_targeted_feedback_id_typos(self):
        """Cases where the explanation-id's don't match anything."""
        xml_str = textwrap.dedent("""
            <problem>
            <p>What is the correct answer?</p>
            <multiplechoiceresponse targeted-feedback="">
              <choicegroup type="MultipleChoice">
                <choice correct="false" explanation-id="feedback1TYPO">wrong-1</choice>
                <choice correct="false" explanation-id="feedback2">wrong-2</choice>
                <choice correct="true" explanation-id="feedbackCTYPO">correct-1</choice>
                <choice correct="false" explanation-id="feedback3">wrong-3</choice>
              </choicegroup>
            </multiplechoiceresponse>

            <targetedfeedbackset>
                <targetedfeedback explanation-id="feedback1">
                <div class="detailed-targeted-feedback">
                    <p>Targeted Feedback</p>
                    <p>This is the 1st WRONG solution</p>
                </div>
                </targetedfeedback>

                <targetedfeedback explanation-id="feedback2">
                <div class="detailed-targeted-feedback">
                    <p>Targeted Feedback</p>
                    <p>This is the 2nd WRONG solution</p>
                </div>
                </targetedfeedback>

                <targetedfeedback explanation-id="feedback3">
                <div class="detailed-targeted-feedback">
                    <p>Targeted Feedback</p>
                    <p>This is the 3rd WRONG solution</p>
                </div>
                </targetedfeedback>

                <targetedfeedback explanation-id="feedbackC">
                <div class="detailed-targeted-feedback-correct">
                    <p>Targeted Feedback</p>
                    <p>Feedback on your correct solution...</p>
                </div>
                </targetedfeedback>

            </targetedfeedbackset>

            <solution explanation-id="feedbackC">
            <div class="detailed-solution">
                <p>Explanation</p>
                <p>This is the solution explanation</p>
                <p>Not much to explain here, sorry!</p>
            </div>
            </solution>
        </problem>
        """)

        # explanation-id does not match anything: fall back to empty targetedfeedbackset
        problem = new_loncapa_problem(xml_str)
        problem.done = True
        problem.student_answers = {'1_2_1': 'choice_0'}
        the_html = problem.get_html()
        self.assertRegexpMatches(the_html, r"<targetedfeedbackset>\s*</targetedfeedbackset>")

        # New problem with same XML -- try the correct choice.
        problem = new_loncapa_problem(xml_str)
        problem.done = True
        problem.student_answers = {'1_2_1': 'choice_2'}  # correct
        the_html = problem.get_html()
        self.assertRegexpMatches(the_html, r"<targetedfeedbackset>\s*</targetedfeedbackset>")
    def test_no_targeted_feedback(self):
        xml_str = textwrap.dedent("""
            <problem>
            <p>What is the correct answer?</p>
            <multiplechoiceresponse>
              <choicegroup type="MultipleChoice">
                <choice correct="false" explanation-id="feedback1">wrong-1</choice>
                <choice correct="false" explanation-id="feedback2">wrong-2</choice>
                <choice correct="true" explanation-id="feedbackC">correct-1</choice>
                <choice correct="false" explanation-id="feedback3">wrong-3</choice>
              </choicegroup>
            </multiplechoiceresponse>

            <targetedfeedbackset>
                <targetedfeedback explanation-id="feedback1">
                <div class="detailed-targeted-feedback">
                    <p>Targeted Feedback</p>
                    <p>This is the 1st WRONG solution</p>
                </div>
                </targetedfeedback>

                <targetedfeedback explanation-id="feedback2">
                <div class="detailed-targeted-feedback">
                    <p>Targeted Feedback</p>
                    <p>This is the 2nd WRONG solution</p>
                </div>
                </targetedfeedback>

                <targetedfeedback explanation-id="feedback3">
                <div class="detailed-targeted-feedback">
                    <p>Targeted Feedback</p>
                    <p>This is the 3rd WRONG solution</p>
                </div>
                </targetedfeedback>

                <targetedfeedback explanation-id="feedbackC">
                <div class="detailed-targeted-feedback-correct">
                    <p>Targeted Feedback</p>
                    <p>Feedback on your correct solution...</p>
                </div>
                </targetedfeedback>

            </targetedfeedbackset>

            <solution explanation-id="feedbackC">
            <div class="detailed-solution">
                <p>Explanation</p>
                <p>This is the solution explanation</p>
                <p>Not much to explain here, sorry!</p>
            </div>
            </solution>
        </problem>

        """)

        problem = new_loncapa_problem(xml_str)

        the_html = problem.get_html()
        without_new_lines = the_html.replace("\n", "")

        self.assertRegexpMatches(without_new_lines, r"<div>.*'wrong-1'.*'wrong-2'.*'correct-1'.*'wrong-3'.*</div>")
        self.assertRegexpMatches(without_new_lines, r"feedback1|feedback2|feedback3|feedbackC")
    def test_targeted_feedback_with_solutionset_explanation(self):
        xml_str = textwrap.dedent("""
            <problem>
            <p>What is the correct answer?</p>
            <multiplechoiceresponse targeted-feedback="alwaysShowCorrectChoiceExplanation">
              <choicegroup type="MultipleChoice">
                <choice correct="false" explanation-id="feedback1">wrong-1</choice>
                <choice correct="false" explanation-id="feedback2">wrong-2</choice>
                <choice correct="true" explanation-id="feedbackC">correct-1</choice>
                <choice correct="false" explanation-id="feedback3">wrong-3</choice>
                <choice correct="true" explanation-id="feedbackC2">correct-2</choice>
              </choicegroup>
            </multiplechoiceresponse>

            <targetedfeedbackset>
                <targetedfeedback explanation-id="feedback1">
                <div class="detailed-targeted-feedback">
                    <p>Targeted Feedback</p>
                    <p>This is the 1st WRONG solution</p>
                </div>
                </targetedfeedback>

                <targetedfeedback explanation-id="feedback2">
                <div class="detailed-targeted-feedback">
                    <p>Targeted Feedback</p>
                    <p>This is the 2nd WRONG solution</p>
                </div>
                </targetedfeedback>

                <targetedfeedback explanation-id="feedback3">
                <div class="detailed-targeted-feedback">
                    <p>Targeted Feedback</p>
                    <p>This is the 3rd WRONG solution</p>
                </div>
                </targetedfeedback>

                <targetedfeedback explanation-id="feedbackC">
                <div class="detailed-targeted-feedback-correct">
                    <p>Targeted Feedback</p>
                    <p>Feedback on your correct solution...</p>
                </div>
                </targetedfeedback>

                <targetedfeedback explanation-id="feedbackC2">
                <div class="detailed-targeted-feedback-correct">
                    <p>Targeted Feedback</p>
                    <p>Feedback on the other solution...</p>
                </div>
                </targetedfeedback>

            </targetedfeedbackset>

            <solutionset>
                <solution explanation-id="feedbackC2">
                <div class="detailed-solution">
                    <p>Explanation</p>
                    <p>This is the other solution explanation</p>
                    <p>Not much to explain here, sorry!</p>
                </div>
                </solution>
            </solutionset>
        </problem>

        """)

        problem = new_loncapa_problem(xml_str)
        problem.done = True
        problem.student_answers = {'1_2_1': 'choice_0'}

        the_html = problem.get_html()
        without_new_lines = the_html.replace("\n", "")
        # pylint: disable=line-too-long
        self.assertRegexpMatches(without_new_lines, r"<targetedfeedback explanation-id=\"feedback1\" role=\"group\" aria-describedby=\"1_2_1-legend\">.*1st WRONG solution")
        self.assertRegexpMatches(without_new_lines, r"<targetedfeedback explanation-id=\"feedbackC2\".*other solution explanation")
        self.assertNotRegexpMatches(without_new_lines, r"<div>\{.*'1_solution_1'.*\}</div>")
        self.assertNotRegexpMatches(without_new_lines, r"feedback2|feedback3")