qti 2.29.0 → 2.30.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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5faafa20e505881d4e305e9346f24060883b8cf86b1861f08021f6def9329ea9
|
|
4
|
+
data.tar.gz: 69bbc14fecc3909cfc905102433e2ae1631261cf56fcda2c13035a35583a9682
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 43f8a769c16f2a2671219c7f2f3b4e25a3c8321dfed55d17035d1b3eab2238dcbae93f8b669e4393971a31c9607cfb3f2ca78ba454538bcfd279673f2f271eae
|
|
7
|
+
data.tar.gz: d51c5c66f01164d227c38ebb3098787096ce1b1257737ffe42ae70db5af39c8e09157b87c6469e7b66599803547c3bf92b6c0c9cacb667ef490e15bec86e27df
|
|
@@ -29,8 +29,35 @@ module Qti
|
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
# Numeric answers cannot be paired with their comment through a
|
|
33
|
+
# `varequal`, the way the base implementation does it: exact and
|
|
34
|
+
# precision answers nest it inside an `or`, and range answers have no
|
|
35
|
+
# single value to emit one for. Canvas instead puts each answer's
|
|
36
|
+
# `displayfeedback` in the same `respcondition` that carries its
|
|
37
|
+
# `setvar SCORE`, so pair them by that node and key the result on the
|
|
38
|
+
# position within `answer_nodes` -- the same list, in the same order,
|
|
39
|
+
# that `scoring_data_structs` is built from.
|
|
40
|
+
def answer_feedback
|
|
41
|
+
answers = answer_nodes.filter_map.with_index do |answer_node, index|
|
|
42
|
+
numeric_answer_feedback_entry(answer_node, index)
|
|
43
|
+
end
|
|
44
|
+
answers unless answers.empty?
|
|
45
|
+
end
|
|
46
|
+
|
|
32
47
|
private
|
|
33
48
|
|
|
49
|
+
def numeric_answer_feedback_entry(answer_node, index)
|
|
50
|
+
refid = answer_node.xpath('./xmlns:displayfeedback[not (@linkrefid="correct_fb" or ' \
|
|
51
|
+
'@linkrefid="general_incorrect_fb" or @linkrefid="general_fb")]').first&.[](:linkrefid)
|
|
52
|
+
feedback = get_feedback(refid)
|
|
53
|
+
return nil unless feedback
|
|
54
|
+
{
|
|
55
|
+
response_index: index,
|
|
56
|
+
texttype: feedback[:texttype],
|
|
57
|
+
feedback: feedback.text
|
|
58
|
+
}
|
|
59
|
+
end
|
|
60
|
+
|
|
34
61
|
def answer_nodes
|
|
35
62
|
@node.xpath('.//xmlns:respcondition/xmlns:setvar[@varname="SCORE"]/..')
|
|
36
63
|
end
|
data/lib/qti/version.rb
CHANGED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!--
|
|
3
|
+
Numeric per-answer feedback fixture. Question1 covers all four numeric answer
|
|
4
|
+
types (exact, precision, margin of error, range) plus item level feedback that
|
|
5
|
+
must be ignored; Question2 has comments on only the first and third of three
|
|
6
|
+
answers, covering index alignment with scoring_data_structs.
|
|
7
|
+
-->
|
|
8
|
+
<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">
|
|
9
|
+
<assessment ident="i98661073a66c6b2567c8f9b1a22c2989" title="Numeric feedback">
|
|
10
|
+
<section ident="root_section">
|
|
11
|
+
<item ident="ia63b6a115fa68a555398a58626a62f82" title="Question1">
|
|
12
|
+
<presentation>
|
|
13
|
+
<material>
|
|
14
|
+
<mattext texttype="text/html"><div><p>question 1</p></div></mattext>
|
|
15
|
+
</material>
|
|
16
|
+
<response_str ident="response1" rcardinality="Single">
|
|
17
|
+
<render_fib fibtype="Decimal">
|
|
18
|
+
<response_label ident="answer1"/>
|
|
19
|
+
</render_fib>
|
|
20
|
+
</response_str>
|
|
21
|
+
</presentation>
|
|
22
|
+
<resprocessing>
|
|
23
|
+
<outcomes>
|
|
24
|
+
<decvar maxvalue="100" minvalue="0" varname="SCORE" vartype="Decimal"/>
|
|
25
|
+
</outcomes>
|
|
26
|
+
<respcondition continue="No">
|
|
27
|
+
<conditionvar>
|
|
28
|
+
<or>
|
|
29
|
+
<varequal respident="response1">42.0</varequal>
|
|
30
|
+
<and>
|
|
31
|
+
<vargte respident="response1">42.0</vargte>
|
|
32
|
+
<varlte respident="response1">42.0</varlte>
|
|
33
|
+
</and>
|
|
34
|
+
</or>
|
|
35
|
+
</conditionvar>
|
|
36
|
+
<setvar action="Set" varname="SCORE">100</setvar>
|
|
37
|
+
<displayfeedback feedbacktype="Response" linkrefid="1001_fb"/>
|
|
38
|
+
<displayfeedback feedbacktype="Response" linkrefid="correct_fb"/>
|
|
39
|
+
</respcondition>
|
|
40
|
+
<respcondition continue="No">
|
|
41
|
+
<conditionvar>
|
|
42
|
+
<or>
|
|
43
|
+
<varequal respident="response1">1000.00</varequal>
|
|
44
|
+
<and>
|
|
45
|
+
<vargt respident="response1">999.995</vargt>
|
|
46
|
+
<varlte respident="response1">1000.005</varlte>
|
|
47
|
+
</and>
|
|
48
|
+
</or>
|
|
49
|
+
</conditionvar>
|
|
50
|
+
<setvar action="Set" varname="SCORE">100</setvar>
|
|
51
|
+
<displayfeedback feedbacktype="Response" linkrefid="1002_fb"/>
|
|
52
|
+
</respcondition>
|
|
53
|
+
<respcondition continue="No">
|
|
54
|
+
<conditionvar>
|
|
55
|
+
<or>
|
|
56
|
+
<varequal respident="response1">77.0</varequal>
|
|
57
|
+
<and>
|
|
58
|
+
<vargte respident="response1">70.0</vargte>
|
|
59
|
+
<varlte respident="response1">84.0</varlte>
|
|
60
|
+
</and>
|
|
61
|
+
</or>
|
|
62
|
+
</conditionvar>
|
|
63
|
+
<setvar action="Set" varname="SCORE">100</setvar>
|
|
64
|
+
<displayfeedback feedbacktype="Response" linkrefid="1003_fb"/>
|
|
65
|
+
</respcondition>
|
|
66
|
+
<respcondition continue="No">
|
|
67
|
+
<conditionvar>
|
|
68
|
+
<vargte respident="response1">3.9</vargte>
|
|
69
|
+
<varlte respident="response1">4.1</varlte>
|
|
70
|
+
</conditionvar>
|
|
71
|
+
<setvar action="Set" varname="SCORE">100</setvar>
|
|
72
|
+
<displayfeedback feedbacktype="Response" linkrefid="1004_fb"/>
|
|
73
|
+
</respcondition>
|
|
74
|
+
</resprocessing>
|
|
75
|
+
<itemfeedback ident="1001_fb">
|
|
76
|
+
<flow_mat>
|
|
77
|
+
<material>
|
|
78
|
+
<mattext texttype="text/html"><p>Exact answer comment</p></mattext>
|
|
79
|
+
</material>
|
|
80
|
+
</flow_mat>
|
|
81
|
+
</itemfeedback>
|
|
82
|
+
<itemfeedback ident="1002_fb">
|
|
83
|
+
<flow_mat>
|
|
84
|
+
<material>
|
|
85
|
+
<mattext texttype="text/html"><p>Precision answer comment</p></mattext>
|
|
86
|
+
</material>
|
|
87
|
+
</flow_mat>
|
|
88
|
+
</itemfeedback>
|
|
89
|
+
<itemfeedback ident="1003_fb">
|
|
90
|
+
<flow_mat>
|
|
91
|
+
<material>
|
|
92
|
+
<mattext texttype="text/html"><p>Margin of error answer comment</p></mattext>
|
|
93
|
+
</material>
|
|
94
|
+
</flow_mat>
|
|
95
|
+
</itemfeedback>
|
|
96
|
+
<itemfeedback ident="1004_fb">
|
|
97
|
+
<flow_mat>
|
|
98
|
+
<material>
|
|
99
|
+
<mattext texttype="text/html"><p>Range answer comment</p></mattext>
|
|
100
|
+
</material>
|
|
101
|
+
</flow_mat>
|
|
102
|
+
</itemfeedback>
|
|
103
|
+
<itemfeedback ident="correct_fb">
|
|
104
|
+
<flow_mat>
|
|
105
|
+
<material>
|
|
106
|
+
<mattext texttype="text/html"><p>General Correct Feedback</p></mattext>
|
|
107
|
+
</material>
|
|
108
|
+
</flow_mat>
|
|
109
|
+
</itemfeedback>
|
|
110
|
+
<itemfeedback ident="general_fb">
|
|
111
|
+
<flow_mat>
|
|
112
|
+
<material>
|
|
113
|
+
<mattext texttype="text/html"><p>General Feedback</p></mattext>
|
|
114
|
+
</material>
|
|
115
|
+
</flow_mat>
|
|
116
|
+
</itemfeedback>
|
|
117
|
+
</item>
|
|
118
|
+
<item ident="ia63b6a115fa68a555398a58626a62f83" title="Question2">
|
|
119
|
+
<presentation>
|
|
120
|
+
<material>
|
|
121
|
+
<mattext texttype="text/html"><div><p>question 2</p></div></mattext>
|
|
122
|
+
</material>
|
|
123
|
+
<response_str ident="response1" rcardinality="Single">
|
|
124
|
+
<render_fib fibtype="Decimal">
|
|
125
|
+
<response_label ident="answer1"/>
|
|
126
|
+
</render_fib>
|
|
127
|
+
</response_str>
|
|
128
|
+
</presentation>
|
|
129
|
+
<resprocessing>
|
|
130
|
+
<outcomes>
|
|
131
|
+
<decvar maxvalue="100" minvalue="0" varname="SCORE" vartype="Decimal"/>
|
|
132
|
+
</outcomes>
|
|
133
|
+
<respcondition continue="No">
|
|
134
|
+
<conditionvar>
|
|
135
|
+
<or>
|
|
136
|
+
<varequal respident="response1">10.0</varequal>
|
|
137
|
+
<and>
|
|
138
|
+
<vargte respident="response1">10.0</vargte>
|
|
139
|
+
<varlte respident="response1">10.0</varlte>
|
|
140
|
+
</and>
|
|
141
|
+
</or>
|
|
142
|
+
</conditionvar>
|
|
143
|
+
<setvar action="Set" varname="SCORE">100</setvar>
|
|
144
|
+
<displayfeedback feedbacktype="Response" linkrefid="2001_fb"/>
|
|
145
|
+
</respcondition>
|
|
146
|
+
<respcondition continue="No">
|
|
147
|
+
<conditionvar>
|
|
148
|
+
<or>
|
|
149
|
+
<varequal respident="response1">20.0</varequal>
|
|
150
|
+
<and>
|
|
151
|
+
<vargte respident="response1">20.0</vargte>
|
|
152
|
+
<varlte respident="response1">20.0</varlte>
|
|
153
|
+
</and>
|
|
154
|
+
</or>
|
|
155
|
+
</conditionvar>
|
|
156
|
+
<setvar action="Set" varname="SCORE">100</setvar>
|
|
157
|
+
</respcondition>
|
|
158
|
+
<respcondition continue="No">
|
|
159
|
+
<conditionvar>
|
|
160
|
+
<or>
|
|
161
|
+
<varequal respident="response1">30.0</varequal>
|
|
162
|
+
<and>
|
|
163
|
+
<vargte respident="response1">30.0</vargte>
|
|
164
|
+
<varlte respident="response1">30.0</varlte>
|
|
165
|
+
</and>
|
|
166
|
+
</or>
|
|
167
|
+
</conditionvar>
|
|
168
|
+
<setvar action="Set" varname="SCORE">100</setvar>
|
|
169
|
+
<displayfeedback feedbacktype="Response" linkrefid="2003_fb"/>
|
|
170
|
+
</respcondition>
|
|
171
|
+
</resprocessing>
|
|
172
|
+
<itemfeedback ident="2001_fb">
|
|
173
|
+
<flow_mat>
|
|
174
|
+
<material>
|
|
175
|
+
<mattext texttype="text/html"><p>First answer comment</p></mattext>
|
|
176
|
+
</material>
|
|
177
|
+
</flow_mat>
|
|
178
|
+
</itemfeedback>
|
|
179
|
+
<itemfeedback ident="2003_fb">
|
|
180
|
+
<flow_mat>
|
|
181
|
+
<material>
|
|
182
|
+
<mattext texttype="text/html"><p>Third answer comment</p></mattext>
|
|
183
|
+
</material>
|
|
184
|
+
</flow_mat>
|
|
185
|
+
</itemfeedback>
|
|
186
|
+
</item>
|
|
187
|
+
</section>
|
|
188
|
+
</assessment>
|
|
189
|
+
</questestinterop>
|
|
@@ -140,4 +140,62 @@ describe Qti::V1::Models::Interactions::NumericInteraction do
|
|
|
140
140
|
end
|
|
141
141
|
end
|
|
142
142
|
end
|
|
143
|
+
|
|
144
|
+
context 'answer feedback' do
|
|
145
|
+
let(:xml_file_name) { 'numeric_answer_feedback.xml' }
|
|
146
|
+
|
|
147
|
+
context 'a question with a comment on every answer type' do
|
|
148
|
+
let(:loaded_class) { described_class.new(assessment_item_refs.first, test_object) }
|
|
149
|
+
|
|
150
|
+
it 'returns a comment for the exact, precision, margin of error and range answers' do
|
|
151
|
+
expect(loaded_class.answer_feedback).to eq(
|
|
152
|
+
[
|
|
153
|
+
{ response_index: 0, texttype: 'text/html', feedback: '<p>Exact answer comment</p>' },
|
|
154
|
+
{ response_index: 1, texttype: 'text/html', feedback: '<p>Precision answer comment</p>' },
|
|
155
|
+
{ response_index: 2, texttype: 'text/html', feedback: '<p>Margin of error answer comment</p>' },
|
|
156
|
+
{ response_index: 3, texttype: 'text/html', feedback: '<p>Range answer comment</p>' }
|
|
157
|
+
]
|
|
158
|
+
)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it 'aligns every response_index with the matching scoring_data_struct' do
|
|
162
|
+
structs = loaded_class.scoring_data_structs
|
|
163
|
+
expect(structs.map(&:type)).to eq(
|
|
164
|
+
%w[exactResponse preciseResponse marginOfError withinARange]
|
|
165
|
+
)
|
|
166
|
+
expect(loaded_class.answer_feedback.map { |fb| structs[fb[:response_index]].type }).to eq(
|
|
167
|
+
%w[exactResponse preciseResponse marginOfError withinARange]
|
|
168
|
+
)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it 'ignores the item level feedback' do
|
|
172
|
+
expect(loaded_class.answer_feedback.map { |fb| fb[:feedback] }).not_to include(
|
|
173
|
+
'<p>General Correct Feedback</p>', '<p>General Feedback</p>'
|
|
174
|
+
)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
context 'a question where only some answers have a comment' do
|
|
179
|
+
let(:loaded_class) { described_class.new(assessment_item_refs[1], test_object) }
|
|
180
|
+
|
|
181
|
+
it 'keeps the index of the commented answers aligned with scoring_data_structs' do
|
|
182
|
+
expect(loaded_class.answer_feedback).to eq(
|
|
183
|
+
[
|
|
184
|
+
{ response_index: 0, texttype: 'text/html', feedback: '<p>First answer comment</p>' },
|
|
185
|
+
{ response_index: 2, texttype: 'text/html', feedback: '<p>Third answer comment</p>' }
|
|
186
|
+
]
|
|
187
|
+
)
|
|
188
|
+
expect(loaded_class.scoring_data_structs.map(&:value)).to eq(%w[10.0 20.0 30.0])
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
context 'without answer feedback' do
|
|
194
|
+
let(:xml_file_name) { 'numeric_exact_match.xml' }
|
|
195
|
+
let(:loaded_class) { described_class.new(assessment_item_refs[1], test_object) }
|
|
196
|
+
|
|
197
|
+
it 'returns nil' do
|
|
198
|
+
expect(loaded_class.answer_feedback).to be_nil
|
|
199
|
+
end
|
|
200
|
+
end
|
|
143
201
|
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.30.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: 2026-07
|
|
15
|
+
date: 2026-09-07 00:00:00.000000000 Z
|
|
16
16
|
dependencies:
|
|
17
17
|
- !ruby/object:Gem::Dependency
|
|
18
18
|
name: actionview
|
|
@@ -412,6 +412,7 @@ files:
|
|
|
412
412
|
- spec/fixtures/items_1.2/nq_multiple_fib_answer_types.xml
|
|
413
413
|
- spec/fixtures/items_1.2/nq_multiple_fib_case_sensitive.xml
|
|
414
414
|
- spec/fixtures/items_1.2/nq_multiple_fib_scoring_algorithms.xml
|
|
415
|
+
- spec/fixtures/items_1.2/numeric_answer_feedback.xml
|
|
415
416
|
- spec/fixtures/items_1.2/numeric_exact_match.xml
|
|
416
417
|
- spec/fixtures/items_1.2/numeric_margin_error.xml
|
|
417
418
|
- spec/fixtures/items_1.2/numeric_margin_error_percent.xml
|
|
@@ -797,6 +798,7 @@ test_files:
|
|
|
797
798
|
- spec/fixtures/items_1.2/nq_multiple_fib_answer_types.xml
|
|
798
799
|
- spec/fixtures/items_1.2/nq_multiple_fib_case_sensitive.xml
|
|
799
800
|
- spec/fixtures/items_1.2/nq_multiple_fib_scoring_algorithms.xml
|
|
801
|
+
- spec/fixtures/items_1.2/numeric_answer_feedback.xml
|
|
800
802
|
- spec/fixtures/items_1.2/numeric_exact_match.xml
|
|
801
803
|
- spec/fixtures/items_1.2/numeric_margin_error.xml
|
|
802
804
|
- spec/fixtures/items_1.2/numeric_margin_error_percent.xml
|