ncs_mdes 0.11.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. data/CHANGELOG.md +16 -0
  2. data/Gemfile +1 -1
  3. data/README.md +15 -5
  4. data/bin/mdes-console +3 -1
  5. data/documents/1.2/child_or_parent_instrument_tables.yml +28 -0
  6. data/documents/2.0/child_or_parent_instrument_tables.yml +68 -0
  7. data/documents/2.1/child_or_parent_instrument_tables.yml +74 -0
  8. data/documents/2.2/child_or_parent_instrument_tables.yml +98 -0
  9. data/documents/3.0/child_or_parent_instrument_tables.yml +119 -0
  10. data/documents/3.1/child_or_parent_instrument_tables.yml +187 -0
  11. data/documents/3.2/child_or_parent_instrument_tables.yml +192 -0
  12. data/documents/scan_p_ids_for_children.rb +39 -0
  13. data/lib/ncs_navigator/mdes/code_list.rb +94 -0
  14. data/lib/ncs_navigator/mdes/differences/collection.rb +47 -0
  15. data/lib/ncs_navigator/mdes/differences/collection_criterion.rb +59 -0
  16. data/lib/ncs_navigator/mdes/differences/entry.rb +48 -0
  17. data/lib/ncs_navigator/mdes/differences/value.rb +7 -0
  18. data/lib/ncs_navigator/mdes/differences/value_criterion.rb +58 -0
  19. data/lib/ncs_navigator/mdes/differences.rb +12 -0
  20. data/lib/ncs_navigator/mdes/source_documents.rb +27 -0
  21. data/lib/ncs_navigator/mdes/specification.rb +45 -0
  22. data/lib/ncs_navigator/mdes/transmission_table.rb +61 -0
  23. data/lib/ncs_navigator/mdes/variable.rb +58 -4
  24. data/lib/ncs_navigator/mdes/variable_type.rb +27 -62
  25. data/lib/ncs_navigator/mdes/version.rb +1 -1
  26. data/lib/ncs_navigator/mdes.rb +5 -1
  27. data/spec/differences_matchers.rb +40 -0
  28. data/spec/ncs_navigator/mdes/code_list_spec.rb +178 -0
  29. data/spec/ncs_navigator/mdes/source_documents_spec.rb +14 -0
  30. data/spec/ncs_navigator/mdes/specification_spec.rb +30 -0
  31. data/spec/ncs_navigator/mdes/transmission_table_spec.rb +77 -0
  32. data/spec/ncs_navigator/mdes/variable_spec.rb +244 -0
  33. data/spec/ncs_navigator/mdes/variable_type_spec.rb +161 -40
  34. data/spec/spec_helper.rb +6 -0
  35. metadata +24 -5
@@ -0,0 +1,192 @@
1
+ child_instrument_tables:
2
+ - birth_visit_baby_name_2
3
+ - birth_visit_baby_name_3
4
+ - birth_visit_baby_name_4
5
+ - birth_visit_li_baby_name
6
+ - birth_visit_li_baby_name_2
7
+ - bitsea_saq
8
+ - child_anthro
9
+ - child_blood
10
+ - child_bp
11
+ - child_saliva
12
+ - child_saliva_saq
13
+ - child_urine
14
+ - core_quest_child_care
15
+ - core_quest_concern
16
+ - core_quest_disability
17
+ - core_quest_emergency_room
18
+ - core_quest_health_care
19
+ - core_quest_hh
20
+ - core_quest_hospitalizations
21
+ - core_quest_housing
22
+ - core_quest_income
23
+ - core_quest_insurance
24
+ - core_quest_interim_med
25
+ - core_quest_media
26
+ - core_quest_medical
27
+ - core_quest_neighborhood
28
+ - core_quest_occupation
29
+ - core_quest_pesticide
30
+ - core_quest_pets
31
+ - core_quest_program
32
+ - core_quest_sleep
33
+ - core_quest_smoke
34
+ - core_quest_well_child_care
35
+ - eighteen_mth_mother_detail
36
+ - eighteen_mth_mother_detail_2
37
+ - eighteen_mth_mother_habits
38
+ - eighteen_mth_mother_habits_2
39
+ - eighteen_mth_mother_habits_3
40
+ - eighteen_mth_mother_mold
41
+ - eighteen_mth_mother_mold_2
42
+ - eighteen_mth_mother_mold_3
43
+ - eighteen_mth_mother_otc
44
+ - eighteen_mth_mother_otc_2
45
+ - eighteen_mth_mother_otc_3
46
+ - eighteen_mth_mother_prescr
47
+ - eighteen_mth_mother_prescr_2
48
+ - eighteen_mth_mother_prescr_3
49
+ - eighteen_mth_mother_saq
50
+ - eighteen_mth_mother_saq_2
51
+ - eighteen_mth_mother_suppl
52
+ - eighteen_mth_mother_suppl_2
53
+ - eighteen_mth_mother_suppl_3
54
+ - fourteen_mth_asq_saq
55
+ - itsp_saq
56
+ - m_chat_saq
57
+ - nine_mth_mother_detail
58
+ - nine_mth_mother_detail_2
59
+ - participant_verif_child
60
+ - reconsideration_ins
61
+ - six_mth_mother_detail
62
+ - six_mth_mother_detail_2
63
+ - six_mth_saq
64
+ - six_mth_saq_2
65
+ - six_mth_saq_3
66
+ - six_mth_saq_4
67
+ - sixteen_mth_asq_saq
68
+ - spec_cord_blood
69
+ - spec_cord_blood_2
70
+ - spec_cord_blood_3
71
+ - thirty_month_interview_child
72
+ - thirty_mth_asq_saq
73
+ - three_mth_mother_child_detail
74
+ - three_mth_mother_child_detail_2
75
+ - three_mth_mother_child_habits
76
+ - three_mth_mother_child_habits_2
77
+ - three_mth_mother_child_habits_3
78
+ - twelve_mth_mother_detail
79
+ - twelve_mth_mother_detail_2
80
+ - twelve_mth_mother_detail_3
81
+ - twelve_mth_mother_mold_3
82
+ - twenty_four_mth_mother_detail
83
+ - twenty_four_mth_mother_detail_2
84
+ - twenty_four_mth_mother_habits_2
85
+ - twenty_four_mth_mother_habits_3
86
+ - twenty_four_mth_mother_mold
87
+ - twenty_four_mth_mother_mold_2
88
+ - twenty_four_mth_mother_otc_2
89
+ - twenty_four_mth_mother_otc_3
90
+ - twenty_four_mth_mother_prescr_2
91
+ - twenty_four_mth_mother_prescr_3
92
+ - twenty_four_mth_mother_suppl_2
93
+ - twenty_four_mth_mother_suppl_3
94
+ - twenty_four_mth_saq
95
+ - twenty_four_mth_saq_2
96
+ - twenty_mth_asq_saq
97
+ - twenty_seven_mth_asq_saq
98
+ - twenty_two_mth_asq_saq
99
+ parent_instrument_tables:
100
+ - birth_visit
101
+ - birth_visit_2
102
+ - birth_visit_3
103
+ - birth_visit_4
104
+ - birth_visit_diagnose_2_3
105
+ - birth_visit_diagnose_2_4
106
+ - birth_visit_household_3
107
+ - birth_visit_household_4
108
+ - birth_visit_li
109
+ - birth_visit_li_2
110
+ - breast_milk_saq
111
+ - bsi_saq
112
+ - eighteen_mth_mother
113
+ - eighteen_mth_mother_2
114
+ - eighteen_mth_mother_3
115
+ - father_pv1
116
+ - father_pv1_2
117
+ - household_enumeration
118
+ - household_enumeration_age_elig
119
+ - household_enumeration_pregnant
120
+ - household_inventory
121
+ - household_inventory_age
122
+ - household_inventory_age_elig
123
+ - internet_usage
124
+ - low_high_script
125
+ - multi_mode
126
+ - nine_mth_mother
127
+ - nine_mth_mother_2
128
+ - non_interview_respondent
129
+ - participant_verif
130
+ - pbs_elig_screener
131
+ - pbs_participant_verif
132
+ - ppg_cati
133
+ - ppg_saq
134
+ - pre_preg
135
+ - pre_preg_saq
136
+ - preg_screen_eh
137
+ - preg_screen_eh_2
138
+ - preg_screen_hi
139
+ - preg_screen_hi_2
140
+ - preg_screen_pb
141
+ - preg_screen_pb_2
142
+ - preg_visit_1
143
+ - preg_visit_1_2
144
+ - preg_visit_1_3
145
+ - preg_visit_1_saq
146
+ - preg_visit_1_saq_2
147
+ - preg_visit_1_saq_3
148
+ - preg_visit_1_saq_4
149
+ - preg_visit_2
150
+ - preg_visit_2_2
151
+ - preg_visit_2_3
152
+ - preg_visit_2_saq
153
+ - preg_visit_2_saq_2
154
+ - preg_visit_2_saq_3
155
+ - preg_visit_2_saq_4
156
+ - preg_visit_li
157
+ - preg_visit_li_2
158
+ - sample_dist
159
+ - six_mth_mother
160
+ - six_mth_mother_2
161
+ - spec_blood
162
+ - spec_blood_2
163
+ - spec_urine
164
+ - tap_water_twf
165
+ - tap_water_twf_2
166
+ - tap_water_twf_saq
167
+ - tap_water_twq
168
+ - tap_water_twq_2
169
+ - tap_water_twq_saq
170
+ - thirty_month_interview
171
+ - three_mth_mother
172
+ - three_mth_mother_2
173
+ - three_mth_mother_3
174
+ - tracing_int
175
+ - twelve_mth_mother
176
+ - twelve_mth_mother_2
177
+ - twelve_mth_mother_3
178
+ - twelve_mth_saq
179
+ - twelve_mth_saq_2
180
+ - twelve_mth_saq_3
181
+ - twenty_four_mth_mother
182
+ - twenty_four_mth_mother_2
183
+ - twenty_four_mth_mother_3
184
+ - twenty_four_mth_mother_habits
185
+ - twenty_four_mth_mother_otc
186
+ - twenty_four_mth_mother_prescr
187
+ - twenty_four_mth_mother_suppl
188
+ - vacuum_bag
189
+ - vacuum_bag_2
190
+ - vacuum_bag_saq
191
+ - validation_ins
192
+ - validation_ins_2
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ##
4
+ # A script which scans an MDES spreadsheet for indications that an instrument
5
+ # table's p_id is for a child (vs. for a parent.)
6
+ #
7
+ # This script requires the 'roo' gem, which is not included in
8
+ # ncs_mdes's gemspec because it has a huge number of dependencies and
9
+ # is not needed at runtime.
10
+
11
+ require 'roo'
12
+ require 'yaml'
13
+
14
+ MDES_XLSX = ARGV.first or fail 'Please provide the path to the MDES spreadsheet'
15
+ SHEET_NAME = 'Data Elements'
16
+
17
+ COLUMNS = {
18
+ 'A' => :table_type,
19
+ 'B' => :table_label,
20
+ 'C' => :table_name,
21
+ 'D' => :variable_name,
22
+ 'I' => :variable_def
23
+ }
24
+
25
+ book = Excelx.new(MDES_XLSX)
26
+
27
+ 3.upto(book.last_row(SHEET_NAME)) do |row_number|
28
+ row = COLUMNS.keys.each_with_object({}) { |col, i| i[COLUMNS[col]] = book.cell(row_number, col, SHEET_NAME) }
29
+ next unless row[:table_type] =~ /instrument/i
30
+ next unless row[:variable_name] =~ /\Ap_id\Z/i
31
+
32
+ puts "#{row[:table_name].downcase}.#{row[:variable_name].downcase}"
33
+ if row[:variable_def] =~ /child/i
34
+ puts "- mentions child in variable def"
35
+ end
36
+ if row[:table_label] =~ /child/i
37
+ puts "- mentions child in table label"
38
+ end
39
+ end
@@ -0,0 +1,94 @@
1
+ require 'ncs_navigator/mdes'
2
+
3
+ module NcsNavigator::Mdes
4
+ ##
5
+ # A specialization of `Array` for code lists.
6
+ #
7
+ # @see VariableType#code_list
8
+ # @see CodeListEntry
9
+ class CodeList < Array
10
+ ##
11
+ # @return [String,nil] the description of the code list if any.
12
+ attr_accessor :description
13
+ end
14
+
15
+ ##
16
+ # A single entry in a code list.
17
+ #
18
+ # @see VariableType#code_list
19
+ # @see CodeList
20
+ class CodeListEntry
21
+ ##
22
+ # @return [String] the local code value for the entry.
23
+ attr_reader :value
24
+
25
+ ##
26
+ # @return [String] the human-readable label for the entry.
27
+ attr_accessor :label
28
+
29
+ ##
30
+ # @return [String] the MDES's globally-unique identifier for
31
+ # this coded value.
32
+ attr_accessor :global_value
33
+
34
+ ##
35
+ # @return [String] the name of MDES's master code list from
36
+ # which this value is derived.
37
+ attr_accessor :master_cl
38
+
39
+ class << self
40
+ ##
41
+ # Creates a new instance from a `xs:enumeration` simple type
42
+ # restriction subelement.
43
+ #
44
+ # @param [Nokogiri::XML::Element] enum the `xs:enumeration`
45
+ # element.
46
+ # @param [Hash] options
47
+ # @option options [#warn] :log the logger to which to direct warnings
48
+ #
49
+ # @return [CodeListEntry]
50
+ def from_xsd_enumeration(enum, options={})
51
+ log = options[:log] || NcsNavigator::Mdes.default_logger
52
+
53
+ log.warn("Missing value for code list entry on line #{enum.line}") unless enum['value']
54
+
55
+ new(enum['value'] && enum['value'].strip).tap do |cle|
56
+ cle.label = enum['ncsdoc:label']
57
+ cle.global_value = enum['ncsdoc:global_value']
58
+ cle.master_cl = enum['ncsdoc:master_cl']
59
+ end
60
+ end
61
+ end
62
+
63
+ def initialize(value)
64
+ @value = value
65
+ end
66
+
67
+ alias :to_s :value
68
+
69
+ def diff_criteria(diff_options)
70
+ if diff_options[:strict]
71
+ {
72
+ :value => Differences::ValueCriterion.new,
73
+ :label => Differences::ValueCriterion.new,
74
+ :global_value => Differences::ValueCriterion.new,
75
+ :master_cl => Differences::ValueCriterion.new
76
+ }
77
+ else
78
+ {
79
+ :value => Differences::ValueCriterion.new,
80
+ :label => Differences::ValueCriterion.new(:value_extractor => :word_chars_downcase)
81
+ }
82
+ end
83
+ end
84
+ protected :diff_criteria
85
+
86
+ ##
87
+ # Computes the differences between this code list entry and the other.
88
+ #
89
+ # @return [Differences::Entry,nil]
90
+ def diff(other, options={})
91
+ Differences::Entry.compute(self, other, diff_criteria(options), options)
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,47 @@
1
+ require 'ncs_navigator/mdes'
2
+ require 'forwardable'
3
+
4
+ module NcsNavigator::Mdes::Differences
5
+ ##
6
+ # Captures the differences between two collections.
7
+ class Collection
8
+ extend Forwardable
9
+
10
+ def_delegators :entry_differences, :[]
11
+
12
+ def initialize(left_only, right_only, entry_differences)
13
+ @left_only = left_only
14
+ @right_only = right_only
15
+ @entry_differences = entry_differences
16
+ end
17
+
18
+ ##
19
+ # A list of those entries which are in the lefthand version of the
20
+ # collection only. Values are the characteristic (alignment) value for each
21
+ # entry.
22
+ #
23
+ # @return [Array<Object>]
24
+ def left_only
25
+ @left_only ||= []
26
+ end
27
+
28
+ ##
29
+ # A list of those entries which are in the righthand version of the
30
+ # collection only. Values are the characteristic (alignment) value for each
31
+ # entry.
32
+ #
33
+ # @return [Array<Object>]
34
+ def right_only
35
+ @right_only ||= []
36
+ end
37
+
38
+ ##
39
+ # Detailed differences for entries which are present in some form in each
40
+ # collection. Keys are the characteristic (alignment) value for the entry.
41
+ #
42
+ # @return [Hash<Object, Entry>]
43
+ def entry_differences
44
+ @entry_differences ||= {}
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,59 @@
1
+ require 'ncs_navigator/mdes'
2
+
3
+ module NcsNavigator::Mdes::Differences
4
+ ##
5
+ # @private implementation detail
6
+ class CollectionCriterion
7
+ ##
8
+ # @return [Symbol] the attribute in the object to which this criterion applies
9
+ attr_reader :attribute
10
+
11
+ def initialize(alignment_attribute, options={})
12
+ @alignment_attribute = alignment_attribute
13
+ @attribute = options.delete(:collection)
14
+ @alignment_value_extractor =
15
+ select_value_extractor(options.delete(:value_extractor))
16
+ end
17
+
18
+ def apply(c1, c2, diff_options)
19
+ c1_map = map_for_alignment(c1)
20
+ c2_map = map_for_alignment(c2)
21
+
22
+ left_only = c1_map.keys - c2_map.keys
23
+ right_only = c2_map.keys - c1_map.keys
24
+
25
+ both = c1_map.keys & c2_map.keys
26
+ entry_differences = both.each_with_object({}) do |key, differences|
27
+ diff = c1_map[key].diff(c2_map[key], diff_options)
28
+ differences[key] = diff if diff
29
+ end
30
+
31
+ if left_only.empty? && right_only.empty? && entry_differences.empty?
32
+ nil
33
+ else
34
+ Collection.new(left_only, right_only, entry_differences)
35
+ end
36
+ end
37
+
38
+ def map_for_alignment(c)
39
+ return {} unless c
40
+ c.each_with_object({}) do |element, map|
41
+ value = @alignment_value_extractor.call(element.send(@alignment_attribute))
42
+ map[value] = element
43
+ end
44
+ end
45
+ private :map_for_alignment
46
+
47
+ def select_value_extractor(param)
48
+ case param
49
+ when nil
50
+ ValueCriterion::VALUE_EXTRACTORS[:identity]
51
+ when Symbol
52
+ ValueCriterion::VALUE_EXTRACTORS[param] or fail "Unknown extractor #{param.inspect}"
53
+ else
54
+ param
55
+ end
56
+ end
57
+ private :select_value_extractor
58
+ end
59
+ end
@@ -0,0 +1,48 @@
1
+ require 'ncs_navigator/mdes'
2
+ require 'forwardable'
3
+
4
+ module NcsNavigator::Mdes::Differences
5
+ ##
6
+ # Captures the differences between two instances of one of the elements that
7
+ # makes up a specification. I.e., {TransmissionTable}, {Variable},
8
+ # {VariableType}, or {CodeListEntry}.
9
+ class Entry
10
+ ##
11
+ # @param [Object] o1 the left object
12
+ # @param [Object] o2 the right object
13
+ # @param [Hash<Symbol, #apply>] attribute_criteria a list of objects which produce difference objects
14
+ # @param [Hash] diff_options options to pass to nested calls to #diff
15
+ # @return [Entry, nil] the differences between o1 and o2 according to the
16
+ # criteria, or nil if there are no differences.
17
+ def self.compute(o1, o2, attribute_criteria, diff_options)
18
+ differences = attribute_criteria.each_with_object({}) do |(diff_attribute, criterion), diffs|
19
+ o_attribute = (criterion.respond_to?(:attribute) && criterion.attribute) || diff_attribute
20
+ d = criterion.apply(o1.send(o_attribute), o2.send(o_attribute), diff_options)
21
+ diffs[diff_attribute] = d if d
22
+ end
23
+
24
+ if differences.empty?
25
+ nil
26
+ else
27
+ Entry.new.tap { |e| e.attribute_differences = differences }
28
+ end
29
+ end
30
+
31
+ extend Forwardable
32
+
33
+ def_delegators :attribute_differences, :[]
34
+
35
+ ##
36
+ # Return the differences for each attribute. Each key is the name of the
37
+ # attribute and each value is an object describing the difference. It might
38
+ # be a {Value} diff, a {Collection} diff, or another {Entry} diff depending
39
+ # on the kind of attribute.
40
+ #
41
+ # @return [Hash<Symbol, Object>]
42
+ def attribute_differences
43
+ @attribute_differences ||= {}
44
+ end
45
+
46
+ attr_writer :attribute_differences
47
+ end
48
+ end
@@ -0,0 +1,7 @@
1
+ require 'ncs_navigator/mdes'
2
+
3
+ module NcsNavigator::Mdes::Differences
4
+ ##
5
+ # Captures the differences between a simple scalar value.
6
+ Value = Struct.new(:left, :right)
7
+ end
@@ -0,0 +1,58 @@
1
+ require 'ncs_navigator/mdes'
2
+
3
+ module NcsNavigator::Mdes::Differences
4
+ ##
5
+ # @private implementation detail
6
+ class ValueCriterion
7
+ COMPARATORS = {
8
+ :equality => lambda { |a, b| a == b },
9
+ :predicate => lambda { |a, b| !(a ^ b) }
10
+ }
11
+
12
+ VALUE_EXTRACTORS = {
13
+ :identity => lambda { |o| o },
14
+ :word_chars_downcase =>
15
+ lambda { |o| o ? o.downcase.gsub(/[^ \w]+/, ' ').gsub(/\s+/, ' ').strip : o }
16
+ }
17
+
18
+ attr_reader :comparator, :value_extractor
19
+
20
+ def initialize(options={})
21
+ @comparator = select_comparator(options.delete(:comparator))
22
+ @value_extractor = select_value_extractor(options.delete(:value_extractor))
23
+ end
24
+
25
+ def apply(v1, v2, diff_options)
26
+ cv1 = value_extractor.call(v1)
27
+ cv2 = value_extractor.call(v2)
28
+
29
+ unless comparator.call(cv1, cv2)
30
+ Value.new(cv1, cv2)
31
+ end
32
+ end
33
+
34
+ def select_comparator(param)
35
+ case param
36
+ when nil
37
+ COMPARATORS[:equality]
38
+ when Symbol
39
+ COMPARATORS[param] or fail "Unknown comparator #{param.inspect}"
40
+ else
41
+ param
42
+ end
43
+ end
44
+ private :select_comparator
45
+
46
+ def select_value_extractor(param)
47
+ case param
48
+ when nil
49
+ VALUE_EXTRACTORS[:identity]
50
+ when Symbol
51
+ VALUE_EXTRACTORS[param] or fail "Unknown extractor #{param.inspect}"
52
+ else
53
+ param
54
+ end
55
+ end
56
+ private :select_value_extractor
57
+ end
58
+ end
@@ -0,0 +1,12 @@
1
+ require 'ncs_navigator/mdes'
2
+
3
+ module NcsNavigator::Mdes
4
+ module Differences
5
+ autoload :Entry, 'ncs_navigator/mdes/differences/entry'
6
+ autoload :Collection, 'ncs_navigator/mdes/differences/collection'
7
+ autoload :Value, 'ncs_navigator/mdes/differences/value'
8
+
9
+ autoload :CollectionCriterion, 'ncs_navigator/mdes/differences/collection_criterion'
10
+ autoload :ValueCriterion, 'ncs_navigator/mdes/differences/value_criterion'
11
+ end
12
+ end
@@ -72,6 +72,7 @@ module NcsNavigator::Mdes
72
72
  sd.schema = schema
73
73
  sd.heuristic_overrides = "#{version}/heuristic_overrides.yml"
74
74
  sd.disposition_codes = "#{version}/disposition_codes.yml"
75
+ sd.child_or_parent_instrument_tables = "#{version}/child_or_parent_instrument_tables.yml"
75
76
  sd.specification_version = specification_version
76
77
  end
77
78
  end
@@ -171,6 +172,32 @@ module NcsNavigator::Mdes
171
172
  @disposition_codes = path
172
173
  end
173
174
 
175
+ ##
176
+ # The absolute path to a YAML-formatted document defining a hash with two
177
+ # keys: `child_instrument_tables` and `parent_instrument_tables`. The value
178
+ # for each should be a list of MDES table names (lower case) which are in
179
+ # that category.
180
+ #
181
+ # This is path is optional; if one is not provided
182
+ # {TransmissionTable#child_instrument_table?} and
183
+ # {TransmissionTable#parent_instrument_table?} will be nil for all tables.
184
+ #
185
+ # @return [String]
186
+ def child_or_parent_instrument_tables
187
+ absolutize(@child_or_parent_instrument_tables)
188
+ end
189
+
190
+ ##
191
+ # Set the path to the child-or-parent instrument tables document.
192
+ # If the path is relative (i.e., it does not begin with `/`), it
193
+ # will be interpreted relative to {#base}.
194
+ #
195
+ # @param [String] path
196
+ # @return [String] the provided path
197
+ def child_or_parent_instrument_tables=(path)
198
+ @child_or_parent_instrument_tables = path
199
+ end
200
+
174
201
  private
175
202
 
176
203
  def absolutize(path)
@@ -94,10 +94,44 @@ module NcsNavigator::Mdes
94
94
  v.resolve_foreign_key!(tables, fk_overrides[v.name], :log => @log, :in_table => t)
95
95
  }
96
96
  }
97
+ tables.each { |t| t.child_instrument_table = lookup_child_or_parent_table_status(t.name) }
97
98
  }
98
99
  end
99
100
  private :read_transmission_tables
100
101
 
102
+ def lookup_child_or_parent_table_status(name)
103
+ if child_or_parent_instrument_tables['child_instrument_tables'].include?(name)
104
+ true
105
+ elsif child_or_parent_instrument_tables['parent_instrument_tables'].include?(name)
106
+ false
107
+ else
108
+ nil
109
+ end
110
+ end
111
+ private :lookup_child_or_parent_table_status
112
+
113
+ def child_or_parent_instrument_tables
114
+ @child_or_parent_instrument_tables ||=
115
+ if source_documents.child_or_parent_instrument_tables
116
+ YAML.load(File.read(source_documents.child_or_parent_instrument_tables)).tap do |result|
117
+ check_child_or_parent_lists(result)
118
+ end
119
+ else
120
+ Hash.new([])
121
+ end
122
+ end
123
+ private :child_or_parent_instrument_tables
124
+
125
+ def check_child_or_parent_lists(lists)
126
+ parent_list = lists['parent_instrument_tables']
127
+ child_list = lists['child_instrument_tables']
128
+ overlap = parent_list & child_list
129
+ unless overlap.empty?
130
+ @log.warn("These tables appear in both the child instrument and parent instrument lists: #{overlap.inspect}")
131
+ end
132
+ end
133
+ private :check_child_or_parent_lists
134
+
101
135
  ##
102
136
  # A shortcut for accessing particular {#transmission_tables}.
103
137
  #
@@ -163,5 +197,16 @@ module NcsNavigator::Mdes
163
197
  def inspect
164
198
  "#<#{self.class} version=#{version.inspect}>"
165
199
  end
200
+
201
+ # @private
202
+ DIFF_CRITERIA = {
203
+ :specification_version => Differences::ValueCriterion.new,
204
+ :transmission_tables => Differences::CollectionCriterion.new(:name),
205
+ :types => Differences::CollectionCriterion.new(:name)
206
+ }
207
+
208
+ def diff(other, options={})
209
+ Differences::Entry.compute(self, other, DIFF_CRITERIA, options)
210
+ end
166
211
  end
167
212
  end