qti 2.15.4 → 2.16.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: f4084fe51b2f6fe08be8575473463acf9654f015d2e314ac7dc5b0cc66bbc7a0
4
- data.tar.gz: 7aef361ec589ca5656e4bb6c0850593e3a0014e1b5e59c06a3f84cc762ca4b49
3
+ metadata.gz: c06cc4e78f29ecb23354e39dc3e93ac5da9600f0b09284a59b1202c553eb0c93
4
+ data.tar.gz: b91ded981174a7822c7b6756f69e4a30daf5c6845f004a9a05da4a3f23a6af28
5
5
  SHA512:
6
- metadata.gz: fccaad1a37691535248d7f2f6c8f587c9cbf319f4db2abd918f682e4a78a9a8351ab3b42e75a2e78f38557babbd437de049bcfb8c4d6b8aea8ed3a407a6355ab
7
- data.tar.gz: 31746c6c8de63f0d854a329b9932245c36b19fb32dbb0e1183d8ff20f04c2ca2f93bf4fd63192083c8cbdd67e749bc46c9cc07fc4b50809ba0d4015f56bf8a5e
6
+ metadata.gz: afcb1b453f47cec30c61b8d56d0a80109b6660a9f71b1e863db23cf8a3f6f79095dffd1512dad87554ba316d512574391dfe1f87da8c4bfa4efbbe74f95fcd2f
7
+ data.tar.gz: a5acbfc53c6508b9d50e795435476b9aa72e32390f739697fd319dc4e4777037db4f7af5b70acbdc698fadcc5e0251af22e2ea7907cbf5b5ee8a3bed298a0b4b
@@ -0,0 +1,71 @@
1
+ module Qti
2
+ module V1
3
+ module Models
4
+ module Interactions
5
+ class HotSpotInteraction < BaseInteraction
6
+ def self.matches(node, parent)
7
+ presentation = node.at_xpath('.//xmlns:presentation')
8
+ return false unless presentation
9
+ xy_responses = presentation.xpath('.//xmlns:response_xy')
10
+ return false unless xy_responses.count.positive?
11
+ new(node, parent)
12
+ end
13
+
14
+ def item_body
15
+ @item_body ||= begin
16
+ node = @node.dup
17
+ presentation = node.at_xpath('.//xmlns:presentation')
18
+ mattext = presentation.at_xpath('.//xmlns:mattext')
19
+ inner_content = return_inner_content!(mattext)
20
+ sanitize_content!(inner_content)
21
+ end
22
+ end
23
+
24
+ def image_url
25
+ @image_url ||= begin
26
+ node = @node.dup
27
+ presentation = node.at_xpath('.//xmlns:presentation')
28
+ matimage = presentation.at_xpath('.//xmlns:matimage')
29
+ matimage&.attributes&.[]('uri')&.value
30
+ end
31
+ end
32
+
33
+ def shape_type
34
+ @shape_type ||= response_label&.attributes&.[]('rarea')&.value
35
+ end
36
+
37
+ def coordinates
38
+ @coordinates ||= begin
39
+ raw_coordinates_array = raw_coordinates.split(',')
40
+ return [] unless (raw_coordinates_array.length % 2).zero?
41
+
42
+ raw_coordinates_array.each_slice(2).map do |point|
43
+ { x: point[0].to_f, y: point[1].to_f }
44
+ end
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def response_label
51
+ @response_label ||= begin
52
+ node = @node.dup
53
+ presentation = node.at_xpath('.//xmlns:presentation')
54
+ presentation.at_xpath('.//xmlns:response_label')
55
+ end
56
+ end
57
+
58
+ def raw_coordinates
59
+ @raw_coordinates ||= response_label&.text || ''
60
+ end
61
+
62
+ def coordinate_hash(coordinate_node)
63
+ %w[x y].map do |axis|
64
+ [axis.to_sym, coordinate_node&.attributes&.[](axis)&.value&.to_f]
65
+ end.to_h
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -16,7 +16,8 @@ module Qti
16
16
  'numerical_question' => NumericInteraction,
17
17
  'calculated_question' => FormulaInteraction,
18
18
  'essay_question ' => StringInteraction,
19
- 'file_upload_question' => UploadInteraction
19
+ 'file_upload_question' => UploadInteraction,
20
+ 'hot_spot_question' => HotSpotInteraction
20
21
  # "text_only_question" => StimulusNoQuestion
21
22
  }.freeze
22
23
 
data/lib/qti/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Qti
2
- VERSION = '2.15.4'.freeze
2
+ VERSION = '2.16.0'.freeze
3
3
  end
data/lib/qti.rb CHANGED
@@ -125,6 +125,7 @@ require 'qti/v1/models/interactions/base_fill_blank_interaction'
125
125
  require 'qti/v1/models/interactions/canvas_multiple_dropdown'
126
126
  require 'qti/v1/models/interactions/fill_blank_interaction'
127
127
  require 'qti/v1/models/interactions/formula_interaction'
128
+ require 'qti/v1/models/interactions/hot_spot_interaction'
128
129
  require 'qti/v1/models/interactions/match_interaction'
129
130
  require 'qti/v1/models/interactions/numeric_interaction'
130
131
  require 'qti/v1/models/interactions/ordering_interaction'
@@ -0,0 +1,124 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <questestinterop xmlns="http://www.imsglobal.org/xsd/ims_qtiasiv1p2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/ims_qtiasiv1p2 http://www.imsglobal.org/xsd/ims_qtiasiv1p2p1.xsd">
3
+ <assessment ident="id2b817165c302c0c57416146dc94ce47" title="Quiz with hot-spot question">
4
+ <qtimetadata>
5
+ <qtimetadatafield>
6
+ <fieldlabel>cc_maxattempts</fieldlabel>
7
+ <fieldentry>1</fieldentry>
8
+ </qtimetadatafield>
9
+ </qtimetadata>
10
+ <section ident="root_section">
11
+ <item ident="g515255148f5a3cae1832584071aa8528" title="this is a hot-spot question">
12
+ <itemmetadata>
13
+ <qtimetadata>
14
+ <qtimetadatafield>
15
+ <fieldlabel>question_type</fieldlabel>
16
+ <fieldentry>hot_spot_question</fieldentry>
17
+ </qtimetadatafield>
18
+ <qtimetadatafield>
19
+ <fieldlabel>points_possible</fieldlabel>
20
+ <fieldentry>1.0</fieldentry>
21
+ </qtimetadatafield>
22
+ <qtimetadatafield>
23
+ <fieldlabel>original_answer_ids</fieldlabel>
24
+ <fieldentry/>
25
+ </qtimetadatafield>
26
+ <qtimetadatafield>
27
+ <fieldlabel>assessment_question_identifierref</fieldlabel>
28
+ <fieldentry>gd6ff15355f3dd3f532816fa268230130</fieldentry>
29
+ </qtimetadatafield>
30
+ </qtimetadata>
31
+ </itemmetadata>
32
+ <presentation>
33
+ <flow>
34
+ <response_xy ident="response1" rcardinality="Single" rtiming="No">
35
+ <material>
36
+ <mattext texttype="text/html">&lt;p&gt;point something on the image&lt;/p&gt;</mattext>
37
+ </material>
38
+ <render_hotspot>
39
+ <material>
40
+ <matimage uri="$IMS-CC-FILEBASE$/Uploaded Media/someuuid"/>
41
+ </material>
42
+ <response_label ident="response1" rarea="square">0.372,0.1376,0.616,0.4256</response_label>
43
+ </render_hotspot>
44
+ </response_xy>
45
+ </flow>
46
+ </presentation>
47
+ </item>
48
+ <item ident="g515255148f5a3cae1832584071aa8529" title="hot-spot with invalid coordinates">
49
+ <itemmetadata>
50
+ <qtimetadata>
51
+ <qtimetadatafield>
52
+ <fieldlabel>question_type</fieldlabel>
53
+ <fieldentry>hot_spot_question</fieldentry>
54
+ </qtimetadatafield>
55
+ <qtimetadatafield>
56
+ <fieldlabel>points_possible</fieldlabel>
57
+ <fieldentry>1.0</fieldentry>
58
+ </qtimetadatafield>
59
+ <qtimetadatafield>
60
+ <fieldlabel>original_answer_ids</fieldlabel>
61
+ <fieldentry/>
62
+ </qtimetadatafield>
63
+ <qtimetadatafield>
64
+ <fieldlabel>assessment_question_identifierref</fieldlabel>
65
+ <fieldentry>gd6ff15355f3dd3f532816fa268230130</fieldentry>
66
+ </qtimetadatafield>
67
+ </qtimetadata>
68
+ </itemmetadata>
69
+ <presentation>
70
+ <flow>
71
+ <response_xy ident="response1" rcardinality="Single" rtiming="No">
72
+ <material>
73
+ <mattext texttype="text/html">&lt;p&gt;point something on the image&lt;/p&gt;</mattext>
74
+ </material>
75
+ <render_hotspot>
76
+ <material>
77
+ <matimage uri="$IMS-CC-FILEBASE$/Uploaded Media/someuuid"/>
78
+ </material>
79
+ <response_label ident="response1" rarea="square">0.372</response_label>
80
+ </render_hotspot>
81
+ </response_xy>
82
+ </flow>
83
+ </presentation>
84
+ </item>
85
+ <item ident="g515255148f5a3cae1832584071aa8529" title="hot-spot without coordinates">
86
+ <itemmetadata>
87
+ <qtimetadata>
88
+ <qtimetadatafield>
89
+ <fieldlabel>question_type</fieldlabel>
90
+ <fieldentry>hot_spot_question</fieldentry>
91
+ </qtimetadatafield>
92
+ <qtimetadatafield>
93
+ <fieldlabel>points_possible</fieldlabel>
94
+ <fieldentry>1.0</fieldentry>
95
+ </qtimetadatafield>
96
+ <qtimetadatafield>
97
+ <fieldlabel>original_answer_ids</fieldlabel>
98
+ <fieldentry/>
99
+ </qtimetadatafield>
100
+ <qtimetadatafield>
101
+ <fieldlabel>assessment_question_identifierref</fieldlabel>
102
+ <fieldentry>gd6ff15355f3dd3f532816fa268230130</fieldentry>
103
+ </qtimetadatafield>
104
+ </qtimetadata>
105
+ </itemmetadata>
106
+ <presentation>
107
+ <flow>
108
+ <response_xy ident="response1" rcardinality="Single" rtiming="No">
109
+ <material>
110
+ <mattext texttype="text/html">&lt;p&gt;point something on the image&lt;/p&gt;</mattext>
111
+ </material>
112
+ <render_hotspot>
113
+ <material>
114
+ <matimage uri="$IMS-CC-FILEBASE$/Uploaded Media/someuuid"/>
115
+ </material>
116
+ <response_label ident="response1" rarea="square"></response_label>
117
+ </render_hotspot>
118
+ </response_xy>
119
+ </flow>
120
+ </presentation>
121
+ </item>
122
+ </section>
123
+ </assessment>
124
+ </questestinterop>
@@ -0,0 +1,57 @@
1
+ describe Qti::V1::Models::Interactions::HotSpotInteraction do
2
+ let(:xml_file_name) { 'hot_spot.xml' }
3
+ let(:fixtures_path) { File.join('spec', 'fixtures', 'items_1.2') }
4
+ let(:file_path) { File.join(fixtures_path, xml_file_name) }
5
+ let(:test_object) { Qti::V1::Models::Assessment.from_path!(file_path) }
6
+ let(:assessment_item_refs) { test_object.assessment_items }
7
+
8
+ describe '.matches' do
9
+ it 'matches the item in file' do
10
+ expect(described_class.matches(test_object.assessment_items, test_object)).to be_truthy
11
+ end
12
+ end
13
+
14
+ describe '#item_body' do
15
+ let(:loaded_class) { described_class.new(assessment_item_refs.first, test_object) }
16
+ it 'returns correct item body' do
17
+ expect(loaded_class.item_body).to eq '<p>point something on the image</p>'
18
+ end
19
+ end
20
+
21
+ describe '#image_url' do
22
+ let(:loaded_class) { described_class.new(assessment_item_refs.first, test_object) }
23
+ it 'returns correct image url' do
24
+ expect(loaded_class.image_url).to eq '$IMS-CC-FILEBASE$/Uploaded Media/someuuid'
25
+ end
26
+ end
27
+
28
+ describe '#shape_type' do
29
+ let(:loaded_class) { described_class.new(assessment_item_refs.first, test_object) }
30
+ it 'returns correct shape type' do
31
+ expect(loaded_class.shape_type).to eq 'square'
32
+ end
33
+ end
34
+
35
+ describe '#coordinates' do
36
+ let(:loaded_class) { described_class.new(assessment_item_refs.first, test_object) }
37
+ it 'returns correct coordinates' do
38
+ expect(loaded_class.coordinates).to eq [{ "x": 0.372, "y": 0.1376 }, { "x": 0.616, "y": 0.4256 }]
39
+ end
40
+
41
+ context 'when the response label does not have an even amount of numbers' do
42
+ let(:loaded_class) { described_class.new(assessment_item_refs[1], test_object) }
43
+
44
+ it 'returns an empty array' do
45
+ expect(loaded_class.coordinates).to eq []
46
+ end
47
+ end
48
+
49
+ context 'when the response label is empty' do
50
+ let(:loaded_class) { described_class.new(assessment_item_refs[2], test_object) }
51
+
52
+ it 'returns an empty array' do
53
+ expect(loaded_class.coordinates).to eq []
54
+ end
55
+ end
56
+ end
57
+ end
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.15.4
4
+ version: 2.16.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: 2023-06-27 00:00:00.000000000 Z
15
+ date: 2023-07-06 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: actionview
@@ -335,6 +335,7 @@ files:
335
335
  - lib/qti/v1/models/interactions/choice_interaction.rb
336
336
  - lib/qti/v1/models/interactions/fill_blank_interaction.rb
337
337
  - lib/qti/v1/models/interactions/formula_interaction.rb
338
+ - lib/qti/v1/models/interactions/hot_spot_interaction.rb
338
339
  - lib/qti/v1/models/interactions/match_interaction.rb
339
340
  - lib/qti/v1/models/interactions/numeric_interaction.rb
340
341
  - lib/qti/v1/models/interactions/ordering_interaction.rb
@@ -404,6 +405,7 @@ files:
404
405
  - spec/fixtures/items_1.2/formula.xml
405
406
  - spec/fixtures/items_1.2/formula_mform.xml
406
407
  - spec/fixtures/items_1.2/formula_mvar.xml
408
+ - spec/fixtures/items_1.2/hot_spot.xml
407
409
  - spec/fixtures/items_1.2/item_no_title.xml
408
410
  - spec/fixtures/items_1.2/matching.xml
409
411
  - spec/fixtures/items_1.2/matching_feedback.xml
@@ -679,6 +681,7 @@ files:
679
681
  - spec/lib/qti/v1/models/interactions/choice_interaction_spec.rb
680
682
  - spec/lib/qti/v1/models/interactions/fill_blank_interaction_spec.rb
681
683
  - spec/lib/qti/v1/models/interactions/formula_interaction_spec.rb
684
+ - spec/lib/qti/v1/models/interactions/hot_spot_interaction_spec.rb
682
685
  - spec/lib/qti/v1/models/interactions/match_interaction_spec.rb
683
686
  - spec/lib/qti/v1/models/interactions/numeric_interaction_spec.rb
684
687
  - spec/lib/qti/v1/models/interactions/ordering_interaction_spec.rb
@@ -767,6 +770,7 @@ test_files:
767
770
  - spec/fixtures/items_1.2/formula.xml
768
771
  - spec/fixtures/items_1.2/formula_mform.xml
769
772
  - spec/fixtures/items_1.2/formula_mvar.xml
773
+ - spec/fixtures/items_1.2/hot_spot.xml
770
774
  - spec/fixtures/items_1.2/item_no_title.xml
771
775
  - spec/fixtures/items_1.2/matching.xml
772
776
  - spec/fixtures/items_1.2/matching_feedback.xml
@@ -1042,6 +1046,7 @@ test_files:
1042
1046
  - spec/lib/qti/v1/models/interactions/choice_interaction_spec.rb
1043
1047
  - spec/lib/qti/v1/models/interactions/fill_blank_interaction_spec.rb
1044
1048
  - spec/lib/qti/v1/models/interactions/formula_interaction_spec.rb
1049
+ - spec/lib/qti/v1/models/interactions/hot_spot_interaction_spec.rb
1045
1050
  - spec/lib/qti/v1/models/interactions/match_interaction_spec.rb
1046
1051
  - spec/lib/qti/v1/models/interactions/numeric_interaction_spec.rb
1047
1052
  - spec/lib/qti/v1/models/interactions/ordering_interaction_spec.rb