qti 0.9.16 → 0.9.17

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
  SHA1:
3
- metadata.gz: 9a0c13169b2a41c3d077eee5defae52d8cf5d60e
4
- data.tar.gz: dff7cff54e6307c3f7d0700b85c208ad6e0e95b6
3
+ metadata.gz: '09f2878d599a454231899fde41beea26c04f38fc'
4
+ data.tar.gz: aa579b1c9d7637e0498824eed23fca4fb1d3279d
5
5
  SHA512:
6
- metadata.gz: f7ca63b2d84bc3be43df780a89b27f873b9423e371e8f3c759d4557cb63b90b42f7e8b3629bc23f9c1f9ca5790a75d73d2c30141f399f7ba68125866fa32c7e8
7
- data.tar.gz: 9a837ed2ff4f71e487784fceba65e054ea1db1007846c83f8c6158877d1df4482414bb8e2b244fea21f6f46c0dfa12b1169d5cdad186fc3eb618b682ca29ab55
6
+ metadata.gz: 1b2c438cf24947f35333a90d39a3a833d91edbea2defbe5ab5617a97625cda7a5c730af612ad7ed2e56165e5bd682e558d6c78e465e4f6e7f6e2f3fb26b97551
7
+ data.tar.gz: 1db85799a68254f20845f51477bb21cb73a0e4855665022413ecb382e44f857999fba6b73e4a2490e7289bd5fab51298a6c8d58da54e6c6d23e1181b1b3dcf21
@@ -0,0 +1,154 @@
1
+ require 'qti/v1/models/base'
2
+
3
+ module Qti
4
+ module Models
5
+ class AssessmentMeta < Qti::Models::Base
6
+ def title
7
+ sanitize_content!(tag_under_quiz('title'))
8
+ end
9
+
10
+ def description
11
+ sanitize_content!(tag_under_quiz('description'))
12
+ end
13
+
14
+ def shuffle_answers
15
+ tag_under_quiz('shuffle_answers')
16
+ end
17
+
18
+ def shuffle_answers?
19
+ string_true?(shuffle_answers)
20
+ end
21
+
22
+ def hide_results
23
+ tag_under_quiz('hide_results')
24
+ end
25
+
26
+ def scoring_policy
27
+ tag_under_quiz('scoring_policy')
28
+ end
29
+
30
+ def quiz_type
31
+ tag_under_quiz('quiz_type')
32
+ end
33
+
34
+ def points_possible_raw
35
+ tag_under_quiz('points_possible')
36
+ end
37
+
38
+ def points_possible
39
+ points_possible_raw.to_f
40
+ end
41
+
42
+ def show_correct_anwers
43
+ tag_under_quiz('show_correct_answers')
44
+ end
45
+
46
+ def anonymous_submissions
47
+ tag_under_quiz('anonymous_submissions')
48
+ end
49
+
50
+ def anonymous_submissions?
51
+ string_true?(anonymous_submissions)
52
+ end
53
+
54
+ def could_be_locked
55
+ tag_under_quiz('could_be_locked')
56
+ end
57
+
58
+ def could_be_locked?
59
+ string_true?(could_be_locked)
60
+ end
61
+
62
+ def allowed_attempts_raw
63
+ tag_under_quiz('allowed_attempts')
64
+ end
65
+
66
+ def allowed_attempts
67
+ allowed_attempts_raw.to_i
68
+ end
69
+
70
+ def one_question_at_a_time
71
+ tag_under_quiz('one_question_at_a_time')
72
+ end
73
+
74
+ def one_question_at_a_time?
75
+ one_question_at_a_time == 'true'
76
+ end
77
+
78
+ def cant_go_back
79
+ tag_under_quiz('cant_go_back')
80
+ end
81
+
82
+ def cant_go_back?
83
+ string_true?(cant_go_back)
84
+ end
85
+
86
+ def available
87
+ tag_under_quiz('available')
88
+ end
89
+
90
+ def available?
91
+ string_true?(available)
92
+ end
93
+
94
+ def one_time_results
95
+ tag_under_quiz('one_time_results')
96
+ end
97
+
98
+ def one_time_results?
99
+ string_true?(one_time_results)
100
+ end
101
+
102
+ def show_correct_answers_last_attempt
103
+ tag_under_quiz('show_correct_answers_last_attempt')
104
+ end
105
+
106
+ def show_correct_answers_last_attempt?
107
+ string_true?(show_correct_answers_last_attempt)
108
+ end
109
+
110
+ def only_visible_to_overreides
111
+ tag_under_quiz('only_visible_to_overrides')
112
+ end
113
+
114
+ def only_visible_to_overrides?
115
+ string_true?(only_visible_to_overrides)
116
+ end
117
+
118
+ def module_locked
119
+ tag_under_quiz('module_locked')
120
+ end
121
+
122
+ def module_locked?
123
+ string_true?(module_locked)
124
+ end
125
+
126
+ private
127
+
128
+ def tag_under_quiz(tag)
129
+ @doc.xpath("//xmlns:quiz/xmlns:#{tag}")&.first&.content
130
+ end
131
+
132
+ def string_true?(value)
133
+ value&.casecmp('true')&.zero?
134
+ end
135
+ end
136
+
137
+ module AssessmentMetaBase
138
+ delegate :title, :description,
139
+ :shuffle_answers?, :scoring_policy, :points_possible,
140
+ :hide_results, :quiz_type, :anonymous_submissions?,
141
+ :could_be_locked?, :allowed_attempts, :one_question_at_a_time?,
142
+ :cant_go_back?, :available?, :one_time_results?,
143
+ :show_correct_answers_attempt?, :only_visible_to_overrides?,
144
+ :module_locked?,
145
+ to: :@canvas_meta_data, prefix: :canvas, allow_nil: true
146
+
147
+ alias canvas_instructions canvas_description
148
+
149
+ def canvas_meta_data(meta_data)
150
+ @canvas_meta_data ||= meta_data
151
+ end
152
+ end
153
+ end
154
+ end
@@ -1,4 +1,5 @@
1
1
  require 'qti/v1/models/base'
2
+ require 'qti/models/assessment_meta'
2
3
  require 'qti/v1/models/assessment'
3
4
  require 'qti/v2/models/assessment_test'
4
5
  require 'qti/v2/models/non_assessment_test'
@@ -51,13 +52,15 @@ module Qti
51
52
  return embedded_non_assessment if identifier == EMBEDDED_NON_ASSESSMENT_ID
52
53
  rsc_ver = xpath_with_single_check(xpath_xmlns_resource("[@identifier='#{identifier}']"))&.[](:type)
53
54
  raise_unsupported unless rsc_ver
54
- builder = assessment_class_from_version(rsc_ver)
55
- raise_unsupported unless builder
56
- builder.from_path!(remap_href_path(asset_resource_for(identifier, rsc_ver)), @package_root)
55
+ assessment_from(rsc_ver, identifier)
57
56
  end
58
57
 
59
- def assessment_class_from_version(version)
60
- ASSESSMENT_CLASSES[version.split('/').first]
58
+ def assessment_from(version, identifier)
59
+ builder = ASSESSMENT_CLASSES[version.split('/').first]
60
+ raise_unsupported unless builder
61
+ assessment = builder.from_path!(remap_href_path(asset_resource_for(identifier, version)), @package_root)
62
+ assessment.canvas_meta_data(canvas_meta_data_for(identifier))
63
+ assessment
61
64
  end
62
65
 
63
66
  def asset_resource_for(identifier, qti_type)
@@ -65,6 +68,18 @@ module Qti
65
68
  xmlns_resource(base_xpath + '/@href') || xmlns_resource(base_xpath + '/xmlns:file/@href')
66
69
  end
67
70
 
71
+ def dependency_id(identifier)
72
+ xmlns_resource("[@identifier='#{identifier}']/xmlns:dependency/@identifierref")
73
+ end
74
+
75
+ def canvas_meta_data_for(identifier)
76
+ dep_id = dependency_id(identifier)
77
+ meta_file = xmlns_resource(
78
+ "[@identifier='#{dep_id}']/xmlns:file[#{xpath_endswith('@href', 'assessment_meta.xml')}]/@href"
79
+ )
80
+ return Qti::Models::AssessmentMeta.from_path!(File.join(@package_root, meta_file)) if meta_file
81
+ end
82
+
68
83
  def xmlns_resource(type)
69
84
  xpath_with_single_check(xpath_xmlns_resource(type))&.content
70
85
  end
@@ -81,12 +96,15 @@ module Qti
81
96
  "//xmlns:resources/xmlns:resource#{type}"
82
97
  end
83
98
 
99
+ def xpath_endswith(tag, tail)
100
+ "substring(#{tag}, string-length(#{tag}) - string-length('#{tail}') + 1) = '#{tail}'"
101
+ end
102
+
84
103
  def rtype_predicate(ver, rsc_type)
85
104
  # XPath 2.0 supports ends-with, which is what substring is doing here.
86
105
  # It also support regex matching with matches.
87
106
  # We only have XPath 1.0 available.
88
- cc_match = "starts-with(@type, '#{ver}') and " \
89
- "substring(@type, string-length(@type) - string-length('#{rsc_type}') + 1) = '#{rsc_type}'"
107
+ cc_match = "starts-with(@type, '#{ver}') and " + xpath_endswith('@type', rsc_type)
90
108
  qti_match = "@type='#{ver}'"
91
109
  "#{qti_match} or (#{cc_match})"
92
110
  end
@@ -1,9 +1,11 @@
1
1
  require 'qti/v1/models/base'
2
+ require 'qti/models/assessment_meta'
2
3
 
3
4
  module Qti
4
5
  module V1
5
6
  module Models
6
7
  class Assessment < Qti::V1::Models::Base
8
+ include Qti::Models::AssessmentMetaBase
7
9
  def title
8
10
  @title ||= xpath_with_single_check('.//xmlns:assessment/@title')&.content || File.basename(@path, '.xml')
9
11
  end
@@ -24,7 +24,7 @@ module Qti
24
24
  node.xpath('.//xmlns:var_set').map do |anode|
25
25
  {
26
26
  inputs: vars_at_node(anode),
27
- output: anode.at_xpath('.//xmlns:answer').text
27
+ output: anode.at_xpath('.//xmlns:answer')&.text&.to_f
28
28
  }
29
29
  end
30
30
  end
@@ -66,7 +66,7 @@ module Qti
66
66
  parent_node.xpath('.//xmlns:var').map do |vnode|
67
67
  {
68
68
  name: vnode.attributes['name']&.value,
69
- value: vnode.text
69
+ value: vnode.text&.to_f
70
70
  }
71
71
  end
72
72
  end
@@ -1,9 +1,11 @@
1
1
  require 'qti/v2/models/base'
2
+ require 'qti/models/assessment_meta'
2
3
 
3
4
  module Qti
4
5
  module V2
5
6
  module Models
6
7
  class AssessmentTest < Qti::V2::Models::Base
8
+ include Qti::Models::AssessmentMetaBase
7
9
  def title
8
10
  @title ||= xpath_with_single_check('//xmlns:assessmentTest/@title')&.content || File.basename(@path, '.xml')
9
11
  end
@@ -1,3 +1,3 @@
1
1
  module Qti
2
- VERSION = '0.9.16'.freeze
2
+ VERSION = '0.9.17'.freeze
3
3
  end
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ../..
3
3
  specs:
4
- qti (0.9.16)
4
+ qti (0.9.17)
5
5
  actionview (>= 4.2.0)
6
6
  activesupport (>= 4.2.9, < 5.2)
7
7
  dry-struct (~> 0.2.1)
@@ -114,7 +114,7 @@ GEM
114
114
  unicode-display_width (~> 1.0, >= 1.0.1)
115
115
  ruby-progressbar (1.9.0)
116
116
  rubyzip (1.2.1)
117
- sanitize (4.6.4)
117
+ sanitize (4.6.5)
118
118
  crass (~> 1.0.2)
119
119
  nokogiri (>= 1.4.4)
120
120
  nokogumbo (~> 1.4)
@@ -146,4 +146,4 @@ DEPENDENCIES
146
146
  wwtd (~> 1.3)
147
147
 
148
148
  BUNDLED WITH
149
- 1.16.1
149
+ 1.16.2
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ../..
3
3
  specs:
4
- qti (0.9.16)
4
+ qti (0.9.17)
5
5
  actionview (>= 4.2.0)
6
6
  activesupport (>= 4.2.9, < 5.2)
7
7
  dry-struct (~> 0.2.1)
@@ -114,7 +114,7 @@ GEM
114
114
  unicode-display_width (~> 1.0, >= 1.0.1)
115
115
  ruby-progressbar (1.9.0)
116
116
  rubyzip (1.2.1)
117
- sanitize (4.6.4)
117
+ sanitize (4.6.5)
118
118
  crass (~> 1.0.2)
119
119
  nokogiri (>= 1.4.4)
120
120
  nokogumbo (~> 1.4)
@@ -146,4 +146,4 @@ DEPENDENCIES
146
146
  wwtd (~> 1.3)
147
147
 
148
148
  BUNDLED WITH
149
- 1.16.1
149
+ 1.16.2
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ../..
3
3
  specs:
4
- qti (0.9.16)
4
+ qti (0.9.17)
5
5
  actionview (>= 4.2.0)
6
6
  activesupport (>= 4.2.9, < 5.2)
7
7
  dry-struct (~> 0.2.1)
@@ -165,7 +165,7 @@ GEM
165
165
  unicode-display_width (~> 1.0, >= 1.0.1)
166
166
  ruby-progressbar (1.9.0)
167
167
  rubyzip (1.2.1)
168
- sanitize (4.6.4)
168
+ sanitize (4.6.5)
169
169
  crass (~> 1.0.2)
170
170
  nokogiri (>= 1.4.4)
171
171
  nokogumbo (~> 1.4)
@@ -205,4 +205,4 @@ DEPENDENCIES
205
205
  wwtd (~> 1.3)
206
206
 
207
207
  BUNDLED WITH
208
- 1.16.1
208
+ 1.16.2
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ../..
3
3
  specs:
4
- qti (0.9.16)
4
+ qti (0.9.17)
5
5
  actionview (>= 4.2.0)
6
6
  activesupport (>= 4.2.9, < 5.2)
7
7
  dry-struct (~> 0.2.1)
@@ -168,7 +168,7 @@ GEM
168
168
  unicode-display_width (~> 1.0, >= 1.0.1)
169
169
  ruby-progressbar (1.9.0)
170
170
  rubyzip (1.2.1)
171
- sanitize (4.6.4)
171
+ sanitize (4.6.5)
172
172
  crass (~> 1.0.2)
173
173
  nokogiri (>= 1.4.4)
174
174
  nokogumbo (~> 1.4)
@@ -211,4 +211,4 @@ DEPENDENCIES
211
211
  wwtd (~> 1.3)
212
212
 
213
213
  BUNDLED WITH
214
- 1.16.1
214
+ 1.16.2
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ context 'Canvas Assessment Meta Data' do
4
+ let(:fixtures_path) { File.join('spec', 'fixtures') }
5
+ let(:file_path) { File.join(fixtures_path, qti_file) }
6
+ let(:assessment) { Qti::Importer.new(file_path).test_object }
7
+
8
+ shared_examples 'loads canvas meta data' do
9
+ it 'creates a vaild assessment' do
10
+ expect(assessment).to be_kind_of(assessment_type)
11
+ expect(assessment.canvas_title).to eq(title)
12
+ expect(assessment.canvas_description).to eq(instructions)
13
+ expect(assessment.canvas_instructions).to eq(instructions)
14
+ expect(assessment.canvas_points_possible).to eq(points_possible)
15
+ expect(assessment.canvas_one_question_at_a_time?).to eq(oqaat)
16
+ expect(assessment.canvas_allowed_attempts).to eq(allowed_attempts)
17
+ expect(assessment.canvas_hide_results).to eq(hide_results)
18
+ expect(assessment.canvas_quiz_type).to eq(quiz_type)
19
+ expect(assessment.canvas_anonymous_submissions?).to eq(anon_submissions)
20
+ expect(assessment.canvas_could_be_locked?).to eq(could_be_locked)
21
+ expect(assessment.canvas_cant_go_back?).to eq(nobacktracking)
22
+ expect(assessment.canvas_available?).to eq(available)
23
+ expect(assessment.canvas_one_time_results?).to eq(one_time_results)
24
+ expect(assessment.canvas_scoring_policy).to eq(scoring_policy)
25
+ expect(assessment.canvas_shuffle_answers?).to eq(shuffle_answers)
26
+ end
27
+ end
28
+
29
+ describe 'in a QTI 1.2 container' do
30
+ let(:qti_file) { 'test_qti_1.2_canvas' }
31
+ let(:assessment_type) { Qti::V1::Models::Assessment }
32
+ let(:title) { 'WIJsfijdi' }
33
+ let(:instructions) { '<p>sdvsv</p>' }
34
+ let(:points_possible) { 4.0 }
35
+ let(:oqaat) { false }
36
+ let(:allowed_attempts) { 1 }
37
+ let(:hide_results) { nil }
38
+ let(:quiz_type) { 'assignment' }
39
+ let('anon_submissions') { false }
40
+ let(:could_be_locked) { false }
41
+ let(:nobacktracking) { false }
42
+ let(:available) { true }
43
+ let(:one_time_results) { false }
44
+ let(:scoring_policy) { 'keep_highest' }
45
+ let(:shuffle_answers) { false }
46
+
47
+ include_examples('loads canvas meta data')
48
+ end
49
+
50
+ describe 'in a Common Cartridge container' do
51
+ let(:qti_file) { 'test_imscc_canvas' }
52
+ let(:assessment_type) { Qti::V1::Models::Assessment }
53
+ let(:title) { 'Quiz #1' }
54
+ let(:instructions) { '' }
55
+ let(:points_possible) { 1.0 }
56
+ let(:oqaat) { false }
57
+ let(:allowed_attempts) { 1 }
58
+ let(:hide_results) { '' }
59
+ let(:quiz_type) { 'assignment' }
60
+ let('anon_submissions') { false }
61
+ let(:could_be_locked) { false }
62
+ let(:nobacktracking) { false }
63
+ let(:available) { true }
64
+ let(:one_time_results) { false }
65
+ let(:scoring_policy) { 'keep_highest' }
66
+ let(:shuffle_answers) { false }
67
+
68
+ include_examples('loads canvas meta data')
69
+ end
70
+ end
@@ -38,7 +38,7 @@ describe Qti::V1::Models::Interactions::FormulaInteraction do
38
38
  context 'Simple Formula' do
39
39
  let(:file_path) { File.join(fixtures_path, 'formula.xml') }
40
40
 
41
- let(:scoring_data_values) { %w[9.0 9.0 6.0 2.0 3.0] }
41
+ let(:scoring_data_values) { [9.0, 9.0, 6.0, 2.0, 3.0] }
42
42
 
43
43
  let(:answer_tolerance) { '0' }
44
44
  let(:margin_of_error) { { margin: '0', margin_type: 'absolute' } }
@@ -58,7 +58,7 @@ describe Qti::V1::Models::Interactions::FormulaInteraction do
58
58
  context 'Multiple variable formula' do
59
59
  let(:file_path) { File.join(fixtures_path, 'formula_mvar.xml') }
60
60
 
61
- let(:scoring_data_values) { %w[16.0 7.0 14.0 5.0 7.0 14.0 11.0 7.0 20.0 13.0] }
61
+ let(:scoring_data_values) { [16.0, 7.0, 14.0, 5.0, 7.0, 14.0, 11.0, 7.0, 20.0, 13.0] }
62
62
 
63
63
  let(:answer_tolerance) { '0' }
64
64
  let(:margin_of_error) { { margin: '0', margin_type: 'absolute' } }
@@ -81,7 +81,7 @@ describe Qti::V1::Models::Interactions::FormulaInteraction do
81
81
  context 'Multiple Formula Steps' do
82
82
  let(:file_path) { File.join(fixtures_path, 'formula_mform.xml') }
83
83
 
84
- let(:scoring_data_values) { %w[82.58 23.72 39.75 46.25 41.03] }
84
+ let(:scoring_data_values) { [82.58, 23.72, 39.75, 46.25, 41.03] }
85
85
 
86
86
  let(:answer_tolerance) { '10%' }
87
87
  let(:margin_of_error) { { margin: '10', margin_type: 'percent' } }
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: 0.9.16
4
+ version: 0.9.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hannah Bottalla
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-05-15 00:00:00.000000000 Z
12
+ date: 2018-05-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -288,6 +288,7 @@ files:
288
288
  - lib/qti/content_packaging/outcome_declaration.rb
289
289
  - lib/qti/content_packaging/simple_choice.rb
290
290
  - lib/qti/exporter.rb
291
+ - lib/qti/models/assessment_meta.rb
291
292
  - lib/qti/models/base.rb
292
293
  - lib/qti/models/manifest.rb
293
294
  - lib/qti/v1/models/assessment.rb
@@ -1209,6 +1210,7 @@ files:
1209
1210
  - spec/gemfiles/sanitize-4.5.gemfile.lock
1210
1211
  - spec/lib/qti/assessment_item_exporter_spec.rb
1211
1212
  - spec/lib/qti/exporter_spec.rb
1213
+ - spec/lib/qti/models/assessment_meta_spec.rb
1212
1214
  - spec/lib/qti/models/base_spec.rb
1213
1215
  - spec/lib/qti/models/manifest_spec.rb
1214
1216
  - spec/lib/qti/v1/models/assessment_item_spec.rb
@@ -2147,6 +2149,7 @@ test_files:
2147
2149
  - spec/gemfiles/sanitize-4.5.gemfile.lock
2148
2150
  - spec/lib/qti/assessment_item_exporter_spec.rb
2149
2151
  - spec/lib/qti/exporter_spec.rb
2152
+ - spec/lib/qti/models/assessment_meta_spec.rb
2150
2153
  - spec/lib/qti/models/base_spec.rb
2151
2154
  - spec/lib/qti/models/manifest_spec.rb
2152
2155
  - spec/lib/qti/v1/models/assessment_item_spec.rb