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.
Files changed (119) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +8 -4
  3. data/README.md +44 -5
  4. data/Rakefile +1 -0
  5. data/lib/ext/data_element.rb +1 -1
  6. data/lib/hqmf-parser.rb +13 -45
  7. data/lib/hqmf-parser/2.0/document.rb +1 -1
  8. data/lib/hqmf-parser/cql/document_helpers/doc_population_helper.rb +6 -1
  9. data/lib/measure-loader/cql_loader.rb +165 -0
  10. data/lib/measure-loader/elm_dependency_finder.rb +72 -0
  11. data/lib/measure-loader/elm_parser.rb +67 -0
  12. data/lib/measure-loader/exceptions.rb +10 -0
  13. data/lib/measure-loader/helpers.rb +11 -0
  14. data/lib/measure-loader/hqmf_measure_loader.rb +170 -0
  15. data/lib/measure-loader/mat_measure_files.rb +138 -0
  16. data/lib/measure-loader/source_data_criteria_loader.rb +65 -0
  17. data/lib/measure-loader/value_set_helpers.rb +68 -0
  18. data/lib/measure-loader/vsac_value_set_loader.rb +97 -0
  19. data/lib/util/util.rb +23 -0
  20. data/lib/util/vsac_api.rb +164 -101
  21. metadata +47 -129
  22. data/lib/ext/code.rb +0 -10
  23. data/lib/qrda-export/catI-r5/_code.mustache +0 -1
  24. data/lib/qrda-export/catI-r5/_codes.mustache +0 -10
  25. data/lib/qrda-export/catI-r5/_header.mustache +0 -28
  26. data/lib/qrda-export/catI-r5/_measure_section.mustache +0 -59
  27. data/lib/qrda-export/catI-r5/_reporting_period.mustache +0 -23
  28. data/lib/qrda-export/catI-r5/_values.mustache +0 -10
  29. data/lib/qrda-export/catI-r5/qrda1_r5.mustache +0 -137
  30. data/lib/qrda-export/catI-r5/qrda1_r5.rb +0 -125
  31. data/lib/qrda-export/catI-r5/qrda_header/_author.mustache +0 -24
  32. data/lib/qrda-export/catI-r5/qrda_header/_custodian.mustache +0 -43
  33. data/lib/qrda-export/catI-r5/qrda_header/_documentation_of_service_event.mustache +0 -82
  34. data/lib/qrda-export/catI-r5/qrda_header/_information_recipient.mustache +0 -7
  35. data/lib/qrda-export/catI-r5/qrda_header/_legal_authenticator.mustache +0 -25
  36. data/lib/qrda-export/catI-r5/qrda_header/_participant.mustache +0 -7
  37. data/lib/qrda-export/catI-r5/qrda_header/_record_target.mustache +0 -28
  38. data/lib/qrda-export/catI-r5/qrda_templates/adverse_event.mustache +0 -28
  39. data/lib/qrda-export/catI-r5/qrda_templates/allergy_intolerance.mustache +0 -28
  40. data/lib/qrda-export/catI-r5/qrda_templates/assessment_performed.mustache +0 -25
  41. data/lib/qrda-export/catI-r5/qrda_templates/communication_from_patient_to_provider.mustache +0 -29
  42. data/lib/qrda-export/catI-r5/qrda_templates/communication_from_provider_to_patient.mustache +0 -24
  43. data/lib/qrda-export/catI-r5/qrda_templates/communication_from_provider_to_provider.mustache +0 -31
  44. data/lib/qrda-export/catI-r5/qrda_templates/device_applied.mustache +0 -32
  45. data/lib/qrda-export/catI-r5/qrda_templates/device_ordered.mustache +0 -31
  46. data/lib/qrda-export/catI-r5/qrda_templates/diagnosis.mustache +0 -38
  47. data/lib/qrda-export/catI-r5/qrda_templates/diagnostic_study_ordered.mustache +0 -19
  48. data/lib/qrda-export/catI-r5/qrda_templates/diagnostic_study_performed.mustache +0 -32
  49. data/lib/qrda-export/catI-r5/qrda_templates/encounter_ordered.mustache +0 -24
  50. data/lib/qrda-export/catI-r5/qrda_templates/encounter_performed.mustache +0 -40
  51. data/lib/qrda-export/catI-r5/qrda_templates/immunization_administered.mustache +0 -29
  52. data/lib/qrda-export/catI-r5/qrda_templates/insurance_provider.mustache +0 -11
  53. data/lib/qrda-export/catI-r5/qrda_templates/intervention_ordered.mustache +0 -18
  54. data/lib/qrda-export/catI-r5/qrda_templates/intervention_performed.mustache +0 -25
  55. data/lib/qrda-export/catI-r5/qrda_templates/lab_test_ordered.mustache +0 -18
  56. data/lib/qrda-export/catI-r5/qrda_templates/lab_test_performed.mustache +0 -22
  57. data/lib/qrda-export/catI-r5/qrda_templates/medication_active.mustache +0 -35
  58. data/lib/qrda-export/catI-r5/qrda_templates/medication_administered.mustache +0 -31
  59. data/lib/qrda-export/catI-r5/qrda_templates/medication_discharge.mustache +0 -55
  60. data/lib/qrda-export/catI-r5/qrda_templates/medication_dispensed.mustache +0 -39
  61. data/lib/qrda-export/catI-r5/qrda_templates/medication_ordered.mustache +0 -38
  62. data/lib/qrda-export/catI-r5/qrda_templates/patient_characteristic_expired.mustache +0 -16
  63. data/lib/qrda-export/catI-r5/qrda_templates/physical_exam_performed.mustache +0 -25
  64. data/lib/qrda-export/catI-r5/qrda_templates/procedure_ordered.mustache +0 -19
  65. data/lib/qrda-export/catI-r5/qrda_templates/procedure_performed.mustache +0 -44
  66. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_admission_source.mustache +0 -6
  67. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_anatomical_location_site.mustache +0 -1
  68. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_author.mustache +0 -7
  69. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_author_participation.mustache +0 -7
  70. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_component.mustache +0 -11
  71. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_encounter_diagnosis.mustache +0 -19
  72. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_encounter_facility_location.mustache +0 -16
  73. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_mediation_frequency.mustache +0 -3
  74. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_medication_details.mustache +0 -11
  75. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_ordinality.mustache +0 -1
  76. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_principal_diagnosis.mustache +0 -8
  77. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_reason.mustache +0 -12
  78. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_related_to.mustache +0 -6
  79. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_results.mustache +0 -19
  80. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_severity.mustache +0 -8
  81. data/lib/qrda-export/helper/cat_1_view_helper.rb +0 -150
  82. data/lib/qrda-export/helper/code_system_helper.rb +0 -77
  83. data/lib/qrda-export/helper/date_helper.rb +0 -89
  84. data/lib/qrda-import/base-importers/demographics_importer.rb +0 -49
  85. data/lib/qrda-import/base-importers/medication_importer.rb +0 -23
  86. data/lib/qrda-import/base-importers/section_importer.rb +0 -203
  87. data/lib/qrda-import/cda_identifier.rb +0 -19
  88. data/lib/qrda-import/data-element-importers/adverse_event_importer.rb +0 -24
  89. data/lib/qrda-import/data-element-importers/allergy_intolerance_importer.rb +0 -22
  90. data/lib/qrda-import/data-element-importers/assessment_performed_importer.rb +0 -26
  91. data/lib/qrda-import/data-element-importers/communication_from_patient_to_provider_importer.rb +0 -20
  92. data/lib/qrda-import/data-element-importers/communication_from_provider_to_patient_importer.rb +0 -20
  93. data/lib/qrda-import/data-element-importers/communication_from_provider_to_provider_importer.rb +0 -22
  94. data/lib/qrda-import/data-element-importers/device_applied_importer.rb +0 -26
  95. data/lib/qrda-import/data-element-importers/device_order_importer.rb +0 -21
  96. data/lib/qrda-import/data-element-importers/diagnosis_importer.rb +0 -24
  97. data/lib/qrda-import/data-element-importers/diagnostic_study_order_importer.rb +0 -23
  98. data/lib/qrda-import/data-element-importers/diagnostic_study_performed_importer.rb +0 -33
  99. data/lib/qrda-import/data-element-importers/encounter_order_importer.rb +0 -23
  100. data/lib/qrda-import/data-element-importers/encounter_performed_importer.rb +0 -42
  101. data/lib/qrda-import/data-element-importers/immunization_administered_importer.rb +0 -20
  102. data/lib/qrda-import/data-element-importers/intervention_order_importer.rb +0 -21
  103. data/lib/qrda-import/data-element-importers/intervention_performed_importer.rb +0 -25
  104. data/lib/qrda-import/data-element-importers/laboratory_test_order_importer.rb +0 -23
  105. data/lib/qrda-import/data-element-importers/laboratory_test_performed_importer.rb +0 -31
  106. data/lib/qrda-import/data-element-importers/medication_active_importer.rb +0 -17
  107. data/lib/qrda-import/data-element-importers/medication_administered_importer.rb +0 -19
  108. data/lib/qrda-import/data-element-importers/medication_discharge_importer.rb +0 -19
  109. data/lib/qrda-import/data-element-importers/medication_dispensed_importer.rb +0 -19
  110. data/lib/qrda-import/data-element-importers/medication_order_importer.rb +0 -18
  111. data/lib/qrda-import/data-element-importers/patient_characteristic_expired.rb +0 -22
  112. data/lib/qrda-import/data-element-importers/physical_exam_performed_importer.rb +0 -29
  113. data/lib/qrda-import/data-element-importers/procedure_order_importer.rb +0 -29
  114. data/lib/qrda-import/data-element-importers/procedure_performed_importer.rb +0 -37
  115. data/lib/qrda-import/data-element-importers/substance_administered_importer.rb +0 -17
  116. data/lib/qrda-import/entry_finder.rb +0 -20
  117. data/lib/qrda-import/entry_package.rb +0 -16
  118. data/lib/qrda-import/narrative_reference_handler.rb +0 -33
  119. data/lib/qrda-import/patient_importer.rb +0 -111
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 5ae0c0edc66dbccfb1f7bff982ed28472220b223
4
- data.tar.gz: a244b962a1ba17b061ac9fbacdb1ce27ef3d9ae0
2
+ SHA256:
3
+ metadata.gz: d83d5f1f9b8c3ee8b00ff2acf4340976cf4055ab43e9ab9cf178d91dc62feea5
4
+ data.tar.gz: 92584ac7191a821a1cce7fff38805f526838b10555b184b643fcb3b479310b32
5
5
  SHA512:
6
- metadata.gz: aa00da03f1b87750f4dd0fdaa9a60b67616afba80c41f741a35b74b2a2e989b738220d28c5cbfab6ed9087f3b71da829c9a0c3da50e276588ea648d2490853dc
7
- data.tar.gz: 29f3149e4f031808f4e956c9d29584ae1acc9cc60b6798192fd4a880fdf41fad302d6da47a0cf1add996a42fccb802e3f5d844a4421d85bc97ca1ba8beb39f45
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 'cqm-models', '~> 0.8.4'
6
- gem 'mongoid', '~> 5.0.0'
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
+ [![Build Status](https://travis-ci.com/projecttacoma/cqm-parsers.svg?branch=master)](https://travis-ci.com/projecttacoma/cqm-parsers)
1
2
  [![codecov](https://codecov.io/gh/projecttacoma/cqm-parsers/branch/master/graph/badge.svg)](https://codecov.io/gh/projecttacoma/cqm-parsers)
3
+ [![Gem Version](https://badge.fury.io/rb/cqm-parsers.svg)](https://badge.fury.io/rb/cqm-parsers)
2
4
 
5
+ # cqm-parsers
3
6
 
4
- cqm-parsers
5
- ===========
7
+ This project contains libraries for parsing HQMF documents and parsing MAT packages.
6
8
 
7
- This project contains libraries for parsing HQMF documents.
9
+ ## Usage (MAT Package Loading)
8
10
 
9
- License
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
@@ -1,3 +1,4 @@
1
+ require 'simplecov'
1
2
  require 'rake/testtask'
2
3
  require 'cane/rake_task'
3
4
 
@@ -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 category == other.category
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)
@@ -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
- require_relative 'ext/code.rb'
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 (!name.nil?) && ((name.include? 'eMeasure Identifier') || (name.include? 'eCQM Identifier'))
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
- cql_define_function[:parameter] = @doc.at_xpath("cda:QualityMeasureDocument/cda:component/cda:populationCriteriaSection/cda:component/cda:measurePopulationCriteria/cda:id[@root = \"#{measure_population_id}\"]/../cda:precondition/cda:criteriaReference/cda:id").attributes['extension'].value.match('\\"([A-Za-z0-9 ]+)\\"')[1]
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