cqm-reports 3.1.2 → 3.1.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9d999a13b64a6a37c2ad61c53c5325162a31eb948e95556a421474eb22cca735
4
- data.tar.gz: f4d69a6e256859b77ea3da62a332f48e358ca4a3589d1d08bbf57889b6977f20
3
+ metadata.gz: b9895c3e39093e4e3b01d4ab490bd91db2b881b5ff9870b8de0c6369fc792601
4
+ data.tar.gz: ff99962ce100bd5f73bb5bc73299d55bd4b2ed1185de971996292b713baf8060
5
5
  SHA512:
6
- metadata.gz: 99e4bb97fe1ecbcb0a4f336b0e2e6d0f5d6d85dd05f539a4ea71ad28fbb091b9cf78c2c23605f0e1344c736b4fdc38e0cda8bc12efb2ca52f4f393f158f979c6
7
- data.tar.gz: 5c0b2e94fa51e623b071aecfc34537bc16efed7069d1bac433b178491462679fde863b24b6ee7cce9191b27e320d24fdfa004b0077f6e5be8492524f23cc89e5
6
+ metadata.gz: b2b306d0fc7131de73ebad247d9bdb6d1032fb18d25397e55ddf0926c53002f141bed46facc8e37926b38b27a57f3d2e8d11d6071403502a4eeca751e2839e20
7
+ data.tar.gz: 27b7e2a699c9f3c897ecbeda9a06cba45dd21e87fc5ddb5315fa07acddd659e6ef9654d9fc4bdf89d28d784ffdecfb1cc907902c228635ffcd94a1971b499acc
@@ -2,7 +2,7 @@
2
2
  <div class="div-table-body">
3
3
  {{#dataElementCodes}}
4
4
  <div class="div-head-row">
5
- <div class="div-table-head--no-border"><span class="criteria-heading">{{{code_system_name}}}:</span> {{code}}</div>
5
+ <div class="div-table-head--no-border"><span class="criteria-heading">{{{code_system_name}}}:</span> {{code}}{{{code_description}}}</div>
6
6
  </div>
7
7
  {{/dataElementCodes}}
8
8
  </div>
@@ -18,7 +18,7 @@
18
18
  <div class="div-table-head patient_narr_th panel-heading"><span class="td_label">Patient</span></div>
19
19
  <div class="div-table-head">{{given_name}} {{familyName}}</div>
20
20
  <div class="div-table-head patient_narr_th panel-heading"><span class="td_label">Sex</span></div>
21
- <div class="div-table-head">{{{gender}}}</div>
21
+ <div class="div-table-head">{{{gender}}}{{#demographic_code_description}}gender{{/demographic_code_description}}</div>
22
22
  </div>
23
23
  <div class="div-head-row patient_narr_tr panel panel-default patient-details">
24
24
  <div class="div-table-head patient_narr_th panel-heading"><span class="td_label">Date of birth</span></div>
@@ -28,13 +28,13 @@
28
28
  </div>
29
29
  <div class="div-head-row patient_narr_tr panel panel-default patient-details">
30
30
  <div class="div-table-head patient_narr_th panel-heading"><span class="td_label">Race</span></div>
31
- <div class="div-table-head">{{{race}}}</div>
31
+ <div class="div-table-head">{{{race}}}{{#demographic_code_description}}race{{/demographic_code_description}}</div>
32
32
  <div class="div-table-head patient_narr_th panel-heading"><span class="td_label">Ethnicity</span></div>
33
- <div class="div-table-head">{{{ethnic_group}}}</div>
33
+ <div class="div-table-head">{{{ethnic_group}}}{{#demographic_code_description}}ethnic_group{{/demographic_code_description}}</div>
34
34
  </div>
35
35
  <div class="div-head-row patient_narr_tr panel panel-default patient-details">
36
36
  <div class="div-table-head patient_narr_th panel-heading"><span class="td_label">Insurance Providers</span></div>
37
- <div class="div-table-head">{{{payer}}}</div>
37
+ <div class="div-table-head">{{{payer}}}{{#demographic_code_description}}payer{{/demographic_code_description}}</div>
38
38
  <div class="div-table-head patient_narr_th panel-heading"><span class="td_label">Patient IDs</span></div>
39
39
  <div class="div-table-head">{{{mrn}}} Cypress</div>
40
40
  </div>
@@ -91,13 +91,31 @@ class QdmPatient < Mustache
91
91
  end
92
92
 
93
93
  def code_for_element(element)
94
- "#{element['code']} (#{HQMF::Util::CodeSystemHelper.code_system_for(element['system'])})"
94
+ "#{element['code']} (#{HQMF::Util::CodeSystemHelper.code_system_for(element['system'])})#{code_description(element)}"
95
95
  end
96
96
 
97
97
  def code_system_name
98
98
  HQMF::Util::CodeSystemHelper.code_system_for(self['system'])
99
99
  end
100
100
 
101
+ def code_description(element = self)
102
+ has_descriptions = @patient.respond_to?(:code_description_hash) && !@patient.code_description_hash.empty?
103
+ # mongo keys cannot contain '.', so replace all '.', key example: '21112-8:2_16_840_1_113883_6_1'
104
+ return " - #{@patient.code_description_hash["#{element['code']}:#{element['system']}".tr('.', '_')]}" if has_descriptions
105
+ # no code description available
106
+ ""
107
+ end
108
+
109
+ def demographic_code_description(code)
110
+ # only have code, don't need code system
111
+ has_descriptions = code && @patient.respond_to?(:code_description_hash) && !@patient.code_description_hash.empty?
112
+ if has_descriptions
113
+ key = @patient.code_description_hash.keys.detect { |k| k.starts_with?("#{send(code)}:") }
114
+ return " - #{@patient.code_description_hash[key]}"
115
+ end
116
+ ""
117
+ end
118
+
101
119
  def result_string
102
120
  return unit_string if self['value']
103
121
  return code_code_system_string if self['code']
@@ -41,5 +41,9 @@
41
41
  <!-- QDM Attribute: Components -->
42
42
  {{> qrda_templates/template_partials/_component}}
43
43
  {{/components}}
44
+ {{#encounter_id}}
45
+ <!-- QDM Attribute: relatedTo -->
46
+ {{> qrda_templates/template_partials/_related_to}}
47
+ {{/encounter_id}}
44
48
  </observation>
45
49
  </entry>
@@ -48,5 +48,9 @@
48
48
  <!-- QDM Attribute: Components -->
49
49
  {{> qrda_templates/template_partials/_component}}
50
50
  {{/components}}
51
+ {{#encounter_id}}
52
+ <!-- QDM Attribute: relatedTo -->
53
+ {{> qrda_templates/template_partials/_related_to}}
54
+ {{/encounter_id}}
51
55
  </observation>
52
56
  </entry>
@@ -1,7 +1,7 @@
1
1
  module QRDA
2
2
  module Cat1
3
3
  module DemographicsImporter
4
- def get_demographics(patient, doc)
4
+ def get_demographics(patient, doc, codes)
5
5
  patient_role_element = doc.at_xpath('/cda:ClinicalDocument/cda:recordTarget/cda:patientRole')
6
6
  patient_element = patient_role_element.at_xpath('./cda:patient')
7
7
  patient.givenNames = [patient_element.at_xpath('cda:name/cda:given').text]
@@ -9,28 +9,29 @@ module QRDA
9
9
  patient.qdmPatient.birthDatetime = DateTime.parse(patient_element.at_xpath('cda:birthTime')['value'])
10
10
  pcbd = QDM::PatientCharacteristicBirthdate.new
11
11
  pcbd.birthDatetime = patient.qdmPatient.birthDatetime
12
- pcbd.dataElementCodes = [{ code: '21112-8', system: '2.16.840.1.113883.6.1' }]
12
+ pcbd.dataElementCodes = [QDM::Code.new('21112-8', '2.16.840.1.113883.6.1')]
13
+ codes.add("21112-8:2.16.840.1.113883.6.1")
13
14
  patient.qdmPatient.dataElements << pcbd
14
15
 
15
16
  pcs = QDM::PatientCharacteristicSex.new
16
17
  code_element = patient_element.at_xpath('cda:administrativeGenderCode')
17
- pcs.dataElementCodes = [code_if_present(code_element)]
18
+ pcs.dataElementCodes = [code_if_present(code_element, codes)]
18
19
  patient.qdmPatient.dataElements << pcs unless pcs.dataElementCodes.compact.blank?
19
20
 
20
21
  pcr = QDM::PatientCharacteristicRace.new
21
22
  code_element = patient_element.at_xpath('cda:raceCode')
22
- pcr.dataElementCodes = [code_if_present(code_element)]
23
+ pcr.dataElementCodes = [code_if_present(code_element, codes)]
23
24
  patient.qdmPatient.dataElements << pcr unless pcr.dataElementCodes.compact.blank?
24
25
 
25
26
  pce = QDM::PatientCharacteristicEthnicity.new
26
27
  code_element = patient_element.at_xpath('cda:ethnicGroupCode')
27
- pce.dataElementCodes = [code_if_present(code_element)]
28
+ pce.dataElementCodes = [code_if_present(code_element, codes)]
28
29
  patient.qdmPatient.dataElements << pce unless pce.dataElementCodes.compact.blank?
29
30
  end
30
31
 
31
- def code_if_present(code_element)
32
+ def code_if_present(code_element, codes)
32
33
  return unless code_element && code_element['code'] && code_element['codeSystem']
33
-
34
+ codes.add("#{code_element['code']}:#{code_element['codeSystem']}")
34
35
  QDM::Code.new(code_element['code'], code_element['codeSystem'])
35
36
  end
36
37
  end
@@ -1,7 +1,7 @@
1
1
  module QRDA
2
2
  module Cat1
3
3
  class SectionImporter
4
- attr_accessor :check_for_usable, :status_xpath, :code_xpath, :warnings
4
+ attr_accessor :check_for_usable, :status_xpath, :code_xpath, :warnings, :codes, :codes_modifiers
5
5
 
6
6
  def initialize(entry_finder)
7
7
  @entry_finder = entry_finder
@@ -10,6 +10,8 @@ module QRDA
10
10
  @check_for_usable = true
11
11
  @entry_class = QDM::DataElement
12
12
  @warnings = []
13
+ @codes = Set.new
14
+ @codes_modifiers = {}
13
15
  end
14
16
 
15
17
  # Traverses an HL7 CDA document passed in and creates an Array of Entry
@@ -81,6 +83,7 @@ module QRDA
81
83
 
82
84
  def code_if_present(code_element)
83
85
  return unless code_element && code_element['code'] && code_element['codeSystem']
86
+ @codes.add("#{code_element['code']}:#{code_element['codeSystem']}")
84
87
  QDM::Code.new(code_element['code'], code_element['codeSystem'])
85
88
  end
86
89
 
@@ -94,7 +97,9 @@ module QRDA
94
97
  end
95
98
 
96
99
  def extract_interval(parent_element, interval_xpath)
97
- return nil unless parent_element.at_xpath(interval_xpath)
100
+ # nil if the time interval does not exist
101
+ return nil unless time_interval_exists(parent_element, interval_xpath)
102
+
98
103
  if parent_element.at_xpath("#{interval_xpath}/@value")
99
104
  low_time = DateTime.parse(parent_element.at_xpath(interval_xpath)['value'])
100
105
  high_time = DateTime.parse(parent_element.at_xpath(interval_xpath)['value'])
@@ -131,6 +136,14 @@ module QRDA
131
136
  QDM::Interval.new(low_time, high_time).shift_dates(0)
132
137
  end
133
138
 
139
+ def time_interval_exists(parent_element, interval_xpath)
140
+ # false if the time interval does not exist
141
+ return false unless parent_element.at_xpath(interval_xpath)
142
+ # false if the time element exists but has a null Flavor
143
+ return false if parent_element.at_xpath(interval_xpath)['nullFlavor']
144
+ true
145
+ end
146
+
134
147
  def extract_time(parent_element, datetime_xpath)
135
148
  DateTime.parse(parent_element.at_xpath(datetime_xpath)['value']) if parent_element.at_xpath("#{datetime_xpath}/@value")
136
149
  end
@@ -145,6 +158,7 @@ module QRDA
145
158
  # If a Direct Reference Code isn't found, return nil
146
159
  return nil unless key
147
160
  # If a Direct Reference Code is found, return that code
161
+ @codes.add("#{key}:#{value[:code_system]}")
148
162
  QDM::Code.new(key, value[:code_system])
149
163
  end
150
164
 
@@ -258,7 +272,7 @@ module QRDA
258
272
  participant_element = facility_location_element.at_xpath("./cda:participantRole[@classCode='SDLOC']/cda:code")
259
273
  facility_location.code = code_if_present(participant_element)
260
274
  facility_location.locationPeriod = extract_interval(facility_location_element, './cda:time')
261
- facility_locations << facility_location
275
+ facility_locations << facility_location if facility_location.code
262
276
  end
263
277
  facility_locations
264
278
  end
@@ -21,15 +21,25 @@ module QRDA
21
21
  encounter_performed.facilityLocations = extract_facility_locations(entry_element)
22
22
  encounter_performed.diagnoses = extract_diagnoses(entry_element)
23
23
  if encounter_performed&.relevantPeriod&.low && encounter_performed&.relevantPeriod&.high
24
- los = encounter_performed.relevantPeriod.high - encounter_performed.relevantPeriod.low
24
+ los = encounter_performed.relevantPeriod.high.to_date - encounter_performed.relevantPeriod.low.to_date
25
25
  encounter_performed.lengthOfStay = QDM::Quantity.new(los.to_i, 'd')
26
26
  end
27
27
  encounter_performed.participant = extract_entity(entry_element, "./cda:entryRelationship/cda:encounter//cda:participant[@typeCode='PRF']")
28
+ extract_modifier_code(encounter_performed, entry_element)
28
29
  encounter_performed
29
30
  end
30
31
 
31
32
  private
32
33
 
34
+ def extract_modifier_code(encounter_performed, entry_element)
35
+ code_element = entry_element.at_xpath(@code_xpath)
36
+ return unless code_element
37
+
38
+ qualifier_name = code_element.at_xpath('./cda:qualifier/cda:name')
39
+ qualifier_value = code_element.at_xpath('./cda:qualifier/cda:value')
40
+ codes_modifiers[encounter_performed.id] = { name: code_if_present(qualifier_name), value: code_if_present(qualifier_value), xpath_location: entry_element.path } if qualifier_value || qualifier_name
41
+ end
42
+
33
43
  def extract_diagnoses(parent_element)
34
44
  diagnosis_elements = parent_element.xpath(@diagnosis_xpath)
35
45
  diagnosis_list = []
@@ -59,19 +59,21 @@ module QRDA
59
59
  @data_element_importers << SubstanceOrderImporter.new
60
60
  @data_element_importers << SubstanceRecommendedImporter.new
61
61
  @data_element_importers << SymptomImporter.new
62
- end
62
+ end
63
63
 
64
64
  def parse_cat1(doc)
65
65
  patient = CQM::Patient.new
66
66
  warnings = []
67
+ codes = Set.new
68
+ codes_modifiers = {}
67
69
  entry_id_map = {}
68
- import_data_elements(patient, doc, entry_id_map, warnings)
70
+ import_data_elements(patient, doc, entry_id_map, codes, codes_modifiers, warnings)
69
71
  normalize_references(patient, entry_id_map)
70
- get_demographics(patient, doc)
71
- [patient, warnings]
72
+ get_demographics(patient, doc, codes)
73
+ [patient, warnings, codes, codes_modifiers]
72
74
  end
73
75
 
74
- def import_data_elements(patient, doc, entry_id_map, warnings = [])
76
+ def import_data_elements(patient, doc, entry_id_map, codes = Set.new, codes_modifiers = {}, warnings = [])
75
77
  context = doc.xpath("/cda:ClinicalDocument/cda:component/cda:structuredBody/cda:component/cda:section[cda:templateId/@root = '2.16.840.1.113883.10.20.24.2.1']")
76
78
  nrh = NarrativeReferenceHandler.new
77
79
  nrh.build_id_map(doc)
@@ -109,8 +111,12 @@ module QRDA
109
111
  patient.qdmPatient.dataElements << new_data_elements
110
112
  entry_id_map.merge!(id_map)
111
113
  warnings.concat(importer.warnings)
112
- # reset warnings after they're captured so that the importer can be re-used
114
+ codes.merge(importer.codes)
115
+ codes_modifiers.merge!(importer.codes_modifiers)
116
+ # reset warnings and codes after they're captured so that the importer can be re-used
113
117
  importer.warnings = []
118
+ importer.codes_modifiers = {}
119
+ importer.codes = Set.new
114
120
  end
115
121
  end
116
122
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cqm-reports
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.2
4
+ version: 3.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - The MITRE Corporation
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-24 00:00:00.000000000 Z
11
+ date: 2020-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cqm-models
@@ -358,7 +358,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
358
358
  - !ruby/object:Gem::Version
359
359
  version: '0'
360
360
  requirements: []
361
- rubygems_version: 3.0.8
361
+ rubygems_version: 3.1.4
362
362
  signing_key:
363
363
  specification_version: 4
364
364
  summary: A library for import and export of reports for use with electronic Clinical