cqm-parsers 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +29 -0
  3. data/README.md +21 -0
  4. data/Rakefile +19 -0
  5. data/lib/ext/code.rb +10 -0
  6. data/lib/ext/data_element.rb +24 -0
  7. data/lib/hqmf-model/attribute.rb +63 -0
  8. data/lib/hqmf-model/data_criteria.rb +467 -0
  9. data/lib/hqmf-model/document.rb +253 -0
  10. data/lib/hqmf-model/population_criteria.rb +102 -0
  11. data/lib/hqmf-model/precondition.rb +94 -0
  12. data/lib/hqmf-model/types.rb +457 -0
  13. data/lib/hqmf-model/utilities.rb +52 -0
  14. data/lib/hqmf-parser.rb +116 -0
  15. data/lib/hqmf-parser/1.0/attribute.rb +121 -0
  16. data/lib/hqmf-parser/1.0/comparison.rb +34 -0
  17. data/lib/hqmf-parser/1.0/data_criteria.rb +92 -0
  18. data/lib/hqmf-parser/1.0/document.rb +195 -0
  19. data/lib/hqmf-parser/1.0/expression.rb +60 -0
  20. data/lib/hqmf-parser/1.0/observation.rb +61 -0
  21. data/lib/hqmf-parser/1.0/population_criteria.rb +75 -0
  22. data/lib/hqmf-parser/1.0/precondition.rb +90 -0
  23. data/lib/hqmf-parser/1.0/range.rb +76 -0
  24. data/lib/hqmf-parser/1.0/restriction.rb +162 -0
  25. data/lib/hqmf-parser/1.0/utilities.rb +55 -0
  26. data/lib/hqmf-parser/2.0/data_criteria.rb +372 -0
  27. data/lib/hqmf-parser/2.0/data_criteria_helpers/dc_base_extract.rb +80 -0
  28. data/lib/hqmf-parser/2.0/data_criteria_helpers/dc_definition_from_template_or_type_extract.rb +201 -0
  29. data/lib/hqmf-parser/2.0/data_criteria_helpers/dc_post_processing.rb +85 -0
  30. data/lib/hqmf-parser/2.0/data_criteria_helpers/dc_specific_occurrences_and_source_data_criteria_extract.rb +117 -0
  31. data/lib/hqmf-parser/2.0/document.rb +304 -0
  32. data/lib/hqmf-parser/2.0/document_helpers/doc_population_helper.rb +173 -0
  33. data/lib/hqmf-parser/2.0/document_helpers/doc_utilities.rb +131 -0
  34. data/lib/hqmf-parser/2.0/field_value_helper.rb +251 -0
  35. data/lib/hqmf-parser/2.0/population_criteria.rb +134 -0
  36. data/lib/hqmf-parser/2.0/precondition.rb +73 -0
  37. data/lib/hqmf-parser/2.0/source_data_criteria_helper.rb +112 -0
  38. data/lib/hqmf-parser/2.0/types.rb +448 -0
  39. data/lib/hqmf-parser/2.0/utilities.rb +45 -0
  40. data/lib/hqmf-parser/2.0/value_set_helper.rb +104 -0
  41. data/lib/hqmf-parser/converter/pass1/data_criteria_converter.rb +257 -0
  42. data/lib/hqmf-parser/converter/pass1/document_converter.rb +133 -0
  43. data/lib/hqmf-parser/converter/pass1/population_criteria_converter.rb +185 -0
  44. data/lib/hqmf-parser/converter/pass1/precondition_converter.rb +173 -0
  45. data/lib/hqmf-parser/converter/pass1/precondition_extractor.rb +201 -0
  46. data/lib/hqmf-parser/converter/pass1/simple_data_criteria.rb +26 -0
  47. data/lib/hqmf-parser/converter/pass1/simple_operator.rb +89 -0
  48. data/lib/hqmf-parser/converter/pass1/simple_population_criteria.rb +10 -0
  49. data/lib/hqmf-parser/converter/pass1/simple_precondition.rb +51 -0
  50. data/lib/hqmf-parser/converter/pass1/simple_restriction.rb +64 -0
  51. data/lib/hqmf-parser/converter/pass2/comparison_converter.rb +112 -0
  52. data/lib/hqmf-parser/converter/pass2/operator_converter.rb +102 -0
  53. data/lib/hqmf-parser/cql/data_criteria.rb +57 -0
  54. data/lib/hqmf-parser/cql/data_criteria_helpers/dc_definition_from_template_or_type_extract.rb +79 -0
  55. data/lib/hqmf-parser/cql/data_criteria_helpers/dc_post_processing.rb +43 -0
  56. data/lib/hqmf-parser/cql/document.rb +78 -0
  57. data/lib/hqmf-parser/cql/document_helpers/doc_population_helper.rb +124 -0
  58. data/lib/hqmf-parser/cql/value_set_helper.rb +103 -0
  59. data/lib/hqmf-parser/parser.rb +100 -0
  60. data/lib/qrda-export/catI-r5/qrda1_r5.rb +125 -0
  61. data/lib/qrda-export/helper/cat_1_view_helper.rb +142 -0
  62. data/lib/qrda-export/helper/code_system_helper.rb +77 -0
  63. data/lib/qrda-export/helper/date_helper.rb +81 -0
  64. data/lib/qrda-import/base-importers/demographics_importer.rb +47 -0
  65. data/lib/qrda-import/base-importers/medication_importer.rb +22 -0
  66. data/lib/qrda-import/base-importers/section_importer.rb +196 -0
  67. data/lib/qrda-import/cda_identifier.rb +19 -0
  68. data/lib/qrda-import/data-element-importers/adverse_event_importer.rb +23 -0
  69. data/lib/qrda-import/data-element-importers/allergy_intolerance_importer.rb +21 -0
  70. data/lib/qrda-import/data-element-importers/assessment_performed_importer.rb +23 -0
  71. data/lib/qrda-import/data-element-importers/communication_from_patient_to_provider_importer.rb +18 -0
  72. data/lib/qrda-import/data-element-importers/communication_from_provider_to_patient_importer.rb +18 -0
  73. data/lib/qrda-import/data-element-importers/communication_from_provider_to_provider_importer.rb +20 -0
  74. data/lib/qrda-import/data-element-importers/device_applied_importer.rb +23 -0
  75. data/lib/qrda-import/data-element-importers/device_order_importer.rb +18 -0
  76. data/lib/qrda-import/data-element-importers/diagnosis_importer.rb +23 -0
  77. data/lib/qrda-import/data-element-importers/diagnostic_study_order_importer.rb +20 -0
  78. data/lib/qrda-import/data-element-importers/diagnostic_study_performed_importer.rb +30 -0
  79. data/lib/qrda-import/data-element-importers/encounter_order_importer.rb +20 -0
  80. data/lib/qrda-import/data-element-importers/encounter_performed_importer.rb +41 -0
  81. data/lib/qrda-import/data-element-importers/immunization_administered_importer.rb +18 -0
  82. data/lib/qrda-import/data-element-importers/intervention_order_importer.rb +18 -0
  83. data/lib/qrda-import/data-element-importers/intervention_performed_importer.rb +22 -0
  84. data/lib/qrda-import/data-element-importers/laboratory_test_order_importer.rb +20 -0
  85. data/lib/qrda-import/data-element-importers/laboratory_test_performed_importer.rb +28 -0
  86. data/lib/qrda-import/data-element-importers/medication_active_importer.rb +17 -0
  87. data/lib/qrda-import/data-element-importers/medication_administered_importer.rb +17 -0
  88. data/lib/qrda-import/data-element-importers/medication_discharge_importer.rb +19 -0
  89. data/lib/qrda-import/data-element-importers/medication_dispensed_importer.rb +19 -0
  90. data/lib/qrda-import/data-element-importers/medication_order_importer.rb +16 -0
  91. data/lib/qrda-import/data-element-importers/patient_characteristic_expired.rb +21 -0
  92. data/lib/qrda-import/data-element-importers/physical_exam_performed_importer.rb +26 -0
  93. data/lib/qrda-import/data-element-importers/procedure_order_importer.rb +26 -0
  94. data/lib/qrda-import/data-element-importers/procedure_performed_importer.rb +34 -0
  95. data/lib/qrda-import/data-element-importers/substance_administered_importer.rb +16 -0
  96. data/lib/qrda-import/entry_finder.rb +20 -0
  97. data/lib/qrda-import/entry_package.rb +16 -0
  98. data/lib/qrda-import/narrative_reference_handler.rb +33 -0
  99. data/lib/qrda-import/patient_importer.rb +105 -0
  100. data/lib/util/code_system_helper.rb +76 -0
  101. data/lib/util/counter.rb +20 -0
  102. data/lib/util/hqmf_template_helper.rb +39 -0
  103. metadata +340 -0
@@ -0,0 +1,55 @@
1
+ module HQMF1
2
+ module Utilities
3
+
4
+ include HQMF::Conversion::Utilities
5
+
6
+ # Utility function to handle optional attributes
7
+ # @param xpath an XPath that identifies an XML attribute
8
+ # @return the value of the attribute or nil if the attribute is missing
9
+ def attr_val(xpath)
10
+ attr = @entry.at_xpath(xpath)
11
+ if attr
12
+ attr.value
13
+ else
14
+ nil
15
+ end
16
+ end
17
+
18
+ def clean_json(json)
19
+ json.reject!{|k,v| v.nil? || (v.respond_to?(:empty?) && v.empty?)}
20
+ end
21
+
22
+ def clean_json_recursive(json)
23
+ json.each do |k,v|
24
+ if v.is_a? Hash
25
+ clean_json_recursive(v)
26
+ clean_json(v)
27
+ elsif v.is_a? Array
28
+ v.each do |e|
29
+ if e.is_a? Hash
30
+ clean_json_recursive(e)
31
+ clean_json(e)
32
+ end
33
+ end
34
+ end
35
+
36
+ end
37
+ clean_json(json)
38
+ end
39
+
40
+ # Preconditions can have nil conjunctions as part of a DATEDIFF, we want to remove these and warn
41
+ def check_nil_conjunction_on_child
42
+ if (@preconditions.length == 1 && @preconditions.first.conjunction.nil?)
43
+ bad_precondition = @preconditions.first
44
+ if (bad_precondition.restrictions.empty? && bad_precondition.subset.nil? && bad_precondition.expression.nil?)
45
+ @preconditions = @preconditions.first.preconditions
46
+ #puts "\t FIXED PRECONDITION WITHOUT CONJUNCTION"
47
+ else
48
+ puts "\t PRECONDITION WITHOUT CONJUNCTION: Cannot be fixed"
49
+ end
50
+ end
51
+ end
52
+
53
+
54
+ end
55
+ end
@@ -0,0 +1,372 @@
1
+ module HQMF2
2
+ # Represents a data criteria specification
3
+ class DataCriteria
4
+ include HQMF2::Utilities, HQMF2::DataCriteriaTypeAndDefinitionExtraction, HQMF2::DataCriteriaPostProcessing
5
+
6
+ attr_accessor :id
7
+ attr_accessor :original_id
8
+ attr_reader :property, :type, :status, :value, :effective_time, :section
9
+ attr_reader :temporal_references, :subset_operators, :children_criteria
10
+ attr_reader :derivation_operator, :negation, :negation_code_list_id, :description
11
+ attr_reader :field_values, :source_data_criteria, :specific_occurrence_const
12
+ attr_reader :specific_occurrence, :comments, :is_derived_specific_occurrence_variable
13
+ attr_reader :entry, :definition, :variable, :local_variable_name
14
+
15
+ CRITERIA_GLOB = "*[substring(name(),string-length(name())-7) = \'Criteria\']"
16
+
17
+ # Create a new instance based on the supplied HQMF entry
18
+ # @param [Nokogiri::XML::Element] entry the parsed HQMF entry
19
+ def initialize(entry, data_criteria_references = {}, occurrences_map = {})
20
+ @entry = entry
21
+ @data_criteria_references = data_criteria_references
22
+ @occurrences_map = occurrences_map
23
+ basic_setup
24
+ @variable = DataCriteriaMethods.extract_variable(@local_variable_name, @id)
25
+ @field_values = DataCriteriaMethods.extract_field_values(@entry, @negation)
26
+ @description = extract_description
27
+ obtain_specific_and_source = SpecificOccurrenceAndSource.new(@entry, @id, @local_variable_name,
28
+ @data_criteria_references, @occurrences_map)
29
+ # Pulling these 5 variables out via destructing
30
+ @source_data_criteria,
31
+ @source_data_criteria_root,
32
+ @source_data_criteria_extension,
33
+ @specific_occurrence,
34
+ @specific_occurrence_const = obtain_specific_and_source.extract_specific_occurrences_and_source_data_criteria
35
+ extract_definition_from_template_or_type
36
+ post_processing
37
+ end
38
+
39
+ def to_s
40
+ props = {
41
+ property: property,
42
+ type: type,
43
+ status: status,
44
+ section: section
45
+ }
46
+ "DataCriteria#{props}"
47
+ end
48
+
49
+ # TODO: Remove id method if id attribute is sufficient
50
+ # Get the identifier of the criteria, used elsewhere within the document for referencing
51
+ # @return [String] the identifier of this data criteria
52
+ # def id
53
+ # attr_val(@id_xpath)
54
+ # end
55
+
56
+ # Get the title of the criteria, provides a human readable description
57
+ # @return [String] the title of this data criteria
58
+ def title
59
+ disp_value = attr_val("#{@code_list_xpath}/cda:displayName/@value")
60
+ @title || disp_value || @description || id # allow defined titles to take precedence
61
+ end
62
+
63
+ # Get the code list OID of the criteria, used as an index to the code list database
64
+ # @return [String] the code list identifier of this data criteria
65
+ def code_list_id
66
+ @code_list_id || attr_val("#{@code_list_xpath}/@valueSet")
67
+ end
68
+
69
+ # Generates this classes hqmf-model equivalent
70
+ def to_model
71
+ mv = value.try(:to_model)
72
+ met = effective_time.try(:to_model)
73
+ mtr = temporal_references.collect(&:to_model)
74
+ mso = subset_operators.collect(&:to_model)
75
+ field_values = retrieve_field_values_model_for_model
76
+
77
+ retrieve_title_and_description_for_model unless @variable || @derivation_operator
78
+
79
+ @code_list_id = nil if @derivation_operator
80
+
81
+ # prevent json model generation of empty children and comments
82
+ cc = children_criteria.present? ? children_criteria : nil
83
+ comments = @comments.present? ? @comments : nil
84
+
85
+ HQMF::DataCriteria.new(id, title, nil, description, @code_list_id, cc, derivation_operator, @definition, status,
86
+ mv, field_values, met, retrieve_code_system_for_model, @negation, @negation_code_list_id,
87
+ mtr, mso, @specific_occurrence, @specific_occurrence_const, @source_data_criteria,
88
+ comments, @variable)
89
+ end
90
+
91
+ # Return a new DataCriteria instance with only grouper attributes set.
92
+ # A grouper criteria allows multiple data criteria events to be contained in a single
93
+ # logical set (a union or intersection of these multiple events - i.e. A during (B or C or D)).
94
+ # Grouper criteria also provide a way to combine multiple criteria that reference a specific
95
+ # occurrence of an event.
96
+ def extract_variable_grouper
97
+ return unless @variable
98
+ @variable = false
99
+ @id = "GROUP_#{@id}"
100
+ if @children_criteria.length == 1 && @children_criteria[0] =~ /GROUP_/
101
+ reference_criteria = @data_criteria_references[@children_criteria.first]
102
+ return if reference_criteria.nil?
103
+ duplicate_child_info(reference_criteria)
104
+ @definition = reference_criteria.definition
105
+ @status = reference_criteria.status
106
+ @children_criteria = []
107
+ end
108
+ @specific_occurrence = nil
109
+ @specific_occurrence_const = nil
110
+ # set the source data criteria id to the id for variables
111
+ @source_data_criteria = @id
112
+ DataCriteria.new(@entry, @data_criteria_references, @occurrences_map).extract_as_grouper
113
+ end
114
+
115
+ # Extract this data criteria as a grouper data criteria
116
+ # SHOULD only be called on a variable data criteria instance
117
+ def extract_as_grouper
118
+ @field_values = {}
119
+ @temporal_references = []
120
+ @subset_operators = []
121
+ @derivation_operator = HQMF::DataCriteria::UNION
122
+ @definition = 'derived'
123
+ @status = nil
124
+ @children_criteria = ["GROUP_#{@id}"]
125
+ @source_data_criteria = @id
126
+ self
127
+ end
128
+
129
+ # Handle elements that are marked as variable groupers that should not be turned into a "holding element"
130
+ # (defined as a data criteria that encapsulates the calculation material for other data criteria elements,
131
+ # where the other data criteria elements reference the holding element as a child element)
132
+ def handle_derived_specific_occurrence_variable
133
+ # If the first child is all the exists, and it has been marked as a "group" element, switch this over to map to
134
+ # the new element.
135
+ if !@data_criteria_references["GROUP_#{@children_criteria.first}"].nil? && @children_criteria.length == 1
136
+ @children_criteria[0] = "GROUP_#{@children_criteria.first}"
137
+ # If the group element is not found, extract the information from the child and force it into the variable.
138
+ elsif @children_criteria.length == 1 && @children_criteria.first.present?
139
+ reference_criteria = @data_criteria_references[@children_criteria.first]
140
+ return if reference_criteria.nil?
141
+ duplicate_child_info(reference_criteria)
142
+ @children_criteria = reference_criteria.children_criteria
143
+ end
144
+ end
145
+
146
+ # clone method. This is needed because we need to extract a new source data criteria for variables
147
+ # typically "cloning" is done by re-parsing the xml entry, however with post processing that does
148
+ # not give us the correct SDC data when we are trying to recreate since we are looping back through
149
+ # the same data criteria before it has finished processing: See: DocUtilities.extract_source_data_criteria
150
+ def clone
151
+ # Using 'self.class.new' in order to allow this DataCriteria class as
152
+ # well as any future new extending DataCriteria classes to use this clone
153
+ # function.
154
+ other = self.class.new(@entry, @data_criteria_references, @occurrences_map)
155
+ other.instance_variable_set(:@id, @id)
156
+ other.instance_variable_set(:@original_id, @original_id)
157
+ other.instance_variable_set(:@property, @property)
158
+ other.instance_variable_set(:@type, @type)
159
+ other.instance_variable_set(:@status, @status)
160
+ other.instance_variable_set(:@code_list_id, @code_list_id)
161
+ other.instance_variable_set(:@value, @value)
162
+ other.instance_variable_set(:@effective_time, @effective_time)
163
+ other.instance_variable_set(:@section, @section)
164
+ other.instance_variable_set(:@temporal_references, @temporal_references)
165
+ other.instance_variable_set(:@subset_operators, @subset_operators)
166
+ other.instance_variable_set(:@children_criteria, @children_criteria)
167
+ other.instance_variable_set(:@derivation_operator, @derivation_operator)
168
+ other.instance_variable_set(:@negation, @negation)
169
+ other.instance_variable_set(:@negation_code_list_id, @negation_code_list_id)
170
+ other.instance_variable_set(:@description, @description)
171
+ other.instance_variable_set(:@field_values, @field_values)
172
+ other.instance_variable_set(:@source_data_criteria, @source_data_criteria)
173
+ other.instance_variable_set(:@specific_occurrence_const, @specific_occurrence_const)
174
+ other.instance_variable_set(:@specific_occurrence, @specific_occurrence)
175
+ other.instance_variable_set(:@comments, @comments)
176
+ other.instance_variable_set(:@is_derived_specific_occurrence_variable, @is_derived_specific_occurrence_variable)
177
+ other.instance_variable_set(:@entry, @entry)
178
+ other.instance_variable_set(:@definition, @definition)
179
+ other.instance_variable_set(:@variable, @variable)
180
+ other.instance_variable_set(:@local_variable_name, @local_variable_name)
181
+ other
182
+ end
183
+
184
+ private
185
+
186
+ # Handles elments that can be extracted directly from the xml. Utilises the "BaseExtractions" class.
187
+ def basic_setup
188
+ @status = attr_val('./*/cda:statusCode/@code')
189
+ @id_xpath = './*/cda:id/@extension'
190
+ @id = "#{attr_val('./*/cda:id/@extension')}_#{attr_val('./*/cda:id/@root')}"
191
+ @comments = @entry.xpath("./#{CRITERIA_GLOB}/cda:text/cda:xml/cda:qdmUserComments/cda:item/text()",
192
+ HQMF2::Document::NAMESPACES).map(&:content)
193
+ @code_list_xpath = './*/cda:code'
194
+ @value_xpath = './*/cda:value'
195
+ @is_derived_specific_occurrence_variable = false
196
+ simple_extractions = DataCriteriaBaseExtractions.new(@entry)
197
+ @template_ids = simple_extractions.extract_template_ids
198
+ @local_variable_name = simple_extractions.extract_local_variable_name
199
+ @temporal_references = simple_extractions.extract_temporal_references
200
+ @derivation_operator = simple_extractions.extract_derivation_operator
201
+ @children_criteria = simple_extractions.extract_child_criteria
202
+ @subset_operators = simple_extractions.extract_subset_operators
203
+ @negation, @negation_code_list_id = simple_extractions.extract_negation
204
+ end
205
+
206
+ # Extract the description (with some special handling if this is a variable). The MAT has added an encoded
207
+ # form of the variable name in the localVariableName field which is used if available. If not, fall back
208
+ # to the extension.
209
+ def extract_description
210
+ if @variable
211
+ encoded_name = attr_val('./cda:localVariableName/@value')
212
+ encoded_name = DataCriteriaMethods.extract_description_for_variable(encoded_name) if encoded_name
213
+ return encoded_name if encoded_name.present?
214
+ attr_val("./#{CRITERIA_GLOB}/cda:id/@extension")
215
+ else
216
+ attr_val("./#{CRITERIA_GLOB}/cda:text/@value") ||
217
+ attr_val("./#{CRITERIA_GLOB}/cda:title/@value") ||
218
+ attr_val("./#{CRITERIA_GLOB}/cda:id/@extension")
219
+ end
220
+ end
221
+
222
+ # Extract the code system from the xml taht the document should use
223
+ def retrieve_code_system_for_model
224
+ code_system = attr_val("#{@code_list_xpath}/@codeSystem")
225
+ if code_system
226
+ code_system_name = HQMF::Util::CodeSystemHelper.code_system_for(code_system)
227
+ else
228
+ code_system_name = attr_val("#{@code_list_xpath}/@codeSystemName")
229
+ end
230
+ code_value = attr_val("#{@code_list_xpath}/@code")
231
+ { code_system_name => [code_value] } if code_system_name && code_value
232
+ end
233
+
234
+ # Duplicates information from a child element to this data criteria if none exits.
235
+ # If the duplication requires that come values should be overwritten, do so only in the function calling this.
236
+ def duplicate_child_info(child_ref)
237
+ @title ||= child_ref.title
238
+ @type ||= child_ref.subset_operators
239
+ @definition ||= child_ref.definition
240
+ @status ||= child_ref.status
241
+ @code_list_id ||= child_ref.code_list_id
242
+ @temporal_references = child_ref.temporal_references if @temporal_references.empty?
243
+ @subset_operators ||= child_ref.subset_operators
244
+ @variable ||= child_ref.variable
245
+ @value ||= child_ref.value
246
+ end
247
+
248
+ # Generate the models of the field values
249
+ def retrieve_field_values_model_for_model
250
+ field_values = {}
251
+ @field_values.each_pair do |id, val|
252
+ field_values[id] = val.to_model
253
+ end
254
+ @code_list_id ||= code_list_id
255
+
256
+ # Model transfers as a field
257
+ if %w(transfer_to transfer_from).include? @definition
258
+ field_code_list_id = @code_list_id
259
+ @code_list_id = nil
260
+ unless field_code_list_id
261
+ field_code_list_id = attr_val("./#{CRITERIA_GLOB}/cda:outboundRelationship/#{CRITERIA_GLOB}/cda:value/@valueSet")
262
+ end
263
+ field_values[@definition.upcase] = HQMF::Coded.for_code_list(field_code_list_id, title)
264
+ end
265
+
266
+ return field_values unless field_values.empty?
267
+ end
268
+
269
+ # Generate the title and description used when producing the model
270
+ def retrieve_title_and_description_for_model
271
+ # drop "* Value Set" from titles
272
+ exact_desc = title.split(' ')[0...-3].join(' ')
273
+ # don't drop anything for patient characterstic titles
274
+ exact_desc = title if @definition.start_with?('patient_characteristic') && !title.end_with?('Value Set')
275
+
276
+ # remove * Value Set from title
277
+ title_match = title.match(/(.*) \w+ [Vv]alue [Ss]et/)
278
+ @title = title_match[1] if title_match && title_match.length > 1
279
+
280
+ @description = "#{@description}: #{exact_desc}"
281
+ end
282
+ end
283
+
284
+ # Holds methods not tied to the data criteria's instance variables
285
+ class DataCriteriaMethods
286
+ # Given an entry, and whether or not it's negated, extract out the proper field values for the data criteria.
287
+ def self.extract_field_values(entry, negation)
288
+ fields = {}
289
+ # extract most fields which use the same structure
290
+ entry.xpath('./*/cda:outboundRelationship[*/cda:code]', HQMF2::Document::NAMESPACES).each do |field|
291
+ code = HQMF2::Utilities.attr_val(field, './*/cda:code/@code')
292
+ code_id = HQMF::DataCriteria::VALUE_FIELDS[code]
293
+ # No need to run if there is no code id
294
+ next if (negation && code_id == 'REASON') || code_id.nil?
295
+ value = DataCriteriaMethods.parse_value(field, './*/cda:value')
296
+ value ||= DataCriteriaMethods.parse_value(field, './*/cda:effectiveTime')
297
+ fields[code_id] = value
298
+ end
299
+ # special case for facility location which uses a very different structure
300
+ entry.xpath('./*/cda:outboundRelationship[*/cda:participation]', HQMF2::Document::NAMESPACES).each do |field|
301
+ code = HQMF2::Utilities.attr_val(field, './*/cda:participation/cda:role/@classCode')
302
+ code_id = HQMF::DataCriteria::VALUE_FIELDS[code]
303
+ next if code_id.nil?
304
+ value = Coded.new(field.at_xpath('./*/cda:participation/cda:role/cda:code', HQMF2::Document::NAMESPACES))
305
+ fields[code_id] = value
306
+ end
307
+
308
+ fields.merge! HQMF2::FieldValueHelper.parse_field_values(entry)
309
+ # special case for fulfills operator. assuming there is only a possibility of having one of these
310
+ fulfills = entry.at_xpath('./*/cda:outboundRelationship[@typeCode="FLFS"]/cda:criteriaReference',
311
+ HQMF2::Document::NAMESPACES)
312
+ # grab the child element if we don't have a reference
313
+ fields['FLFS'] = TypedReference.new(fulfills) if fulfills
314
+ fields
315
+ end
316
+
317
+ # Use the new MAT feature to extract the human generated (or computer generated) variable names from the xml.
318
+ def self.extract_description_for_variable(encoded_name)
319
+ if encoded_name.match(/^qdm_var_/)
320
+ # Strip out initial qdm_var_ string, trailing _*, and possible occurrence reference
321
+ encoded_name.gsub!(/^qdm_var_|/, '')
322
+ encoded_name.gsub!(/Occurrence[A-Z]of/, '')
323
+ # This code needs to handle measures created before the MAT added variable name hints; for those, don't strip
324
+ # the final identifier
325
+ unless encoded_name.match(/^(SATISFIES ALL|SATISFIES ANY|UNION|INTERSECTION)/)
326
+ encoded_name.gsub!(/_[^_]+$/, '')
327
+ end
328
+ encoded_name
329
+ elsif encoded_name.match(/^localVar_/)
330
+ encoded_name.gsub!(/^localVar_/, '')
331
+ encoded_name
332
+ end
333
+ end
334
+
335
+ # Parses the value for a given xpath
336
+ def self.parse_value(node, xpath)
337
+ value_def = node.at_xpath(xpath, HQMF2::Document::NAMESPACES)
338
+ if value_def
339
+ return AnyValue.new if value_def.at_xpath('@flavorId') == 'ANY.NONNULL'
340
+ value_type_def = value_def.at_xpath('@xsi:type', HQMF2::Document::NAMESPACES)
341
+ return handle_value_type(value_type_def, value_def) if value_type_def
342
+ end
343
+ end
344
+
345
+ # Derives the type associated with a specific value
346
+ def self.handle_value_type(value_type_def, value_def)
347
+ value_type = value_type_def.value
348
+ case value_type
349
+ when 'PQ'
350
+ Value.new(value_def, 'PQ', true)
351
+ when 'TS'
352
+ Value.new(value_def)
353
+ when 'IVL_PQ', 'IVL_INT'
354
+ Range.new(value_def)
355
+ when 'CD'
356
+ Coded.new(value_def)
357
+ when 'ANY', 'IVL_TS'
358
+ # FIXME: (10/26/2015) IVL_TS should be able to handle other values, not just AnyValue
359
+ AnyValue.new
360
+ else
361
+ fail "Unknown value type [#{value_type}]"
362
+ end
363
+ end
364
+
365
+ # Determine if this instance is a qdm variable
366
+ def self.extract_variable(local_variable_name, id)
367
+ variable = (local_variable_name =~ /.*qdm_var_/).present? unless local_variable_name.blank?
368
+ variable ||= (id =~ /.*qdm_var_/).present? unless id.blank?
369
+ variable
370
+ end
371
+ end
372
+ end
@@ -0,0 +1,80 @@
1
+ module HQMF2
2
+ # Contains extraction methods which are self-contained (rely only on the xml and an xpath, no other instance
3
+ # variables)
4
+ class DataCriteriaBaseExtractions
5
+ include HQMF2::Utilities
6
+ CONJUNCTION_CODE_TO_DERIVATION_OP = {
7
+ 'OR' => 'UNION',
8
+ 'AND' => 'XPRODUCT'
9
+ }
10
+
11
+ def initialize(entry)
12
+ @entry = entry
13
+ end
14
+
15
+ # Extract the local variable name (held in the value of the localVariableName element)
16
+ def extract_local_variable_name
17
+ lvn = @entry.at_xpath('./cda:localVariableName')
18
+ lvn['value'] if lvn
19
+ end
20
+
21
+ # Generate a list of child criterias
22
+ def extract_child_criteria
23
+ @entry.xpath("./*/cda:outboundRelationship[@typeCode='COMP']/cda:criteriaReference/cda:id",
24
+ HQMF2::Document::NAMESPACES).collect do |ref|
25
+ Reference.new(ref).id
26
+ end.compact
27
+ end
28
+
29
+ # Extracts the derivation operator to be used by the data criteria, and fails out if it finds more than one (should
30
+ # not be valid)
31
+ def extract_derivation_operator
32
+ codes = @entry.xpath("./*/cda:outboundRelationship[@typeCode='COMP']/cda:conjunctionCode/@code",
33
+ HQMF2::Document::NAMESPACES)
34
+ codes.inject(nil) do |d_op, code|
35
+ if d_op && d_op != CONJUNCTION_CODE_TO_DERIVATION_OP[code.value]
36
+ fail 'More than one derivation operator in data criteria'
37
+ end
38
+ CONJUNCTION_CODE_TO_DERIVATION_OP[code.value]
39
+ end
40
+ end
41
+
42
+ def extract_temporal_references
43
+ @entry.xpath('./*/cda:temporallyRelatedInformation', HQMF2::Document::NAMESPACES).collect do |temporal_reference|
44
+ TemporalReference.new(temporal_reference)
45
+ end
46
+ end
47
+
48
+ # Filters all the subset operators to only include the ones of type 'UNION' and 'XPRODUCT'
49
+ def extract_subset_operators
50
+ all_subset_operators.select do |operator|
51
+ operator.type != 'UNION' && operator.type != 'XPRODUCT'
52
+ end
53
+ end
54
+
55
+ # Extracts all subset operators contained in the entry xml
56
+ def all_subset_operators
57
+ @entry.xpath('./*/cda:excerpt', HQMF2::Document::NAMESPACES).collect do |subset_operator|
58
+ SubsetOperator.new(subset_operator)
59
+ end
60
+ end
61
+
62
+ def extract_template_ids
63
+ @entry.xpath('./*/cda:templateId/cda:item', HQMF2::Document::NAMESPACES).collect do |template_def|
64
+ HQMF2::Utilities.attr_val(template_def, '@root')
65
+ end
66
+ end
67
+
68
+ # Extract the negation (and the negation_code_list_id if appropriate)
69
+ def extract_negation
70
+ negation = (attr_val('./*/@actionNegationInd').to_s.downcase == 'true')
71
+ negation_code_list_id = nil
72
+ if negation
73
+ res = @entry.at_xpath('./*/cda:outboundRelationship/*/cda:code[@code="410666004"]/../cda:value/@valueSet',
74
+ HQMF2::Document::NAMESPACES)
75
+ negation_code_list_id = res.value if res
76
+ end
77
+ [negation, negation_code_list_id]
78
+ end
79
+ end
80
+ end