hqmf-parser 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +23 -0
- data/README.md +903 -0
- data/Rakefile +19 -0
- data/VERSION +1 -0
- data/lib/hqmf-generator/hqmf-generator.rb +308 -0
- data/lib/hqmf-model/attribute.rb +35 -0
- data/lib/hqmf-model/data_criteria.rb +322 -0
- data/lib/hqmf-model/document.rb +172 -0
- data/lib/hqmf-model/population_criteria.rb +90 -0
- data/lib/hqmf-model/precondition.rb +85 -0
- data/lib/hqmf-model/types.rb +318 -0
- data/lib/hqmf-model/utilities.rb +52 -0
- data/lib/hqmf-parser.rb +54 -0
- data/lib/hqmf-parser/1.0/attribute.rb +68 -0
- data/lib/hqmf-parser/1.0/comparison.rb +34 -0
- data/lib/hqmf-parser/1.0/data_criteria.rb +105 -0
- data/lib/hqmf-parser/1.0/document.rb +209 -0
- data/lib/hqmf-parser/1.0/expression.rb +52 -0
- data/lib/hqmf-parser/1.0/population_criteria.rb +79 -0
- data/lib/hqmf-parser/1.0/precondition.rb +89 -0
- data/lib/hqmf-parser/1.0/range.rb +65 -0
- data/lib/hqmf-parser/1.0/restriction.rb +157 -0
- data/lib/hqmf-parser/1.0/utilities.rb +41 -0
- data/lib/hqmf-parser/2.0/data_criteria.rb +319 -0
- data/lib/hqmf-parser/2.0/document.rb +165 -0
- data/lib/hqmf-parser/2.0/population_criteria.rb +53 -0
- data/lib/hqmf-parser/2.0/precondition.rb +44 -0
- data/lib/hqmf-parser/2.0/types.rb +223 -0
- data/lib/hqmf-parser/2.0/utilities.rb +30 -0
- data/lib/hqmf-parser/converter/pass1/data_criteria_converter.rb +254 -0
- data/lib/hqmf-parser/converter/pass1/document_converter.rb +183 -0
- data/lib/hqmf-parser/converter/pass1/population_criteria_converter.rb +135 -0
- data/lib/hqmf-parser/converter/pass1/precondition_converter.rb +164 -0
- data/lib/hqmf-parser/converter/pass1/precondition_extractor.rb +159 -0
- data/lib/hqmf-parser/converter/pass1/simple_data_criteria.rb +35 -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 +63 -0
- data/lib/hqmf-parser/converter/pass1/simple_restriction.rb +64 -0
- data/lib/hqmf-parser/converter/pass2/comparison_converter.rb +91 -0
- data/lib/hqmf-parser/converter/pass2/operator_converter.rb +169 -0
- data/lib/hqmf-parser/converter/pass3/specific_occurrence_converter.rb +86 -0
- data/lib/hqmf-parser/converter/pass3/specific_occurrence_converter_bak.rb +70 -0
- data/lib/hqmf-parser/parser.rb +22 -0
- data/lib/hqmf-parser/value_sets/value_set_parser.rb +206 -0
- data/lib/tasks/coverme.rake +8 -0
- data/lib/tasks/hqmf.rake +141 -0
- data/lib/tasks/value_sets.rake +23 -0
- metadata +159 -0
@@ -0,0 +1,319 @@
|
|
1
|
+
module HQMF2
|
2
|
+
# Represents a data criteria specification
|
3
|
+
class DataCriteria
|
4
|
+
|
5
|
+
include HQMF2::Utilities
|
6
|
+
|
7
|
+
attr_reader :property, :type, :status, :value, :effective_time, :section
|
8
|
+
attr_reader :temporal_references, :subset_operators, :children_criteria
|
9
|
+
attr_reader :derivation_operator, :negation, :negation_code_list_id, :description
|
10
|
+
attr_reader :field_values, :source_data_criteria, :specific_occurrence_const
|
11
|
+
attr_reader :specific_occurrence, :is_source_data_criteria
|
12
|
+
|
13
|
+
# Create a new instance based on the supplied HQMF entry
|
14
|
+
# @param [Nokogiri::XML::Element] entry the parsed HQMF entry
|
15
|
+
def initialize(entry)
|
16
|
+
@entry = entry
|
17
|
+
@status = attr_val('./*/cda:statusCode/@code')
|
18
|
+
@description = attr_val('./*/cda:text/@value')
|
19
|
+
extract_negation()
|
20
|
+
extract_specific_or_source()
|
21
|
+
@effective_time = extract_effective_time
|
22
|
+
@temporal_references = extract_temporal_references
|
23
|
+
@derivation_operator = extract_derivation_operator
|
24
|
+
@field_values = extract_field_values
|
25
|
+
@subset_operators = extract_subset_operators
|
26
|
+
@children_criteria = extract_child_criteria
|
27
|
+
@id_xpath = './*/cda:id/cda:item/@extension'
|
28
|
+
@code_list_xpath = './*/cda:code'
|
29
|
+
@value_xpath = './*/cda:value'
|
30
|
+
|
31
|
+
# Try to determine what kind of data criteria we are dealing with
|
32
|
+
# First we look for a template id and if we find one just use the definition
|
33
|
+
# status and negation associated with that
|
34
|
+
if !extract_type_from_template_id()
|
35
|
+
# If no template id or not one we recognize then try to determine type from
|
36
|
+
# the definition element
|
37
|
+
extract_type_from_definition()
|
38
|
+
end
|
39
|
+
|
40
|
+
patch_xpaths_for_criteria_type()
|
41
|
+
end
|
42
|
+
|
43
|
+
def patch_xpaths_for_criteria_type
|
44
|
+
# Patch xpaths when necessary, HQMF data criteria are irregular in structure so
|
45
|
+
# the same information is found in different places depending on the type of
|
46
|
+
# criteria
|
47
|
+
# Assumes @definition and @status are already set
|
48
|
+
case @definition
|
49
|
+
when 'diagnosis', 'diagnosis_family_history'
|
50
|
+
@code_list_xpath = './cda:observationCriteria/cda:value'
|
51
|
+
when 'risk_category_assessment', 'procedure_result', 'laboratory_test', 'diagnostic_study_result', 'functional_status_result', 'intervention_result'
|
52
|
+
@value = extract_value
|
53
|
+
when 'medication'
|
54
|
+
case @status
|
55
|
+
when 'dispensed', 'ordered'
|
56
|
+
@code_list_xpath = './cda:supplyCriteria/cda:participation/cda:role/cda:code'
|
57
|
+
else # active or administered
|
58
|
+
@code_list_xpath = './cda:substanceAdministrationCriteria/cda:participation/cda:role/cda:code'
|
59
|
+
end
|
60
|
+
when 'patient_characteristic', 'patient_characteristic_birthdate', 'patient_characteristic_clinical_trial_participant', 'patient_characteristic_expired', 'patient_characteristic_gender', 'patient_characteristic_age', 'patient_characteristic_languages', 'patient_characteristic_marital_status', 'patient_characteristic_race'
|
61
|
+
@value = extract_value
|
62
|
+
when 'variable'
|
63
|
+
@value = extract_value
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def extract_type_from_definition
|
68
|
+
# See if we can find a match for the entry definition value and status.
|
69
|
+
entry_type = attr_val('./*/cda:definition/*/cda:id/@extension')
|
70
|
+
begin
|
71
|
+
settings = HQMF::DataCriteria.get_settings_for_definition(entry_type, @status)
|
72
|
+
@definition = entry_type
|
73
|
+
rescue
|
74
|
+
# if no exact match then try a string match just using entry definition value
|
75
|
+
case entry_type
|
76
|
+
when 'Problem', 'Problems'
|
77
|
+
@definition = 'diagnosis'
|
78
|
+
when 'Encounter', 'Encounters'
|
79
|
+
@definition = 'encounter'
|
80
|
+
when 'LabResults', 'Results'
|
81
|
+
@definition = 'laboratory_test'
|
82
|
+
when 'Procedure', 'Procedures'
|
83
|
+
@definition = 'procedure'
|
84
|
+
when 'Medication', 'Medications'
|
85
|
+
@definition = 'medication'
|
86
|
+
if !@status
|
87
|
+
@status = 'active'
|
88
|
+
end
|
89
|
+
when 'RX'
|
90
|
+
@definition = 'medication'
|
91
|
+
if !@status
|
92
|
+
@status = 'dispensed'
|
93
|
+
end
|
94
|
+
when 'Demographics'
|
95
|
+
@definition = definition_for_demographic
|
96
|
+
when 'Derived'
|
97
|
+
@definition = 'derived'
|
98
|
+
when nil
|
99
|
+
@definition = 'variable'
|
100
|
+
else
|
101
|
+
raise "Unknown data criteria template identifier [#{entry_type}]"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def extract_type_from_template_id
|
107
|
+
template_ids = @entry.xpath('./*/cda:templateId/cda:item', HQMF2::Document::NAMESPACES).collect do |template_def|
|
108
|
+
HQMF2::Utilities.attr_val(template_def, '@root')
|
109
|
+
end
|
110
|
+
if template_ids.include?(HQMF::DataCriteria::SOURCE_DATA_CRITERIA_TEMPLATE_ID)
|
111
|
+
@is_source_data_criteria = true
|
112
|
+
end
|
113
|
+
template_ids.each do |template_id|
|
114
|
+
defs = HQMF::DataCriteria.definition_for_template_id(template_id)
|
115
|
+
if defs
|
116
|
+
@definition = defs['definition']
|
117
|
+
@status = defs['status'].length > 0 ? defs['status'] : nil
|
118
|
+
@negation = defs['negation']
|
119
|
+
return true
|
120
|
+
end
|
121
|
+
end
|
122
|
+
false
|
123
|
+
end
|
124
|
+
|
125
|
+
def to_s
|
126
|
+
props = {
|
127
|
+
:property => property,
|
128
|
+
:type => type,
|
129
|
+
:status => status,
|
130
|
+
:section => section
|
131
|
+
}
|
132
|
+
"DataCriteria#{props.to_s}"
|
133
|
+
end
|
134
|
+
|
135
|
+
# Get the identifier of the criteria, used elsewhere within the document for referencing
|
136
|
+
# @return [String] the identifier of this data criteria
|
137
|
+
def id
|
138
|
+
attr_val(@id_xpath)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Get the title of the criteria, provides a human readable description
|
142
|
+
# @return [String] the title of this data criteria
|
143
|
+
def title
|
144
|
+
attr_val("#{@code_list_xpath}/cda:displayName/@value") || id
|
145
|
+
end
|
146
|
+
|
147
|
+
# Get the code list OID of the criteria, used as an index to the code list database
|
148
|
+
# @return [String] the code list identifier of this data criteria
|
149
|
+
def code_list_id
|
150
|
+
attr_val("#{@code_list_xpath}/@valueSet")
|
151
|
+
end
|
152
|
+
|
153
|
+
def inline_code_list
|
154
|
+
codeSystem = attr_val("#{@code_list_xpath}/@codeSystem")
|
155
|
+
if codeSystem
|
156
|
+
codeSystemName = HealthDataStandards::Util::CodeSystemHelper.code_system_for(codeSystem)
|
157
|
+
else
|
158
|
+
codeSystemName = attr_val("#{@code_list_xpath}/@codeSystemName")
|
159
|
+
end
|
160
|
+
codeValue = attr_val("#{@code_list_xpath}/@code")
|
161
|
+
if codeSystemName && codeValue
|
162
|
+
{codeSystemName => [codeValue]}
|
163
|
+
else
|
164
|
+
nil
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def to_model
|
169
|
+
mv = value ? value.to_model : nil
|
170
|
+
met = effective_time ? effective_time.to_model : nil
|
171
|
+
mtr = temporal_references.collect {|ref| ref.to_model}
|
172
|
+
mso = subset_operators.collect {|opr| opr.to_model}
|
173
|
+
field_values = {}
|
174
|
+
@field_values.each_pair do |id, val|
|
175
|
+
field_values[id] = val.to_model
|
176
|
+
end
|
177
|
+
|
178
|
+
HQMF::DataCriteria.new(id, title, nil, description, code_list_id, children_criteria,
|
179
|
+
derivation_operator, @definition, status, mv, field_values, met, inline_code_list,
|
180
|
+
@negation, @negation_code_list_id, mtr, mso, @specific_occurrence,
|
181
|
+
@specific_occurrence_const, @source_data_criteria)
|
182
|
+
end
|
183
|
+
|
184
|
+
private
|
185
|
+
|
186
|
+
def extract_negation
|
187
|
+
negation = attr_val('./*/@actionNegationInd')
|
188
|
+
@negation = (negation=='true')
|
189
|
+
if @negation
|
190
|
+
@negation_code_list_id = attr_val('./*/cda:reasonCode/cda:item/@valueSet')
|
191
|
+
else
|
192
|
+
@negation_code_list_id = nil
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def extract_child_criteria
|
197
|
+
@entry.xpath('./*/cda:excerpt/*/cda:id', HQMF2::Document::NAMESPACES).collect do |ref|
|
198
|
+
Reference.new(ref).id
|
199
|
+
end.compact
|
200
|
+
end
|
201
|
+
|
202
|
+
def extract_effective_time
|
203
|
+
effective_time_def = @entry.at_xpath('./*/cda:effectiveTime', HQMF2::Document::NAMESPACES)
|
204
|
+
if effective_time_def
|
205
|
+
EffectiveTime.new(effective_time_def)
|
206
|
+
else
|
207
|
+
nil
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def all_subset_operators
|
212
|
+
@entry.xpath('./*/cda:excerpt', HQMF2::Document::NAMESPACES).collect do |subset_operator|
|
213
|
+
SubsetOperator.new(subset_operator)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def extract_derivation_operator
|
218
|
+
derivation_operators = all_subset_operators.select do |operator|
|
219
|
+
['UNION', 'XPRODUCT'].include?(operator.type)
|
220
|
+
end
|
221
|
+
raise "More than one derivation operator in data criteria" if derivation_operators.size>1
|
222
|
+
derivation_operators.first ? derivation_operators.first.type : nil
|
223
|
+
end
|
224
|
+
|
225
|
+
def extract_subset_operators
|
226
|
+
all_subset_operators.select do |operator|
|
227
|
+
operator.type != 'UNION' && operator.type != 'XPRODUCT'
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def extract_specific_or_source
|
232
|
+
specific_def = @entry.at_xpath('./*/cda:outboundRelationship[cda:subsetCode/@code="SPECIFIC"]', HQMF2::Document::NAMESPACES)
|
233
|
+
source_def = @entry.at_xpath('./*/cda:outboundRelationship[cda:subsetCode/@code="SOURCE"]', HQMF2::Document::NAMESPACES)
|
234
|
+
if specific_def
|
235
|
+
@source_data_criteria = HQMF2::Utilities.attr_val(specific_def, './cda:observationReference/cda:id/@extension')
|
236
|
+
@specific_occurrence_const = HQMF2::Utilities.attr_val(specific_def, './cda:localVariableName/@controlInformationRoot')
|
237
|
+
@specific_occurrence = HQMF2::Utilities.attr_val(specific_def, './cda:localVariableName/@controlInformationExtension')
|
238
|
+
elsif source_def
|
239
|
+
@source_data_criteria = HQMF2::Utilities.attr_val(source_def, './cda:observationReference/cda:id/@extension')
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def extract_field_values
|
244
|
+
fields = {}
|
245
|
+
# extract most fields which use the same structure
|
246
|
+
@entry.xpath('./*/cda:outboundRelationship[*/cda:code]', HQMF2::Document::NAMESPACES).each do |field|
|
247
|
+
code = HQMF2::Utilities.attr_val(field, './*/cda:code/@code')
|
248
|
+
code_id = HQMF::DataCriteria::VALUE_FIELDS[code]
|
249
|
+
value = DataCriteria.parse_value(field, './*/cda:value')
|
250
|
+
fields[code_id] = value
|
251
|
+
end
|
252
|
+
# special case for facility location which uses a very different structure
|
253
|
+
@entry.xpath('./*/cda:outboundRelationship[*/cda:participation]', HQMF2::Document::NAMESPACES).each do |field|
|
254
|
+
code = HQMF2::Utilities.attr_val(field, './*/cda:participation/cda:role/@classCode')
|
255
|
+
code_id = HQMF::DataCriteria::VALUE_FIELDS[code]
|
256
|
+
value = Coded.new(field.at_xpath('./*/cda:participation/cda:role/cda:code', HQMF2::Document::NAMESPACES))
|
257
|
+
fields[code_id] = value
|
258
|
+
end
|
259
|
+
fields
|
260
|
+
end
|
261
|
+
|
262
|
+
def extract_temporal_references
|
263
|
+
@entry.xpath('./*/cda:temporallyRelatedInformation', HQMF2::Document::NAMESPACES).collect do |temporal_reference|
|
264
|
+
TemporalReference.new(temporal_reference)
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def extract_value()
|
269
|
+
DataCriteria.parse_value(@entry, @value_xpath)
|
270
|
+
end
|
271
|
+
|
272
|
+
def self.parse_value(node, xpath)
|
273
|
+
value = nil
|
274
|
+
value_def = node.at_xpath(xpath, HQMF2::Document::NAMESPACES)
|
275
|
+
if value_def
|
276
|
+
value_type_def = value_def.at_xpath('@xsi:type', HQMF2::Document::NAMESPACES)
|
277
|
+
if value_type_def
|
278
|
+
value_type = value_type_def.value
|
279
|
+
case value_type
|
280
|
+
when 'TS'
|
281
|
+
value = Value.new(value_def)
|
282
|
+
when 'IVL_PQ', 'IVL_INT'
|
283
|
+
value = Range.new(value_def)
|
284
|
+
when 'CD'
|
285
|
+
value = Coded.new(value_def)
|
286
|
+
when 'ANY'
|
287
|
+
value = AnyValue.new()
|
288
|
+
else
|
289
|
+
raise "Unknown value type [#{value_type}]"
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
value
|
294
|
+
end
|
295
|
+
|
296
|
+
def definition_for_demographic
|
297
|
+
demographic_type = attr_val('./cda:observationCriteria/cda:code/@code')
|
298
|
+
case demographic_type
|
299
|
+
when '21112-8'
|
300
|
+
"patient_characteristic_birthdate"
|
301
|
+
when '424144002'
|
302
|
+
"patient_characteristic_age"
|
303
|
+
when '263495000'
|
304
|
+
"patient_characteristic_gender"
|
305
|
+
when '102902016'
|
306
|
+
"patient_characteristic_languages"
|
307
|
+
when '125680007'
|
308
|
+
"patient_characteristic_marital_status"
|
309
|
+
when '103579009'
|
310
|
+
"patient_characteristic_race"
|
311
|
+
else
|
312
|
+
raise "Unknown demographic identifier [#{demographic_type}]"
|
313
|
+
end
|
314
|
+
|
315
|
+
end
|
316
|
+
|
317
|
+
end
|
318
|
+
|
319
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
module HQMF2
|
2
|
+
# Class representing an HQMF document
|
3
|
+
class Document
|
4
|
+
|
5
|
+
include HQMF2::Utilities
|
6
|
+
NAMESPACES = {'cda' => 'urn:hl7-org:v3', 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance'}
|
7
|
+
|
8
|
+
attr_reader :measure_period, :id, :hqmf_set_id, :hqmf_version_number, :populations, :attributes, :source_data_criteria
|
9
|
+
|
10
|
+
# Create a new HQMF2::Document instance by parsing at file at the supplied path
|
11
|
+
# @param [String] path the path to the HQMF document
|
12
|
+
def initialize(hqmf_contents)
|
13
|
+
@doc = @entry = Document.parse(hqmf_contents)
|
14
|
+
@id = attr_val('cda:QualityMeasureDocument/cda:id/@extension')
|
15
|
+
@hqmf_set_id = attr_val('cda:QualityMeasureDocument/cda:setId/@extension')
|
16
|
+
@hqmf_version_number = attr_val('cda:QualityMeasureDocument/cda:versionNumber/@value').to_i
|
17
|
+
measure_period_def = @doc.at_xpath('cda:QualityMeasureDocument/cda:controlVariable/cda:measurePeriod/cda:value', NAMESPACES)
|
18
|
+
if measure_period_def
|
19
|
+
@measure_period = EffectiveTime.new(measure_period_def)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Extract measure attributes
|
23
|
+
@attributes = @doc.xpath('/cda:QualityMeasureDocument/cda:subjectOf/cda:measureAttribute', NAMESPACES).collect do |attribute|
|
24
|
+
id = attribute.at_xpath('./cda:id/@extension', NAMESPACES).try(:value)
|
25
|
+
code = attribute.at_xpath('./cda:code/@code', NAMESPACES).try(:value)
|
26
|
+
name = attribute.at_xpath('./cda:code/cda:displayName/@value', NAMESPACES).try(:value)
|
27
|
+
value = attribute.at_xpath('./cda:value/@value', NAMESPACES).try(:value)
|
28
|
+
HQMF::Attribute.new(id, code, value, nil, name)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Extract the data criteria
|
32
|
+
@data_criteria = []
|
33
|
+
@source_data_criteria = []
|
34
|
+
@doc.xpath('cda:QualityMeasureDocument/cda:component/cda:dataCriteriaSection/cda:entry', NAMESPACES).each do |entry|
|
35
|
+
criteria = DataCriteria.new(entry)
|
36
|
+
if criteria.is_source_data_criteria
|
37
|
+
@source_data_criteria << criteria
|
38
|
+
else
|
39
|
+
@data_criteria << criteria
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Extract the population criteria and population collections
|
44
|
+
@populations = []
|
45
|
+
@population_criteria = []
|
46
|
+
|
47
|
+
population_counters = {}
|
48
|
+
ids_by_hqmf_id = {}
|
49
|
+
|
50
|
+
@doc.xpath('cda:QualityMeasureDocument/cda:component/cda:populationCriteriaSection', NAMESPACES).each_with_index do |population_def, population_index|
|
51
|
+
population = {}
|
52
|
+
|
53
|
+
stratifier_id_def = population_def.at_xpath('cda:templateId/cda:item[@root="'+HQMF::Document::STRATIFIED_POPULATION_TEMPLATE_ID+'"]/@controlInformationRoot', NAMESPACES)
|
54
|
+
population['stratification'] = stratifier_id_def.value if stratifier_id_def
|
55
|
+
|
56
|
+
{
|
57
|
+
HQMF::PopulationCriteria::IPP => 'patientPopulationCriteria',
|
58
|
+
HQMF::PopulationCriteria::DENOM => 'denominatorCriteria',
|
59
|
+
HQMF::PopulationCriteria::NUMER => 'numeratorCriteria',
|
60
|
+
HQMF::PopulationCriteria::EXCEP => 'denominatorExceptionCriteria',
|
61
|
+
HQMF::PopulationCriteria::DENEX => 'denominatorExclusionCriteria'
|
62
|
+
}.each_pair do |criteria_id, criteria_element_name|
|
63
|
+
criteria_def = population_def.at_xpath("cda:component[cda:#{criteria_element_name}]", NAMESPACES)
|
64
|
+
|
65
|
+
if criteria_def
|
66
|
+
|
67
|
+
criteria = PopulationCriteria.new(criteria_def, self)
|
68
|
+
|
69
|
+
# check to see if we have an identical population criteria.
|
70
|
+
# this can happen since the hqmf 2.0 will export a DENOM, NUMER, etc for each population, even if identical.
|
71
|
+
# if we have identical, just re-use it rather than creating DENOM_1, NUMER_1, etc.
|
72
|
+
identical = @population_criteria.select {|pc| pc.to_model.base_json.to_json == criteria.to_model.base_json.to_json}
|
73
|
+
|
74
|
+
if (identical.empty?)
|
75
|
+
# this section constructs a human readable id. The first IPP will be IPP, the second will be IPP_1, etc. This allows the populations to be
|
76
|
+
# more readable. The alternative would be to have the hqmf ids in the populations, which would work, but is difficult to read the populations.
|
77
|
+
if ids_by_hqmf_id["#{criteria.hqmf_id}-#{population['stratification']}"]
|
78
|
+
criteria.create_human_readable_id(ids_by_hqmf_id[criteria.hqmf_id])
|
79
|
+
else
|
80
|
+
if population_counters[criteria_id]
|
81
|
+
population_counters[criteria_id] += 1
|
82
|
+
criteria.create_human_readable_id("#{criteria_id}_#{population_counters[criteria_id]}")
|
83
|
+
else
|
84
|
+
population_counters[criteria_id] = 0
|
85
|
+
criteria.create_human_readable_id(criteria_id)
|
86
|
+
end
|
87
|
+
ids_by_hqmf_id["#{criteria.hqmf_id}-#{population['stratification']}"] = criteria.id
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
@population_criteria << criteria
|
92
|
+
population[criteria_id] = criteria.id
|
93
|
+
else
|
94
|
+
population[criteria_id] = identical.first.id
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
id_def = population_def.at_xpath('cda:id/@extension', NAMESPACES)
|
99
|
+
population['id'] = id_def ? id_def.value : "Population#{population_index}"
|
100
|
+
title_def = population_def.at_xpath('cda:title/@value', NAMESPACES)
|
101
|
+
population['title'] = title_def ? title_def.value : "Population #{population_index}"
|
102
|
+
@populations << population
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Get the title of the measure
|
107
|
+
# @return [String] the title
|
108
|
+
def title
|
109
|
+
@doc.at_xpath('cda:QualityMeasureDocument/cda:title/@value', NAMESPACES).inner_text
|
110
|
+
end
|
111
|
+
|
112
|
+
# Get the description of the measure
|
113
|
+
# @return [String] the description
|
114
|
+
def description
|
115
|
+
description = @doc.at_xpath('cda:QualityMeasureDocument/cda:text/@value', NAMESPACES)
|
116
|
+
description==nil ? '' : description.inner_text
|
117
|
+
end
|
118
|
+
|
119
|
+
# Get all the population criteria defined by the measure
|
120
|
+
# @return [Array] an array of HQMF2::PopulationCriteria
|
121
|
+
def all_population_criteria
|
122
|
+
@population_criteria
|
123
|
+
end
|
124
|
+
|
125
|
+
# Get a specific population criteria by id.
|
126
|
+
# @param [String] id the population identifier
|
127
|
+
# @return [HQMF2::PopulationCriteria] the matching criteria, raises an Exception if not found
|
128
|
+
def population_criteria(id)
|
129
|
+
find(@population_criteria, :id, id)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Get all the data criteria defined by the measure
|
133
|
+
# @return [Array] an array of HQMF2::DataCriteria describing the data elements used by the measure
|
134
|
+
def all_data_criteria
|
135
|
+
@data_criteria
|
136
|
+
end
|
137
|
+
|
138
|
+
# Get a specific data criteria by id.
|
139
|
+
# @param [String] id the data criteria identifier
|
140
|
+
# @return [HQMF2::DataCriteria] the matching data criteria, raises an Exception if not found
|
141
|
+
def data_criteria(id)
|
142
|
+
find(@data_criteria, :id, id)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Parse an XML document at the supplied path
|
146
|
+
# @return [Nokogiri::XML::Document]
|
147
|
+
def self.parse(hqmf_contents)
|
148
|
+
doc = Nokogiri::XML(hqmf_contents)
|
149
|
+
doc
|
150
|
+
end
|
151
|
+
|
152
|
+
def to_model
|
153
|
+
dcs = all_data_criteria.collect {|dc| dc.to_model}
|
154
|
+
pcs = all_population_criteria.collect {|pc| pc.to_model}
|
155
|
+
sdc = source_data_criteria.collect{|dc| dc.to_model}
|
156
|
+
HQMF::Document.new(id, id, hqmf_set_id, hqmf_version_number, title, description, pcs, dcs, sdc, attributes, measure_period.to_model, populations)
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
def find(collection, attribute, value)
|
162
|
+
collection.find {|e| e.send(attribute)==value}
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|