qti 2.15.4 → 2.17.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f4084fe51b2f6fe08be8575473463acf9654f015d2e314ac7dc5b0cc66bbc7a0
4
- data.tar.gz: 7aef361ec589ca5656e4bb6c0850593e3a0014e1b5e59c06a3f84cc762ca4b49
3
+ metadata.gz: 8ecae7fca00b12644bc2942b43f7fe47c6b959823c6b4b3a92e70193529b064f
4
+ data.tar.gz: b9c35c2873033a84e0b673b58e4859d434de0ea0052a4891b38e448de034759d
5
5
  SHA512:
6
- metadata.gz: fccaad1a37691535248d7f2f6c8f587c9cbf319f4db2abd918f682e4a78a9a8351ab3b42e75a2e78f38557babbd437de049bcfb8c4d6b8aea8ed3a407a6355ab
7
- data.tar.gz: 31746c6c8de63f0d854a329b9932245c36b19fb32dbb0e1183d8ff20f04c2ca2f93bf4fd63192083c8cbdd67e749bc46c9cc07fc4b50809ba0d4015f56bf8a5e
6
+ metadata.gz: c7ec25c08693f1b467121f8d528859c99ff58d57ad9eb55d44fb31871210a6d04f0184517db425ce62509ab3ea00700ea6203e1d4ae3323de4b4eaad3577f0a1
7
+ data.tar.gz: aa5a807baa86a4554c52a83c0d2c16c7421c82ef1bb5a4c680cfc1e2f4cb9c5bbcea79936d1bc02984fb0bb74d6c4b6d44a8d1bdc0b8d7f4272f49fc27249018
@@ -0,0 +1,81 @@
1
+ module Qti
2
+ module V1
3
+ module Models
4
+ module Interactions
5
+ class CategorizationInteraction < BaseInteraction
6
+ def self.matches(node, parent)
7
+ return false if canvas_multiple_fib?(node)
8
+ matches = node.xpath('.//xmlns:response_lid')
9
+ return false if matches.count <= 1
10
+ rcardinality = matches.first.attributes['rcardinality']&.value || 'Single'
11
+ return false if rcardinality != 'Multiple'
12
+ new(node, parent)
13
+ end
14
+
15
+ def item_body
16
+ @item_body ||= begin
17
+ node = @node.dup
18
+ presentation = node.at_xpath('.//xmlns:presentation')
19
+ mattext = presentation.at_xpath('.//xmlns:mattext')
20
+ inner_content = return_inner_content!(mattext)
21
+ sanitize_content!(inner_content)
22
+ end
23
+ end
24
+
25
+ def categories
26
+ node.xpath('.//xmlns:response_lid').map do |lid_node|
27
+ mattext = lid_node.at_xpath('.//xmlns:mattext')
28
+ inner_content = return_inner_content!(mattext)
29
+ item_body = sanitize_content!(inner_content)
30
+ category_id = lid_node.attributes['ident'].value
31
+ [category_id, { id: category_id, item_body: item_body }]
32
+ end.to_h
33
+ end
34
+
35
+ def distractors
36
+ @distractors ||= choices.reduce({}) do |acc, answer|
37
+ acc.update answer.identifier => answer.item_body
38
+ end
39
+ end
40
+
41
+ def scoring_data_structs
42
+ @scoring_data_structs ||= parse_scoring_data
43
+ end
44
+
45
+ private
46
+
47
+ def parse_scoring_data
48
+ # This preserves the original behavior while not breaking on item feedback
49
+ path = './/xmlns:respcondition/xmlns:setvar/../xmlns:conditionvar/xmlns:varequal'
50
+ raw_scoring_data = node.xpath(path).reduce({}) do |acc, node|
51
+ category_id = node.attributes['respident'].value
52
+ values = (acc[category_id] || []) + [node.text]
53
+ acc.update category_id => values
54
+ end
55
+
56
+ raw_scoring_data.map do |id, values|
57
+ Models::ScoringData.new(values, rcardinality, id: id)
58
+ end
59
+ end
60
+
61
+ def choices
62
+ @choices ||= answer_nodes.map do |node|
63
+ V1::Models::Choices::LogicalIdentifierChoice.new(node, self)
64
+ end
65
+ end
66
+
67
+ def answer_nodes
68
+ responses = []
69
+ response_ids = Set.new
70
+ node.xpath('.//xmlns:response_label').each do |answer_node|
71
+ ident = answer_node.attributes['ident'].value
72
+ responses << answer_node unless response_ids.include? ident
73
+ response_ids << ident
74
+ end
75
+ responses
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -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,9 @@ 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,
21
+ 'categorization_question' => CategorizationInteraction
20
22
  # "text_only_question" => StimulusNoQuestion
21
23
  }.freeze
22
24
 
data/lib/qti/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Qti
2
- VERSION = '2.15.4'.freeze
2
+ VERSION = '2.17.0'.freeze
3
3
  end
data/lib/qti.rb CHANGED
@@ -123,8 +123,10 @@ require 'qti/v1/models/interactions/choice_interaction'
123
123
 
124
124
  require 'qti/v1/models/interactions/base_fill_blank_interaction'
125
125
  require 'qti/v1/models/interactions/canvas_multiple_dropdown'
126
+ require 'qti/v1/models/interactions/categorization_interaction'
126
127
  require 'qti/v1/models/interactions/fill_blank_interaction'
127
128
  require 'qti/v1/models/interactions/formula_interaction'
129
+ require 'qti/v1/models/interactions/hot_spot_interaction'
128
130
  require 'qti/v1/models/interactions/match_interaction'
129
131
  require 'qti/v1/models/interactions/numeric_interaction'
130
132
  require 'qti/v1/models/interactions/ordering_interaction'
@@ -0,0 +1,133 @@
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="d74b47a4abeeed159e030bfecbcecea2" title="quiz with categorization question">
12
+ <itemmetadata>
13
+ <qtimetadata>
14
+ <qtimetadatafield>
15
+ <fieldlabel>question_type</fieldlabel>
16
+ <fieldentry>categorization_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>9f47365e112840d80a79a960b1b0b6a7</fieldentry>
29
+ </qtimetadatafield>
30
+ </qtimetadata>
31
+ </itemmetadata>
32
+ <presentation>
33
+ <material>
34
+ <mattext texttype="text/html">&lt;p&gt;Hello there&lt;/p&gt;</mattext>
35
+ </material>
36
+ <response_lid ident="df4fb8d7-8033-445a-b8be-171dd9b973ba" rcardinality="Multiple">
37
+ <material>
38
+ <mattext texttype="text/plain">Category_A</mattext>
39
+ </material>
40
+ <render_choice>
41
+ <response_label ident="19e37ce3-3a8b-4e26-ab61-9770e58af00e">
42
+ <material>
43
+ <mattext texttype="text/plain">Distractor2</mattext>
44
+ </material>
45
+ </response_label>
46
+ <response_label ident="3b8dcef8-c8c1-4827-85b0-6e381739adbf">
47
+ <material>
48
+ <mattext texttype="text/plain">B1</mattext>
49
+ </material>
50
+ </response_label>
51
+ <response_label ident="53a97aa7-d55c-402c-a061-f85d837c7c77">
52
+ <material>
53
+ <mattext texttype="text/plain">A2</mattext>
54
+ </material>
55
+ </response_label>
56
+ <response_label ident="5a67c627-cf85-4b70-922a-c7d1b7c720b8">
57
+ <material>
58
+ <mattext texttype="text/plain">B2</mattext>
59
+ </material>
60
+ </response_label>
61
+ <response_label ident="5d78f3f5-28c2-4e71-9c33-70c6b301e017">
62
+ <material>
63
+ <mattext texttype="text/plain">Distractor1</mattext>
64
+ </material>
65
+ </response_label>
66
+ <response_label ident="9432e8cb-1d3e-4d04-89b5-95185b7e557d">
67
+ <material>
68
+ <mattext texttype="text/plain">A1</mattext>
69
+ </material>
70
+ </response_label>
71
+ </render_choice>
72
+ </response_lid>
73
+ <response_lid ident="eb2f823d-b857-4c06-9d5f-998d14ee8d58" rcardinality="Multiple">
74
+ <material>
75
+ <mattext texttype="text/plain">Category_B</mattext>
76
+ </material>
77
+ <render_choice>
78
+ <response_label ident="19e37ce3-3a8b-4e26-ab61-9770e58af00e">
79
+ <material>
80
+ <mattext texttype="text/plain">Distractor2</mattext>
81
+ </material>
82
+ </response_label>
83
+ <response_label ident="3b8dcef8-c8c1-4827-85b0-6e381739adbf">
84
+ <material>
85
+ <mattext texttype="text/plain">B1</mattext>
86
+ </material>
87
+ </response_label>
88
+ <response_label ident="53a97aa7-d55c-402c-a061-f85d837c7c77">
89
+ <material>
90
+ <mattext texttype="text/plain">A2</mattext>
91
+ </material>
92
+ </response_label>
93
+ <response_label ident="5a67c627-cf85-4b70-922a-c7d1b7c720b8">
94
+ <material>
95
+ <mattext texttype="text/plain">B2</mattext>
96
+ </material>
97
+ </response_label>
98
+ <response_label ident="5d78f3f5-28c2-4e71-9c33-70c6b301e017">
99
+ <material>
100
+ <mattext texttype="text/plain">Distractor1</mattext>
101
+ </material>
102
+ </response_label>
103
+ <response_label ident="9432e8cb-1d3e-4d04-89b5-95185b7e557d">
104
+ <material>
105
+ <mattext texttype="text/plain">A1</mattext>
106
+ </material>
107
+ </response_label>
108
+ </render_choice>
109
+ </response_lid>
110
+ </presentation>
111
+ <resprocessing>
112
+ <outcomes>
113
+ <decvar maxvalue="100" minvalue="0" varname="SCORE" vartype="Decimal"/>
114
+ </outcomes>
115
+ <respcondition>
116
+ <conditionvar>
117
+ <varequal respident="df4fb8d7-8033-445a-b8be-171dd9b973ba">9432e8cb-1d3e-4d04-89b5-95185b7e557d</varequal>
118
+ <varequal respident="df4fb8d7-8033-445a-b8be-171dd9b973ba">53a97aa7-d55c-402c-a061-f85d837c7c77</varequal>
119
+ </conditionvar>
120
+ <setvar action="Add" varname="SCORE">50.00</setvar>
121
+ </respcondition>
122
+ <respcondition>
123
+ <conditionvar>
124
+ <varequal respident="eb2f823d-b857-4c06-9d5f-998d14ee8d58">3b8dcef8-c8c1-4827-85b0-6e381739adbf</varequal>
125
+ <varequal respident="eb2f823d-b857-4c06-9d5f-998d14ee8d58">5a67c627-cf85-4b70-922a-c7d1b7c720b8</varequal>
126
+ </conditionvar>
127
+ <setvar action="Add" varname="SCORE">50.00</setvar>
128
+ </respcondition>
129
+ </resprocessing>
130
+ </item>
131
+ </section>
132
+ </assessment>
133
+ </questestinterop>
@@ -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,62 @@
1
+ describe Qti::V1::Models::Interactions::CategorizationInteraction do
2
+ let(:xml_file_name) { 'categorization.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
+ let(:item) { assessment_item_refs.first }
8
+
9
+ describe '.matches' do
10
+ it 'matches the item in file' do
11
+ expect(described_class.matches(item, test_object)).to be_truthy
12
+ end
13
+ end
14
+
15
+ describe '#item_body' do
16
+ let(:loaded_class) { described_class.new(item, test_object) }
17
+ it 'returns correct item body' do
18
+ expect(loaded_class.item_body).to eq '<p>Hello there</p>'
19
+ end
20
+ end
21
+
22
+ describe '#categories' do
23
+ let(:loaded_class) { described_class.new(item, test_object) }
24
+ it 'returns correct categories' do
25
+ expected_categories =
26
+ {
27
+ 'df4fb8d7-8033-445a-b8be-171dd9b973ba' => { id: 'df4fb8d7-8033-445a-b8be-171dd9b973ba',
28
+ item_body: 'Category_A' },
29
+ 'eb2f823d-b857-4c06-9d5f-998d14ee8d58' => { id: 'eb2f823d-b857-4c06-9d5f-998d14ee8d58',
30
+ item_body: 'Category_B' }
31
+ }
32
+ expect(loaded_class.categories).to eq(expected_categories)
33
+ end
34
+ end
35
+
36
+ describe '#distractors' do
37
+ let(:loaded_class) { described_class.new(item, test_object) }
38
+ it 'returns the correct distractors' do
39
+ expected_distractors =
40
+ { '19e37ce3-3a8b-4e26-ab61-9770e58af00e' => 'Distractor2',
41
+ '3b8dcef8-c8c1-4827-85b0-6e381739adbf' => 'B1',
42
+ '53a97aa7-d55c-402c-a061-f85d837c7c77' => 'A2',
43
+ '5a67c627-cf85-4b70-922a-c7d1b7c720b8' => 'B2',
44
+ '5d78f3f5-28c2-4e71-9c33-70c6b301e017' => 'Distractor1',
45
+ '9432e8cb-1d3e-4d04-89b5-95185b7e557d' => 'A1' }
46
+ expect(loaded_class.distractors).to eq(expected_distractors)
47
+ end
48
+ end
49
+
50
+ describe '#scoring_data_structs' do
51
+ let(:loaded_class) { described_class.new(item, test_object) }
52
+ it 'returns the correct scoring data structs' do
53
+ scoring_data_ids = %w[df4fb8d7-8033-445a-b8be-171dd9b973ba eb2f823d-b857-4c06-9d5f-998d14ee8d58]
54
+ scoring_data_values = [
55
+ %w[9432e8cb-1d3e-4d04-89b5-95185b7e557d 53a97aa7-d55c-402c-a061-f85d837c7c77],
56
+ %w[3b8dcef8-c8c1-4827-85b0-6e381739adbf 5a67c627-cf85-4b70-922a-c7d1b7c720b8]
57
+ ]
58
+ expect(loaded_class.scoring_data_structs.map(&:id)).to eq(scoring_data_ids)
59
+ expect(loaded_class.scoring_data_structs.map(&:values)).to eq(scoring_data_values)
60
+ end
61
+ end
62
+ end
@@ -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.17.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-10 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: actionview
@@ -332,9 +332,11 @@ files:
332
332
  - lib/qti/v1/models/interactions/base_fill_blank_interaction.rb
333
333
  - lib/qti/v1/models/interactions/base_interaction.rb
334
334
  - lib/qti/v1/models/interactions/canvas_multiple_dropdown.rb
335
+ - lib/qti/v1/models/interactions/categorization_interaction.rb
335
336
  - lib/qti/v1/models/interactions/choice_interaction.rb
336
337
  - lib/qti/v1/models/interactions/fill_blank_interaction.rb
337
338
  - lib/qti/v1/models/interactions/formula_interaction.rb
339
+ - lib/qti/v1/models/interactions/hot_spot_interaction.rb
338
340
  - lib/qti/v1/models/interactions/match_interaction.rb
339
341
  - lib/qti/v1/models/interactions/numeric_interaction.rb
340
342
  - lib/qti/v1/models/interactions/ordering_interaction.rb
@@ -396,6 +398,7 @@ files:
396
398
  - spec/fixtures/items_1.2/canvas_multiple_fib.xml
397
399
  - spec/fixtures/items_1.2/canvas_multiple_fib_as_single.xml
398
400
  - spec/fixtures/items_1.2/canvas_multiple_fib_extra_brackets.xml
401
+ - spec/fixtures/items_1.2/categorization.xml
399
402
  - spec/fixtures/items_1.2/choice.xml
400
403
  - spec/fixtures/items_1.2/essay.xml
401
404
  - spec/fixtures/items_1.2/fib.xml
@@ -404,6 +407,7 @@ files:
404
407
  - spec/fixtures/items_1.2/formula.xml
405
408
  - spec/fixtures/items_1.2/formula_mform.xml
406
409
  - spec/fixtures/items_1.2/formula_mvar.xml
410
+ - spec/fixtures/items_1.2/hot_spot.xml
407
411
  - spec/fixtures/items_1.2/item_no_title.xml
408
412
  - spec/fixtures/items_1.2/matching.xml
409
413
  - spec/fixtures/items_1.2/matching_feedback.xml
@@ -676,9 +680,11 @@ files:
676
680
  - spec/lib/qti/v1/models/interactions/base_fill_blank_interaction_spec.rb
677
681
  - spec/lib/qti/v1/models/interactions/base_interaction_spec.rb
678
682
  - spec/lib/qti/v1/models/interactions/canvas_multiple_dropdown_spec.rb
683
+ - spec/lib/qti/v1/models/interactions/categorization_interaction_spec.rb
679
684
  - spec/lib/qti/v1/models/interactions/choice_interaction_spec.rb
680
685
  - spec/lib/qti/v1/models/interactions/fill_blank_interaction_spec.rb
681
686
  - spec/lib/qti/v1/models/interactions/formula_interaction_spec.rb
687
+ - spec/lib/qti/v1/models/interactions/hot_spot_interaction_spec.rb
682
688
  - spec/lib/qti/v1/models/interactions/match_interaction_spec.rb
683
689
  - spec/lib/qti/v1/models/interactions/numeric_interaction_spec.rb
684
690
  - spec/lib/qti/v1/models/interactions/ordering_interaction_spec.rb
@@ -759,6 +765,7 @@ test_files:
759
765
  - spec/fixtures/items_1.2/canvas_multiple_fib.xml
760
766
  - spec/fixtures/items_1.2/canvas_multiple_fib_as_single.xml
761
767
  - spec/fixtures/items_1.2/canvas_multiple_fib_extra_brackets.xml
768
+ - spec/fixtures/items_1.2/categorization.xml
762
769
  - spec/fixtures/items_1.2/choice.xml
763
770
  - spec/fixtures/items_1.2/essay.xml
764
771
  - spec/fixtures/items_1.2/fib.xml
@@ -767,6 +774,7 @@ test_files:
767
774
  - spec/fixtures/items_1.2/formula.xml
768
775
  - spec/fixtures/items_1.2/formula_mform.xml
769
776
  - spec/fixtures/items_1.2/formula_mvar.xml
777
+ - spec/fixtures/items_1.2/hot_spot.xml
770
778
  - spec/fixtures/items_1.2/item_no_title.xml
771
779
  - spec/fixtures/items_1.2/matching.xml
772
780
  - spec/fixtures/items_1.2/matching_feedback.xml
@@ -1039,9 +1047,11 @@ test_files:
1039
1047
  - spec/lib/qti/v1/models/interactions/base_fill_blank_interaction_spec.rb
1040
1048
  - spec/lib/qti/v1/models/interactions/base_interaction_spec.rb
1041
1049
  - spec/lib/qti/v1/models/interactions/canvas_multiple_dropdown_spec.rb
1050
+ - spec/lib/qti/v1/models/interactions/categorization_interaction_spec.rb
1042
1051
  - spec/lib/qti/v1/models/interactions/choice_interaction_spec.rb
1043
1052
  - spec/lib/qti/v1/models/interactions/fill_blank_interaction_spec.rb
1044
1053
  - spec/lib/qti/v1/models/interactions/formula_interaction_spec.rb
1054
+ - spec/lib/qti/v1/models/interactions/hot_spot_interaction_spec.rb
1045
1055
  - spec/lib/qti/v1/models/interactions/match_interaction_spec.rb
1046
1056
  - spec/lib/qti/v1/models/interactions/numeric_interaction_spec.rb
1047
1057
  - spec/lib/qti/v1/models/interactions/ordering_interaction_spec.rb