qti 0.9.8 → 0.9.9

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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/lib/qti.rb +7 -0
  3. data/lib/qti/v1/models/interactions.rb +0 -1
  4. data/lib/qti/v1/models/interactions/numeric_interaction.rb +4 -14
  5. data/lib/qti/v1/models/numerics/exact_match.rb +43 -0
  6. data/lib/qti/v1/models/numerics/margin_error.rb +51 -0
  7. data/lib/qti/v1/models/numerics/precision.rb +65 -0
  8. data/lib/qti/v1/models/numerics/scoring_base.rb +29 -0
  9. data/lib/qti/v1/models/numerics/scoring_data.rb +26 -0
  10. data/lib/qti/v1/models/numerics/scoring_node.rb +29 -0
  11. data/lib/qti/v1/models/numerics/within_range.rb +36 -0
  12. data/lib/qti/version.rb +1 -1
  13. data/spec/fixtures/items_1.2/{numeric.xml → numeric_exact_match.xml} +0 -0
  14. data/spec/fixtures/items_1.2/numeric_margin_error.xml +58 -0
  15. data/spec/fixtures/items_1.2/numeric_precision.xml +58 -0
  16. data/spec/fixtures/items_1.2/numeric_within_range.xml +53 -0
  17. data/spec/gemfiles/nokogiri-1.8.gemfile.lock +35 -18
  18. data/spec/lib/qti/v1/models/interactions/numeric_interaction_spec.rb +92 -27
  19. data/spec/lib/qti/v1/models/numerics/exact_match_spec.rb +84 -0
  20. data/spec/lib/qti/v1/models/numerics/margin_error_spec.rb +80 -0
  21. data/spec/lib/qti/v1/models/numerics/precision_spec.rb +88 -0
  22. data/spec/lib/qti/v1/models/numerics/scoring_base_spec.rb +35 -0
  23. data/spec/lib/qti/v1/models/numerics/scoring_node_spec.rb +29 -0
  24. data/spec/lib/qti/v1/models/numerics/within_range_spec.rb +64 -0
  25. data/spec/spec_helper.rb +1 -0
  26. metadata +44 -17
  27. data/spec/gemfiles/nokogiri-1.6.gemfile.lock +0 -127
  28. data/spec/gemfiles/rails-4.2.gemfile.lock +0 -204
  29. data/spec/gemfiles/rails-5.0.gemfile.lock +0 -210
  30. data/spec/gemfiles/rails-5.1.gemfile.lock +0 -210
  31. data/spec/gemfiles/sanitize-4.2.gemfile.lock +0 -127
  32. data/spec/gemfiles/sanitize-4.5.gemfile.lock +0 -127
@@ -0,0 +1,53 @@
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
+ <assessment ident="i594c176f4526d4d01741c4ae1b59d93c" title="Quiz.1.10">
4
+ <qtimetadata>
5
+ <qtimetadatafield>
6
+ <fieldlabel>cc_maxattempts</fieldlabel>
7
+ <fieldentry>1</fieldentry>
8
+ </qtimetadatafield>
9
+ </qtimetadata>
10
+ <section ident="root_section">
11
+ <item ident="i483a0f31974d8fbeed56d14354466279" title="Question1">
12
+ <itemmetadata>
13
+ <qtimetadata>
14
+ <qtimetadatafield>
15
+ <fieldlabel>question_type</fieldlabel>
16
+ <fieldentry>numerical_question</fieldentry>
17
+ </qtimetadatafield>
18
+ <qtimetadatafield>
19
+ <fieldlabel>points_possible</fieldlabel>
20
+ <fieldentry>1.0</fieldentry>
21
+ </qtimetadatafield>
22
+ <qtimetadatafield>
23
+ <fieldlabel>assessment_question_identifierref</fieldlabel>
24
+ <fieldentry>i4f7326d31c16ec8a46320153fe13581d</fieldentry>
25
+ </qtimetadatafield>
26
+ </qtimetadata>
27
+ </itemmetadata>
28
+ <presentation>
29
+ <material>
30
+ <mattext texttype="text/html">&lt;div&gt;&lt;p&gt;QQ1&lt;/p&gt;&lt;/div&gt;</mattext>
31
+ </material>
32
+ <response_str ident="response1" rcardinality="Single">
33
+ <render_fib fibtype="Decimal">
34
+ <response_label ident="answer1"/>
35
+ </render_fib>
36
+ </response_str>
37
+ </presentation>
38
+ <resprocessing>
39
+ <outcomes>
40
+ <decvar maxvalue="100" minvalue="0" varname="SCORE" vartype="Decimal"/>
41
+ </outcomes>
42
+ <respcondition continue="No">
43
+ <conditionvar>
44
+ <vargte respident="response1">1.0</vargte>
45
+ <varlte respident="response1">22.0</varlte>
46
+ </conditionvar>
47
+ <setvar action="Set" varname="SCORE">100</setvar>
48
+ </respcondition>
49
+ </resprocessing>
50
+ </item>
51
+ </section>
52
+ </assessment>
53
+ </questestinterop>
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: ../..
3
3
  specs:
4
- qti (0.9.8)
4
+ qti (0.9.9)
5
+ actionview (>= 4.2.0)
5
6
  activesupport (>= 4.2.9, < 5.2)
6
7
  dry-struct (~> 0.2.1)
7
8
  dry-types (~> 0.12.0)
@@ -12,12 +13,19 @@ PATH
12
13
  GEM
13
14
  remote: https://rubygems.org/
14
15
  specs:
16
+ actionview (5.1.4)
17
+ activesupport (= 5.1.4)
18
+ builder (~> 3.1)
19
+ erubi (~> 1.4)
20
+ rails-dom-testing (~> 2.0)
21
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
15
22
  activesupport (5.1.4)
16
23
  concurrent-ruby (~> 1.0, >= 1.0.2)
17
24
  i18n (~> 0.7)
18
25
  minitest (~> 5.1)
19
26
  tzinfo (~> 1.1)
20
27
  ast (2.3.0)
28
+ builder (3.2.3)
21
29
  byebug (9.1.0)
22
30
  coderay (1.1.2)
23
31
  concurrent-ruby (1.0.5)
@@ -49,11 +57,15 @@ GEM
49
57
  dry-equalizer (~> 0.2)
50
58
  dry-logic (~> 0.4, >= 0.4.2)
51
59
  inflecto (~> 0.0.0, >= 0.0.2)
60
+ erubi (1.7.0)
52
61
  i18n (0.9.1)
53
62
  concurrent-ruby (~> 1.0)
54
63
  ice_nine (0.11.2)
55
64
  inflecto (0.0.2)
56
65
  json (2.1.0)
66
+ loofah (2.1.1)
67
+ crass (~> 1.0.2)
68
+ nokogiri (>= 1.5.9)
57
69
  method_source (0.9.0)
58
70
  mini_portile2 (2.3.0)
59
71
  minitest (5.10.3)
@@ -61,29 +73,34 @@ GEM
61
73
  mini_portile2 (~> 2.3.0)
62
74
  nokogumbo (1.4.13)
63
75
  nokogiri
64
- parallel (1.12.0)
65
- parser (2.4.0.0)
66
- ast (~> 2.2)
76
+ parallel (1.12.1)
77
+ parser (2.4.0.2)
78
+ ast (~> 2.3)
67
79
  powerpack (0.1.1)
68
- pry (0.11.1)
80
+ pry (0.11.3)
69
81
  coderay (~> 1.1.0)
70
82
  method_source (~> 0.9.0)
83
+ rails-dom-testing (2.0.3)
84
+ activesupport (>= 4.2.0)
85
+ nokogiri (>= 1.6)
86
+ rails-html-sanitizer (1.0.3)
87
+ loofah (~> 2.0)
71
88
  rainbow (2.2.2)
72
89
  rake
73
90
  rake (0.9.6)
74
- rspec (3.6.0)
75
- rspec-core (~> 3.6.0)
76
- rspec-expectations (~> 3.6.0)
77
- rspec-mocks (~> 3.6.0)
78
- rspec-core (3.6.0)
79
- rspec-support (~> 3.6.0)
80
- rspec-expectations (3.6.0)
91
+ rspec (3.7.0)
92
+ rspec-core (~> 3.7.0)
93
+ rspec-expectations (~> 3.7.0)
94
+ rspec-mocks (~> 3.7.0)
95
+ rspec-core (3.7.0)
96
+ rspec-support (~> 3.7.0)
97
+ rspec-expectations (3.7.0)
81
98
  diff-lcs (>= 1.2.0, < 2.0)
82
- rspec-support (~> 3.6.0)
83
- rspec-mocks (3.6.0)
99
+ rspec-support (~> 3.7.0)
100
+ rspec-mocks (3.7.0)
84
101
  diff-lcs (>= 1.2.0, < 2.0)
85
- rspec-support (~> 3.6.0)
86
- rspec-support (3.6.0)
102
+ rspec-support (~> 3.7.0)
103
+ rspec-support (3.7.0)
87
104
  rubocop (0.50.0)
88
105
  parallel (~> 1.10)
89
106
  parser (>= 2.3.3.1, < 3.0)
@@ -91,7 +108,7 @@ GEM
91
108
  rainbow (>= 2.2.2, < 3.0)
92
109
  ruby-progressbar (~> 1.7)
93
110
  unicode-display_width (~> 1.0, >= 1.0.1)
94
- ruby-progressbar (1.8.3)
111
+ ruby-progressbar (1.9.0)
95
112
  rubyzip (1.2.1)
96
113
  sanitize (4.5.0)
97
114
  crass (~> 1.0.2)
@@ -125,4 +142,4 @@ DEPENDENCIES
125
142
  wwtd (~> 1.3)
126
143
 
127
144
  BUNDLED WITH
128
- 1.15.4
145
+ 1.16.0
@@ -2,48 +2,113 @@ require 'spec_helper'
2
2
 
3
3
  describe Qti::V1::Models::Interactions::NumericInteraction do
4
4
  let(:fixtures_path) { File.join('spec', 'fixtures', 'items_1.2') }
5
- let(:file_path) { File.join(fixtures_path, 'numeric.xml') }
5
+ let(:file_path) { File.join(fixtures_path, xml_file_name) }
6
6
  let(:test_object) { Qti::V1::Models::Assessment.from_path!(file_path) }
7
7
  let(:assessment_item_refs) { test_object.assessment_items }
8
8
 
9
- shared_examples_for 'answers' do
10
- it 'returns the answers' do
11
- expect(loaded_class.answers.count).to eq answer_count
12
- expect(loaded_class.answers.first).to be_an_instance_of(Qti::V1::Models::Choices::FillBlankChoice)
9
+ shared_examples_for 'scoring_data_structs' do |items|
10
+ items.each do |key, values|
11
+ it 'returns the scoring_data_structs' do
12
+ expect(loaded_class.scoring_data_structs.map(&key)).to eq(values)
13
+ end
13
14
  end
14
15
  end
15
16
 
16
- shared_examples_for 'scoring_data_structs' do
17
- it 'returns the scoring_data_structs' do
18
- expect(loaded_class.scoring_data_structs.map(&:id)).to eq(scoring_data_ids)
19
- expect(loaded_class.scoring_data_structs.map(&:values)).to eq(scoring_data_values)
17
+ context 'exact match' do
18
+ let(:xml_file_name) { 'numeric_exact_match.xml' }
19
+ context 'the first item in numeric_exact_match.xml' do
20
+ let(:loaded_class) { described_class.new(assessment_item_refs.first, test_object) }
21
+ let(:answer_count) { 1 }
22
+
23
+ include_examples 'scoring_data_structs', id: %w[response1], value: %w[1234.0]
24
+ end
25
+
26
+ context 'the second item in numeric_exact_match.xml' do
27
+ let(:loaded_class) { described_class.new(assessment_item_refs[1], test_object) }
28
+ let(:scoring_data_ids) { %w[response1 response1 response1] }
29
+ let(:scoring_data_values) { %w[4321.0 1234.0 1111.0] }
30
+ let(:verification_data) do
31
+ [{ id: scoring_data_ids }, { value: scoring_data_values }]
32
+ end
33
+ let(:answer_count) { 3 }
34
+
35
+ include_examples(
36
+ 'scoring_data_structs',
37
+ id: %w[response1 response1 response1],
38
+ value: %w[4321.0 1234.0 1111.0]
39
+ )
40
+ end
41
+
42
+ context '#item_body' do
43
+ let(:loaded_class) { described_class.new(assessment_item_refs.first, test_object) }
44
+ it 'returns right title' do
45
+ expect(loaded_class.item_body).to eq '<div><p>question 1</p></div>'
46
+ end
20
47
  end
21
48
  end
22
49
 
23
- context 'the first item in numeric.xml' do
24
- let(:loaded_class) { described_class.new(assessment_item_refs.first, test_object) }
25
- let(:scoring_data_ids) { %w[response1] }
26
- let(:scoring_data_values) { %w[1234.0] }
27
- let(:answer_count) { 1 }
50
+ context 'margin error' do
51
+ let(:xml_file_name) { 'numeric_margin_error.xml' }
52
+ context 'the first item in numeric_margin_error.xml' do
53
+ let(:loaded_class) { described_class.new(assessment_item_refs.first, test_object) }
54
+ let(:answer_count) { 1 }
55
+
56
+ include_examples 'scoring_data_structs',
57
+ id: %w[response1],
58
+ type: %w[marginOfError],
59
+ value: %w[77.0],
60
+ margin: %w[7.0],
61
+ margin_type: %w[absolute]
62
+ end
28
63
 
29
- include_examples 'answers'
30
- include_examples 'scoring_data_structs'
64
+ context '#item_body' do
65
+ let(:loaded_class) { described_class.new(assessment_item_refs.first, test_object) }
66
+ it 'returns right title' do
67
+ expect(loaded_class.item_body).to eq '<div><p>QQ1.margin error</p></div>'
68
+ end
69
+ end
31
70
  end
32
71
 
33
- context 'the second item in numeric.xml' do
34
- let(:loaded_class) { described_class.new(assessment_item_refs[1], test_object) }
35
- let(:scoring_data_ids) { %w[response1 response1 response1] }
36
- let(:scoring_data_values) { %w[4321.0 1234.0 1111.0] }
37
- let(:answer_count) { 3 }
72
+ context 'precision' do
73
+ let(:xml_file_name) { 'numeric_precision.xml' }
74
+ context 'the first item in numeric_precision.xml' do
75
+ let(:loaded_class) { described_class.new(assessment_item_refs.first, test_object) }
76
+ let(:answer_count) { 1 }
77
+
78
+ include_examples 'scoring_data_structs',
79
+ id: %w[response1],
80
+ type: %w[preciseResponse],
81
+ value: %w[1000.00],
82
+ precision: %w[6],
83
+ precision_type: %w[significantDigits]
84
+ end
38
85
 
39
- include_examples 'answers'
40
- include_examples 'scoring_data_structs'
86
+ context '#item_body' do
87
+ let(:loaded_class) { described_class.new(assessment_item_refs.first, test_object) }
88
+ it 'returns right title' do
89
+ expect(loaded_class.item_body).to eq '<div><p>QQ1.presision ss</p></div>'
90
+ end
91
+ end
41
92
  end
42
93
 
43
- context '#item_body' do
44
- let(:loaded_class) { described_class.new(assessment_item_refs.first, test_object) }
45
- it 'returns right title' do
46
- expect(loaded_class.item_body).to eq '<div><p>question 1</p></div>'
94
+ context 'wthin range' do
95
+ let(:xml_file_name) { 'numeric_within_range.xml' }
96
+ context 'the first item in numeric_within_range.xml' do
97
+ let(:loaded_class) { described_class.new(assessment_item_refs.first, test_object) }
98
+ let(:answer_count) { 1 }
99
+
100
+ include_examples 'scoring_data_structs',
101
+ id: %w[response1],
102
+ type: %w[withinARange],
103
+ start: %w[1.0],
104
+ end: %w[22.0]
105
+ end
106
+
107
+ context '#item_body' do
108
+ let(:loaded_class) { described_class.new(assessment_item_refs.first, test_object) }
109
+ it 'returns right title' do
110
+ expect(loaded_class.item_body).to eq '<div><p>QQ1</p></div>'
111
+ end
47
112
  end
48
113
  end
49
114
  end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ describe Qti::V1::Models::Numerics::ExactMatch do
4
+ context '#scoring_data' do
5
+ let(:equal_node) do
6
+ double(
7
+ attributes: { 'respident' => double(value: 'response1') },
8
+ content: '1234'
9
+ )
10
+ end
11
+
12
+ let(:gte_node_value) { '1234' }
13
+ let(:gte_node) do
14
+ double(
15
+ attributes: { 'respident' => double(value: 'response1') },
16
+ content: gte_node_value
17
+ )
18
+ end
19
+
20
+ let(:lte_node_value) { '1234' }
21
+ let(:lte_node) do
22
+ double(
23
+ attributes: { 'respident' => double(value: 'response1') },
24
+ content: lte_node_value
25
+ )
26
+ end
27
+
28
+ let(:scoring_node) do
29
+ double(
30
+ equal_node: equal_node,
31
+ gte_node: gte_node,
32
+ lte_node: lte_node
33
+ )
34
+ end
35
+
36
+ subject { described_class.new(scoring_node) }
37
+
38
+ context 'missing node' do
39
+ context 'equal_node is missing' do
40
+ let(:equal_node) { nil }
41
+ it 'returns nil' do
42
+ expect(subject.scoring_data).to be_nil
43
+ end
44
+ end
45
+
46
+ context 'gte_node is missing' do
47
+ let(:gte_node) { nil }
48
+ it 'returns nil' do
49
+ expect(subject.scoring_data).to be_nil
50
+ end
51
+ end
52
+
53
+ context 'lte_node is missing' do
54
+ let(:lte_node) { nil }
55
+ it 'returns nil' do
56
+ expect(subject.scoring_data).to be_nil
57
+ end
58
+ end
59
+ end
60
+
61
+ context 'mismatching values for exact match' do
62
+ context 'gte_node is mismatching with equal_node' do
63
+ let(:gte_node_value) { '11111' }
64
+ it 'returns nil' do
65
+ expect(subject.scoring_data).to be_nil
66
+ end
67
+ end
68
+
69
+ context 'lte_node is missing' do
70
+ let(:lte_node_value) { '11111' }
71
+ it 'returns nil' do
72
+ expect(subject.scoring_data).to be_nil
73
+ end
74
+ end
75
+ end
76
+
77
+ it 'returns correct attributes' do
78
+ ret_val = subject.scoring_data
79
+ expect(ret_val.id).to eq('response1')
80
+ expect(ret_val.type).to eq('exactResponse')
81
+ expect(ret_val.value).to eq('1234')
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ describe Qti::V1::Models::Numerics::MarginError do
4
+ context '#scoring_data' do
5
+ let(:equal_node) do
6
+ double(
7
+ attributes: { 'respident' => double(value: 'response1') },
8
+ content: '1234'
9
+ )
10
+ end
11
+
12
+ let(:gte_node_value) { '1232' }
13
+ let(:gte_node) do
14
+ double(
15
+ attributes: { 'respident' => double(value: 'response1') },
16
+ content: gte_node_value
17
+ )
18
+ end
19
+
20
+ let(:lte_node_value) { '1236' }
21
+ let(:lte_node) do
22
+ double(
23
+ attributes: { 'respident' => double(value: 'response1') },
24
+ content: lte_node_value
25
+ )
26
+ end
27
+
28
+ let(:scoring_node) do
29
+ double(
30
+ equal_node: equal_node,
31
+ gte_node: gte_node,
32
+ lte_node: lte_node
33
+ )
34
+ end
35
+
36
+ subject { described_class.new(scoring_node) }
37
+
38
+ context 'missing node' do
39
+ context 'equal_node is missing' do
40
+ let(:equal_node) { nil }
41
+ it 'returns nil' do
42
+ expect(subject.scoring_data).to be_nil
43
+ end
44
+ end
45
+
46
+ context 'gte_node is missing' do
47
+ let(:gte_node) { nil }
48
+ it 'returns nil' do
49
+ expect(subject.scoring_data).to be_nil
50
+ end
51
+ end
52
+
53
+ context 'lte_node is missing' do
54
+ let(:lte_node) { nil }
55
+ it 'returns nil' do
56
+ expect(subject.scoring_data).to be_nil
57
+ end
58
+ end
59
+ end
60
+
61
+ context 'mismatching values for exact match' do
62
+ context 'values from gte_node, lte_node, equal_node are same' do
63
+ let(:gte_node_value) { '1234' }
64
+ let(:lte_node_value) { '1234' }
65
+ it 'returns nil' do
66
+ expect(subject.scoring_data).to be_nil
67
+ end
68
+ end
69
+ end
70
+
71
+ it 'returns correct attributes' do
72
+ ret_val = subject.scoring_data
73
+ expect(ret_val.id).to eq('response1')
74
+ expect(ret_val.type).to eq('marginOfError')
75
+ expect(ret_val.value).to eq('1234.0')
76
+ expect(ret_val.margin).to eq('2.0')
77
+ expect(ret_val.margin_type).to eq('absolute')
78
+ end
79
+ end
80
+ end