qti 2.15.4 → 2.17.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/v1/models/interactions/categorization_interaction.rb +81 -0
- data/lib/qti/v1/models/interactions/hot_spot_interaction.rb +71 -0
- data/lib/qti/v1/models/interactions.rb +3 -1
- data/lib/qti/version.rb +1 -1
- data/lib/qti.rb +2 -0
- data/spec/fixtures/items_1.2/categorization.xml +133 -0
- data/spec/fixtures/items_1.2/hot_spot.xml +124 -0
- data/spec/lib/qti/v1/models/interactions/categorization_interaction_spec.rb +62 -0
- data/spec/lib/qti/v1/models/interactions/hot_spot_interaction_spec.rb +57 -0
- metadata +12 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8ecae7fca00b12644bc2942b43f7fe47c6b959823c6b4b3a92e70193529b064f
|
|
4
|
+
data.tar.gz: b9c35c2873033a84e0b673b58e4859d434de0ea0052a4891b38e448de034759d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
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"><p>Hello there</p></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"><p>point something on the image</p></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"><p>point something on the image</p></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"><p>point something on the image</p></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.
|
|
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-
|
|
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
|