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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 01dd841b1a9215aeaddf4e9211f7e2327544067afa5fb2e24a58186f7c035115
4
- data.tar.gz: b4d688f36fb284da0ba8168463b667eb59bf612b2750ef50650230dcc129e2bb
3
+ metadata.gz: 795e15592723e240bf42bb23030ae237606cdb9c4191ce010bf241ae1d23d6f7
4
+ data.tar.gz: ce7a0e634699b57c08ce425d247b928405b36de2dc98f34435dbf6e84e7e4551
5
5
  SHA512:
6
- metadata.gz: 301bfa283539d68e2feca734a9aa97046c2604c5eedc8f656e217cdbd90949d3ddf88dcdda39ecbb1c19dde68d0b51c5f582aa9b33c3f79995109e921126dbe2
7
- data.tar.gz: 880e9ce56fe47276d833ab1b19d1f6b30646cf5a641ddce0f7e6468bb9922b05ccfd5ff140b413f7e8698c532798ade8fa50262a37e93231cfef5af5b3417ae8
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, canvas_blank_id(stem_item[1..-2]))
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
@@ -1,3 +1,3 @@
1
1
  module Qti
2
- VERSION = '2.7.1'.freeze
2
+ VERSION = '2.9.0'.freeze
3
3
  end
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.7.1
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: 2021-12-01 00:00:00.000000000 Z
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.1.4
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: