cqm-parsers 0.1.0

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