health-data-standards 3.7.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/lib/health-data-standards.rb +3 -0
  3. data/lib/health-data-standards/export/helper/html_view_helper.rb +12 -12
  4. data/lib/health-data-standards/import/bulk_record_importer.rb +4 -4
  5. data/lib/health-data-standards/import/cda/encounter_importer.rb +2 -2
  6. data/lib/health-data-standards/models/admission_source.rb +11 -0
  7. data/lib/health-data-standards/models/adverse_event.rb +6 -0
  8. data/lib/health-data-standards/models/allergy.rb +2 -1
  9. data/lib/health-data-standards/models/assessment.rb +1 -0
  10. data/lib/health-data-standards/models/care_goal.rb +0 -1
  11. data/lib/health-data-standards/models/component.rb +14 -0
  12. data/lib/health-data-standards/models/encounter.rb +10 -13
  13. data/lib/health-data-standards/models/entry.rb +1 -1
  14. data/lib/health-data-standards/models/facility.rb +3 -2
  15. data/lib/health-data-standards/models/lab_result.rb +10 -4
  16. data/lib/health-data-standards/models/medication.rb +3 -0
  17. data/lib/health-data-standards/models/procedure.rb +7 -2
  18. data/lib/health-data-standards/models/record.rb +6 -2
  19. data/lib/health-data-standards/models/transfer.rb +0 -5
  20. data/lib/health-data-standards/util/hqmf_template_helper.rb +3 -2
  21. data/lib/health-data-standards/util/hqmfr2_template_oid_map.json +8 -0
  22. data/lib/health-data-standards/util/hqmfr2cql_template_oid_map.json +390 -0
  23. data/lib/health-data-standards/util/vs_api.rb +3 -3
  24. data/lib/hqmf-model/data_criteria.json +19 -1
  25. data/lib/hqmf-model/data_criteria.rb +31 -7
  26. data/lib/hqmf-model/document.rb +11 -2
  27. data/lib/hqmf-model/types.rb +31 -5
  28. data/lib/hqmf-parser.rb +7 -0
  29. data/lib/hqmf-parser/2.0/data_criteria.rb +4 -1
  30. data/lib/hqmf-parser/2.0/data_criteria_helpers/dc_base_extract.rb +1 -1
  31. data/lib/hqmf-parser/2.0/document.rb +4 -4
  32. data/lib/hqmf-parser/2.0/document_helpers/doc_population_helper.rb +0 -2
  33. data/lib/hqmf-parser/2.0/population_criteria.rb +2 -2
  34. data/lib/hqmf-parser/2.0/value_set_helper.rb +4 -1
  35. data/lib/hqmf-parser/cql/data_criteria.rb +57 -0
  36. data/lib/hqmf-parser/cql/data_criteria_helpers/dc_definition_from_template_or_type_extract.rb +79 -0
  37. data/lib/hqmf-parser/cql/data_criteria_helpers/dc_post_processing.rb +43 -0
  38. data/lib/hqmf-parser/cql/document.rb +78 -0
  39. data/lib/hqmf-parser/cql/document_helpers/doc_population_helper.rb +124 -0
  40. data/lib/hqmf-parser/cql/value_set_helper.rb +103 -0
  41. data/lib/hqmf-parser/parser.rb +24 -1
  42. metadata +11 -1
@@ -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
@@ -4,6 +4,7 @@ module HQMF
4
4
  HQMF_VERSION_1 = "1.0"
5
5
  HQMF_VERSION_2 = "2.0"
6
6
 
7
+ # HQMF v2 Parser for measures that use QDM for measure logic
7
8
  class V2Parser
8
9
  def initialize
9
10
  end
@@ -13,7 +14,7 @@ module HQMF
13
14
  HQMF2::Document.new(xml_contents).to_model
14
15
  end
15
16
 
16
- def parse_fileds(xml_contents)
17
+ def parse_fields(xml_contents)
17
18
  result = {}
18
19
  doc = HQMF2::Document.parse(xml_contents)
19
20
  type = doc.at_xpath('/cda:QualityMeasureDocument/cda:code/@code').value
@@ -39,6 +40,28 @@ module HQMF
39
40
 
40
41
  end
41
42
 
43
+ # HQMF v2 Parser for measures that use CQL for measure logic
44
+ class V2CQLParser < V2Parser
45
+
46
+ def parse(xml_contents, codes=nil)
47
+ HQMF::Counter.instance.reset()
48
+ HQMF2CQL::Document.new(xml_contents).to_model
49
+ end
50
+
51
+ def self.valid?(xml_contents)
52
+ doc = HQMF2::Document.parse(xml_contents)
53
+ hqmf2 = !doc.at_xpath("/cda:QualityMeasureDocument/cda:typeId[@root='2.16.840.1.113883.1.3' and @extension='POQM_HD000001UV02']").nil?
54
+ cql = !doc.at_xpath("/cda:QualityMeasureDocument/cda:relatedDocument/cda:expressionDocument/cda:text[@mediaType='application/cql']").nil?
55
+ if !cql
56
+ # The media type changed for MAT version 5.3
57
+ cql = !doc.at_xpath("/cda:QualityMeasureDocument/cda:relatedDocument/cda:expressionDocument/cda:text[@mediaType='text/cql']").nil?
58
+ end
59
+ hqmf2 && cql
60
+ end
61
+
62
+ end
63
+
64
+ # HQMF v1 Parser for measures that use QDM for measure logic
42
65
  class V1Parser
43
66
 
44
67
  def parse(xml_contents, codes=nil)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: health-data-standards
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.7.0
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - The MITRE Corporation
@@ -304,6 +304,8 @@ files:
304
304
  - lib/health-data-standards/import/hdata/metadata_importer.rb
305
305
  - lib/health-data-standards/import/provider_import_utils.rb
306
306
  - lib/health-data-standards/models/address.rb
307
+ - lib/health-data-standards/models/admission_source.rb
308
+ - lib/health-data-standards/models/adverse_event.rb
307
309
  - lib/health-data-standards/models/allergy.rb
308
310
  - lib/health-data-standards/models/assessment.rb
309
311
  - lib/health-data-standards/models/care_experience.rb
@@ -311,6 +313,7 @@ files:
311
313
  - lib/health-data-standards/models/cda_identifier.rb
312
314
  - lib/health-data-standards/models/coded_result_value.rb
313
315
  - lib/health-data-standards/models/communication.rb
316
+ - lib/health-data-standards/models/component.rb
314
317
  - lib/health-data-standards/models/condition.rb
315
318
  - lib/health-data-standards/models/cqm/aggregate_objects.rb
316
319
  - lib/health-data-standards/models/cqm/bundle.rb
@@ -373,6 +376,7 @@ files:
373
376
  - lib/health-data-standards/util/hqmf_template_helper.rb
374
377
  - lib/health-data-standards/util/hqmf_template_oid_map.json
375
378
  - lib/health-data-standards/util/hqmfr2_template_oid_map.json
379
+ - lib/health-data-standards/util/hqmfr2cql_template_oid_map.json
376
380
  - lib/health-data-standards/util/nlm_helper.rb
377
381
  - lib/health-data-standards/util/vs_api.rb
378
382
  - lib/health-data-standards/validate/base_validator.rb
@@ -467,6 +471,12 @@ files:
467
471
  - lib/hqmf-parser/converter/pass1/simple_restriction.rb
468
472
  - lib/hqmf-parser/converter/pass2/comparison_converter.rb
469
473
  - lib/hqmf-parser/converter/pass2/operator_converter.rb
474
+ - lib/hqmf-parser/cql/data_criteria.rb
475
+ - lib/hqmf-parser/cql/data_criteria_helpers/dc_definition_from_template_or_type_extract.rb
476
+ - lib/hqmf-parser/cql/data_criteria_helpers/dc_post_processing.rb
477
+ - lib/hqmf-parser/cql/document.rb
478
+ - lib/hqmf-parser/cql/document_helpers/doc_population_helper.rb
479
+ - lib/hqmf-parser/cql/value_set_helper.rb
470
480
  - lib/hqmf-parser/parser.rb
471
481
  - lib/util/counter.rb
472
482
  - resources/schema/infrastructure/cda/CDA_SDTC.xsd