cqm-parsers 0.2.2 → 3.0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +7 -3
  3. data/README.md +57 -5
  4. data/Rakefile +1 -0
  5. data/lib/{hqmf-parser.rb → cqm-parsers.rb} +13 -45
  6. data/lib/ext/data_element.rb +1 -1
  7. data/lib/hqmf-parser/2.0/document.rb +1 -1
  8. data/lib/hqmf-parser/2.0/population_criteria.rb +1 -1
  9. data/lib/hqmf-parser/cql/document_helpers/doc_population_helper.rb +11 -6
  10. data/lib/measure-loader/cql_loader.rb +165 -0
  11. data/lib/measure-loader/elm_dependency_finder.rb +72 -0
  12. data/lib/measure-loader/elm_parser.rb +67 -0
  13. data/lib/measure-loader/exceptions.rb +10 -0
  14. data/lib/measure-loader/helpers.rb +11 -0
  15. data/lib/measure-loader/hqmf_measure_loader.rb +170 -0
  16. data/lib/measure-loader/mat_measure_files.rb +138 -0
  17. data/lib/measure-loader/source_data_criteria_loader.rb +75 -0
  18. data/lib/measure-loader/value_set_helpers.rb +68 -0
  19. data/lib/measure-loader/vsac_value_set_loader.rb +97 -0
  20. data/lib/tasks/hqmf.rake +1 -1
  21. data/lib/util/util.rb +23 -0
  22. data/lib/util/vsac_api.rb +166 -103
  23. metadata +49 -116
  24. data/lib/ext/code.rb +0 -10
  25. data/lib/qrda-export/catI-r5/_code.mustache +0 -1
  26. data/lib/qrda-export/catI-r5/_codes.mustache +0 -10
  27. data/lib/qrda-export/catI-r5/_header.mustache +0 -28
  28. data/lib/qrda-export/catI-r5/_measure_section.mustache +0 -59
  29. data/lib/qrda-export/catI-r5/_reporting_period.mustache +0 -23
  30. data/lib/qrda-export/catI-r5/_values.mustache +0 -10
  31. data/lib/qrda-export/catI-r5/qrda1_r5.mustache +0 -137
  32. data/lib/qrda-export/catI-r5/qrda1_r5.rb +0 -125
  33. data/lib/qrda-export/catI-r5/qrda_header/_author.mustache +0 -24
  34. data/lib/qrda-export/catI-r5/qrda_header/_custodian.mustache +0 -43
  35. data/lib/qrda-export/catI-r5/qrda_header/_documentation_of_service_event.mustache +0 -82
  36. data/lib/qrda-export/catI-r5/qrda_header/_information_recipient.mustache +0 -7
  37. data/lib/qrda-export/catI-r5/qrda_header/_legal_authenticator.mustache +0 -25
  38. data/lib/qrda-export/catI-r5/qrda_header/_participant.mustache +0 -7
  39. data/lib/qrda-export/catI-r5/qrda_header/_record_target.mustache +0 -28
  40. data/lib/qrda-export/catI-r5/qrda_templates/adverse_event.mustache +0 -28
  41. data/lib/qrda-export/catI-r5/qrda_templates/allergy_intolerance.mustache +0 -28
  42. data/lib/qrda-export/catI-r5/qrda_templates/assessment_performed.mustache +0 -25
  43. data/lib/qrda-export/catI-r5/qrda_templates/communication_from_patient_to_provider.mustache +0 -29
  44. data/lib/qrda-export/catI-r5/qrda_templates/communication_from_provider_to_patient.mustache +0 -24
  45. data/lib/qrda-export/catI-r5/qrda_templates/communication_from_provider_to_provider.mustache +0 -31
  46. data/lib/qrda-export/catI-r5/qrda_templates/device_applied.mustache +0 -32
  47. data/lib/qrda-export/catI-r5/qrda_templates/device_ordered.mustache +0 -31
  48. data/lib/qrda-export/catI-r5/qrda_templates/diagnosis.mustache +0 -38
  49. data/lib/qrda-export/catI-r5/qrda_templates/diagnostic_study_ordered.mustache +0 -19
  50. data/lib/qrda-export/catI-r5/qrda_templates/diagnostic_study_performed.mustache +0 -29
  51. data/lib/qrda-export/catI-r5/qrda_templates/encounter_ordered.mustache +0 -24
  52. data/lib/qrda-export/catI-r5/qrda_templates/encounter_performed.mustache +0 -40
  53. data/lib/qrda-export/catI-r5/qrda_templates/immunization_administered.mustache +0 -29
  54. data/lib/qrda-export/catI-r5/qrda_templates/insurance_provider.mustache +0 -11
  55. data/lib/qrda-export/catI-r5/qrda_templates/intervention_ordered.mustache +0 -18
  56. data/lib/qrda-export/catI-r5/qrda_templates/intervention_performed.mustache +0 -25
  57. data/lib/qrda-export/catI-r5/qrda_templates/lab_test_ordered.mustache +0 -18
  58. data/lib/qrda-export/catI-r5/qrda_templates/lab_test_performed.mustache +0 -22
  59. data/lib/qrda-export/catI-r5/qrda_templates/medication_active.mustache +0 -35
  60. data/lib/qrda-export/catI-r5/qrda_templates/medication_administered.mustache +0 -31
  61. data/lib/qrda-export/catI-r5/qrda_templates/medication_discharge.mustache +0 -55
  62. data/lib/qrda-export/catI-r5/qrda_templates/medication_dispensed.mustache +0 -39
  63. data/lib/qrda-export/catI-r5/qrda_templates/medication_ordered.mustache +0 -38
  64. data/lib/qrda-export/catI-r5/qrda_templates/patient_characteristic_expired.mustache +0 -16
  65. data/lib/qrda-export/catI-r5/qrda_templates/physical_exam_performed.mustache +0 -25
  66. data/lib/qrda-export/catI-r5/qrda_templates/procedure_ordered.mustache +0 -19
  67. data/lib/qrda-export/catI-r5/qrda_templates/procedure_performed.mustache +0 -44
  68. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_admission_source.mustache +0 -6
  69. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_anatomical_location_site.mustache +0 -1
  70. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_author.mustache +0 -7
  71. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_author_participation.mustache +0 -7
  72. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_component.mustache +0 -11
  73. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_encounter_diagnosis.mustache +0 -19
  74. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_encounter_facility_location.mustache +0 -16
  75. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_mediation_frequency.mustache +0 -3
  76. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_medication_details.mustache +0 -11
  77. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_ordinality.mustache +0 -1
  78. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_principal_diagnosis.mustache +0 -8
  79. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_reason.mustache +0 -12
  80. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_related_to.mustache +0 -6
  81. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_results.mustache +0 -21
  82. data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_severity.mustache +0 -8
  83. data/lib/qrda-export/helper/cat_1_view_helper.rb +0 -142
  84. data/lib/qrda-export/helper/code_system_helper.rb +0 -77
  85. data/lib/qrda-export/helper/date_helper.rb +0 -85
  86. data/lib/qrda-import/base-importers/demographics_importer.rb +0 -43
  87. data/lib/qrda-import/base-importers/medication_importer.rb +0 -23
  88. data/lib/qrda-import/base-importers/section_importer.rb +0 -196
  89. data/lib/qrda-import/cda_identifier.rb +0 -19
  90. data/lib/qrda-import/data-element-importers/adverse_event_importer.rb +0 -24
  91. data/lib/qrda-import/data-element-importers/allergy_intolerance_importer.rb +0 -22
  92. data/lib/qrda-import/data-element-importers/assessment_performed_importer.rb +0 -24
  93. data/lib/qrda-import/data-element-importers/communication_from_patient_to_provider_importer.rb +0 -19
  94. data/lib/qrda-import/data-element-importers/communication_from_provider_to_patient_importer.rb +0 -19
  95. data/lib/qrda-import/data-element-importers/communication_from_provider_to_provider_importer.rb +0 -21
  96. data/lib/qrda-import/data-element-importers/device_applied_importer.rb +0 -24
  97. data/lib/qrda-import/data-element-importers/device_order_importer.rb +0 -19
  98. data/lib/qrda-import/data-element-importers/diagnosis_importer.rb +0 -24
  99. data/lib/qrda-import/data-element-importers/diagnostic_study_order_importer.rb +0 -21
  100. data/lib/qrda-import/data-element-importers/diagnostic_study_performed_importer.rb +0 -31
  101. data/lib/qrda-import/data-element-importers/encounter_order_importer.rb +0 -21
  102. data/lib/qrda-import/data-element-importers/encounter_performed_importer.rb +0 -42
  103. data/lib/qrda-import/data-element-importers/immunization_administered_importer.rb +0 -18
  104. data/lib/qrda-import/data-element-importers/intervention_order_importer.rb +0 -19
  105. data/lib/qrda-import/data-element-importers/intervention_performed_importer.rb +0 -23
  106. data/lib/qrda-import/data-element-importers/laboratory_test_order_importer.rb +0 -21
  107. data/lib/qrda-import/data-element-importers/laboratory_test_performed_importer.rb +0 -29
  108. data/lib/qrda-import/data-element-importers/medication_active_importer.rb +0 -17
  109. data/lib/qrda-import/data-element-importers/medication_administered_importer.rb +0 -17
  110. data/lib/qrda-import/data-element-importers/medication_discharge_importer.rb +0 -19
  111. data/lib/qrda-import/data-element-importers/medication_dispensed_importer.rb +0 -19
  112. data/lib/qrda-import/data-element-importers/medication_order_importer.rb +0 -16
  113. data/lib/qrda-import/data-element-importers/patient_characteristic_expired.rb +0 -22
  114. data/lib/qrda-import/data-element-importers/physical_exam_performed_importer.rb +0 -27
  115. data/lib/qrda-import/data-element-importers/procedure_order_importer.rb +0 -27
  116. data/lib/qrda-import/data-element-importers/procedure_performed_importer.rb +0 -35
  117. data/lib/qrda-import/data-element-importers/substance_administered_importer.rb +0 -16
  118. data/lib/qrda-import/entry_finder.rb +0 -20
  119. data/lib/qrda-import/entry_package.rb +0 -16
  120. data/lib/qrda-import/narrative_reference_handler.rb +0 -33
  121. data/lib/qrda-import/patient_importer.rb +0 -111
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: 1b1bca119f15302832fea054a9bcc5ad90736bfe3ad898ec05410ba9932c4fa6
4
- data.tar.gz: 94776039cbf7dadafc0b8bf2308cca80b60e298a5291494d3d63d2ade0f852d4
2
+ SHA1:
3
+ metadata.gz: 9946e3dc5e1e74875ed24bb690332fc260f0b103
4
+ data.tar.gz: aa9298b5a7e4790a0534210a1278ec3b78ad387c
5
5
  SHA512:
6
- metadata.gz: 476c7290726a10f52dfb3f65a066516f755d7913be15c74783d5fbc836a2718c7cf65b9580ef309368ec4bdb45d9d6d175881b190660eb94f12d9ed6b2eeed08
7
- data.tar.gz: 14d8e37e7fe9c737df8175192d935e3b908a53fa4511ccff107772108ef055375091241206fa964a5c430dcb944b5ce7b05b1bd94b52a5860a902d212843b0ca
6
+ metadata.gz: c9c64d13dc66b78ea4c8066ec1229d0503b217b2a10e440b6bb6f77911b3145049e3933bc8d5674928efb20e0d333e0830bfe2be304e6a7a46b808494e44fbf4
7
+ data.tar.gz: 578f9b9d566c4c67804fb33635c3dfaccfe9e09cd2fa5978fe4af57cde7b1955bf8f62b84de7a9f859bd57fc3026eee14dca1757d7655163d6de29c2692dd6a8
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
5
  gem 'mongoid', '~> 5.0.0'
7
6
 
7
+ # gem 'cqm-models', '~> 3.0.0'
8
+ # gem 'cqm-models', git: 'https://github.com/projecttacoma/cqm-models.git', branch: 'master'
9
+ # gem 'cqm-models', :path => '../cqm-models'
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,65 @@
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
+
46
+ ## Versioning
47
+
48
+ Starting with version **2.0.0** released on 6/20/19, cqm-parsers versioning has the format **X.Y.Z**, where:
49
+
50
+ * **X** maps to a version of the CQL-based HQMF IG. See the table below to see the existing mapping to CQL-based HQMF IG versions.
51
+
52
+ | X | CQL-based HQMF IG|
53
+ | --- | --- |
54
+ | 2 | R1 STU3 |
55
+
56
+ * **Y** indicates major changes (incompatible API changes)
57
+
58
+ * **Z** indicates minor changes (added functionality in a backwards-compatible manner) and patch changes (backwards-compatible bug fixes)
59
+
60
+ For the versions available, see [tags on this repository](https://github.com/projecttacoma/cqm-parsers/tags).
61
+
62
+ ## License
11
63
 
12
64
  Copyright 2018 The MITRE Corporation
13
65
 
data/Rakefile CHANGED
@@ -1,3 +1,4 @@
1
+ require 'simplecov'
1
2
  require 'rake/testtask'
2
3
  require 'cane/rake_task'
3
4
 
@@ -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'
@@ -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)
@@ -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
@@ -74,7 +74,7 @@ module HQMF2
74
74
  fail 'Measure Observations criteria is missing computed expression(s) ' if exp.nil?
75
75
  parts = exp.to_s.split('-')
76
76
  dc = parse_parts_to_dc(parts)
77
- @doc.add_data_criteria(dc)
77
+ @doc.add_data_criteria(dc) unless dc.nil?
78
78
  # Update reference_ids with any newly referenced data criteria
79
79
  dc.children_criteria.each { |cc| @doc.add_reference_id(cc) } unless dc&.children_criteria.nil?
80
80
  dc
@@ -11,7 +11,7 @@ module HQMF2CQL
11
11
  end
12
12
 
13
13
  # Extracts potential measure observations from the CQL based HQMF.
14
- # This function needs to return a boolean so that it will continue to work with
14
+ # This function needs to return a boolean so that it will continue to work with
15
15
  # HQMF2::DocumentPopulationHelper::extract_populations_and_criteria
16
16
  # This function is being overridden because in CQL the observations are no longer data criteria in the HQMF.
17
17
  def extract_observations
@@ -22,10 +22,10 @@ module HQMF2CQL
22
22
  observation_section = @doc.xpath('/cda:QualityMeasureDocument/cda:component/cda:measureObservationSection',
23
23
  HQMF2::Document::NAMESPACES)
24
24
  unless observation_section.empty?
25
- observation_section.each do |entry|
25
+ observation_section.xpath('cda:definition').each do |obs_def|
26
26
  # Need to add population criteria for observations
27
27
  criteria_id = 'OBSERV'
28
- criteria = HQMF2::PopulationCriteria.new(entry.xpath('cda:definition'), @document, @id_generator)
28
+ criteria = HQMF2::PopulationCriteria.new(obs_def, @document, @id_generator)
29
29
  criteria.type = 'OBSERV'
30
30
  if @ids_by_hqmf_id["#{criteria.hqmf_id}"]
31
31
  criteria.create_human_readable_id(@ids_by_hqmf_id[criteria.hqmf_id])
@@ -39,11 +39,16 @@ module HQMF2CQL
39
39
  cql_define_function = {}
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
- cql_define_function[:function_name] = entry.at_xpath("*/cda:measureObservationDefinition/cda:value/cda:expression").values.first.match('\\"([A-Za-z0-9 ]+)\\"')[1]
42
+ cql_define_function[:function_name] = obs_def.at_xpath("cda:measureObservationDefinition/cda:value/cda:expression").values.first.match('\\"([A-Za-z0-9 ]+)\\"')[1]
43
+ cql_define_function[:function_aggregation_type] = obs_def.at_xpath("cda:measureObservationDefinition/cda:methodCode/cda:item").attributes['code'].value
44
+ cql_define_function[:function_hqmf_oid] = obs_def.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
- measure_population_id = entry.at_xpath("*/cda:measureObservationDefinition/cda:component/cda:criteriaReference/cda:id").attributes['root'].value
46
+ measure_population_id = obs_def.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 = obs_def.at_xpath("cda:measureObservationDefinition/cda:component/cda:criteriaReference/cda:id").attributes['extension'].value
50
+ criteria_reference_id = @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")
51
+ cql_define_function[:parameter] = criteria_reference_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