health-data-standards 3.1.1 → 3.2.0
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.
- data/Rakefile +2 -8
- data/lib/health-data-standards.rb +4 -2
- data/lib/health-data-standards/export/hdata/metadata.rb +2 -2
- data/lib/health-data-standards/export/helper/cat1_view_helper.rb +7 -101
- data/lib/health-data-standards/export/helper/html_view_helper.rb +2 -0
- data/lib/health-data-standards/export/helper/scooped_view_helper.rb +109 -0
- data/lib/health-data-standards/export/html.rb +2 -2
- data/lib/health-data-standards/export/qrda/entry_template_resolver.rb +29 -5
- data/lib/health-data-standards/export/view_helper.rb +2 -2
- data/lib/health-data-standards/import/bundle/importer.rb +8 -2
- data/lib/health-data-standards/import/cda/result_importer.rb +5 -0
- data/lib/health-data-standards/import/hdata/metadata_importer.rb +1 -1
- data/lib/health-data-standards/models/condition.rb +5 -0
- data/lib/health-data-standards/models/cqm/measure.rb +118 -1
- data/lib/health-data-standards/models/cqm/patient_cache.rb +60 -0
- data/lib/health-data-standards/models/encounter.rb +11 -0
- data/lib/health-data-standards/models/entry.rb +8 -0
- data/lib/health-data-standards/models/facility.rb +5 -0
- data/lib/health-data-standards/models/fulfillment_history.rb +4 -0
- data/lib/health-data-standards/models/guarantor.rb +5 -0
- data/lib/health-data-standards/models/insurance_provider.rb +10 -0
- data/lib/health-data-standards/models/medical_equipment.rb +5 -0
- data/lib/health-data-standards/models/medication.rb +11 -0
- data/lib/health-data-standards/models/order_information.rb +6 -0
- data/lib/health-data-standards/models/procedure.rb +5 -0
- data/lib/health-data-standards/models/provider_performance.rb +5 -0
- data/lib/health-data-standards/models/record.rb +16 -0
- data/lib/health-data-standards/models/thing_with_codes.rb +35 -5
- data/lib/health-data-standards/util/code_system_helper.rb +12 -3
- data/lib/hqmf-model/data_criteria.json +2 -2
- data/lib/hqmf-model/document.rb +9 -2
- data/lib/hqmf-parser/1.0/document.rb +6 -1
- data/templates/_author.hdata.erb +3 -0
- data/templates/_pedigree.hdata.erb +16 -15
- data/templates/cat1/_2.16.840.1.113883.10.20.22.4.85.cat1.erb +1 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.1.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.103.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.105.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.11.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.12.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.13.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.14.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.17.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.18.cat1.erb +2 -2
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.2.cat1.erb +2 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.20.cat1.erb +2 -2
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.22.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.23.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.28.cat1.erb +2 -2
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.3.cat1.erb +3 -2
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.31.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.32.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.34.cat1.erb +2 -2
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.37.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.38.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.4.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.40.cat1.erb +2 -2
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.41.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.42.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.43.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.44.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.45.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.46.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.47.cat1.erb +2 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.55.cat1.erb +2 -2
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.57.cat1.erb +2 -2
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.59.cat1.erb +2 -2
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.62.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.63.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.64.cat1.erb +6 -2
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.66.cat1.erb +3 -3
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.69.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.7.cat1.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.76.cat1.erb +1 -1
- data/templates/cat1/_measures.cat1.erb +0 -2
- data/templates/cat1/_medication_details.cat1.erb +3 -3
- data/templates/cat1/_patient_data.cat1.erb +3 -0
- data/templates/cat1/_record_target.cat1.erb +6 -1
- data/templates/cat1/_result_value.cat1.erb +16 -13
- data/templates/html/_entries_by_section.html.erb +13 -5
- data/templates/html/show.html.erb +4 -2
- data/templates/metadata.hdata.erb +27 -24
- metadata +5 -2
data/Rakefile
CHANGED
|
@@ -10,15 +10,9 @@ Rake::TestTask.new(:test_unit) do |t|
|
|
|
10
10
|
t.verbose = true
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
Cane::RakeTask.new(:quality) do |cane|
|
|
14
|
-
cane.abc_max = 45
|
|
15
|
-
# cane.add_threshold 'coverage/covered_percent', :>=, 97
|
|
16
|
-
cane.style_measure = 120
|
|
17
|
-
cane.no_style = true
|
|
18
|
-
cane.no_doc = true
|
|
19
|
-
end
|
|
20
13
|
|
|
21
|
-
|
|
14
|
+
|
|
15
|
+
task :test => [:test_unit] do
|
|
22
16
|
|
|
23
17
|
system("open coverage/index.html")
|
|
24
18
|
end
|
|
@@ -25,8 +25,6 @@ require_relative 'health-data-standards/export/c32'
|
|
|
25
25
|
require_relative 'health-data-standards/export/ccda'
|
|
26
26
|
require_relative 'health-data-standards/export/ccr'
|
|
27
27
|
require_relative 'health-data-standards/export/csv'
|
|
28
|
-
require_relative 'health-data-standards/export/helper/html_view_helper'
|
|
29
|
-
require_relative 'health-data-standards/export/html'
|
|
30
28
|
require_relative 'health-data-standards/export/hdata/metadata'
|
|
31
29
|
|
|
32
30
|
require_relative 'health-data-standards/export/helper/gc32_view_helper'
|
|
@@ -76,6 +74,9 @@ require_relative 'health-data-standards/models/metadata/link_info'
|
|
|
76
74
|
require_relative 'health-data-standards/models/metadata/pedigree'
|
|
77
75
|
|
|
78
76
|
require_relative 'health-data-standards/export/qrda/entry_template_resolver'
|
|
77
|
+
require_relative 'health-data-standards/export/helper/scooped_view_helper'
|
|
78
|
+
require_relative 'health-data-standards/export/helper/html_view_helper'
|
|
79
|
+
require_relative 'health-data-standards/export/html'
|
|
79
80
|
require_relative 'health-data-standards/export/helper/cat1_view_helper'
|
|
80
81
|
require_relative 'health-data-standards/export/cat_1'
|
|
81
82
|
require_relative 'health-data-standards/export/cat_3'
|
|
@@ -158,6 +159,7 @@ require_relative 'health-data-standards/models/cqm/aggregate_objects'
|
|
|
158
159
|
require_relative 'health-data-standards/models/cqm/query_cache'
|
|
159
160
|
require_relative 'health-data-standards/models/cqm/bundle'
|
|
160
161
|
require_relative 'health-data-standards/models/cqm/measure'
|
|
162
|
+
require_relative 'health-data-standards/models/cqm/patient_cache'
|
|
161
163
|
require_relative 'health-data-standards/import/bundle/importer'
|
|
162
164
|
|
|
163
165
|
|
|
@@ -8,8 +8,8 @@ module HealthDataStandards
|
|
|
8
8
|
@rendering_context.template_helper = template_helper
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
def export(entry, metadata)
|
|
12
|
-
@rendering_context.render(:template => 'metadata', :locals => {entry: entry, metadata: metadata})
|
|
11
|
+
def export(entry, metadata, include_namespace=false)
|
|
12
|
+
@rendering_context.render(:template => 'metadata', :locals => {entry: entry, metadata: metadata, namespace: include_namespace})
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
15
|
end
|
|
@@ -2,91 +2,16 @@ module HealthDataStandards
|
|
|
2
2
|
module Export
|
|
3
3
|
module Helper
|
|
4
4
|
module Cat1ViewHelper
|
|
5
|
-
include HealthDataStandards::
|
|
6
|
-
include HealthDataStandards::SVS
|
|
7
|
-
|
|
8
|
-
def value_set_map
|
|
9
|
-
@@vs_map ||= Hash[*ValueSet.all.map{ |p| [p.oid, p] }.flatten]
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
# Find all of the entries on a patient that match the given data criteria
|
|
13
|
-
def entries_for_data_criteria(data_criteria, patient)
|
|
14
|
-
data_criteria_oid = HQMFTemplateHelper.template_id_by_definition_and_status(data_criteria.definition,
|
|
15
|
-
data_criteria.status || '',
|
|
16
|
-
data_criteria.negation)
|
|
17
|
-
filtered_entries = []
|
|
18
|
-
case data_criteria_oid
|
|
19
|
-
when '2.16.840.1.113883.3.560.1.404'
|
|
20
|
-
filtered_entries = handle_patient_expired(patient)
|
|
21
|
-
when '2.16.840.1.113883.3.560.1.401'
|
|
22
|
-
filtered_entries = handle_clinical_trial_participant(patient)
|
|
23
|
-
when '2.16.840.1.113883.3.560.1.405'
|
|
24
|
-
filtered_entries = handle_payer_information(patient)
|
|
25
|
-
else
|
|
26
|
-
entries = patient.entries_for_oid(data_criteria_oid)
|
|
27
|
-
codes = []
|
|
28
|
-
vs = value_set_map[data_criteria.code_list_id]
|
|
29
|
-
if vs
|
|
30
|
-
codes = vs.code_set_map
|
|
31
|
-
else
|
|
32
|
-
HealthDataStandards.logger.warn("No codes for #{data_criteria.code_list_id}")
|
|
33
|
-
end
|
|
34
|
-
filtered_entries = entries.find_all do |entry|
|
|
35
|
-
# This special case is for when the code list is a reason
|
|
36
|
-
if data_criteria.code_list_id =~ /2\.16\.840\.1\.113883\.3\.526\.3\.100[7-9]/
|
|
37
|
-
entry.negation_reason.present? && codes.first['values'].include?(entry.negation_reason['code'])
|
|
38
|
-
else
|
|
39
|
-
# The !! hack makes sure that negation_ind is a boolean
|
|
40
|
-
entry.is_in_code_set?(codes) && !!entry.negation_ind == data_criteria.negation
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
if filtered_entries.empty?
|
|
45
|
-
HealthDataStandards.logger.debug("No entries for #{data_criteria.title}")
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
filtered_entries
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# Given a set of measures, find the data criteria/value set pairs that are unique across all of them
|
|
52
|
-
# Returns an Array of Hashes. Hashes will have a three key/value pairs. One for the data criteria oid,
|
|
53
|
-
# one for the value set oid and one for the data criteria itself
|
|
54
|
-
def unique_data_criteria(measures)
|
|
55
|
-
all_data_criteria = measures.map {|measure| measure.all_data_criteria}.flatten
|
|
56
|
-
dc_oids_and_vs_oids = all_data_criteria.map do |data_criteria|
|
|
57
|
-
data_criteria_oid = HQMFTemplateHelper.template_id_by_definition_and_status(data_criteria.definition,
|
|
58
|
-
(data_criteria.status || ""),
|
|
59
|
-
data_criteria.negation)
|
|
60
|
-
value_set_oid = data_criteria.code_list_id
|
|
61
|
-
{'data_criteria_oid' => data_criteria_oid, 'value_set_oid' => value_set_oid, 'data_criteria' => data_criteria}
|
|
62
|
-
end
|
|
63
|
-
dc_oids_and_vs_oids.uniq_by {|thingy| [thingy['data_criteria_oid'], thingy['value_set_oid']]}
|
|
64
|
-
end
|
|
5
|
+
include HealthDataStandards::Export::Helper::ScoopedViewHelper
|
|
65
6
|
|
|
66
7
|
def render_data_criteria(dc_oid, vs_oid, entries)
|
|
67
8
|
html_array = entries.map do |entry|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
# Patient Characteristic: ECOG Performance Status-Poor
|
|
75
|
-
render(:partial => '2.16.840.1.113883.10.20.24.3.103', :locals => {:entry => entry,
|
|
76
|
-
:value_set_oid => vs_oid})
|
|
77
|
-
elsif vs_oid == "2.16.840.1.113883.3.117.1.7.1.402" || vs_oid == "2.16.840.1.113883.3.117.1.7.1.403"
|
|
78
|
-
# Patient Charasteristic Gestational Age
|
|
79
|
-
render(:partial => '2.16.840.1.113883.10.20.24.3.101', :locals => {:entry => entry,
|
|
80
|
-
:value_set_oid => vs_oid})
|
|
81
|
-
elsif vs_oid == "2.16.840.1.113883.3.526.3.1189" || vs_oid == "2.16.840.1.113883.3.526.3.1170"
|
|
82
|
-
# Patient Characteristic Tobacco User/Non-User
|
|
83
|
-
render(:partial => '2.16.840.1.113883.10.20.22.4.85', :locals => {:entry => entry,
|
|
84
|
-
:value_set_oid => vs_oid})
|
|
85
|
-
end
|
|
86
|
-
else
|
|
87
|
-
render(:partial => HealthDataStandards::Export::QRDA::EntryTemplateResolver.partial_for(dc_oid), :locals => {:entry => entry,
|
|
88
|
-
:value_set_oid => vs_oid})
|
|
89
|
-
end
|
|
9
|
+
bundle_id = entry.record ? entry.record["bundle_id"] : nil
|
|
10
|
+
vs_map = (value_set_map(bundle_id) || {})[vs_oid]
|
|
11
|
+
binding.pry if vs_map.nil? || vs_map.empty?
|
|
12
|
+
render(:partial => HealthDataStandards::Export::QRDA::EntryTemplateResolver.partial_for(dc_oid, vs_oid), :locals => {:entry => entry,
|
|
13
|
+
:value_set_oid => vs_oid,
|
|
14
|
+
:value_set_map => vs_map})
|
|
90
15
|
end
|
|
91
16
|
html_array.join("\n")
|
|
92
17
|
end
|
|
@@ -108,25 +33,6 @@ module HealthDataStandards
|
|
|
108
33
|
end
|
|
109
34
|
end
|
|
110
35
|
|
|
111
|
-
def handle_clinical_trial_participant(patient)
|
|
112
|
-
if patient.clinical_trial_participant
|
|
113
|
-
[{dummy_entry: true}]
|
|
114
|
-
else
|
|
115
|
-
[]
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def handle_patient_expired(patient)
|
|
120
|
-
if patient.expired
|
|
121
|
-
[OpenStruct.new(start_date: patient.deathdate)]
|
|
122
|
-
else
|
|
123
|
-
[]
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
def handle_payer_information(patient)
|
|
128
|
-
patient.insurance_providers
|
|
129
|
-
end
|
|
130
36
|
end
|
|
131
37
|
end
|
|
132
38
|
end
|
|
@@ -2,6 +2,8 @@ module HealthDataStandards
|
|
|
2
2
|
module Export
|
|
3
3
|
module Helper
|
|
4
4
|
module HTMLViewHelper
|
|
5
|
+
include HealthDataStandards::Export::Helper::ScoopedViewHelper
|
|
6
|
+
|
|
5
7
|
def decode_hqmf_section(section, oid)
|
|
6
8
|
if oid
|
|
7
9
|
HealthDataStandards::Util::HQMFTemplateHelper.definition_for_template_id(oid)['definition'].pluralize.to_sym
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
module HealthDataStandards
|
|
2
|
+
module Export
|
|
3
|
+
module Helper
|
|
4
|
+
module ScoopedViewHelper
|
|
5
|
+
include HealthDataStandards::Util
|
|
6
|
+
include HealthDataStandards::SVS
|
|
7
|
+
VS_MAP = {}
|
|
8
|
+
def value_set_map(bundle_id=nil)
|
|
9
|
+
|
|
10
|
+
VS_MAP[bundle_id] ||= Hash[ValueSet.where({bundle_id: bundle_id}).map{ |p| [p.oid, p.code_set_map] }]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Given a set of measures, find the data criteria/value set pairs that are unique across all of them
|
|
14
|
+
# Returns an Array of Hashes. Hashes will have a three key/value pairs. One for the data criteria oid,
|
|
15
|
+
# one for the value set oid and one for the data criteria itself
|
|
16
|
+
def unique_data_criteria(measures)
|
|
17
|
+
all_data_criteria = measures.map {|measure| measure.all_data_criteria}.flatten
|
|
18
|
+
unioned_data_criteria = all_data_criteria.map do |data_criteria|
|
|
19
|
+
data_criteria_oid = HQMFTemplateHelper.template_id_by_definition_and_status(data_criteria.definition,
|
|
20
|
+
(data_criteria.status || ""),
|
|
21
|
+
data_criteria.negation)
|
|
22
|
+
value_set_oid = data_criteria.code_list_id
|
|
23
|
+
{'data_criteria_oid' => data_criteria_oid, 'value_set_oid' => value_set_oid, 'data_criteria' => data_criteria}
|
|
24
|
+
end
|
|
25
|
+
unioned_data_criteria.uniq_by {|thingy| [thingy['data_criteria_oid'], thingy['value_set_oid']]}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Returns true if the supplied entry matches any of the supplied data criteria, false otherwise
|
|
29
|
+
def entry_matches_criteria(entry, data_criteria_info_list)
|
|
30
|
+
data_criteria_info_list.each do |data_criteria_info|
|
|
31
|
+
data_criteria = data_criteria_info['data_criteria']
|
|
32
|
+
data_criteria_oid = HQMFTemplateHelper.template_id_by_definition_and_status(data_criteria.definition,
|
|
33
|
+
data_criteria.status || '',
|
|
34
|
+
data_criteria.negation)
|
|
35
|
+
if entry.respond_to?(:oid) && (entry.oid == data_criteria_oid)
|
|
36
|
+
codes = *(value_set_map(entry.record["bundle_id"])[data_criteria_info['value_set_oid']] || [])
|
|
37
|
+
if codes.empty?
|
|
38
|
+
HealthDataStandards.logger.warn("No codes for #{data_criteria_info['value_set_oid']}")
|
|
39
|
+
end
|
|
40
|
+
if entry.is_in_code_set?(codes) && !!entry.negation_ind == data_criteria.negation
|
|
41
|
+
# The !! hack makes sure that negation_ind is a boolean
|
|
42
|
+
return true
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Find all of the entries on a patient that match the given data criteria
|
|
51
|
+
def entries_for_data_criteria(data_criteria, patient)
|
|
52
|
+
data_criteria_oid = HQMFTemplateHelper.template_id_by_definition_and_status(data_criteria.definition,
|
|
53
|
+
data_criteria.status || '',
|
|
54
|
+
data_criteria.negation)
|
|
55
|
+
filtered_entries = []
|
|
56
|
+
case data_criteria_oid
|
|
57
|
+
when '2.16.840.1.113883.3.560.1.404'
|
|
58
|
+
filtered_entries = handle_patient_expired(patient)
|
|
59
|
+
when '2.16.840.1.113883.3.560.1.401'
|
|
60
|
+
filtered_entries = handle_clinical_trial_participant(patient)
|
|
61
|
+
when '2.16.840.1.113883.3.560.1.405'
|
|
62
|
+
filtered_entries = handle_payer_information(patient)
|
|
63
|
+
else
|
|
64
|
+
entries = patient.entries_for_oid(data_criteria_oid)
|
|
65
|
+
codes = (value_set_map(patient["bundle_id"])[data_criteria.code_list_id] || [])
|
|
66
|
+
if codes.empty?
|
|
67
|
+
HealthDataStandards.logger.warn("No codes for #{data_criteria.code_list_id}")
|
|
68
|
+
end
|
|
69
|
+
filtered_entries = entries.find_all do |entry|
|
|
70
|
+
# This special case is for when the code list is a reason
|
|
71
|
+
if data_criteria.code_list_id =~ /2\.16\.840\.1\.113883\.3\.526\.3\.100[7-9]/
|
|
72
|
+
entry.negation_reason.present? && codes.first['values'].include?(entry.negation_reason['code'])
|
|
73
|
+
else
|
|
74
|
+
# The !! hack makes sure that negation_ind is a boolean
|
|
75
|
+
entry.is_in_code_set?(codes) && !!entry.negation_ind == data_criteria.negation
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
if filtered_entries.empty?
|
|
80
|
+
HealthDataStandards.logger.debug("No entries for #{data_criteria.title}")
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
filtered_entries
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def handle_clinical_trial_participant(patient)
|
|
87
|
+
if patient.clinical_trial_participant
|
|
88
|
+
[{dummy_entry: true}]
|
|
89
|
+
else
|
|
90
|
+
[]
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def handle_patient_expired(patient)
|
|
95
|
+
if patient.expired
|
|
96
|
+
[OpenStruct.new(start_date: patient.deathdate)]
|
|
97
|
+
else
|
|
98
|
+
[]
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def handle_payer_information(patient)
|
|
103
|
+
patient.insurance_providers
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -9,9 +9,9 @@ module HealthDataStandards
|
|
|
9
9
|
@code_map = nil
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def export(patient)
|
|
12
|
+
def export(patient, measures=[])
|
|
13
13
|
@code_map ||= self.build_code_map
|
|
14
|
-
@rendering_context.render(:template => 'show', :locals => {:patient => patient, :code_map => @code_map})
|
|
14
|
+
@rendering_context.render(:template => 'show', :locals => {:patient => patient, :code_map => @code_map, :measures => measures})
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def build_code_map
|
|
@@ -14,12 +14,36 @@ module HealthDataStandards
|
|
|
14
14
|
hqmf_qrda_oid_map.any? {|map_tuple| map_tuple['qrda_oid'] == oid}
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
def qrda_oid_for_hqmf_oid(hqmf_oid)
|
|
18
|
-
|
|
19
|
-
if
|
|
20
|
-
|
|
17
|
+
def qrda_oid_for_hqmf_oid(hqmf_oid, vs_oid = nil)
|
|
18
|
+
|
|
19
|
+
if (vs_oid && hqmf_oid == '2.16.840.1.113883.3.560.1.1001')
|
|
20
|
+
qrda_oid_for_hqmf_patient_characteristic(vs_oid)
|
|
21
|
+
else
|
|
22
|
+
oid_tuple = hqmf_qrda_oid_map.find {|map_tuple| map_tuple['hqmf_oid'] == hqmf_oid }
|
|
23
|
+
if oid_tuple.nil?
|
|
24
|
+
puts "no qrda oid for #{hqmf_oid}"
|
|
25
|
+
end
|
|
26
|
+
oid_tuple['qrda_oid']
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# given a value set oid for a patient characteristic, return the qrda oid for the correct characteristic
|
|
32
|
+
def qrda_oid_for_hqmf_patient_characteristic(vs_oid)
|
|
33
|
+
# This is a special case. This HQMF OID maps to more than one QRDA OID.
|
|
34
|
+
# So we need to try to figure out what template we should use based on the
|
|
35
|
+
# content of the entry
|
|
36
|
+
if vs_oid == '2.16.840.1.113883.3.526.3.1279'
|
|
37
|
+
# Patient Characteristic Observation Assertion template for
|
|
38
|
+
# Patient Characteristic: ECOG Performance Status-Poor
|
|
39
|
+
'2.16.840.1.113883.10.20.24.3.103'
|
|
40
|
+
elsif ["2.16.840.1.113883.3.117.1.7.1.402" , "2.16.840.1.113883.3.117.1.7.1.403" , "2.16.840.1.113883.3.117.1.7.1.287" ,"2.16.840.1.113883.3.117.1.7.1.307"].index(vs_oid)
|
|
41
|
+
# Patient Charasteristic Gestational Age
|
|
42
|
+
'2.16.840.1.113883.10.20.24.3.101'
|
|
43
|
+
elsif vs_oid == "2.16.840.1.113883.3.526.3.1189" || vs_oid == "2.16.840.1.113883.3.526.3.1170" || vs_oid == '2.16.840.1.113883.3.600.2390'
|
|
44
|
+
# Patient Characteristic Tobacco User/Non-User
|
|
45
|
+
'2.16.840.1.113883.10.20.22.4.85'
|
|
21
46
|
end
|
|
22
|
-
oid_tuple['qrda_oid']
|
|
23
47
|
end
|
|
24
48
|
|
|
25
49
|
alias :partial_for :qrda_oid_for_hqmf_oid
|
|
@@ -6,7 +6,7 @@ module HealthDataStandards
|
|
|
6
6
|
options['attribute'] ||= :codes
|
|
7
7
|
options['exclude_null_flavor'] ||= false
|
|
8
8
|
code_string = nil
|
|
9
|
-
preferred_code = entry.preferred_code(options['preferred_code_sets'], options['attribute'])
|
|
9
|
+
preferred_code = entry.preferred_code(options['preferred_code_sets'], options['attribute'], options['value_set_map'])
|
|
10
10
|
if preferred_code
|
|
11
11
|
code_system_oid = HealthDataStandards::Util::CodeSystemHelper.oid_for_code_system(preferred_code['code_set'])
|
|
12
12
|
code_string = "<#{options['tag_name']} code=\"#{preferred_code['code']}\" codeSystem=\"#{code_system_oid}\" #{options['extra_content']}>"
|
|
@@ -20,7 +20,7 @@ module HealthDataStandards
|
|
|
20
20
|
|
|
21
21
|
if options["attribute"] == :codes && entry.respond_to?(:translation_codes)
|
|
22
22
|
code_string += "<originalText>#{ERB::Util.html_escape entry.description}</originalText>" if entry.respond_to?(:description)
|
|
23
|
-
entry.translation_codes(options['preferred_code_sets']).each do |translation|
|
|
23
|
+
entry.translation_codes(options['preferred_code_sets'], options['value_set_map']).each do |translation|
|
|
24
24
|
code_string += "<translation code=\"#{translation['code']}\" codeSystem=\"#{HealthDataStandards::Util::CodeSystemHelper.oid_for_code_system(translation['code_set'])}\"/>\n"
|
|
25
25
|
end
|
|
26
26
|
end
|
|
@@ -41,8 +41,14 @@ module HealthDataStandards
|
|
|
41
41
|
b.delete
|
|
42
42
|
end if options[:delete_existing]
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
#find the highest bundle version and see if one is installed that is greater than the one
|
|
45
|
+
# we are currently installing. Do not load the libs other wise
|
|
46
|
+
vers = bundle_versions.values.sort.reverse[0]
|
|
47
|
+
if (vers.nil? || vers <= bundle.version || options["force_js_install"])
|
|
48
|
+
unpack_and_store_system_js(zip_file)
|
|
49
|
+
else
|
|
50
|
+
puts "javascript libraries will not being updated as a more recent bundle version is already installed"
|
|
51
|
+
end
|
|
46
52
|
# Store the bundle metadata.
|
|
47
53
|
unless bundle.save
|
|
48
54
|
raise bundle.errors.full_messages.join(",")
|
|
@@ -11,6 +11,7 @@ module HealthDataStandards
|
|
|
11
11
|
result = super
|
|
12
12
|
extract_value(entry_element, result)
|
|
13
13
|
extract_interpretation(entry_element, result)
|
|
14
|
+
extract_reference_range(entry_element, result)
|
|
14
15
|
extract_negation(entry_element, result)
|
|
15
16
|
result
|
|
16
17
|
end
|
|
@@ -24,6 +25,10 @@ module HealthDataStandards
|
|
|
24
25
|
result.interpretation = {'code' => code, 'codeSystem' => code_system}
|
|
25
26
|
end
|
|
26
27
|
end
|
|
28
|
+
|
|
29
|
+
def extract_reference_range(parent_element, result)
|
|
30
|
+
result.reference_range = parent_element.at_xpath("./cda:referenceRange/cda:observationRange/cda:text").try(:text)
|
|
31
|
+
end
|
|
27
32
|
|
|
28
33
|
end
|
|
29
34
|
end
|
|
@@ -6,7 +6,7 @@ module HealthDataStandards
|
|
|
6
6
|
|
|
7
7
|
def import(meta_xml)
|
|
8
8
|
meta_xml.root.add_namespace_definition("hrf-md", Metadata::NS)
|
|
9
|
-
meta_element = meta_xml.at_xpath("./hrf-md:
|
|
9
|
+
meta_element = meta_xml.at_xpath("./hrf-md:DocumentMetaData")
|
|
10
10
|
return unless meta_element
|
|
11
11
|
|
|
12
12
|
meta = Metadata::Base.new
|
|
@@ -16,8 +16,15 @@ module HealthDataStandards
|
|
|
16
16
|
field :category, type: String
|
|
17
17
|
field :population_ids , type: Hash
|
|
18
18
|
field :oids, type: Array
|
|
19
|
-
field :data_criteria, type: Array
|
|
20
19
|
|
|
20
|
+
field :population_criteria, type: Hash
|
|
21
|
+
field :data_criteria, type: Hash, default: {}
|
|
22
|
+
field :source_data_criteria, type: Hash, default: {}
|
|
23
|
+
field :measure_period, type: Hash
|
|
24
|
+
field :measure_attributes, type: Hash
|
|
25
|
+
field :populations, type: Array
|
|
26
|
+
field :preconditions, type: Hash
|
|
27
|
+
field :hqmf_document, type: Hash
|
|
21
28
|
scope :top_level_by_type , ->(type){where({"type"=> type}).any_of({"sub_id" => nil}, {"sub_id" => "a"})}
|
|
22
29
|
scope :top_level , any_of({"sub_id" => nil}, {"sub_id" => "a"})
|
|
23
30
|
scope :order_by_id_sub_id, order_by([["id", :asc],["sub_id", :asc]])
|
|
@@ -25,6 +32,9 @@ module HealthDataStandards
|
|
|
25
32
|
index({oids: 1})
|
|
26
33
|
index({hqmf_id: 1})
|
|
27
34
|
index({category: 1})
|
|
35
|
+
index({sub_id: 1})
|
|
36
|
+
index({_id: 1, sub_id: 1})
|
|
37
|
+
|
|
28
38
|
index "bundle_id" => 1
|
|
29
39
|
|
|
30
40
|
validates_presence_of :id
|
|
@@ -52,6 +62,113 @@ module HealthDataStandards
|
|
|
52
62
|
pipeline << {'$sort' => {"category" => 1}}
|
|
53
63
|
Mongoid.default_session.command(aggregate: 'measures', pipeline: pipeline)['result']
|
|
54
64
|
end
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# Returns the hqmf-parser's ruby implementation of an HQMF document.
|
|
68
|
+
# Rebuild from population_criteria, data_criteria, and measure_period JSON
|
|
69
|
+
def as_hqmf_model
|
|
70
|
+
@hqmf ||= HQMF::Document.from_json(self.hqmf_document)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def smoking_gun_data(patient_cache_filter={})
|
|
74
|
+
::Measure.calculate_smoking_gun_data(self.hqmf_id, patient_cache_filter)
|
|
75
|
+
end
|
|
76
|
+
# Calculate the smoking gun data for the given hqmf_id with the given patient_cache_filter
|
|
77
|
+
# The filter will allow us to segment the cache by things like test_id required for Cypress.
|
|
78
|
+
|
|
79
|
+
def self.calculate_smoking_gun_data(hqmf_id, patient_cache_filter={})
|
|
80
|
+
population_keys = ('a'..'zz').to_a
|
|
81
|
+
values = {}
|
|
82
|
+
measure = Measure.top_level.where({hqmf_id: hqmf_id}).first
|
|
83
|
+
sub_ids = []
|
|
84
|
+
hqmf_measure = measure.as_hqmf_model
|
|
85
|
+
population_codes = []
|
|
86
|
+
if hqmf_measure.populations.length == 1
|
|
87
|
+
sub_ids = nil
|
|
88
|
+
else
|
|
89
|
+
#Do not bother with populaions that contain stratifications
|
|
90
|
+
hqmf_measure.populations.each_with_index do |population,index|
|
|
91
|
+
if population["stratification"].nil?
|
|
92
|
+
sub_ids << population_keys[index]
|
|
93
|
+
HQMF::PopulationCriteria::ALL_POPULATION_CODES.each do |code|
|
|
94
|
+
population_codes << population[code] if population[code]
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
population_codes.uniq!
|
|
101
|
+
|
|
102
|
+
rationals = PatientCache.smoking_gun_rational(measure.hqmf_id,sub_ids,patient_cache_filter)
|
|
103
|
+
rationals.each_pair do |mrn,rash|
|
|
104
|
+
values[mrn] = []
|
|
105
|
+
population_codes.each do |pop_code|
|
|
106
|
+
# if (population[pop_code])
|
|
107
|
+
population_criteria = hqmf_measure.population_criteria(pop_code)
|
|
108
|
+
if population_criteria.preconditions
|
|
109
|
+
array = []
|
|
110
|
+
|
|
111
|
+
parent = population_criteria.preconditions[0]
|
|
112
|
+
values[mrn].concat self.loop_preconditions(hqmf_measure, parent, rash)
|
|
113
|
+
end # end population_criteria.preconditions
|
|
114
|
+
#end # end (population[pop_code])
|
|
115
|
+
end # population_codes
|
|
116
|
+
values[mrn].uniq!
|
|
117
|
+
end
|
|
118
|
+
values
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
private
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def self.loop_data_criteria(hqmf, data_criteria, rationale)
|
|
125
|
+
result = []
|
|
126
|
+
if (rationale[data_criteria.id])
|
|
127
|
+
|
|
128
|
+
if data_criteria.type != :derived
|
|
129
|
+
template = HQMF::DataCriteria.template_id_for_definition(data_criteria.definition, data_criteria.status, data_criteria.negation)
|
|
130
|
+
value_set_oid = data_criteria.code_list_id
|
|
131
|
+
begin
|
|
132
|
+
qrda_template = HealthDataStandards::Export::QRDA::EntryTemplateResolver.qrda_oid_for_hqmf_oid(template,value_set_oid)
|
|
133
|
+
rescue
|
|
134
|
+
value_set_oid = 'In QRDA Header (Non Null Value)'
|
|
135
|
+
qrda_template = 'N/A'
|
|
136
|
+
end # end begin recue
|
|
137
|
+
result << {description: data_criteria.description, oid: value_set_oid, template: qrda_template}
|
|
138
|
+
|
|
139
|
+
if data_criteria.temporal_references
|
|
140
|
+
data_criteria.temporal_references.each do |temporal_reference|
|
|
141
|
+
if temporal_reference.reference.id != 'MeasurePeriod'
|
|
142
|
+
result.concat loop_data_criteria(hqmf, hqmf.data_criteria(temporal_reference.reference.id), rationale)
|
|
143
|
+
end #if temporal_reference.reference.id
|
|
144
|
+
end # end data_criteria.temporal_references.each do |temporal_reference|
|
|
145
|
+
end# end if data_criteria.temporal_references
|
|
146
|
+
else #data_criteria.type != :derived
|
|
147
|
+
(data_criteria.children_criteria || []).each do |child_id|
|
|
148
|
+
result.concat loop_data_criteria(hqmf, hqmf.data_criteria(child_id), rationale)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
result
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def self.loop_preconditions(hqmf, parent, rationale)
|
|
156
|
+
result = []
|
|
157
|
+
parent.preconditions.each do |precondition|
|
|
158
|
+
parent_key = "precondition_#{parent.id}"
|
|
159
|
+
key = "precondition_#{precondition.id}"
|
|
160
|
+
if precondition.preconditions.empty?
|
|
161
|
+
data_criteria = hqmf.data_criteria(precondition.reference.id)
|
|
162
|
+
result.concat loop_data_criteria(hqmf, data_criteria, rationale)
|
|
163
|
+
else
|
|
164
|
+
if (rationale[parent_key] && rationale[key])
|
|
165
|
+
result.concat loop_preconditions(hqmf, precondition, rationale)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
result
|
|
170
|
+
end
|
|
171
|
+
|
|
55
172
|
end
|
|
56
173
|
end
|
|
57
174
|
end
|