-
-
Notifications
You must be signed in to change notification settings - Fork 304
refactor: extract reusable legacy→QTI conversion into qti/convert.py #6031
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
rtibbles
merged 4 commits into
learningequality:unstable
from
rtibblesbot:issue-6003-b9d97e
Jul 5, 2026
+626
−569
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
34093b1
refactor: extract reusable legacy→QTI conversion into qti/convert.py
rtibblesbot d28255b
test: move QTI convert expected XML into fixture files, dedupe conver…
rtibblesbot 2b7c2fe
test: remove stale flake8 noqa from test_convert.py
rtibblesbot c70bfb1
refactor: extract shared response declaration builder, dedupe test it…
rtibblesbot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
12 changes: 12 additions & 0 deletions
12
contentcuration/contentcuration/tests/utils/qti/fixtures/free_response_no_answers.xml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <qti-assessment-item xmlns="http://www.imsglobal.org/xsd/imsqtiasi_v3p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqtiasi_v3p0 https://purl.imsglobal.org/spec/qti/v3p0/schema/xsd/imsqti_asiv3p0p1_v1p0.xsd" identifier="K_ty6CYdlQyH-3LoJh2VDIQ" title="Test Question 1" adaptive="false" time-dependent="false" language="en-US" tool-name="kolibri" tool-version="0.1"> | ||
| <qti-response-declaration identifier="RESPONSE" cardinality="single" base-type="string" /> | ||
| <qti-outcome-declaration identifier="SCORE" cardinality="single" base-type="float" /> | ||
| <qti-item-body> | ||
| <div> | ||
| <p>What is the capital of France?</p> | ||
| <p><qti-text-entry-interaction response-identifier="RESPONSE" expected-length="50" placeholder-text="Enter your answer here" /></p> | ||
| </div> | ||
| </qti-item-body> | ||
| <qti-response-processing template="https://purl.imsglobal.org/spec/qti/v3p0/rptemplates/match_correct" /> | ||
| </qti-assessment-item> |
26 changes: 26 additions & 0 deletions
26
contentcuration/contentcuration/tests/utils/qti/fixtures/free_response_with_maths.xml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <qti-assessment-item xmlns="http://www.imsglobal.org/xsd/imsqtiasi_v3p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqtiasi_v3p0 https://purl.imsglobal.org/spec/qti/v3p0/schema/xsd/imsqti_asiv3p0p1_v1p0.xsd" identifier="K_ty6CYdlQyH-3LoJh2VDIQ" title="Test Question 1" adaptive="false" time-dependent="false" language="en-US" tool-name="kolibri" tool-version="0.1"> | ||
| <qti-response-declaration identifier="RESPONSE" cardinality="single" base-type="string"> | ||
| <qti-correct-response> | ||
| <qti-value>Nothing</qti-value> | ||
| </qti-correct-response> | ||
| </qti-response-declaration> | ||
| <qti-outcome-declaration identifier="SCORE" cardinality="single" base-type="float" /> | ||
| <qti-item-body> | ||
| <div> | ||
| <math display="block"> | ||
| <semantics> | ||
| <mrow> | ||
| <munderover><mo>∑</mo><mi>n</mi><mi>s</mi></munderover> | ||
| <mi>x</mi> | ||
| <msup><mi>a</mi><mi>n</mi></msup> | ||
| </mrow> | ||
| <annotation encoding="application/x-tex">\sum_n^sxa^n</annotation> | ||
| </semantics> | ||
| </math> | ||
| <p>What does this even mean?</p> | ||
| <p><qti-text-entry-interaction response-identifier="RESPONSE" expected-length="50" placeholder-text="Enter your answer here" /></p> | ||
| </div> | ||
| </qti-item-body> | ||
| <qti-response-processing template="https://purl.imsglobal.org/spec/qti/v3p0/rptemplates/match_correct" /> | ||
| </qti-assessment-item> |
17 changes: 17 additions & 0 deletions
17
contentcuration/contentcuration/tests/utils/qti/fixtures/input_question.xml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <qti-assessment-item xmlns="http://www.imsglobal.org/xsd/imsqtiasi_v3p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqtiasi_v3p0 https://purl.imsglobal.org/spec/qti/v3p0/schema/xsd/imsqti_asiv3p0p1_v1p0.xsd" identifier="K_ty6CYdlQyH-3LoJh2VDIQ" title="Test Question 1" adaptive="false" time-dependent="false" language="en-US" tool-name="kolibri" tool-version="0.1"> | ||
| <qti-response-declaration identifier="RESPONSE" cardinality="multiple" base-type="float"> | ||
| <qti-correct-response> | ||
| <qti-value>1</qti-value> | ||
| <qti-value>2</qti-value> | ||
| </qti-correct-response> | ||
| </qti-response-declaration> | ||
| <qti-outcome-declaration identifier="SCORE" cardinality="single" base-type="float" /> | ||
| <qti-item-body> | ||
| <div> | ||
| <p>What positive integers are less than 3?</p> | ||
| <p><qti-text-entry-interaction response-identifier="RESPONSE" expected-length="50" placeholder-text="Enter your answer here" /></p> | ||
| </div> | ||
| </qti-item-body> | ||
| <qti-response-processing template="https://purl.imsglobal.org/spec/qti/v3p0/rptemplates/match_correct" /> | ||
| </qti-assessment-item> |
21 changes: 21 additions & 0 deletions
21
contentcuration/contentcuration/tests/utils/qti/fixtures/math_content_choice_interaction.xml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <qti-assessment-item xmlns="http://www.imsglobal.org/xsd/imsqtiasi_v3p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqtiasi_v3p0 https://purl.imsglobal.org/spec/qti/v3p0/schema/xsd/imsqti_asiv3p0p1_v1p0.xsd" identifier="K3d3d3d3d3d3d3d3d3d3d3Q" title="Test Question 1" adaptive="false" time-dependent="false" language="en-US" tool-name="kolibri" tool-version="0.1"> | ||
| <qti-response-declaration identifier="RESPONSE" cardinality="single" base-type="identifier"> | ||
| <qti-correct-response> | ||
| <qti-value>choice_0</qti-value> | ||
| </qti-correct-response> | ||
| </qti-response-declaration> | ||
| <qti-outcome-declaration identifier="SCORE" cardinality="single" base-type="float" /> | ||
| <qti-item-body> | ||
| <qti-choice-interaction response-identifier="RESPONSE" shuffle="true" max-choices="1" min-choices="0" orientation="vertical"> | ||
| <qti-prompt> | ||
| <p>Solve the equation <math display="inline"><semantics><mrow><mfrac><mrow><mi>x</mi></mrow><mrow><mn>2</mn></mrow></mfrac><mo>=</mo><mn>3</mn></mrow><annotation encoding="application/x-tex">\frac{x}{2} = 3</annotation></semantics></math> for x. What is the value of x?</p> | ||
| </qti-prompt> | ||
| <qti-simple-choice identifier="choice_0" show-hide="show" fixed="false"><p>6</p></qti-simple-choice> | ||
| <qti-simple-choice identifier="choice_1" show-hide="show" fixed="false"><p>3</p></qti-simple-choice> | ||
| <qti-simple-choice identifier="choice_2" show-hide="show" fixed="false"><p>1.5</p></qti-simple-choice> | ||
| <qti-simple-choice identifier="choice_3" show-hide="show" fixed="false"><p>9</p></qti-simple-choice> | ||
| </qti-choice-interaction> | ||
| </qti-item-body> | ||
| <qti-response-processing template="https://purl.imsglobal.org/spec/qti/v3p0/rptemplates/match_correct" /> | ||
| </qti-assessment-item> |
23 changes: 23 additions & 0 deletions
23
contentcuration/contentcuration/tests/utils/qti/fixtures/multiple_selection.xml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <qti-assessment-item xmlns="http://www.imsglobal.org/xsd/imsqtiasi_v3p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqtiasi_v3p0 https://purl.imsglobal.org/spec/qti/v3p0/schema/xsd/imsqti_asiv3p0p1_v1p0.xsd" identifier="Kq83vEjRWeJCrze8SNFZ4kA" title="Test Question 1" adaptive="false" time-dependent="false" language="en-US" tool-name="kolibri" tool-version="0.1"> | ||
| <qti-response-declaration identifier="RESPONSE" cardinality="multiple" base-type="identifier"> | ||
| <qti-correct-response> | ||
| <qti-value>choice_0</qti-value> | ||
| <qti-value>choice_1</qti-value> | ||
| <qti-value>choice_3</qti-value> | ||
| </qti-correct-response> | ||
| </qti-response-declaration> | ||
| <qti-outcome-declaration identifier="SCORE" cardinality="single" base-type="float" /> | ||
| <qti-item-body> | ||
| <qti-choice-interaction response-identifier="RESPONSE" shuffle="true" max-choices="4" min-choices="0" orientation="vertical"> | ||
| <qti-prompt> | ||
| <p>Select all prime numbers:</p> | ||
| </qti-prompt> | ||
| <qti-simple-choice identifier="choice_0" show-hide="show" fixed="false"><p>2</p></qti-simple-choice> | ||
| <qti-simple-choice identifier="choice_1" show-hide="show" fixed="false"><p>3</p></qti-simple-choice> | ||
| <qti-simple-choice identifier="choice_2" show-hide="show" fixed="false"><p>4</p></qti-simple-choice> | ||
| <qti-simple-choice identifier="choice_3" show-hide="show" fixed="false"><p>5</p></qti-simple-choice> | ||
| </qti-choice-interaction> | ||
| </qti-item-body> | ||
| <qti-response-processing template="https://purl.imsglobal.org/spec/qti/v3p0/rptemplates/match_correct" /> | ||
| </qti-assessment-item> |
20 changes: 20 additions & 0 deletions
20
contentcuration/contentcuration/tests/utils/qti/fixtures/single_selection.xml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <qti-assessment-item xmlns="http://www.imsglobal.org/xsd/imsqtiasi_v3p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqtiasi_v3p0 https://purl.imsglobal.org/spec/qti/v3p0/schema/xsd/imsqti_asiv3p0p1_v1p0.xsd" identifier="KEjRWeJCrze8SNFZ4kKvN7w" title="Test Question 1" adaptive="false" time-dependent="false" language="en-US" tool-name="kolibri" tool-version="0.1"> | ||
| <qti-response-declaration identifier="RESPONSE" cardinality="single" base-type="identifier"> | ||
| <qti-correct-response> | ||
| <qti-value>choice_0</qti-value> | ||
| </qti-correct-response> | ||
| </qti-response-declaration> | ||
| <qti-outcome-declaration identifier="SCORE" cardinality="single" base-type="float" /> | ||
| <qti-item-body> | ||
| <qti-choice-interaction response-identifier="RESPONSE" shuffle="true" max-choices="1" min-choices="0" orientation="vertical"> | ||
| <qti-prompt> | ||
| <p>What is 2+2?</p> | ||
| </qti-prompt> | ||
| <qti-simple-choice identifier="choice_0" show-hide="show" fixed="false"><p>4</p></qti-simple-choice> | ||
| <qti-simple-choice identifier="choice_1" show-hide="show" fixed="false"><p>3</p></qti-simple-choice> | ||
| <qti-simple-choice identifier="choice_2" show-hide="show" fixed="false"><p>5</p></qti-simple-choice> | ||
| </qti-choice-interaction> | ||
| </qti-item-body> | ||
| <qti-response-processing template="https://purl.imsglobal.org/spec/qti/v3p0/rptemplates/match_correct" /> | ||
| </qti-assessment-item> |
19 changes: 19 additions & 0 deletions
19
contentcuration/contentcuration/tests/utils/qti/fixtures/true_false.xml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <qti-assessment-item xmlns="http://www.imsglobal.org/xsd/imsqtiasi_v3p0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqtiasi_v3p0 https://purl.imsglobal.org/spec/qti/v3p0/schema/xsd/imsqti_asiv3p0p1_v1p0.xsd" identifier="KEjRWeJCrze8SNFZ4kKvN7w" title="Test Question 1" adaptive="false" time-dependent="false" language="en-US" tool-name="kolibri" tool-version="0.1"> | ||
| <qti-response-declaration identifier="RESPONSE" cardinality="single" base-type="identifier"> | ||
| <qti-correct-response> | ||
| <qti-value>choice_0</qti-value> | ||
| </qti-correct-response> | ||
| </qti-response-declaration> | ||
| <qti-outcome-declaration identifier="SCORE" cardinality="single" base-type="float" /> | ||
| <qti-item-body> | ||
| <qti-choice-interaction response-identifier="RESPONSE" shuffle="false" max-choices="1" min-choices="0" orientation="vertical"> | ||
| <qti-prompt> | ||
| <p>Is the sky blue?</p> | ||
| </qti-prompt> | ||
| <qti-simple-choice identifier="choice_0" show-hide="show" fixed="false"><p>True</p></qti-simple-choice> | ||
| <qti-simple-choice identifier="choice_1" show-hide="show" fixed="false"><p>False</p></qti-simple-choice> | ||
| </qti-choice-interaction> | ||
| </qti-item-body> | ||
| <qti-response-processing template="https://purl.imsglobal.org/spec/qti/v3p0/rptemplates/match_correct" /> | ||
| </qti-assessment-item> |
246 changes: 246 additions & 0 deletions
246
contentcuration/contentcuration/tests/utils/qti/test_convert.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,246 @@ | ||
| import os | ||
| import unittest | ||
|
|
||
| from le_utils.constants import exercises | ||
|
|
||
| from contentcuration.utils.assessment.qti.convert import ( | ||
| convert_legacy_assessment_item_to_qti, | ||
| ) | ||
| from contentcuration.utils.assessment.qti.convert import LegacyAssessmentItem | ||
| from contentcuration.utils.assessment.qti.validation import validate_qti_item | ||
|
|
||
|
|
||
| FIXTURES_DIR = os.path.join(os.path.dirname(__file__), "fixtures") | ||
|
|
||
|
|
||
| def _load_fixture(filename): | ||
| with open(os.path.join(FIXTURES_DIR, filename)) as f: | ||
| return f.read() | ||
|
|
||
|
|
||
| def _normalize_xml(xml_string): | ||
| return "".join(x.strip() for x in xml_string.split("\n")) | ||
|
|
||
|
|
||
| def _make_item( | ||
| type, | ||
| question, | ||
| answers, | ||
| assessment_id, | ||
| randomize=False, | ||
| title="Test Question 1", | ||
| language="en-US", | ||
| ): | ||
| return LegacyAssessmentItem( | ||
| type=type, | ||
| question=question, | ||
| answers=answers, | ||
| randomize=randomize, | ||
| assessment_id=assessment_id, | ||
| title=title, | ||
| language=language, | ||
| ) | ||
|
|
||
|
|
||
| class ChoiceInteractionConversionTests(unittest.TestCase): | ||
| def test_single_selection(self): | ||
| item = _make_item( | ||
| type=exercises.SINGLE_SELECTION, | ||
| question="What is 2+2?", | ||
| answers=[ | ||
| {"answer": "4", "correct": True, "order": 1}, | ||
| {"answer": "3", "correct": False, "order": 2}, | ||
| {"answer": "5", "correct": False, "order": 3}, | ||
| ], | ||
| randomize=True, | ||
| assessment_id="1234567890abcdef1234567890abcdef", | ||
| ) | ||
|
|
||
| result = convert_legacy_assessment_item_to_qti(item) | ||
|
|
||
| self.assertEqual(result.identifier, "KEjRWeJCrze8SNFZ4kKvN7w") | ||
| self.assertEqual( | ||
| _normalize_xml(_load_fixture("single_selection.xml")), | ||
| _normalize_xml(result.xml), | ||
| ) | ||
| self.assertTrue(validate_qti_item(result.xml.encode("utf-8")).is_valid) | ||
|
|
||
| def test_multiple_selection(self): | ||
| item = _make_item( | ||
| type=exercises.MULTIPLE_SELECTION, | ||
| question="Select all prime numbers:", | ||
| answers=[ | ||
| {"answer": "2", "correct": True, "order": 1}, | ||
| {"answer": "3", "correct": True, "order": 2}, | ||
| {"answer": "4", "correct": False, "order": 3}, | ||
| {"answer": "5", "correct": True, "order": 4}, | ||
| ], | ||
| randomize=True, | ||
| assessment_id="abcdef1234567890abcdef1234567890", | ||
| ) | ||
|
|
||
| result = convert_legacy_assessment_item_to_qti(item) | ||
|
|
||
| self.assertEqual(result.identifier, "Kq83vEjRWeJCrze8SNFZ4kA") | ||
| self.assertEqual( | ||
| _normalize_xml(_load_fixture("multiple_selection.xml")), | ||
| _normalize_xml(result.xml), | ||
| ) | ||
| self.assertTrue(validate_qti_item(result.xml.encode("utf-8")).is_valid) | ||
|
|
||
| def test_true_false(self): | ||
| item = _make_item( | ||
| type="true_false", | ||
| question="Is the sky blue?", | ||
| answers=[ | ||
| {"answer": "True", "correct": True, "order": 1}, | ||
| {"answer": "False", "correct": False, "order": 2}, | ||
| ], | ||
| assessment_id="1234567890abcdef1234567890abcdef", | ||
| ) | ||
|
|
||
| result = convert_legacy_assessment_item_to_qti(item) | ||
|
|
||
| self.assertEqual(result.identifier, "KEjRWeJCrze8SNFZ4kKvN7w") | ||
| self.assertEqual( | ||
| _normalize_xml(_load_fixture("true_false.xml")), | ||
| _normalize_xml(result.xml), | ||
| ) | ||
| self.assertTrue(validate_qti_item(result.xml.encode("utf-8")).is_valid) | ||
|
|
||
| def test_media_reference_survives(self): | ||
| item = _make_item( | ||
| type=exercises.SINGLE_SELECTION, | ||
| question="See the diagram: ", | ||
| answers=[ | ||
| { | ||
| "answer": "Correct ", | ||
| "correct": True, | ||
| "order": 1, | ||
| }, | ||
| {"answer": "Wrong", "correct": False, "order": 2}, | ||
| ], | ||
| assessment_id="1234567890abcdef1234567890abcdef", | ||
| title="Media Test", | ||
| ) | ||
|
|
||
| result = convert_legacy_assessment_item_to_qti(item) | ||
|
|
||
| self.assertIn('<img alt="diagram" src="images/abc123.png" />', result.xml) | ||
| self.assertIn('<img alt="opt" src="images/def456.png" />', result.xml) | ||
| self.assertEqual( | ||
| {"images/abc123.png", "images/def456.png"}, set(result.file_dependencies) | ||
| ) | ||
|
|
||
| def test_math_content_in_choice_interaction(self): | ||
| item = _make_item( | ||
| type=exercises.SINGLE_SELECTION, | ||
| question="Solve the equation $$\\frac{x}{2} = 3$$ for x. What is the value of x?", | ||
| answers=[ | ||
| {"answer": "6", "correct": True, "order": 1}, | ||
| {"answer": "3", "correct": False, "order": 2}, | ||
| {"answer": "1.5", "correct": False, "order": 3}, | ||
| {"answer": "9", "correct": False, "order": 4}, | ||
| ], | ||
| randomize=True, | ||
| assessment_id="dddddddddddddddddddddddddddddddd", | ||
| ) | ||
|
|
||
| result = convert_legacy_assessment_item_to_qti(item) | ||
|
|
||
| self.assertEqual(result.identifier, "K3d3d3d3d3d3d3d3d3d3d3Q") | ||
| self.assertEqual( | ||
| _normalize_xml(_load_fixture("math_content_choice_interaction.xml")), | ||
| _normalize_xml(result.xml), | ||
| ) | ||
|
|
||
|
|
||
| class TextEntryInteractionConversionTests(unittest.TestCase): | ||
| def test_input_question(self): | ||
| item = _make_item( | ||
| type=exercises.INPUT_QUESTION, | ||
| question="What positive integers are less than 3?", | ||
| answers=[ | ||
| {"answer": 1, "correct": True, "order": 1}, | ||
| {"answer": 2, "correct": True, "order": 2}, | ||
| ], | ||
| randomize=True, | ||
| assessment_id="fedcba0987654321fedcba0987654321", | ||
| ) | ||
|
|
||
| result = convert_legacy_assessment_item_to_qti(item) | ||
|
|
||
| self.assertEqual(result.identifier, "K_ty6CYdlQyH-3LoJh2VDIQ") | ||
| self.assertEqual( | ||
| _normalize_xml(_load_fixture("input_question.xml")), | ||
| _normalize_xml(result.xml), | ||
| ) | ||
| self.assertTrue(validate_qti_item(result.xml.encode("utf-8")).is_valid) | ||
|
|
||
| def test_free_response_question(self): | ||
| item = _make_item( | ||
| type=exercises.FREE_RESPONSE, | ||
| question="What positive integers are less than 3?", | ||
| answers=[ | ||
| {"answer": 1, "correct": True, "order": 1}, | ||
| {"answer": 2, "correct": True, "order": 2}, | ||
| ], | ||
| randomize=True, | ||
| assessment_id="fedcba0987654321fedcba0987654321", | ||
| ) | ||
|
|
||
| result = convert_legacy_assessment_item_to_qti(item) | ||
|
|
||
| self.assertIn("<qti-text-entry-interaction", result.xml) | ||
| self.assertTrue(validate_qti_item(result.xml.encode("utf-8")).is_valid) | ||
|
|
||
| def test_free_response_no_answers(self): | ||
| item = _make_item( | ||
| type=exercises.FREE_RESPONSE, | ||
| question="What is the capital of France?", | ||
| answers=[], | ||
| randomize=True, | ||
| assessment_id="fedcba0987654321fedcba0987654321", | ||
| ) | ||
|
|
||
| result = convert_legacy_assessment_item_to_qti(item) | ||
|
|
||
| self.assertEqual(result.identifier, "K_ty6CYdlQyH-3LoJh2VDIQ") | ||
| self.assertEqual( | ||
| _normalize_xml(_load_fixture("free_response_no_answers.xml")), | ||
| _normalize_xml(result.xml), | ||
| ) | ||
| self.assertTrue(validate_qti_item(result.xml.encode("utf-8")).is_valid) | ||
|
|
||
| def test_free_response_with_maths(self): | ||
| item = _make_item( | ||
| type=exercises.FREE_RESPONSE, | ||
| question="$$\\sum_n^sxa^n$$\n\n What does this even mean?", | ||
| answers=[{"answer": "Nothing", "correct": True, "order": 1}], | ||
| randomize=True, | ||
| assessment_id="fedcba0987654321fedcba0987654321", | ||
| ) | ||
|
|
||
| result = convert_legacy_assessment_item_to_qti(item) | ||
|
|
||
| self.assertEqual(result.identifier, "K_ty6CYdlQyH-3LoJh2VDIQ") | ||
| self.assertEqual( | ||
| _normalize_xml(_load_fixture("free_response_with_maths.xml")), | ||
| _normalize_xml(result.xml), | ||
| ) | ||
|
|
||
|
|
||
| class UnsupportedTypeConversionTests(unittest.TestCase): | ||
| def test_unsupported_type_raises(self): | ||
| item = _make_item( | ||
| type="NOT_A_REAL_TYPE", | ||
| question="x", | ||
| answers=[], | ||
| assessment_id="1234567890abcdef1234567890abcdef", | ||
| title="t", | ||
| ) | ||
|
|
||
| with self.assertRaises(ValueError) as ctx: | ||
| convert_legacy_assessment_item_to_qti(item) | ||
|
|
||
| self.assertIn("Unsupported question type", str(ctx.exception)) | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that this is now the right place for this, but I think we should also clean up the conversion specific tests from the archive creation tests here: https://github.com/learningequality/studio/blob/unstable/contentcuration/contentcuration/tests/utils/test_exercise_creation.py#L1226
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cleaned up
TestQTIExerciseCreationintest_exercise_creation.py: removedtest_multiple_selection_question,test_free_response_question, andtest_input_question, which only re-asserted per-type conversion XML already covered bytest_convert.py. Trimmedtest_basic_qti_exercise_creationto drop its duplicate item-XML assertion (kept the zip/manifest checks, which are archive-specific).test_question_with_mathematical_content,test_free_response_question_no_answers, andtest_free_response_question_with_mathstested conversion edge cases (MathML rendering, empty-answers response declaration) with no archive-specific behavior, so I ported them intotest_convert.pyastest_math_content_in_choice_interaction,test_free_response_no_answers, andtest_free_response_with_maths(using fixture files per the other thread) rather than dropping that coverage. Left the tests that exercise genuinely archive-specific behavior (manifest structure, image bundling/resizing, native QTI passthrough, Perseus/unsupported-type dispatch) as-is. Full suite still green (29 passed intest_exercise_creation.py, 10 intest_convert.py).