qti 2.10.0 → 2.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/qti/sanitizer.rb +21 -7
- data/lib/qti/v2/models/assessment_item.rb +1 -1
- data/lib/qti/v2/models/interactions/gap_match_interaction.rb +14 -7
- data/lib/qti/v2/models/non_assessment_test.rb +1 -1
- data/lib/qti/version.rb +1 -1
- data/spec/fixtures/imsqti_2.2_package/choice.xml +30 -0
- data/spec/fixtures/imsqti_2.2_package/images/sign.png +0 -0
- data/spec/fixtures/imsqti_2.2_package/imsmanifest.xml +101 -0
- data/spec/lib/qti/sanitizer_spec.rb +50 -0
- data/spec/lib/qti/v2/models/assessment_item_spec.rb +17 -0
- data/spec/lib/qti/v2/models/interactions/gap_match_interaction_spec.rb +2 -2
- data/spec/lib/qti/v2/models/non_assessment_test_spec.rb +8 -0
- metadata +30 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 36f18933b8d36d5a0c9ff5a5b4788852a9061c434385c3dd542b9cfb6c736c8d
|
4
|
+
data.tar.gz: 97641eb71ae7efa662ed2e6052e7cb4f35297e8927a3c01c808dc85dcacbf6e3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db6f0e6c2987afa8da40dd633ae1ecbb3fd5e099ddf6de86d6bf3b65d075caf8cfd4e1a4070810fdca2452ee669f347c33dfbb3d508f8ab44b26ccbc651f2009
|
7
|
+
data.tar.gz: bb12cf21f40ea16f3238d7a04f73b8a203b4a54881fd1f1576841abbf26045501e745b358ad77b94d8b4ee1f0ff16fe88876f7d7df073a85efa92ada6e14c75d
|
data/lib/qti/sanitizer.rb
CHANGED
@@ -9,26 +9,37 @@ module Qti
|
|
9
9
|
}.freeze
|
10
10
|
|
11
11
|
PROTOCOLS = ['http', 'https', :relative].freeze
|
12
|
-
FILTER_TAGS = %w[iframe object embed].freeze
|
12
|
+
FILTER_TAGS = %w[iframe object embed video audio source].freeze
|
13
|
+
MEDIA_SRC_ATTR = %w[src data type codebase].freeze
|
14
|
+
MEDIA_FMT_ATTR = %w[width height classid].freeze
|
15
|
+
MEDIA_ALT_ATTR = %w[title alt allow allowfullscreen].freeze
|
16
|
+
MEDIA_EXT_ATTR = %w[data-media-type data-media-id].freeze
|
17
|
+
MEDIA_ATTR = [MEDIA_SRC_ATTR, MEDIA_FMT_ATTR, MEDIA_ALT_ATTR, MEDIA_EXT_ATTR].flatten.freeze
|
13
18
|
|
14
19
|
CONFIG =
|
15
20
|
{
|
16
|
-
elements: FILTER_TAGS,
|
21
|
+
elements: Sanitize::Config::RELAXED[:elements] + FILTER_TAGS,
|
17
22
|
protocols:
|
18
23
|
{
|
19
24
|
'iframe' => { 'src' => PROTOCOLS },
|
20
25
|
'object' => { 'src' => PROTOCOLS, 'data' => PROTOCOLS },
|
21
|
-
'embed' => { 'src' => PROTOCOLS }
|
22
|
-
|
26
|
+
'embed' => { 'src' => PROTOCOLS },
|
27
|
+
'video' => { 'src' => PROTOCOLS },
|
28
|
+
'audio' => { 'src' => PROTOCOLS },
|
29
|
+
'source' => { 'src' => PROTOCOLS }
|
30
|
+
},
|
23
31
|
attributes:
|
24
32
|
{
|
25
|
-
'
|
33
|
+
'video' => MEDIA_ATTR,
|
34
|
+
'audio' => MEDIA_ATTR,
|
35
|
+
'source' => MEDIA_ATTR,
|
36
|
+
'object' => MEDIA_ATTR,
|
26
37
|
'embed' => %w[name src type allowfullscreen pluginspage wmode
|
27
38
|
allowscriptaccess width height],
|
28
39
|
'iframe' => %w[src width height name align frameborder scrolling sandbox
|
29
40
|
allowfullscreen webkitallowfullscreen mozallowfullscreen
|
30
41
|
allow] # TODO: remove explicit allow with domain whitelist account setting
|
31
|
-
}
|
42
|
+
}
|
32
43
|
}.freeze
|
33
44
|
|
34
45
|
def clean(html)
|
@@ -86,7 +97,10 @@ module Qti
|
|
86
97
|
transformers << object_tag_transformer if import_objects
|
87
98
|
transformers << remap_unknown_tags_transformer
|
88
99
|
transformers << method(:convert_canvas_math_images) if Qti.configuration.extract_latex_from_image_tags
|
89
|
-
Sanitize::Config
|
100
|
+
Sanitize::Config.merge(
|
101
|
+
Sanitize::Config.merge(Sanitize::Config::RELAXED, CONFIG),
|
102
|
+
transformers: transformers
|
103
|
+
)
|
90
104
|
end
|
91
105
|
|
92
106
|
def remap_href_path(href)
|
@@ -10,7 +10,7 @@ module Qti
|
|
10
10
|
# ensure a prompt is carried into the html
|
11
11
|
prompt = node.at_xpath('//xmlns:prompt')
|
12
12
|
filter_item_body(node)
|
13
|
-
node.add_child(prompt) if prompt&.parent && prompt.parent != node
|
13
|
+
node.add_child(prompt&.dup) if prompt&.parent && prompt.parent != node
|
14
14
|
sanitize_content!(node.to_html)
|
15
15
|
end
|
16
16
|
end
|
@@ -59,9 +59,11 @@ module Qti
|
|
59
59
|
def stem_text
|
60
60
|
clean_stem_items.search('p').children.map do |stem_item|
|
61
61
|
if stem_item.name == 'gap'
|
62
|
+
blank_id = stem_item.attributes['identifier'].value
|
62
63
|
{
|
63
64
|
type: 'blank',
|
64
|
-
blank_id:
|
65
|
+
blank_id: blank_id,
|
66
|
+
blank_name: correct_choice_value(blank_id)
|
65
67
|
}
|
66
68
|
else
|
67
69
|
{
|
@@ -84,12 +86,15 @@ module Qti
|
|
84
86
|
end
|
85
87
|
end
|
86
88
|
|
89
|
+
def correct_choice_value(node_id)
|
90
|
+
answer_choice(choices, question_response_id_mapping[node_id]).content
|
91
|
+
end
|
92
|
+
|
87
93
|
def scoring_data_structs
|
88
|
-
mapping = question_response_id_mapping
|
89
94
|
answer_nodes.map do |value_node|
|
90
95
|
node_id = value_node.attributes['identifier']&.value
|
91
96
|
ScoringData.new(
|
92
|
-
|
97
|
+
correct_choice_value(node_id),
|
93
98
|
'directedPair',
|
94
99
|
id: node_id,
|
95
100
|
case: false
|
@@ -114,11 +119,13 @@ module Qti
|
|
114
119
|
end
|
115
120
|
|
116
121
|
def question_response_id_mapping
|
117
|
-
|
118
|
-
value.
|
122
|
+
@question_response_id_mapping ||= begin
|
123
|
+
question_response_pairs = node.xpath('.//xmlns:correctResponse//xmlns:value').map do |value|
|
124
|
+
value.content.split
|
125
|
+
end
|
126
|
+
question_response_pairs.map!(&:reverse)
|
127
|
+
Hash[question_response_pairs]
|
119
128
|
end
|
120
|
-
question_response_pairs.map!(&:reverse)
|
121
|
-
Hash[question_response_pairs]
|
122
129
|
end
|
123
130
|
end
|
124
131
|
end
|
data/lib/qti/version.rb
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<!-- Thie example adapted from the PET Handbook, copyright University of Cambridge ESOL Examinations -->
|
3
|
+
<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_v2p2"
|
4
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
5
|
+
xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_v2p2 http://www.imsglobal.org/xsd/qti/qtiv2p2/imsqti_v2p2p2.xsd"
|
6
|
+
identifier="choice" title="Unattended Luggage" adaptive="false" timeDependent="false">
|
7
|
+
<responseDeclaration identifier="RESPONSE" cardinality="single" baseType="identifier">
|
8
|
+
<correctResponse>
|
9
|
+
<value>ChoiceA</value>
|
10
|
+
</correctResponse>
|
11
|
+
</responseDeclaration>
|
12
|
+
<outcomeDeclaration identifier="SCORE" cardinality="single" baseType="float">
|
13
|
+
<defaultValue>
|
14
|
+
<value>0</value>
|
15
|
+
</defaultValue>
|
16
|
+
</outcomeDeclaration>
|
17
|
+
<itemBody>
|
18
|
+
<p>Look at the text in the picture.</p>
|
19
|
+
<p>
|
20
|
+
<img src="images/sign.png" alt="NEVER LEAVE LUGGAGE UNATTENDED"/>
|
21
|
+
</p>
|
22
|
+
<choiceInteraction responseIdentifier="RESPONSE" shuffle="false" maxChoices="1">
|
23
|
+
<prompt>What does it say?</prompt>
|
24
|
+
<simpleChoice identifier="ChoiceA">You must stay with your luggage at all times.</simpleChoice>
|
25
|
+
<simpleChoice identifier="ChoiceB">Do not let someone else look after your luggage.</simpleChoice>
|
26
|
+
<simpleChoice identifier="ChoiceC">Remember your luggage when you leave.</simpleChoice>
|
27
|
+
</choiceInteraction>
|
28
|
+
</itemBody>
|
29
|
+
<responseProcessing template="http://www.imsglobal.org/question/qti_v2p2/rptemplates/match_correct"/>
|
30
|
+
</assessmentItem>
|
Binary file
|
@@ -0,0 +1,101 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<!--This is a Reload version 1.3 Content Package document-->
|
3
|
+
<!--Spawned from the Reload Content Package Generator - http://www.reload.ac.uk-->
|
4
|
+
<manifest xmlns="http://www.imsglobal.org/xsd/imscp_v1p1"
|
5
|
+
xmlns:imsmd="http://ltsc.ieee.org/xsd/LOM"
|
6
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
7
|
+
xmlns:imsqti="http://www.imsglobal.org/xsd/imsqti_metadata_v2p2"
|
8
|
+
identifier="MANIFEST-85D76736-6D19-9DC0-7C0B-57C31A9FD390"
|
9
|
+
xsi:schemaLocation="http://www.imsglobal.org/xsd/imscp_v1p1 http://www.imsglobal.org/xsd/qti/qtiv2p2/qtiv2p2_imscpv1p2_v1p0.xsd
|
10
|
+
http://ltsc.ieee.org/xsd/LOM http://www.imsglobal.org/xsd/imsmd_loose_v1p3p2.xsd
|
11
|
+
http://www.imsglobal.org/xsd/imsqti_metadata_v2p2 http://www.imsglobal.org/xsd/qti/qtiv2p2/imsqti_metadata_v2p2.xsd">
|
12
|
+
<metadata>
|
13
|
+
<schema>QTIv2.2 Package</schema>
|
14
|
+
<schemaversion>1.0.0</schemaversion>
|
15
|
+
<imsmd:lom>
|
16
|
+
<imsmd:general>
|
17
|
+
<imsmd:title>
|
18
|
+
<imsmd:string>Example Package</imsmd:string>
|
19
|
+
</imsmd:title>
|
20
|
+
<imsmd:language>en</imsmd:language>
|
21
|
+
<imsmd:description>
|
22
|
+
<imsmd:string>This is an example Contentpackage containing a
|
23
|
+
single QTI v2.2 item</imsmd:string>
|
24
|
+
</imsmd:description>
|
25
|
+
</imsmd:general>
|
26
|
+
<imsmd:lifeCycle>
|
27
|
+
<imsmd:version>
|
28
|
+
<imsmd:string>2.1</imsmd:string>
|
29
|
+
</imsmd:version>
|
30
|
+
<imsmd:status>
|
31
|
+
<imsmd:source>LOMv1.0</imsmd:source>
|
32
|
+
<imsmd:value>Final</imsmd:value>
|
33
|
+
</imsmd:status>
|
34
|
+
</imsmd:lifeCycle>
|
35
|
+
<imsmd:metaMetadata>
|
36
|
+
<imsmd:metadataschema>LOMv1.0</imsmd:metadataschema>
|
37
|
+
<imsmd:metadataschema>QTIv2.1</imsmd:metadataschema>
|
38
|
+
<imsmd:language>en</imsmd:language>
|
39
|
+
</imsmd:metaMetadata>
|
40
|
+
<imsmd:technical>
|
41
|
+
<imsmd:format>text/x-imsqti-item-xml</imsmd:format>
|
42
|
+
<imsmd:format>image/png</imsmd:format>
|
43
|
+
</imsmd:technical>
|
44
|
+
<imsmd:rights>
|
45
|
+
<imsmd:description>
|
46
|
+
<imsmd:string>(c) 2005, IMS Global Learning Consortium;
|
47
|
+
individual questions may have their own copyright statements.</imsmd:string>
|
48
|
+
</imsmd:description>
|
49
|
+
</imsmd:rights>
|
50
|
+
</imsmd:lom>
|
51
|
+
</metadata>
|
52
|
+
<organizations/>
|
53
|
+
<resources>
|
54
|
+
<resource identifier="RES-B38DF83F-A291-86DA-4EC3-B2CEBD1515A4" type="imsqti_item_xmlv2p2"
|
55
|
+
href="choice.xml">
|
56
|
+
<metadata>
|
57
|
+
<imsqti:qtiMetadata>
|
58
|
+
<imsqti:timeDependent>false</imsqti:timeDependent>
|
59
|
+
<imsqti:interactionType>choiceInteraction</imsqti:interactionType>
|
60
|
+
<imsqti:feedbackType>none</imsqti:feedbackType>
|
61
|
+
<imsqti:solutionAvailable>true</imsqti:solutionAvailable>
|
62
|
+
</imsqti:qtiMetadata>
|
63
|
+
<imsmd:lom>
|
64
|
+
<imsmd:general>
|
65
|
+
<imsmd:identifier>
|
66
|
+
<imsmd:entry>choice</imsmd:entry>
|
67
|
+
</imsmd:identifier>
|
68
|
+
<imsmd:title>
|
69
|
+
<imsmd:string>Unattended Luggage</imsmd:string>
|
70
|
+
</imsmd:title>
|
71
|
+
<imsmd:description>
|
72
|
+
<imsmd:string>This example illustrates the
|
73
|
+
choiceInteraction being used to obtain a single response from the candidate.</imsmd:string>
|
74
|
+
</imsmd:description>
|
75
|
+
</imsmd:general>
|
76
|
+
<imsmd:lifeCycle>
|
77
|
+
<imsmd:version>
|
78
|
+
<imsmd:string>2.1</imsmd:string>
|
79
|
+
</imsmd:version>
|
80
|
+
<imsmd:status>
|
81
|
+
<imsmd:source>LOMv1.0</imsmd:source>
|
82
|
+
<imsmd:value>Final</imsmd:value>
|
83
|
+
</imsmd:status>
|
84
|
+
</imsmd:lifeCycle>
|
85
|
+
<imsmd:technical>
|
86
|
+
<imsmd:format>text/x-imsqti-item-xml</imsmd:format>
|
87
|
+
<imsmd:format>image/png</imsmd:format>
|
88
|
+
</imsmd:technical>
|
89
|
+
<imsmd:rights>
|
90
|
+
<imsmd:description>
|
91
|
+
<imsmd:string>This example has been adapted from the
|
92
|
+
PET Handbook, copyright University of Cambridge ESOL Examinations .</imsmd:string>
|
93
|
+
</imsmd:description>
|
94
|
+
</imsmd:rights>
|
95
|
+
</imsmd:lom>
|
96
|
+
</metadata>
|
97
|
+
<file href="choice.xml"/>
|
98
|
+
<file href="images/sign.png"/>
|
99
|
+
</resource>
|
100
|
+
</resources>
|
101
|
+
</manifest>
|
@@ -0,0 +1,50 @@
|
|
1
|
+
describe Qti::Sanitizer do
|
2
|
+
let(:sanitizer) { Qti::Sanitizer.new }
|
3
|
+
|
4
|
+
describe '#sanitize' do
|
5
|
+
it 'keeps media tags' do
|
6
|
+
expect(sanitizer.clean('<img>')).to eq('<img>')
|
7
|
+
expect(sanitizer.clean('<video>')).to eq('<video></video>')
|
8
|
+
expect(sanitizer.clean('<audio>')).to eq('<audio></audio>')
|
9
|
+
expect(sanitizer.clean('<object>')).to eq('<object></object>')
|
10
|
+
expect(sanitizer.clean('<embed>')).to eq('<embed>')
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'blocks undesirable tags do' do
|
14
|
+
expect(sanitizer.clean('<danger>')).to eq('')
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'allows needed media src attributes' do
|
18
|
+
html = '<audio src="http://a.url" data="B64" type="media" codebase="???">'
|
19
|
+
|
20
|
+
expect(sanitizer.clean(html)).to include 'src'
|
21
|
+
expect(sanitizer.clean(html)).to include 'data'
|
22
|
+
expect(sanitizer.clean(html)).to include 'type'
|
23
|
+
expect(sanitizer.clean(html)).to include 'codebase'
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'allows needed media format attributes' do
|
27
|
+
html = '<video width="12" height=14 classid="yes">'
|
28
|
+
|
29
|
+
expect(sanitizer.clean(html)).to include 'width'
|
30
|
+
expect(sanitizer.clean(html)).to include 'height'
|
31
|
+
expect(sanitizer.clean(html)).to include 'classid'
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'allows needed media extension attributes' do
|
35
|
+
html = '<object data-media-type="thing" data-media-id=123456789>'
|
36
|
+
|
37
|
+
expect(sanitizer.clean(html)).to include 'data-media-type'
|
38
|
+
expect(sanitizer.clean(html)).to include 'data-media-id'
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'allows needed media alt attributes' do
|
42
|
+
html = '<source title="Title" alt="description" allow="fullscreen" allowfullscreen=1>'
|
43
|
+
|
44
|
+
expect(sanitizer.clean(html)).to include 'title'
|
45
|
+
expect(sanitizer.clean(html)).to include 'alt'
|
46
|
+
expect(sanitizer.clean(html)).to include 'allow'
|
47
|
+
expect(sanitizer.clean(html)).to include 'allowfullscreen'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -20,6 +20,10 @@ describe Qti::V2::Models::AssessmentItem do
|
|
20
20
|
expect(loaded_class.item_body).to include 'Look at the text in the picture.'
|
21
21
|
end
|
22
22
|
|
23
|
+
it 'includes the prompt in the item_body' do
|
24
|
+
expect(loaded_class.item_body).to include 'What does it say?'
|
25
|
+
end
|
26
|
+
|
23
27
|
it 'falls back onto nil points possible value' do
|
24
28
|
expect(loaded_class.points_possible).to eq nil
|
25
29
|
end
|
@@ -43,6 +47,19 @@ describe Qti::V2::Models::AssessmentItem do
|
|
43
47
|
end
|
44
48
|
end
|
45
49
|
|
50
|
+
context 'gap_match.xml' do
|
51
|
+
let(:fixtures_path) { File.join('spec', 'fixtures') }
|
52
|
+
let(:file_path) { File.join(fixtures_path, 'items_2.1', 'gap_match.xml') }
|
53
|
+
let(:loaded_class) { described_class.from_path!(file_path) }
|
54
|
+
|
55
|
+
it 'returns the prompt as the first stem item even after calculating item_body' do
|
56
|
+
loaded_class.item_body
|
57
|
+
stem_items = loaded_class.interaction_model.stem_items
|
58
|
+
expect(stem_items.count).to equal(12)
|
59
|
+
expect(stem_items.first[:value]).to include('Identify the missing words')
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
46
63
|
context 'all test files' do
|
47
64
|
test_files = Dir.glob(File.join('spec', 'fixtures', 'items_2.1', '*.xml'))
|
48
65
|
test_files.each do |file|
|
@@ -11,11 +11,11 @@ describe Qti::V2::Models::Interactions::GapMatchInteraction do
|
|
11
11
|
value: "Identify the missing words in this famous quote from Shakespeare's Richard III.",
|
12
12
|
id: 'stem_0', position: 1 },
|
13
13
|
{ type: 'text', value: 'Now is the ', id: 'stem_1', position: 2 },
|
14
|
-
{ type: 'blank', blank_id: 'G1', id: 'stem_2', position: 3 },
|
14
|
+
{ type: 'blank', blank_id: 'G1', blank_name: 'winter', id: 'stem_2', position: 3 },
|
15
15
|
{ type: 'text', value: ' of our discontent', id: 'stem_3', position: 4 },
|
16
16
|
{ type: 'text', value: ' ', id: 'stem_4', position: 5 },
|
17
17
|
{ type: 'text', value: ' Made glorious ', id: 'stem_5', position: 6 },
|
18
|
-
{ type: 'blank', blank_id: 'G2', id: 'stem_6', position: 7 },
|
18
|
+
{ type: 'blank', blank_id: 'G2', blank_name: 'summer', id: 'stem_6', position: 7 },
|
19
19
|
{ type: 'text', value: ' by this sun of York;', id: 'stem_7', position: 8 },
|
20
20
|
{ type: 'text', value: ' ', id: 'stem_8', position: 9 },
|
21
21
|
{ type: 'text', value: " And all the clouds that lour'd\n upon our house",
|
@@ -33,6 +33,14 @@ describe Qti::V2::Models::NonAssessmentTest do
|
|
33
33
|
include_examples 'loading_a_non-assessment'
|
34
34
|
end
|
35
35
|
|
36
|
+
describe 'imsqti_2.2_package' do
|
37
|
+
let(:path) { File.join(fixtures_path, 'imsqti_2.2_package', 'imsmanifest.xml') }
|
38
|
+
let(:loaded_class) { described_class.from_path!(path) }
|
39
|
+
let(:title) { 'Example Package' }
|
40
|
+
|
41
|
+
include_examples 'loading_a_non-assessment'
|
42
|
+
end
|
43
|
+
|
36
44
|
# describe '#stimulus_ref' do
|
37
45
|
# it 'should return the stimulus ref if it exists' do
|
38
46
|
# item = loaded_class.assessment_items[1]
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: qti
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adrian Diaz
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2022-
|
15
|
+
date: 2022-12-14 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: actionview
|
@@ -54,6 +54,26 @@ dependencies:
|
|
54
54
|
- - "<"
|
55
55
|
- !ruby/object:Gem::Version
|
56
56
|
version: '6.2'
|
57
|
+
- !ruby/object:Gem::Dependency
|
58
|
+
name: dry-logic
|
59
|
+
requirement: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - "~>"
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 1.2.0
|
64
|
+
- - "<"
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '1.3'
|
67
|
+
type: :runtime
|
68
|
+
prerelease: false
|
69
|
+
version_requirements: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - "~>"
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: 1.2.0
|
74
|
+
- - "<"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '1.3'
|
57
77
|
- !ruby/object:Gem::Dependency
|
58
78
|
name: dry-struct
|
59
79
|
requirement: !ruby/object:Gem::Requirement
|
@@ -366,6 +386,9 @@ files:
|
|
366
386
|
- spec/fixtures/canvas_cartridge/non_cc_assessments/ib5281f3537a06f58877107a2501a4e2d.xml.qti
|
367
387
|
- spec/fixtures/edge_cases_1.2.xml
|
368
388
|
- spec/fixtures/feedback_quiz_1.2.xml
|
389
|
+
- spec/fixtures/imsqti_2.2_package/choice.xml
|
390
|
+
- spec/fixtures/imsqti_2.2_package/images/sign.png
|
391
|
+
- spec/fixtures/imsqti_2.2_package/imsmanifest.xml
|
369
392
|
- spec/fixtures/interaction_checks_1.2.xml
|
370
393
|
- spec/fixtures/items_1.2/canvas_multiple_dropdown.xml
|
371
394
|
- spec/fixtures/items_1.2/canvas_multiple_dropdowns.xml
|
@@ -639,6 +662,7 @@ files:
|
|
639
662
|
- spec/lib/qti/models/manifest_spec.rb
|
640
663
|
- spec/lib/qti/models/metadata_spec.rb
|
641
664
|
- spec/lib/qti/models/resource_spec.rb
|
665
|
+
- spec/lib/qti/sanitizer_spec.rb
|
642
666
|
- spec/lib/qti/v1/models/assessment_item_spec.rb
|
643
667
|
- spec/lib/qti/v1/models/assessment_spec.rb
|
644
668
|
- spec/lib/qti/v1/models/choices/fill_blank_choice_spec.rb
|
@@ -719,6 +743,9 @@ test_files:
|
|
719
743
|
- spec/fixtures/canvas_cartridge/non_cc_assessments/ib5281f3537a06f58877107a2501a4e2d.xml.qti
|
720
744
|
- spec/fixtures/edge_cases_1.2.xml
|
721
745
|
- spec/fixtures/feedback_quiz_1.2.xml
|
746
|
+
- spec/fixtures/imsqti_2.2_package/choice.xml
|
747
|
+
- spec/fixtures/imsqti_2.2_package/images/sign.png
|
748
|
+
- spec/fixtures/imsqti_2.2_package/imsmanifest.xml
|
722
749
|
- spec/fixtures/interaction_checks_1.2.xml
|
723
750
|
- spec/fixtures/items_1.2/canvas_multiple_dropdown.xml
|
724
751
|
- spec/fixtures/items_1.2/canvas_multiple_dropdowns.xml
|
@@ -992,6 +1019,7 @@ test_files:
|
|
992
1019
|
- spec/lib/qti/models/manifest_spec.rb
|
993
1020
|
- spec/lib/qti/models/metadata_spec.rb
|
994
1021
|
- spec/lib/qti/models/resource_spec.rb
|
1022
|
+
- spec/lib/qti/sanitizer_spec.rb
|
995
1023
|
- spec/lib/qti/v1/models/assessment_item_spec.rb
|
996
1024
|
- spec/lib/qti/v1/models/assessment_spec.rb
|
997
1025
|
- spec/lib/qti/v1/models/choices/fill_blank_choice_spec.rb
|