cqm-reports 3.0.1 → 3.1.4
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 -2
- data/README.md +2 -1
- data/lib/cqm-reports.rb +0 -1
- data/lib/html-export/qdm-patient/data_element/_data_element_codes.mustache +1 -1
- data/lib/html-export/qdm-patient/qdm_patient.mustache +13 -11
- data/lib/html-export/qdm-patient/qdm_patient.rb +50 -2
- 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/_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 +2 -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-export/helper/patient_view_helper.rb +39 -0
- data/lib/qrda-import/base-importers/demographics_importer.rb +8 -7
- data/lib/qrda-import/base-importers/section_importer.rb +56 -11
- data/lib/qrda-import/data-element-importers/encounter_performed_importer.rb +11 -1
- data/lib/qrda-import/patient_importer.rb +64 -60
- metadata +32 -14
- data/lib/qrda-import/entry_package.rb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 294e31e72ca88d7e9a85c08b1995728693cb2cf80a906273a8f1762ef5a1cc01
|
4
|
+
data.tar.gz: c969fcf65e9f7f8efabb02c636aad2bef9659d2221c0e72a94afe9ec993c8e16
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa75bf444859f1fdff85ae788fa277f0acb8f75e4d6e036717a0545f7af2a19800f446500101f36f9773bff9eb815b3cf4601bf1694d26af2468f04ffa3cc5cc
|
7
|
+
data.tar.gz: 78fe7570a996c1cd80517e74a856c2149a5cb351b933dccda3870f8b6781f707cd0c957b343421a0b54a5d8205c3bcfe96d535f4fe2fc53658c2cd0db8e2ba95
|
data/Gemfile
CHANGED
@@ -17,7 +17,7 @@ group :development do
|
|
17
17
|
end
|
18
18
|
|
19
19
|
group :test do
|
20
|
-
gem 'cqm-models',
|
20
|
+
gem 'cqm-models', '~> 3.0'
|
21
21
|
gem 'factory_girl', '~> 4.1.0'
|
22
22
|
gem 'tailor', '~> 1.1.2'
|
23
23
|
gem 'cane', '~> 2.3.0'
|
@@ -27,6 +27,5 @@ group :test do
|
|
27
27
|
gem 'minitest', '~> 5.3'
|
28
28
|
gem 'minitest-reporters'
|
29
29
|
gem 'awesome_print', :require => 'ap'
|
30
|
-
gem 'cqm-validators', git: 'https://github.com/projecttacoma/cqm-validators', branch: 'master'
|
31
30
|
gem 'nokogiri-diff'
|
32
31
|
end
|
data/README.md
CHANGED
@@ -11,6 +11,7 @@ Starting with version **2.0.0** released on 6/20/2019, cqm-reports versioning ha
|
|
11
11
|
| X | QRDA Cat 1 | QRDA Cat 3 |
|
12
12
|
| --- | --- | --- |
|
13
13
|
| 2 | R1 STU5.1 | R1 STU2.1 |
|
14
|
+
| 3 | R1 STU5.2 | R1 STU2.1 |
|
14
15
|
|
15
16
|
* **Y** indicates major changes (incompatible API changes)
|
16
17
|
|
@@ -24,7 +25,7 @@ Importing QRDA
|
|
24
25
|
A QRDA document can be imported into a CQM::Patient (defined in [cqm-models](https://github.com/projecttacoma/cqm-models)) using the following commands.
|
25
26
|
|
26
27
|
doc = Nokogiri::XML(file)
|
27
|
-
patient = QRDA::Cat1::PatientImporter.instance.parse_cat1(doc)
|
28
|
+
patient, warnings = QRDA::Cat1::PatientImporter.instance.parse_cat1(doc)
|
28
29
|
|
29
30
|
Exporting QRDA Category I
|
30
31
|
==========
|
data/lib/cqm-reports.rb
CHANGED
@@ -19,7 +19,6 @@ require_relative 'html-export/qdm-patient/qdm_patient.rb'
|
|
19
19
|
require_relative 'qrda-export/catI-r5/qrda1_r5.rb'
|
20
20
|
require_relative 'qrda-export/catIII-r2-1/qrda3_r21.rb'
|
21
21
|
|
22
|
-
require_relative 'qrda-import/entry_package.rb'
|
23
22
|
require_relative 'qrda-import/cda_identifier.rb'
|
24
23
|
require_relative 'qrda-import/narrative_reference_handler.rb'
|
25
24
|
require_relative 'qrda-import/entry_finder.rb'
|
@@ -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,36 +11,38 @@
|
|
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>
|
25
25
|
<div class="div-table-head">{{{birthdate}}}</div>
|
26
26
|
<div class="div-table-head patient_narr_th panel-heading"><span class="td_label">Date of expiration</span></div>
|
27
|
-
<div class="div-table-head"
|
27
|
+
<div class="div-table-head">{{{expiration}}}</div>
|
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>
|
41
41
|
<div class="div-head-row patient_narr_tr panel panel-default patient-details">
|
42
|
-
<div class="div-table-head patient_narr_th panel-heading"><span class="td_label">
|
43
|
-
<div class="div-table-head"
|
42
|
+
<div class="div-table-head patient_narr_th panel-heading"><span class="td_label">Address</span></div>
|
43
|
+
<div class="div-table-head">{{{patient_addresses}}}</div>
|
44
|
+
<div class="div-table-head patient_narr_th panel-heading"><span class="td_label">Telecom</span></div>
|
45
|
+
<div class="div-table-head">{{{patient_telecoms}}}</div>
|
44
46
|
</div>
|
45
47
|
</div>
|
46
48
|
</div>
|
@@ -9,6 +9,36 @@ class QdmPatient < Mustache
|
|
9
9
|
@include_style = include_style
|
10
10
|
@patient = patient
|
11
11
|
@qdmPatient = patient.qdmPatient
|
12
|
+
@patient_addresses = patient['addresses']
|
13
|
+
@patient_telecoms = patient['telecoms']
|
14
|
+
end
|
15
|
+
|
16
|
+
def patient_addresses
|
17
|
+
@patient_addresses ||= [CQM::Address.new(
|
18
|
+
use: 'HP',
|
19
|
+
street: ['202 Burlington Rd.'],
|
20
|
+
city: 'Bedford',
|
21
|
+
state: 'MA',
|
22
|
+
zip: '01730',
|
23
|
+
country: 'US'
|
24
|
+
)]
|
25
|
+
address_str = ""
|
26
|
+
@patient_addresses.each do |address|
|
27
|
+
# create formatted address
|
28
|
+
address_str += "<address>"
|
29
|
+
address['street'].each { |street| address_str += "#{street}<br>" }
|
30
|
+
address_str += "#{address['city']}, #{address['state']} #{address['zip']}<br> #{address['country']} </address>"
|
31
|
+
end
|
32
|
+
address_str
|
33
|
+
end
|
34
|
+
|
35
|
+
def patient_telecoms
|
36
|
+
@patient_telecoms ||= [CQM::Telecom.new(
|
37
|
+
use: 'HP',
|
38
|
+
value: '555-555-2003'
|
39
|
+
)]
|
40
|
+
# create formatted telecoms
|
41
|
+
@patient_telecoms.map { |telecom| "(#{telecom['use']}) #{telecom['value']}" }.join("<br>")
|
12
42
|
end
|
13
43
|
|
14
44
|
def include_style?
|
@@ -25,7 +55,7 @@ class QdmPatient < Mustache
|
|
25
55
|
end
|
26
56
|
|
27
57
|
def unit_string
|
28
|
-
return "#{self['value']} "
|
58
|
+
return "#{self['value']} " if !self['unit'] || self['unit'] == '1'
|
29
59
|
"#{self['value']} #{self['unit']}"
|
30
60
|
end
|
31
61
|
|
@@ -61,13 +91,31 @@ class QdmPatient < Mustache
|
|
61
91
|
end
|
62
92
|
|
63
93
|
def code_for_element(element)
|
64
|
-
"#{element['code']} (#{HQMF::Util::CodeSystemHelper.code_system_for(element['system'])})"
|
94
|
+
"#{element['code']} (#{HQMF::Util::CodeSystemHelper.code_system_for(element['system'])})#{code_description(element)}"
|
65
95
|
end
|
66
96
|
|
67
97
|
def code_system_name
|
68
98
|
HQMF::Util::CodeSystemHelper.code_system_for(self['system'])
|
69
99
|
end
|
70
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
|
+
|
71
119
|
def result_string
|
72
120
|
return unit_string if self['value']
|
73
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>
|
@@ -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}}"/>
|
@@ -58,14 +58,11 @@ class Qrda3R21 < Mustache
|
|
58
58
|
end
|
59
59
|
|
60
60
|
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
|
61
|
+
self['observation']
|
65
62
|
end
|
66
63
|
|
67
64
|
def population_observation
|
68
|
-
|
65
|
+
self['observation']
|
69
66
|
end
|
70
67
|
|
71
68
|
def supplemental_template_ids
|
@@ -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
|
@@ -16,16 +16,19 @@ module Qrda
|
|
16
16
|
|
17
17
|
def provider_npi
|
18
18
|
return nil unless self['ids']
|
19
|
+
|
19
20
|
self['ids'].map { |id| id if id['namingSystem'] == '2.16.840.1.113883.4.6' }.compact
|
20
21
|
end
|
21
22
|
|
22
23
|
def provider_tin
|
23
24
|
return nil unless self['ids']
|
25
|
+
|
24
26
|
self['ids'].map { |id| id if id['namingSystem'] == '2.16.840.1.113883.4.2' }.compact
|
25
27
|
end
|
26
28
|
|
27
29
|
def provider_ccn
|
28
30
|
return nil unless self['ids']
|
31
|
+
|
29
32
|
self['ids'].map { |id| id if id['namingSystem'] == '2.16.840.1.113883.4.336' }.compact
|
30
33
|
end
|
31
34
|
|
@@ -40,6 +43,42 @@ module Qrda
|
|
40
43
|
def given_name
|
41
44
|
self['givenNames'].join(' ')
|
42
45
|
end
|
46
|
+
|
47
|
+
def gender
|
48
|
+
gender_elements = @qdmPatient.dataElements.select { |de| de._type == "QDM::PatientCharacteristicSex" }
|
49
|
+
return if gender_elements.empty?
|
50
|
+
gender_elements.first.dataElementCodes.first['code']
|
51
|
+
end
|
52
|
+
|
53
|
+
def birthdate
|
54
|
+
birthdate_elements = @qdmPatient.dataElements.select { |de| de._type == "QDM::PatientCharacteristicBirthdate" }
|
55
|
+
return "None" if birthdate_elements.empty?
|
56
|
+
birthdate_elements.first['birthDatetime']
|
57
|
+
end
|
58
|
+
|
59
|
+
def expiration
|
60
|
+
expired_elements = @qdmPatient.dataElements.select { |de| de._type == "QDM::PatientCharacteristicExpired" }
|
61
|
+
return "None" if expired_elements.empty?
|
62
|
+
expired_elements.first['expiredDatetime']
|
63
|
+
end
|
64
|
+
|
65
|
+
def race
|
66
|
+
race_elements = @qdmPatient.dataElements.select { |de| de._type == "QDM::PatientCharacteristicRace" }
|
67
|
+
return if race_elements.empty?
|
68
|
+
race_elements.first.dataElementCodes.first['code']
|
69
|
+
end
|
70
|
+
|
71
|
+
def ethnic_group
|
72
|
+
ethnic_elements = @qdmPatient.dataElements.select { |de| de._type == "QDM::PatientCharacteristicEthnicity" }
|
73
|
+
return if ethnic_elements.empty?
|
74
|
+
ethnic_elements.first.dataElementCodes.first['code']
|
75
|
+
end
|
76
|
+
|
77
|
+
def payer
|
78
|
+
payer_elements = @qdmPatient.dataElements.select { |de| de._type == "QDM::PatientCharacteristicPayer" }
|
79
|
+
return if payer_elements.empty?
|
80
|
+
payer_elements.first.dataElementCodes.first['code']
|
81
|
+
end
|
43
82
|
end
|
44
83
|
end
|
45
84
|
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
|
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
|
@@ -9,6 +9,9 @@ module QRDA
|
|
9
9
|
@entry_id_map = {}
|
10
10
|
@check_for_usable = true
|
11
11
|
@entry_class = QDM::DataElement
|
12
|
+
@warnings = []
|
13
|
+
@codes = Set.new
|
14
|
+
@codes_modifiers = {}
|
12
15
|
end
|
13
16
|
|
14
17
|
# Traverses an HL7 CDA document passed in and creates an Array of Entry
|
@@ -41,7 +44,7 @@ module QRDA
|
|
41
44
|
# This is the id found in the QRDA file
|
42
45
|
entry_qrda_id = extract_id(entry_element, @id_xpath)
|
43
46
|
# Create a hash to map all of entry.ids to the same QRDA ids. This will be used to merge QRDA entries
|
44
|
-
# that represent the same event.
|
47
|
+
# that represent the same event.
|
45
48
|
@entry_id_map["#{entry_qrda_id.value}_#{entry_qrda_id.namingSystem}"] ||= []
|
46
49
|
@entry_id_map["#{entry_qrda_id.value}_#{entry_qrda_id.namingSystem}"] << entry.id
|
47
50
|
entry.dataElementCodes = extract_codes(entry_element, @code_xpath)
|
@@ -80,6 +83,7 @@ module QRDA
|
|
80
83
|
|
81
84
|
def code_if_present(code_element)
|
82
85
|
return unless code_element && code_element['code'] && code_element['codeSystem']
|
86
|
+
@codes.add("#{code_element['code']}:#{code_element['codeSystem']}")
|
83
87
|
QDM::Code.new(code_element['code'], code_element['codeSystem'])
|
84
88
|
end
|
85
89
|
|
@@ -93,7 +97,9 @@ module QRDA
|
|
93
97
|
end
|
94
98
|
|
95
99
|
def extract_interval(parent_element, interval_xpath)
|
96
|
-
|
100
|
+
# nil if the time interval does not exist
|
101
|
+
return nil unless time_interval_exists(parent_element, interval_xpath)
|
102
|
+
|
97
103
|
if parent_element.at_xpath("#{interval_xpath}/@value")
|
98
104
|
low_time = DateTime.parse(parent_element.at_xpath(interval_xpath)['value'])
|
99
105
|
high_time = DateTime.parse(parent_element.at_xpath(interval_xpath)['value'])
|
@@ -112,9 +118,32 @@ module QRDA
|
|
112
118
|
low_time = Time.parse(parent_element.at_xpath("#{interval_xpath}/cda:center")['value'])
|
113
119
|
high_time = Time.parse(parent_element.at_xpath("#{interval_xpath}/cda:center")['value'])
|
114
120
|
end
|
121
|
+
if low_time && high_time && low_time > high_time
|
122
|
+
# pass warning: current code continues as expected, but adds warning
|
123
|
+
id_attr = parent_element.at_xpath(".//cda:id")&.attributes
|
124
|
+
id_str = id_attr ? "and id: #{id_attr['root']&.value}(root), #{id_attr['extension']&.value}(extension)" : ""
|
125
|
+
qrda_type = @entry_class.to_s.split("::")[1]
|
126
|
+
@warnings << ValidationError.new(message: "Interval with low time after high time. Located in element with QRDA type: #{qrda_type} #{id_str}",
|
127
|
+
location: parent_element.path)
|
128
|
+
end
|
129
|
+
if low_time.nil? && high_time.nil?
|
130
|
+
id_attr = parent_element.at_xpath(".//cda:id")&.attributes
|
131
|
+
id_str = id_attr ? "and id: #{id_attr['root']&.value}(root), #{id_attr['extension']&.value}(extension)" : ""
|
132
|
+
qrda_type = @entry_class.to_s.split("::")[1]
|
133
|
+
@warnings << ValidationError.new(message: "Interval with nullFlavor low time and nullFlavor high time. Located in element with QRDA type: #{qrda_type} #{id_str}",
|
134
|
+
location: parent_element.path)
|
135
|
+
end
|
115
136
|
QDM::Interval.new(low_time, high_time).shift_dates(0)
|
116
137
|
end
|
117
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
|
+
|
118
147
|
def extract_time(parent_element, datetime_xpath)
|
119
148
|
DateTime.parse(parent_element.at_xpath(datetime_xpath)['value']) if parent_element.at_xpath("#{datetime_xpath}/@value")
|
120
149
|
end
|
@@ -129,6 +158,7 @@ module QRDA
|
|
129
158
|
# If a Direct Reference Code isn't found, return nil
|
130
159
|
return nil unless key
|
131
160
|
# If a Direct Reference Code is found, return that code
|
161
|
+
@codes.add("#{key}:#{value[:code_system]}")
|
132
162
|
QDM::Code.new(key, value[:code_system])
|
133
163
|
end
|
134
164
|
|
@@ -154,7 +184,7 @@ module QRDA
|
|
154
184
|
parent_element.xpath(@result_xpath).each do |elem|
|
155
185
|
result << extract_result_value(elem)
|
156
186
|
end
|
157
|
-
result.size > 1 ? result : result.first
|
187
|
+
result.size > 1 ? result : result.first
|
158
188
|
end
|
159
189
|
|
160
190
|
def extract_result_value(value_element)
|
@@ -167,6 +197,11 @@ module QRDA
|
|
167
197
|
elsif value_element['code'].present?
|
168
198
|
return code_if_present(value_element)
|
169
199
|
elsif value_element.text.present?
|
200
|
+
id_attr = value_element.parent.at_xpath(".//cda:id")&.attributes
|
201
|
+
id_str = id_attr ? "and id: #{id_attr['root']&.value}(root), #{id_attr['extension']&.value}(extension)" : ""
|
202
|
+
qrda_type = @entry_class.to_s.split("::")[1]
|
203
|
+
@warnings << ValidationError.new(message: "Value with string type found. When possible, it's best practice to use a coded value or scalar. Located in element with QRDA type: #{qrda_type} #{id_str}",
|
204
|
+
location: value_element.path)
|
170
205
|
return value_element.text
|
171
206
|
end
|
172
207
|
end
|
@@ -177,16 +212,16 @@ module QRDA
|
|
177
212
|
negation_indicator = parent_element['negationInd']
|
178
213
|
# Return and do not set reason attribute if the entry is negated
|
179
214
|
return nil if negation_indicator.eql?('true')
|
180
|
-
|
181
|
-
reason_element.blank? ? nil : code_if_present(reason_element.first)
|
215
|
+
|
216
|
+
reason_element.blank? ? nil : code_if_present(reason_element.first)
|
182
217
|
end
|
183
218
|
|
184
219
|
def extract_negation(parent_element, entry)
|
185
220
|
negation_element = parent_element.xpath("./cda:entryRelationship[@typeCode='RSON']/cda:observation[cda:templateId/@root='2.16.840.1.113883.10.20.24.3.88']/cda:value")
|
186
221
|
negation_indicator = parent_element['negationInd']
|
187
222
|
# Return and do not set negationRationale attribute if the entry is not negated
|
188
|
-
return unless negation_indicator.eql?('true')
|
189
|
-
|
223
|
+
return unless negation_indicator.eql?('true')
|
224
|
+
|
190
225
|
entry.negationRationale = code_if_present(negation_element.first) unless negation_element.blank?
|
191
226
|
extract_negated_code(parent_element, entry)
|
192
227
|
end
|
@@ -194,8 +229,18 @@ module QRDA
|
|
194
229
|
def extract_negated_code(parent_element, entry)
|
195
230
|
code_elements = parent_element.xpath(@code_xpath)
|
196
231
|
code_elements.each do |code_element|
|
197
|
-
if code_element['nullFlavor'] == 'NA'
|
198
|
-
|
232
|
+
if code_element['nullFlavor'] == 'NA'
|
233
|
+
if code_element['sdtc:valueSet']
|
234
|
+
entry.dataElementCodes = [{ code: code_element['sdtc:valueSet'], system: '1.2.3.4.5.6.7.8.9.10' }]
|
235
|
+
else
|
236
|
+
# negated code is nullFlavored with no valueset
|
237
|
+
entry.dataElementCodes = [{ code: "NA", system: '1.2.3.4.5.6.7.8.9.10' }]
|
238
|
+
id_attr = parent_element.at_xpath(".//cda:id")&.attributes
|
239
|
+
id_str = id_attr ? "and id: #{id_attr['root']&.value}(root), #{id_attr['extension']&.value}(extension)" : ""
|
240
|
+
qrda_type = @entry_class.to_s.split("::")[1]
|
241
|
+
@warnings << ValidationError.new(message: "Negated code element contains nullFlavor code but no valueset. Located in element with QRDA type: #{qrda_type} #{id_str}.",
|
242
|
+
location: parent_element.path)
|
243
|
+
end
|
199
244
|
end
|
200
245
|
end
|
201
246
|
end
|
@@ -227,7 +272,7 @@ module QRDA
|
|
227
272
|
participant_element = facility_location_element.at_xpath("./cda:participantRole[@classCode='SDLOC']/cda:code")
|
228
273
|
facility_location.code = code_if_present(participant_element)
|
229
274
|
facility_location.locationPeriod = extract_interval(facility_location_element, './cda:time')
|
230
|
-
facility_locations << facility_location
|
275
|
+
facility_locations << facility_location if facility_location.code
|
231
276
|
end
|
232
277
|
facility_locations
|
233
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 = []
|
@@ -12,70 +12,73 @@ module QRDA
|
|
12
12
|
def initialize
|
13
13
|
# This differs from other HDS patient importers in that sections can have multiple importers
|
14
14
|
@data_element_importers = []
|
15
|
-
@data_element_importers <<
|
16
|
-
@data_element_importers <<
|
17
|
-
@data_element_importers <<
|
18
|
-
@data_element_importers <<
|
19
|
-
@data_element_importers <<
|
20
|
-
@data_element_importers <<
|
21
|
-
@data_element_importers <<
|
22
|
-
@data_element_importers <<
|
23
|
-
@data_element_importers <<
|
24
|
-
@data_element_importers <<
|
25
|
-
@data_element_importers <<
|
26
|
-
@data_element_importers <<
|
27
|
-
@data_element_importers <<
|
28
|
-
@data_element_importers <<
|
29
|
-
@data_element_importers <<
|
30
|
-
@data_element_importers <<
|
31
|
-
@data_element_importers <<
|
32
|
-
@data_element_importers <<
|
33
|
-
@data_element_importers <<
|
34
|
-
@data_element_importers <<
|
35
|
-
@data_element_importers <<
|
36
|
-
@data_element_importers <<
|
37
|
-
@data_element_importers <<
|
38
|
-
@data_element_importers <<
|
39
|
-
@data_element_importers <<
|
40
|
-
@data_element_importers <<
|
41
|
-
@data_element_importers <<
|
42
|
-
@data_element_importers <<
|
43
|
-
@data_element_importers <<
|
44
|
-
@data_element_importers <<
|
45
|
-
@data_element_importers <<
|
46
|
-
@data_element_importers <<
|
47
|
-
@data_element_importers <<
|
48
|
-
@data_element_importers <<
|
49
|
-
@data_element_importers <<
|
50
|
-
@data_element_importers <<
|
51
|
-
@data_element_importers <<
|
52
|
-
@data_element_importers <<
|
53
|
-
@data_element_importers <<
|
54
|
-
@data_element_importers <<
|
55
|
-
@data_element_importers <<
|
56
|
-
@data_element_importers <<
|
57
|
-
@data_element_importers <<
|
58
|
-
@data_element_importers <<
|
59
|
-
@data_element_importers <<
|
60
|
-
@data_element_importers <<
|
61
|
-
@data_element_importers <<
|
15
|
+
@data_element_importers << AdverseEventImporter.new
|
16
|
+
@data_element_importers << AllergyIntoleranceImporter.new
|
17
|
+
@data_element_importers << AssessmentOrderImporter.new
|
18
|
+
@data_element_importers << AssessmentPerformedImporter.new
|
19
|
+
@data_element_importers << AssessmentRecommendedImporter.new
|
20
|
+
@data_element_importers << CommunicationPerformedImporter.new
|
21
|
+
@data_element_importers << DeviceAppliedImporter.new
|
22
|
+
@data_element_importers << DeviceOrderImporter.new
|
23
|
+
@data_element_importers << DeviceRecommendedImporter.new
|
24
|
+
@data_element_importers << DiagnosisImporter.new
|
25
|
+
@data_element_importers << DiagnosticStudyOrderImporter.new
|
26
|
+
@data_element_importers << DiagnosticStudyPerformedImporter.new
|
27
|
+
@data_element_importers << DiagnosticStudyRecommendedImporter.new
|
28
|
+
@data_element_importers << EncounterOrderImporter.new
|
29
|
+
@data_element_importers << EncounterPerformedImporter.new
|
30
|
+
@data_element_importers << EncounterRecommendedImporter.new
|
31
|
+
@data_element_importers << FamilyHistoryImporter.new
|
32
|
+
@data_element_importers << ImmunizationAdministeredImporter.new
|
33
|
+
@data_element_importers << ImmunizationOrderImporter.new
|
34
|
+
@data_element_importers << InterventionOrderImporter.new
|
35
|
+
@data_element_importers << InterventionPerformedImporter.new
|
36
|
+
@data_element_importers << InterventionRecommendedImporter.new
|
37
|
+
@data_element_importers << LaboratoryTestOrderImporter.new
|
38
|
+
@data_element_importers << LaboratoryTestPerformedImporter.new
|
39
|
+
@data_element_importers << LaboratoryTestRecommendedImporter.new
|
40
|
+
@data_element_importers << MedicationActiveImporter.new
|
41
|
+
@data_element_importers << MedicationAdministeredImporter.new
|
42
|
+
@data_element_importers << MedicationDischargeImporter.new
|
43
|
+
@data_element_importers << MedicationDispensedImporter.new
|
44
|
+
@data_element_importers << MedicationOrderImporter.new
|
45
|
+
@data_element_importers << PatientCareExperienceImporter.new
|
46
|
+
@data_element_importers << PatientCharacteristicClinicalTrialParticipantImporter.new
|
47
|
+
@data_element_importers << PatientCharacteristicExpiredImporter.new
|
48
|
+
@data_element_importers << PatientCharacteristicPayerImporter.new
|
49
|
+
@data_element_importers << PhysicalExamOrderImporter.new
|
50
|
+
@data_element_importers << PhysicalExamPerformedImporter.new
|
51
|
+
@data_element_importers << PhysicalExamRecommendedImporter.new
|
52
|
+
@data_element_importers << ProcedureOrderImporter.new
|
53
|
+
@data_element_importers << ProcedurePerformedImporter.new
|
54
|
+
@data_element_importers << ProcedureRecommendedImporter.new
|
55
|
+
@data_element_importers << ProgramParticipationImporter.new
|
56
|
+
@data_element_importers << ProviderCareExperienceImporter.new
|
57
|
+
@data_element_importers << RelatedPersonImporter.new
|
58
|
+
@data_element_importers << SubstanceAdministeredImporter.new
|
59
|
+
@data_element_importers << SubstanceOrderImporter.new
|
60
|
+
@data_element_importers << SubstanceRecommendedImporter.new
|
61
|
+
@data_element_importers << SymptomImporter.new
|
62
62
|
end
|
63
63
|
|
64
64
|
def parse_cat1(doc)
|
65
65
|
patient = CQM::Patient.new
|
66
|
+
warnings = []
|
67
|
+
codes = Set.new
|
68
|
+
codes_modifiers = {}
|
66
69
|
entry_id_map = {}
|
67
|
-
import_data_elements(patient, doc, entry_id_map)
|
70
|
+
import_data_elements(patient, doc, entry_id_map, codes, codes_modifiers, warnings)
|
68
71
|
normalize_references(patient, entry_id_map)
|
69
|
-
get_demographics(patient, doc)
|
70
|
-
patient
|
72
|
+
get_demographics(patient, doc, codes)
|
73
|
+
[patient, warnings, codes, codes_modifiers]
|
71
74
|
end
|
72
75
|
|
73
|
-
def import_data_elements(patient, doc, entry_id_map)
|
76
|
+
def import_data_elements(patient, doc, entry_id_map, codes = Set.new, codes_modifiers = {}, warnings = [])
|
74
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']")
|
75
78
|
nrh = NarrativeReferenceHandler.new
|
76
79
|
nrh.build_id_map(doc)
|
77
|
-
@data_element_importers.each do |
|
78
|
-
data_elements, id_map =
|
80
|
+
@data_element_importers.each do |importer|
|
81
|
+
data_elements, id_map = importer.create_entries(context, nrh)
|
79
82
|
new_data_elements = []
|
80
83
|
|
81
84
|
id_map.each_value do |elem_ids|
|
@@ -92,7 +95,7 @@ module QRDA
|
|
92
95
|
unique_element_keys << key_elements_for_determining_encounter_uniqueness(data_element)
|
93
96
|
|
94
97
|
# Loop through all other data elements with the same id
|
95
|
-
elem_ids[1,elem_ids.length].each do |dup_id|
|
98
|
+
elem_ids[1,elem_ids.length].each do |dup_id|
|
96
99
|
dup_element = data_elements.find { |de| de.id == dup_id }
|
97
100
|
dup_element_keys = key_elements_for_determining_encounter_uniqueness(dup_element)
|
98
101
|
# See if a previously selected data element shared all of the keys files
|
@@ -107,6 +110,13 @@ module QRDA
|
|
107
110
|
|
108
111
|
patient.qdmPatient.dataElements << new_data_elements
|
109
112
|
entry_id_map.merge!(id_map)
|
113
|
+
warnings.concat(importer.warnings)
|
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
|
117
|
+
importer.warnings = []
|
118
|
+
importer.codes_modifiers = {}
|
119
|
+
importer.codes = Set.new
|
110
120
|
end
|
111
121
|
end
|
112
122
|
|
@@ -136,12 +146,6 @@ module QRDA
|
|
136
146
|
data_element.relatedTo = relations_to_add.map(&:to_s)
|
137
147
|
end
|
138
148
|
end
|
139
|
-
|
140
|
-
private
|
141
|
-
|
142
|
-
def generate_importer(importer_class)
|
143
|
-
EntryPackage.new(importer_class.new)
|
144
|
-
end
|
145
149
|
end
|
146
150
|
end
|
147
151
|
end
|
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.
|
4
|
+
version: 3.1.4
|
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-06-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cqm-models
|
@@ -16,14 +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
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: cqm-validators
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: mustache
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -70,16 +84,22 @@ dependencies:
|
|
70
84
|
name: nokogiri
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
72
86
|
requirements:
|
73
|
-
- - "
|
87
|
+
- - ">="
|
74
88
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
89
|
+
version: 1.8.5
|
90
|
+
- - "<"
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: 1.12.0
|
76
93
|
type: :runtime
|
77
94
|
prerelease: false
|
78
95
|
version_requirements: !ruby/object:Gem::Requirement
|
79
96
|
requirements:
|
80
|
-
- - "
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: 1.8.5
|
100
|
+
- - "<"
|
81
101
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
102
|
+
version: 1.12.0
|
83
103
|
- !ruby/object:Gem::Dependency
|
84
104
|
name: uuid
|
85
105
|
requirement: !ruby/object:Gem::Requirement
|
@@ -314,7 +334,6 @@ files:
|
|
314
334
|
- lib/qrda-import/data-element-importers/substance_recommended_importer.rb
|
315
335
|
- lib/qrda-import/data-element-importers/symptom_importer.rb
|
316
336
|
- lib/qrda-import/entry_finder.rb
|
317
|
-
- lib/qrda-import/entry_package.rb
|
318
337
|
- lib/qrda-import/narrative_reference_handler.rb
|
319
338
|
- lib/qrda-import/patient_importer.rb
|
320
339
|
- lib/util/code_system_helper.rb
|
@@ -330,7 +349,7 @@ homepage: https://github.com/projecttacoma/cqm-reports
|
|
330
349
|
licenses:
|
331
350
|
- Apache-2.0
|
332
351
|
metadata: {}
|
333
|
-
post_install_message:
|
352
|
+
post_install_message:
|
334
353
|
rdoc_options: []
|
335
354
|
require_paths:
|
336
355
|
- lib
|
@@ -345,9 +364,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
345
364
|
- !ruby/object:Gem::Version
|
346
365
|
version: '0'
|
347
366
|
requirements: []
|
348
|
-
|
349
|
-
|
350
|
-
signing_key:
|
367
|
+
rubygems_version: 3.1.4
|
368
|
+
signing_key:
|
351
369
|
specification_version: 4
|
352
370
|
summary: A library for import and export of reports for use with electronic Clinical
|
353
371
|
Quality Measures (eCQMs).
|