qti 0.9.1 → 0.9.2

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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/lib/qti/assessment_item_exporter.rb +1 -1
  3. data/lib/qti/content_packaging/assessment_item.rb +1 -1
  4. data/lib/qti/content_packaging/assessment_test.rb +2 -2
  5. data/lib/qti/content_packaging/choice_interaction.rb +1 -1
  6. data/lib/qti/models/base.rb +13 -5
  7. data/lib/qti/models/manifest.rb +21 -7
  8. data/lib/qti/v1/models/assessment.rb +3 -1
  9. data/lib/qti/v1/models/assessment_item.rb +13 -5
  10. data/lib/qti/v1/models/choices/fill_blank_choice.rb +1 -1
  11. data/lib/qti/v1/models/choices/logical_identifier_choice.rb +1 -1
  12. data/lib/qti/v1/models/interactions/base_interaction.rb +1 -1
  13. data/lib/qti/v1/models/interactions/choice_interaction.rb +19 -5
  14. data/lib/qti/v2/models/assessment_item.rb +23 -6
  15. data/lib/qti/v2/models/assessment_test.rb +3 -1
  16. data/lib/qti/v2/models/choices/gap_match_choice.rb +1 -1
  17. data/lib/qti/v2/models/choices/simple_choice.rb +1 -1
  18. data/lib/qti/v2/models/interactions/base_interaction.rb +1 -1
  19. data/lib/qti/v2/models/interactions/gap_match_interaction.rb +16 -7
  20. data/lib/qti/version.rb +1 -1
  21. data/spec/lib/qti/v1/models/interactions/choice_interaction_spec.rb +9 -2
  22. data/spec/lib/qti/v2/models/assessment_item_spec.rb +12 -0
  23. data/spec/lib/qti/v2/models/assessment_test_spec.rb +9 -2
  24. data/spec/lib/qti_spec.rb +1 -0
  25. data/spec/lib/stupid.xml +128 -0
  26. metadata +19 -17
  27. data/spec/gemfiles/nokogiri-1.6.gemfile.lock +0 -126
  28. data/spec/gemfiles/nokogiri-1.8.gemfile.lock +0 -126
  29. data/spec/gemfiles/rails-4.2.gemfile.lock +0 -203
  30. data/spec/gemfiles/rails-5.0.gemfile.lock +0 -209
  31. data/spec/gemfiles/rails-5.1.gemfile.lock +0 -209
  32. data/spec/gemfiles/sanitize-4.2.gemfile.lock +0 -126
  33. data/spec/gemfiles/sanitize-4.5.gemfile.lock +0 -126
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 202d9ba3ef99c4e0df030cf13a8e25f4e19441c2
4
- data.tar.gz: 077f7118e61d94d1d7796494e481355e0b399833
3
+ metadata.gz: 9278a5814a878f23f53159c3adb0c64e7d7a8e69
4
+ data.tar.gz: 8892e930ffbf68ef68b5f08fddd5ed3fa63f34da
5
5
  SHA512:
6
- metadata.gz: 6119e426a2f6c4b91cfb84ac7da7ca78317490439c0ace3ec067fc65ffa1b8b929979dfa76a4cb648b9d44781e5cb55d38490c89151ab1e66581c9315c8b88ff
7
- data.tar.gz: 76f7caf87c7c9efd299b770d2adfcfa5737991dbaa3b2ee450616d0ae987bac40bd2a362932769f462006e80a11b40b4cb00c249cf48b1565c70fa50b71d559e
6
+ metadata.gz: 25f42764b0ac0109aa2ab6edf7e3e814fd4150b94b195684f856acaa6faaa859712287a6262141352456b1e341e6d1caef0e037fdfce476d2742cc67d53f3203
7
+ data.tar.gz: e3cda7d07238a30879125895ef9b73bb1283d2776803ace6489e5226e0ddd736ebf09321e8f65a7fe5cb01a1783ba8a01d0ddb9476be4724b8b34a79d07146b2
@@ -1,6 +1,6 @@
1
1
  module Qti
2
2
  class AssessmentItemExporter
3
- attr_reader :assessment_item, :package_root_path, :exported_file_path
3
+ attr_reader :assessment_item, :package_root_path
4
4
 
5
5
  def initialize(assessment_item, args = {})
6
6
  @assessment_item = assessment_item
@@ -4,7 +4,7 @@ module Qti
4
4
  attribute :identifier, Types::Strict::String
5
5
  attribute :title, Types::Strict::String
6
6
  attribute :interaction, ContentPackaging::ChoiceInteraction
7
- attribute :response, Types::Strict::Array.member(String) | Types::Strict::String
7
+ attribute :response, Types::Strict::Array.of(String) | Types::Strict::String
8
8
  end
9
9
  end
10
10
  end
@@ -3,8 +3,8 @@ module Qti
3
3
  class AssessmentTest < Dry::Struct
4
4
  attribute :identifier, Types::Strict::String
5
5
  attribute :title, Types::Strict::String
6
- attribute :items, Types::Strict::Array.member(ContentPackaging::AssessmentItem)
7
- attribute :outcome_declarations, Types::Strict::Array.member(ContentPackaging::OutcomeDeclaration)
6
+ attribute :items, Types::Strict::Array.of(ContentPackaging::AssessmentItem)
7
+ attribute :outcome_declarations, Types::Strict::Array.of(ContentPackaging::OutcomeDeclaration)
8
8
  end
9
9
  end
10
10
  end
@@ -6,7 +6,7 @@ module Qti
6
6
  attribute :prompt, Types::Strict::String
7
7
  attribute :shuffle, Types::Strict::Bool.default(false)
8
8
  attribute :maxChoices, Types::Coercible::Int
9
- attribute :choices, Types::Strict::Array.member(ContentPackaging::SimpleChoice)
9
+ attribute :choices, Types::Strict::Array.of(ContentPackaging::SimpleChoice)
10
10
  end
11
11
  end
12
12
  end
@@ -10,6 +10,7 @@ module Qti
10
10
  module Models
11
11
  class Base
12
12
  attr_reader :doc, :path, :package_root
13
+ attr_accessor :manifest
13
14
 
14
15
  ELEMENTS_REMAP = {
15
16
  'prompt' => 'div',
@@ -58,7 +59,7 @@ module Qti
58
59
 
59
60
  def initialize(path:, package_root: nil, html: false)
60
61
  @path = path
61
- set_package_root(package_root || File.dirname(path))
62
+ self.package_root = package_root || File.dirname(path)
62
63
  @doc = html ? parse_html(File.read(path)) : parse_xml(File.read(path))
63
64
  raise ArgumentError unless @doc
64
65
  end
@@ -89,20 +90,27 @@ module Qti
89
90
  if @package_root.nil?
90
91
  raise Qti::ParseError, "Potentially unsafe href '#{href}'" if href.split('/').include?('..')
91
92
  else
92
- raise Qti::ParseError, "Unsafe href '#{href}'" unless Pathname.new(path).cleanpath.to_s.start_with?(@package_root)
93
+ unless Pathname.new(path).cleanpath.to_s.start_with?(@package_root)
94
+ raise Qti::ParseError, "Unsafe href '#{href}'"
95
+ end
93
96
  end
94
97
  path
95
98
  end
96
99
 
97
100
  protected
98
101
 
99
- def set_package_root(package_root)
102
+ def package_root=(package_root)
100
103
  @package_root = package_root
101
104
  return unless @package_root
102
105
  @package_root = Pathname.new(@package_root).cleanpath.to_s + '/'
103
106
  end
104
107
 
105
- def set_paths_from_item(other_item)
108
+ def relative_path
109
+ return nil if @path.nil? || @package_root.nil?
110
+ @path.sub(/\A#{Regexp.quote(@package_root)}/, '')
111
+ end
112
+
113
+ def copy_paths_from_item(other_item)
106
114
  @package_root = other_item.package_root
107
115
  @path = other_item.path
108
116
  end
@@ -111,7 +119,7 @@ module Qti
111
119
  path = remap_href_path(node[:data])
112
120
  if path
113
121
  case node[:type]
114
- when /^image\//
122
+ when %r{^image\/}
115
123
  return replace_with_image(node, node[:data])
116
124
  when 'text/html'
117
125
  return replace_with_html(node, path)
@@ -4,19 +4,27 @@ module Qti
4
4
  module Models
5
5
  class Manifest < Qti::Models::Base
6
6
  def assessment_test
7
- qti_2_x_href || qti_1_href || qti_2_non_assessment_href || unknown_type
7
+ test = qti_2_x_href || qti_1_href || qti_2_non_assessment_href || unknown_type
8
+ test.manifest = self
9
+ test
8
10
  end
9
11
 
10
12
  def qti_1_href
11
- test_path = xpath_with_single_check("//xmlns:resources/xmlns:resource[@type='imsqti_xmlv1p2']/@href")&.content ||
12
- xpath_with_single_check("//xmlns:resources/xmlns:resource[@type='imsqti_xmlv1p2']/xmlns:file/@href")&.content
13
- Qti::V1::Models::Assessment.from_path!(remap_href_path(test_path), @package_root) if test_path
13
+ test_path = xmlns_resource("[@type='imsqti_xmlv1p2']/@href") ||
14
+ xmlns_resource("[@type='imsqti_xmlv1p2']/xmlns:file/@href")
15
+ return unless test_path
16
+ Qti::V1::Models::Assessment.from_path!(
17
+ remap_href_path(test_path), @package_root
18
+ )
14
19
  end
15
20
 
16
21
  def qti_2_x_href
17
- test_path = xpath_with_single_check("//xmlns:resources/xmlns:resource[@type='imsqti_test_xmlv2p1']/@href")&.content ||
18
- xpath_with_single_check("//xmlns:resources/xmlns:resource[@type='imsqti_test_xmlv2p2']/@href")&.content
19
- Qti::V2::Models::AssessmentTest.from_path!(remap_href_path(test_path), @package_root) if test_path
22
+ test_path = xmlns_resource("[@type='imsqti_test_xmlv2p1']/@href") ||
23
+ xmlns_resource("[@type='imsqti_test_xmlv2p2']/@href")
24
+ return unless test_path
25
+ Qti::V2::Models::AssessmentTest.from_path!(
26
+ remap_href_path(test_path), @package_root
27
+ )
20
28
  end
21
29
 
22
30
  def qti_2_non_assessment_href
@@ -27,6 +35,12 @@ module Qti
27
35
  def unknown_type
28
36
  raise Qti::UnsupportedSchema, 'Unsupported QTI version'
29
37
  end
38
+
39
+ private
40
+
41
+ def xmlns_resource(type)
42
+ xpath_with_single_check("//xmlns:resources/xmlns:resource#{type}")&.content
43
+ end
30
44
  end
31
45
  end
32
46
  end
@@ -13,7 +13,9 @@ module Qti
13
13
  end
14
14
 
15
15
  def create_assessment_item(assessment_item)
16
- Qti::V1::Models::AssessmentItem.new(assessment_item, @package_root)
16
+ item = Qti::V1::Models::AssessmentItem.new(assessment_item, @package_root)
17
+ item.manifest = manifest
18
+ item
17
19
  end
18
20
 
19
21
  def stimulus_ref(_ref)
@@ -10,7 +10,7 @@ module Qti
10
10
  def initialize(item, package_root = nil)
11
11
  @doc = item
12
12
  @path = item.document.url
13
- set_package_root(package_root)
13
+ self.package_root = package_root
14
14
  end
15
15
 
16
16
  def item_body
@@ -34,7 +34,7 @@ module Qti
34
34
  @doc.at_xpath('.//xmlns:qtimetadata')&.children
35
35
  end
36
36
 
37
- def has_points_possible_qti_metadata?
37
+ def points_possible_qti_metadata?
38
38
  if @doc.at_xpath('.//xmlns:qtimetadata').present?
39
39
  points_possible_label = qti_metadata_children.children.find { |node| node.text == 'points_possible' }
40
40
  points_possible_label.present?
@@ -43,11 +43,19 @@ module Qti
43
43
  end
44
44
  end
45
45
 
46
+ # @deprecated Please use {#points_possible_qti_metadata?} instead
47
+ def has_points_possible_qti_metadata? # rubocop:disable Naming/PredicateName
48
+ warn "DEPRECATED: '#{__method__}' is renamed to 'points_possible_qti_metadata?'."
49
+ points_possible_qti_metadata?
50
+ end
51
+
46
52
  def points_possible
47
53
  @points_possible ||= begin
48
- if has_points_possible_qti_metadata?
49
- points_possible_label = qti_metadata_children.children.find { |node| node.text == 'points_possible' }
50
- points_possible_node = points_possible_label.next.text
54
+ if points_possible_qti_metadata?
55
+ points_possible_label = qti_metadata_children.children.find do |node|
56
+ node.text == 'points_possible'
57
+ end
58
+ points_possible_label.next.text
51
59
  else
52
60
  decvar_maxvalue
53
61
  end
@@ -7,7 +7,7 @@ module Qti
7
7
  class FillBlankChoice < Qti::V1::Models::Base
8
8
  def initialize(node, parent)
9
9
  @node = node
10
- set_paths_from_item(parent)
10
+ copy_paths_from_item(parent)
11
11
  end
12
12
 
13
13
  def identifier
@@ -7,7 +7,7 @@ module Qti
7
7
  class LogicalIdentifierChoice < Qti::V1::Models::Base
8
8
  def initialize(node, parent)
9
9
  @node = node
10
- set_paths_from_item(parent)
10
+ copy_paths_from_item(parent)
11
11
  end
12
12
 
13
13
  def identifier
@@ -11,7 +11,7 @@ module Qti
11
11
 
12
12
  def initialize(node, parent)
13
13
  @node = node
14
- set_paths_from_item(parent)
14
+ copy_paths_from_item(parent)
15
15
  end
16
16
 
17
17
  def shuffled?
@@ -21,12 +21,9 @@ module Qti
21
21
  def scoring_data_structs
22
22
  choice_nodes = node.xpath('.//xmlns:respcondition')
23
23
  if choice_nodes.at_xpath('.//xmlns:and').present?
24
- answer_choices = choice_nodes.at_xpath('.//xmlns:and')
25
- answer_choices.children.filter('not').each(&:remove)
26
- answer_choices.children.map { |value_node| ScoringData.new(value_node.content, rcardinality) }
24
+ scoring_data_condition(choice_nodes)
27
25
  else
28
- set_var_nodes = choice_nodes.select { |choice_node| choice_node.at_xpath('.//xmlns:setvar')&.content&.to_f&.positive? }
29
- set_var_nodes.map { |value_node| ScoringData.new(value_node.at_xpath('.//xmlns:varequal').content, rcardinality) }
26
+ scoring_data(choice_nodes)
30
27
  end
31
28
  end
32
29
 
@@ -35,6 +32,23 @@ module Qti
35
32
  def answer_nodes
36
33
  node.xpath('.//xmlns:response_label')
37
34
  end
35
+
36
+ def scoring_data_condition(choice_nodes)
37
+ answer_choices = choice_nodes.at_xpath('.//xmlns:and')
38
+ answer_choices.children.filter('not').each(&:remove)
39
+ answer_choices.children.map do |value_node|
40
+ ScoringData.new(value_node.content, rcardinality)
41
+ end
42
+ end
43
+
44
+ def scoring_data(choice_nodes)
45
+ set_var_nodes = choice_nodes.select do |choice_node|
46
+ choice_node.at_xpath('.//xmlns:setvar')&.content&.to_f&.positive?
47
+ end
48
+ set_var_nodes.map do |value_node|
49
+ ScoringData.new(value_node.at_xpath('.//xmlns:varequal').content, rcardinality)
50
+ end
51
+ end
38
52
  end
39
53
  end
40
54
  end
@@ -12,12 +12,7 @@ module Qti
12
12
  node = item_body_node.dup
13
13
  # ensure a prompt is carried into the html
14
14
  prompt = node.at_xpath('//xmlns:prompt')
15
-
16
- # Filter undesired interaction nodes out of the list (need to make this a deep traversal)
17
- node.children.filter(INTERACTION_ELEMENTS_CSS).map(&:unlink)
18
- # Filter out rubrics
19
- node.children.filter('rubricBlock').map(&:unlink)
20
-
15
+ filter_item_body(node)
21
16
  node.add_child(prompt) if prompt&.parent && prompt.parent != node
22
17
  sanitize_content!(node.to_html)
23
18
  end
@@ -54,6 +49,28 @@ module Qti
54
49
  def item_body_node
55
50
  @item_body_node ||= xpath_with_single_check('//xmlns:itemBody')
56
51
  end
52
+
53
+ def filter_item_body(node)
54
+ # Filter undesired interaction nodes out of the list (need to make this a deep traversal)
55
+ node.children.filter(INTERACTION_ELEMENTS_CSS).map(&:unlink)
56
+ # Filter out rubrics
57
+ node.children.filter('rubricBlock').map(&:unlink)
58
+ # Filter out stimulus passages (these will be handled separately)
59
+ filter_stimulus_passages!(node)
60
+ end
61
+
62
+ def dependency_hrefs
63
+ return [] unless manifest
64
+ manifest.doc.xpath("//xmlns:resource[@href='#{relative_path}']/xmlns:dependency/@identifierref").map do |id|
65
+ manifest.xpath_with_single_check("//xmlns:resource[@identifier='#{id}']/@href")
66
+ end
67
+ end
68
+
69
+ def filter_stimulus_passages!(node)
70
+ dependency_hrefs.each do |href|
71
+ node.at_css("object[type='text/html'][data='#{href}']").try(:unlink)
72
+ end
73
+ end
57
74
  end
58
75
  end
59
76
  end
@@ -26,7 +26,9 @@ module Qti
26
26
  end
27
27
 
28
28
  def create_assessment_item(assessment_item_ref)
29
- Qti::V2::Models::AssessmentItem.from_path!(assessment_item_ref, @package_root)
29
+ item = Qti::V2::Models::AssessmentItem.from_path!(assessment_item_ref, @package_root)
30
+ item.manifest = manifest
31
+ item
30
32
  end
31
33
 
32
34
  def stimulus_ref(_ref)
@@ -8,7 +8,7 @@ module Qti
8
8
  PROHIBITED_NODE_NAMES = 'feedbackInline'.freeze
9
9
  def initialize(node, parent)
10
10
  @node = node
11
- set_paths_from_item(parent)
11
+ copy_paths_from_item(parent)
12
12
  end
13
13
 
14
14
  def identifier
@@ -9,7 +9,7 @@ module Qti
9
9
 
10
10
  def initialize(node, parent)
11
11
  @node = node
12
- set_paths_from_item(parent)
12
+ copy_paths_from_item(parent)
13
13
  end
14
14
 
15
15
  def identifier
@@ -11,7 +11,7 @@ module Qti
11
11
 
12
12
  def initialize(node, parent)
13
13
  @node = node
14
- set_paths_from_item(parent)
14
+ copy_paths_from_item(parent)
15
15
  end
16
16
 
17
17
  def shuffled?
@@ -85,16 +85,11 @@ module Qti
85
85
  end
86
86
 
87
87
  def scoring_data_structs
88
- question_response_pairs = node.xpath('.//xmlns:correctResponse//xmlns:value').map do |value|
89
- value.content.split
90
- end
91
- question_response_pairs.map!(&:reverse)
92
- question_response_id_mapping = Hash[question_response_pairs]
88
+ mapping = question_response_id_mapping
93
89
  answer_nodes.map do |value_node|
94
90
  node_id = value_node.attributes['identifier']&.value
95
- answer_choice = choices.find { |choice| choice.attributes['identifier']&.value == question_response_id_mapping[node_id] }
96
91
  ScoringData.new(
97
- answer_choice.content,
92
+ answer_choice(choices, mapping[node_id]).content,
98
93
  'directedPair',
99
94
  id: node_id,
100
95
  case: false
@@ -111,6 +106,20 @@ module Qti
111
106
  def answer_nodes
112
107
  @node.xpath('.//xmlns:gap')
113
108
  end
109
+
110
+ def answer_choice(choices, response)
111
+ choices.find do |choice|
112
+ choice.attributes['identifier']&.value == response
113
+ end
114
+ end
115
+
116
+ def question_response_id_mapping
117
+ question_response_pairs = node.xpath('.//xmlns:correctResponse//xmlns:value').map do |value|
118
+ value.content.split
119
+ end
120
+ question_response_pairs.map!(&:reverse)
121
+ Hash[question_response_pairs]
122
+ end
114
123
  end
115
124
  end
116
125
  end
data/lib/qti/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Qti
2
- VERSION = '0.9.1'.freeze
2
+ VERSION = '0.9.2'.freeze
3
3
  end
@@ -15,7 +15,8 @@ describe Qti::V1::Models::Interactions::ChoiceInteraction do
15
15
  shared_examples_for 'answers' do
16
16
  it 'returns the answers' do
17
17
  expect(loaded_class.answers.count).to eq answer_choices_count
18
- expect(loaded_class.answers.first).to be_an_instance_of(Qti::V1::Models::Choices::LogicalIdentifierChoice)
18
+ expect(loaded_class.answers.first)
19
+ .to be_an_instance_of(Qti::V1::Models::Choices::LogicalIdentifierChoice)
19
20
  end
20
21
  end
21
22
 
@@ -54,7 +55,13 @@ describe Qti::V1::Models::Interactions::ChoiceInteraction do
54
55
 
55
56
  context 'multiple respconditions with empty setvars' do
56
57
  let(:fixtures_path) { File.join('spec', 'fixtures', 'test_with_comments') }
57
- let(:file_path) { File.join(fixtures_path, 'i6c88aaf29feba2ffa58a487a20665394', 'i6c88aaf29feba2ffa58a487a20665394.xml') }
58
+ let(:file_path) do
59
+ File.join(
60
+ fixtures_path,
61
+ 'i6c88aaf29feba2ffa58a487a20665394',
62
+ 'i6c88aaf29feba2ffa58a487a20665394.xml'
63
+ )
64
+ end
58
65
 
59
66
  it 'loads the items' do
60
67
  expect(loaded_class.scoring_data_structs.count).to eq 1
@@ -77,4 +77,16 @@ describe Qti::V2::Models::AssessmentItem do
77
77
  end
78
78
  end
79
79
  end
80
+
81
+ context 'stimulus passages' do
82
+ let(:file_path) { File.join('spec', 'fixtures', 'no_assessment_XML') }
83
+ let(:importer) { Qti::Importer.new(file_path) }
84
+ let(:test_object) { importer.test_object }
85
+ let(:item_ref) { File.join(file_path, 'a4f3621a-195c-4847-a72c-69388250a078.xml') }
86
+
87
+ it 'removes the passage from the item_body' do
88
+ item = test_object.create_assessment_item(item_ref)
89
+ expect(item.item_body).not_to include '¡El equipo de hockey te necesita!'
90
+ end
91
+ end
80
92
  end
@@ -35,7 +35,11 @@ describe Qti::V2::Models::AssessmentTest do
35
35
  test_files = Dir.glob(File.join(fixtures_path, 'tests', 'tests', 'rtest[0-9][0-9].xml'))
36
36
  test_files << File.join(fixtures_path, 'tests', 'tests', 'complete.xml')
37
37
  test_files << File.join(fixtures_path, 'tests', 'feedbackTest', 'assessment.xml')
38
- test_files << File.join(fixtures_path, 'tests', 'interactionmix_saxony_v3', 'InteractionMixSachsen_1901710679.xml')
38
+ test_files << File.join(
39
+ fixtures_path, 'tests',
40
+ 'interactionmix_saxony_v3',
41
+ 'InteractionMixSachsen_1901710679.xml'
42
+ )
39
43
  test_files.each do |file|
40
44
  context File.basename(file) do
41
45
  let(:loaded_class) { described_class.from_path!(file) }
@@ -77,7 +81,10 @@ describe Qti::V2::Models::AssessmentTest do
77
81
  let(:loaded_class) { described_class.from_path!(path) }
78
82
 
79
83
  it 'creates a stimulus from a given file' do
80
- stimulus_path = File.join(fixtures_path, 'no_assessment_XML', 'passages', '0cfd5cf7-2c91-4b35-a57a-9f5d1709f68f.html')
84
+ stimulus_path = File.join(
85
+ fixtures_path, 'no_assessment_XML',
86
+ 'passages', '0cfd5cf7-2c91-4b35-a57a-9f5d1709f68f.html'
87
+ )
81
88
  stimulus = loaded_class.create_stimulus(stimulus_path)
82
89
  expect(stimulus.title).to eq '¡El equipo de hockey te necesita!'
83
90
  end
data/spec/lib/qti_spec.rb CHANGED
@@ -67,6 +67,7 @@ describe Qti::Importer do
67
67
  item = importer.create_assessment_item(ref)
68
68
  expect(item.path).to eq ref
69
69
  expect(item.package_root).to eq file_path + '/'
70
+ expect(item.manifest).not_to be_nil
70
71
  end
71
72
  end
72
73
  end
@@ -0,0 +1,128 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <manifest xmlns="http://www.imsglobal.org/xsd/imscp_v1p1" identifier="Manifest-d1042982-b88a-4c04-a6e0-9bb718b41178"
3
+ xmlns="http://www.imsglobal.org/xsd/imscp_v1p1" xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2"
4
+ xmlns:imsqti="http://www.imsglobal.org/xsd/imsqti_v2p1">
5
+ <organizations/>
6
+ <metadata>
7
+ <schema>IMS Content</schema>
8
+ <schemaversion>2.1</schemaversion>
9
+ <imsmd:lom xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2">
10
+ <imsmd:general>
11
+ <imsmd:title>
12
+ <imsmd:langstring>Assignment test? Worth Points Question Pool 1</imsmd:langstring>
13
+ </imsmd:title>
14
+ </imsmd:general>
15
+ </imsmd:lom>
16
+ </metadata>
17
+ <resources>
18
+ <resource href="1009/question4928.xml" identifier="1788:4928" type="imsqti_item_xmlv2p1">
19
+ <metadata>
20
+ <imsqti:qtiMetadata xmlns:imsqti="http://www.imsglobal.org/xsd/imsqti_v2p1">
21
+ <imsqti:itemTemplate>false</imsqti:itemTemplate>
22
+ <imsqti:composite>false</imsqti:composite>
23
+ <imsqti:interactionType>extendedTextInteraction</imsqti:interactionType>
24
+ <imsqti:feedbackType>none</imsqti:feedbackType>
25
+ </imsqti:qtiMetadata>
26
+ </metadata>
27
+ <file href="1009/question4928.xml"/>
28
+ <file href="1009/Resources/Ixia.gif"/>
29
+ <file href="1009/Resources/aperture.png"/>
30
+ </resource>
31
+ <resource href="1009/question4900.xml" identifier="1785:4900" type="imsqti_item_xmlv2p1">
32
+ <metadata>
33
+ <imsqti:qtiMetadata xmlns:imsqti="http://www.imsglobal.org/xsd/imsqti_v2p1">
34
+ <imsqti:itemTemplate>false</imsqti:itemTemplate>
35
+ <imsqti:composite>false</imsqti:composite>
36
+ <imsqti:interactionType>choiceInteraction</imsqti:interactionType>
37
+ <imsqti:feedbackType>none</imsqti:feedbackType>
38
+ </imsqti:qtiMetadata>
39
+ </metadata>
40
+ <file href="1009/question4900.xml"/>
41
+ <file href="1009/Resources/512634641600_00_93874928374.jpg"/>
42
+ <file href="1009/Resources/fmath-equation-9666DCA9-6656-C4BA-4E2D-63AA5B5954C1.png"/>
43
+ </resource>
44
+ <resource href="1009/question4961.xml" identifier="1785:4961" type="imsqti_item_xmlv2p1">
45
+ <metadata>
46
+ <imsqti:qtiMetadata xmlns:imsqti="http://www.imsglobal.org/xsd/imsqti_v2p1">
47
+ <imsqti:itemTemplate>false</imsqti:itemTemplate>
48
+ <imsqti:composite>false</imsqti:composite>
49
+ <imsqti:interactionType>choiceInteraction</imsqti:interactionType>
50
+ <imsqti:feedbackType>none</imsqti:feedbackType>
51
+ </imsqti:qtiMetadata>
52
+ </metadata>
53
+ <file href="1009/question4961.xml"/>
54
+ </resource>
55
+ <resource href="1009/question4962.xml" identifier="1785:4962" type="imsqti_item_xmlv2p1">
56
+ <metadata>
57
+ <imsqti:qtiMetadata xmlns:imsqti="http://www.imsglobal.org/xsd/imsqti_v2p1">
58
+ <imsqti:itemTemplate>false</imsqti:itemTemplate>
59
+ <imsqti:composite>false</imsqti:composite>
60
+ <imsqti:interactionType>choiceInteraction</imsqti:interactionType>
61
+ <imsqti:feedbackType>none</imsqti:feedbackType>
62
+ </imsqti:qtiMetadata>
63
+ </metadata>
64
+ <file href="1009/question4962.xml"/>
65
+ <file href="1009/Resources/waterfall_desktop_background-1440x900.jpg"/>
66
+ </resource>
67
+ <resource href="1009/question4967.xml" identifier="1788:4967" type="imsqti_item_xmlv2p1">
68
+ <metadata>
69
+ <imsqti:qtiMetadata xmlns:imsqti="http://www.imsglobal.org/xsd/imsqti_v2p1">
70
+ <imsqti:itemTemplate>false</imsqti:itemTemplate>
71
+ <imsqti:composite>false</imsqti:composite>
72
+ <imsqti:interactionType>matchInteraction</imsqti:interactionType>
73
+ <imsqti:feedbackType>none</imsqti:feedbackType>
74
+ </imsqti:qtiMetadata>
75
+ </metadata>
76
+ <file href="1009/question4967.xml"/>
77
+ </resource>
78
+ <resource href="1009/question4965.xml" identifier="1788:4965" type="imsqti_item_xmlv2p1">
79
+ <metadata>
80
+ <imsmd:lom xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2">
81
+ <imsmd:general>
82
+ <imsmd:identifier>survey</imsmd:identifier>
83
+ </imsmd:general>
84
+ </imsmd:lom>
85
+ <imsqti:qtiMetadata xmlns:imsqti="http://www.imsglobal.org/xsd/imsqti_v2p1">
86
+ <imsqti:itemTemplate>false</imsqti:itemTemplate>
87
+ <imsqti:composite>false</imsqti:composite>
88
+ <imsqti:interactionType>choiceInteraction</imsqti:interactionType>
89
+ <imsqti:feedbackType>none</imsqti:feedbackType>
90
+ </imsqti:qtiMetadata>
91
+ </metadata>
92
+ <file href="1009/question4965.xml"/>
93
+ </resource>
94
+ <resource href="1009/question4966.xml" identifier="1788:4966" type="imsqti_item_xmlv2p1">
95
+ <metadata>
96
+ <imsqti:qtiMetadata xmlns:imsqti="http://www.imsglobal.org/xsd/imsqti_v2p1">
97
+ <imsqti:itemTemplate>false</imsqti:itemTemplate>
98
+ <imsqti:composite>false</imsqti:composite>
99
+ <imsqti:interactionType>textEntryInteraction</imsqti:interactionType>
100
+ <imsqti:feedbackType>none</imsqti:feedbackType>
101
+ </imsqti:qtiMetadata>
102
+ </metadata>
103
+ <file href="1009/question4966.xml"/>
104
+ </resource>
105
+ <resource href="1009/assessment1.xml" identifier="Resource1" type="imsqti_test_xmlv2p1">
106
+ <metadata>
107
+ <imsmd:lom xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2">
108
+ <imsmd:general>
109
+ <imsmd:identifier>assignment</imsmd:identifier>
110
+ <imsmd:title>
111
+ <imsmd:langstring>Assignment test? Worth Points</imsmd:langstring>
112
+ </imsmd:title>
113
+ </imsmd:general>
114
+ </imsmd:lom>
115
+ </metadata>
116
+ <file href="1009/assessment1.xml"/>
117
+ <dependency identifierref="1788:4928"/>
118
+ <dependency identifierref="1785:4900"/>
119
+ <dependency identifierref="1785:4961"/>
120
+ <dependency identifierref="1785:4962"/>
121
+ <dependency identifierref="1788:4967"/>
122
+ <dependency identifierref="1788:4965"/>
123
+ <dependency identifierref="1788:4966"/>
124
+ </resource>
125
+ <resource identifier="POOL1788" title="Assignment test? Worth Points"/>
126
+ <resource identifier="POOL1785" title="Question Pool 1"/>
127
+ </resources>
128
+ </manifest>