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,45 @@
|
|
1
|
+
module HQMF2
|
2
|
+
# Module containing parser helper functions
|
3
|
+
module Utilities
|
4
|
+
include HQMF::Conversion::Utilities
|
5
|
+
|
6
|
+
# Utility function to handle optional attributes
|
7
|
+
# @param xpath an XPath that identifies an XML attribute
|
8
|
+
# @return the value of the attribute or nil if the attribute is missing
|
9
|
+
def attr_val(xpath)
|
10
|
+
Utilities.attr_val(@entry, xpath)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Utility function to handle optional attributes
|
14
|
+
# @param xpath an XPath that identifies an XML attribute
|
15
|
+
# @return the value of the attribute or nil if the attribute is missing
|
16
|
+
def self.attr_val(node, xpath)
|
17
|
+
attr = node.at_xpath(xpath, HQMF2::Document::NAMESPACES)
|
18
|
+
return attr.value if attr
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_xml
|
22
|
+
@entry.to_xml
|
23
|
+
end
|
24
|
+
|
25
|
+
# General helper for stripping '-' and ',' into '_' for processable ids
|
26
|
+
def strip_tokens(value)
|
27
|
+
return nil if value.nil?
|
28
|
+
stripped = value.gsub(/[^0-9a-z]/i, '_')
|
29
|
+
# Prefix digits with 'prefix_' to prevent JS syntax errors
|
30
|
+
stripped.gsub(/^[0-9]/, "prefix_#{value[0]}")
|
31
|
+
end
|
32
|
+
|
33
|
+
# Class that generates incremental ids
|
34
|
+
class IdGenerator
|
35
|
+
def initialize
|
36
|
+
@current_id = 0
|
37
|
+
end
|
38
|
+
|
39
|
+
def next_id
|
40
|
+
@current_id += 1
|
41
|
+
@current_id
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module HQMF2
|
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.3.7' => { valueset_path: './*/cda:value', result_path: nil },
|
7
|
+
'2.16.840.1.113883.10.20.28.3.8' => { valueset_path: './*/cda:code', result_path: nil },
|
8
|
+
'2.16.840.1.113883.10.20.28.3.9' => { valueset_path: './*/cda:code', result_path: nil },
|
9
|
+
'2.16.840.1.113883.10.20.28.3.10' => { valueset_path: './*/cda:code', result_path: nil },
|
10
|
+
'2.16.840.1.113883.10.20.28.3.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.3.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.3.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.3.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.3.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.3.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.3.1' => { valueset_path: './*/cda:value', result_path: nil },
|
17
|
+
'2.16.840.1.113883.10.20.28.3.17' => { valueset_path: './*/cda:value', result_path: nil },
|
18
|
+
'2.16.840.1.113883.10.20.28.3.18' => { valueset_path: './*/cda:value', result_path: nil },
|
19
|
+
'2.16.840.1.113883.10.20.28.3.19' => { valueset_path: './*/cda:value', result_path: nil },
|
20
|
+
'2.16.840.1.113883.10.20.28.3.20' => { valueset_path: "./*/cda:outboundRelationship[@typeCode='CAUS']/cda:observationCriteria/cda:code", result_path: nil },
|
21
|
+
'2.16.840.1.113883.10.20.28.3.21' => { valueset_path: "./*/cda:outboundRelationship[@typeCode='CAUS']/cda:observationCriteria/cda:code", result_path: nil },
|
22
|
+
'2.16.840.1.113883.10.20.28.3.22' => { valueset_path: './*/cda:code', result_path: nil },
|
23
|
+
'2.16.840.1.113883.10.20.28.3.23' => { valueset_path: './*/cda:code', result_path: './*/cda:value' },
|
24
|
+
'2.16.840.1.113883.10.20.28.3.24' => { valueset_path: './*/cda:code', result_path: nil },
|
25
|
+
'2.16.840.1.113883.10.20.28.3.26' => { valueset_path: './*/cda:code', result_path: nil },
|
26
|
+
'2.16.840.1.113883.10.20.28.3.27' => { valueset_path: './*/cda:code', result_path: nil },
|
27
|
+
'2.16.840.1.113883.10.20.28.3.5' => { valueset_path: './*/cda:code', result_path: nil },
|
28
|
+
'2.16.840.1.113883.10.20.28.3.28' => { valueset_path: './*/cda:code', result_path: nil },
|
29
|
+
'2.16.840.1.113883.10.20.28.3.29' => { valueset_path: './*/cda:code', result_path: nil },
|
30
|
+
'2.16.840.1.113883.10.20.28.3.30' => { valueset_path: './*/cda:code', result_path: './*/cda:value' },
|
31
|
+
'2.16.840.1.113883.10.20.28.3.31' => { valueset_path: './*/cda:code', result_path: nil },
|
32
|
+
'2.16.840.1.113883.10.20.28.3.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.3.34' => { valueset_path: "./*/cda:outboundRelationship[@typeCode='CAUS']/cda:actCriteria/cda:code", result_path: nil },
|
34
|
+
'2.16.840.1.113883.10.20.28.3.35' => { valueset_path: './*/cda:code', result_path: nil },
|
35
|
+
'2.16.840.1.113883.10.20.28.3.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.3.37' => { valueset_path: './*/cda:code', result_path: nil },
|
37
|
+
'2.16.840.1.113883.10.20.28.3.39' => { valueset_path: "./*/cda:outboundRelationship[@typeCode='CAUS']/cda:observationCriteria/cda:code", result_path: nil },
|
38
|
+
'2.16.840.1.113883.10.20.28.3.40' => { valueset_path: "./*/cda:outboundRelationship[@typeCode='CAUS']/cda:observationCriteria/cda:code", result_path: nil },
|
39
|
+
'2.16.840.1.113883.10.20.28.3.41' => { valueset_path: './*/cda:code', result_path: nil },
|
40
|
+
'2.16.840.1.113883.10.20.28.3.42' => { valueset_path: './*/cda:code', result_path: './*/cda:value' },
|
41
|
+
'2.16.840.1.113883.10.20.28.3.43' => { valueset_path: './*/cda:code', result_path: nil },
|
42
|
+
'2.16.840.1.113883.10.20.28.3.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.3.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.3.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.3.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.3.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.3.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.3.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.3.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.3.52' => { valueset_path: './*/cda:value', result_path: nil },
|
51
|
+
'2.16.840.1.113883.10.20.28.3.53' => { valueset_path: './*/cda:code', result_path: './*/cda:value' },
|
52
|
+
'2.16.840.1.113883.10.20.28.3.6' => { valueset_path: './*/cda:value', result_path: nil },
|
53
|
+
'2.16.840.1.113883.10.20.28.3.54' => { valueset_path: nil, result_path: nil },
|
54
|
+
'2.16.840.1.113883.10.20.28.3.56' => { valueset_path: './*/cda:value', result_path: nil },
|
55
|
+
'2.16.840.1.113883.10.20.28.3.57' => { valueset_path: './*/cda:value', result_path: nil },
|
56
|
+
'2.16.840.1.113883.10.20.28.3.58' => { valueset_path: './*/cda:value', result_path: nil },
|
57
|
+
'2.16.840.1.113883.10.20.28.3.59' => { valueset_path: './*/cda:value', result_path: nil },
|
58
|
+
'2.16.840.1.113883.10.20.28.3.55' => { valueset_path: './*/cda:value', result_path: nil },
|
59
|
+
'2.16.840.1.113883.10.20.28.3.86' => { valueset_path: './*/cda:value', result_path: nil },
|
60
|
+
'2.16.840.1.113883.10.20.28.3.61' => { valueset_path: './*/cda:value', result_path: nil },
|
61
|
+
'2.16.840.1.113883.10.20.28.3.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.3.63' => { valueset_path: './*/cda:value', result_path: nil },
|
63
|
+
'2.16.840.1.113883.10.20.28.3.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.3.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.3.66' => { valueset_path: './*/cda:code', result_path: nil },
|
66
|
+
'2.16.840.1.113883.10.20.28.3.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.3.68' => { valueset_path: './*/cda:code', result_path: nil },
|
68
|
+
'2.16.840.1.113883.10.20.28.3.70' => { valueset_path: './*/cda:value', result_path: nil },
|
69
|
+
'2.16.840.1.113883.10.20.28.3.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.3.87' => { valueset_path: './*/cda:value', result_path: nil },
|
71
|
+
'2.16.840.1.113883.10.20.28.3.72' => { valueset_path: './*/cda:code', result_path: './*/cda:value' },
|
72
|
+
'2.16.840.1.113883.10.20.28.3.93' => { valueset_path: './*/cda:value', result_path: nil },
|
73
|
+
'2.16.840.1.113883.10.20.28.3.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.3.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.3.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.3.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.3.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.3.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.3.79' => { valueset_path: './*/cda:value', result_path: nil },
|
80
|
+
'2.16.840.1.113883.10.20.28.3.80' => { valueset_path: './*/cda:value', result_path: nil },
|
81
|
+
'2.16.840.1.113883.10.20.28.3.81' => { valueset_path: './*/cda:value', result_path: nil },
|
82
|
+
'2.16.840.1.113883.10.20.28.3.82' => { valueset_path: './*/cda:value', result_path: nil },
|
83
|
+
'2.16.840.1.113883.10.20.28.3.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.3.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.3.110' => { valueset_path: './*/cda:value', result_path: nil },
|
86
|
+
'2.16.840.1.113883.10.20.28.3.111' => { valueset_path: './*/cda:value', result_path: nil },
|
87
|
+
'2.16.840.1.113883.10.20.28.3.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.3.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.3.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.3.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.3.116' => { valueset_path: './*/cda:value', result_path: nil },
|
92
|
+
'2.16.840.1.113883.10.20.28.3.117' => { valueset_path: './*/cda:code', result_path: './*/cda:value' },
|
93
|
+
'2.16.840.1.113883.10.20.28.3.118' => { valueset_path: './*/cda:code', result_path: nil },
|
94
|
+
'2.16.840.1.113883.10.20.28.3.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.3.120' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='MANU']/cda:playingEntity[@classCode='MMAT']/cda:code", result_path: nil }
|
96
|
+
|
97
|
+
}
|
98
|
+
# rubocop:enable Metrics/LineLength
|
99
|
+
|
100
|
+
def self.get_mapping_for_template(template)
|
101
|
+
VALUESET_MAP[template]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,257 @@
|
|
1
|
+
module HQMF
|
2
|
+
# Class representing an HQMF document
|
3
|
+
class DataCriteriaConverter
|
4
|
+
|
5
|
+
attr_reader :v1_data_criteria_by_id, :v2_data_criteria, :v2_data_criteria_to_delete, :measure_period_criteria, :measure_period_v1_keys, :specific_occurrences
|
6
|
+
|
7
|
+
def initialize(doc, measure_period)
|
8
|
+
@doc = doc
|
9
|
+
@v1_data_criteria_by_id = {}
|
10
|
+
@v2_data_criteria = []
|
11
|
+
@v2_data_criteria_to_delete = {}
|
12
|
+
@specific_occurrences = {}
|
13
|
+
@measure_period = measure_period
|
14
|
+
parse()
|
15
|
+
end
|
16
|
+
|
17
|
+
def final_v2_data_criteria
|
18
|
+
@v2_data_criteria.delete_if {|criteria| @v2_data_criteria_to_delete[criteria.id] }
|
19
|
+
end
|
20
|
+
|
21
|
+
# duplicates a data criteria. This is important because we may be modifying source data criteria like patient characteristic birthdate to add restrictions
|
22
|
+
# the restrictions added may be different for the numerator, denominator, different IPP_1, IPP_2, etc.
|
23
|
+
def duplicate_data_criteria(data_criteria, parent_id)
|
24
|
+
|
25
|
+
if (data_criteria.is_a? HQMF::Converter::SimpleDataCriteria and data_criteria.precondition_id == parent_id)
|
26
|
+
new_data_criteria = data_criteria
|
27
|
+
else
|
28
|
+
new_data_criteria = HQMF::Converter::SimpleDataCriteria.from_data_criteria(data_criteria)
|
29
|
+
new_data_criteria.assign_precondition(parent_id)
|
30
|
+
@v2_data_criteria << new_data_criteria
|
31
|
+
# we want to delete the original for data criteria that have been duplicated
|
32
|
+
@v2_data_criteria_to_delete[data_criteria.id] = true if !@v2_data_criteria_to_delete.keys.include? data_criteria.id
|
33
|
+
end
|
34
|
+
|
35
|
+
new_data_criteria
|
36
|
+
end
|
37
|
+
|
38
|
+
# make sure that if a data criteria is used as a target, that it is not deleted by someone else.
|
39
|
+
# this is required for birthdate in NQF0106
|
40
|
+
def validate_not_deleted(target)
|
41
|
+
@v2_data_criteria_to_delete[target] = false
|
42
|
+
end
|
43
|
+
|
44
|
+
# grouping data criteria are used to allow a single reference off of a temporal reference or subset operator
|
45
|
+
# grouping data criteria can reference either regular data criteria as children, or other grouping data criteria
|
46
|
+
def create_group_data_criteria(preconditions, type, value, parent_id, id, standard_category, qds_data_type)
|
47
|
+
extract_group_data_criteria_tree(HQMF::DataCriteria::UNION,preconditions, type, parent_id)
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_group_data_criteria(children, section, parent_id, derivation_operator)
|
51
|
+
|
52
|
+
criteria_ids = children.map(&:id)
|
53
|
+
# make sure nobody else is going to delete the criteria we've grouped
|
54
|
+
criteria_ids.each {|target| validate_not_deleted(target)}
|
55
|
+
|
56
|
+
id = "#{parent_id}_#{section}_#{HQMF::Counter.instance.next}"
|
57
|
+
title = "#{id}"
|
58
|
+
description = ""
|
59
|
+
definition = 'derived'
|
60
|
+
_display_name,_code_list_id,_status,_value,_field_values,_effective_time,_inline_code_list,_negation_code_list_id, = nil
|
61
|
+
_negation = false
|
62
|
+
|
63
|
+
group_criteria = HQMF::DataCriteria.new(id, title, _display_name, description, _code_list_id, criteria_ids, derivation_operator, definition, _status,
|
64
|
+
_value, _field_values, _effective_time, _inline_code_list,_negation,_negation_code_list_id,nil,nil,nil,nil)
|
65
|
+
|
66
|
+
@v2_data_criteria << group_criteria
|
67
|
+
|
68
|
+
group_criteria
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
# pull the children data criteria out of a set of preconditions
|
73
|
+
def extract_group_data_criteria_tree(conjunction, preconditions, type, parent_id)
|
74
|
+
|
75
|
+
children = []
|
76
|
+
preconditions.each do |precondition|
|
77
|
+
if (precondition.comparison?)
|
78
|
+
if (precondition.reference.id == HQMF::Document::MEASURE_PERIOD_ID)
|
79
|
+
children << measure_period_criteria
|
80
|
+
else
|
81
|
+
children << v2_data_criteria_by_id[precondition.reference.id]
|
82
|
+
end
|
83
|
+
else
|
84
|
+
converted_conjunction = convert_grouping_conjunction(precondition.conjunction_code)
|
85
|
+
children << extract_group_data_criteria_tree(converted_conjunction, precondition.preconditions, type, parent_id)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# if we have just one child element, just return it. An AND or OR of a single item is not useful.
|
90
|
+
if (children.size > 1)
|
91
|
+
build_group_data_criteria(children, type, parent_id, conjunction)
|
92
|
+
else
|
93
|
+
children.first
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
def convert_grouping_conjunction(conjunction)
|
99
|
+
case conjunction
|
100
|
+
when HQMF::Precondition::AT_LEAST_ONE_TRUE
|
101
|
+
HQMF::DataCriteria::UNION
|
102
|
+
when HQMF::Precondition::ALL_TRUE
|
103
|
+
HQMF::DataCriteria::XPRODUCT
|
104
|
+
else
|
105
|
+
'unknown'
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# pull the children data criteria out of a set of preconditions
|
110
|
+
def self.extract_data_criteria(preconditions, data_criteria_converter)
|
111
|
+
flattened = []
|
112
|
+
preconditions.each do |precondition|
|
113
|
+
if (precondition.comparison?)
|
114
|
+
if (precondition.reference.id == HQMF::Document::MEASURE_PERIOD_ID)
|
115
|
+
flattened << data_criteria_converter.measure_period_criteria
|
116
|
+
else
|
117
|
+
flattened << data_criteria_converter.v2_data_criteria_by_id[precondition.reference.id]
|
118
|
+
end
|
119
|
+
else
|
120
|
+
flattened.concat(extract_data_criteria(precondition.preconditions,data_criteria_converter))
|
121
|
+
end
|
122
|
+
end
|
123
|
+
flattened
|
124
|
+
end
|
125
|
+
|
126
|
+
def v2_data_criteria_by_id
|
127
|
+
criteria_by_id = {}
|
128
|
+
@v2_data_criteria.each do |criteria|
|
129
|
+
criteria_by_id[criteria.id] = criteria
|
130
|
+
end
|
131
|
+
criteria_by_id
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
def parse()
|
137
|
+
@doc[:data_criteria].each do |key,criteria|
|
138
|
+
parsed_criteria = HQMF::DataCriteriaConverter.convert(key, criteria)
|
139
|
+
@v2_data_criteria << parsed_criteria
|
140
|
+
@v1_data_criteria_by_id[criteria[:id]] = parsed_criteria
|
141
|
+
@specific_occurrences[parsed_criteria.id] = criteria[:derived_from] != nil
|
142
|
+
end
|
143
|
+
create_measure_period_v1_data_criteria(@doc,@measure_period,@v1_data_criteria_by_id)
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.convert(key, criteria)
|
147
|
+
|
148
|
+
# @param [String] id
|
149
|
+
# @param [String] title
|
150
|
+
# @param [String] standard_category
|
151
|
+
# @param [String] qds_data_type
|
152
|
+
# @param [String] subset_code
|
153
|
+
# @param [String] code_list_id
|
154
|
+
# @param [String] property
|
155
|
+
# @param [String] type
|
156
|
+
# @param [String] status
|
157
|
+
# @param [boolean] negation
|
158
|
+
# @param [String] negation_code_list_id
|
159
|
+
# @param [Value|Range|Coded] value
|
160
|
+
# @param [Range] effective_time
|
161
|
+
# @param [Hash<String,String>] inline_code_list
|
162
|
+
|
163
|
+
id = convert_key(key)
|
164
|
+
title = criteria[:title]
|
165
|
+
title = title.match(/.*:\s+(.+)/)[1]
|
166
|
+
description = criteria[:description]
|
167
|
+
code_list_id = criteria[:code_list_id]
|
168
|
+
definition = criteria[:definition]
|
169
|
+
status = criteria[:status]
|
170
|
+
negation = criteria[:negation]
|
171
|
+
negation_code_list_id = criteria[:negation_code_list_id]
|
172
|
+
specific_occurrence = criteria[:specific_occurrence]
|
173
|
+
specific_occurrence_const = nil
|
174
|
+
|
175
|
+
# specific occurrences do not properly set the description, so we want to add the definition and status
|
176
|
+
if (specific_occurrence)
|
177
|
+
if status
|
178
|
+
statusText = ", #{status.titleize}"
|
179
|
+
elsif definition == 'laboratory_test'
|
180
|
+
# laboratory_test without a status is actually a Result
|
181
|
+
statusText = ", Result"
|
182
|
+
end
|
183
|
+
description = "#{definition.titleize}#{statusText}: #{description}"
|
184
|
+
specific_occurrence_const = (description.gsub(/\W/,' ').split.collect {|word| word.strip.upcase }).join '_'
|
185
|
+
end
|
186
|
+
|
187
|
+
value = nil # value is filled out by backfill_patient_characteristics for things like gender and by REFR restrictions
|
188
|
+
effective_time = nil # filled out by temporal reference code
|
189
|
+
temporal_references = # filled out by operator code
|
190
|
+
subset_operators = nil # filled out by operator code
|
191
|
+
children_criteria = nil # filled out by operator and temporal reference code
|
192
|
+
derivation_operator = nil # filled out by operator and temporal reference code
|
193
|
+
negation_code_list_id = nil # filled out by RSON restrictions
|
194
|
+
field_values = nil # field values are filled out by SUBJ and REFR restrictions
|
195
|
+
inline_code_list = nil # inline code list is only used in HQMF V2, so we can just pass in nil
|
196
|
+
display_name=nil
|
197
|
+
|
198
|
+
# transfers should be modeled as a field. The code_list_id of the transfer data criteria is cleared and the oid is added to a transfer field
|
199
|
+
# The definition of the data criteria is still transfer, but it is marked as an encounter using the patient api funciton.
|
200
|
+
if ['transfer_to', 'transfer_from'].include? definition
|
201
|
+
field_values ||= {}
|
202
|
+
field_values[definition.upcase] = HQMF::Coded.for_code_list(code_list_id, title)
|
203
|
+
code_list_id = nil
|
204
|
+
end
|
205
|
+
|
206
|
+
HQMF::DataCriteria.new(id, title, display_name, description, code_list_id, children_criteria, derivation_operator, definition, status,
|
207
|
+
value, field_values, effective_time, inline_code_list, negation, negation_code_list_id, temporal_references, subset_operators,specific_occurrence,specific_occurrence_const)
|
208
|
+
|
209
|
+
end
|
210
|
+
|
211
|
+
|
212
|
+
# this method creates V1 data criteria for the measurement period. These data criteria can be
|
213
|
+
# referenced properly within the restrictions
|
214
|
+
def create_measure_period_v1_data_criteria(doc,measure_period,v1_data_criteria_by_id)
|
215
|
+
|
216
|
+
attributes = doc[:attributes]
|
217
|
+
attributes.keys.each {|key| attributes[key.to_s] = attributes[key]}
|
218
|
+
|
219
|
+
measure_period_key = attributes['MEASUREMENT_PERIOD'][:id]
|
220
|
+
measure_start_key = attributes['MEASUREMENT_START_DATE'][:id]
|
221
|
+
measure_end_key = attributes['MEASUREMENT_END_DATE'][:id]
|
222
|
+
|
223
|
+
@measure_period_v1_keys = {measure_start: measure_start_key, measure_end: measure_end_key, measure_period: measure_period_key}
|
224
|
+
|
225
|
+
type = 'variable'
|
226
|
+
code_list_id,negation_code_list_id,property,status,field_values,effective_time,inline_code_list,children_criteria,derivation_operator,temporal_references,subset_operators=nil
|
227
|
+
|
228
|
+
#####
|
229
|
+
##
|
230
|
+
######### SET MEASURE PERIOD
|
231
|
+
##
|
232
|
+
#####
|
233
|
+
|
234
|
+
measure_period_id = HQMF::Document::MEASURE_PERIOD_ID
|
235
|
+
value = measure_period
|
236
|
+
measure_criteria = HQMF::DataCriteria.new(measure_period_id,measure_period_id,nil,measure_period_id,code_list_id,children_criteria,derivation_operator,measure_period_id,status,
|
237
|
+
value,field_values,effective_time,inline_code_list, false, nil, temporal_references,subset_operators,nil,nil)
|
238
|
+
|
239
|
+
# set the measure period data criteria for all measure period keys
|
240
|
+
v1_data_criteria_by_id[measure_period_key] = measure_criteria
|
241
|
+
v1_data_criteria_by_id[measure_start_key] = measure_criteria
|
242
|
+
v1_data_criteria_by_id[measure_end_key] = measure_criteria
|
243
|
+
@measure_period_criteria = measure_criteria
|
244
|
+
|
245
|
+
end
|
246
|
+
|
247
|
+
|
248
|
+
def self.title_from_description(title, description)
|
249
|
+
title.gsub(/^#{Regexp.escape(description).gsub('\\ ',':?,?\\ ')}:\s*/i,'')
|
250
|
+
end
|
251
|
+
|
252
|
+
def self.convert_key(key)
|
253
|
+
key.to_s.downcase.gsub('_', ' ').split(' ').map {|w| w.capitalize }.join('')
|
254
|
+
end
|
255
|
+
|
256
|
+
end
|
257
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module HQMF
|
2
|
+
# Class for converting an HQMF 1.0 representation to an HQMF 2.0 representation
|
3
|
+
class DocumentConverter
|
4
|
+
|
5
|
+
BIRTHTIME_CODE_LIST = {'LOINC'=>['21112-8']}
|
6
|
+
|
7
|
+
def self.convert(json, codes)
|
8
|
+
|
9
|
+
title = json[:title]
|
10
|
+
description = json[:description]
|
11
|
+
|
12
|
+
# TODO: Investigate why we never use json[:attributes]
|
13
|
+
metadata = json[:metadata]
|
14
|
+
metadata.keys.each {|key| metadata[key.to_s] = metadata[key]; metadata.delete(key.to_sym)}
|
15
|
+
id = metadata["NQF_ID_NUMBER"][:value] if metadata["NQF_ID_NUMBER"]
|
16
|
+
emeasure_id = metadata['EMEASURE_IDENTIFIER'][:value] if metadata['EMEASURE_IDENTIFIER']
|
17
|
+
attributes = parse_attributes(metadata)
|
18
|
+
hqmf_id = json[:hqmf_id]
|
19
|
+
hqmf_set_id = json[:hqmf_set_id]
|
20
|
+
hqmf_version_number = json[:hqmf_version_number]
|
21
|
+
cms_id = "CMS#{emeasure_id}v#{hqmf_version_number}"
|
22
|
+
|
23
|
+
measure_period = parse_measure_period(json)
|
24
|
+
@data_criteria_converter = DataCriteriaConverter.new(json, measure_period)
|
25
|
+
|
26
|
+
# source data criteria are the original unmodified v2 data criteria
|
27
|
+
source_data_criteria = []
|
28
|
+
@data_criteria_converter.v2_data_criteria.each {|criteria| source_data_criteria << criteria}
|
29
|
+
|
30
|
+
# PASS 1
|
31
|
+
@population_criteria_converter = PopulationCriteriaConverter.new(json, @data_criteria_converter)
|
32
|
+
population_criteria = @population_criteria_converter.population_criteria
|
33
|
+
|
34
|
+
# PASS 2
|
35
|
+
comparison_converter = HQMF::ComparisonConverter.new(@data_criteria_converter)
|
36
|
+
comparison_converter.convert_comparisons(population_criteria)
|
37
|
+
|
38
|
+
# PASS 3
|
39
|
+
# specific_occurrence_converter = HQMF::SpecificOccurrenceConverter.new(@data_criteria_converter)
|
40
|
+
# specific_occurrence_converter.convert_specific_occurrences(population_criteria)
|
41
|
+
|
42
|
+
data_criteria = @data_criteria_converter.final_v2_data_criteria
|
43
|
+
|
44
|
+
populations = @population_criteria_converter.sub_measures
|
45
|
+
|
46
|
+
doc = HQMF::Document.new(id, hqmf_id, hqmf_set_id, hqmf_version_number, cms_id, title, description, population_criteria, data_criteria, source_data_criteria, attributes, measure_period, populations)
|
47
|
+
|
48
|
+
HQMF::DocumentConverter.validate(doc, codes) if codes
|
49
|
+
|
50
|
+
doc
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def self.parse_attributes(metadata)
|
57
|
+
attributes = []
|
58
|
+
metadata.keys.each do |key|
|
59
|
+
attributes << HQMF::Attribute.from_json(metadata[key])
|
60
|
+
end
|
61
|
+
attributes
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.parse_measure_period(json)
|
65
|
+
|
66
|
+
# Create a new HQMF::EffectiveTime
|
67
|
+
# @param [Value] low
|
68
|
+
# @param [Value] high
|
69
|
+
# @param [Value] width
|
70
|
+
# ----------
|
71
|
+
# Create a new HQMF::Value
|
72
|
+
# @param [String] type
|
73
|
+
# @param [String] unit
|
74
|
+
# @param [String] value
|
75
|
+
# @param [String] inclusive
|
76
|
+
# @param [String] derived
|
77
|
+
# @param [String] expression
|
78
|
+
|
79
|
+
low = HQMF::Value.new('TS',nil, '201201010000',nil, nil, nil)
|
80
|
+
high = HQMF::Value.new('TS',nil,'201212312359',nil, nil, nil)
|
81
|
+
width = HQMF::Value.new('PQ','a','1',nil, nil, nil)
|
82
|
+
|
83
|
+
# puts ('need to figure out a way to make dates dynamic')
|
84
|
+
|
85
|
+
HQMF::EffectiveTime.new(low,high,width)
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.validate(document,codes)
|
89
|
+
puts "\t(#{document.id})document is nil!!!!!!!!!!!" unless document
|
90
|
+
puts "\t(#{document.id})codes are nil!!!!!!!!!!!" unless codes
|
91
|
+
return unless document and codes
|
92
|
+
|
93
|
+
referenced_oids = document.all_data_criteria.map(&:code_list_id).compact.uniq
|
94
|
+
|
95
|
+
referenced_oids.each do |oid|
|
96
|
+
value_set = codes[oid]
|
97
|
+
puts "\tDC (#{document.id},#{document.title}): referenced OID could not be found #{oid}" unless value_set
|
98
|
+
end
|
99
|
+
|
100
|
+
oid_values = document.all_data_criteria.select {|dc| dc.value != nil and dc.value.type == 'CD'}
|
101
|
+
|
102
|
+
if oid_values.size > 0
|
103
|
+
referenced_oids = (oid_values.map {|dc| dc.value.code_list_id }).compact.uniq
|
104
|
+
referenced_oids.each do |oid|
|
105
|
+
value_set = codes[oid]
|
106
|
+
puts "\tVALUE (#{document.id},#{document.title}): referenced OID could not be found #{oid}" unless value_set
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
oid_negation = document.all_data_criteria.select {|dc| dc.negation_code_list_id != nil}
|
112
|
+
if oid_negation.size > 0
|
113
|
+
referenced_oids = (oid_negation.map {|dc| dc.negation_code_list_id}).compact.uniq
|
114
|
+
referenced_oids.each do |oid|
|
115
|
+
value_set = codes[oid]
|
116
|
+
puts "\tNEGATION (#{document.id},#{document.title}): referenced OID could not be found #{oid}" unless value_set
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
oid_fields = document.all_data_criteria.select {|dc| dc.field_values != nil}
|
121
|
+
if oid_fields.size > 0
|
122
|
+
referenced_oids = (oid_fields.map{|dc| dc.field_values.map {|key,field| puts "field: #{key} is nil" unless field || key.match(/DATETIME/); field.code_list_id if field != nil and field.type == 'CD'}}).flatten.compact.uniq
|
123
|
+
referenced_oids.each do |oid|
|
124
|
+
value_set = codes[oid]
|
125
|
+
puts "\tFIELDS (#{document.id},#{document.title}): referenced OID could not be found #{oid}" unless value_set
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|