health-data-standards 3.7.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
@@ -66,13 +66,13 @@ module HealthDataStandards
66
66
  def get_valueset(oid, options = {}, &block)
67
67
  version = options.fetch(:version, nil)
68
68
  include_draft = options.fetch(:include_draft, false)
69
- profile = options.fetch(:profile, DEFAULT_PROFILE)
69
+ profile = options.fetch(:profile, nil)
70
70
  effective_date = options.fetch(:effective_date, nil)
71
71
  program_name = options.fetch(:program, nil)
72
72
  params = { id: oid, ticket: get_ticket }
73
73
  params[:version] = version if version
74
- params[:includeDraft] = 'yes' if include_draft
75
- params[:profile] = profile if include_draft
74
+ params[:includeDraft] = 'yes' if profile && include_draft
75
+ params[:profile] = profile if profile
76
76
  params[:effectiveDate] = effective_date if effective_date
77
77
  params[:programType] = program_name if program_name
78
78
  begin
@@ -157,7 +157,7 @@
157
157
  "status":"",
158
158
  "sub_category":"payer",
159
159
  "hard_status":false,
160
- "patient_api_function":"",
160
+ "patient_api_function":"allProblems",
161
161
  "property":"payer",
162
162
  "not_supported":false},
163
163
  "patient_characteristic_race":{
@@ -1024,5 +1024,23 @@
1024
1024
  "sub_category":"",
1025
1025
  "hard_status":false,
1026
1026
  "patient_api_function":"assessments",
1027
+ "not_supported":false},
1028
+ "adverse_event":{
1029
+ "title":"adverse event",
1030
+ "category":"adverse_events",
1031
+ "definition":"adverse_event",
1032
+ "status":"",
1033
+ "sub_category":"",
1034
+ "hard_status":false,
1035
+ "patient_api_function":"adverseEvents",
1036
+ "not_supported":false},
1037
+ "allergy_intolerance":{
1038
+ "title":"allergy/intolerance",
1039
+ "category":"allergies_intolerances",
1040
+ "definition":"allergy_intolerance",
1041
+ "status":"",
1042
+ "sub_category":"",
1043
+ "hard_status":false,
1044
+ "patient_api_function":"allergies",
1027
1045
  "not_supported":false}
1028
1046
  }
@@ -27,19 +27,27 @@ module HQMF
27
27
  FIELDS = {'ABATEMENT_DATETIME' => {title:'Abatement Datetime', coded_entry_method: :end_date, field_type: :timestamp},
28
28
  'ACTIVE_DATETIME' => {title:'Active Date/Time', coded_entry_method: :active_date_time, field_type: :timestamp},
29
29
  'ADMISSION_DATETIME' => {title:'Admission Date/Time', coded_entry_method: :admit_time, code: '399423000', code_system:'2.16.840.1.113883.6.96', field_type: :timestamp},
30
+ # QDM 5.0 addition. This is the same as FACILITY_LOCATION.
31
+ # TODO: (LDY 10/5/2016) this is a new attribute from QDM 5.0. We do not yet have the code or template_id for this. This should be updated when we do.
32
+ 'ADMISSION_SOURCE' => {title:'Admission Source', coded_entry_method: :admission_source, field_type: :value},
30
33
  'ANATOMICAL_APPROACH_SITE' => {title:'Anatomical Approach Site', coded_entry_method: :anatomical_approach, field_type: :value},
31
34
  'ANATOMICAL_LOCATION_SITE' => {title:'Anatomical Location Site', coded_entry_method: :anatomical_location, field_type: :value},
32
35
  'ANATOMICAL_STRUCTURE' => {title:'Anatomical Structure', coded_entry_method: :anatomical_structure, code: '91723000', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1000.2', field_type: :value},
33
36
  'CAUSE' => {title:'Cause', coded_entry_method: :cause_of_death, code: '42752001', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1017.2', field_type: :value},
37
+ # TODO: Determine actual code and code_system for component attribute
38
+ 'COMPONENT' => {title: 'Component', coded_entry_method: :components, field_type: :value},
34
39
  'CUMULATIVE_MEDICATION_DURATION' => {title:'Cumulative Medication Duration', coded_entry_method: :cumulative_medication_duration, code: '261773006', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1001.3', field_type: :value},
35
40
  # MISSING Date - The date that the patient passed away. - Patient Characteristic Expired
36
41
  'DIAGNOSIS' => {title:'Diagnosis', coded_entry_method: :diagnosis, field_type: :value},
37
42
  'DISCHARGE_DATETIME' => {title:'Discharge Date/Time', coded_entry_method: :discharge_time, code: '442864001', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1025.1', field_type: :timestamp},
38
- 'DISCHARGE_STATUS' => {title:'Discharge Status', coded_entry_method: :discharge_disposition, code: '309039003', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1003.2', field_type: :value},
39
- 'DOSE' => {title:'Dose', coded_entry_method: :dose, code: '398232005', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1004.1', field_type: :value},
43
+ # TODO: (LDY 10/5/2016) this changed from "discharge status" to "discharge disposition". likely there is a code and template id change necessary. these are not yet known.
44
+ 'DISCHARGE_STATUS' => {title:'Discharge Disposition', coded_entry_method: :discharge_disposition, code: '309039003', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1003.2', field_type: :value},
45
+ # TODO: (LDY 10/4/2016) this changed from "dose" to "dosage". it's possible that there's another code associated with this. this code was not available at the time of this change.
46
+ 'DOSE' => {title:'Dosage', coded_entry_method: :dose, code: '398232005', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1004.1', field_type: :value},
40
47
  'FACILITY_LOCATION' => {title:'Facility Location', coded_entry_method: :facility, code: 'SDLOC', field_type: :value},
41
- 'FACILITY_LOCATION_ARRIVAL_DATETIME' => {title:'Facility Location Arrival Date/Time', coded_entry_method: :facility_arrival, code: 'SDLOC_ARRIVAL', field_type: :nested_timestamp},
42
- 'FACILITY_LOCATION_DEPARTURE_DATETIME' => {title:'Facility Location Departure Date/Time', coded_entry_method: :facility_departure, code: 'SDLOC_DEPARTURE', field_type: :nested_timestamp},
48
+ # TODO: (LDY 10/5/2016) this changed from 'facility arrival/departure' to 'location period'. likely there is a code and template id change necessary. these are not yet known.
49
+ 'FACILITY_LOCATION_ARRIVAL_DATETIME' => {title:'Location Period Start Date/Time', coded_entry_method: :facility_arrival, code: 'SDLOC_ARRIVAL', field_type: :nested_timestamp},
50
+ 'FACILITY_LOCATION_DEPARTURE_DATETIME' => {title:'Location Period End Date/Time', coded_entry_method: :facility_departure, code: 'SDLOC_DEPARTURE', field_type: :nested_timestamp},
43
51
  'FREQUENCY' => {title:'Frequency', coded_entry_method: :administration_timing, code: '307430002', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1006.1', field_type: :value},
44
52
  'HEALTH_RECORD_FIELD' => {title: 'Health Record Field', coded_entry_method: :health_record_field, code: '395676008', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.10.20.28.3.102:2014-11-24', field_type: :value},
45
53
  'INCISION_DATETIME' => {title:'Incision Date/Time', coded_entry_method: :incision_time, code: '34896006', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.10.20.24.3.89', field_type: :timestamp},
@@ -59,13 +67,15 @@ module HQMF
59
67
  'REACTION'=> {title:'Reaction', coded_entry_method: :reaction, code: '263851003', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.10.20.24.3.85', field_type: :value},
60
68
  'REASON' => {title:'Reason', coded_entry_method: :reason, code: '410666004', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.10.20.24.3.88', field_type: :value},
61
69
  'RECORDED_DATETIME' => {title:'Recorded Datetime', coded_entry_method: :start_date, field_type: :timestamp},
62
- 'REFERENCE_RANGE_HIGH' => {title:'Reference Range High', coded_entry_method: :reference_range_high, field_type: :value},
63
- 'REFERENCE_RANGE_LOW' => {title:'Reference Range Low', coded_entry_method: :reference_range_low, field_type: :value},
70
+ 'REFERENCE_RANGE_HIGH' => {title:'Reference Range - High', coded_entry_method: :reference_range_high, field_type: :value},
71
+ 'REFERENCE_RANGE_LOW' => {title:'Reference Range - Low', coded_entry_method: :reference_range_low, field_type: :value},
64
72
  'REFILLS' => {title:'Refills', coded_entry_method: :refills, field_type: :value},
65
73
  'RELATED_TO' => {title:'Related To', coded_entry_method: :related_to, code: 'REL', codeSystem: '2.16.840.1.113883.1.11.11603', field_type: :value},
66
74
  'RELATIONSHIP' => {title:'Relationship', coded_entry_method: :relationship_to_patient, field_type: :value},
67
75
  'REMOVAL_DATETIME' => {title:'Removal Date/Time', coded_entry_method: :removal_time, code: '118292001', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1032.1', field_type: :timestamp},
68
76
  # Result isn't encoded
77
+ # TODO: (LDY 10/4/2016) RESULT_DATETIME is a new attribute in QDM 5.0. We do not yet have codes/template information for this.
78
+ 'RESULT_DATETIME' => {title:'Result Date/Time', coded_entry_method: :result_date_time, field_type: :timestamp},
69
79
  'ROUTE' => {title:'Route', coded_entry_method: :route, code: '263513008', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1020.2', field_type: :value},
70
80
  'SEVERITY' => {title:'Severity', coded_entry_method: :severity, code: 'SEV', code_system:'2.16.840.1.113883.5.4', template_id: '2.16.840.1.113883.10.20.22.4.8', field_type: :value},
71
81
  'SIGNED_DATETIME' => {title:'Signed Date/Time', coded_entry_method: :signed_date_time, field_type: :timestamp},
@@ -73,11 +83,14 @@ module HQMF
73
83
  # STATUS is referenced in the code as `qdm_status` because entry/Record already has a `status`/`status_code` field which has a different meaning
74
84
  'STATUS' => {title: 'Status', coded_entry_method: :qdm_status, code: '33999-4', code_system:'2.16.840.1.113883.6.1', field_type: :value},
75
85
  'STOP_DATETIME' => {title:'Stop Date/Time', coded_entry_method: :end_date, code: '397898000', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.1026.1', field_type: :timestamp},
86
+ # TODO: (LDY 10/4/2016) SUPPLY is a new attribute in QDM 5.0. We do not yet have codes/template information for this.
87
+ 'SUPPLY' => {title:'Supply', coded_entry_method: :supply, field_type: :value},
76
88
  'TARGET_OUTCOME' => {title:'Target Outcome', coded_entry_method: :target_outcome, code: '385676005', code_system:'2.16.840.1.113883.6.96', template_id: '', field_type: :value},
77
89
  # MISSING Time - The time that the patient passed away
78
90
 
79
91
  # Custom field values
80
- 'FLFS' => {title:'Fulfills', coded_entry_method: :fulfills, code: 'FLFS', field_type: :reference},
92
+ # QDM 5.3 Update: "Related To" replaces Fulfills. We are keeping the fulfills code and only make changes to the UI.
93
+ 'FLFS' => {title:'Related To', coded_entry_method: :fulfills, code: 'FLFS', field_type: :reference},
81
94
  'SOURCE' => {title:'Source', coded_entry_method: :source, code: '260753009', code_system:'2.16.840.1.113883.6.96', template_id: '2.16.840.1.113883.3.560.1.2001.2', field_type: :value},
82
95
  'TRANSFER_FROM' => {title:'Transfer From', coded_entry_method: :transfer_from, code: 'TRANSFER_FROM', template_id: '2.16.840.1.113883.10.20.24.3.81', field_type: :value},
83
96
  'TRANSFER_FROM_DATETIME' => {title:'Transfer From Date/Time', coded_entry_method: :transfer_from_time, code: 'ORG_TIME', template_id: '2.16.840.1.113883.10.20.24.3.81', field_type: :nested_timestamp},
@@ -431,6 +444,17 @@ module HQMF
431
444
  when 'ACT'
432
445
  # Currentlty forcing this as the SimpleXML reresentation contains a fulfills for these types
433
446
  value = HQMF::TypedReference.new(json["reference"], 'FLFS', '')
447
+ when 'CMP'
448
+ # For ResultComponent, json will have a value or "" for referenceRangeLow_value, for Component, will be nil
449
+ if json['referenceRangeLow_value']
450
+ value = ResultComponent.new(json)
451
+ else
452
+ value = Component.new(json)
453
+ end
454
+ when 'COL'
455
+ value = HQMF::Collection.from_json(json)
456
+ when 'FAC'
457
+ value = Facility.new(json)
434
458
  else
435
459
  raise "Unknown value type [#{type}]"
436
460
  end
@@ -9,7 +9,7 @@ module HQMF
9
9
 
10
10
  include HQMF::Conversion::Utilities
11
11
 
12
- attr_reader :id, :title, :description, :measure_period, :attributes, :populations, :source_data_criteria, :hqmf_id, :hqmf_set_id, :hqmf_version_number, :cms_id
12
+ attr_reader :id, :title, :description, :measure_period, :attributes, :populations, :source_data_criteria, :hqmf_id, :hqmf_set_id, :hqmf_version_number, :cms_id, :populations_cql_map, :cql_measure_library, :observations
13
13
 
14
14
  # Create a new HQMF::Document which can be converted to JavaScript
15
15
  # @param [String] id
@@ -24,7 +24,7 @@ module HQMF
24
24
  # @param [Array#Attribute] attributes
25
25
  # @param [Array#Hash] populations
26
26
  # @param [Range] measure_period
27
- def initialize(id, hqmf_id, hqmf_set_id, hqmf_version_number, cms_id, title, description, population_criteria, data_criteria, source_data_criteria, attributes, measure_period, populations=nil)
27
+ def initialize(id, hqmf_id, hqmf_set_id, hqmf_version_number, cms_id, title, description, population_criteria, data_criteria, source_data_criteria, attributes, measure_period, populations=nil, populations_cql_map=nil, cql_measure_library=nil, observations=nil)
28
28
  @id = id
29
29
  @hqmf_id = hqmf_id
30
30
  @hqmf_set_id = hqmf_set_id
@@ -46,6 +46,9 @@ module HQMF
46
46
  }
47
47
  ]
48
48
  @measure_period = measure_period
49
+ @populations_cql_map = populations_cql_map
50
+ @cql_measure_library = cql_measure_library
51
+ @observations = observations
49
52
  end
50
53
 
51
54
  # Create a new HQMF::Document from a JSON hash keyed with symbols
@@ -104,6 +107,12 @@ module HQMF
104
107
 
105
108
  json[:populations] = @populations
106
109
 
110
+ json[:populations_cql_map] = @populations_cql_map
111
+
112
+ json[:observations] = @observations
113
+
114
+ json[:cql_measure_library] = @cql_measure_library
115
+
107
116
  json[:measure_period] = @measure_period.to_json
108
117
 
109
118
  json
@@ -155,7 +155,6 @@ module HQMF
155
155
  class Coded
156
156
  include HQMF::Conversion::Utilities
157
157
  attr_reader :type, :system, :code, :code_list_id, :title, :null_flavor, :original_text
158
-
159
158
  # Create a new HQMF::Coded
160
159
  # @param [String] type
161
160
  # @param [String] system
@@ -285,9 +284,7 @@ module HQMF
285
284
 
286
285
  def self.from_json(json)
287
286
  type = json["type"] if json["type"]
288
-
289
- value = HQMF::DataCriteria.convert_value(json["value"]) if json["value"]
290
-
287
+ value = HQMF::DataCriteria.convert_value(json["value"]) if json["value"]
291
288
  HQMF::SubsetOperator.new(type,value)
292
289
  end
293
290
 
@@ -428,4 +425,33 @@ module HQMF
428
425
  end
429
426
  end
430
427
 
431
- end
428
+ class Collection
429
+ include HQMF::Conversion::Utilities
430
+ attr_accessor :type, :values
431
+
432
+ def initialize(type, values)
433
+ @type = type || 'COL'
434
+ @values = values || []
435
+ end
436
+
437
+ def self.from_json(json)
438
+ json = json.with_indifferent_access
439
+ values = []
440
+ type = json['type']
441
+ json['values'].each { |value| values.push(HQMF::DataCriteria.convert_value(value))}
442
+ HQMF::Collection.new(type, values)
443
+ end
444
+
445
+ def to_json
446
+ json = build_hash(self, [:type])
447
+ json[:values] = []
448
+ @values.each {|value| json[:values] << value.to_json }
449
+ json
450
+ end
451
+
452
+ def ==(other)
453
+ check_equality(self,other)
454
+ end
455
+ end
456
+
457
+ end
data/lib/hqmf-parser.rb CHANGED
@@ -36,6 +36,13 @@ require_relative 'hqmf-parser/2.0/data_criteria'
36
36
  require_relative 'hqmf-parser/2.0/population_criteria'
37
37
  require_relative 'hqmf-parser/2.0/precondition'
38
38
 
39
+ require_relative 'hqmf-parser/cql/document'
40
+ require_relative 'hqmf-parser/cql/data_criteria_helpers/dc_definition_from_template_or_type_extract'
41
+ require_relative 'hqmf-parser/cql/document_helpers/doc_population_helper'
42
+ require_relative 'hqmf-parser/cql/data_criteria_helpers/dc_post_processing'
43
+ require_relative 'hqmf-parser/cql/data_criteria'
44
+ require_relative 'hqmf-parser/cql/value_set_helper'
45
+
39
46
  require_relative 'hqmf-model/data_criteria.rb'
40
47
  require_relative 'hqmf-model/document.rb'
41
48
  require_relative 'hqmf-model/population_criteria.rb'
@@ -148,7 +148,10 @@ module HQMF2
148
148
  # not give us the correct SDC data when we are trying to recreate since we are looping back through
149
149
  # the same data criteria before it has finished processing: See: DocUtilities.extract_source_data_criteria
150
150
  def clone
151
- other = DataCriteria.new(@entry, @data_criteria_references, @occurrences_map)
151
+ # Using 'self.class.new' in order to allow this DataCriteria class as
152
+ # well as any future new extending DataCriteria classes to use this clone
153
+ # function.
154
+ other = self.class.new(@entry, @data_criteria_references, @occurrences_map)
152
155
  other.instance_variable_set(:@id, @id)
153
156
  other.instance_variable_set(:@original_id, @original_id)
154
157
  other.instance_variable_set(:@property, @property)
@@ -67,7 +67,7 @@ module HQMF2
67
67
 
68
68
  # Extract the negation (and the negation_code_list_id if appropriate)
69
69
  def extract_negation
70
- negation = (attr_val('./*/@actionNegationInd') == 'true')
70
+ negation = (attr_val('./*/@actionNegationInd').to_s.downcase == 'true')
71
71
  negation_code_list_id = nil
72
72
  if negation
73
73
  res = @entry.at_xpath('./*/cda:outboundRelationship/*/cda:code[@code="410666004"]/../cda:value/@valueSet',
@@ -191,8 +191,8 @@ module HQMF2
191
191
  value_obj = nil
192
192
  value_obj = handle_attribute_value(attribute, value) if attribute.at_xpath('./cda:value', NAMESPACES)
193
193
 
194
- # Handle the cms_id
195
- @cms_id = "CMS#{value}v#{@hqmf_version_number.to_i}" if name.include? 'eMeasure Identifier'
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.include? 'eMeasure Identifier') || (name.include? 'eCQM Identifier')
196
196
 
197
197
  HQMF::Attribute.new(id, code, value, nil, name, id_obj, code_obj, value_obj)
198
198
  end
@@ -283,7 +283,7 @@ module HQMF2
283
283
  end
284
284
  end
285
285
  end
286
-
286
+
287
287
  # For specific occurrence data criteria, make sure the source data criteria reference points
288
288
  # to the correct source data criteria.
289
289
  def handle_specific_source_data_criteria_reference(criteria)
@@ -299,6 +299,6 @@ module HQMF2
299
299
  original_sdc.instance_variable_set(:@code_list_id, criteria.code_list_id)
300
300
  end
301
301
  end
302
-
302
+
303
303
  end
304
304
  end
@@ -51,7 +51,6 @@ module HQMF2
51
51
 
52
52
  population['OBSERV'] = 'OBSERV' if has_observation
53
53
  @populations << population
54
-
55
54
  handle_stratifications(population_def, number_of_populations, population, id_def, population_index)
56
55
  end
57
56
 
@@ -116,7 +115,6 @@ module HQMF2
116
115
  # Skip this Stratification if any precondition doesn't contain any preconditions
117
116
  next unless PopulationCriteria.new(criteria_def, @document, @id_generator)
118
117
  .preconditions.all? { |prcn| prcn.preconditions.length > 0 }
119
-
120
118
  index = number_of_populations + ((population_index - 1) * criteria_def.xpath('./*/cda:precondition').length) +
121
119
  criteria_def_index
122
120
  criteria_id = HQMF::PopulationCriteria::STRAT
@@ -34,7 +34,7 @@ module HQMF2
34
34
  else
35
35
  # Extract the data criteria this population references
36
36
  dc = handle_observation_criteria
37
- @preconditions = [Precondition.new(id_generator.next_id, nil, nil, false, HQMF2::Reference.new(dc.id))]
37
+ @preconditions = [Precondition.new(id_generator.next_id, nil, nil, false, HQMF2::Reference.new(dc.id))] if dc
38
38
  end
39
39
  end
40
40
 
@@ -76,7 +76,7 @@ module HQMF2
76
76
  dc = parse_parts_to_dc(parts)
77
77
  @doc.add_data_criteria(dc)
78
78
  # Update reference_ids with any newly referenced data criteria
79
- dc.children_criteria.each { |cc| @doc.add_reference_id(cc) } unless dc.children_criteria.nil?
79
+ dc.children_criteria.each { |cc| @doc.add_reference_id(cc) } unless dc&.children_criteria.nil?
80
80
  dc
81
81
  end
82
82
 
@@ -90,7 +90,10 @@ module HQMF2
90
90
  '2.16.840.1.113883.10.20.28.3.115' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role/cda:playingMaterial[@classCode='MMAT']/cda:code", result_path: nil },
91
91
  '2.16.840.1.113883.10.20.28.3.116' => { valueset_path: './*/cda:value', result_path: nil },
92
92
  '2.16.840.1.113883.10.20.28.3.117' => { valueset_path: './*/cda:code', result_path: './*/cda:value' },
93
- '2.16.840.1.113883.10.20.28.3.118' => { valueset_path: './*/cda:code', result_path: nil }
93
+ '2.16.840.1.113883.10.20.28.3.118' => { valueset_path: './*/cda:code', result_path: nil },
94
+ '2.16.840.1.113883.10.20.28.3.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.3.120' => { valueset_path: "./*/cda:participation[@typeCode='CSM']/cda:role[@classCode='MANU']/cda:playingEntity[@classCode='MMAT']/cda:code", result_path: nil }
96
+
94
97
  }
95
98
  # rubocop:enable Metrics/LineLength
96
99
 
@@ -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