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,57 @@
|
|
1
|
+
module HQMF2CQL
|
2
|
+
# Represents a data criteria specification
|
3
|
+
class DataCriteria < HQMF2::DataCriteria
|
4
|
+
include HQMF2CQL::DataCriteriaPostProcessing, HQMF2CQL::DataCriteriaTypeAndDefinitionExtraction
|
5
|
+
|
6
|
+
# Get the title of the criteria, provides a human readable description
|
7
|
+
# @return [String] the title of this data criteria
|
8
|
+
def title
|
9
|
+
disp_value = attr_val("#{@code_list_xpath}/cda:displayName/@value")
|
10
|
+
# Attempt to pull display value from the localVariableName for
|
11
|
+
# MAT 5.3+ exports that appear to no longer include displayName for
|
12
|
+
# code entries.
|
13
|
+
# NOTE: A long term replacement for this and for other portions of the
|
14
|
+
# parsing process should involve reaching out to VSAC for oid information
|
15
|
+
# pulled from the CQL, and then to use that information while parsing.
|
16
|
+
unless disp_value.present?
|
17
|
+
# Grab the localVariableName from the XML
|
18
|
+
disp_value = attr_val('./cda:localVariableName/@value')
|
19
|
+
# Grab everything before the first underscore
|
20
|
+
disp_value = disp_value.partition('_').first unless disp_value.nil?
|
21
|
+
end
|
22
|
+
@title || disp_value || @description || id # allow defined titles to take precedence
|
23
|
+
end
|
24
|
+
|
25
|
+
# Generate the title and description used when producing the model
|
26
|
+
def retrieve_title_and_description_for_model
|
27
|
+
# remove * Value Set from title
|
28
|
+
title_match = title.match(/(.*) \w+ [Vv]alue [Ss]et/)
|
29
|
+
@title = title_match[1] if title_match && title_match.length > 1
|
30
|
+
|
31
|
+
@description = "#{@description}: #{title}"
|
32
|
+
end
|
33
|
+
|
34
|
+
# In certain situations it is necessary to have a negated data criterion
|
35
|
+
# copied to a "positive" form.
|
36
|
+
def make_criterion_positive
|
37
|
+
@negation = false
|
38
|
+
|
39
|
+
# Remove negation from description
|
40
|
+
# sometimes "Not Done" used: "Communication: From Provider To Patient, Not Done"
|
41
|
+
# should transform to "Communication: From Provider To Patient"
|
42
|
+
@description.gsub!(', Not Done', '')
|
43
|
+
|
44
|
+
# sometimes just "Not" used: "Encounter, Not Performed"
|
45
|
+
# should transform to "Encounter, Performed"
|
46
|
+
@description.gsub!(', Not', ',')
|
47
|
+
|
48
|
+
@source_data_criteria = 'Derived from ' + @source_data_criteria
|
49
|
+
|
50
|
+
# Looking to remove the word 'Not'. Using lookahead and lookbehind in the regex
|
51
|
+
# criterion.id = criterion.id.gsub(/(?<=[a-z])Not(?=[A-Z])/, '') + '_spoof'
|
52
|
+
@id = @id.gsub(/(?<=[a-z])Not(?=[A-Z])/, '') + '_spoofed'
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module HQMF2CQL
|
2
|
+
# Extracts the type, and modifies the data criteria, based on the template id or definition
|
3
|
+
module DataCriteriaTypeAndDefinitionExtraction
|
4
|
+
extend HQMF2::DataCriteriaTypeAndDefinitionExtraction
|
5
|
+
|
6
|
+
def extract_definition_from_template_or_type
|
7
|
+
# Try to determine what kind of data criteria we are dealing with
|
8
|
+
# First we look for a template id and if we find one just use the definition
|
9
|
+
# status and negation associated with that
|
10
|
+
# If no template id or not one we recognize then try to determine type from
|
11
|
+
# the definition element.
|
12
|
+
extract_definition_from_type unless extract_definition_from_template_id
|
13
|
+
end
|
14
|
+
|
15
|
+
# Given a template id, derive (if available) the definition for the template.
|
16
|
+
# The definitions are stored in hqmf-model/data_criteria.json.
|
17
|
+
def extract_definition_from_template_id
|
18
|
+
found = false
|
19
|
+
|
20
|
+
@template_ids.each do |template_id|
|
21
|
+
# NOTE! (Adam 6/14): The following logic should absolutely be changed
|
22
|
+
# when Bonnie CQL support goes production. The "try this then try
|
23
|
+
# that" approach is an artifact of the template oids changing as of
|
24
|
+
# MAT 5.3; we want to support measures exported using 5.3, but also
|
25
|
+
# measures that were exported using previous versions of the MAT.
|
26
|
+
|
27
|
+
# Try a lookup using the newer template oids.
|
28
|
+
defs = HQMF::DataCriteria.definition_for_template_id(template_id, 'r2cql')
|
29
|
+
|
30
|
+
# If the new template oids didn't work, try a lookup using the older
|
31
|
+
# template oids.
|
32
|
+
defs = HQMF::DataCriteria.definition_for_template_id(template_id, 'r2') unless defs
|
33
|
+
|
34
|
+
if defs
|
35
|
+
@definition = defs['definition']
|
36
|
+
@status = defs['status'].length > 0 ? defs['status'] : nil
|
37
|
+
found ||= true
|
38
|
+
else
|
39
|
+
found ||= handle_known_template_id(template_id)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
found
|
44
|
+
end
|
45
|
+
|
46
|
+
# Extract the definition (sometimes status, sometimes other elements) of the data criteria based on the type
|
47
|
+
def extract_definition_from_type
|
48
|
+
if @entry.at_xpath('./cda:grouperCriteria')
|
49
|
+
@definition ||= 'derived'
|
50
|
+
return
|
51
|
+
end
|
52
|
+
# See if we can find a match for the entry definition value and status.
|
53
|
+
entry_type = attr_val('./*/cda:definition/*/cda:id/@extension')
|
54
|
+
handle_entry_type(entry_type)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Generate the definition and/or status from the entry type in most cases.
|
58
|
+
# If the entry type is nil, and the value is a specific occurrence, more parsing may be necessary.
|
59
|
+
def handle_entry_type(entry_type)
|
60
|
+
# settings is required to trigger exceptions, which set the definition
|
61
|
+
HQMF::DataCriteria.get_settings_for_definition(entry_type, @status)
|
62
|
+
@definition = entry_type
|
63
|
+
rescue
|
64
|
+
# if no exact match then try a string match just using entry definition value
|
65
|
+
case entry_type
|
66
|
+
when 'Medication', 'Medications'
|
67
|
+
@definition = 'medication'
|
68
|
+
@status = 'active' unless @status
|
69
|
+
when 'RX'
|
70
|
+
@definition = 'medication'
|
71
|
+
@status = 'dispensed' unless @status
|
72
|
+
when nil
|
73
|
+
definition_for_nil_entry
|
74
|
+
else
|
75
|
+
@definition = extract_definition_from_entry_type(entry_type)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module HQMF2CQL
|
2
|
+
# Processing on data criteria after the initial extractions have taken place
|
3
|
+
module DataCriteriaPostProcessing
|
4
|
+
extend HQMF2::DataCriteriaPostProcessing
|
5
|
+
|
6
|
+
# Handles settings values after (most) values have been setup
|
7
|
+
def post_processing
|
8
|
+
extract_code_list_path_and_result_value
|
9
|
+
|
10
|
+
# Prefix ids that start with numerical values, and strip tokens from others
|
11
|
+
@id = strip_tokens(@id)
|
12
|
+
@children_criteria.map! { |cc| strip_tokens(cc) }
|
13
|
+
|
14
|
+
# append "_source" to the criteria since all the source criteria are separated from the non-source with the "_source" identifier
|
15
|
+
# "_source" is added to the SDC ids so that we are not duplicating ids between source and non source data criteria lists
|
16
|
+
# the derived source data criteria maintain their original ids since they are duplicated in the data criteria and source data criteria lists from the simple xml
|
17
|
+
@source_data_criteria = "#{@id}_source" unless (@definition == 'derived' || @definition == 'satisfies_all' || @definition == 'satisfies_any')
|
18
|
+
@source_data_criteria = strip_tokens(@source_data_criteria) unless @source_data_criteria.nil?
|
19
|
+
end
|
20
|
+
|
21
|
+
# Extract the code_list_xpath and the criteria's value from any of the
|
22
|
+
# template ids (if multiple exist)
|
23
|
+
def extract_code_list_path_and_result_value
|
24
|
+
@template_ids.each do |t|
|
25
|
+
# NOTE! (Adam 6/14): The following logic should absolutely be changed
|
26
|
+
# when Bonnie CQL support goes production. The "try this then try
|
27
|
+
# that" approach is an artifact of the template oids changing as of
|
28
|
+
# MAT 5.3; we want to support measures exported using 5.3, but also
|
29
|
+
# measures that were exported using previous versions of the MAT.
|
30
|
+
|
31
|
+
# Try a lookup using the newer template oids.
|
32
|
+
mapping = HQMF2CQL::ValueSetHelper.get_mapping_for_template(t)
|
33
|
+
|
34
|
+
# If the new template oids didn't work, try a lookup using the older
|
35
|
+
# template oids.
|
36
|
+
mapping = HQMF2::ValueSetHelper.get_mapping_for_template(t)unless mapping
|
37
|
+
|
38
|
+
handle_mapping_template(mapping)
|
39
|
+
break if mapping # Quit if one template id with a mapping has set these values
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module HQMF2CQL
|
2
|
+
|
3
|
+
# Class representing a HQMF v2 document that uses CQL for measure logic.
|
4
|
+
class Document < HQMF2::Document
|
5
|
+
|
6
|
+
# Create a new HQMF2CQL::Document instance by parsing the given HQMF contents.
|
7
|
+
def initialize(hqmf_contents, use_default_measure_period = true)
|
8
|
+
# Set up basic measure values
|
9
|
+
setup_default_values(hqmf_contents, use_default_measure_period)
|
10
|
+
|
11
|
+
# Extract data criteria
|
12
|
+
extract_criteria
|
13
|
+
|
14
|
+
# Extract the population criteria and population collections
|
15
|
+
pop_helper = HQMF2CQL::DocumentPopulationHelper.new(@entry, @doc, self, @id_generator, @reference_ids)
|
16
|
+
# @populations_cql_map and @observations are needed by the frontend
|
17
|
+
@populations, @population_criteria, @populations_cql_map, @observations = pop_helper.extract_populations
|
18
|
+
@cql_measure_library = pop_helper.extract_main_library
|
19
|
+
end
|
20
|
+
|
21
|
+
# Generates this classes hqmf-model equivalent.
|
22
|
+
def to_model
|
23
|
+
dcs = all_data_criteria.compact.collect(&:to_model)
|
24
|
+
sdc = source_data_criteria.compact.collect(&:to_model)
|
25
|
+
pcs = all_population_criteria.compact.collect(&:to_model)
|
26
|
+
HQMF::Document.new(@id, @id, @hqmf_set_id, @hqmf_version_number, @cms_id,
|
27
|
+
title, description, pcs, dcs, sdc,
|
28
|
+
@attributes, @measure_period, @populations,
|
29
|
+
populations_cql_map=@populations_cql_map, cql_measure_library=@cql_measure_library, observations=@observations)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Extracts data criteria from the HQMF document.
|
33
|
+
def extract_criteria
|
34
|
+
# Grab each data criteria entry from the HQMF
|
35
|
+
extracted_data_criteria = []
|
36
|
+
@doc.xpath('cda:QualityMeasureDocument/cda:component/cda:dataCriteriaSection/cda:entry', NAMESPACES).each do |entry|
|
37
|
+
extracted_data_criteria << entry
|
38
|
+
dc = HQMF2CQL::DataCriteria.new(entry) # Create new data criteria
|
39
|
+
sdc = dc.clone # Clone data criteria
|
40
|
+
sdc.id += '_source' # Make it a source
|
41
|
+
|
42
|
+
@data_criteria << dc
|
43
|
+
@source_data_criteria << sdc
|
44
|
+
end
|
45
|
+
make_positive_entry
|
46
|
+
end
|
47
|
+
|
48
|
+
# This method is needed for situations when there is a only
|
49
|
+
# a negated version of a data criteria. Bonnie will only
|
50
|
+
# show the affirmative version of data criteria. This method
|
51
|
+
# will create an affirmative version of a data criteria when there
|
52
|
+
# is only the negative one in the HQMF.
|
53
|
+
def make_positive_entry
|
54
|
+
negated_criteria = []
|
55
|
+
data_criteria_index_lookup = []
|
56
|
+
# Find the criteria that are negated
|
57
|
+
# At the same time build a hash of all criteria and their code_list_id, definition, status, and negation status
|
58
|
+
@data_criteria.each_with_index do |criterion, source_index|
|
59
|
+
negated_criteria << criterion if criterion.negation
|
60
|
+
data_criteria_index_lookup << [criterion.code_list_id, criterion.definition, criterion.status, criterion.negation]
|
61
|
+
end
|
62
|
+
|
63
|
+
negated_criteria.each do |criterion|
|
64
|
+
# Check if there is a criterion with the same OID, definition and status BUT that isn't negated
|
65
|
+
unless data_criteria_index_lookup.include?([criterion.code_list_id, criterion.definition, criterion.status, false])
|
66
|
+
spoofed_positive_instance = criterion.clone
|
67
|
+
spoofed_positive_instance.make_criterion_positive
|
68
|
+
@data_criteria << spoofed_positive_instance
|
69
|
+
sdc = spoofed_positive_instance.clone
|
70
|
+
sdc.id += '_source'
|
71
|
+
@source_data_criteria << sdc
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module HQMF2CQL
|
2
|
+
# Handles generation of populations for the main document
|
3
|
+
class DocumentPopulationHelper < HQMF2::DocumentPopulationHelper
|
4
|
+
include HQMF2::Utilities
|
5
|
+
|
6
|
+
def extract_populations
|
7
|
+
@populations_cql_map = extract_populations_cql_map
|
8
|
+
extract_populations_and_criteria
|
9
|
+
# Return via destructuring
|
10
|
+
[@populations, @population_criteria, @populations_cql_map, @observations]
|
11
|
+
end
|
12
|
+
|
13
|
+
# Extracts potential measure observations from the CQL based HQMF.
|
14
|
+
# This function needs to return a boolean so that it will continue to work with
|
15
|
+
# HQMF2::DocumentPopulationHelper::extract_populations_and_criteria
|
16
|
+
# This function is being overridden because in CQL the observations are no longer data criteria in the HQMF.
|
17
|
+
def extract_observations
|
18
|
+
@observations = []
|
19
|
+
|
20
|
+
# Look for observations in the measureObservationSection of the CQL based HQMF document, and if they exist extract the name of the CQL statement that calculates the observation. This is the name of the "define function" statement in the CQL.
|
21
|
+
# In addition to the function name we also need to retreive the parameter for the function.
|
22
|
+
observation_section = @doc.xpath('/cda:QualityMeasureDocument/cda:component/cda:measureObservationSection',
|
23
|
+
HQMF2::Document::NAMESPACES)
|
24
|
+
unless observation_section.empty?
|
25
|
+
observation_section.each do |entry|
|
26
|
+
# Need to add population criteria for observations
|
27
|
+
criteria_id = 'OBSERV'
|
28
|
+
criteria = HQMF2::PopulationCriteria.new(entry.xpath('cda:definition'), @document, @id_generator)
|
29
|
+
criteria.type = 'OBSERV'
|
30
|
+
if @ids_by_hqmf_id["#{criteria.hqmf_id}"]
|
31
|
+
criteria.create_human_readable_id(@ids_by_hqmf_id[criteria.hqmf_id])
|
32
|
+
else
|
33
|
+
criteria.create_human_readable_id(population_id_with_counter(criteria_id))
|
34
|
+
@ids_by_hqmf_id["#{criteria.hqmf_id}"] = criteria.id
|
35
|
+
end
|
36
|
+
@population_criteria << criteria
|
37
|
+
|
38
|
+
# Extract CQL function specific details
|
39
|
+
cql_define_function = {}
|
40
|
+
# The at_xpath(...).values returns an array of a single element.
|
41
|
+
# The match returns an array and since we don't want the double quotes we take the second element
|
42
|
+
cql_define_function[:function_name] = entry.at_xpath("*/cda:measureObservationDefinition/cda:value/cda:expression").values.first.match('\\"([A-Za-z0-9 ]+)\\"')[1]
|
43
|
+
cql_define_function[:parameter] = entry.at_xpath("*/cda:measureObservationDefinition/cda:component/cda:criteriaReference/cda:id").attributes['extension'].value.match('\\"([A-Za-z0-9 ]+)\\"')[1]
|
44
|
+
@observations << cql_define_function
|
45
|
+
end
|
46
|
+
end
|
47
|
+
!@observations.empty?
|
48
|
+
end
|
49
|
+
|
50
|
+
# Generate the stratifications of populations, if any exist
|
51
|
+
# for CQL, adds 'population_index' and 'stratification_index'
|
52
|
+
def handle_stratifications(population_def, number_of_populations, population, id_def, population_index)
|
53
|
+
# handle stratifications (EP137, EP155)
|
54
|
+
stratifier_criteria_xpath = "cda:component/cda:stratifierCriteria[not(cda:component/cda:measureAttribute/cda:code[@code = 'SDE'])]/.."
|
55
|
+
population_def.xpath(stratifier_criteria_xpath, HQMF2::Document::NAMESPACES)
|
56
|
+
.each_with_index do |criteria_def, criteria_def_index|
|
57
|
+
# Skip this Stratification if any precondition doesn't contain any preconditions
|
58
|
+
next unless HQMF2::PopulationCriteria.new(criteria_def, @document, @id_generator)
|
59
|
+
.preconditions.all? { |prcn| prcn.preconditions.length > 0 }
|
60
|
+
index = number_of_populations + ((population_index - 1) * criteria_def.xpath('./*/cda:precondition').length) +
|
61
|
+
criteria_def_index
|
62
|
+
criteria_id = HQMF::PopulationCriteria::STRAT
|
63
|
+
stratified_population = population.dup
|
64
|
+
stratified_population['stratification'] = criteria_def.at_xpath('./*/cda:id/@root').try(:value) ||
|
65
|
+
"#{criteria_id}-#{criteria_def_index}"
|
66
|
+
build_population_criteria(criteria_def, criteria_id, stratified_population)
|
67
|
+
|
68
|
+
stratified_population['id'] = id_def ? "#{id_def.value} - Stratification #{criteria_def_index + 1}" : "Population#{index}"
|
69
|
+
title_def = population_def.at_xpath('cda:title/@value', HQMF2::Document::NAMESPACES)
|
70
|
+
stratified_population['title'] = title_def ? "#{title_def.value} - Stratification #{criteria_def_index + 1}" : "Population #{index}"
|
71
|
+
stratified_population['population_index'] = population_index
|
72
|
+
stratified_population['stratification_index'] = criteria_def_index
|
73
|
+
@stratifications << stratified_population
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Extracts the mappings between actual HQMF populations and their
|
78
|
+
# corresponding CQL define statements.
|
79
|
+
def extract_populations_cql_map
|
80
|
+
populations_cql_map = {}
|
81
|
+
@doc.xpath("//cda:populationCriteriaSection/cda:component[@typeCode='COMP']", HQMF2::Document::NAMESPACES).each do |population_def|
|
82
|
+
{
|
83
|
+
HQMF::PopulationCriteria::IPP => 'initialPopulationCriteria',
|
84
|
+
HQMF::PopulationCriteria::DENOM => 'denominatorCriteria',
|
85
|
+
HQMF::PopulationCriteria::NUMER => 'numeratorCriteria',
|
86
|
+
HQMF::PopulationCriteria::NUMEX => 'numeratorExclusionCriteria',
|
87
|
+
HQMF::PopulationCriteria::DENEXCEP => 'denominatorExceptionCriteria',
|
88
|
+
HQMF::PopulationCriteria::DENEX => 'denominatorExclusionCriteria',
|
89
|
+
HQMF::PopulationCriteria::MSRPOPL => 'measurePopulationCriteria',
|
90
|
+
HQMF::PopulationCriteria::MSRPOPLEX => 'measurePopulationExclusionCriteria',
|
91
|
+
HQMF::PopulationCriteria::STRAT => 'stratifierCriteria'
|
92
|
+
}.each_pair do |criteria_id, criteria_element_name|
|
93
|
+
criteria_def = population_def.at_xpath("cda:#{criteria_element_name}", HQMF2::Document::NAMESPACES)
|
94
|
+
if criteria_def
|
95
|
+
# Ignore Supplemental Data Elements
|
96
|
+
next if HQMF::PopulationCriteria::STRAT == criteria_id &&
|
97
|
+
!criteria_def.xpath("cda:component[@typeCode='COMP']/cda:measureAttribute/cda:code[@code='SDE']").empty?
|
98
|
+
cql_statement = criteria_def.at_xpath("*/*/cda:id", HQMF2::Document::NAMESPACES).attribute('extension').to_s.match(/"([^"]*)"/)
|
99
|
+
if populations_cql_map[criteria_id].nil?
|
100
|
+
populations_cql_map[criteria_id] = []
|
101
|
+
end
|
102
|
+
cql_statement = cql_statement.to_s.delete('\\"')
|
103
|
+
populations_cql_map[criteria_id].push cql_statement
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
populations_cql_map
|
108
|
+
end
|
109
|
+
|
110
|
+
# Extracts the name of the main cql library from the Population Criteria Section.
|
111
|
+
def extract_main_library
|
112
|
+
population_criteria_sections = @doc.xpath("//cda:populationCriteriaSection/cda:component[@typeCode='COMP']", HQMF2::Document::NAMESPACES)
|
113
|
+
criteria_section = population_criteria_sections.at_xpath("cda:initialPopulationCriteria", HQMF2::Document::NAMESPACES)
|
114
|
+
if criteria_section
|
115
|
+
# Example: the full name for the population criteria section is "BonnieNesting01.\"Initial Population\""
|
116
|
+
# The regex returns everything before the "." (BonnieNesting01), which is the file name of the cql measure
|
117
|
+
cql_main_library_name = criteria_section.at_xpath("*/*/cda:id", HQMF2::Document::NAMESPACES).attribute('extension').to_s.match(/[^.]*/).to_s
|
118
|
+
else
|
119
|
+
nil
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module HQMF2CQL
|
2
|
+
# Class containing value set information
|
3
|
+
class ValueSetHelper
|
4
|
+
# rubocop:disable Metrics/LineLength
|
5
|
+
VALUESET_MAP = {
|
6
|
+
'2.16.840.1.113883.10.20.28.4.7' => { valueset_path: './*/cda:value', result_path: nil },
|
7
|
+
'2.16.840.1.113883.10.20.28.4.8' => { valueset_path: './*/cda:code', result_path: nil },
|
8
|
+
'2.16.840.1.113883.10.20.28.4.9' => { valueset_path: './*/cda:code', result_path: nil },
|
9
|
+
'2.16.840.1.113883.10.20.28.4.10' => { valueset_path: './*/cda:code', result_path: nil },
|
10
|
+
'2.16.840.1.113883.10.20.28.4.11' => { valueset_path: "./*/cda:participation[@typeCode='PRD']/cda:role[@classCode='MANU']/cda:playingDevice[@classCode='DEV']/cda:code", result_path: nil },
|
11
|
+
'2.16.840.1.113883.10.20.28.4.12' => { valueset_path: "./*/cda:participation[@typeCode='PRD']/cda:role[@classCode='MANU']/cda:playingDevice[@classCode='DEV']/cda:code", result_path: nil },
|
12
|
+
'2.16.840.1.113883.10.20.28.4.13' => { valueset_path: "./*/cda:participation[@typeCode='DEV']/cda:role[@classCode='MANU']/cda:playingDevice[@classCode='DEV']/cda:code", result_path: nil },
|
13
|
+
'2.16.840.1.113883.10.20.28.4.14' => { valueset_path: "./*/cda:participation[@typeCode='PRD']/cda:role[@classCode='MANU']/cda:playingDevice[@classCode='DEV']/cda:code", result_path: nil },
|
14
|
+
'2.16.840.1.113883.10.20.28.4.15' => { valueset_path: "./*/cda:participation[@typeCode='DEV']/cda:role[@classCode='MANU']/cda:playingDevice[@classCode='DEV']/cda:code", result_path: nil },
|
15
|
+
'2.16.840.1.113883.10.20.28.4.16' => { valueset_path: "./*/cda:participation[@typeCode='DEV']/cda:role[@classCode='MANU']/cda:playingDevice[@classCode='DEV']/cda:code", result_path: nil },
|
16
|
+
'2.16.840.1.113883.10.20.28.4.1' => { valueset_path: './*/cda:value', result_path: nil },
|
17
|
+
'2.16.840.1.113883.10.20.28.4.17' => { valueset_path: './*/cda:value', result_path: nil },
|
18
|
+
'2.16.840.1.113883.10.20.28.4.18' => { valueset_path: './*/cda:value', result_path: nil },
|
19
|
+
'2.16.840.1.113883.10.20.28.4.19' => { valueset_path: './*/cda:value', result_path: nil },
|
20
|
+
'2.16.840.1.113883.10.20.28.4.20' => { valueset_path: "./*/cda:outboundRelationship[@typeCode='CAUS']/cda:observationCriteria/cda:code", result_path: nil },
|
21
|
+
'2.16.840.1.113883.10.20.28.4.21' => { valueset_path: "./*/cda:outboundRelationship[@typeCode='CAUS']/cda:observationCriteria/cda:code", result_path: nil },
|
22
|
+
'2.16.840.1.113883.10.20.28.4.22' => { valueset_path: './*/cda:code', result_path: nil },
|
23
|
+
'2.16.840.1.113883.10.20.28.4.23' => { valueset_path: './*/cda:code', result_path: './*/cda:value' },
|
24
|
+
'2.16.840.1.113883.10.20.28.4.24' => { valueset_path: './*/cda:code', result_path: nil },
|
25
|
+
'2.16.840.1.113883.10.20.28.4.26' => { valueset_path: './*/cda:code', result_path: nil },
|
26
|
+
'2.16.840.1.113883.10.20.28.4.27' => { valueset_path: './*/cda:code', result_path: nil },
|
27
|
+
'2.16.840.1.113883.10.20.28.4.5' => { valueset_path: './*/cda:code', result_path: nil },
|
28
|
+
'2.16.840.1.113883.10.20.28.4.28' => { valueset_path: './*/cda:code', result_path: nil },
|
29
|
+
'2.16.840.1.113883.10.20.28.4.29' => { valueset_path: './*/cda:code', result_path: nil },
|
30
|
+
'2.16.840.1.113883.10.20.28.4.30' => { valueset_path: './*/cda:code', result_path: './*/cda:value' },
|
31
|
+
'2.16.840.1.113883.10.20.28.4.31' => { valueset_path: './*/cda:code', result_path: nil },
|
32
|
+
'2.16.840.1.113883.10.20.28.4.33' => { valueset_path: "./*/cda:outboundRelationship[@typeCode='CAUS' and @inversionInd='true']/cda:procedureCriteria/cda:code", result_path: nil },
|
33
|
+
'2.16.840.1.113883.10.20.28.4.34' => { valueset_path: "./*/cda:outboundRelationship[@typeCode='CAUS']/cda:actCriteria/cda:code", result_path: nil },
|
34
|
+
'2.16.840.1.113883.10.20.28.4.35' => { valueset_path: './*/cda:code', result_path: nil },
|
35
|
+
'2.16.840.1.113883.10.20.28.4.36' => { valueset_path: './*/cda:code', result_path: "./*/cda:outboundRelationship[@typeCode='REFR']//cda:code[@code='394617004']/../cda:value" },
|
36
|
+
'2.16.840.1.113883.10.20.28.4.37' => { valueset_path: './*/cda:code', result_path: nil },
|
37
|
+
'2.16.840.1.113883.10.20.28.4.39' => { valueset_path: "./*/cda:outboundRelationship[@typeCode='CAUS']/cda:observationCriteria/cda:code", result_path: nil },
|
38
|
+
'2.16.840.1.113883.10.20.28.4.40' => { valueset_path: "./*/cda:outboundRelationship[@typeCode='CAUS']/cda:observationCriteria/cda:code", result_path: nil },
|
39
|
+
'2.16.840.1.113883.10.20.28.4.41' => { valueset_path: './*/cda:code', result_path: nil },
|
40
|
+
'2.16.840.1.113883.10.20.28.4.42' => { valueset_path: './*/cda:code', result_path: './*/cda:value' },
|
41
|
+
'2.16.840.1.113883.10.20.28.4.43' => { valueset_path: './*/cda:code', result_path: nil },
|
42
|
+
'2.16.840.1.113883.10.20.28.4.44' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role/cda:playingMaterial[@classCode='MMAT']/cda:code", result_path: nil },
|
43
|
+
'2.16.840.1.113883.10.20.28.4.45' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='MANU']/cda:playingManufacturedMaterial[@classCode='MMAT']/cda:code", result_path: nil },
|
44
|
+
'2.16.840.1.113883.10.20.28.4.46' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='MANU']/cda:playingEntity[@classCode='MMAT']/cda:code", result_path: nil },
|
45
|
+
'2.16.840.1.113883.10.20.28.4.47' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='MANU']/cda:playingEntity[@classCode='MMAT']/cda:code", result_path: nil },
|
46
|
+
'2.16.840.1.113883.10.20.28.4.48' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='MANU']/cda:playingManufacturedMaterial[@classCode='MMAT']/cda:code", result_path: nil },
|
47
|
+
'2.16.840.1.113883.10.20.28.4.49' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='MANU']/cda:playingMaterial[@classCode='MMAT']/cda:code", result_path: nil },
|
48
|
+
'2.16.840.1.113883.10.20.28.4.50' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='MANU']/cda:playingMaterial[@classCode='MMAT']/cda:code", result_path: nil },
|
49
|
+
'2.16.840.1.113883.10.20.28.4.51' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='MANU']/cda:playingMaterial[@classCode='MMAT']/cda:code", result_path: nil },
|
50
|
+
'2.16.840.1.113883.10.20.28.4.52' => { valueset_path: './*/cda:value', result_path: nil },
|
51
|
+
'2.16.840.1.113883.10.20.28.4.53' => { valueset_path: './*/cda:code', result_path: './*/cda:value' },
|
52
|
+
'2.16.840.1.113883.10.20.28.4.6' => { valueset_path: './*/cda:value', result_path: nil },
|
53
|
+
'2.16.840.1.113883.10.20.28.4.54' => { valueset_path: nil, result_path: nil },
|
54
|
+
'2.16.840.1.113883.10.20.28.4.56' => { valueset_path: './*/cda:value', result_path: nil },
|
55
|
+
'2.16.840.1.113883.10.20.28.4.57' => { valueset_path: './*/cda:value', result_path: nil },
|
56
|
+
'2.16.840.1.113883.10.20.28.4.58' => { valueset_path: './*/cda:value', result_path: nil },
|
57
|
+
'2.16.840.1.113883.10.20.28.4.59' => { valueset_path: './*/cda:value', result_path: nil },
|
58
|
+
'2.16.840.1.113883.10.20.28.4.55' => { valueset_path: './*/cda:value', result_path: nil },
|
59
|
+
'2.16.840.1.113883.10.20.28.4.86' => { valueset_path: './*/cda:value', result_path: nil },
|
60
|
+
'2.16.840.1.113883.10.20.28.4.61' => { valueset_path: './*/cda:value', result_path: nil },
|
61
|
+
'2.16.840.1.113883.10.20.28.4.62' => { valueset_path: './*/cda:value', result_path: "./*/cda:outboundRelationship[@typeCode='REFR']//cda:code[@code='394617004']/../cda:value" },
|
62
|
+
'2.16.840.1.113883.10.20.28.4.63' => { valueset_path: './*/cda:value', result_path: nil },
|
63
|
+
'2.16.840.1.113883.10.20.28.4.64' => { valueset_path: "./*/cda:outboundRelationship[@typeCode='CAUS' and @inversionInd='true']/cda:procedureCriteria/cda:code", result_path: nil },
|
64
|
+
'2.16.840.1.113883.10.20.28.4.65' => { valueset_path: "./*/cda:outboundRelationship[@typeCode='CAUS' and @inversionInd='true']/cda:procedureCriteria/cda:code", result_path: nil },
|
65
|
+
'2.16.840.1.113883.10.20.28.4.66' => { valueset_path: './*/cda:code', result_path: nil },
|
66
|
+
'2.16.840.1.113883.10.20.28.4.67' => { valueset_path: './*/cda:code', result_path: "./*/cda:outboundRelationship[@typeCode='REFR']//cda:code[@code='394617004']/../cda:value" },
|
67
|
+
'2.16.840.1.113883.10.20.28.4.68' => { valueset_path: './*/cda:code', result_path: nil },
|
68
|
+
'2.16.840.1.113883.10.20.28.4.70' => { valueset_path: './*/cda:value', result_path: nil },
|
69
|
+
'2.16.840.1.113883.10.20.28.4.71' => { valueset_path: "./*/cda:participation/cda:role[@classCode='ASSIGNED']/cda:playingDevice[@classCode='DEV' and @determinerCode='KIND']/cda:code", result_path: nil },
|
70
|
+
'2.16.840.1.113883.10.20.28.4.87' => { valueset_path: './*/cda:value', result_path: nil },
|
71
|
+
'2.16.840.1.113883.10.20.28.4.72' => { valueset_path: './*/cda:code', result_path: './*/cda:value' },
|
72
|
+
'2.16.840.1.113883.10.20.28.4.93' => { valueset_path: './*/cda:value', result_path: nil },
|
73
|
+
'2.16.840.1.113883.10.20.28.4.73' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='ADMM']/cda:playingMaterial[@classCode='MAT' and @determinerCode='KIND']/cda:code", result_path: nil },
|
74
|
+
'2.16.840.1.113883.10.20.28.4.74' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='ADMM']/cda:playingMaterial[@classCode='MAT' and @determinerCode='KIND']/cda:code", result_path: nil },
|
75
|
+
'2.16.840.1.113883.10.20.28.4.75' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='ADMM']/cda:playingMaterial[@classCode='MAT' and @determinerCode='KIND']/cda:code", result_path: nil },
|
76
|
+
'2.16.840.1.113883.10.20.28.4.76' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='ADMM']/cda:playingMaterial[@classCode='MAT' and @determinerCode='KIND']/cda:code", result_path: nil },
|
77
|
+
'2.16.840.1.113883.10.20.28.4.77' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='ADMM']/cda:playingMaterial[@classCode='MAT' and @determinerCode='KIND']/cda:code", result_path: nil },
|
78
|
+
'2.16.840.1.113883.10.20.28.4.78' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='MANU']/cda:playingMaterial[@classCode='MMAT' and @determinerCode='KIND']/cda:code", result_path: nil },
|
79
|
+
'2.16.840.1.113883.10.20.28.4.79' => { valueset_path: './*/cda:value', result_path: nil },
|
80
|
+
'2.16.840.1.113883.10.20.28.4.80' => { valueset_path: './*/cda:value', result_path: nil },
|
81
|
+
'2.16.840.1.113883.10.20.28.4.81' => { valueset_path: './*/cda:value', result_path: nil },
|
82
|
+
'2.16.840.1.113883.10.20.28.4.82' => { valueset_path: './*/cda:value', result_path: nil },
|
83
|
+
'2.16.840.1.113883.10.20.28.4.84' => { valueset_path: "./*/cda:participation[@typeCode='ORG']/cda:role[@classCode='LOCE']/cda:code", result_path: nil },
|
84
|
+
'2.16.840.1.113883.10.20.28.4.85' => { valueset_path: "./*/cda:participation[@typeCode='ORG']/cda:role[@classCode='LOCE']/cda:code", result_path: nil },
|
85
|
+
'2.16.840.1.113883.10.20.28.4.110' => { valueset_path: './*/cda:value', result_path: nil },
|
86
|
+
'2.16.840.1.113883.10.20.28.4.111' => { valueset_path: './*/cda:value', result_path: nil },
|
87
|
+
'2.16.840.1.113883.10.20.28.4.112' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role/cda:playingManufacturedMaterial[@classCode='MMAT']/cda:code", result_path: nil },
|
88
|
+
'2.16.840.1.113883.10.20.28.4.113' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role/cda:playingManufacturedMaterial[@classCode='MMAT']/cda:code", result_path: nil },
|
89
|
+
'2.16.840.1.113883.10.20.28.4.114' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role/cda:playingEntity[@classCode='MMAT']/cda:code", result_path: nil },
|
90
|
+
'2.16.840.1.113883.10.20.28.4.115' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role/cda:playingMaterial[@classCode='MMAT']/cda:code", result_path: nil },
|
91
|
+
'2.16.840.1.113883.10.20.28.4.116' => { valueset_path: './*/cda:value', result_path: nil },
|
92
|
+
'2.16.840.1.113883.10.20.28.4.117' => { valueset_path: './*/cda:code', result_path: './*/cda:value' },
|
93
|
+
'2.16.840.1.113883.10.20.28.4.118' => { valueset_path: './*/cda:code', result_path: nil },
|
94
|
+
'2.16.840.1.113883.10.20.28.4.119' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='MANU']/cda:playingEntity[@classCode='MMAT']/cda:code", result_path: nil },
|
95
|
+
'2.16.840.1.113883.10.20.28.4.120' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='MANU']/cda:playingEntity[@classCode='MMAT']/cda:code", result_path: nil }
|
96
|
+
}
|
97
|
+
# rubocop:enable Metrics/LineLength
|
98
|
+
|
99
|
+
def self.get_mapping_for_template(template)
|
100
|
+
VALUESET_MAP[template]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|