cqm-parsers 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +29 -0
- data/README.md +21 -0
- data/Rakefile +19 -0
- data/lib/ext/code.rb +10 -0
- data/lib/ext/data_element.rb +24 -0
- data/lib/hqmf-model/attribute.rb +63 -0
- data/lib/hqmf-model/data_criteria.rb +467 -0
- data/lib/hqmf-model/document.rb +253 -0
- data/lib/hqmf-model/population_criteria.rb +102 -0
- data/lib/hqmf-model/precondition.rb +94 -0
- data/lib/hqmf-model/types.rb +457 -0
- data/lib/hqmf-model/utilities.rb +52 -0
- data/lib/hqmf-parser.rb +116 -0
- data/lib/hqmf-parser/1.0/attribute.rb +121 -0
- data/lib/hqmf-parser/1.0/comparison.rb +34 -0
- data/lib/hqmf-parser/1.0/data_criteria.rb +92 -0
- data/lib/hqmf-parser/1.0/document.rb +195 -0
- data/lib/hqmf-parser/1.0/expression.rb +60 -0
- data/lib/hqmf-parser/1.0/observation.rb +61 -0
- data/lib/hqmf-parser/1.0/population_criteria.rb +75 -0
- data/lib/hqmf-parser/1.0/precondition.rb +90 -0
- data/lib/hqmf-parser/1.0/range.rb +76 -0
- data/lib/hqmf-parser/1.0/restriction.rb +162 -0
- data/lib/hqmf-parser/1.0/utilities.rb +55 -0
- data/lib/hqmf-parser/2.0/data_criteria.rb +372 -0
- data/lib/hqmf-parser/2.0/data_criteria_helpers/dc_base_extract.rb +80 -0
- data/lib/hqmf-parser/2.0/data_criteria_helpers/dc_definition_from_template_or_type_extract.rb +201 -0
- data/lib/hqmf-parser/2.0/data_criteria_helpers/dc_post_processing.rb +85 -0
- data/lib/hqmf-parser/2.0/data_criteria_helpers/dc_specific_occurrences_and_source_data_criteria_extract.rb +117 -0
- data/lib/hqmf-parser/2.0/document.rb +304 -0
- data/lib/hqmf-parser/2.0/document_helpers/doc_population_helper.rb +173 -0
- data/lib/hqmf-parser/2.0/document_helpers/doc_utilities.rb +131 -0
- data/lib/hqmf-parser/2.0/field_value_helper.rb +251 -0
- data/lib/hqmf-parser/2.0/population_criteria.rb +134 -0
- data/lib/hqmf-parser/2.0/precondition.rb +73 -0
- data/lib/hqmf-parser/2.0/source_data_criteria_helper.rb +112 -0
- data/lib/hqmf-parser/2.0/types.rb +448 -0
- data/lib/hqmf-parser/2.0/utilities.rb +45 -0
- data/lib/hqmf-parser/2.0/value_set_helper.rb +104 -0
- data/lib/hqmf-parser/converter/pass1/data_criteria_converter.rb +257 -0
- data/lib/hqmf-parser/converter/pass1/document_converter.rb +133 -0
- data/lib/hqmf-parser/converter/pass1/population_criteria_converter.rb +185 -0
- data/lib/hqmf-parser/converter/pass1/precondition_converter.rb +173 -0
- data/lib/hqmf-parser/converter/pass1/precondition_extractor.rb +201 -0
- data/lib/hqmf-parser/converter/pass1/simple_data_criteria.rb +26 -0
- data/lib/hqmf-parser/converter/pass1/simple_operator.rb +89 -0
- data/lib/hqmf-parser/converter/pass1/simple_population_criteria.rb +10 -0
- data/lib/hqmf-parser/converter/pass1/simple_precondition.rb +51 -0
- data/lib/hqmf-parser/converter/pass1/simple_restriction.rb +64 -0
- data/lib/hqmf-parser/converter/pass2/comparison_converter.rb +112 -0
- data/lib/hqmf-parser/converter/pass2/operator_converter.rb +102 -0
- data/lib/hqmf-parser/cql/data_criteria.rb +57 -0
- data/lib/hqmf-parser/cql/data_criteria_helpers/dc_definition_from_template_or_type_extract.rb +79 -0
- data/lib/hqmf-parser/cql/data_criteria_helpers/dc_post_processing.rb +43 -0
- data/lib/hqmf-parser/cql/document.rb +78 -0
- data/lib/hqmf-parser/cql/document_helpers/doc_population_helper.rb +124 -0
- data/lib/hqmf-parser/cql/value_set_helper.rb +103 -0
- data/lib/hqmf-parser/parser.rb +100 -0
- data/lib/qrda-export/catI-r5/qrda1_r5.rb +125 -0
- data/lib/qrda-export/helper/cat_1_view_helper.rb +142 -0
- data/lib/qrda-export/helper/code_system_helper.rb +77 -0
- data/lib/qrda-export/helper/date_helper.rb +81 -0
- data/lib/qrda-import/base-importers/demographics_importer.rb +47 -0
- data/lib/qrda-import/base-importers/medication_importer.rb +22 -0
- data/lib/qrda-import/base-importers/section_importer.rb +196 -0
- data/lib/qrda-import/cda_identifier.rb +19 -0
- data/lib/qrda-import/data-element-importers/adverse_event_importer.rb +23 -0
- data/lib/qrda-import/data-element-importers/allergy_intolerance_importer.rb +21 -0
- data/lib/qrda-import/data-element-importers/assessment_performed_importer.rb +23 -0
- data/lib/qrda-import/data-element-importers/communication_from_patient_to_provider_importer.rb +18 -0
- data/lib/qrda-import/data-element-importers/communication_from_provider_to_patient_importer.rb +18 -0
- data/lib/qrda-import/data-element-importers/communication_from_provider_to_provider_importer.rb +20 -0
- data/lib/qrda-import/data-element-importers/device_applied_importer.rb +23 -0
- data/lib/qrda-import/data-element-importers/device_order_importer.rb +18 -0
- data/lib/qrda-import/data-element-importers/diagnosis_importer.rb +23 -0
- data/lib/qrda-import/data-element-importers/diagnostic_study_order_importer.rb +20 -0
- data/lib/qrda-import/data-element-importers/diagnostic_study_performed_importer.rb +30 -0
- data/lib/qrda-import/data-element-importers/encounter_order_importer.rb +20 -0
- data/lib/qrda-import/data-element-importers/encounter_performed_importer.rb +41 -0
- data/lib/qrda-import/data-element-importers/immunization_administered_importer.rb +18 -0
- data/lib/qrda-import/data-element-importers/intervention_order_importer.rb +18 -0
- data/lib/qrda-import/data-element-importers/intervention_performed_importer.rb +22 -0
- data/lib/qrda-import/data-element-importers/laboratory_test_order_importer.rb +20 -0
- data/lib/qrda-import/data-element-importers/laboratory_test_performed_importer.rb +28 -0
- data/lib/qrda-import/data-element-importers/medication_active_importer.rb +17 -0
- data/lib/qrda-import/data-element-importers/medication_administered_importer.rb +17 -0
- data/lib/qrda-import/data-element-importers/medication_discharge_importer.rb +19 -0
- data/lib/qrda-import/data-element-importers/medication_dispensed_importer.rb +19 -0
- data/lib/qrda-import/data-element-importers/medication_order_importer.rb +16 -0
- data/lib/qrda-import/data-element-importers/patient_characteristic_expired.rb +21 -0
- data/lib/qrda-import/data-element-importers/physical_exam_performed_importer.rb +26 -0
- data/lib/qrda-import/data-element-importers/procedure_order_importer.rb +26 -0
- data/lib/qrda-import/data-element-importers/procedure_performed_importer.rb +34 -0
- data/lib/qrda-import/data-element-importers/substance_administered_importer.rb +16 -0
- data/lib/qrda-import/entry_finder.rb +20 -0
- data/lib/qrda-import/entry_package.rb +16 -0
- data/lib/qrda-import/narrative_reference_handler.rb +33 -0
- data/lib/qrda-import/patient_importer.rb +105 -0
- data/lib/util/code_system_helper.rb +76 -0
- data/lib/util/counter.rb +20 -0
- data/lib/util/hqmf_template_helper.rb +39 -0
- 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
|