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 +4 -4
- data/lib/html-export/qdm-patient/data_element/_data_element_codes.mustache +1 -1
- data/lib/html-export/qdm-patient/qdm_patient.mustache +4 -4
- data/lib/html-export/qdm-patient/qdm_patient.rb +19 -1
- data/lib/qrda-export/catI-r5/qrda_templates/laboratory_test_performed.mustache +4 -0
- data/lib/qrda-export/catI-r5/qrda_templates/physical_exam_performed.mustache +4 -0
- data/lib/qrda-import/base-importers/demographics_importer.rb +8 -7
- data/lib/qrda-import/base-importers/section_importer.rb +17 -3
- data/lib/qrda-import/data-element-importers/encounter_performed_importer.rb +11 -1
- data/lib/qrda-import/patient_importer.rb +12 -6
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b9895c3e39093e4e3b01d4ab490bd91db2b881b5ff9870b8de0c6369fc792601
|
4
|
+
data.tar.gz: ff99962ce100bd5f73bb5bc73299d55bd4b2ed1185de971996292b713baf8060
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 = [
|
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
|
-
|
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
|
-
|
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.
|
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
|
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.
|
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
|