cqm-reports 3.1.1 → 3.1.5
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 +5 -5
- data/Gemfile +1 -1
- data/lib/html-export/qdm-patient/data_element/_data_element_codes.mustache +1 -1
- data/lib/html-export/qdm-patient/qdm_patient.mustache +8 -8
- data/lib/html-export/qdm-patient/qdm_patient.rb +19 -1
- data/lib/qrda-export/catI-r5/qrda_header/_record_target.mustache +1 -1
- data/lib/qrda-export/catI-r5/qrda_templates/communication_performed.mustache +0 -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-export/catI-r5/qrda_templates/template_partials/_reaction_observation.mustache +1 -1
- data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_reason.mustache +0 -1
- data/lib/qrda-export/catIII-r2-1/_continuous_variable_value.mustache +5 -5
- data/lib/qrda-export/catIII-r2-1/_header.mustache +7 -1
- data/lib/qrda-export/catIII-r2-1/_measure_data.mustache +0 -2
- data/lib/qrda-export/catIII-r2-1/_stratification.mustache +3 -5
- data/lib/qrda-export/catIII-r2-1/qrda3_r21.rb +7 -5
- data/lib/qrda-export/helper/aggregate_object_helper.rb +14 -18
- data/lib/qrda-export/helper/date_helper.rb +2 -2
- 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 +20 -15
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: e0ceaca18d15bf285ae1a74d6c9844a5fa3226377857da71f654dad0185945f6
|
|
4
|
+
data.tar.gz: dff07718975472430f917e25772df47078df534fccbe985d37c37630c7c9f641
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c881930baa2fd8835a9e043278592e390c83acddaf43cef5eba5ce5dd37333151b0d7bdc418c90958a05ea6f9f36319d93df07cce09f7a27bb63428be3f36609
|
|
7
|
+
data.tar.gz: 6159a2125f19038c9524c2085a2a0e660a032ff31f4e0ea308f28ad8918febba2b54f2f7d695c78e87f40ff19699ec43828dc72555eb73216e979317b5afa81d
|
data/Gemfile
CHANGED
|
@@ -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>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|
5
5
|
{{#patient}}
|
|
6
|
-
<title>Cypress Certification Patient Test Record: {{
|
|
6
|
+
<title>Cypress Certification Patient Test Record: {{given_name}} {{familyName}}</title>
|
|
7
7
|
{{/patient}}
|
|
8
8
|
{{#include_style?}}
|
|
9
9
|
{{> _header_css}}
|
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
13
13
|
{{#patient}}
|
|
14
|
-
<h1 class="h1center">Cypress Certification Patient Test Record: {{
|
|
14
|
+
<h1 class="h1center">Cypress Certification Patient Test Record: {{given_name}} {{familyName}}</h1>
|
|
15
15
|
<div class="div-table">
|
|
16
16
|
<div class="div-table-body">
|
|
17
17
|
<div class="div-head-row patient_narr_tr panel panel-default patient-details">
|
|
18
18
|
<div class="div-table-head patient_narr_th panel-heading"><span class="td_label">Patient</span></div>
|
|
19
|
-
<div class="div-table-head">{{
|
|
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>
|
|
34
|
-
</div>
|
|
33
|
+
<div class="div-table-head">{{{ethnic_group}}}{{#demographic_code_description}}ethnic_group{{/demographic_code_description}}</div>
|
|
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']
|
|
@@ -48,7 +48,6 @@
|
|
|
48
48
|
<entryRelationship typeCode="REFR">
|
|
49
49
|
<observation classCode="OBS" moodCode="EVN">
|
|
50
50
|
<templateId root="2.16.840.1.113883.10.20.24.3.88" extension="2017-08-01"/>
|
|
51
|
-
<id root="1.3.6.1.4.1.115" extension="{{object_id}}" />
|
|
52
51
|
<code code="77301-0" codeSystem="2.16.840.1.113883.6.1" displayName="reason" codeSystemName="LOINC"/>
|
|
53
52
|
<statusCode code="completed"/>
|
|
54
53
|
<!-- QDM Attribute: 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>
|
data/lib/qrda-export/catI-r5/qrda_templates/template_partials/_reaction_observation.mustache
CHANGED
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
<id root="4adc1020-7b14-11db-9fe1-0800200c9a64" />
|
|
5
5
|
<code code="ASSERTION" codeSystem="2.16.840.1.113883.5.4" />
|
|
6
6
|
<statusCode code="completed" />
|
|
7
|
-
{{>
|
|
7
|
+
<value xsi:type="CD" {{> _code}}/>
|
|
8
8
|
</observation>
|
|
9
9
|
</entryRelationship>
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<entryRelationship typeCode="RSON">
|
|
2
2
|
<observation classCode="OBS" moodCode="EVN">
|
|
3
3
|
<templateId root="2.16.840.1.113883.10.20.24.3.88" extension="2017-08-01"/>
|
|
4
|
-
<id root="1.3.6.1.4.1.115" extension="{{random_id}}" />
|
|
5
4
|
<code code="77301-0" codeSystem="2.16.840.1.113883.6.1" displayName="reason" codeSystemName="LOINC"/>
|
|
6
5
|
{{#relevantPeriod}}
|
|
7
6
|
{{{relevant_period}}}
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
<observation classCode="OBS" moodCode="EVN">
|
|
3
3
|
<templateId root="2.16.840.1.113883.10.20.27.3.2"/>
|
|
4
4
|
<code nullFlavor="OTH">
|
|
5
|
-
<originalText>
|
|
5
|
+
<originalText>Other</originalText>
|
|
6
6
|
</code>
|
|
7
7
|
<statusCode code="completed"/>
|
|
8
|
-
<value xsi:type="
|
|
9
|
-
<methodCode code="
|
|
8
|
+
<value xsi:type="REAL" value="{{value}}"/>
|
|
9
|
+
<methodCode code="{{method}}" displayName="{{method}}" codeSystem="2.16.840.1.113883.5.84" codeSystemName="ObservationMethod"/>
|
|
10
10
|
<reference typeCode="REFR">
|
|
11
11
|
<!-- reference to the relevant measure observation in the eMeasure -->
|
|
12
12
|
<externalObservation classCode="OBS" moodCode="EVN">
|
|
13
|
-
<id root="{{
|
|
13
|
+
<id root="{{hqmf_id}}"/>
|
|
14
14
|
</externalObservation>
|
|
15
15
|
</reference>
|
|
16
16
|
</observation>
|
|
17
|
-
</entryRelationship>
|
|
17
|
+
</entryRelationship>
|
|
@@ -3,8 +3,14 @@
|
|
|
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="2017-06-01"/>
|
|
6
|
-
|
|
6
|
+
{{#ry2022_submission?}}
|
|
7
|
+
<!-- QRDA Category III Report - CMS (V6) -->
|
|
8
|
+
<templateId root="2.16.840.1.113883.10.20.27.1.2" extension="2021-07-01"/>
|
|
9
|
+
{{/ry2022_submission?}}
|
|
10
|
+
{{^ry2022_submission?}}
|
|
11
|
+
<!-- QRDA Category III Report - CMS (V5) -->
|
|
7
12
|
<templateId root="2.16.840.1.113883.10.20.27.1.2" extension="2020-05-01"/>
|
|
13
|
+
{{/ry2022_submission?}}
|
|
8
14
|
<!-- This is the globally unique identifier for this QRDA document -->
|
|
9
15
|
<id root="{{random_id}}"/>
|
|
10
16
|
<!-- QRDA III document type code -->
|
|
@@ -27,11 +27,9 @@
|
|
|
27
27
|
{{> _supplemental_data}}
|
|
28
28
|
{{/population_supplemental_data}}
|
|
29
29
|
{{/supplemental_data}}
|
|
30
|
-
{{#msrpopl?}}
|
|
31
30
|
{{#population_observation}}
|
|
32
31
|
{{> _continuous_variable_value}}
|
|
33
32
|
{{/population_observation}}
|
|
34
|
-
{{/msrpopl?}}
|
|
35
33
|
<reference typeCode="REFR">
|
|
36
34
|
<externalObservation classCode="OBS" moodCode="EVN">
|
|
37
35
|
<id root="{{id}}"/>
|
|
@@ -15,11 +15,9 @@
|
|
|
15
15
|
<methodCode code="COUNT" displayName="Count" codeSystem="2.16.840.1.113883.5.84" codeSystemName="ObservationMethod"/>
|
|
16
16
|
</observation>
|
|
17
17
|
</entryRelationship>
|
|
18
|
-
{{#
|
|
19
|
-
{{
|
|
20
|
-
|
|
21
|
-
{{/stratification_observation}}
|
|
22
|
-
{{/msrpopl?}}
|
|
18
|
+
{{#stratification_observation}}
|
|
19
|
+
{{> _continuous_variable_value}}
|
|
20
|
+
{{/stratification_observation}}
|
|
23
21
|
<reference typeCode="REFR">
|
|
24
22
|
<externalObservation classCode="OBS" moodCode="EVN">
|
|
25
23
|
<id root="{{id}}"/>
|
|
@@ -27,6 +27,7 @@ class Qrda3R21 < Mustache
|
|
|
27
27
|
@performance_period_start = options[:start_time]
|
|
28
28
|
@performance_period_end = options[:end_time]
|
|
29
29
|
@submission_program = options[:submission_program]
|
|
30
|
+
@ry2022_submission = options[:ry2022_submission]
|
|
30
31
|
end
|
|
31
32
|
|
|
32
33
|
def agg_results(measure_id, cache_entries, population_sets)
|
|
@@ -58,14 +59,11 @@ class Qrda3R21 < Mustache
|
|
|
58
59
|
end
|
|
59
60
|
|
|
60
61
|
def stratification_observation
|
|
61
|
-
|
|
62
|
-
stratification_observation = @measure_result_hash[self['measure_id']].aggregate_count.populations.find {|p| p.type == "OBSERV"}.stratifications.find {|s| s.id == self['id'] }
|
|
63
|
-
stratification_observation.id = observation.id
|
|
64
|
-
stratification_observation
|
|
62
|
+
self['observation']
|
|
65
63
|
end
|
|
66
64
|
|
|
67
65
|
def population_observation
|
|
68
|
-
|
|
66
|
+
self['observation']
|
|
69
67
|
end
|
|
70
68
|
|
|
71
69
|
def supplemental_template_ids
|
|
@@ -90,6 +88,10 @@ class Qrda3R21 < Mustache
|
|
|
90
88
|
@submission_program == 'CPCPLUS'
|
|
91
89
|
end
|
|
92
90
|
|
|
91
|
+
def ry2022_submission?
|
|
92
|
+
@ry2022_submission
|
|
93
|
+
end
|
|
94
|
+
|
|
93
95
|
def payer_code?
|
|
94
96
|
self['type'] == 'PAYER'
|
|
95
97
|
end
|
|
@@ -57,23 +57,24 @@ module Qrda
|
|
|
57
57
|
end
|
|
58
58
|
end
|
|
59
59
|
class Population
|
|
60
|
-
attr_accessor :type, :value, :id, :stratifications, :supplemental_data
|
|
60
|
+
attr_accessor :type, :value, :id, :stratifications, :supplemental_data, :observation
|
|
61
61
|
|
|
62
62
|
def initialize
|
|
63
63
|
@stratifications = []
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
def add_stratification(id,value)
|
|
67
|
-
stratifications << Stratification.new(id,value) unless stratifications.find {|st| st.id == id}
|
|
66
|
+
def add_stratification(id,value,observation)
|
|
67
|
+
stratifications << Stratification.new(id,value,observation) unless stratifications.find {|st| st.id == id}
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
class Stratification
|
|
73
|
-
attr_accessor :id, :value
|
|
74
|
-
def initialize(id,value)
|
|
73
|
+
attr_accessor :id, :value, :observation
|
|
74
|
+
def initialize(id,value, observation)
|
|
75
75
|
@id = id
|
|
76
76
|
@value = value
|
|
77
|
+
@observation = observation
|
|
77
78
|
end
|
|
78
79
|
|
|
79
80
|
end
|
|
@@ -107,15 +108,17 @@ module Qrda
|
|
|
107
108
|
def add_entry(cache_entry, population_sets)
|
|
108
109
|
population_set = population_sets.where(population_set_id: cache_entry.pop_set_hash[:population_set_id]).first
|
|
109
110
|
entry_populations = []
|
|
110
|
-
%w[IPP DENOM NUMER NUMEX DENEX DENEXCEP MSRPOPL MSRPOPLEX
|
|
111
|
-
next unless population_set.populations[pop_code]
|
|
111
|
+
%w[IPP DENOM NUMER NUMEX DENEX DENEXCEP MSRPOPL MSRPOPLEX].each do |pop_code|
|
|
112
|
+
next unless population_set.populations[pop_code]
|
|
112
113
|
|
|
113
114
|
population = create_population_from_population_set(pop_code, population_set, cache_entry)
|
|
114
115
|
if cache_entry.pop_set_hash[:stratification_id]
|
|
115
116
|
strat_id = population_set.stratifications.where(stratification_id: cache_entry.pop_set_hash[:stratification_id]).first&.hqmf_id
|
|
116
|
-
|
|
117
|
+
observation = cache_entry['observations'][pop_code] if cache_entry['observations'] && cache_entry['observations'][pop_code]
|
|
118
|
+
population.add_stratification(strat_id,cache_entry[pop_code], observation)
|
|
117
119
|
else
|
|
118
120
|
population.value = cache_entry[pop_code]
|
|
121
|
+
population.observation = cache_entry['observations'][pop_code] if cache_entry['observations'] && cache_entry['observations'][pop_code]
|
|
119
122
|
population.supplemental_data = cache_entry.supplemental_data[pop_code]
|
|
120
123
|
end
|
|
121
124
|
entry_populations << population if population
|
|
@@ -128,19 +131,12 @@ module Qrda
|
|
|
128
131
|
end
|
|
129
132
|
|
|
130
133
|
def create_population_from_population_set(pop_code, population_set, cache_entry)
|
|
131
|
-
population = if pop_code
|
|
132
|
-
populations.find { |pop| pop.id == population_set.observations&.first&.hqmf_id }
|
|
133
|
-
elsif pop_code != 'STRAT'
|
|
134
|
-
populations.find { |pop| pop.id == population_set.populations[pop_code]&.hqmf_id }
|
|
135
|
-
end
|
|
134
|
+
population = populations.find { |pop| pop.id == population_set.populations[pop_code]&.hqmf_id } if pop_code != 'STRAT'
|
|
136
135
|
return population unless population.nil? && !cache_entry.pop_set_hash[:stratification_id]
|
|
136
|
+
|
|
137
137
|
population = Population.new
|
|
138
138
|
population.type = pop_code
|
|
139
|
-
population.id =
|
|
140
|
-
population_set.observations&.first&.hqmf_id
|
|
141
|
-
else
|
|
142
|
-
population_set.populations[pop_code]&.hqmf_id
|
|
143
|
-
end
|
|
139
|
+
population.id = population_set.populations[pop_code]&.hqmf_id
|
|
144
140
|
populations << population
|
|
145
141
|
population
|
|
146
142
|
end
|
|
@@ -101,7 +101,7 @@ module Qrda
|
|
|
101
101
|
end
|
|
102
102
|
|
|
103
103
|
def relevant_date_period_or_null_flavor
|
|
104
|
-
return relevant_period if self['relevantPeriod']
|
|
104
|
+
return relevant_period if self['relevantPeriod'] && (self['relevantPeriod']['low'] || self['relevantPeriod']['high'])
|
|
105
105
|
return relevant_date_time_value if self['relevantDatetime']
|
|
106
106
|
"<effectiveTime nullFlavor='UNK'/>"
|
|
107
107
|
end
|
|
@@ -128,4 +128,4 @@ module Qrda
|
|
|
128
128
|
end
|
|
129
129
|
end
|
|
130
130
|
end
|
|
131
|
-
end
|
|
131
|
+
end
|
|
@@ -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.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- The MITRE Corporation
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2021-07-16 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: cqm-models
|
|
@@ -16,28 +16,28 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 3.0
|
|
19
|
+
version: '3.0'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 3.0
|
|
26
|
+
version: '3.0'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: cqm-validators
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
31
|
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: 3.0
|
|
33
|
+
version: '3.0'
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: 3.0
|
|
40
|
+
version: '3.0'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: mustache
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -84,16 +84,22 @@ dependencies:
|
|
|
84
84
|
name: nokogiri
|
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
|
86
86
|
requirements:
|
|
87
|
-
- - "
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: 1.8.5
|
|
90
|
+
- - "<"
|
|
88
91
|
- !ruby/object:Gem::Version
|
|
89
|
-
version:
|
|
92
|
+
version: 1.12.0
|
|
90
93
|
type: :runtime
|
|
91
94
|
prerelease: false
|
|
92
95
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
96
|
requirements:
|
|
94
|
-
- - "
|
|
97
|
+
- - ">="
|
|
98
|
+
- !ruby/object:Gem::Version
|
|
99
|
+
version: 1.8.5
|
|
100
|
+
- - "<"
|
|
95
101
|
- !ruby/object:Gem::Version
|
|
96
|
-
version:
|
|
102
|
+
version: 1.12.0
|
|
97
103
|
- !ruby/object:Gem::Dependency
|
|
98
104
|
name: uuid
|
|
99
105
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -343,7 +349,7 @@ homepage: https://github.com/projecttacoma/cqm-reports
|
|
|
343
349
|
licenses:
|
|
344
350
|
- Apache-2.0
|
|
345
351
|
metadata: {}
|
|
346
|
-
post_install_message:
|
|
352
|
+
post_install_message:
|
|
347
353
|
rdoc_options: []
|
|
348
354
|
require_paths:
|
|
349
355
|
- lib
|
|
@@ -358,9 +364,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
358
364
|
- !ruby/object:Gem::Version
|
|
359
365
|
version: '0'
|
|
360
366
|
requirements: []
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
signing_key:
|
|
367
|
+
rubygems_version: 3.1.4
|
|
368
|
+
signing_key:
|
|
364
369
|
specification_version: 4
|
|
365
370
|
summary: A library for import and export of reports for use with electronic Clinical
|
|
366
371
|
Quality Measures (eCQMs).
|