qti 0.9.8 → 0.9.9

Sign up to get free protection for your applications and to get access to all the features.
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