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
@@ -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