ncs_mdes 0.12.0 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -61,6 +61,8 @@ module NcsNavigator::Mdes
61
61
  create('3.1', '3.1/NCS_Transmission_Schema_3.1.01.00.xsd', '3.1.01.00')
62
62
  when '3.2'
63
63
  create('3.2', '3.2/NCS_Transmission_Schema_3.2.00.00.xsd', '3.2.00.00')
64
+ when '3.3'
65
+ create('3.3', '3.3/NCS_Transmission_Schema_3.3.00.00.xsd', '3.3.00.00')
64
66
  else
65
67
  raise "MDES #{version} is not supported by this version of ncs_mdes"
66
68
  end
@@ -83,7 +83,7 @@ module NcsNavigator::Mdes
83
83
  '//xs:element[@name="transmission_tables"]/xs:complexType/xs:sequence/xs:element',
84
84
  source_documents.xmlns
85
85
  ).collect { |table_elt|
86
- TransmissionTable.from_element(table_elt, :log => @log)
86
+ TransmissionTable.from_element(table_elt, :log => @log, :heuristic_overrides => heuristic_overrides)
87
87
  }.tap { |tables|
88
88
  tables.each { |t| t.variables.each { |v| v.resolve_type!(types, :log => @log) } }
89
89
  # All types must be resolved before doing FK resolution or
@@ -165,7 +165,7 @@ module NcsNavigator::Mdes
165
165
 
166
166
  def read_types
167
167
  xsd.xpath('//xs:simpleType[@name]', source_documents.xmlns).collect do |type_elt|
168
- VariableType.from_xsd_simple_type(type_elt, :log => @log)
168
+ VariableType.from_xsd_simple_type(type_elt, :log => @log, :heuristic_overrides => heuristic_overrides)
169
169
  end
170
170
  end
171
171
  private :read_types
@@ -12,9 +12,13 @@ module NcsNavigator::Mdes
12
12
  log = options[:log] || NcsNavigator::Mdes.default_logger
13
13
 
14
14
  new(element['name']).tap do |table|
15
+ opts = options.merge({
16
+ :log => log,
17
+ :current_table_name => table.name
18
+ })
15
19
  table.variables = element.
16
20
  xpath('xs:complexType/xs:sequence/xs:element', SourceDocuments.xmlns).
17
- collect { |col_elt| Variable.from_element(col_elt, options) }
21
+ collect { |col_elt| Variable.from_element(col_elt, opts) }
18
22
  end
19
23
  end
20
24
 
@@ -171,5 +175,24 @@ module NcsNavigator::Mdes
171
175
  def diff(other_table, options={})
172
176
  Differences::Entry.compute(self, other_table, DIFF_CRITERIA, options)
173
177
  end
178
+
179
+ ##
180
+ # This method allows for a table to have a multiple column PK. At this
181
+ # writing there are not any, so this is out of an abundance of caution.
182
+ #
183
+ # @return [Array<Variable>] the variables comprising the PK for this table,
184
+ # or an empty array if they can't be determined.
185
+ def primary_key_variables
186
+ pks = variables.select { |v| v.type.name == 'primaryKeyType' }
187
+ if !pks.empty?
188
+ pks
189
+ elsif name == 'study_center'
190
+ variables.select { |v| v.name == 'sc_id' }
191
+ elsif name == 'psu'
192
+ variables.select { |v| v.name == 'psu_id' }
193
+ else
194
+ []
195
+ end
196
+ end
174
197
  end
175
198
  end
@@ -90,21 +90,40 @@ module NcsNavigator::Mdes
90
90
  when '4'; :retired;
91
91
  else element['ncsdoc:status'];
92
92
  end
93
- var.type =
94
- if element['type']
95
- if element['type'] =~ /^xs:/
96
- VariableType.xml_schema_type(element['type'].sub(/^xs:/, ''))
97
- else
98
- VariableType.reference(element['type'])
99
- end
100
- elsif element.elements.collect { |e| e.name } == %w(simpleType)
101
- VariableType.from_xsd_simple_type(element.elements.first, options)
102
- else
103
- log.warn("Could not determine a type for variable #{var.name.inspect} on line #{element.line}")
104
- nil
105
- end
93
+ var.type = type_for(element, var.name, log, options)
94
+ end
95
+ end
96
+
97
+ def type_for(element, name, log, options)
98
+ variable_type_override = deep_key_or_nil(
99
+ options, :heuristic_overrides, 'variable_type_references', options[:current_table_name], name)
100
+
101
+ named_type = variable_type_override || element['type']
102
+
103
+ if named_type
104
+ if named_type =~ /^xs:/
105
+ VariableType.xml_schema_type(named_type.sub(/^xs:/, ''))
106
+ else
107
+ VariableType.reference(named_type)
108
+ end
109
+ elsif element.elements.collect { |e| e.name } == %w(simpleType)
110
+ VariableType.from_xsd_simple_type(element.elements.first, options)
111
+ else
112
+ log.warn("Could not determine a type for variable #{name.inspect} on line #{element.line}")
113
+ nil
114
+ end
115
+ end
116
+ private :type_for
117
+
118
+ def deep_key_or_nil(h, *keys)
119
+ value = h[keys.shift]
120
+ if value.respond_to?(:[]) && !keys.empty?
121
+ deep_key_or_nil(value, *keys)
122
+ else
123
+ value
106
124
  end
107
125
  end
126
+ private :deep_key_or_nil
108
127
  end
109
128
 
110
129
  def initialize(name)
@@ -1,5 +1,5 @@
1
1
  module NcsNavigator
2
2
  module Mdes
3
- VERSION = '0.12.0'
3
+ VERSION = '0.13.0'
4
4
  end
5
5
  end
@@ -19,7 +19,7 @@ National Children's Study's Master Data Element Specification.
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
20
  s.require_paths = ["lib"]
21
21
 
22
- s.add_dependency 'nokogiri', '~> 1.5.6'
22
+ s.add_dependency 'nokogiri', '~> 1.5.7'
23
23
 
24
24
  s.add_development_dependency 'rspec', '~> 2.6.0' # Can't use 2.7.0 due to #477
25
25
  s.add_development_dependency 'rake', '~> 0.9.2'
@@ -200,6 +200,20 @@ module NcsNavigator::Mdes
200
200
  end
201
201
  end
202
202
 
203
+ describe '3.3' do
204
+ let(:version) { '3.3' }
205
+
206
+ include_context 'version docs'
207
+
208
+ it 'has the correct path for the schema' do
209
+ subject.schema.should =~ %r{3.3/NCS_Transmission_Schema_3.3.00.00.xsd$}
210
+ end
211
+
212
+ it 'has a different specification_version' do
213
+ subject.specification_version.should == '3.3.00.00'
214
+ end
215
+ end
216
+
203
217
  it 'fails for an unsupported version' do
204
218
  lambda { SourceDocuments.get('1.0') }.
205
219
  should raise_error('MDES 1.0 is not supported by this version of ncs_mdes')
@@ -31,6 +31,17 @@ module NcsNavigator::Mdes
31
31
  should be_a TransmissionTable
32
32
  end
33
33
 
34
+ it 'passes down heuristic overrides' do
35
+ spec = Specification.new('2.0', :log => logger)
36
+
37
+ TransmissionTable.should_receive(:from_element).
38
+ with(anything, include(:heuristic_overrides => spec.heuristic_overrides)).
39
+ at_least(:once).
40
+ and_return(TransmissionTable.new('mocked'))
41
+
42
+ spec.transmission_tables # force parsing
43
+ end
44
+
34
45
  shared_examples 'tables fully resolved' do
35
46
  let!(:tables) { Specification.new(version, :log => logger).transmission_tables }
36
47
 
@@ -65,6 +76,18 @@ module NcsNavigator::Mdes
65
76
  index = tables.each_with_object(Hash.new(0)) { |t, acc| acc[t.child_instrument_table?] += 1 }
66
77
  index.keys.sort_by { |k| k.inspect }.should == [false, nil, true]
67
78
  end
79
+
80
+ it 'has a primary key for every table' do
81
+ no_pk_tables = tables.select { |table| table.primary_key_variables.empty? }
82
+
83
+ no_pk_tables.should == []
84
+ end
85
+
86
+ it 'has no multi-column primary keys' do
87
+ multi_column_key_tables = tables.select { |table| table.primary_key_variables.size > 1 }
88
+
89
+ multi_column_key_tables.should == []
90
+ end
68
91
  end
69
92
 
70
93
  context 'in version 1.2' do
@@ -115,6 +138,13 @@ module NcsNavigator::Mdes
115
138
 
116
139
  include_examples 'tables fully resolved'
117
140
  end
141
+
142
+ context 'in version 3.3' do
143
+ let(:version) { '3.3' }
144
+ let(:expected_table_count) { 668 }
145
+
146
+ include_examples 'tables fully resolved'
147
+ end
118
148
  end
119
149
 
120
150
  describe '#specification_version' do
@@ -198,6 +228,14 @@ module NcsNavigator::Mdes
198
228
  disposition_codes.size.should == 332
199
229
  end
200
230
  end
231
+
232
+ context 'in version 3.3' do
233
+ let(:disposition_codes) { Specification.new('3.3', :log => logger).disposition_codes }
234
+
235
+ it 'has 332 codes' do
236
+ disposition_codes.size.should == 332
237
+ end
238
+ end
201
239
  end
202
240
 
203
241
  describe '#[]' do
@@ -226,6 +264,17 @@ module NcsNavigator::Mdes
226
264
  should be_a VariableType
227
265
  end
228
266
 
267
+ it 'passes down heuristic overrides when parsing' do
268
+ spec = Specification.new('2.0', :log => logger)
269
+
270
+ VariableType.should_receive(:from_xsd_simple_type).
271
+ with(anything, include(:heuristic_overrides => spec.heuristic_overrides)).
272
+ at_least(:once).
273
+ and_return(VariableType.new('mocked'))
274
+
275
+ spec.types # force parsing
276
+ end
277
+
229
278
  shared_examples 'types fully resolved' do
230
279
  let!(:types) { Specification.new(version, :log => logger).types }
231
280
 
@@ -286,6 +335,13 @@ module NcsNavigator::Mdes
286
335
 
287
336
  include_examples 'types fully resolved'
288
337
  end
338
+
339
+ context 'version 3.3' do
340
+ let(:version) { '3.3' }
341
+ let(:expected_type_count) { 656 }
342
+
343
+ include_examples 'types fully resolved'
344
+ end
289
345
  end
290
346
 
291
347
  describe '#diff' do
@@ -46,7 +46,8 @@ module NcsNavigator::Mdes
46
46
  XSD
47
47
  }
48
48
 
49
- subject { TransmissionTable.from_element(element) }
49
+ subject { TransmissionTable.from_element(element, options) }
50
+ let(:options) { { } }
50
51
 
51
52
  it 'has the right name' do
52
53
  subject.name.should == 'study_center'
@@ -56,6 +57,29 @@ XSD
56
57
  subject.variables.collect(&:name).
57
58
  should == %w(sc_id sc_name comments transaction_type)
58
59
  end
60
+
61
+ it 'sends the current table name when constructing variables' do
62
+ Variable.should_receive(:from_element).
63
+ with(anything, include(:current_table_name => 'study_center')).
64
+ at_least(:once)
65
+
66
+ subject # to force parsing
67
+ end
68
+
69
+ it 'does not leak the current table name to the outer options hash' do
70
+ subject # to force parsing
71
+ options.should_not have_key(:current_table_name)
72
+ end
73
+
74
+ it 'passes down provided options when constructing variables' do
75
+ options[:foo] = 'bar'
76
+
77
+ Variable.should_receive(:from_element).
78
+ with(anything, include(:foo => 'bar')).
79
+ at_least(:once)
80
+
81
+ subject # to force parsing
82
+ end
59
83
  end
60
84
 
61
85
  describe '#initialize' do
@@ -256,5 +280,58 @@ XSD
256
280
  end
257
281
  end
258
282
  end
283
+
284
+ describe '#primary_key_variables' do
285
+ def table_with_typed_variables(table_name, variables_and_types)
286
+ TransmissionTable.new(table_name).tap do |t|
287
+ t.variables = variables_and_types.collect do |vn, type|
288
+ Variable.new(vn).tap do |v|
289
+ v.type = VariableType.new(type)
290
+ end
291
+ end
292
+ end
293
+ end
294
+
295
+ describe 'for a regular table' do
296
+ let(:table) {
297
+ table_with_typed_variables('example',
298
+ 'foo' => 'primaryKeyType',
299
+ 'bar' => 'primaryKeyType',
300
+ 'baz' => 'foreignKeyTypeRequired',
301
+ )
302
+ }
303
+
304
+ it 'finds all the variables with the PK type' do
305
+ table.primary_key_variables.collect(&:name).should == %w(foo bar)
306
+ end
307
+ end
308
+
309
+ describe 'for psu' do
310
+ let(:psu_table) {
311
+ table_with_typed_variables('psu',
312
+ 'sc_id' => 'study_center_cl1',
313
+ 'psu_id' => 'psu_cl1',
314
+ 'psu_name' => nil,
315
+ )
316
+ }
317
+
318
+ it 'finds the PK' do
319
+ psu_table.primary_key_variables.collect(&:name).should == ['psu_id']
320
+ end
321
+ end
322
+
323
+ describe 'for study_center' do
324
+ let(:sc_table) {
325
+ table_with_typed_variables('study_center',
326
+ 'sc_id' => 'study_center_cl1',
327
+ 'sc_name' => nil,
328
+ )
329
+ }
330
+
331
+ it 'finds the PK' do
332
+ sc_table.primary_key_variables.collect(&:name).should == ['sc_id']
333
+ end
334
+ end
335
+ end
259
336
  end
260
337
  end
@@ -5,8 +5,8 @@ require 'nokogiri'
5
5
  module NcsNavigator::Mdes
6
6
  describe Variable do
7
7
  describe '.from_element' do
8
- def variable(s)
9
- Variable.from_element(schema_element(s), :log => logger)
8
+ def variable(s, options={})
9
+ Variable.from_element(schema_element(s), { :log => logger }.merge(options))
10
10
  end
11
11
 
12
12
  let(:comments) {
@@ -74,6 +74,61 @@ XSD
74
74
  sc_id.type.should be_reference
75
75
  end
76
76
  end
77
+
78
+ context 'with an override' do
79
+ let(:options) {
80
+ {
81
+ :heuristic_overrides => {
82
+ 'variable_type_references' => {
83
+ overridden_table_name => {
84
+ 'not_a_pk' => override_type
85
+ }
86
+ }
87
+ },
88
+ :current_table_name => overridden_table_name
89
+ }
90
+ }
91
+
92
+ let(:overridden_table_name) { 'link_whatever' }
93
+ let(:type_in_xsd) { 'ncs:primaryKeyType' }
94
+ let(:override_type) { 'ncs:foreignKeyTypeRequired' }
95
+
96
+ let(:subject) {
97
+ variable("<xs:element name='not_a_pk' type='#{type_in_xsd}'/>", options)
98
+ }
99
+
100
+ it 'uses an override which matches' do
101
+ subject.type.name.should == 'ncs:foreignKeyTypeRequired'
102
+ end
103
+
104
+ it 'does not use an override which does not match by table name' do
105
+ options[:current_table_name] = 'another_table'
106
+
107
+ subject.type.name.should == type_in_xsd
108
+ end
109
+
110
+ it 'does not use an override which does not match by variable name' do
111
+ options[:heuristic_overrides]['variable_type_references']['link_whatever'] = {
112
+ 'another_overridden_variable' => 'xs:integer'
113
+ }
114
+
115
+ subject.type.name.should == type_in_xsd
116
+ end
117
+
118
+ context 'when the override is an schema type' do
119
+ let(:override_type) { 'xs:integer' }
120
+
121
+ it 'treats extracts the XSD type' do
122
+ subject.type.base_type.should == :integer
123
+ end
124
+ end
125
+
126
+ context 'when the override is an internal reference' do
127
+ it 'is a reference' do
128
+ subject.type.should be_reference
129
+ end
130
+ end
131
+ end
77
132
  end
78
133
 
79
134
  context 'when none present' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ncs_mdes
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 0.13.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-12 00:00:00.000000000 Z
12
+ date: 2013-05-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: 1.5.6
21
+ version: 1.5.7
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
- version: 1.5.6
29
+ version: 1.5.7
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: rspec
32
32
  requirement: !ruby/object:Gem::Requirement
@@ -148,6 +148,11 @@ files:
148
148
  - documents/3.2/disposition_codes.yml
149
149
  - documents/3.2/extract_disposition_codes.rb
150
150
  - documents/3.2/heuristic_overrides.yml
151
+ - documents/3.3/NCS_Transmission_Schema_3.3.00.00.xsd
152
+ - documents/3.3/child_or_parent_instrument_tables.yml
153
+ - documents/3.3/disposition_codes.yml
154
+ - documents/3.3/extract_disposition_codes.rb
155
+ - documents/3.3/heuristic_overrides.yml
151
156
  - documents/scan_p_ids_for_children.rb
152
157
  - lib/ncs_navigator/mdes.rb
153
158
  - lib/ncs_navigator/mdes/code_list.rb
@@ -190,7 +195,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
190
195
  version: '0'
191
196
  segments:
192
197
  - 0
193
- hash: -1618062826933135286
198
+ hash: 2077666531396508902
194
199
  required_rubygems_version: !ruby/object:Gem::Requirement
195
200
  none: false
196
201
  requirements:
@@ -199,7 +204,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
199
204
  version: '0'
200
205
  segments:
201
206
  - 0
202
- hash: -1618062826933135286
207
+ hash: 2077666531396508902
203
208
  requirements: []
204
209
  rubyforge_project:
205
210
  rubygems_version: 1.8.25