qti 2.16.0 → 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.rb +2 -1
- data/lib/qti/version.rb +1 -1
- data/lib/qti.rb +1 -0
- data/spec/fixtures/items_1.2/categorization.xml +133 -0
- data/spec/lib/qti/v1/models/interactions/categorization_interaction_spec.rb +62 -0
- metadata +7 -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
|
@@ -17,7 +17,8 @@ module Qti
|
|
17
17
|
'calculated_question' => FormulaInteraction,
|
18
18
|
'essay_question ' => StringInteraction,
|
19
19
|
'file_upload_question' => UploadInteraction,
|
20
|
-
'hot_spot_question' => HotSpotInteraction
|
20
|
+
'hot_spot_question' => HotSpotInteraction,
|
21
|
+
'categorization_question' => CategorizationInteraction
|
21
22
|
# "text_only_question" => StimulusNoQuestion
|
22
23
|
}.freeze
|
23
24
|
|
data/lib/qti/version.rb
CHANGED
data/lib/qti.rb
CHANGED
@@ -123,6 +123,7 @@ 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'
|
128
129
|
require 'qti/v1/models/interactions/hot_spot_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,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
|
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-07-
|
15
|
+
date: 2023-07-10 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: actionview
|
@@ -332,6 +332,7 @@ 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
|
@@ -397,6 +398,7 @@ files:
|
|
397
398
|
- spec/fixtures/items_1.2/canvas_multiple_fib.xml
|
398
399
|
- spec/fixtures/items_1.2/canvas_multiple_fib_as_single.xml
|
399
400
|
- spec/fixtures/items_1.2/canvas_multiple_fib_extra_brackets.xml
|
401
|
+
- spec/fixtures/items_1.2/categorization.xml
|
400
402
|
- spec/fixtures/items_1.2/choice.xml
|
401
403
|
- spec/fixtures/items_1.2/essay.xml
|
402
404
|
- spec/fixtures/items_1.2/fib.xml
|
@@ -678,6 +680,7 @@ files:
|
|
678
680
|
- spec/lib/qti/v1/models/interactions/base_fill_blank_interaction_spec.rb
|
679
681
|
- spec/lib/qti/v1/models/interactions/base_interaction_spec.rb
|
680
682
|
- spec/lib/qti/v1/models/interactions/canvas_multiple_dropdown_spec.rb
|
683
|
+
- spec/lib/qti/v1/models/interactions/categorization_interaction_spec.rb
|
681
684
|
- spec/lib/qti/v1/models/interactions/choice_interaction_spec.rb
|
682
685
|
- spec/lib/qti/v1/models/interactions/fill_blank_interaction_spec.rb
|
683
686
|
- spec/lib/qti/v1/models/interactions/formula_interaction_spec.rb
|
@@ -762,6 +765,7 @@ test_files:
|
|
762
765
|
- spec/fixtures/items_1.2/canvas_multiple_fib.xml
|
763
766
|
- spec/fixtures/items_1.2/canvas_multiple_fib_as_single.xml
|
764
767
|
- spec/fixtures/items_1.2/canvas_multiple_fib_extra_brackets.xml
|
768
|
+
- spec/fixtures/items_1.2/categorization.xml
|
765
769
|
- spec/fixtures/items_1.2/choice.xml
|
766
770
|
- spec/fixtures/items_1.2/essay.xml
|
767
771
|
- spec/fixtures/items_1.2/fib.xml
|
@@ -1043,6 +1047,7 @@ test_files:
|
|
1043
1047
|
- spec/lib/qti/v1/models/interactions/base_fill_blank_interaction_spec.rb
|
1044
1048
|
- spec/lib/qti/v1/models/interactions/base_interaction_spec.rb
|
1045
1049
|
- spec/lib/qti/v1/models/interactions/canvas_multiple_dropdown_spec.rb
|
1050
|
+
- spec/lib/qti/v1/models/interactions/categorization_interaction_spec.rb
|
1046
1051
|
- spec/lib/qti/v1/models/interactions/choice_interaction_spec.rb
|
1047
1052
|
- spec/lib/qti/v1/models/interactions/fill_blank_interaction_spec.rb
|
1048
1053
|
- spec/lib/qti/v1/models/interactions/formula_interaction_spec.rb
|