cqm-parsers 0.2.4 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/Gemfile +8 -4
- data/README.md +44 -5
- data/Rakefile +1 -0
- data/lib/ext/data_element.rb +1 -1
- data/lib/hqmf-parser.rb +13 -45
- data/lib/hqmf-parser/2.0/document.rb +1 -1
- data/lib/hqmf-parser/cql/document_helpers/doc_population_helper.rb +6 -1
- data/lib/measure-loader/cql_loader.rb +165 -0
- data/lib/measure-loader/elm_dependency_finder.rb +72 -0
- data/lib/measure-loader/elm_parser.rb +67 -0
- data/lib/measure-loader/exceptions.rb +10 -0
- data/lib/measure-loader/helpers.rb +11 -0
- data/lib/measure-loader/hqmf_measure_loader.rb +170 -0
- data/lib/measure-loader/mat_measure_files.rb +138 -0
- data/lib/measure-loader/source_data_criteria_loader.rb +65 -0
- data/lib/measure-loader/value_set_helpers.rb +68 -0
- data/lib/measure-loader/vsac_value_set_loader.rb +97 -0
- data/lib/util/util.rb +23 -0
- data/lib/util/vsac_api.rb +164 -101
- metadata +47 -129
- data/lib/ext/code.rb +0 -10
- data/lib/qrda-export/catI-r5/_code.mustache +0 -1
- data/lib/qrda-export/catI-r5/_codes.mustache +0 -10
- data/lib/qrda-export/catI-r5/_header.mustache +0 -28
- data/lib/qrda-export/catI-r5/_measure_section.mustache +0 -59
- data/lib/qrda-export/catI-r5/_reporting_period.mustache +0 -23
- data/lib/qrda-export/catI-r5/_values.mustache +0 -10
- data/lib/qrda-export/catI-r5/qrda1_r5.mustache +0 -137
- data/lib/qrda-export/catI-r5/qrda1_r5.rb +0 -125
- data/lib/qrda-export/catI-r5/qrda_header/_author.mustache +0 -24
- data/lib/qrda-export/catI-r5/qrda_header/_custodian.mustache +0 -43
- data/lib/qrda-export/catI-r5/qrda_header/_documentation_of_service_event.mustache +0 -82
- data/lib/qrda-export/catI-r5/qrda_header/_information_recipient.mustache +0 -7
- data/lib/qrda-export/catI-r5/qrda_header/_legal_authenticator.mustache +0 -25
- data/lib/qrda-export/catI-r5/qrda_header/_participant.mustache +0 -7
- data/lib/qrda-export/catI-r5/qrda_header/_record_target.mustache +0 -28
- data/lib/qrda-export/catI-r5/qrda_templates/adverse_event.mustache +0 -28
- data/lib/qrda-export/catI-r5/qrda_templates/allergy_intolerance.mustache +0 -28
- data/lib/qrda-export/catI-r5/qrda_templates/assessment_performed.mustache +0 -25
- data/lib/qrda-export/catI-r5/qrda_templates/communication_from_patient_to_provider.mustache +0 -29
- data/lib/qrda-export/catI-r5/qrda_templates/communication_from_provider_to_patient.mustache +0 -24
- data/lib/qrda-export/catI-r5/qrda_templates/communication_from_provider_to_provider.mustache +0 -31
- data/lib/qrda-export/catI-r5/qrda_templates/device_applied.mustache +0 -32
- data/lib/qrda-export/catI-r5/qrda_templates/device_ordered.mustache +0 -31
- data/lib/qrda-export/catI-r5/qrda_templates/diagnosis.mustache +0 -38
- data/lib/qrda-export/catI-r5/qrda_templates/diagnostic_study_ordered.mustache +0 -19
- data/lib/qrda-export/catI-r5/qrda_templates/diagnostic_study_performed.mustache +0 -32
- data/lib/qrda-export/catI-r5/qrda_templates/encounter_ordered.mustache +0 -24
- data/lib/qrda-export/catI-r5/qrda_templates/encounter_performed.mustache +0 -40
- data/lib/qrda-export/catI-r5/qrda_templates/immunization_administered.mustache +0 -29
- data/lib/qrda-export/catI-r5/qrda_templates/insurance_provider.mustache +0 -11
- data/lib/qrda-export/catI-r5/qrda_templates/intervention_ordered.mustache +0 -18
- data/lib/qrda-export/catI-r5/qrda_templates/intervention_performed.mustache +0 -25
- data/lib/qrda-export/catI-r5/qrda_templates/lab_test_ordered.mustache +0 -18
- data/lib/qrda-export/catI-r5/qrda_templates/lab_test_performed.mustache +0 -22
- data/lib/qrda-export/catI-r5/qrda_templates/medication_active.mustache +0 -35
- data/lib/qrda-export/catI-r5/qrda_templates/medication_administered.mustache +0 -31
- data/lib/qrda-export/catI-r5/qrda_templates/medication_discharge.mustache +0 -55
- data/lib/qrda-export/catI-r5/qrda_templates/medication_dispensed.mustache +0 -39
- data/lib/qrda-export/catI-r5/qrda_templates/medication_ordered.mustache +0 -38
- data/lib/qrda-export/catI-r5/qrda_templates/patient_characteristic_expired.mustache +0 -16
- data/lib/qrda-export/catI-r5/qrda_templates/physical_exam_performed.mustache +0 -25
- data/lib/qrda-export/catI-r5/qrda_templates/procedure_ordered.mustache +0 -19
- data/lib/qrda-export/catI-r5/qrda_templates/procedure_performed.mustache +0 -44
- data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_admission_source.mustache +0 -6
- data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_anatomical_location_site.mustache +0 -1
- data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_author.mustache +0 -7
- data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_author_participation.mustache +0 -7
- data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_component.mustache +0 -11
- data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_encounter_diagnosis.mustache +0 -19
- data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_encounter_facility_location.mustache +0 -16
- data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_mediation_frequency.mustache +0 -3
- data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_medication_details.mustache +0 -11
- data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_ordinality.mustache +0 -1
- data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_principal_diagnosis.mustache +0 -8
- data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_reason.mustache +0 -12
- data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_related_to.mustache +0 -6
- data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_results.mustache +0 -19
- data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_severity.mustache +0 -8
- data/lib/qrda-export/helper/cat_1_view_helper.rb +0 -150
- data/lib/qrda-export/helper/code_system_helper.rb +0 -77
- data/lib/qrda-export/helper/date_helper.rb +0 -89
- data/lib/qrda-import/base-importers/demographics_importer.rb +0 -49
- data/lib/qrda-import/base-importers/medication_importer.rb +0 -23
- data/lib/qrda-import/base-importers/section_importer.rb +0 -203
- data/lib/qrda-import/cda_identifier.rb +0 -19
- data/lib/qrda-import/data-element-importers/adverse_event_importer.rb +0 -24
- data/lib/qrda-import/data-element-importers/allergy_intolerance_importer.rb +0 -22
- data/lib/qrda-import/data-element-importers/assessment_performed_importer.rb +0 -26
- data/lib/qrda-import/data-element-importers/communication_from_patient_to_provider_importer.rb +0 -20
- data/lib/qrda-import/data-element-importers/communication_from_provider_to_patient_importer.rb +0 -20
- data/lib/qrda-import/data-element-importers/communication_from_provider_to_provider_importer.rb +0 -22
- data/lib/qrda-import/data-element-importers/device_applied_importer.rb +0 -26
- data/lib/qrda-import/data-element-importers/device_order_importer.rb +0 -21
- data/lib/qrda-import/data-element-importers/diagnosis_importer.rb +0 -24
- data/lib/qrda-import/data-element-importers/diagnostic_study_order_importer.rb +0 -23
- data/lib/qrda-import/data-element-importers/diagnostic_study_performed_importer.rb +0 -33
- data/lib/qrda-import/data-element-importers/encounter_order_importer.rb +0 -23
- data/lib/qrda-import/data-element-importers/encounter_performed_importer.rb +0 -42
- data/lib/qrda-import/data-element-importers/immunization_administered_importer.rb +0 -20
- data/lib/qrda-import/data-element-importers/intervention_order_importer.rb +0 -21
- data/lib/qrda-import/data-element-importers/intervention_performed_importer.rb +0 -25
- data/lib/qrda-import/data-element-importers/laboratory_test_order_importer.rb +0 -23
- data/lib/qrda-import/data-element-importers/laboratory_test_performed_importer.rb +0 -31
- data/lib/qrda-import/data-element-importers/medication_active_importer.rb +0 -17
- data/lib/qrda-import/data-element-importers/medication_administered_importer.rb +0 -19
- data/lib/qrda-import/data-element-importers/medication_discharge_importer.rb +0 -19
- data/lib/qrda-import/data-element-importers/medication_dispensed_importer.rb +0 -19
- data/lib/qrda-import/data-element-importers/medication_order_importer.rb +0 -18
- data/lib/qrda-import/data-element-importers/patient_characteristic_expired.rb +0 -22
- data/lib/qrda-import/data-element-importers/physical_exam_performed_importer.rb +0 -29
- data/lib/qrda-import/data-element-importers/procedure_order_importer.rb +0 -29
- data/lib/qrda-import/data-element-importers/procedure_performed_importer.rb +0 -37
- data/lib/qrda-import/data-element-importers/substance_administered_importer.rb +0 -17
- data/lib/qrda-import/entry_finder.rb +0 -20
- data/lib/qrda-import/entry_package.rb +0 -16
- data/lib/qrda-import/narrative_reference_handler.rb +0 -33
- data/lib/qrda-import/patient_importer.rb +0 -111
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: d83d5f1f9b8c3ee8b00ff2acf4340976cf4055ab43e9ab9cf178d91dc62feea5
|
|
4
|
+
data.tar.gz: 92584ac7191a821a1cce7fff38805f526838b10555b184b643fcb3b479310b32
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0fb80c3815803eb39df96cfee2dda409e3c8d3b675148d8aee06b5fc081ebe8996e952045cc8c0344204c29d02d449063c01850359b009de53bdae8a7f4a262c
|
|
7
|
+
data.tar.gz: bcb9fa25b6d7c1ae6f9d31dfacd1a242a945a0b91d2c2e46fe58a6ea4e487dde327eee0d8b88b7ad67a70cdfb1482e50f09adbd924e5f2da3c9deb90f0d0a821
|
data/Gemfile
CHANGED
|
@@ -2,17 +2,22 @@ source 'https://rubygems.org'
|
|
|
2
2
|
|
|
3
3
|
gemspec :development_group => :test
|
|
4
4
|
|
|
5
|
-
gem '
|
|
6
|
-
|
|
5
|
+
gem 'mongoid', '~> 6.4.2'
|
|
6
|
+
|
|
7
|
+
gem 'cqm-models', '~> 2.0.0'
|
|
8
|
+
# gem 'cqm-models', git: 'https://github.com/projecttacoma/cqm-models.git', branch: 'master'
|
|
9
|
+
# gem 'cqm-models', :path => '../cqm-models'
|
|
7
10
|
|
|
8
11
|
group :development, :test do
|
|
9
12
|
gem 'bundler-audit'
|
|
10
13
|
gem 'rubocop', '~> 0.52.1', require: false
|
|
14
|
+
gem 'byebug', '~> 6.0.2', platforms: [:ruby_20, :ruby_21, :ruby_22, :ruby_23]
|
|
15
|
+
gem 'pry'
|
|
16
|
+
gem 'pry-nav'
|
|
11
17
|
end
|
|
12
18
|
|
|
13
19
|
group :development do
|
|
14
20
|
gem 'rake'
|
|
15
|
-
gem 'byebug', '~> 6.0.2', platforms: [:ruby_20, :ruby_21, :ruby_22, :ruby_23]
|
|
16
21
|
end
|
|
17
22
|
|
|
18
23
|
group :test do
|
|
@@ -25,6 +30,5 @@ group :test do
|
|
|
25
30
|
gem 'minitest', '~> 5.3'
|
|
26
31
|
gem 'minitest-reporters'
|
|
27
32
|
gem 'awesome_print', :require => 'ap'
|
|
28
|
-
gem 'simplexml_parser', :git => 'https://github.com/projecttacoma/simplexml_parser.git', :branch => 'master'
|
|
29
33
|
gem 'vcr'
|
|
30
34
|
end
|
data/README.md
CHANGED
|
@@ -1,13 +1,52 @@
|
|
|
1
|
+
[](https://travis-ci.com/projecttacoma/cqm-parsers)
|
|
1
2
|
[](https://codecov.io/gh/projecttacoma/cqm-parsers)
|
|
3
|
+
[](https://badge.fury.io/rb/cqm-parsers)
|
|
2
4
|
|
|
5
|
+
# cqm-parsers
|
|
3
6
|
|
|
4
|
-
|
|
5
|
-
===========
|
|
7
|
+
This project contains libraries for parsing HQMF documents and parsing MAT packages.
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
## Usage (MAT Package Loading)
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
To load measures from a MAT package file into the measure model, use the `Measures::CqlLoader` class. It can be used to create an array of measure models. For a composite measure, the array will contain the component measures and the last element will be the composite measure. For a non-composite measure (most measures), the array will contain one item.
|
|
12
|
+
Example measure loading:
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
|
|
16
|
+
# Set the VSACValueSetLoader options; in this example we are fetching a specific profile.
|
|
17
|
+
vsac_options = { profile: 'MU2 Update 2016-04-01' }
|
|
18
|
+
|
|
19
|
+
# Set the measure details. For defaults, you can just pass in {}.
|
|
20
|
+
measure_details = { 'episode_of_care'=> false }
|
|
21
|
+
|
|
22
|
+
# Load a MAT package from test fixtures.
|
|
23
|
+
measure_file = File.new File.join('some/path/CMS158_v5_4_Artifacts.zip')
|
|
24
|
+
|
|
25
|
+
# Initialize a value set loader, in this case we are using the VSACValueSetLoader.
|
|
26
|
+
value_set_loader = Measures::VSACValueSetLoader.new(vsac_options, get_ticket_granting_ticket)
|
|
27
|
+
|
|
28
|
+
# Initialize the CqlLoader with the needed parameters.
|
|
29
|
+
loader = Measures::CqlLoader.new(measure_file, measure_details, value_set_loader)
|
|
30
|
+
# Build an array of measure models.
|
|
31
|
+
measures = loader.extract_measures
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Note that a different value set loader could be passed in; for example if you had a file containing value sets you could create a loader that read the value sets from file instead of fetching them from VSAC.
|
|
36
|
+
|
|
37
|
+
## Running the tests
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
|
|
41
|
+
bundle exec rake test
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Versioning
|
|
46
|
+
|
|
47
|
+
We use [SemVer](http://semver.org/) for versioning. For the versions available, see [tags on this repository](https://github.com/projecttacoma/cqm-parsers/tags).
|
|
48
|
+
|
|
49
|
+
## License
|
|
11
50
|
|
|
12
51
|
Copyright 2018 The MITRE Corporation
|
|
13
52
|
|
data/Rakefile
CHANGED
data/lib/ext/data_element.rb
CHANGED
|
@@ -2,7 +2,7 @@ module QDM
|
|
|
2
2
|
class DataElement
|
|
3
3
|
def merge!(other)
|
|
4
4
|
# ensure they're the same category (e.g. 'encounter')
|
|
5
|
-
return unless
|
|
5
|
+
return unless qdmCategory == other.qdmCategory
|
|
6
6
|
|
|
7
7
|
# ensure they're the same status (e.g. 'performed'), and that they both have a status set (or that they both don't)
|
|
8
8
|
return if respond_to?(:qdmStatus) && !other.respond_to?(:qdmStatus)
|
data/lib/hqmf-parser.rb
CHANGED
|
@@ -7,6 +7,8 @@ require 'ostruct'
|
|
|
7
7
|
require_relative 'util/counter.rb'
|
|
8
8
|
require_relative 'util/code_system_helper'
|
|
9
9
|
require_relative 'util/hqmf_template_helper'
|
|
10
|
+
require_relative 'util/vsac_api'
|
|
11
|
+
require_relative 'util/util'
|
|
10
12
|
|
|
11
13
|
require_relative 'hqmf-model/utilities.rb'
|
|
12
14
|
|
|
@@ -68,49 +70,15 @@ require_relative 'hqmf-parser/converter/pass2/operator_converter'
|
|
|
68
70
|
|
|
69
71
|
require_relative 'hqmf-parser/parser'
|
|
70
72
|
|
|
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
73
|
require_relative 'ext/data_element.rb'
|
|
116
|
-
|
|
74
|
+
|
|
75
|
+
require_relative 'measure-loader/helpers'
|
|
76
|
+
require_relative 'measure-loader/cql_loader'
|
|
77
|
+
require_relative 'measure-loader/elm_dependency_finder'
|
|
78
|
+
require_relative 'measure-loader/elm_parser'
|
|
79
|
+
require_relative 'measure-loader/exceptions'
|
|
80
|
+
require_relative 'measure-loader/hqmf_measure_loader'
|
|
81
|
+
require_relative 'measure-loader/mat_measure_files'
|
|
82
|
+
require_relative 'measure-loader/source_data_criteria_loader'
|
|
83
|
+
require_relative 'measure-loader/value_set_helpers'
|
|
84
|
+
require_relative 'measure-loader/vsac_value_set_loader'
|
|
@@ -192,7 +192,7 @@ module HQMF2
|
|
|
192
192
|
value_obj = handle_attribute_value(attribute, value) if attribute.at_xpath('./cda:value', NAMESPACES)
|
|
193
193
|
|
|
194
194
|
# Handle the cms_id - changed to eCQM in MAT 5.4 (QDM 5.3)
|
|
195
|
-
@cms_id = "CMS#{value}v#{@hqmf_version_number.to_i}" if
|
|
195
|
+
@cms_id = "CMS#{value}v#{@hqmf_version_number.to_i}" if name&.start_with?('eMeasure Identifier', 'eCQM Identifier')
|
|
196
196
|
|
|
197
197
|
HQMF::Attribute.new(id, code, value, nil, name, id_obj, code_obj, value_obj)
|
|
198
198
|
end
|
|
@@ -40,10 +40,15 @@ module HQMF2CQL
|
|
|
40
40
|
# The at_xpath(...).values returns an array of a single element.
|
|
41
41
|
# The match returns an array and since we don't want the double quotes we take the second element
|
|
42
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[:function_aggregation_type] = entry.at_xpath("*/cda:measureObservationDefinition/cda:methodCode/cda:item").attributes['code'].value
|
|
44
|
+
cql_define_function[:function_hqmf_oid] = entry.at_xpath("*/cda:measureObservationDefinition/cda:id").attributes['root'].value
|
|
43
45
|
# The criteria_reference_id is the id of the measurePopulationCriteria that should be used for this observation function
|
|
44
46
|
measure_population_id = entry.at_xpath("*/cda:measureObservationDefinition/cda:component/cda:criteriaReference/cda:id").attributes['root'].value
|
|
45
47
|
# Get the name of the parameter to the observation function within the measurePopulationCriteria section
|
|
46
|
-
|
|
48
|
+
|
|
49
|
+
measure_population_name = entry.at_xpath("*/cda:measureObservationDefinition/cda:component/cda:criteriaReference/cda:id").attributes['extension'].value
|
|
50
|
+
# Get the name of the parameter to the observation function within the relevant population criteria section
|
|
51
|
+
cql_define_function[:parameter] = @doc.at_xpath("cda:QualityMeasureDocument/cda:component/cda:populationCriteriaSection/cda:component/cda:#{measure_population_name}Criteria/cda:id[@root = \"#{measure_population_id}\"]/../cda:precondition/cda:criteriaReference/cda:id").attributes['extension'].value.match('\\"([A-Za-z0-9 ]+)\\"')[1]
|
|
47
52
|
|
|
48
53
|
@observations << cql_define_function
|
|
49
54
|
end
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
module Measures
|
|
2
|
+
class CqlLoader
|
|
3
|
+
|
|
4
|
+
def initialize(measure_zip, measure_details, value_set_loader = nil)
|
|
5
|
+
@measure_zip = measure_zip
|
|
6
|
+
@measure_details = measure_details.deep_symbolize_keys
|
|
7
|
+
@vs_model_cache = {}
|
|
8
|
+
@value_set_loader = value_set_loader
|
|
9
|
+
@value_set_loader.vs_model_cache = @vs_model_cache if @value_set_loader.present?
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Returns an array of measures, will contain a single measure if it is a non-composite measure
|
|
13
|
+
def extract_measures
|
|
14
|
+
measure_files = MATMeasureFiles.create_from_zip_file(@measure_zip)
|
|
15
|
+
|
|
16
|
+
measures = []
|
|
17
|
+
if measure_files.components.present?
|
|
18
|
+
measure, component_measures = create_measure_and_components(measure_files)
|
|
19
|
+
measures.push(*component_measures)
|
|
20
|
+
else
|
|
21
|
+
measure = create_measure(measure_files)
|
|
22
|
+
end
|
|
23
|
+
measure.package = CQM::MeasurePackage.new(file: BSON::Binary.new(@measure_zip.read))
|
|
24
|
+
measures << measure
|
|
25
|
+
|
|
26
|
+
measures.each { |m| CqlLoader.update_population_set_and_strat_titles(m, @measure_details[:population_titles]) }
|
|
27
|
+
return measures
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.update_population_set_and_strat_titles(measure, population_titles)
|
|
31
|
+
# Sample population_titles: [pop set 1 title, pop set 2 title, pop set 1 strat 1 title,
|
|
32
|
+
# pop set 1 strat 2 title, pop set 2 strat 1 title, pop set 2 strat 2 title]
|
|
33
|
+
# Note RE composite measures: components and composite must have same population sets and strats
|
|
34
|
+
return if population_titles.nil? || population_titles.empty?
|
|
35
|
+
title_idx = 0
|
|
36
|
+
measure.population_sets.each do |population_set|
|
|
37
|
+
population_set.title = population_titles[title_idx] if population_titles[title_idx].present?
|
|
38
|
+
title_idx += 1
|
|
39
|
+
break if title_idx >= population_titles.size
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
return if title_idx >= population_titles.size
|
|
43
|
+
|
|
44
|
+
measure.population_sets.flat_map(&:stratifications).each do |strat|
|
|
45
|
+
strat.title = population_titles[title_idx] if population_titles[title_idx].present?
|
|
46
|
+
title_idx += 1
|
|
47
|
+
break if title_idx >= population_titles.size
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def create_measure_and_components(measure_files)
|
|
54
|
+
top_level_library_ids = measure_files.cql_libraries.map { |lib| "#{lib.id}_v#{lib.version}" }
|
|
55
|
+
add_component_cql_library_files_to_composite_measure_files(measure_files)
|
|
56
|
+
measure = create_measure(measure_files)
|
|
57
|
+
component_measures = create_component_measures(measure_files, measure.hqmf_set_id)
|
|
58
|
+
measure.component_hqmf_set_ids = component_measures.map(&:hqmf_set_id)
|
|
59
|
+
unset_top_level_flag_on_cql_libraries_imported_from_components(measure, top_level_library_ids)
|
|
60
|
+
|
|
61
|
+
return measure, component_measures
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def create_component_measures(measure_files, composite_measure_hqmf_set_id)
|
|
65
|
+
component_measures = measure_files.components.map { |comp_files| create_measure(comp_files) }
|
|
66
|
+
component_measures.each do |component_measure|
|
|
67
|
+
# Set the components' hqmf_set_id to: <composite_hqmf_set_id>&<component_hqmf_set_id>
|
|
68
|
+
component_measure.hqmf_set_id = "#{composite_measure_hqmf_set_id}&#{component_measure.hqmf_set_id}"
|
|
69
|
+
component_measure.component = true
|
|
70
|
+
component_measure.composite_hqmf_set_id = composite_measure_hqmf_set_id
|
|
71
|
+
end
|
|
72
|
+
return component_measures
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def unset_top_level_flag_on_cql_libraries_imported_from_components(composite_measure, top_level_library_ids)
|
|
76
|
+
composite_measure.cql_libraries.each do |lib|
|
|
77
|
+
unless "#{lib.library_name}_v#{lib.library_version}".in? top_level_library_ids
|
|
78
|
+
lib.is_top_level = false # is_top_level defaults to true
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def add_component_cql_library_files_to_composite_measure_files(measure_files)
|
|
84
|
+
component_cql_library_files = measure_files.components.flat_map(&:cql_libraries)
|
|
85
|
+
measure_files.cql_libraries.push(*component_cql_library_files)
|
|
86
|
+
measure_files.cql_libraries.uniq! { |cl| cl.id + cl.version }
|
|
87
|
+
return nil
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Creates and returns a measure
|
|
91
|
+
def create_measure(measure_files)
|
|
92
|
+
hqmf_xml = measure_files.hqmf_xml
|
|
93
|
+
# update the valueset info in each elm (update version and remove urn:oid)
|
|
94
|
+
measure_files.cql_libraries.each { |cql_lib_files| modify_elm_valueset_information(cql_lib_files.elm) }
|
|
95
|
+
|
|
96
|
+
measure = CQM::Measure.new(HQMFMeasureLoader.extract_fields(hqmf_xml))
|
|
97
|
+
measure.cql_libraries = create_cql_libraries(measure_files.cql_libraries, measure.main_cql_library)
|
|
98
|
+
measure.composite = measure_files.components.present?
|
|
99
|
+
measure.calculation_method = @measure_details[:episode_of_care] ? 'EPISODE_OF_CARE' : 'PATIENT'
|
|
100
|
+
measure.calculate_sdes = @measure_details[:calculate_sdes]
|
|
101
|
+
|
|
102
|
+
hqmf_model = HQMF::Parser::V2CQLParser.new.parse(hqmf_xml) # TODO: move away from using V2CQLParser
|
|
103
|
+
|
|
104
|
+
elms = measure.cql_libraries.map(&:elm)
|
|
105
|
+
elm_valuesets = ValueSetHelpers.unique_list_of_valuesets_referenced_by_elms(elms)
|
|
106
|
+
verify_hqmf_valuesets_match_elm_valuesets(elm_valuesets, hqmf_model)
|
|
107
|
+
|
|
108
|
+
value_sets_from_single_code_references = ValueSetHelpers.make_fake_valuesets_from_single_code_references(elms, @vs_model_cache)
|
|
109
|
+
measure.source_data_criteria = SourceDataCriteriaLoader.new(hqmf_xml, value_sets_from_single_code_references).extract_data_criteria
|
|
110
|
+
measure.value_sets = value_sets_from_single_code_references
|
|
111
|
+
measure.value_sets.concat(@value_set_loader.retrieve_and_modelize_value_sets_from_vsac(elm_valuesets)) if @value_set_loader.present?
|
|
112
|
+
|
|
113
|
+
## this to_json is needed, it doesn't actually produce json, it just makes a hash that is better
|
|
114
|
+
## suited for our uses (e.g. source_data_criteria goes from an array to a hash keyed by id)
|
|
115
|
+
hqmf_model_hash = hqmf_model.to_json.deep_symbolize_keys!
|
|
116
|
+
HQMFMeasureLoader.add_fields_from_hqmf_model_hash(measure, hqmf_model_hash)
|
|
117
|
+
|
|
118
|
+
return measure
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def create_cql_libraries(cql_library_files, main_cql_lib)
|
|
122
|
+
cql_statement_dependencies_all_libs = ElmDependencyFinder.find_dependencies(cql_library_files, main_cql_lib)
|
|
123
|
+
|
|
124
|
+
cql_libraries = cql_library_files.map do |cql_lib_files|
|
|
125
|
+
cql_statement_dependencies = cql_statement_dependencies_all_libs[cql_lib_files.id]
|
|
126
|
+
is_main_cql_lib = cql_lib_files.id == main_cql_lib
|
|
127
|
+
modelize_cql_library(cql_lib_files, cql_statement_dependencies, is_main_cql_lib)
|
|
128
|
+
end
|
|
129
|
+
return cql_libraries
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def modelize_cql_library(cql_lib_files, cql_statement_dependencies, is_main_cql_lib)
|
|
133
|
+
return CQM::CQLLibrary.new(
|
|
134
|
+
library_name: cql_lib_files.id,
|
|
135
|
+
library_version: cql_lib_files.version,
|
|
136
|
+
elm: cql_lib_files.elm,
|
|
137
|
+
cql: cql_lib_files.cql,
|
|
138
|
+
elm_annotations: ElmParser.parse(cql_lib_files.elm_xml),
|
|
139
|
+
statement_dependencies: modelize_cql_statement_dependencies(cql_statement_dependencies),
|
|
140
|
+
is_main_library: is_main_cql_lib
|
|
141
|
+
)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def modelize_cql_statement_dependencies(cql_statment_deps)
|
|
145
|
+
return cql_statment_deps.map do |name, refs|
|
|
146
|
+
refs = refs.map { |ref| CQM::StatementReference.new(ref) }
|
|
147
|
+
CQM::StatementDependency.new(statement_name: name, statement_references: refs)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def modify_elm_valueset_information(elm)
|
|
152
|
+
ValueSetHelpers.remove_urnoid(elm)
|
|
153
|
+
ValueSetHelpers.modify_value_set_versions(elm)
|
|
154
|
+
return nil
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def verify_hqmf_valuesets_match_elm_valuesets(elm_valuesets, measure_hqmf_model)
|
|
158
|
+
# Exclude patient birthdate OID (2.16.840.1.113883.3.117.1.7.1.70) and patient expired
|
|
159
|
+
# OID (2.16.840.1.113883.3.117.1.7.1.309) used by SimpleXML parser for AGE_AT handling
|
|
160
|
+
# and bad oid protection in missing VS check
|
|
161
|
+
missing = (measure_hqmf_model.all_code_set_oids - elm_valuesets.map {|v| v[:oid]} - ['2.16.840.1.113883.3.117.1.7.1.70', '2.16.840.1.113883.3.117.1.7.1.309'])
|
|
162
|
+
raise MeasureLoadingInvalidPackageException.new("The HQMF file references the following valuesets not present in the CQL: #{missing}") unless missing.empty?
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
module Measures
|
|
2
|
+
module ElmDependencyFinder
|
|
3
|
+
class << self
|
|
4
|
+
|
|
5
|
+
def find_dependencies(cql_library_files, main_cql_library_id)
|
|
6
|
+
elms = cql_library_files.map(&:elm)
|
|
7
|
+
all_elms_dep_map = Hash[elms.map { |elm| [Helpers.elm_id(elm), make_statement_deps_for_elm(elm)] }]
|
|
8
|
+
needed_deps_map = Hash[elms.map { |elm| [Helpers.elm_id(elm), {}] }]
|
|
9
|
+
|
|
10
|
+
needed_deps_map[main_cql_library_id] = all_elms_dep_map[main_cql_library_id]
|
|
11
|
+
needed_deps_map[main_cql_library_id].each_value do |stmnts|
|
|
12
|
+
stmnts.each { |stmnt| deep_add_external_library_deps(stmnt, needed_deps_map, all_elms_dep_map) }
|
|
13
|
+
end
|
|
14
|
+
return needed_deps_map
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def make_library_alias_to_path_hash(elm)
|
|
20
|
+
lib_alias_to_path = { nil => Helpers.elm_id(elm) } # nil value used for statements without libraryName
|
|
21
|
+
(elm.dig('library','includes','def') || []).each do |library_hash|
|
|
22
|
+
lib_alias_to_path[library_hash['localIdentifier']] = library_hash['path']
|
|
23
|
+
end
|
|
24
|
+
return lib_alias_to_path
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def make_statement_deps_for_elm(elm)
|
|
28
|
+
deps = {}
|
|
29
|
+
lib_alias_to_path = make_library_alias_to_path_hash(elm)
|
|
30
|
+
make_statement_deps_for_elm_helper(elm, nil, deps, lib_alias_to_path)
|
|
31
|
+
deps.each_value(&:uniq!)
|
|
32
|
+
return deps
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def make_statement_deps_for_elm_helper(obj, parent_name, deps, lib_alias_to_path)
|
|
36
|
+
if obj.is_a? Array
|
|
37
|
+
obj.each { |el| make_statement_deps_for_elm_helper(el, parent_name, deps, lib_alias_to_path) }
|
|
38
|
+
elsif obj.is_a? Hash
|
|
39
|
+
if obj['type'].in?(['ExpressionRef', 'FunctionRef']) && parent_name != 'Patient'
|
|
40
|
+
dep = { library_name: lib_alias_to_path[obj['libraryName']], statement_name: obj['name'] }
|
|
41
|
+
deps[parent_name] << dep
|
|
42
|
+
elsif obj.key?('name') && obj.key?('expression')
|
|
43
|
+
parent_name = obj['name']
|
|
44
|
+
deps[parent_name] = [] unless deps.key?('parent_name')
|
|
45
|
+
end
|
|
46
|
+
obj.each_pair do |k,v|
|
|
47
|
+
make_statement_deps_for_elm_helper(v, parent_name, deps, lib_alias_to_path) unless k == 'annotation'
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def deep_add_external_library_deps(statement, needed_deps_map, all_elms_dep_map)
|
|
53
|
+
statement_library = statement[:library_name]
|
|
54
|
+
statement_name = statement[:statement_name]
|
|
55
|
+
|
|
56
|
+
return unless needed_deps_map.dig(statement_library, statement_name).nil? # return if key already exists
|
|
57
|
+
|
|
58
|
+
if all_elms_dep_map[statement_library].nil?
|
|
59
|
+
raise MeasureLoadingInvalidPackageException.new("Elm library #{statement_library} referenced but not found.")
|
|
60
|
+
end
|
|
61
|
+
if all_elms_dep_map[statement_library][statement_name].nil?
|
|
62
|
+
raise MeasureLoadingException.new("Elm statement '#{statement_name}' referenced but not found in library '#{statement_library}'.")
|
|
63
|
+
end
|
|
64
|
+
deps_to_add = all_elms_dep_map[statement_library][statement_name]
|
|
65
|
+
needed_deps_map.deep_merge!(statement_library => { statement_name => deps_to_add })
|
|
66
|
+
|
|
67
|
+
deps_to_add.each { |stmnt| deep_add_external_library_deps(stmnt, needed_deps_map, all_elms_dep_map) }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|