cqm-reports 4.0.1 → 4.1.1
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/Gemfile +1 -4
- data/lib/html-export/qdm-patient/data_element/_data_element_other_fields.mustache +1 -1
- data/lib/html-export/qdm-patient/qdm_patient.rb +22 -5
- data/lib/qrda-export/catI-r5/qrda1_r5.rb +12 -0
- data/lib/qrda-export/catI-r5/qrda_header/_participant.mustache +3 -2
- data/lib/qrda-export/catI-r5/qrda_header/_record_target.mustache +9 -0
- data/lib/qrda-export/catI-r5/qrda_templates/encounter_performed.mustache +49 -56
- data/lib/qrda-export/catI-r5/qrda_templates/medication_discharge.mustache +1 -1
- data/lib/qrda-export/catI-r5/qrda_templates/medication_dispensed.mustache +1 -1
- data/lib/qrda-export/catI-r5/qrda_templates/medication_order.mustache +1 -1
- data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_medication_details.mustache +1 -1
- data/lib/qrda-export/catIII/_header.mustache +2 -4
- data/lib/qrda-export/catIII/_measure_section.mustache +2 -2
- data/lib/qrda-export/catIII/qrda_header/_participant_ehr.mustache +4 -1
- data/lib/qrda-export/helper/aggregate_object_helper.rb +10 -10
- data/lib/qrda-export/helper/cat1_view_helper.rb +23 -5
- data/lib/qrda-export/helper/view_helper.rb +9 -1
- data/lib/qrda-import/base-importers/section_importer.rb +32 -4
- data/lib/qrda-import/data-element-importers/encounter_performed_importer.rb +12 -12
- data/lib/qrda-import/data-element-importers/medication_discharge_importer.rb +1 -1
- data/lib/qrda-import/data-element-importers/medication_dispensed_importer.rb +1 -1
- data/lib/qrda-import/data-element-importers/medication_order_importer.rb +1 -1
- data/lib/qrda-import/data-element-importers/substance_order_importer.rb +1 -1
- data/lib/qrda-import/data-element-importers/substance_recommended_importer.rb +1 -1
- data/lib/qrda-import/patient_importer.rb +22 -6
- data/lib/util/hqmf_template_helper.rb +4 -4
- metadata +5 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 253b7e13bd3b5c1755304e4ea37ddb94bbe59a4a94692c59c4bf9332ec56a6ee
|
4
|
+
data.tar.gz: 22e3f623d94257e68e0902ab7787e2d97305412d85619010b0ee5b52ac66d792
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fad5c31a8d282ed50d4d628bb0ceb3bef287a00a1acf26232766e12db2a080585825cbb65293bea9b5f8818c0fc9b72fb2205f5af34a37a42f8b05bc658a525e
|
7
|
+
data.tar.gz: 725ddfa8f662591803a2a578637ebff876597f62c67156b6fad8064fa7b5c7b10a9e82383cbbedfbc96e34b7b77afb7d06705065b5c509a10c071c12f0831fa2
|
data/Gemfile
CHANGED
@@ -2,13 +2,11 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
gemspec :development_group => :test
|
4
4
|
|
5
|
-
gem 'mongoid', '~> 6.4.2'
|
6
|
-
|
7
5
|
gem 'protected_attributes_continued'
|
8
6
|
|
9
7
|
group :development, :test do
|
10
8
|
gem 'bundler-audit'
|
11
|
-
gem 'rubocop', '~> 0.
|
9
|
+
gem 'rubocop', '~> 0.93.0', require: false
|
12
10
|
end
|
13
11
|
|
14
12
|
group :development do
|
@@ -18,7 +16,6 @@ end
|
|
18
16
|
|
19
17
|
group :test do
|
20
18
|
gem 'cqm-models', '~> 4.0'
|
21
|
-
gem 'factory_girl', '~> 4.1.0'
|
22
19
|
gem 'tailor', '~> 1.1.2'
|
23
20
|
gem 'cane', '~> 2.3.0'
|
24
21
|
gem 'codecov'
|
@@ -49,7 +49,7 @@
|
|
49
49
|
{{/daysSupplied}}
|
50
50
|
{{#diagnoses}}
|
51
51
|
<div class="div-head-row">
|
52
|
-
<div class="div-table-head--no-border"><span class="criteria-heading">Diagnosis{{{
|
52
|
+
<div class="div-table-head--no-border"><span class="criteria-heading">Diagnosis:</span> {{{nested_code_string}}} {{{diagnosis_string}}}</div>
|
53
53
|
</div>
|
54
54
|
{{/diagnoses}}
|
55
55
|
{{#dischargeDisposition}}
|
@@ -10,6 +10,7 @@ class QdmPatient < Mustache
|
|
10
10
|
@patient = patient
|
11
11
|
@qdmPatient = patient.qdmPatient
|
12
12
|
@patient_addresses = patient['addresses']
|
13
|
+
@patient_email = patient['email']
|
13
14
|
@patient_telecoms = patient['telecoms']
|
14
15
|
end
|
15
16
|
|
@@ -35,6 +36,7 @@ class QdmPatient < Mustache
|
|
35
36
|
def patient_telecoms
|
36
37
|
@patient_telecoms ||= [CQM::Telecom.new(use: 'HP', value: '555-555-2003')]
|
37
38
|
# create formatted telecoms
|
39
|
+
@patient_telecoms << CQM::Telecom.new(use: 'HP', value: @patient_email) if @patient_email
|
38
40
|
@patient_telecoms.map { |telecom| "(#{telecom['use']}) #{telecom['value']}" }.join("<br>")
|
39
41
|
end
|
40
42
|
|
@@ -52,8 +54,14 @@ class QdmPatient < Mustache
|
|
52
54
|
end
|
53
55
|
|
54
56
|
def unit_string
|
55
|
-
return
|
56
|
-
"#{self['value']} #{self['unit']}"
|
57
|
+
return trimed_value(self['value']).to_s if !self['unit'] || self['unit'] == '1'
|
58
|
+
"#{trimed_value(self['value'])} #{self['unit']}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def trimed_value(number)
|
62
|
+
i = number.to_i
|
63
|
+
f = number.to_f
|
64
|
+
i == f ? i : f
|
57
65
|
end
|
58
66
|
|
59
67
|
def entity_string
|
@@ -120,15 +128,24 @@ class QdmPatient < Mustache
|
|
120
128
|
return unit_string if self['value']
|
121
129
|
return code_code_system_string if self['code']
|
122
130
|
|
123
|
-
|
131
|
+
# Checks to see if the result is a DateTime value, String, or Numeric
|
132
|
+
begin
|
133
|
+
DateTime.parse(self['result'])
|
134
|
+
rescue ArgumentError, TypeError
|
135
|
+
# If the value is not numeric, just print out the result
|
136
|
+
self['result'].is_a?(Numeric) ? trimed_value(self['result']) : self['result']
|
137
|
+
end
|
124
138
|
end
|
125
139
|
|
126
140
|
def nested_code_string
|
127
141
|
code_for_element(self['code'])
|
128
142
|
end
|
129
143
|
|
130
|
-
def
|
131
|
-
|
144
|
+
def diagnosis_string
|
145
|
+
dx_string = ''
|
146
|
+
dx_string += "</br> rank: #{self['rank']}" if self['rank']
|
147
|
+
dx_string += "</br> presentOnAdmissionIndicator: #{self['presentOnAdmissionIndicator']['code']}" if self['presentOnAdmissionIndicator']
|
148
|
+
dx_string
|
132
149
|
end
|
133
150
|
|
134
151
|
def end_time?
|
@@ -16,9 +16,12 @@ class Qrda1R5 < Mustache
|
|
16
16
|
@provider = options[:provider]
|
17
17
|
@patient_address_option = options[:patient_addresses]
|
18
18
|
@patient_telecom_option = options[:patient_telecoms]
|
19
|
+
@patient_email_option = options[:patient_email]
|
19
20
|
@performance_period_start = options[:start_time]
|
20
21
|
@performance_period_end = options[:end_time]
|
21
22
|
@submission_program = options[:submission_program]
|
23
|
+
@medicare_beneficiary_identifier = options[:medicare_beneficiary_identifier]
|
24
|
+
@hicn = options[:hicn]
|
22
25
|
end
|
23
26
|
|
24
27
|
def patient_addresses
|
@@ -41,6 +44,15 @@ class Qrda1R5 < Mustache
|
|
41
44
|
JSON.parse(@patient_telecom_option.to_json)
|
42
45
|
end
|
43
46
|
|
47
|
+
def patient_email
|
48
|
+
return unless @patient_email_option
|
49
|
+
telecom_email = [CQM::Telecom.new(
|
50
|
+
use: 'HP',
|
51
|
+
value: @patient_email_option
|
52
|
+
)]
|
53
|
+
JSON.parse(telecom_email.to_json)
|
54
|
+
end
|
55
|
+
|
44
56
|
def patient_characteristic_payer
|
45
57
|
JSON.parse(@qdmPatient.get_data_elements('patient_characteristic', 'payer').to_json)
|
46
58
|
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
<participant typeCode="DEV">
|
2
2
|
<associatedEntity classCode="RGPR">
|
3
3
|
<!-- CMS EHR Certification Number (formerly known as Office of the
|
4
|
-
National Coordinator Certification Number)
|
5
|
-
|
4
|
+
National Coordinator Certification Number). Note, this is a test
|
5
|
+
file, and the provided ID is not for a certified product set. -->
|
6
|
+
<id extension="0015CPV4ZTB4WBU" root="2.16.840.1.113883.3.2074.1"/>
|
6
7
|
</associatedEntity>
|
7
8
|
</participant>
|
@@ -1,6 +1,12 @@
|
|
1
1
|
<recordTarget>
|
2
2
|
<patientRole>
|
3
3
|
<id extension="{{mrn}}" root="1.3.6.1.4.1.115" />
|
4
|
+
{{#medicare_beneficiary_identifier}}
|
5
|
+
<id extension="{{medicare_beneficiary_identifier}}" root="2.16.840.1.113883.4.927" />
|
6
|
+
{{/medicare_beneficiary_identifier}}
|
7
|
+
{{#hicn}}
|
8
|
+
<id extension="{{hicn}}" root="2.16.840.1.113883.4.572" />
|
9
|
+
{{/hicn}}
|
4
10
|
{{#patient_addresses}}
|
5
11
|
<addr use="{{use}}">
|
6
12
|
{{#street}}
|
@@ -15,6 +21,9 @@
|
|
15
21
|
{{#patient_telecoms}}
|
16
22
|
<telecom use="{{use}}" value="tel:{{value}}"/>
|
17
23
|
{{/patient_telecoms}}
|
24
|
+
{{#patient_email}}
|
25
|
+
<telecom use="{{use}}" value="mailto:{{value}}"/>
|
26
|
+
{{/patient_email}}
|
18
27
|
<patient>
|
19
28
|
{{#patient}}
|
20
29
|
<name>
|
@@ -1,58 +1,51 @@
|
|
1
1
|
<entry>
|
2
|
-
<
|
3
|
-
<!--Encounter
|
4
|
-
<templateId extension="
|
5
|
-
|
6
|
-
<
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
{{#relatedTo}}
|
52
|
-
<!-- QDM Attribute: relatedTo -->
|
53
|
-
{{> qrda_templates/template_partials/_related_to}}
|
54
|
-
{{/relatedTo}}
|
55
|
-
</encounter>
|
56
|
-
</entryRelationship>
|
57
|
-
</act>
|
2
|
+
<encounter classCode="ENC" moodCode="EVN">
|
3
|
+
<!-- Encounter activities template -->
|
4
|
+
<templateId extension="2015-08-01" root="2.16.840.1.113883.10.20.22.4.49"/>
|
5
|
+
<!-- Encounter performed template -->
|
6
|
+
<templateId extension="2021-08-01" root="2.16.840.1.113883.10.20.24.3.23"/>
|
7
|
+
<id extension="{{object_id}}" root="1.3.6.1.4.1.115"/>
|
8
|
+
<!-- QDM Attribute: Code -->
|
9
|
+
{{> _codes}}
|
10
|
+
<text>{{description}}</text>
|
11
|
+
<statusCode code="completed"/>
|
12
|
+
{{#relevantPeriod}}
|
13
|
+
<!-- QDM Attribute: Relevant Period -->
|
14
|
+
{{{relevant_period}}}
|
15
|
+
{{/relevantPeriod}}
|
16
|
+
{{#dischargeDisposition}}
|
17
|
+
<!-- QDM Attribute: Discharge Disposition -->
|
18
|
+
<sdtc:dischargeDispositionCode {{> _code}}/>
|
19
|
+
{{/dischargeDisposition}}
|
20
|
+
{{#authorDatetime}}
|
21
|
+
<!-- QDM Attribute: Author dateTime -->
|
22
|
+
{{> qrda_templates/template_partials/_author}}
|
23
|
+
{{/authorDatetime}}
|
24
|
+
{{#facilityLocations}}
|
25
|
+
<!-- QDM Attribute: Facility Locations -->
|
26
|
+
{{> qrda_templates/template_partials/_facility_location}}
|
27
|
+
{{/facilityLocations}}
|
28
|
+
{{#admissionSource}}
|
29
|
+
<!-- QDM Attribute: Admission Source -->
|
30
|
+
{{> qrda_templates/template_partials/_admission_source}}
|
31
|
+
{{/admissionSource}}
|
32
|
+
{{#participant}}
|
33
|
+
<!-- QDM Attribute: Participant -->
|
34
|
+
<participant typeCode="PRF">
|
35
|
+
{{> qrda_templates/template_partials/_entity}}
|
36
|
+
</participant>
|
37
|
+
{{/participant}}
|
38
|
+
{{#clazz}}
|
39
|
+
<!-- QDM Attribute: Class -->
|
40
|
+
{{> qrda_templates/template_partials/_encounter_class}}
|
41
|
+
{{/clazz}}
|
42
|
+
{{#diagnoses}}
|
43
|
+
<!-- QDM Attribute: Diagnoses -->
|
44
|
+
{{> qrda_templates/template_partials/_encounter_diagnosis_qdm}}
|
45
|
+
{{/diagnoses}}
|
46
|
+
{{#relatedTo}}
|
47
|
+
<!-- QDM Attribute: relatedTo -->
|
48
|
+
{{> qrda_templates/template_partials/_related_to}}
|
49
|
+
{{/relatedTo}}
|
50
|
+
</encounter>
|
58
51
|
</entry>
|
@@ -3,10 +3,8 @@
|
|
3
3
|
<typeId root="2.16.840.1.113883.1.3" extension="POCD_HD000040"/>
|
4
4
|
<!-- US Realm Header Template Id -->
|
5
5
|
<templateId root="2.16.840.1.113883.10.20.27.1.1" extension="2020-12-01"/>
|
6
|
-
|
7
|
-
|
8
|
-
<templateId root="2.16.840.1.113883.10.20.27.1.2" extension="2021-07-01"/>
|
9
|
-
{{/ry2022_submission?}}
|
6
|
+
<!-- QRDA Category III Report - CMS (V7) -->
|
7
|
+
<templateId root="2.16.840.1.113883.10.20.27.1.2" extension="2022-12-01"/>
|
10
8
|
<!-- This is the globally unique identifier for this QRDA document -->
|
11
9
|
<id root="{{random_id}}"/>
|
12
10
|
<!-- QRDA III document type code -->
|
@@ -11,7 +11,7 @@
|
|
11
11
|
<!-- QRDA Category III Measure Section template -->
|
12
12
|
<templateId extension="2020-12-01" root="2.16.840.1.113883.10.20.27.2.1"/>
|
13
13
|
<!-- QRDA Category III Measure Section - CMS (V4) -->
|
14
|
-
<templateId extension="
|
14
|
+
<templateId extension="2022-05-01" root="2.16.840.1.113883.10.20.27.2.3"/>
|
15
15
|
<code code="55186-1" codeSystem="2.16.840.1.113883.6.1"/>
|
16
16
|
<title>Measure Section</title>
|
17
17
|
<text>
|
@@ -41,7 +41,7 @@
|
|
41
41
|
<templateId root="2.16.840.1.113883.10.20.24.3.98"/>
|
42
42
|
<!-- SHALL 1..* (one for each referenced measure) Measure Reference and Results template -->
|
43
43
|
<templateId extension="2020-12-01" root="2.16.840.1.113883.10.20.27.3.1"/>
|
44
|
-
<templateId extension="
|
44
|
+
<templateId extension="2022-05-01" root="2.16.840.1.113883.10.20.27.3.17"/>
|
45
45
|
<id extension="{{random_id}}" root="1.3.6.1.4.1.115"/>
|
46
46
|
<statusCode code="completed"/>
|
47
47
|
<!-- Containing isBranch external references -->
|
@@ -1,6 +1,9 @@
|
|
1
1
|
<participant typeCode="DEV">
|
2
2
|
<associatedEntity classCode="RGPR">
|
3
|
-
|
3
|
+
<!-- CMS EHR Certification Number (formerly known as Office of the
|
4
|
+
National Coordinator Certification Number). Note, this is a test
|
5
|
+
file, and the provided ID is not for a certified product set. -->
|
6
|
+
<id root="2.16.840.1.113883.3.2074.1" extension="0015CPV4ZTB4WBU"/>
|
4
7
|
<code code="129465004" displayName="medical record, device" codeSystem="2.16.840.1.113883.6.96" codeSystemName="SNOMED-CT"/>
|
5
8
|
</associatedEntity>
|
6
9
|
</participant>
|
@@ -3,23 +3,23 @@ module Qrda
|
|
3
3
|
module Helper
|
4
4
|
module PopulationSelectors
|
5
5
|
def numerator
|
6
|
-
populations.find {|pop| pop.type == 'NUMER'}
|
6
|
+
populations.find { |pop| pop.type == 'NUMER' }
|
7
7
|
end
|
8
8
|
|
9
9
|
def denominator
|
10
|
-
populations.find {|pop| pop.type == 'DENOM'}
|
10
|
+
populations.find { |pop| pop.type == 'DENOM' }
|
11
11
|
end
|
12
12
|
|
13
13
|
def denominator_exceptions
|
14
|
-
populations.find {|pop| pop.type == 'DENEXCEP'}
|
14
|
+
populations.find { |pop| pop.type == 'DENEXCEP' }
|
15
15
|
end
|
16
16
|
|
17
17
|
def denominator_exclusions
|
18
|
-
populations.find {|pop| pop.type == 'DENEX'}
|
18
|
+
populations.find { |pop| pop.type == 'DENEX' }
|
19
19
|
end
|
20
20
|
|
21
21
|
def population_count(population_type, population_id)
|
22
|
-
population = populations.find {|pop| pop.type == population_type && pop.id == population_id}
|
22
|
+
population = populations.find { |pop| pop.type == population_type && pop.id == population_id }
|
23
23
|
if population
|
24
24
|
population.value
|
25
25
|
else
|
@@ -28,7 +28,7 @@ module Qrda
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def population_id(population_type)
|
31
|
-
populations.find {|pop| pop.type == population_type}.id
|
31
|
+
populations.find { |pop| pop.type == population_type }.id
|
32
32
|
end
|
33
33
|
|
34
34
|
def method_missing(method, *args, &block)
|
@@ -64,7 +64,7 @@ module Qrda
|
|
64
64
|
end
|
65
65
|
|
66
66
|
def add_stratification(id,value,observation)
|
67
|
-
stratifications << Stratification.new(id,value,observation) unless stratifications.find {|st| st.id == id}
|
67
|
+
stratifications << Stratification.new(id,value,observation) unless stratifications.find { |st| st.id == id }
|
68
68
|
end
|
69
69
|
|
70
70
|
end
|
@@ -91,7 +91,7 @@ module Qrda
|
|
91
91
|
end
|
92
92
|
|
93
93
|
def is_cv?
|
94
|
-
populations.any? {|pop| pop.type == 'MSRPOPL'}
|
94
|
+
populations.any? { |pop| pop.type == 'MSRPOPL' }
|
95
95
|
end
|
96
96
|
|
97
97
|
end
|
@@ -123,7 +123,7 @@ module Qrda
|
|
123
123
|
end
|
124
124
|
entry_populations << population if population
|
125
125
|
end
|
126
|
-
return if population_groups.find {|pg| pg.populations.collect(&:id).compact.sort == entry_populations.collect(&:id).compact.sort }
|
126
|
+
return if population_groups.find { |pg| pg.populations.collect(&:id).compact.sort == entry_populations.collect(&:id).compact.sort }
|
127
127
|
|
128
128
|
pg = PopulationGroup.new
|
129
129
|
pg.populations = entry_populations
|
@@ -142,7 +142,7 @@ module Qrda
|
|
142
142
|
end
|
143
143
|
|
144
144
|
def is_cv?
|
145
|
-
populations.any? {|pop| pop.type == 'MSRPOPL'}
|
145
|
+
populations.any? { |pop| pop.type == 'MSRPOPL' }
|
146
146
|
end
|
147
147
|
end
|
148
148
|
end
|
@@ -55,22 +55,30 @@ module Qrda
|
|
55
55
|
self['value'].to_f
|
56
56
|
end
|
57
57
|
|
58
|
+
def refills_as_repeat_number
|
59
|
+
self['refills'] + 1
|
60
|
+
end
|
61
|
+
|
58
62
|
def dose_quantity_value
|
59
|
-
return "<doseQuantity value=\"#{value_as_float}\" unit=\"#{self['unit']}\"/>" if self['unit']
|
63
|
+
return "<doseQuantity value=\"#{value_as_float}\" unit=\"#{self['unit']}\"/>" if self['unit'] && self['unit'] != ''
|
60
64
|
"<doseQuantity value=\"#{value_as_float}\" />"
|
61
65
|
end
|
62
66
|
|
63
67
|
def result_value
|
64
68
|
return "<value xsi:type=\"CD\" nullFlavor=\"UNK\"/>" unless self['result']
|
65
|
-
|
66
69
|
result_string = if self['result'].is_a? Array
|
67
70
|
result_value_as_string(self['result'][0])
|
68
71
|
elsif self['result'].is_a? Hash
|
69
72
|
result_value_as_string(self['result'])
|
70
73
|
elsif self['result'].is_a? String
|
71
|
-
|
74
|
+
begin
|
75
|
+
DateTime.parse self['result']
|
76
|
+
"<value xsi:type=\"TS\" #{value_or_null_flavor(self['result'])}/>"
|
77
|
+
rescue StandardError
|
78
|
+
"<value xsi:type=\"ST\">#{self['result']}</value>"
|
79
|
+
end
|
72
80
|
elsif !self['result'].nil?
|
73
|
-
|
81
|
+
integer_or_pq(self['result'])
|
74
82
|
end
|
75
83
|
result_string
|
76
84
|
end
|
@@ -79,7 +87,17 @@ module Qrda
|
|
79
87
|
return "<value xsi:type=\"CD\" nullFlavor=\"UNK\"/>" unless result
|
80
88
|
oid = result['system'] || result['codeSystem']
|
81
89
|
return "<value xsi:type=\"CD\" code=\"#{result['code']}\" codeSystem=\"#{oid}\" codeSystemName=\"#{HQMF::Util::CodeSystemHelper.code_system_for(oid)}\"/>" if result['code']
|
82
|
-
return
|
90
|
+
return integer_or_pq(result['value'], result['unit']) if result['unit'] && result['unit'] != ''
|
91
|
+
end
|
92
|
+
|
93
|
+
def integer_or_pq(number, unit = nil)
|
94
|
+
i = number.to_i
|
95
|
+
f = number.to_f
|
96
|
+
if i == f
|
97
|
+
unit ? "<value xsi:type=\"PQ\" value=\"#{i}\" unit=\"#{unit}\"/>" : "<value xsi:type=\"INT\" value=\"#{i}\"/>"
|
98
|
+
else
|
99
|
+
unit ? "<value xsi:type=\"PQ\" value=\"#{f}\" unit=\"#{unit}\"/>" : "<value xsi:type=\"REAL\" value=\"#{f}\"/>"
|
100
|
+
end
|
83
101
|
end
|
84
102
|
|
85
103
|
def authordatetime_or_dispenserid?
|
@@ -4,7 +4,7 @@ module Qrda
|
|
4
4
|
module Helper
|
5
5
|
module ViewHelper
|
6
6
|
def measures
|
7
|
-
@measures.
|
7
|
+
@measures.as_json
|
8
8
|
end
|
9
9
|
|
10
10
|
def random_id
|
@@ -22,6 +22,14 @@ module Qrda
|
|
22
22
|
def submission_program
|
23
23
|
@submission_program
|
24
24
|
end
|
25
|
+
|
26
|
+
def medicare_beneficiary_identifier
|
27
|
+
@medicare_beneficiary_identifier
|
28
|
+
end
|
29
|
+
|
30
|
+
def hicn
|
31
|
+
@hicn
|
32
|
+
end
|
25
33
|
end
|
26
34
|
end
|
27
35
|
end
|
@@ -47,8 +47,8 @@ module QRDA
|
|
47
47
|
entry_qrda_id = extract_id(entry_element, @id_xpath)
|
48
48
|
# Create a hash to map all of entry.ids to the same QRDA ids. This will be used to merge QRDA entries
|
49
49
|
# that represent the same event.
|
50
|
-
@entry_id_map["#{entry_qrda_id.value}
|
51
|
-
@entry_id_map["#{entry_qrda_id.value}
|
50
|
+
@entry_id_map["#{entry_qrda_id.value}***#{entry_qrda_id.namingSystem}"] ||= []
|
51
|
+
@entry_id_map["#{entry_qrda_id.value}***#{entry_qrda_id.namingSystem}"] << entry.id
|
52
52
|
entry.dataElementCodes = extract_codes(entry_element, @code_xpath)
|
53
53
|
extract_dates(entry_element, entry)
|
54
54
|
if @result_xpath
|
@@ -77,7 +77,13 @@ module QRDA
|
|
77
77
|
code_list << code_if_present(code_element)
|
78
78
|
translations = code_element.xpath('cda:translation')
|
79
79
|
translations.each do |translation|
|
80
|
-
|
80
|
+
translation_code = code_if_present(translation)
|
81
|
+
next unless translation_code
|
82
|
+
|
83
|
+
code_list << translation_code
|
84
|
+
root_code_string = "#{code_list[0].system}:#{code_list[0].code}"
|
85
|
+
@warnings << ValidationError.new(message: "Translation code #{translation_code.system}:#{translation_code.code} may not be used for eCQM calculation by a receiving system. Ensure that the root code #{root_code_string} is from an eCQM valueset.",
|
86
|
+
location: coded_element.path)
|
81
87
|
end
|
82
88
|
end
|
83
89
|
code_list.compact
|
@@ -193,7 +199,14 @@ module QRDA
|
|
193
199
|
return unless value_element && !value_element['nullFlavor']
|
194
200
|
value = value_element['value']
|
195
201
|
if value.present?
|
196
|
-
|
202
|
+
if ['TS'].include? value_element.at_xpath("@xsi:type")&.value
|
203
|
+
begin
|
204
|
+
return DateTime.parse(value_element['value'])
|
205
|
+
rescue StandardError
|
206
|
+
return nil
|
207
|
+
end
|
208
|
+
end
|
209
|
+
return value.strip.to_f if unitless?(value_element)
|
197
210
|
|
198
211
|
return QDM::Quantity.new(value.strip.to_f, value_element['unit'])
|
199
212
|
elsif value_element['code'].present?
|
@@ -208,6 +221,12 @@ module QRDA
|
|
208
221
|
end
|
209
222
|
end
|
210
223
|
|
224
|
+
def unitless?(value_element)
|
225
|
+
return true if value_element['unit'].nil?
|
226
|
+
return true if value_element['unit'] == '1'
|
227
|
+
return true if value_element['unit'][0] == '{' && value_element['unit'][-1] == '}'
|
228
|
+
end
|
229
|
+
|
211
230
|
def extract_reason(parent_element)
|
212
231
|
return unless @reason_xpath
|
213
232
|
reason_element = parent_element.xpath(@reason_xpath)
|
@@ -254,6 +273,15 @@ module QRDA
|
|
254
273
|
QDM::Quantity.new(scalar_element['value'].to_f, scalar_element['unit'])
|
255
274
|
end
|
256
275
|
|
276
|
+
def extract_refills(parent_element, repeat_number_xpath)
|
277
|
+
repeat_number_element = parent_element.at_xpath(repeat_number_xpath)
|
278
|
+
return unless repeat_number_element && repeat_number_element['value'].present?
|
279
|
+
|
280
|
+
# Refills is the Repeat Number - 1
|
281
|
+
repeat_number = repeat_number_element['value'].to_i
|
282
|
+
repeat_number.positive? ? repeat_number - 1 : 0
|
283
|
+
end
|
284
|
+
|
257
285
|
def extract_components(parent_element)
|
258
286
|
component_elements = parent_element.xpath(@components_xpath)
|
259
287
|
components = []
|
@@ -1,18 +1,18 @@
|
|
1
1
|
module QRDA
|
2
2
|
module Cat1
|
3
3
|
class EncounterPerformedImporter < SectionImporter
|
4
|
-
def initialize(entry_finder = QRDA::Cat1::EntryFinder.new("./cda:entry/cda:
|
4
|
+
def initialize(entry_finder = QRDA::Cat1::EntryFinder.new("./cda:entry/cda:encounter[cda:templateId/@root = '2.16.840.1.113883.10.20.24.3.23']"))
|
5
5
|
super(entry_finder)
|
6
|
-
@id_xpath = './cda:
|
7
|
-
@code_xpath = "./cda:
|
8
|
-
@relevant_period_xpath = "./cda:
|
9
|
-
@author_datetime_xpath = "./cda:
|
10
|
-
@admission_source_xpath = "./cda:
|
11
|
-
@discharge_disposition_xpath = "./
|
12
|
-
@facility_locations_xpath = "./cda:
|
13
|
-
@clazz_xpath = "./cda:entryRelationship/cda:
|
14
|
-
@diagnosis_xpath = "./cda:entryRelationship/cda:
|
15
|
-
@related_to_xpath = "./
|
6
|
+
@id_xpath = './cda:id'
|
7
|
+
@code_xpath = "./cda:code"
|
8
|
+
@relevant_period_xpath = "./cda:effectiveTime"
|
9
|
+
@author_datetime_xpath = "./cda:author[cda:templateId/@root='2.16.840.1.113883.10.20.24.3.155']/cda:time"
|
10
|
+
@admission_source_xpath = "./cda:participant/cda:participantRole[cda:templateId/@root='2.16.840.1.113883.10.20.24.3.151']/cda:code"
|
11
|
+
@discharge_disposition_xpath = "./sdtc:dischargeDispositionCode"
|
12
|
+
@facility_locations_xpath = "./cda:participant[cda:templateId/@root = '2.16.840.1.113883.10.20.24.3.100']"
|
13
|
+
@clazz_xpath = "./cda:entryRelationship/cda:act[cda:templateId/@root='2.16.840.1.113883.10.20.24.3.171']/cda:code"
|
14
|
+
@diagnosis_xpath = "./cda:entryRelationship/cda:observation[cda:templateId/@root='2.16.840.1.113883.10.20.24.3.168']"
|
15
|
+
@related_to_xpath = "./sdtc:inFulfillmentOf1/sdtc:actReference"
|
16
16
|
@entry_class = QDM::EncounterPerformed
|
17
17
|
end
|
18
18
|
|
@@ -27,7 +27,7 @@ module QRDA
|
|
27
27
|
los = encounter_performed.relevantPeriod.high.to_date - encounter_performed.relevantPeriod.low.to_date
|
28
28
|
encounter_performed.lengthOfStay = QDM::Quantity.new(los.to_i, 'd')
|
29
29
|
end
|
30
|
-
entity = extract_entity(entry_element, "./cda:
|
30
|
+
entity = extract_entity(entry_element, "./cda:participant[@typeCode='PRF']")
|
31
31
|
encounter_performed.participant.concat(entity) if entity
|
32
32
|
extract_modifier_code(encounter_performed, entry_element)
|
33
33
|
encounter_performed.relatedTo = extract_related_to(entry_element)
|
@@ -18,7 +18,7 @@ module QRDA
|
|
18
18
|
|
19
19
|
def create_entry(entry_element, nrh = NarrativeReferenceHandler.new)
|
20
20
|
medication_discharge = super
|
21
|
-
medication_discharge.refills =
|
21
|
+
medication_discharge.refills = extract_refills(entry_element, @refills_xpath)
|
22
22
|
medication_discharge.dosage = extract_scalar(entry_element, @dosage_xpath)
|
23
23
|
medication_discharge.supply = extract_scalar(entry_element, @supply_xpath)
|
24
24
|
medication_discharge.frequency = frequency_as_coded_value(entry_element, @frequency_xpath)
|
@@ -19,7 +19,7 @@ module QRDA
|
|
19
19
|
|
20
20
|
def create_entry(entry_element, nrh = NarrativeReferenceHandler.new)
|
21
21
|
medication_dispensed = super
|
22
|
-
medication_dispensed.refills =
|
22
|
+
medication_dispensed.refills = extract_refills(entry_element, @refills_xpath)
|
23
23
|
medication_dispensed.dosage = extract_scalar(entry_element, @dosage_xpath)
|
24
24
|
medication_dispensed.supply = extract_scalar(entry_element, @supply_xpath)
|
25
25
|
medication_dispensed.frequency = frequency_as_coded_value(entry_element, @frequency_xpath)
|
@@ -25,7 +25,7 @@ module QRDA
|
|
25
25
|
medication_order.dosage = extract_scalar(entry_element, @dosage_xpath)
|
26
26
|
medication_order.supply = extract_scalar(entry_element, @supply_xpath)
|
27
27
|
medication_order.frequency = frequency_as_coded_value(entry_element, @frequency_xpath)
|
28
|
-
medication_order.refills =
|
28
|
+
medication_order.refills = extract_refills(entry_element, @refills_xpath)
|
29
29
|
medication_order.route = code_if_present(entry_element.at_xpath(@route_xpath))
|
30
30
|
medication_order.setting = code_if_present(entry_element.at_xpath(@setting_xpath))
|
31
31
|
medication_order.daysSupplied = extract_scalar(entry_element, @days_supplied_xpath)&.value
|
@@ -21,7 +21,7 @@ module QRDA
|
|
21
21
|
substance_order.dosage = extract_scalar(entry_element, @dosage_xpath)
|
22
22
|
substance_order.supply = extract_scalar(entry_element, @supply_xpath)
|
23
23
|
substance_order.frequency = frequency_as_coded_value(entry_element, @frequency_xpath)
|
24
|
-
substance_order.refills =
|
24
|
+
substance_order.refills = extract_refills(entry_element, @refills_xpath)
|
25
25
|
substance_order.route = code_if_present(entry_element.at_xpath(@route_xpath))
|
26
26
|
substance_order.reason = extract_reason(entry_element)
|
27
27
|
substance_order
|
@@ -18,7 +18,7 @@ module QRDA
|
|
18
18
|
substance_recommended = super
|
19
19
|
substance_recommended.dosage = extract_scalar(entry_element, @dosage_xpath)
|
20
20
|
substance_recommended.frequency = frequency_as_coded_value(entry_element, @frequency_xpath)
|
21
|
-
substance_recommended.refills =
|
21
|
+
substance_recommended.refills = extract_refills(entry_element, @refills_xpath)
|
22
22
|
substance_recommended.route = code_if_present(entry_element.at_xpath(@route_xpath))
|
23
23
|
substance_recommended.reason = extract_reason(entry_element)
|
24
24
|
entity = extract_entity(entry_element, "./cda:participant[@typeCode='PRF']")
|
@@ -67,7 +67,7 @@ module QRDA
|
|
67
67
|
codes_modifiers = {}
|
68
68
|
entry_id_map = {}
|
69
69
|
import_data_elements(patient, doc, entry_id_map, codes, codes_modifiers, warnings)
|
70
|
-
normalize_references(patient, entry_id_map)
|
70
|
+
normalize_references(patient, entry_id_map, warnings)
|
71
71
|
get_demographics(patient, doc, codes)
|
72
72
|
[patient, warnings, codes, codes_modifiers]
|
73
73
|
end
|
@@ -80,9 +80,19 @@ module QRDA
|
|
80
80
|
data_elements, id_map = importer.create_entries(context, nrh)
|
81
81
|
new_data_elements = []
|
82
82
|
|
83
|
-
id_map.
|
84
|
-
|
83
|
+
id_map.each_pair do |key, elem_ids|
|
84
|
+
split_id = key.split('***')
|
85
|
+
id_string = "#{split_id[1]}(root), #{split_id[0]}(extension)"
|
86
|
+
warnings << ValidationError.new(message: "Two or more entries share the Id: #{id_string}.") if elem_ids.length > 1
|
87
|
+
elem_id = elem_ids.last
|
85
88
|
data_element = data_elements.find { |de| de.id == elem_id }
|
89
|
+
|
90
|
+
# If a data_element isn't returned, there was an issue parsing the template, provide a warning
|
91
|
+
if data_element.nil?
|
92
|
+
warnings << ValidationError.new(message: "Error parsing template with Id: #{id_string}.")
|
93
|
+
next
|
94
|
+
end
|
95
|
+
|
86
96
|
# Keep the first element with a shared ID
|
87
97
|
new_data_elements << data_element
|
88
98
|
|
@@ -94,7 +104,7 @@ module QRDA
|
|
94
104
|
unique_element_keys << key_elements_for_determining_encounter_uniqueness(data_element)
|
95
105
|
|
96
106
|
# Loop through all other data elements with the same id
|
97
|
-
elem_ids[
|
107
|
+
elem_ids[0,elem_ids.length - 1].each do |dup_id|
|
98
108
|
dup_element = data_elements.find { |de| de.id == dup_id }
|
99
109
|
dup_element_keys = key_elements_for_determining_encounter_uniqueness(dup_element)
|
100
110
|
# See if a previously selected data element shared all of the keys files
|
@@ -134,13 +144,19 @@ module QRDA
|
|
134
144
|
record.deathdate = DateTime.parse(entry_elements.at_xpath("./cda:effectiveTime/cda:low")['value']).to_i
|
135
145
|
end
|
136
146
|
|
137
|
-
def normalize_references(patient, entry_id_map)
|
147
|
+
def normalize_references(patient, entry_id_map, warnings)
|
138
148
|
patient.qdmPatient.dataElements.each do |data_element|
|
139
149
|
next unless data_element.respond_to?(:relatedTo) && data_element.relatedTo
|
140
150
|
|
141
151
|
relations_to_add = []
|
142
152
|
data_element.relatedTo.each do |related_to|
|
143
|
-
|
153
|
+
relation_to_add = entry_id_map["#{related_to['value']}***#{related_to['namingSystem']}"]
|
154
|
+
# Add the relation if it can be found, otherwise return a warning
|
155
|
+
relations_to_add += relation_to_add unless relation_to_add.nil?
|
156
|
+
if relation_to_add.nil?
|
157
|
+
id_warning_str = "Related To Id: #{related_to['namingSystem']}(root), #{related_to['value']}(extension) cannot be found in QRDA file."
|
158
|
+
warnings << ValidationError.new(message: id_warning_str)
|
159
|
+
end
|
144
160
|
end
|
145
161
|
data_element.relatedTo = relations_to_add.map(&:to_s)
|
146
162
|
end
|
@@ -3,7 +3,7 @@ module HQMF
|
|
3
3
|
# General helpers for working with codes and code systems
|
4
4
|
class HQMFTemplateHelper
|
5
5
|
|
6
|
-
def self.definition_for_template_id(template_id, version="r1")
|
6
|
+
def self.definition_for_template_id(template_id, version = "r1")
|
7
7
|
template_id_map(version)[template_id]
|
8
8
|
end
|
9
9
|
|
@@ -18,14 +18,14 @@ module HQMF
|
|
18
18
|
@id_map[version]
|
19
19
|
end
|
20
20
|
|
21
|
-
def self.template_id_by_definition_and_status(definition, status, negation
|
21
|
+
def self.template_id_by_definition_and_status(definition, status, negation, version)
|
22
22
|
case version
|
23
23
|
when "r1"
|
24
|
-
kv_pair = template_id_map(version).find {|k, v| v['definition'] == definition &&
|
24
|
+
kv_pair = template_id_map(version).find { |k, v| v['definition'] == definition &&
|
25
25
|
v['status'] == status &&
|
26
26
|
v['negation'] == negation}
|
27
27
|
when "r2", "r2cql"
|
28
|
-
kv_pair = template_id_map(version).find {|k, v| v['definition'] == definition &&
|
28
|
+
kv_pair = template_id_map(version).find { |k, v| v['definition'] == definition &&
|
29
29
|
v['status'] == status}
|
30
30
|
end
|
31
31
|
if kv_pair
|
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: 4.
|
4
|
+
version: 4.1.1
|
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:
|
11
|
+
date: 2024-05-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cqm-models
|
@@ -86,20 +86,14 @@ dependencies:
|
|
86
86
|
requirements:
|
87
87
|
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: 1.
|
90
|
-
- - "<"
|
91
|
-
- !ruby/object:Gem::Version
|
92
|
-
version: 1.14.0
|
89
|
+
version: 1.16.2
|
93
90
|
type: :runtime
|
94
91
|
prerelease: false
|
95
92
|
version_requirements: !ruby/object:Gem::Requirement
|
96
93
|
requirements:
|
97
94
|
- - ">="
|
98
95
|
- !ruby/object:Gem::Version
|
99
|
-
version: 1.
|
100
|
-
- - "<"
|
101
|
-
- !ruby/object:Gem::Version
|
102
|
-
version: 1.14.0
|
96
|
+
version: 1.16.2
|
103
97
|
- !ruby/object:Gem::Dependency
|
104
98
|
name: uuid
|
105
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -365,7 +359,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
365
359
|
- !ruby/object:Gem::Version
|
366
360
|
version: '0'
|
367
361
|
requirements: []
|
368
|
-
rubygems_version: 3.1.
|
362
|
+
rubygems_version: 3.1.4
|
369
363
|
signing_key:
|
370
364
|
specification_version: 4
|
371
365
|
summary: A library for import and export of reports for use with electronic Clinical
|