Commit db6d76d6 by Will Daly

Patched textbook TOC request during course import.

Patched Snuggletex server calls
parent a374fceb
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mstyle displaystyle="true">
<mrow>
<mi>cos</mi>
<mrow><mo>(</mo><mi>&#x3B8;</mi><mo>)</mo></mrow>
</mrow>
<mo>&#x22C5;</mo>
<mrow>
<mo>[</mo>
<mtable>
<mtr>
<mtd><mn>1</mn></mtd><mtd><mn>0</mn></mtd>
</mtr>
<mtr>
<mtd><mn>0</mn></mtd><mtd><mn>1</mn></mtd>
</mtr>
</mtable>
<mo>]</mo>
</mrow>
<mo>+</mo>
<mi>i</mi>
<mo>&#x22C5;</mo>
<mrow>
<mi>sin</mi>
<mrow>
<mo>(</mo><mi>&#x3B8;</mi><mo>)</mo>
</mrow>
</mrow>
<mo>&#x22C5;</mo>
<mrow>
<mo>[</mo>
<mtable>
<mtr>
<mtd><mn>0</mn></mtd><mtd><mn>1</mn></mtd>
</mtr>
<mtr>
<mtd><mn>1</mn></mtd><mtd><mn>0</mn></mtd>
</mtr>
</mtable>
<mo>]</mo>
</mrow>
</mstyle>
</math>
......@@ -9,6 +9,7 @@ import pyparsing
import random
import unittest
import textwrap
import requests
import mock
from . import new_loncapa_problem, test_system
......@@ -199,9 +200,8 @@ class SymbolicResponseTest(ResponseTest):
from capa.tests.response_xml_factory import SymbolicResponseXMLFactory
xml_factory_class = SymbolicResponseXMLFactory
def test_grade_single_input(self):
problem = self.build_problem(math_display=True,
expect="2*x+3*y")
def test_grade_single_input_correct(self):
problem = self.build_problem(math_display=True, expect="2*x+3*y")
# Correct answers
correct_inputs = [
......@@ -209,17 +209,27 @@ class SymbolicResponseTest(ResponseTest):
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mstyle displaystyle="true">
<mn>2</mn><mo>*</mo><mi>x</mi><mo>+</mo><mn>3</mn><mo>*</mo><mi>y</mi>
</mstyle></math>""")),
</mstyle></math>"""),
'snuggletex_2x+3y.xml'),
('x+x+3y', textwrap.dedent("""
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mstyle displaystyle="true">
<mi>x</mi><mo>+</mo><mi>x</mi><mo>+</mo><mn>3</mn><mo>*</mo><mi>y</mi>
</mstyle></math>""")),
</mstyle></math>"""),
'snuggletex_x+x+3y.xml'),
]
for (input_str, input_mathml) in correct_inputs:
self._assert_symbolic_grade(problem, input_str, input_mathml, 'correct')
for (input_str, input_mathml, server_fixture) in correct_inputs:
print "Testing input: {0}".format(input_str)
server_resp = self._load_fixture(server_fixture)
self._assert_symbolic_grade(
problem, input_str, input_mathml,
'correct', snuggletex_resp=server_resp
)
def test_grade_single_input_incorrect(self):
problem = self.build_problem(math_display=True, expect="2*x+3*y")
# Incorrect answers
incorrect_inputs = [
......@@ -234,112 +244,86 @@ class SymbolicResponseTest(ResponseTest):
for (input_str, input_mathml) in incorrect_inputs:
self._assert_symbolic_grade(problem, input_str, input_mathml, 'incorrect')
def test_complex_number_grade(self):
problem = self.build_problem(math_display=True,
expect="[[cos(theta),i*sin(theta)],[i*sin(theta),cos(theta)]]",
options=["matrix", "imaginary"])
# For LaTeX-style inputs, symmath_check() will try to contact
# a server to convert the input to MathML.
# We mock out the server, simulating the response that it would give
# for this input.
import requests
dirpath = os.path.dirname(__file__)
correct_snuggletex_response = open(os.path.join(dirpath, "test_files/snuggletex_correct.html")).read().decode('utf8')
wrong_snuggletex_response = open(os.path.join(dirpath, "test_files/snuggletex_wrong.html")).read().decode('utf8')
def test_complex_number_grade_correct(self):
problem = self.build_problem(
math_display=True,
expect="[[cos(theta),i*sin(theta)],[i*sin(theta),cos(theta)]]",
options=["matrix", "imaginary"]
)
# Correct answer
with mock.patch.object(requests, 'post') as mock_post:
correct_snuggletex = self._load_fixture('snuggletex_correct.html')
dynamath_input = self._load_fixture('dynamath_input.txt')
student_response = "cos(theta)*[[1,0],[0,1]] + i*sin(theta)*[[0,1],[1,0]]"
# Simulate what the LaTeX-to-MathML server would
# send for the correct response input
mock_post.return_value.text = correct_snuggletex_response
self._assert_symbolic_grade(
problem, student_response, dynamath_input,
'correct',
snuggletex_resp=correct_snuggletex
)
self._assert_symbolic_grade(problem,
"cos(theta)*[[1,0],[0,1]] + i*sin(theta)*[[0,1],[1,0]]",
textwrap.dedent("""
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mstyle displaystyle="true">
<mrow>
<mi>cos</mi>
<mrow><mo>(</mo><mi>&#x3B8;</mi><mo>)</mo></mrow>
</mrow>
<mo>&#x22C5;</mo>
<mrow>
<mo>[</mo>
<mtable>
<mtr>
<mtd><mn>1</mn></mtd><mtd><mn>0</mn></mtd>
</mtr>
<mtr>
<mtd><mn>0</mn></mtd><mtd><mn>1</mn></mtd>
</mtr>
</mtable>
<mo>]</mo>
</mrow>
<mo>+</mo>
<mi>i</mi>
<mo>&#x22C5;</mo>
<mrow>
<mi>sin</mi>
<mrow>
<mo>(</mo><mi>&#x3B8;</mi><mo>)</mo>
</mrow>
</mrow>
<mo>&#x22C5;</mo>
<mrow>
<mo>[</mo>
<mtable>
<mtr>
<mtd><mn>0</mn></mtd><mtd><mn>1</mn></mtd>
</mtr>
<mtr>
<mtd><mn>1</mn></mtd><mtd><mn>0</mn></mtd>
</mtr>
</mtable>
<mo>]</mo>
</mrow>
</mstyle>
</math>
"""),
'correct')
def test_complex_number_grade_incorrect(self):
# Incorrect answer
with mock.patch.object(requests, 'post') as mock_post:
problem = self.build_problem(math_display=True,
expect="[[cos(theta),i*sin(theta)],[i*sin(theta),cos(theta)]]",
options=["matrix", "imaginary"])
# Simulate what the LaTeX-to-MathML server would
# send for the incorrect response input
mock_post.return_value.text = wrong_snuggletex_response
wrong_snuggletex = self._load_fixture('snuggletex_wrong.html')
dynamath_input = textwrap.dedent("""
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mstyle displaystyle="true"><mn>2</mn></mstyle>
</math>
""")
self._assert_symbolic_grade(problem, "2",
textwrap.dedent("""
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mstyle displaystyle="true"><mn>2</mn></mstyle>
</math>
"""),
'incorrect')
self._assert_symbolic_grade(
problem, "2", dynamath_input,
'incorrect',
snuggletex_resp=wrong_snuggletex,
)
def test_multiple_inputs_exception(self):
# Should not allow multiple inputs, since we specify
# only one "expect" value
with self.assertRaises(Exception):
self.build_problem(math_display=True,
expect="2*x+3*y",
num_inputs=3)
def _assert_symbolic_grade(self, problem,
student_input,
dynamath_input,
expected_correctness):
self.build_problem(math_display=True, expect="2*x+3*y", num_inputs=3)
def _assert_symbolic_grade(
self, problem, student_input, dynamath_input, expected_correctness,
snuggletex_resp=""
):
"""
Assert that the symbolic response has a certain grade.
`problem` is the capa problem containing the symbolic response.
`student_input` is the text the student entered.
`dynamath_input` is the JavaScript rendered MathML from the page.
`expected_correctness` is either "correct" or "incorrect"
`snuggletex_resp` is the simulated response from the Snuggletex server
"""
input_dict = {'1_2_1': str(student_input),
'1_2_1_dynamath': str(dynamath_input)}
correct_map = problem.grade_answers(input_dict)
# Simulate what the Snuggletex server would respond
with mock.patch.object(requests, 'post') as mock_post:
mock_post.return_value.text = snuggletex_resp
self.assertEqual(
correct_map.get_correctness('1_2_1'), expected_correctness
)
correct_map = problem.grade_answers(input_dict)
self.assertEqual(
correct_map.get_correctness('1_2_1'), expected_correctness
)
@staticmethod
def _load_fixture(relpath):
"""
Return a `unicode` object representing the contents
of the fixture file at `relpath` (relative to the test files dir)
"""
abspath = os.path.join(os.path.dirname(__file__), 'test_files', relpath)
with open(abspath) as fixture_file:
contents = fixture_file.read()
return contents.decode('utf8')
class OptionResponseTest(ResponseTest):
......
......@@ -6,6 +6,8 @@ from datetime import datetime, timedelta, tzinfo
from tempfile import mkdtemp
import unittest
import shutil
from textwrap import dedent
import mock
import pytz
from fs.osfs import OSFS
......@@ -35,12 +37,23 @@ def strip_filenames(descriptor):
class RoundTripTestCase(unittest.TestCase):
''' Check that our test courses roundtrip properly.
Same course imported , than exported, then imported again.
And we compare original import with second import (after export).
Thus we make sure that export and import work properly.
'''
def check_export_roundtrip(self, data_dir, course_dir):
"""
Check that our test courses roundtrip properly.
Same course imported , than exported, then imported again.
And we compare original import with second import (after export).
Thus we make sure that export and import work properly.
"""
@mock.patch('xmodule.course_module.requests.get')
def check_export_roundtrip(self, data_dir, course_dir, mock_get):
# Patch network calls to retrieve the textbook TOC
mock_get.return_value.text = dedent("""
<?xml version="1.0"?><table_of_contents>
<entry page="5" page_label="ii" name="Table of Contents"/>
</table_of_contents>
""").strip()
root_dir = path(self.temp_dir)
print("Copying test course to temp dir {0}".format(root_dir))
......
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