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,52 @@
|
|
1
|
+
|
2
|
+
module HQMF
|
3
|
+
module Conversion
|
4
|
+
module Utilities
|
5
|
+
def build_hash(source, elements)
|
6
|
+
hash = {}
|
7
|
+
elements.each do |element|
|
8
|
+
value = source.send(element)
|
9
|
+
hash[element] = value unless value.nil?
|
10
|
+
end
|
11
|
+
hash
|
12
|
+
end
|
13
|
+
|
14
|
+
def json_array(elements)
|
15
|
+
return nil if elements.nil?
|
16
|
+
array = []
|
17
|
+
elements.each do |element|
|
18
|
+
if (element.is_a? OpenStruct)
|
19
|
+
array << openstruct_to_json(element)
|
20
|
+
else
|
21
|
+
array << element.to_json
|
22
|
+
end
|
23
|
+
end
|
24
|
+
array.compact!
|
25
|
+
(array.empty?) ? nil : array
|
26
|
+
end
|
27
|
+
|
28
|
+
def openstruct_to_json(element)
|
29
|
+
json = {}
|
30
|
+
element.marshal_dump.each do |key,value|
|
31
|
+
if value.is_a? OpenStruct
|
32
|
+
json[key] = openstruct_to_json(value)
|
33
|
+
elsif (value.class.to_s.split("::").first.start_with? 'HQMF')
|
34
|
+
json[key] = value.to_json
|
35
|
+
else
|
36
|
+
json[key] = value
|
37
|
+
end
|
38
|
+
end
|
39
|
+
json
|
40
|
+
end
|
41
|
+
|
42
|
+
def check_equality(left,right)
|
43
|
+
same = true
|
44
|
+
left.instance_variables.each do |variable|
|
45
|
+
same &&= left.instance_variable_get(variable) == right.instance_variable_get(variable)
|
46
|
+
end
|
47
|
+
same
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/hqmf-parser.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
# require
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'json'
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
# require_relative
|
7
|
+
require_relative 'util/counter.rb'
|
8
|
+
require_relative 'util/code_system_helper'
|
9
|
+
require_relative 'util/hqmf_template_helper'
|
10
|
+
|
11
|
+
require_relative 'hqmf-model/utilities.rb'
|
12
|
+
|
13
|
+
require_relative 'hqmf-parser/1.0/utilities'
|
14
|
+
require_relative 'hqmf-parser/1.0/range'
|
15
|
+
require_relative 'hqmf-parser/1.0/document'
|
16
|
+
require_relative 'hqmf-parser/1.0/data_criteria'
|
17
|
+
require_relative 'hqmf-parser/1.0/attribute'
|
18
|
+
require_relative 'hqmf-parser/1.0/population_criteria'
|
19
|
+
require_relative 'hqmf-parser/1.0/observation'
|
20
|
+
require_relative 'hqmf-parser/1.0/precondition'
|
21
|
+
require_relative 'hqmf-parser/1.0/restriction'
|
22
|
+
require_relative 'hqmf-parser/1.0/comparison'
|
23
|
+
require_relative 'hqmf-parser/1.0/expression'
|
24
|
+
|
25
|
+
require_relative 'hqmf-parser/2.0/utilities'
|
26
|
+
require_relative 'hqmf-parser/2.0/types'
|
27
|
+
require_relative 'hqmf-parser/2.0/document_helpers/doc_population_helper'
|
28
|
+
require_relative 'hqmf-parser/2.0/document_helpers/doc_utilities'
|
29
|
+
require_relative 'hqmf-parser/2.0/document'
|
30
|
+
require_relative 'hqmf-parser/2.0/field_value_helper'
|
31
|
+
require_relative 'hqmf-parser/2.0/value_set_helper'
|
32
|
+
require_relative 'hqmf-parser/2.0/source_data_criteria_helper'
|
33
|
+
require_relative 'hqmf-parser/2.0/data_criteria_helpers/dc_definition_from_template_or_type_extract'
|
34
|
+
require_relative 'hqmf-parser/2.0/data_criteria_helpers/dc_specific_occurrences_and_source_data_criteria_extract'
|
35
|
+
require_relative 'hqmf-parser/2.0/data_criteria_helpers/dc_post_processing'
|
36
|
+
require_relative 'hqmf-parser/2.0/data_criteria_helpers/dc_base_extract'
|
37
|
+
require_relative 'hqmf-parser/2.0/data_criteria'
|
38
|
+
require_relative 'hqmf-parser/2.0/population_criteria'
|
39
|
+
require_relative 'hqmf-parser/2.0/precondition'
|
40
|
+
|
41
|
+
require_relative 'hqmf-parser/cql/document'
|
42
|
+
require_relative 'hqmf-parser/cql/data_criteria_helpers/dc_definition_from_template_or_type_extract'
|
43
|
+
require_relative 'hqmf-parser/cql/document_helpers/doc_population_helper'
|
44
|
+
require_relative 'hqmf-parser/cql/data_criteria_helpers/dc_post_processing'
|
45
|
+
require_relative 'hqmf-parser/cql/data_criteria'
|
46
|
+
require_relative 'hqmf-parser/cql/value_set_helper'
|
47
|
+
|
48
|
+
require_relative 'hqmf-model/data_criteria.rb'
|
49
|
+
require_relative 'hqmf-model/document.rb'
|
50
|
+
require_relative 'hqmf-model/population_criteria.rb'
|
51
|
+
require_relative 'hqmf-model/precondition.rb'
|
52
|
+
require_relative 'hqmf-model/types.rb'
|
53
|
+
require_relative 'hqmf-model/attribute.rb'
|
54
|
+
|
55
|
+
require_relative 'hqmf-parser/converter/pass1/document_converter'
|
56
|
+
require_relative 'hqmf-parser/converter/pass1/data_criteria_converter'
|
57
|
+
require_relative 'hqmf-parser/converter/pass1/population_criteria_converter'
|
58
|
+
require_relative 'hqmf-parser/converter/pass1/precondition_converter'
|
59
|
+
require_relative 'hqmf-parser/converter/pass1/precondition_extractor'
|
60
|
+
require_relative 'hqmf-parser/converter/pass1/simple_restriction'
|
61
|
+
require_relative 'hqmf-parser/converter/pass1/simple_operator'
|
62
|
+
require_relative 'hqmf-parser/converter/pass1/simple_precondition'
|
63
|
+
require_relative 'hqmf-parser/converter/pass1/simple_data_criteria'
|
64
|
+
require_relative 'hqmf-parser/converter/pass1/simple_population_criteria'
|
65
|
+
|
66
|
+
require_relative 'hqmf-parser/converter/pass2/comparison_converter'
|
67
|
+
require_relative 'hqmf-parser/converter/pass2/operator_converter'
|
68
|
+
|
69
|
+
require_relative 'hqmf-parser/parser'
|
70
|
+
|
71
|
+
require_relative 'qrda-export/helper/code_system_helper.rb'
|
72
|
+
require_relative 'qrda-export/helper/date_helper.rb'
|
73
|
+
require_relative 'qrda-export/helper/cat_1_view_helper.rb'
|
74
|
+
|
75
|
+
require_relative 'qrda-export/catI-r5/qrda1_r5.rb'
|
76
|
+
|
77
|
+
require_relative 'qrda-import/entry_package.rb'
|
78
|
+
require_relative 'qrda-import/cda_identifier.rb'
|
79
|
+
require_relative 'qrda-import/narrative_reference_handler.rb'
|
80
|
+
require_relative 'qrda-import/entry_finder.rb'
|
81
|
+
|
82
|
+
require_relative 'qrda-import/base-importers/section_importer.rb'
|
83
|
+
require_relative 'qrda-import/base-importers/demographics_importer.rb'
|
84
|
+
require_relative 'qrda-import/base-importers/medication_importer.rb'
|
85
|
+
|
86
|
+
require_relative 'qrda-import/data-element-importers/allergy_intolerance_importer.rb'
|
87
|
+
require_relative 'qrda-import/data-element-importers/diagnostic_study_order_importer.rb'
|
88
|
+
require_relative 'qrda-import/data-element-importers/intervention_order_importer.rb'
|
89
|
+
require_relative 'qrda-import/data-element-importers/encounter_performed_importer.rb'
|
90
|
+
require_relative 'qrda-import/data-element-importers/diagnosis_importer.rb'
|
91
|
+
require_relative 'qrda-import/data-element-importers/medication_active_importer.rb'
|
92
|
+
require_relative 'qrda-import/data-element-importers/medication_order_importer.rb'
|
93
|
+
require_relative 'qrda-import/data-element-importers/procedure_performed_importer.rb'
|
94
|
+
require_relative 'qrda-import/data-element-importers/physical_exam_performed_importer.rb'
|
95
|
+
require_relative 'qrda-import/data-element-importers/laboratory_test_performed_importer.rb'
|
96
|
+
require_relative 'qrda-import/data-element-importers/adverse_event_importer.rb'
|
97
|
+
require_relative 'qrda-import/data-element-importers/assessment_performed_importer.rb'
|
98
|
+
require_relative 'qrda-import/data-element-importers/communication_from_patient_to_provider_importer.rb'
|
99
|
+
require_relative 'qrda-import/data-element-importers/communication_from_provider_to_patient_importer.rb'
|
100
|
+
require_relative 'qrda-import/data-element-importers/communication_from_provider_to_provider_importer.rb'
|
101
|
+
require_relative 'qrda-import/data-element-importers/device_applied_importer.rb'
|
102
|
+
require_relative 'qrda-import/data-element-importers/device_order_importer.rb'
|
103
|
+
require_relative 'qrda-import/data-element-importers/diagnostic_study_performed_importer.rb'
|
104
|
+
require_relative 'qrda-import/data-element-importers/encounter_order_importer.rb'
|
105
|
+
require_relative 'qrda-import/data-element-importers/immunization_administered_importer.rb'
|
106
|
+
require_relative 'qrda-import/data-element-importers/intervention_performed_importer.rb'
|
107
|
+
require_relative 'qrda-import/data-element-importers/laboratory_test_order_importer.rb'
|
108
|
+
require_relative 'qrda-import/data-element-importers/medication_administered_importer.rb'
|
109
|
+
require_relative 'qrda-import/data-element-importers/medication_discharge_importer.rb'
|
110
|
+
require_relative 'qrda-import/data-element-importers/medication_dispensed_importer.rb'
|
111
|
+
require_relative 'qrda-import/data-element-importers/patient_characteristic_expired.rb'
|
112
|
+
require_relative 'qrda-import/data-element-importers/procedure_order_importer.rb'
|
113
|
+
require_relative 'qrda-import/data-element-importers/substance_administered_importer.rb'
|
114
|
+
require_relative 'qrda-import/patient_importer.rb'
|
115
|
+
require_relative 'ext/data_element.rb'
|
116
|
+
require_relative 'ext/code.rb'
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module HQMF1
|
2
|
+
# Represents a HQMF measure attribute
|
3
|
+
class Attribute
|
4
|
+
|
5
|
+
include HQMF1::Utilities
|
6
|
+
|
7
|
+
# Create a new instance based on the supplied HQMF
|
8
|
+
# @param [Nokogiri::XML::Element] entry the measure attribute element
|
9
|
+
def initialize(entry)
|
10
|
+
@entry = entry
|
11
|
+
end
|
12
|
+
|
13
|
+
# Get the attribute code
|
14
|
+
# @return [String] the code
|
15
|
+
def code
|
16
|
+
if (@entry.at_xpath('./cda:code/@code'))
|
17
|
+
@entry.at_xpath('./cda:code/@code').value
|
18
|
+
elsif @entry.at_xpath('./cda:code/@nullFlavor')
|
19
|
+
@entry.at_xpath('./cda:code/@nullFlavor').value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Get the attribute name
|
24
|
+
# @return [String] the name
|
25
|
+
def name
|
26
|
+
if (@entry.at_xpath('./cda:code/@displayName'))
|
27
|
+
@entry.at_xpath('./cda:code/@displayName').value
|
28
|
+
elsif @entry.at_xpath('cda:code/cda:originalText')
|
29
|
+
@entry.at_xpath('cda:code/cda:originalText').text
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Get the attribute id, used elsewhere in the document to refer to the attribute
|
34
|
+
# @return [String] the id
|
35
|
+
def id
|
36
|
+
attr_val('./cda:id/@root')
|
37
|
+
end
|
38
|
+
|
39
|
+
# Get the attribute value
|
40
|
+
# @return [String] the value
|
41
|
+
def value
|
42
|
+
val = attr_val('./cda:value/@value')
|
43
|
+
val ||= attr_val('./cda:value/@extension')
|
44
|
+
if val
|
45
|
+
val
|
46
|
+
else
|
47
|
+
@entry.at_xpath('./cda:value').inner_text
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Get the unit of the attribute value or nil if none is defined
|
52
|
+
# @return [String] the unit
|
53
|
+
def unit
|
54
|
+
attr_val('./cda:value/@unit')
|
55
|
+
end
|
56
|
+
|
57
|
+
# Get a JS friendly constant name for this measure attribute
|
58
|
+
def const_name
|
59
|
+
components = name.gsub(/\W/,' ').split.collect {|word| word.strip.upcase }
|
60
|
+
components.join '_'
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_json
|
64
|
+
json = {self.const_name => build_hash(self, [:code,:value,:unit,:name,:id])}
|
65
|
+
extend_json_for_enhanced_model(json)
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def extend_json_for_enhanced_model(json)
|
71
|
+
if (@entry.at_xpath('./cda:id'))
|
72
|
+
json[self.const_name][:id_obj] = id_xml_to_json('./cda:id')
|
73
|
+
end
|
74
|
+
|
75
|
+
if (@entry.at_xpath('./cda:code'))
|
76
|
+
json[self.const_name][:code_obj] = code_xml_to_json('./cda:code')
|
77
|
+
end
|
78
|
+
|
79
|
+
if (@entry.at_xpath('./cda:value'))
|
80
|
+
value_hash = { :type => attr_val('./cda:value/@xsi:type')}
|
81
|
+
case value_hash[:type]
|
82
|
+
when 'II'
|
83
|
+
value_hash.merge!(id_xml_to_json('./cda:value'))
|
84
|
+
when 'CD'
|
85
|
+
value_hash.merge!(code_xml_to_json('./cda:value'))
|
86
|
+
when 'ED'
|
87
|
+
value_hash[:value] = @entry.at_xpath('./cda:value').inner_text
|
88
|
+
value_hash[:media_type] = attr_val('./cda:value/@mediaType')
|
89
|
+
else
|
90
|
+
if (attr_val('./cda:value/@value'))
|
91
|
+
value_hash.merge!(HQMF1::Value.new(@entry.at_xpath('./cda:value')).to_json)
|
92
|
+
value_hash[:expression] = @entry.at_xpath('./cda:value/cda:expression').try(:inner_text).try(:strip)
|
93
|
+
else
|
94
|
+
value_hash[:value] = @entry.at_xpath('./cda:value').inner_text.try(:strip)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
json[self.const_name][:value_obj] = value_hash
|
98
|
+
end
|
99
|
+
json
|
100
|
+
end
|
101
|
+
|
102
|
+
def id_xml_to_json(entry_xpath)
|
103
|
+
{
|
104
|
+
:root => attr_val("#{entry_xpath}/@root"),
|
105
|
+
:extension => attr_val("#{entry_xpath}/@extension")
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
def code_xml_to_json(entry_xpath)
|
110
|
+
{
|
111
|
+
:type => attr_val("#{entry_xpath}/@xsi:type") || 'CD',
|
112
|
+
:system => attr_val("#{entry_xpath}/@codeSystem"),
|
113
|
+
:code => attr_val("#{entry_xpath}/@code"),
|
114
|
+
:code_list_id => attr_val("#{entry_xpath}/@valueSet"),
|
115
|
+
:title => attr_val("#{entry_xpath}/@displayName"),
|
116
|
+
:null_flavor => attr_val("#{entry_xpath}/@nullFlavor"),
|
117
|
+
:original_text => @entry.at_xpath("#{entry_xpath}/cda:originalText").try(:inner_text)
|
118
|
+
}
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module HQMF1
|
2
|
+
class Comparison
|
3
|
+
|
4
|
+
include HQMF1::Utilities
|
5
|
+
|
6
|
+
attr_reader :restrictions, :data_criteria_id, :title, :subset
|
7
|
+
|
8
|
+
def initialize(data_criteria_id, entry, parent, doc)
|
9
|
+
@doc = doc
|
10
|
+
@data_criteria_id = data_criteria_id
|
11
|
+
@entry = entry
|
12
|
+
title_def = @entry.at_xpath('./*/cda:title')
|
13
|
+
if title_def
|
14
|
+
@title = title_def.inner_text
|
15
|
+
end
|
16
|
+
@restrictions = []
|
17
|
+
restriction_def = @entry.at_xpath('./*/cda:sourceOf')
|
18
|
+
if restriction_def
|
19
|
+
@entry.xpath('./*/cda:sourceOf').each do |restriction|
|
20
|
+
@restrictions << Restriction.new(restriction, self, @doc)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_json
|
26
|
+
|
27
|
+
json = build_hash(self, [:data_criteria_id,:title,:subset])
|
28
|
+
json[:restrictions] = json_array(@restrictions)
|
29
|
+
json
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module HQMF1
|
2
|
+
# Represents a data criteria specification
|
3
|
+
class DataCriteria
|
4
|
+
|
5
|
+
include HQMF1::Utilities
|
6
|
+
|
7
|
+
attr_accessor :code_list_id, :derived_from, :definition, :status, :negation, :specific_occurrence
|
8
|
+
|
9
|
+
# Create a new instance based on the supplied HQMF entry
|
10
|
+
# @param [Nokogiri::XML::Element] entry the parsed HQMF entry
|
11
|
+
def initialize(entry, occurrence_counters)
|
12
|
+
@entry = entry
|
13
|
+
|
14
|
+
template_map = HQMF::DataCriteria.get_template_id_map()
|
15
|
+
oid_xpath_file = File.expand_path('../data_criteria_oid_xpath.json', __FILE__)
|
16
|
+
oid_xpath_map = JSON.parse(File.read(oid_xpath_file))
|
17
|
+
template_id = attr_val('cda:act/cda:templateId/@root') || attr_val('cda:observation/cda:templateId/@root')
|
18
|
+
|
19
|
+
# check to see if this is a derived data criteria. These are used for multiple occurrences.
|
20
|
+
derived_entry = @entry.at_xpath('./*/cda:sourceOf[@typeCode="DRIV"]')
|
21
|
+
if derived_entry
|
22
|
+
derived = derived_entry.at_xpath('cda:act/cda:id/@root') || derived_entry.at_xpath('cda:observation/cda:id/@root')
|
23
|
+
@derived_from = derived.value
|
24
|
+
occurrence_counters[@derived_from] ||= HQMF::InstanceCounter.new
|
25
|
+
@occurrence_key = occurrence_counters[@derived_from].next-1
|
26
|
+
@specific_occurrence = "#{('A'..'ZZ').to_a[@occurrence_key]}"
|
27
|
+
end
|
28
|
+
|
29
|
+
template = template_map[template_id]
|
30
|
+
if template
|
31
|
+
@negation=template["negation"]
|
32
|
+
@definition=template["definition"]
|
33
|
+
@status=template["status"]
|
34
|
+
@key=@definition+(@status.empty? ? '' : "_#{@status}")
|
35
|
+
else
|
36
|
+
raise "Unknown data criteria template identifier [#{template_id}]"
|
37
|
+
end
|
38
|
+
|
39
|
+
# Get the code list OID of the criteria, used as an index to the code list database
|
40
|
+
@code_list_id = attr_val(oid_xpath_map[@key]['oid_xpath'])
|
41
|
+
unless @code_list_id
|
42
|
+
puts "\tcode list id not found, getting default" if !@derived_from
|
43
|
+
@code_list_id = attr_val('cda:act/cda:sourceOf//cda:code/@code')
|
44
|
+
end
|
45
|
+
|
46
|
+
puts "\tno oid defined for data criteria: #{@key}" if !@code_list_id and !@derived_from
|
47
|
+
|
48
|
+
end
|
49
|
+
|
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('cda:act/cda:id/@root') || attr_val('cda:observation/cda:id/@root')
|
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
|
+
title = description
|
60
|
+
title = "Occurrence #{@specific_occurrence}: #{title}" if @derived_from
|
61
|
+
title
|
62
|
+
end
|
63
|
+
|
64
|
+
def description
|
65
|
+
if (@entry.at_xpath('.//cda:title'))
|
66
|
+
description = @entry.at_xpath('.//cda:title').inner_text
|
67
|
+
else
|
68
|
+
description = @entry.at_xpath('.//cda:localVariableName').inner_text
|
69
|
+
end
|
70
|
+
description
|
71
|
+
end
|
72
|
+
|
73
|
+
# Get a JS friendly constant name for this measure attribute
|
74
|
+
def const_name
|
75
|
+
components = title.gsub(/\W/,' ').split.collect {|word| word.strip.upcase }
|
76
|
+
if @derived_from
|
77
|
+
components << HQMF::Counter.instance.next
|
78
|
+
end
|
79
|
+
components.join '_'
|
80
|
+
end
|
81
|
+
|
82
|
+
def to_json
|
83
|
+
json = build_hash(self, [:id,:title,:code_list_id,:derived_from,:description, :definition, :status, :negation, :specific_occurrence])
|
84
|
+
{
|
85
|
+
self.const_name => json
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
module HQMF1
|
2
|
+
# Class representing an HQMF document
|
3
|
+
class Document
|
4
|
+
|
5
|
+
include HQMF1::Utilities
|
6
|
+
|
7
|
+
attr_reader :hqmf_id, :hqmf_set_id, :hqmf_version_number
|
8
|
+
|
9
|
+
# Create a new HQMF1::Document instance by parsing the supplied contents
|
10
|
+
# @param [String] hqmf_contents the contents of an HQMF v1.0 document
|
11
|
+
def initialize(hqmf_contents)
|
12
|
+
|
13
|
+
@doc = Document.parse(hqmf_contents)
|
14
|
+
occurrence_counters = {}
|
15
|
+
@data_criteria = @doc.xpath('//cda:section[cda:code/@code="57025-9"]/cda:entry').collect do |entry|
|
16
|
+
DataCriteria.new(entry, occurrence_counters)
|
17
|
+
end
|
18
|
+
|
19
|
+
@supplemental= @doc.xpath('//cda:section[cda:code/@code="69670-8"]/cda:entry').collect do |entry|
|
20
|
+
DataCriteria.new(entry, occurrence_counters)
|
21
|
+
end
|
22
|
+
|
23
|
+
@data_criteria.concat @supplemental
|
24
|
+
backfill_derived_code_lists
|
25
|
+
|
26
|
+
@attributes = @doc.xpath('//cda:subjectOf/cda:measureAttribute').collect do |attr|
|
27
|
+
Attribute.new(attr)
|
28
|
+
end
|
29
|
+
@population_criteria = @doc.xpath('//cda:section[cda:code/@code="57026-7"]/cda:entry').collect do |criteria|
|
30
|
+
PopulationCriteria.new(criteria, self)
|
31
|
+
end
|
32
|
+
observations = @doc.xpath('//cda:section[cda:code/@code="57027-5"]/cda:entry').collect do |observation|
|
33
|
+
Observation.new(observation, self)
|
34
|
+
end
|
35
|
+
@population_criteria.concat(observations)
|
36
|
+
|
37
|
+
@stratification = @doc.xpath('//cda:section[cda:code/@code="69669-0"]/cda:entry').collect do |strat|
|
38
|
+
PopulationCriteria.new(strat, self)
|
39
|
+
end
|
40
|
+
|
41
|
+
if (@stratification and !@stratification.empty?)
|
42
|
+
@stratification.each do |stratification|
|
43
|
+
@population_criteria << stratification
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
@hqmf_set_id = @doc.at_xpath('//cda:setId/@root').value.upcase
|
48
|
+
@hqmf_id = @doc.at_xpath('//cda:id/@root').value.upcase
|
49
|
+
@hqmf_version_number = @doc.at_xpath('//cda:versionNumber/@value').value.to_i
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
# Get the title of the measure
|
54
|
+
# @return [String] the title
|
55
|
+
def title
|
56
|
+
@doc.at_xpath('cda:QualityMeasureDocument/cda:title').inner_text
|
57
|
+
end
|
58
|
+
|
59
|
+
# Get the description of the measure
|
60
|
+
# @return [String] the description
|
61
|
+
def description
|
62
|
+
@doc.at_xpath('cda:QualityMeasureDocument/cda:text').inner_text
|
63
|
+
end
|
64
|
+
|
65
|
+
# Get all the attributes defined by the measure
|
66
|
+
# @return [Array] an array of HQMF1::Attribute
|
67
|
+
def all_attributes
|
68
|
+
@attributes
|
69
|
+
end
|
70
|
+
|
71
|
+
# Get a specific attribute by id.
|
72
|
+
# @param [String] id the attribute identifier
|
73
|
+
# @return [HQMF1::Attribute] the matching attribute, raises an Exception if not found
|
74
|
+
def attribute(id)
|
75
|
+
find(@attributes, :id, id)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Get a specific attribute by code.
|
79
|
+
# @param [String] code the attribute code
|
80
|
+
# @return [HQMF1::Attribute] the matching attribute, raises an Exception if not found
|
81
|
+
def attribute_for_code(code)
|
82
|
+
find(@attributes, :code, code)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Get all the population criteria defined by the measure
|
86
|
+
# @return [Array] an array of HQMF1::PopulationCriteria
|
87
|
+
def all_population_criteria
|
88
|
+
@population_criteria
|
89
|
+
end
|
90
|
+
|
91
|
+
def stratification
|
92
|
+
@stratification
|
93
|
+
end
|
94
|
+
|
95
|
+
# Get a specific population criteria by id.
|
96
|
+
# @param [String] id the population identifier
|
97
|
+
# @return [HQMF1::PopulationCriteria] the matching criteria, raises an Exception if not found
|
98
|
+
def population_criteria(id)
|
99
|
+
find(@population_criteria, :id, id)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Get a specific population criteria by code.
|
103
|
+
# @param [String] code the population criteria code
|
104
|
+
# @return [HQMF1::PopulationCriteria] the matching criteria, raises an Exception if not found
|
105
|
+
def population_criteria_for_code(code)
|
106
|
+
find(@population_criteria, :code, code)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Get all the data criteria defined by the measure
|
110
|
+
# @return [Array] an array of HQMF1::DataCriteria describing the data elements used by the measure
|
111
|
+
def all_data_criteria
|
112
|
+
@data_criteria
|
113
|
+
end
|
114
|
+
|
115
|
+
# Get a specific data criteria by id.
|
116
|
+
# @param [String] id the data criteria identifier
|
117
|
+
# @return [HQMF1::DataCriteria] the matching data criteria, raises an Exception if not found
|
118
|
+
def data_criteria(id)
|
119
|
+
val = find(@data_criteria, :id, id) || raise("unknown data criteria #{id}")
|
120
|
+
end
|
121
|
+
|
122
|
+
# Parse an XML document from the supplied contents
|
123
|
+
# @return [Nokogiri::XML::Document]
|
124
|
+
def self.parse(hqmf_contents)
|
125
|
+
doc = hqmf_contents.kind_of?(Nokogiri::XML::Document) ? hqmf_contents : Nokogiri::XML(hqmf_contents)
|
126
|
+
doc.root.add_namespace_definition('cda', 'urn:hl7-org:v3')
|
127
|
+
doc
|
128
|
+
end
|
129
|
+
|
130
|
+
# if the data criteria is derived from another criteria, then we want to grab the properties from the derived criteria
|
131
|
+
# this is typically the case with Occurrence A, Occurrence B type data criteria
|
132
|
+
def backfill_derived_code_lists
|
133
|
+
data_criteria_by_id = {}
|
134
|
+
@data_criteria.each {|criteria| data_criteria_by_id[criteria.id] = criteria}
|
135
|
+
@data_criteria.each do |criteria|
|
136
|
+
if (criteria.derived_from)
|
137
|
+
derived_from = data_criteria_by_id[criteria.derived_from]
|
138
|
+
criteria.definition = derived_from.definition
|
139
|
+
criteria.status = derived_from.status
|
140
|
+
criteria.code_list_id = derived_from.code_list_id
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def to_json
|
146
|
+
json = build_hash(self, [:title, :description, :hqmf_id, :hqmf_set_id, :hqmf_version_number])
|
147
|
+
|
148
|
+
json[:data_criteria] = {}
|
149
|
+
@data_criteria.each do |criteria|
|
150
|
+
criteria_json = criteria.to_json
|
151
|
+
# check if the key already exists... if it does redefine the key
|
152
|
+
if (json[:data_criteria][criteria_json.keys.first])
|
153
|
+
criteria_json = {"#{criteria_json.keys.first}_#{HQMF::Counter.instance.next}" => criteria_json.values.first}
|
154
|
+
end
|
155
|
+
json[:data_criteria].merge! criteria_json
|
156
|
+
end
|
157
|
+
|
158
|
+
# TODO: Investigate why we never use json[:attributes] in the model
|
159
|
+
json[:metadata] = {}
|
160
|
+
json[:attributes] = {}
|
161
|
+
@attributes.each do |attribute|
|
162
|
+
if (attribute.id)
|
163
|
+
json[:attributes].merge! attribute.to_json
|
164
|
+
else
|
165
|
+
json[:metadata].merge! attribute.to_json
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
|
170
|
+
json[:logic] = {}
|
171
|
+
counters = {}
|
172
|
+
@population_criteria.each do |population|
|
173
|
+
population_json = population.to_json
|
174
|
+
key = population_json.keys.first
|
175
|
+
if json[:logic][key]
|
176
|
+
counters[key] ||= 0
|
177
|
+
counters[key] += 1
|
178
|
+
population_json["#{key}_#{counters[key]}"] = population_json[key]
|
179
|
+
population_json.delete(key)
|
180
|
+
end
|
181
|
+
json[:logic].merge! population_json
|
182
|
+
end
|
183
|
+
|
184
|
+
clean_json_recursive(json)
|
185
|
+
json
|
186
|
+
end
|
187
|
+
|
188
|
+
private
|
189
|
+
|
190
|
+
def find(collection, attribute, value)
|
191
|
+
collection.find {|e| e.send(attribute)==value}
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
end
|