qti 1.0.3 → 1.0.4
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.rb +1 -1
- data/lib/qti/models/base.rb +10 -4
- data/lib/qti/models/manifest.rb +9 -87
- data/lib/qti/models/metadata.rb +63 -0
- data/lib/qti/models/resource.rb +115 -0
- data/lib/qti/v1/models/assessment.rb +1 -1
- data/lib/qti/v1/models/assessment_item.rb +4 -1
- data/lib/qti/v2/models/assessment_test.rb +5 -3
- data/lib/qti/v2/models/non_assessment_test.rb +7 -10
- data/lib/qti/version.rb +1 -1
- data/lib/qti/xpath_helpers.rb +20 -0
- data/spec/fixtures/test_qti_2.1_taxonpath/imsmanifest.xml +85 -0
- data/spec/lib/qti/models/metadata_spec.rb +94 -0
- data/spec/lib/qti/models/resource_spec.rb +53 -0
- data/spec/lib/qti/v2/models/assessment_test_spec.rb +2 -2
- data/spec/lib/qti_spec.rb +1 -1
- metadata +12 -9
- data/spec/gemfiles/nokogiri-1.6.gemfile.lock +0 -149
- data/spec/gemfiles/nokogiri-1.8.gemfile.lock +0 -149
- data/spec/gemfiles/rails-5.2.gemfile.lock +0 -222
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 852302a8eb0d02d23199e435c997d027f720c476
|
4
|
+
data.tar.gz: c52b00f23b75fc6b0c0d14ccf1bf3e1309b7016b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0cb5f6a63677f60d18ed90dd6729c061f2a64189168f8ffb7bc8ee93693c938ec9fbfa102c0f1ccda4996c2db97b066d38d01c9aa1c40e4a8001f1f63d6ea114
|
7
|
+
data.tar.gz: 436eeb7b20019d9f95b61df809993f4b2293fbd06598dd8e2f70d2bb29177093080a2da5633dc4f395e92b968ead921abb0c7a9cf969de8b924146811e138b2a
|
data/lib/qti.rb
CHANGED
@@ -27,7 +27,7 @@ module Qti
|
|
27
27
|
def self.manifest(path)
|
28
28
|
mpath = manifest_path(path)
|
29
29
|
package_root = File.dirname(mpath)
|
30
|
-
manifest = Qti::Models::Manifest.from_path!(mpath, package_root)
|
30
|
+
manifest = Qti::Models::Manifest.from_path!(mpath, package_root = package_root)
|
31
31
|
[mpath, package_root, manifest]
|
32
32
|
end
|
33
33
|
|
data/lib/qti/models/base.rb
CHANGED
@@ -10,19 +10,21 @@ module Qti
|
|
10
10
|
|
11
11
|
module Models
|
12
12
|
class Base
|
13
|
-
attr_reader :doc, :path, :package_root
|
13
|
+
attr_reader :doc, :path, :package_root, :resource
|
14
14
|
attr_accessor :manifest
|
15
|
+
delegate :metadata, to: :@resource, allow_nil: true
|
15
16
|
|
16
17
|
def sanitize_content!(html)
|
17
18
|
sanitizer.clean(html)
|
18
19
|
end
|
19
20
|
|
20
|
-
def self.from_path!(path, package_root = nil)
|
21
|
-
new(path: path, package_root: package_root)
|
21
|
+
def self.from_path!(path, package_root = nil, resource = nil)
|
22
|
+
new(path: path, package_root: package_root, resource: resource)
|
22
23
|
end
|
23
24
|
|
24
|
-
def initialize(path:, package_root: nil, html: false)
|
25
|
+
def initialize(path:, package_root: nil, html: false, resource: nil)
|
25
26
|
@path = path
|
27
|
+
@resource = resource
|
26
28
|
self.package_root = package_root || File.dirname(path)
|
27
29
|
@doc = html ? parse_html(File.read(path)) : parse_xml(File.read(path))
|
28
30
|
raise ArgumentError unless @doc
|
@@ -76,6 +78,10 @@ module Qti
|
|
76
78
|
path
|
77
79
|
end
|
78
80
|
|
81
|
+
def raise_unsupported(message = 'Unsupported QTI version')
|
82
|
+
raise Qti::UnsupportedSchema, message
|
83
|
+
end
|
84
|
+
|
79
85
|
protected
|
80
86
|
|
81
87
|
def package_root=(package_root)
|
data/lib/qti/models/manifest.rb
CHANGED
@@ -3,22 +3,13 @@ require 'qti/models/assessment_meta'
|
|
3
3
|
require 'qti/v1/models/assessment'
|
4
4
|
require 'qti/v2/models/assessment_test'
|
5
5
|
require 'qti/v2/models/non_assessment_test'
|
6
|
+
require 'qti/models/resource'
|
6
7
|
|
7
8
|
module Qti
|
8
9
|
module Models
|
9
10
|
class Manifest < Qti::Models::Base
|
10
|
-
|
11
|
-
|
12
|
-
imsqti_xmlv1p2].freeze
|
13
|
-
ASSESSMENT_CLASSES = {
|
14
|
-
'imsqti_xmlv1p2' => Qti::V1::Models::Assessment,
|
15
|
-
'imsqti_test_xmlv2p1' => Qti::V2::Models::AssessmentTest,
|
16
|
-
'imsqti_test_xmlv2p2' => Qti::V2::Models::AssessmentTest
|
17
|
-
}.freeze
|
18
|
-
EMBEDDED_QTI_TYPES = %w[imsqti_item_xmlv2p1
|
19
|
-
imsqti_item_xmlv2p2].freeze
|
20
|
-
EMBEDDED_NON_ASSESSMENT_ID = '@embedded_non_assessment'.freeze
|
21
|
-
|
11
|
+
include Qti::Models::ResourceGroup
|
12
|
+
include Qti::XPathHelpers
|
22
13
|
def assessment_test(resource_id = nil)
|
23
14
|
resource_id ||= assessment_identifiers.first
|
24
15
|
test = assessment_from_identifier(resource_id)
|
@@ -26,31 +17,11 @@ module Qti
|
|
26
17
|
test
|
27
18
|
end
|
28
19
|
|
29
|
-
def raise_unsupported
|
30
|
-
raise Qti::UnsupportedSchema, 'Unsupported QTI version'
|
31
|
-
end
|
32
|
-
|
33
|
-
def assessment_identifiers(embedded_as_assessment = true)
|
34
|
-
id_list = identifier_list('/assessment')
|
35
|
-
return id_list + [EMBEDDED_NON_ASSESSMENT_ID] if embedded_as_assessment && embedded_non_assessment?
|
36
|
-
id_list
|
37
|
-
end
|
38
|
-
|
39
|
-
def question_bank_identifiers
|
40
|
-
identifier_list('/question-bank')
|
41
|
-
end
|
42
|
-
|
43
|
-
def identifier_list(rsc_type)
|
44
|
-
RESOURCE_QTI_TYPES.map do |v|
|
45
|
-
xmlns_resource_list("[#{rtype_predicate(v, rsc_type)}]").map { |r| r[:identifier] }
|
46
|
-
end.flatten
|
47
|
-
end
|
48
|
-
|
49
20
|
private
|
50
21
|
|
51
22
|
def assessment_from_identifier(identifier)
|
52
23
|
return embedded_non_assessment if identifier == EMBEDDED_NON_ASSESSMENT_ID
|
53
|
-
rsc_ver = xpath_with_single_check(
|
24
|
+
rsc_ver = xpath_with_single_check(xpath_resource("[@identifier='#{identifier}']"))&.[](:type)
|
54
25
|
raise_unsupported unless rsc_ver
|
55
26
|
assessment_from(rsc_ver, identifier)
|
56
27
|
end
|
@@ -58,73 +29,24 @@ module Qti
|
|
58
29
|
def assessment_from(version, identifier)
|
59
30
|
builder = ASSESSMENT_CLASSES[version.split('/').first]
|
60
31
|
raise_unsupported unless builder
|
61
|
-
|
62
|
-
assessment = builder.from_path!(
|
63
|
-
|
64
|
-
@package_root
|
65
|
-
)
|
66
|
-
assessment.canvas_meta_data(canvas_meta_data_for(identifier))
|
32
|
+
rsc = resource_for(identifier, version)
|
33
|
+
assessment = builder.from_path!(remap_href_path(asset_resource_for(rsc)), @package_root, rsc)
|
34
|
+
assessment.canvas_meta_data(rsc.canvas_metadata)
|
67
35
|
assessment
|
68
36
|
end
|
69
37
|
|
70
|
-
def asset_resource_for(identifier, qti_type, canvas_ident)
|
71
|
-
asset_resource_for_canvas(canvas_ident) || asset_resource_for_ims(identifier, qti_type)
|
72
|
-
end
|
73
|
-
|
74
|
-
def asset_resource_for_canvas(identifier)
|
75
|
-
canvas_extra_file(identifier, '.xml.qti')
|
76
|
-
end
|
77
|
-
|
78
|
-
def asset_resource_for_ims(identifier, qti_type)
|
79
|
-
base_xpath = "[@identifier='#{identifier}' and starts-with(@type, '#{qti_type}')]"
|
80
|
-
xmlns_resource(base_xpath + '/@href') || xmlns_resource(base_xpath + '/xmlns:file/@href')
|
81
|
-
end
|
82
|
-
|
83
|
-
def dependency_id(identifier)
|
84
|
-
xmlns_resource("[@identifier='#{identifier}']/xmlns:dependency/@identifierref")
|
85
|
-
end
|
86
|
-
|
87
|
-
def canvas_meta_data_for(identifier)
|
88
|
-
meta_file = canvas_extra_file(identifier, 'assessment_meta.xml')
|
89
|
-
return Qti::Models::AssessmentMeta.from_path!(File.join(@package_root, meta_file)) if meta_file
|
90
|
-
end
|
91
|
-
|
92
|
-
def canvas_extra_file(identifier, filename)
|
93
|
-
dep_id = dependency_id(identifier)
|
94
|
-
xmlns_resource(
|
95
|
-
"[@identifier='#{dep_id}']/xmlns:file[#{xpath_endswith('@href', filename)}]/@href"
|
96
|
-
)
|
97
|
-
end
|
98
|
-
|
99
38
|
def xmlns_resource(type)
|
100
|
-
xpath_with_single_check(
|
39
|
+
xpath_with_single_check(xpath_resource(type))
|
101
40
|
end
|
102
41
|
|
103
42
|
def xmlns_resource_list(type)
|
104
|
-
@doc.xpath(
|
43
|
+
@doc.xpath(xpath_resource(type))
|
105
44
|
end
|
106
45
|
|
107
46
|
def xmlns_resource_count(type)
|
108
47
|
xmlns_resource_list(type).count
|
109
48
|
end
|
110
49
|
|
111
|
-
def xpath_xmlns_resource(type = '')
|
112
|
-
"//xmlns:resources/xmlns:resource#{type}"
|
113
|
-
end
|
114
|
-
|
115
|
-
def xpath_endswith(tag, tail)
|
116
|
-
"substring(#{tag}, string-length(#{tag}) - string-length('#{tail}') + 1) = '#{tail}'"
|
117
|
-
end
|
118
|
-
|
119
|
-
def rtype_predicate(ver, rsc_type)
|
120
|
-
# XPath 2.0 supports ends-with, which is what substring is doing here.
|
121
|
-
# It also support regex matching with matches.
|
122
|
-
# We only have XPath 1.0 available.
|
123
|
-
cc_match = "starts-with(@type, '#{ver}') and " + xpath_endswith('@type', rsc_type)
|
124
|
-
qti_match = "@type='#{ver}'"
|
125
|
-
"#{qti_match} or (#{cc_match})"
|
126
|
-
end
|
127
|
-
|
128
50
|
def embedded_non_assessment?
|
129
51
|
EMBEDDED_QTI_TYPES.map { |typ| xmlns_resource_count("[@type='#{typ}']/@href") }.flatten.sum.positive?
|
130
52
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Qti
|
2
|
+
module Models
|
3
|
+
class MetaData < Qti::Models::Base
|
4
|
+
def initialize(node)
|
5
|
+
@node = node
|
6
|
+
end
|
7
|
+
|
8
|
+
def taxonpaths
|
9
|
+
return unless lom
|
10
|
+
hier = Hash.new { |h, k| h[k] = [] }
|
11
|
+
lom.xpath('imsmd:classification/imsmd:taxonPath').each do |tp|
|
12
|
+
entry = taxonpath_entry(tp)
|
13
|
+
hier[entry[:source]] = entry[:taxonpath]
|
14
|
+
end
|
15
|
+
hier
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def taxonpath_entry(node)
|
21
|
+
{
|
22
|
+
source: node.xpath('imsmd:source/imsmd:string').text,
|
23
|
+
taxonpath: taxons(node)
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def metadata
|
28
|
+
@metadata ||= @node&.xpath('xmlns:metadata')&.first
|
29
|
+
end
|
30
|
+
|
31
|
+
def lom
|
32
|
+
return unless imsmd
|
33
|
+
@lom ||= metadata&.xpath('imsmd:lom')&.first
|
34
|
+
end
|
35
|
+
|
36
|
+
def imsmd
|
37
|
+
@node.namespaces&.keys&.include?('xmlns:imsmd')
|
38
|
+
end
|
39
|
+
|
40
|
+
def taxons(node)
|
41
|
+
hier = Hash.new { |h, k| h[k] = [] }
|
42
|
+
taxon(node, hier)
|
43
|
+
end
|
44
|
+
|
45
|
+
def taxon(node, path)
|
46
|
+
return path unless node
|
47
|
+
xpath = 'imsmd:taxon/imsmd:entry/*[self::imsmd:string or self::imsmd:langstring]'
|
48
|
+
node.xpath(xpath).each do |taxon|
|
49
|
+
lang = taxon.attr('language') || taxon.attr('lang') || taxon.attr('xml:lang') || 'default'
|
50
|
+
path[lang].push(taxon.text)
|
51
|
+
end
|
52
|
+
taxon(node.xpath('imsmd:taxon')&.first, path)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
module MetaDataBase
|
57
|
+
delegate :taxonpath, to: :@meta_data, allow_nil: true
|
58
|
+
def metadata_from_node!(node)
|
59
|
+
@meta_data ||= MetaData.new(node)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'qti/v1/models/base'
|
2
|
+
require 'qti/models/assessment_meta'
|
3
|
+
require 'qti/v1/models/assessment'
|
4
|
+
require 'qti/v2/models/assessment_test'
|
5
|
+
require 'qti/v2/models/non_assessment_test'
|
6
|
+
require 'qti/xpath_helpers'
|
7
|
+
require 'qti/models/metadata'
|
8
|
+
|
9
|
+
module Qti
|
10
|
+
module Models
|
11
|
+
class Resource < Qti::Models::Base
|
12
|
+
include Qti::Models::MetaDataBase
|
13
|
+
include Qti::XPathHelpers
|
14
|
+
def initialize(node, parent)
|
15
|
+
@node = node
|
16
|
+
@parent = parent
|
17
|
+
@resource_type = node.attr('type')
|
18
|
+
@identifier = node.attr('identifier')
|
19
|
+
copy_paths_from_item(parent)
|
20
|
+
end
|
21
|
+
|
22
|
+
def href
|
23
|
+
@href ||= @node.attr('href') || @node.xpath('xmlns:file/@href')&.first&.content
|
24
|
+
end
|
25
|
+
|
26
|
+
def metadata
|
27
|
+
@metadata = metadata_from_node!(@node)
|
28
|
+
end
|
29
|
+
|
30
|
+
def canvas_metadata
|
31
|
+
@canvas_meta_file ||= canvas_extra_file('assessment_meta.xml')
|
32
|
+
return unless @canvas_meta_file
|
33
|
+
meta_file = File.join(@package_root, @canvas_meta_file)
|
34
|
+
@canvas_metadata ||= Qti::Models::AssessmentMeta.from_path!(meta_file) if @canvas_meta_file
|
35
|
+
end
|
36
|
+
|
37
|
+
def canvas_extra_file(filename)
|
38
|
+
dep_id = dependency_id
|
39
|
+
rsc = @parent.resource_node(
|
40
|
+
"[@identifier='#{dep_id}']/xmlns:file[#{xpath_endswith('@href', filename)}]/@href"
|
41
|
+
)
|
42
|
+
rsc&.content
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# Canvas Metadata Helpers
|
48
|
+
def dependency_id
|
49
|
+
@node.xpath('xmlns:dependency/@identifierref')&.first&.content
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
module ResourceGroup
|
54
|
+
RESOURCE_QTI_TYPES = %w[imsqti_test_xmlv2p1
|
55
|
+
imsqti_test_xmlv2p2
|
56
|
+
imsqti_xmlv1p2].freeze
|
57
|
+
ASSESSMENT_CLASSES = {
|
58
|
+
'imsqti_xmlv1p2' => Qti::V1::Models::Assessment,
|
59
|
+
'imsqti_test_xmlv2p1' => Qti::V2::Models::AssessmentTest,
|
60
|
+
'imsqti_test_xmlv2p2' => Qti::V2::Models::AssessmentTest
|
61
|
+
}.freeze
|
62
|
+
EMBEDDED_QTI_TYPES = %w[imsqti_item_xmlv2p1
|
63
|
+
imsqti_item_xmlv2p2].freeze
|
64
|
+
EMBEDDED_NON_ASSESSMENT_ID = '@embedded_non_assessment'.freeze
|
65
|
+
|
66
|
+
def resources(type = '')
|
67
|
+
@doc.xpath(xpath_resource(type))
|
68
|
+
end
|
69
|
+
|
70
|
+
def asset_resource_for(rsc)
|
71
|
+
asset_resource_for_canvas(rsc) || asset_resource_for_ims(rsc)
|
72
|
+
end
|
73
|
+
|
74
|
+
def asset_resource_for_canvas(rsc)
|
75
|
+
rsc.canvas_extra_file('.xml.qti')
|
76
|
+
end
|
77
|
+
|
78
|
+
def asset_resource_for_ims(rsc)
|
79
|
+
rsc.href
|
80
|
+
end
|
81
|
+
|
82
|
+
def identifier_list(rsc_type)
|
83
|
+
RESOURCE_QTI_TYPES.map do |v|
|
84
|
+
xmlns_resource_list("[#{rtype_predicate(v, rsc_type)}]").map { |r| r[:identifier] }
|
85
|
+
end.flatten
|
86
|
+
end
|
87
|
+
|
88
|
+
def resource_for(identifier, qti_type = nil)
|
89
|
+
qti_type = " and starts-with(@type, '#{qti_type}')" if qti_type
|
90
|
+
base_xpath = "[@identifier='#{identifier}'#{qti_type}]"
|
91
|
+
Resource.new(resource_node(base_xpath), self)
|
92
|
+
end
|
93
|
+
|
94
|
+
def assessment_identifiers(embedded_as_assessment = true)
|
95
|
+
id_list = identifier_list('/assessment')
|
96
|
+
return id_list + [EMBEDDED_NON_ASSESSMENT_ID] if embedded_as_assessment && embedded_non_assessment?
|
97
|
+
id_list
|
98
|
+
end
|
99
|
+
|
100
|
+
def question_bank_identifiers
|
101
|
+
identifier_list('/question-bank')
|
102
|
+
end
|
103
|
+
|
104
|
+
def item_resources_v2
|
105
|
+
nodes = resources('[@type="imsqti_item_xmlv2p2"]')
|
106
|
+
return nodes if nodes.count >= 1
|
107
|
+
resources('[@type="imsqti_item_xmlv2p1"]')
|
108
|
+
end
|
109
|
+
|
110
|
+
def resource_node(type)
|
111
|
+
xpath_with_single_check(xpath_resource(type))
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -18,7 +18,7 @@ module Qti
|
|
18
18
|
|
19
19
|
def create_assessment_item(assessment_item)
|
20
20
|
return nil if sub_section?(assessment_item)
|
21
|
-
item = Qti::V1::Models::AssessmentItem.new(assessment_item, @package_root)
|
21
|
+
item = Qti::V1::Models::AssessmentItem.new(assessment_item, @package_root, self)
|
22
22
|
item.manifest = manifest
|
23
23
|
item
|
24
24
|
end
|
@@ -6,10 +6,13 @@ module Qti
|
|
6
6
|
module Models
|
7
7
|
class AssessmentItem < Qti::V1::Models::Base
|
8
8
|
attr_reader :doc
|
9
|
+
attr_reader :resource
|
10
|
+
delegate :metadata, to: :@resource
|
9
11
|
|
10
|
-
def initialize(item, package_root = nil)
|
12
|
+
def initialize(item, package_root = nil, resource = nil)
|
11
13
|
@doc = item
|
12
14
|
@path = item.document.url
|
15
|
+
@resource = resource
|
13
16
|
self.package_root = package_root
|
14
17
|
end
|
15
18
|
|
@@ -1,11 +1,13 @@
|
|
1
1
|
require 'qti/v2/models/base'
|
2
2
|
require 'qti/models/assessment_meta'
|
3
|
+
require 'qti/xpath_helpers'
|
3
4
|
|
4
5
|
module Qti
|
5
6
|
module V2
|
6
7
|
module Models
|
7
8
|
class AssessmentTest < Qti::V2::Models::Base
|
8
9
|
include Qti::Models::AssessmentMetaBase
|
10
|
+
include Qti::XPathHelpers
|
9
11
|
def title
|
10
12
|
@title ||= xpath_with_single_check('//xmlns:assessmentTest/@title')&.content || File.basename(@path, '.xml')
|
11
13
|
end
|
@@ -14,7 +16,7 @@ module Qti
|
|
14
16
|
# Return the xml files we should be parsing
|
15
17
|
@assessment_item_reference_hrefs ||= begin
|
16
18
|
@doc.xpath('//xmlns:assessmentItemRef/@href').map(&:content).map do |href|
|
17
|
-
remap_href_path(href)
|
19
|
+
{ path: remap_href_path(href), resource: self }
|
18
20
|
end
|
19
21
|
end
|
20
22
|
end
|
@@ -27,8 +29,8 @@ module Qti
|
|
27
29
|
@assessment_sections ||= test_parts.first.xpath('//xmlns:assessmentSection')
|
28
30
|
end
|
29
31
|
|
30
|
-
def create_assessment_item(
|
31
|
-
item = Qti::V2::Models::AssessmentItem.from_path!(
|
32
|
+
def create_assessment_item(ref)
|
33
|
+
item = Qti::V2::Models::AssessmentItem.from_path!(ref[:path], @package_root, ref[:resource])
|
32
34
|
item.manifest = manifest
|
33
35
|
item
|
34
36
|
end
|
@@ -1,31 +1,28 @@
|
|
1
1
|
require 'qti/v2/models/base'
|
2
|
+
require 'qti/models/resource'
|
2
3
|
|
3
4
|
module Qti
|
4
5
|
module V2
|
5
6
|
module Models
|
6
7
|
class NonAssessmentTest < Qti::V2::Models::AssessmentTest
|
8
|
+
include Qti::Models::ResourceGroup
|
7
9
|
def assessment_items
|
8
10
|
# Return the xml files we should be parsing
|
9
|
-
@
|
10
|
-
|
11
|
-
|
11
|
+
@assessment_item_resources ||= begin
|
12
|
+
item_resources_v2.map do |node|
|
13
|
+
rsc = Qti::Models::Resource.new(node, self)
|
14
|
+
{ path: remap_href_path(rsc.href), resource: rsc }
|
12
15
|
end
|
13
16
|
end
|
14
17
|
end
|
15
18
|
|
16
19
|
def stimulus_ref(assessment_item_ref)
|
17
|
-
ref = assessment_item_ref.sub(@package_root, '')
|
20
|
+
ref = assessment_item_ref[:path].sub(@package_root, '')
|
18
21
|
dependencies = @doc.xpath("//xmlns:resource[@href='#{ref}']/xmlns:dependency/@identifierref")
|
19
22
|
return unless dependencies&.count == 1
|
20
23
|
href = xpath_with_single_check("//xmlns:resource[@identifier='#{dependencies.first}']/@href")
|
21
24
|
remap_href_path(href)
|
22
25
|
end
|
23
|
-
|
24
|
-
def hrefs
|
25
|
-
nodes = @doc.xpath("//xmlns:resource[@type='imsqti_item_xmlv2p2']/@href")
|
26
|
-
return nodes if nodes.count >= 1
|
27
|
-
@doc.xpath("//xmlns:resource[@type='imsqti_item_xmlv2p1']/@href")
|
28
|
-
end
|
29
26
|
end
|
30
27
|
end
|
31
28
|
end
|