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.
- data/CHANGELOG.md +12 -0
- data/README.md +2 -1
- data/ci-exec.sh +1 -1
- data/documents/3.3/NCS_Transmission_Schema_3.3.00.00.xsd +95528 -0
- data/documents/3.3/child_or_parent_instrument_tables.yml +236 -0
- data/documents/3.3/disposition_codes.yml +2361 -0
- data/documents/3.3/extract_disposition_codes.rb +58 -0
- data/documents/3.3/heuristic_overrides.yml +736 -0
- data/lib/ncs_navigator/mdes/source_documents.rb +2 -0
- data/lib/ncs_navigator/mdes/specification.rb +2 -2
- data/lib/ncs_navigator/mdes/transmission_table.rb +24 -1
- data/lib/ncs_navigator/mdes/variable.rb +32 -13
- data/lib/ncs_navigator/mdes/version.rb +1 -1
- data/ncs_mdes.gemspec +1 -1
- data/spec/ncs_navigator/mdes/source_documents_spec.rb +14 -0
- data/spec/ncs_navigator/mdes/specification_spec.rb +56 -0
- data/spec/ncs_navigator/mdes/transmission_table_spec.rb +78 -1
- data/spec/ncs_navigator/mdes/variable_spec.rb +57 -2
- metadata +11 -6
@@ -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,
|
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
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
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)
|
data/ncs_mdes.gemspec
CHANGED
@@ -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.
|
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.
|
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-
|
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.
|
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.
|
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:
|
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:
|
207
|
+
hash: 2077666531396508902
|
203
208
|
requirements: []
|
204
209
|
rubyforge_project:
|
205
210
|
rubygems_version: 1.8.25
|