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 ...@@ -9,6 +9,7 @@ import pyparsing
import random import random
import unittest import unittest
import textwrap import textwrap
import requests
import mock import mock
from . import new_loncapa_problem, test_system from . import new_loncapa_problem, test_system
...@@ -199,9 +200,8 @@ class SymbolicResponseTest(ResponseTest): ...@@ -199,9 +200,8 @@ class SymbolicResponseTest(ResponseTest):
from capa.tests.response_xml_factory import SymbolicResponseXMLFactory from capa.tests.response_xml_factory import SymbolicResponseXMLFactory
xml_factory_class = SymbolicResponseXMLFactory xml_factory_class = SymbolicResponseXMLFactory
def test_grade_single_input(self): def test_grade_single_input_correct(self):
problem = self.build_problem(math_display=True, problem = self.build_problem(math_display=True, expect="2*x+3*y")
expect="2*x+3*y")
# Correct answers # Correct answers
correct_inputs = [ correct_inputs = [
...@@ -209,17 +209,27 @@ class SymbolicResponseTest(ResponseTest): ...@@ -209,17 +209,27 @@ class SymbolicResponseTest(ResponseTest):
<math xmlns="http://www.w3.org/1998/Math/MathML"> <math xmlns="http://www.w3.org/1998/Math/MathML">
<mstyle displaystyle="true"> <mstyle displaystyle="true">
<mn>2</mn><mo>*</mo><mi>x</mi><mo>+</mo><mn>3</mn><mo>*</mo><mi>y</mi> <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(""" ('x+x+3y', textwrap.dedent("""
<math xmlns="http://www.w3.org/1998/Math/MathML"> <math xmlns="http://www.w3.org/1998/Math/MathML">
<mstyle displaystyle="true"> <mstyle displaystyle="true">
<mi>x</mi><mo>+</mo><mi>x</mi><mo>+</mo><mn>3</mn><mo>*</mo><mi>y</mi> <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: for (input_str, input_mathml, server_fixture) in correct_inputs:
self._assert_symbolic_grade(problem, input_str, input_mathml, 'correct') 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 answers
incorrect_inputs = [ incorrect_inputs = [
...@@ -234,112 +244,86 @@ class SymbolicResponseTest(ResponseTest): ...@@ -234,112 +244,86 @@ class SymbolicResponseTest(ResponseTest):
for (input_str, input_mathml) in incorrect_inputs: for (input_str, input_mathml) in incorrect_inputs:
self._assert_symbolic_grade(problem, input_str, input_mathml, 'incorrect') self._assert_symbolic_grade(problem, input_str, input_mathml, 'incorrect')
def test_complex_number_grade(self): def test_complex_number_grade_correct(self):
problem = self.build_problem(math_display=True, problem = self.build_problem(
expect="[[cos(theta),i*sin(theta)],[i*sin(theta),cos(theta)]]", math_display=True,
options=["matrix", "imaginary"]) 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')
# Correct answer correct_snuggletex = self._load_fixture('snuggletex_correct.html')
with mock.patch.object(requests, 'post') as mock_post: 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 self._assert_symbolic_grade(
# send for the correct response input problem, student_response, dynamath_input,
mock_post.return_value.text = correct_snuggletex_response 'correct',
snuggletex_resp=correct_snuggletex
)
self._assert_symbolic_grade(problem, def test_complex_number_grade_incorrect(self):
"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')
# Incorrect answer problem = self.build_problem(math_display=True,
with mock.patch.object(requests, 'post') as mock_post: expect="[[cos(theta),i*sin(theta)],[i*sin(theta),cos(theta)]]",
options=["matrix", "imaginary"])
# Simulate what the LaTeX-to-MathML server would wrong_snuggletex = self._load_fixture('snuggletex_wrong.html')
# send for the incorrect response input dynamath_input = textwrap.dedent("""
mock_post.return_value.text = wrong_snuggletex_response <math xmlns="http://www.w3.org/1998/Math/MathML">
<mstyle displaystyle="true"><mn>2</mn></mstyle>
</math>
""")
self._assert_symbolic_grade(problem, "2", self._assert_symbolic_grade(
textwrap.dedent(""" problem, "2", dynamath_input,
<math xmlns="http://www.w3.org/1998/Math/MathML"> 'incorrect',
<mstyle displaystyle="true"><mn>2</mn></mstyle> snuggletex_resp=wrong_snuggletex,
</math> )
"""),
'incorrect')
def test_multiple_inputs_exception(self): def test_multiple_inputs_exception(self):
# Should not allow multiple inputs, since we specify # Should not allow multiple inputs, since we specify
# only one "expect" value # only one "expect" value
with self.assertRaises(Exception): with self.assertRaises(Exception):
self.build_problem(math_display=True, self.build_problem(math_display=True, expect="2*x+3*y", num_inputs=3)
expect="2*x+3*y",
num_inputs=3) def _assert_symbolic_grade(
self, problem, student_input, dynamath_input, expected_correctness,
def _assert_symbolic_grade(self, problem, snuggletex_resp=""
student_input, ):
dynamath_input, """
expected_correctness): 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), input_dict = {'1_2_1': str(student_input),
'1_2_1_dynamath': str(dynamath_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 = problem.grade_answers(input_dict)
correct_map.get_correctness('1_2_1'), expected_correctness
) 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): class OptionResponseTest(ResponseTest):
......
...@@ -6,6 +6,8 @@ from datetime import datetime, timedelta, tzinfo ...@@ -6,6 +6,8 @@ from datetime import datetime, timedelta, tzinfo
from tempfile import mkdtemp from tempfile import mkdtemp
import unittest import unittest
import shutil import shutil
from textwrap import dedent
import mock
import pytz import pytz
from fs.osfs import OSFS from fs.osfs import OSFS
...@@ -35,12 +37,23 @@ def strip_filenames(descriptor): ...@@ -35,12 +37,23 @@ def strip_filenames(descriptor):
class RoundTripTestCase(unittest.TestCase): class RoundTripTestCase(unittest.TestCase):
''' Check that our test courses roundtrip properly. """
Same course imported , than exported, then imported again. Check that our test courses roundtrip properly.
And we compare original import with second import (after export). Same course imported , than exported, then imported again.
Thus we make sure that export and import work properly. 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): """
@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) root_dir = path(self.temp_dir)
print("Copying test course to temp dir {0}".format(root_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