qti 2.7.1 → 2.9.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 +13 -0
- data/lib/qti/v1/models/interactions/base_fill_blank_interaction.rb +12 -2
- data/lib/qti/v1/models/interactions/base_interaction.rb +5 -0
- data/lib/qti/v1/models/object_bank.rb +6 -0
- data/lib/qti/version.rb +1 -1
- data/lib/qti.rb +9 -0
- data/spec/fixtures/items_1.2/true_false.xml +1 -1
- data/spec/lib/qti/v1/models/assessment_item_spec.rb +12 -0
- data/spec/lib/qti/v1/models/interactions/canvas_multiple_dropdown_spec.rb +2 -2
- data/spec/lib/qti/v1/models/interactions/fill_blank_interaction_spec.rb +10 -10
- data/spec/lib/qti/v1/models/object_bank_spec.rb +12 -0
- data/spec/spec_helper.rb +4 -0
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 795e15592723e240bf42bb23030ae237606cdb9c4191ce010bf241ae1d23d6f7
|
4
|
+
data.tar.gz: ce7a0e634699b57c08ce425d247b928405b36de2dc98f34435dbf6e84e7e4551
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2699fb585fa48d0ee298426ae9ecca58724f5298cc3422e0d5363acbffae04bc3e58502bd72e7cc7433571975cb0152c81887d743412678c4a79bc691dfde2e0
|
7
|
+
data.tar.gz: 8b6bd0ac53d3c5783c7ebdea6d2cabe238b37ccb0365ae8ef045207a1d9d1493ebc48f4ae13371a4ecb872ca67fdbc8d9232e45c156e6c83e70b10fa311161db
|
data/lib/qti/sanitizer.rb
CHANGED
@@ -37,6 +37,18 @@ module Qti
|
|
37
37
|
|
38
38
|
private
|
39
39
|
|
40
|
+
def convert_canvas_math_images(env)
|
41
|
+
node = env[:node]
|
42
|
+
node_name = env[:node_name]
|
43
|
+
latex = node['data-equation-content']
|
44
|
+
|
45
|
+
return if env[:is_whitelisted] || !env[:node].element?
|
46
|
+
return unless node_name == 'img'
|
47
|
+
return unless latex
|
48
|
+
|
49
|
+
node.replace("\\(#{latex}\\)")
|
50
|
+
end
|
51
|
+
|
40
52
|
def object_tag_transformer
|
41
53
|
lambda do |env|
|
42
54
|
return unless env[:node_name] == 'object'
|
@@ -73,6 +85,7 @@ module Qti
|
|
73
85
|
transformers << src_transformers
|
74
86
|
transformers << object_tag_transformer if import_objects
|
75
87
|
transformers << remap_unknown_tags_transformer
|
88
|
+
transformers << method(:convert_canvas_math_images) if Qti.configuration.extract_latex_from_image_tags
|
76
89
|
Sanitize::Config::RELAXED.merge transformers: transformers
|
77
90
|
end
|
78
91
|
|
@@ -9,7 +9,7 @@ module Qti
|
|
9
9
|
item_prompt.split(CANVAS_REGEX).map.with_index do |stem_item, index|
|
10
10
|
if canvas_fib_response_ids.include?(stem_item)
|
11
11
|
# Strip the brackets before searching
|
12
|
-
stem_blank(index,
|
12
|
+
stem_blank(index, stem_item[1..-2])
|
13
13
|
else
|
14
14
|
stem_text(index, stem_item)
|
15
15
|
end
|
@@ -21,7 +21,8 @@ module Qti
|
|
21
21
|
id: "stem_#{index}",
|
22
22
|
position: index + 1,
|
23
23
|
type: 'blank',
|
24
|
-
blank_id: value
|
24
|
+
blank_id: blank_id(value),
|
25
|
+
blank_name: value
|
25
26
|
}
|
26
27
|
end
|
27
28
|
|
@@ -34,6 +35,15 @@ module Qti
|
|
34
35
|
}
|
35
36
|
end
|
36
37
|
|
38
|
+
def blank_id(stem_item)
|
39
|
+
return stem_item unless canvas_custom_fitb?
|
40
|
+
canvas_blank_id(stem_item)
|
41
|
+
end
|
42
|
+
|
43
|
+
def canvas_custom_fitb?
|
44
|
+
@canvas_custom_fitb ||= BaseInteraction.canvas_custom_fitb?(@node)
|
45
|
+
end
|
46
|
+
|
37
47
|
def canvas_blank_id(stem_item)
|
38
48
|
blank_id = nil
|
39
49
|
node.xpath('.//xmlns:response_lid/xmlns:material').children.map do |response_lid_node|
|
@@ -9,6 +9,11 @@ module Qti
|
|
9
9
|
false
|
10
10
|
end
|
11
11
|
|
12
|
+
def self.canvas_custom_fitb?(node)
|
13
|
+
qtype = question_type(node)
|
14
|
+
%w[fill_in_multiple_blanks_question multiple_dropdowns_question].include? qtype
|
15
|
+
end
|
16
|
+
|
12
17
|
def self.canvas_multiple_fib?(node)
|
13
18
|
matches = node.xpath('.//xmlns:response_lid')
|
14
19
|
return false if matches.count < 1
|
@@ -19,6 +19,12 @@ module Qti
|
|
19
19
|
'.//xmlns:qtimetadatafield/xmlns:fieldlabel[text()="bank_type"]/../xmlns:fieldentry'
|
20
20
|
)&.content
|
21
21
|
end
|
22
|
+
|
23
|
+
def bank_context_uuid
|
24
|
+
@bank_context_uuid ||= xpath_with_single_check(
|
25
|
+
'.//xmlns:qtimetadatafield/xmlns:fieldlabel[text()="bank_context_uuid"]/../xmlns:fieldentry'
|
26
|
+
)&.content
|
27
|
+
end
|
22
28
|
end
|
23
29
|
end
|
24
30
|
end
|
data/lib/qti/version.rb
CHANGED
data/lib/qti.rb
CHANGED
@@ -4,6 +4,7 @@ require 'active_support/core_ext/string'
|
|
4
4
|
require 'active_support/core_ext/hash/except'
|
5
5
|
require 'active_support/core_ext/module/delegation'
|
6
6
|
require 'dry-struct'
|
7
|
+
require 'ostruct'
|
7
8
|
require 'find'
|
8
9
|
require 'forwardable'
|
9
10
|
require 'mathml2latex'
|
@@ -68,6 +69,14 @@ module Qti
|
|
68
69
|
@import.create_question_group(question_group_ref)
|
69
70
|
end
|
70
71
|
end
|
72
|
+
|
73
|
+
def self.configuration
|
74
|
+
@configuration ||= OpenStruct.new
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.configure
|
78
|
+
yield(configuration)
|
79
|
+
end
|
71
80
|
end
|
72
81
|
|
73
82
|
# The load order of all of these is important.
|
@@ -5,7 +5,7 @@
|
|
5
5
|
<item title="Grading - specific - 3 pt score" ident="QUE_1003">
|
6
6
|
<presentation>
|
7
7
|
<material>
|
8
|
-
<mattext texttype="text/html"><![CDATA[If I get a 3, I must have done something wrong. <img align="bottom" alt="image.png" src="org0/images/image.png" border="0"/>]]></mattext>
|
8
|
+
<mattext texttype="text/html"><![CDATA[If I get a 3, I must have done something wrong. <img data-equation-content="sample equation" script="alert('bad')" align="bottom" alt="image.png" src="org0/images/image.png" border="0"/> <img script="alert('bad')" align="bottom" alt="image.png" src="org0/images/image.png" border="0"/>]]></mattext>
|
9
9
|
</material>
|
10
10
|
<response_lid ident="QUE_1004_RL" rcardinality="Single" rtiming="No">
|
11
11
|
<render_choice shuffle="Yes">
|
@@ -29,6 +29,18 @@ describe Qti::V1::Models::AssessmentItem do
|
|
29
29
|
it 'has sanitized item_body' do
|
30
30
|
expect(loaded_class.item_body).to include '<img'
|
31
31
|
expect(loaded_class.item_body).to include 'If I get a 3, I must have done something wrong.'
|
32
|
+
expect(loaded_class.item_body).not_to include 'script="alert(\'bad\')"'
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'transforms canvas math content when conversion is enabled' do
|
36
|
+
expect(loaded_class.item_body).to include '\(sample equation\)'
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'does not transform math content when conversion is Disabled' do
|
40
|
+
Qti.configure do |config|
|
41
|
+
config.extract_latex_from_image_tags = false
|
42
|
+
end
|
43
|
+
expect(loaded_class.item_body).not_to include '"sample equation"'
|
32
44
|
end
|
33
45
|
|
34
46
|
describe '#points_possible' do
|
@@ -44,9 +44,9 @@ describe Qti::V1::Models::Interactions::CanvasMultipleDropdownInteraction do
|
|
44
44
|
let(:expected_stem_items) do
|
45
45
|
[
|
46
46
|
{ id: 'stem_0', position: 1, type: 'text', value: '<div><p>Roses are ' },
|
47
|
-
{ id: 'stem_1', position: 2, type: 'blank', blank_id: 'response_color1' },
|
47
|
+
{ id: 'stem_1', position: 2, type: 'blank', blank_id: 'response_color1', blank_name: 'color1' },
|
48
48
|
{ id: 'stem_2', position: 3, type: 'text', value: ', violets are ' },
|
49
|
-
{ id: 'stem_3', position: 4, type: 'blank', blank_id: 'response_color2' },
|
49
|
+
{ id: 'stem_3', position: 4, type: 'blank', blank_id: 'response_color2', blank_name: 'color2' },
|
50
50
|
{ id: 'stem_4', position: 5, type: 'text', value: '.</p></div>' }
|
51
51
|
]
|
52
52
|
end
|
@@ -47,7 +47,7 @@ describe Qti::V1::Models::Interactions::FillBlankInteraction do
|
|
47
47
|
let(:expected_stem_items) do
|
48
48
|
[
|
49
49
|
{ id: 'stem_0', position: 1, type: 'text', value: '<div><p>Chicago is in what state?</p></div>' },
|
50
|
-
{ id: 'stem_1', position: 2, type: 'blank', blank_id: 'response1' }
|
50
|
+
{ id: 'stem_1', position: 2, type: 'blank', blank_id: 'response1', blank_name: 'response1' }
|
51
51
|
]
|
52
52
|
end
|
53
53
|
|
@@ -72,9 +72,9 @@ describe Qti::V1::Models::Interactions::FillBlankInteraction do
|
|
72
72
|
let(:expected_stem_items) do
|
73
73
|
[
|
74
74
|
{ id: 'stem_0', position: 1, type: 'text', value: '<div><p><span>Roses are ' },
|
75
|
-
{ id: 'stem_1', position: 2, type: 'blank', blank_id: 'response_color1' },
|
75
|
+
{ id: 'stem_1', position: 2, type: 'blank', blank_id: 'response_color1', blank_name: 'color1' },
|
76
76
|
{ id: 'stem_2', position: 3, type: 'text', value: ', violets are ' },
|
77
|
-
{ id: 'stem_3', position: 4, type: 'blank', blank_id: 'response_color2' },
|
77
|
+
{ id: 'stem_3', position: 4, type: 'blank', blank_id: 'response_color2', blank_name: 'color2' },
|
78
78
|
{ id: 'stem_4', position: 5, type: 'text', value: '</span></p></div>' }
|
79
79
|
]
|
80
80
|
end
|
@@ -102,11 +102,11 @@ describe Qti::V1::Models::Interactions::FillBlankInteraction do
|
|
102
102
|
[
|
103
103
|
{ id: 'stem_0', position: 1, type: 'text', value: 'Fill-in-the blanks in this text from Richard III: ' },
|
104
104
|
{ id: 'stem_1', position: 2, type: 'text', value: 'Now is the ' },
|
105
|
-
{ id: 'stem_2', position: 3, type: 'blank', blank_id: 'FIB01' },
|
105
|
+
{ id: 'stem_2', position: 3, type: 'blank', blank_id: 'FIB01', blank_name: 'FIB01' },
|
106
106
|
{ id: 'stem_3', position: 4, type: 'text', value: ' of our discontent made glorious ' },
|
107
|
-
{ id: 'stem_4', position: 5, type: 'blank', blank_id: 'FIB02' },
|
107
|
+
{ id: 'stem_4', position: 5, type: 'blank', blank_id: 'FIB02', blank_name: 'FIB02' },
|
108
108
|
{ id: 'stem_5', position: 6, type: 'text', value: ' by these sons of ' },
|
109
|
-
{ id: 'stem_6', position: 7, type: 'blank', blank_id: 'FIB03' }
|
109
|
+
{ id: 'stem_6', position: 7, type: 'blank', blank_id: 'FIB03', blank_name: 'FIB03' }
|
110
110
|
]
|
111
111
|
end
|
112
112
|
|
@@ -165,11 +165,11 @@ describe Qti::V1::Models::Interactions::FillBlankInteraction do
|
|
165
165
|
[
|
166
166
|
{ id: 'stem_0', position: 1, type: 'text', value: 'Fill-in-the blanks in this text from Richard III: ' },
|
167
167
|
{ id: 'stem_1', position: 2, type: 'text', value: 'Now is the ' },
|
168
|
-
{ id: 'stem_2', position: 3, type: 'blank', blank_id: 'FIB01' },
|
168
|
+
{ id: 'stem_2', position: 3, type: 'blank', blank_id: 'FIB01', blank_name: 'FIB01' },
|
169
169
|
{ id: 'stem_3', position: 4, type: 'text', value: ' of our discontent made glorious ' },
|
170
|
-
{ id: 'stem_4', position: 5, type: 'blank', blank_id: 'FIB02' },
|
170
|
+
{ id: 'stem_4', position: 5, type: 'blank', blank_id: 'FIB02', blank_name: 'FIB02' },
|
171
171
|
{ id: 'stem_5', position: 6, type: 'text', value: ' by these sons of ' },
|
172
|
-
{ id: 'stem_6', position: 7, type: 'blank', blank_id: 'FIB03' }
|
172
|
+
{ id: 'stem_6', position: 7, type: 'blank', blank_id: 'FIB03', blank_name: 'FIB03' }
|
173
173
|
]
|
174
174
|
end
|
175
175
|
|
@@ -195,7 +195,7 @@ describe Qti::V1::Models::Interactions::FillBlankInteraction do
|
|
195
195
|
let(:expected_stem_items) do
|
196
196
|
[
|
197
197
|
{ id: 'stem_0', position: 1, type: 'text', value: '<div><p>Bird, bird, bird, bird is the ' },
|
198
|
-
{ id: 'stem_1', position: 2, type: 'blank', blank_id: 'response_word' },
|
198
|
+
{ id: 'stem_1', position: 2, type: 'blank', blank_id: 'response_word', blank_name: 'word' },
|
199
199
|
{ id: 'stem_2', position: 3, type: 'text', value: '</p></div>' }
|
200
200
|
]
|
201
201
|
end
|
@@ -13,6 +13,10 @@ describe Qti::V1::Models::ObjectBank do
|
|
13
13
|
<fieldlabel>bank_type</fieldlabel>
|
14
14
|
<fieldentry>account</fieldentry>
|
15
15
|
</qtimetadatafield>
|
16
|
+
<qtimetadatafield>
|
17
|
+
<fieldlabel>bank_context_uuid</fieldlabel>
|
18
|
+
<fieldentry>oAORQgMEvQquzZyKIW6Usg6CFveihQH5pOqHadsb</fieldentry>
|
19
|
+
</qtimetadatafield>
|
16
20
|
</qtimetadata>
|
17
21
|
</objectbank>
|
18
22
|
</questestinterop>
|
@@ -60,4 +64,12 @@ describe Qti::V1::Models::ObjectBank do
|
|
60
64
|
expect(objectbank.bank_type).to eq 'account'
|
61
65
|
end
|
62
66
|
end
|
67
|
+
|
68
|
+
describe '#bank_context_uuid' do
|
69
|
+
it 'has the bank_context_uuid attribute' do
|
70
|
+
allow(File).to receive(:read).and_return(doc)
|
71
|
+
objectbank = described_class.new path: '/etc/FakeBank008.xml'
|
72
|
+
expect(objectbank.bank_context_uuid).to eq 'oAORQgMEvQquzZyKIW6Usg6CFveihQH5pOqHadsb'
|
73
|
+
end
|
74
|
+
end
|
63
75
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -16,6 +16,10 @@ end
|
|
16
16
|
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
17
17
|
require 'qti'
|
18
18
|
|
19
|
+
Qti.configure do |config|
|
20
|
+
config.extract_latex_from_image_tags = true
|
21
|
+
end
|
22
|
+
|
19
23
|
Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
|
20
24
|
|
21
25
|
RSpec.configure do |config|
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: qti
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hannah Bottalla
|
8
8
|
- Robinson Rodríguez
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2022-03-30 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: actionview
|
@@ -275,7 +275,7 @@ dependencies:
|
|
275
275
|
- - "~>"
|
276
276
|
- !ruby/object:Gem::Version
|
277
277
|
version: '1.4'
|
278
|
-
description:
|
278
|
+
description:
|
279
279
|
email:
|
280
280
|
- hannah@instructure.com
|
281
281
|
- rrodriguez-bd@instructure.com
|
@@ -684,7 +684,7 @@ homepage: https://github.com/instructure/qti
|
|
684
684
|
licenses:
|
685
685
|
- MIT
|
686
686
|
metadata: {}
|
687
|
-
post_install_message:
|
687
|
+
post_install_message:
|
688
688
|
rdoc_options: []
|
689
689
|
require_paths:
|
690
690
|
- lib
|
@@ -699,8 +699,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
699
699
|
- !ruby/object:Gem::Version
|
700
700
|
version: '0'
|
701
701
|
requirements: []
|
702
|
-
rubygems_version: 3.
|
703
|
-
signing_key:
|
702
|
+
rubygems_version: 3.0.3
|
703
|
+
signing_key:
|
704
704
|
specification_version: 4
|
705
705
|
summary: QTI 1.2 and 2.1 import and export models
|
706
706
|
test_files:
|