qti 0.2.12 → 0.2.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +9 -1
  3. data/lib/qti/v1/models/assessment.rb +2 -2
  4. data/lib/qti/v1/models/assessment_item.rb +5 -29
  5. data/lib/qti/v1/models/interactions/base_interaction.rb +33 -0
  6. data/lib/qti/v1/models/interactions/choice_interaction.rb +44 -0
  7. data/lib/qti/v1/models/interactions/match_interaction.rb +60 -0
  8. data/lib/qti/v1/models/interactions/ordering_interaction.rb +23 -0
  9. data/lib/qti/v1/models/interactions/string_interaction.rb +1 -3
  10. data/lib/qti/v1/models/interactions.rb +1 -3
  11. data/lib/qti/v1/models/scoring_data.rb +7 -0
  12. data/lib/qti/v2/models/assessment_item.rb +2 -6
  13. data/lib/qti/v2/models/choices/simple_associable_choice.rb +12 -0
  14. data/lib/qti/v2/models/interactions/base_interaction.rb +43 -0
  15. data/lib/qti/v2/models/interactions/choice_interaction.rb +2 -12
  16. data/lib/qti/v2/models/interactions/extended_text_interaction.rb +1 -5
  17. data/lib/qti/v2/models/interactions/match_interaction.rb +52 -0
  18. data/lib/qti/v2/models/scoring_data.rb +7 -0
  19. data/lib/qti.rb +6 -0
  20. data/spec/fixtures/items_1.2/matching.xml +74 -0
  21. data/spec/fixtures/items_1.2/multiple_answer.xml +1 -1
  22. data/spec/fixtures/items_1.2/multiple_choice.xml +1 -1
  23. data/spec/fixtures/items_1.2/ordering.xml +20 -20
  24. data/spec/fixtures/items_1.2/true_false.xml +1 -1
  25. data/spec/fixtures/items_2.1/match.xml +33 -39
  26. data/spec/fixtures/test_qti_1.2/quiz.xml +209 -216
  27. data/spec/lib/qti/v1/models/assessment_item_spec.rb +20 -2
  28. data/spec/lib/qti/v1/models/interactions/{logical_identifier_interaction_spec.rb → choice_interaction_spec.rb} +1 -1
  29. data/spec/lib/qti/v2/models/assessment_item_spec.rb +24 -0
  30. metadata +15 -5
  31. data/lib/qti/v1/models/interactions/logical_identifier_interaction.rb +0 -36
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4276f67d46173a7c7059c36687cf77c930a0bb36
4
- data.tar.gz: 05b05ab5d7c63ce1bcb12ae3a8a688eb30d491c3
3
+ metadata.gz: 7e3dbac8ff4eda0f1c6f67c6f8e7e1673ac8014b
4
+ data.tar.gz: 807c9a6be3ea4e1887966bb5734015dc017efa04
5
5
  SHA512:
6
- metadata.gz: 10314c5ed21040925853f8b98e1e07f222d3a8921ce44875f26962dafbf43cfc8480b32523653d0161d1dcffe00573e038415ef4fff2d7956341322e3c1d530d
7
- data.tar.gz: 372d29818743f5f26d26db7b2a02d995e68f4f73a7022fff9a7691584b39631a30c0f2b74110f5b39d7c4e7b3fa41816351ac444e92792e3885a7408d4b3e11d
6
+ metadata.gz: a628c8aa5c49fdea190b112136ab3ad22861725437b7c167c2ac6188ae7970a5641a6d794243726d4b9c24c0986ad33900d22816a4576fa056e5acc50c328db8
7
+ data.tar.gz: 1a92b8176ca835e95923c936ae5c86b77c60e9ba702c7f6dd0d84d082c1ae6aa6eb63a670681bbfb34bb54ada67391c2ab394f4d6182f631712bf57b5376e83f
data/Rakefile CHANGED
@@ -1 +1,9 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
+
3
+ desc 'Initialize console'
4
+ task :console do
5
+ require 'pry'
6
+ require 'qti'
7
+ ARGV.clear
8
+ Pry.start
9
+ end
@@ -5,11 +5,11 @@ module Qti
5
5
  module Models
6
6
  class Assessment < Qti::V1::Models::Base
7
7
  def title
8
- @title ||= xpath_with_single_check('//assessment/@title').content
8
+ @title ||= xpath_with_single_check('.//xmlns:assessment/@title')&.content
9
9
  end
10
10
 
11
11
  def assessment_items
12
- @doc.xpath('//item')
12
+ @doc.xpath('.//xmlns:item')
13
13
  end
14
14
  end
15
15
  end
@@ -7,8 +7,6 @@ module Qti
7
7
  class AssessmentItem < Qti::V1::Models::Base
8
8
  attr_reader :doc
9
9
 
10
- ScoringData = Struct.new(:values, :rcardinality)
11
-
12
10
  def initialize(item)
13
11
  @doc = item
14
12
  end
@@ -16,8 +14,8 @@ module Qti
16
14
  def item_body
17
15
  @item_body ||= begin
18
16
  node = @doc.dup
19
- presentation = node.at_xpath('./presentation')
20
- prompt = presentation.at_xpath('//material/mattext').content
17
+ presentation = node.at_xpath('.//xmlns:presentation')
18
+ prompt = presentation.at_xpath('.//xmlns:mattext').content
21
19
  sanitize_content!(prompt)
22
20
  end
23
21
  end
@@ -32,12 +30,12 @@ module Qti
32
30
 
33
31
  def points_possible
34
32
  @points_possible ||= begin
35
- @doc.at_xpath('.//decvar/@maxvalue')&.value || @doc.at_xpath('.//decvar/@defaultval')&.value
33
+ @doc.at_xpath('.//xmlns:decvar/@maxvalue')&.value || @doc.at_xpath('.//xmlns:decvar/@defaultval')&.value
36
34
  end
37
35
  end
38
36
 
39
37
  def rcardinality
40
- @rcardinality ||= @doc.at_xpath('.//response_lid/@rcardinality').value
38
+ @rcardinality ||= @doc.at_xpath('.//xmlns:response_lid/@rcardinality').value
41
39
  end
42
40
 
43
41
  def interaction_model
@@ -47,29 +45,7 @@ module Qti
47
45
  end
48
46
 
49
47
  def scoring_data_structs
50
- @scoring_data_structs ||=
51
- if ordering?
52
- scoring_data_structs_for_ordering
53
- else
54
- default_scoring_data_structs
55
- end
56
- end
57
-
58
- private
59
-
60
- def default_scoring_data_structs
61
- choice_nodes = doc.xpath('.//respcondition')
62
- choice_nodes.select { |choice_node| choice_node.at_xpath('.//setvar').content.to_f.positive? }
63
- .map { |value_node| ScoringData.new(value_node.at_xpath('.//varequal').content, rcardinality) }
64
- end
65
-
66
- def scoring_data_structs_for_ordering
67
- correct_order = @doc.xpath('.//conditionvar/varequal').map{|node| node.content }
68
- [ScoringData.new(correct_order, rcardinality)]
69
- end
70
-
71
- def ordering?
72
- rcardinality == 'Ordered'
48
+ @scoring_data_structs ||= interaction_model.scoring_data_structs
73
49
  end
74
50
  end
75
51
  end
@@ -0,0 +1,33 @@
1
+ module Qti
2
+ module V1
3
+ module Models
4
+ module Interactions
5
+ class BaseInteraction < Qti::V1::Models::Base
6
+ attr_reader :node
7
+
8
+ def self.matches(node)
9
+ false
10
+ end
11
+
12
+ def initialize(node)
13
+ @node = node
14
+ end
15
+
16
+ def shuffled?
17
+ @node.at_xpath('.//xmlns:render_choice/@shuffle')&.value == 'Yes'
18
+ end
19
+
20
+ def scoring_data_structs
21
+ raise NotImplementedError
22
+ end
23
+
24
+ private
25
+
26
+ def rcardinality
27
+ @rcardinality ||= @node.at_xpath('.//xmlns:response_lid/@rcardinality').value
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,44 @@
1
+ module Qti
2
+ module V1
3
+ module Models
4
+ module Interactions
5
+ class ChoiceInteraction < BaseInteraction
6
+ # This will know if a class matches
7
+ def self.matches(node)
8
+ matches = node.xpath('.//xmlns:response_lid')
9
+ return false if matches.count > 1 || matches.empty?
10
+ rcardinality = matches.first.attributes['rcardinality']&.value || 'Single'
11
+ return false if rcardinality == 'Ordered'
12
+ new(node)
13
+ end
14
+
15
+ def initialize(node)
16
+ @node = node
17
+ end
18
+
19
+ def shuffled?
20
+ node.at_xpath('.//xmlns:render_choice/@shuffle')&.value == 'Yes'
21
+ end
22
+
23
+ def answers
24
+ @answers ||= answer_nodes.map do |node|
25
+ V1::Models::Choices::LogicalIdentifierChoice.new(node)
26
+ end
27
+ end
28
+
29
+ def scoring_data_structs
30
+ choice_nodes = node.xpath('.//xmlns:respcondition')
31
+ set_var_nodes = choice_nodes.select { |choice_node| choice_node.at_xpath('.//xmlns:setvar').content.to_f.positive? }
32
+ set_var_nodes.map { |value_node| ScoringData.new(value_node.at_xpath('.//xmlns:varequal').content, rcardinality) }
33
+ end
34
+
35
+ private
36
+
37
+ def answer_nodes
38
+ node.xpath('.//xmlns:response_label')
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,60 @@
1
+ module Qti
2
+ module V1
3
+ module Models
4
+ module Interactions
5
+ class MatchInteraction < BaseInteraction
6
+ attr_reader :node
7
+
8
+ def self.matches(node)
9
+ matches = node.xpath('.//xmlns:response_lid')
10
+ return false if matches.count <= 1
11
+ new(node)
12
+ end
13
+
14
+ def initialize(node)
15
+ @node = node
16
+ end
17
+
18
+ def answers
19
+ @answers ||= answer_nodes.map do |node|
20
+ V1::Models::Choices::LogicalIdentifierChoice.new(node)
21
+ end
22
+ end
23
+
24
+ def questions
25
+ node.xpath('.//xmlns:response_lid').map do |lid_node|
26
+ question_body = lid_node.at_xpath('.//xmlns:mattext').text
27
+ { id: lid_node.attributes['ident'].value, question_body: question_body }
28
+ end
29
+ end
30
+
31
+ def scoring_data_structs
32
+ matches = node.xpath('.//xmlns:varequal').map do |node|
33
+ [node.attributes['respident'].value, answers_map[node.content]]
34
+ end
35
+ [Models::ScoringData.new(Hash[matches], rcardinality)]
36
+ end
37
+
38
+ private
39
+
40
+ def answers_map
41
+ @answers_map ||= answers.reduce({}) do |acc, answer|
42
+ acc.update answer.identifier => answer.item_body
43
+ end
44
+ end
45
+
46
+ def answer_nodes
47
+ responses = []
48
+ response_ids = {}
49
+ node.xpath('.//xmlns:response_label').each do |answer_node|
50
+ ident = answer_node.attributes['ident'].value
51
+ responses << answer_node unless response_ids.key? ident
52
+ response_ids[ident] = 1
53
+ end
54
+ responses
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,23 @@
1
+ module Qti
2
+ module V1
3
+ module Models
4
+ module Interactions
5
+ class OrderingInteraction < ChoiceInteraction
6
+ # This will know if a class matches
7
+ def self.matches(node)
8
+ matches = node.xpath('.//xmlns:response_lid')
9
+ return false if matches.count > 1 || matches.empty?
10
+ rcardinality = matches.first.attributes['rcardinality']&.value || 'Single'
11
+ return false if rcardinality != 'Ordered'
12
+ new(node)
13
+ end
14
+
15
+ def scoring_data_structs
16
+ correct_order = node.xpath('.//xmlns:varequal').map(&:content)
17
+ [ScoringData.new(correct_order, rcardinality)]
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -2,14 +2,12 @@ module Qti
2
2
  module V1
3
3
  module Models
4
4
  module Interactions
5
- class StringInteraction < Qti::V1::Models::Base
5
+ class StringInteraction < BaseInteraction
6
6
  @node_name = 'response_str'.freeze
7
7
  # This will know if a class matches
8
8
  def self.matches(node)
9
9
  matches = node.children.children.select { |n| n.name == @node_name }
10
10
  return false if matches.empty?
11
-
12
- raise Qti::UnsupportedSchema if matches.size > 1
13
11
  new(matches.first)
14
12
  end
15
13
 
@@ -14,9 +14,7 @@ module Qti
14
14
  match = interaction_class.matches(node)
15
15
  result << match if match
16
16
  end
17
-
18
- raise UnsupportedSchema if matches.size > 1
19
-
17
+ raise UnsupportedSchema if matches.size != 1
20
18
  matches.first
21
19
  end
22
20
  end
@@ -0,0 +1,7 @@
1
+ module Qti
2
+ module V1
3
+ module Models
4
+ ScoringData = Struct.new(:values, :rcardinality)
5
+ end
6
+ end
7
+ end
@@ -39,16 +39,12 @@ module Qti
39
39
 
40
40
  def interaction_model
41
41
  @interaction_model ||= begin
42
- V2::Models::Interactions.interaction_model(item_body_node)
42
+ V2::Models::Interactions.interaction_model(@doc)
43
43
  end
44
44
  end
45
45
 
46
46
  def scoring_data_structs
47
- @scoring_data_structs ||= begin
48
- type = xpath_with_single_check('//xmlns:responseDeclaration/@baseType').content
49
- value_nodes = @doc.xpath('//xmlns:responseDeclaration/xmlns:correctResponse/xmlns:value')
50
- value_nodes.map { |value_node| ScoringData.new(value_node.content, type) }
51
- end
47
+ @scoring_data_structs ||= interaction_model.scoring_data_structs
52
48
  end
53
49
 
54
50
  private
@@ -0,0 +1,12 @@
1
+ require 'qti/v2/models/base'
2
+
3
+ module Qti
4
+ module V2
5
+ module Models
6
+ module Choices
7
+ class SimpleAssociableChoice < SimpleChoice
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,43 @@
1
+ module Qti
2
+ module V2
3
+ module Models
4
+ module Interactions
5
+ class BaseInteraction < Qti::V2::Models::Base
6
+ attr_reader :node
7
+
8
+ def self.matches(node)
9
+ false
10
+ end
11
+
12
+ def initialize(node)
13
+ @node = node
14
+ end
15
+
16
+ def shuffled?
17
+ @node.at_xpath('.//render_choice/@shuffle')&.value == 'Yes'
18
+ end
19
+
20
+ def scoring_data_structs
21
+ @scoring_data_structs ||= begin
22
+ type = xpath_with_single_check('//xmlns:responseDeclaration/@baseType').content
23
+ value_nodes = node.xpath('//xmlns:responseDeclaration/xmlns:correctResponse/xmlns:value')
24
+ value_nodes.map { |value_node| Models::ScoringData.new(value_node.content, type) }
25
+ end
26
+ end
27
+
28
+ def xpath_with_single_check(xpath)
29
+ node_list = node.xpath(xpath)
30
+ raise Qti::ParseError, 'Too many matches' if node_list.count > 1
31
+ node_list.first
32
+ end
33
+
34
+ private
35
+
36
+ def rcardinality
37
+ @rcardinality ||= @node.at_xpath('.//response_lid/@rcardinality').value
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -2,25 +2,15 @@ module Qti
2
2
  module V2
3
3
  module Models
4
4
  module Interactions
5
- class ChoiceInteraction < Qti::V2::Models::Base
6
- @node_name = 'choiceInteraction'.freeze
7
- # This will know if a class matches
5
+ class ChoiceInteraction < BaseInteraction
8
6
  def self.matches(node)
9
- matches = node.children.filter(INTERACTION_ELEMENTS_CSS).select { |n| n.name == @node_name }
7
+ matches = node.xpath('.//xmlns:choiceInteraction')
10
8
  return false if matches.empty?
11
9
 
12
10
  raise Qti::UnsupportedSchema if matches.size > 1
13
11
  new(matches.first)
14
12
  end
15
13
 
16
- def initialize(node)
17
- @node = node
18
- end
19
-
20
- def shuffled?
21
- @node.attributes['shuffle'].value == 'true'
22
- end
23
-
24
14
  def answers
25
15
  @answers ||= answer_nodes.map do |node|
26
16
  V2::Models::Choices::SimpleChoice.new(node)
@@ -2,7 +2,7 @@ module Qti
2
2
  module V2
3
3
  module Models
4
4
  module Interactions
5
- class ExtendedTextInteraction < Qti::V2::Models::Base
5
+ class ExtendedTextInteraction < BaseInteraction
6
6
  @node_name = 'extendedTextInteraction'.freeze
7
7
  # This will know if a class matches
8
8
  def self.matches(node)
@@ -13,10 +13,6 @@ module Qti
13
13
  new(matches.first)
14
14
  end
15
15
 
16
- def initialize(node)
17
- @node = node
18
- end
19
-
20
16
  # not used yet
21
17
  def expected_lines
22
18
  @node.attributes['expectedLines']&.value&.to_i || 0
@@ -0,0 +1,52 @@
1
+ module Qti
2
+ module V2
3
+ module Models
4
+ module Interactions
5
+ class MatchInteraction < BaseInteraction
6
+ def self.matches(node)
7
+ matches = node.xpath('//xmlns:matchInteraction')
8
+ return false if matches.empty?
9
+
10
+ raise Qti::UnsupportedSchema if matches.size > 1
11
+ new(node)
12
+ end
13
+
14
+ def questions
15
+ node.xpath('.//xmlns:correctResponse//xmlns:value')
16
+ .map { |value| value.content.split.first }
17
+ .map { |id| { id: id, question_body: choices_by_identifier[id].content } }
18
+ end
19
+
20
+ def answers
21
+ answer_nodes.map { |node| Choices::SimpleAssociableChoice.new(node) }
22
+ end
23
+
24
+ def scoring_data_structs
25
+ question_response_pairs = node.xpath('.//xmlns:correctResponse//xmlns:value').map do |value|
26
+ value.content.split
27
+ end
28
+ question_response_id_mapping = Hash[question_response_pairs]
29
+ data = question_response_id_mapping.reduce({}) do |acc, pair|
30
+ question_id, answer_id = pair
31
+ acc.update question_id => choices_by_identifier[answer_id].content
32
+ end
33
+ [ScoringData.new(data, 'directedPair')]
34
+ end
35
+
36
+ private
37
+
38
+ def choices_by_identifier
39
+ choices = node.xpath('.//xmlns:simpleAssociableChoice')
40
+ @choices_by_identifier ||= choices.reduce({}) do |acc, choice|
41
+ acc.update choice.attributes['identifier'].value => choice
42
+ end
43
+ end
44
+
45
+ def answer_nodes
46
+ node.xpath('.//xmlns:matchInteraction//xmlns:simpleMatchSet[2]//xmlns:simpleAssociableChoice')
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,7 @@
1
+ module Qti
2
+ module V2
3
+ module Models
4
+ ScoringData = Struct.new(:values, :type)
5
+ end
6
+ end
7
+ end
data/lib/qti.rb CHANGED
@@ -52,14 +52,20 @@ require 'qti/models/manifest'
52
52
  require 'qti/models/base'
53
53
 
54
54
  require 'qti/v1/models/base'
55
+ require 'qti/v1/models/interactions/base_interaction'
56
+ require 'qti/v1/models/interactions/choice_interaction'
55
57
  require 'qti/v1/models/assessment'
56
58
  require 'qti/v1/models/assessment_item'
57
59
  require 'qti/v1/models/choices/logical_identifier_choice'
60
+ require 'qti/v1/models/scoring_data'
58
61
 
59
62
  require 'qti/v2/models/base'
63
+ require 'qti/v2/models/interactions/base_interaction'
60
64
  require 'qti/v2/models/choices/simple_choice'
65
+ require 'qti/v2/models/choices/simple_associable_choice'
61
66
  require 'qti/v2/models/assessment_item'
62
67
  require 'qti/v2/models/assessment_test'
68
+ require 'qti/v2/models/scoring_data'
63
69
 
64
70
  require 'zip'
65
71
  require 'qti/exporter'
@@ -0,0 +1,74 @@
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
+ <item title="" ident="4772">
4
+ <itemmetadata>
5
+ <qtimetadata>
6
+ <qtimetadatafield>
7
+ <fieldlabel>question_type</fieldlabel>
8
+ <fieldentry>matching_question</fieldentry>
9
+ </qtimetadatafield>
10
+ </qtimetadata>
11
+ </itemmetadata>
12
+ <presentation>
13
+ <material>
14
+ <mattext texttype="text/html">Match the statements with the correct type of microscope</mattext>
15
+ </material>
16
+ <response_lid ident="question_1" rcardinality="Single">
17
+ <material>
18
+ <mattext texttype="text/plain">Light Microscope</mattext>
19
+ </material>
20
+ <render_choice>
21
+ <response_label ident="8440">
22
+ <material>
23
+ <mattext texttype="text/plain">Magnify up to about 400 times. Sometimes requires colored staining of cells.</mattext>
24
+ </material>
25
+ </response_label>
26
+ <response_label ident="5653">
27
+ <material>
28
+ <mattext texttype="text/plain">Uses a beam of electrons. Can provide details of cells' internal structure.</mattext>
29
+ </material>
30
+ </response_label>
31
+ </render_choice>
32
+ </response_lid>
33
+ <response_lid ident="question_2" rcardinality="Single">
34
+ <material>
35
+ <mattext texttype="text/plain">Electron Microscopes</mattext>
36
+ </material>
37
+ <render_choice>
38
+ <response_label ident="8440">
39
+ <material>
40
+ <mattext texttype="text/plain">Magnify up to about 400 times. Sometimes requires colored staining of cells.</mattext>
41
+ </material>
42
+ </response_label>
43
+ <response_label ident="5653">
44
+ <material>
45
+ <mattext texttype="text/plain">Uses a beam of electrons. Can provide details of cells' internal structure.</mattext>
46
+ </material>
47
+ </response_label>
48
+ <response_label ident="7777">
49
+ <material>
50
+ <mattext texttype="text/plain">A distractor answer.</mattext>
51
+ </material>
52
+ </response_label>
53
+ </render_choice>
54
+ </response_lid>
55
+ </presentation>
56
+ <resprocessing>
57
+ <outcomes>
58
+ <decvar maxvalue="100" minvalue="0" varname="SCORE" vartype="Decimal"/>
59
+ </outcomes>
60
+ <respcondition>
61
+ <conditionvar>
62
+ <varequal respident="question_1">8440</varequal>
63
+ </conditionvar>
64
+ <setvar varname="SCORE" action="Add">50.00</setvar>
65
+ </respcondition>
66
+ <respcondition>
67
+ <conditionvar>
68
+ <varequal respident="question_2">5653</varequal>
69
+ </conditionvar>
70
+ <setvar varname="SCORE" action="Add">50.00</setvar>
71
+ </respcondition>
72
+ </resprocessing>
73
+ </item>
74
+ </questestinterop>
@@ -1,6 +1,6 @@
1
1
  <?xml version="1.0" encoding="ISO-8859-1"?>
2
2
 
3
- <questestinterop>
3
+ <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">
4
4
 
5
5
  <assessment title="Quiz" ident="A1001">
6
6
  <section title="Main" ident="S1002">
@@ -1,6 +1,6 @@
1
1
  <?xml version="1.0" encoding="ISO-8859-1"?>
2
2
 
3
- <questestinterop>
3
+ <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">
4
4
 
5
5
  <assessment title="1.2 Import Quiz" ident="A1001">
6
6
  <section title="Main" ident="S1002">