health-data-standards 3.1.1 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|