health-data-standards 3.0.6 → 3.1.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/README.md +3 -1
- data/Rakefile +2 -0
- data/lib/health-data-standards.rb +10 -0
- data/lib/health-data-standards/export/cat_3.rb +24 -0
- data/lib/health-data-standards/export/html.rb +2 -1
- data/lib/health-data-standards/import/bundle/importer.rb +178 -146
- data/lib/health-data-standards/import/cat1/diagnosis_active_importer.rb +0 -5
- data/lib/health-data-standards/import/cat1/diagnosis_inactive_importer.rb +1 -6
- data/lib/health-data-standards/import/cat1/diagnostic_study_order_importer.rb +0 -4
- data/lib/health-data-standards/import/cat1/ecog_status_importer.rb +12 -0
- data/lib/health-data-standards/import/cat1/encounter_order_importer.rb +0 -5
- data/lib/health-data-standards/import/cat1/lab_order_importer.rb +1 -5
- data/lib/health-data-standards/import/cat1/lab_result_importer.rb +18 -0
- data/lib/health-data-standards/import/cat1/medication_active_importer.rb +27 -0
- data/lib/health-data-standards/import/cat1/patient_importer.rb +54 -46
- data/lib/health-data-standards/import/cat1/procedure_performed_importer.rb +28 -0
- data/lib/health-data-standards/import/cat1/symptom_active_importer.rb +12 -0
- data/lib/health-data-standards/import/cda/condition_importer.rb +2 -0
- data/lib/health-data-standards/import/cda/encounter_importer.rb +6 -0
- data/lib/health-data-standards/import/cda/procedure_importer.rb +1 -0
- data/lib/health-data-standards/import/cda/section_importer.rb +27 -5
- data/lib/health-data-standards/models/cda_identifier.rb +17 -0
- data/lib/health-data-standards/models/cqm/aggregate_objects.rb +92 -0
- data/lib/health-data-standards/models/cqm/measure.rb +51 -30
- data/lib/health-data-standards/models/cqm/query_cache.rb +64 -0
- data/lib/health-data-standards/models/entry.rb +1 -1
- data/lib/health-data-standards/models/record.rb +28 -3
- data/lib/health-data-standards/models/svs/value_set.rb +20 -0
- data/lib/health-data-standards/tasks/bundle.rake +18 -8
- data/lib/health-data-standards/util/hqmf_template_helper.rb +6 -2
- data/lib/health-data-standards/util/hqmf_template_oid_map.json +4 -0
- data/lib/health-data-standards/util/vs_api.rb +9 -6
- data/lib/hqmf-model/data_criteria.json +10 -10
- data/lib/hqmf-parser.rb +0 -2
- data/lib/hqmf-parser/1.0/range.rb +21 -9
- data/templates/c32/show.c32.erb +1 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.4.cat1.erb +1 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.40.cat1.erb +2 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.44.cat1.erb +1 -0
- 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.54.cat1.erb +16 -1
- 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.69.cat1.erb +7 -2
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.76.cat1.erb +2 -1
- data/templates/cat1/_medication_details.cat1.erb +4 -4
- data/templates/cat1/_record_target.cat1.erb +2 -2
- data/templates/cat1/_result_value.cat1.erb +1 -1
- data/templates/cat1/show.cat1.erb +2 -2
- data/templates/cat3/_continuous_variable_value.cat3.erb +20 -0
- data/templates/cat3/_measure_data.cat3.erb +126 -0
- data/templates/cat3/_performance_rate.cat3.erb +16 -0
- data/templates/cat3/_supplemental_data.cat3.erb +36 -0
- data/templates/cat3/show.cat3.erb +157 -0
- data/templates/ccda/show.ccda.erb +1 -1
- metadata +25 -45
- data/lib/hqmf-parser/value_sets/value_set_parser.rb +0 -241
data/README.md
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
This is a project to generate and consume HITSP C32, ASTM CCR and PQRI
|
1
|
+
This is a project to generate and consume HITSP C32, ASTM CCR, QRDA Category I and PQRI.
|
2
|
+
|
3
|
+
In addition this project also contains libaries for parsing hqmf documents and for dealing with NLM valuesets.
|
2
4
|
|
3
5
|
Environment
|
4
6
|
===========
|
data/Rakefile
CHANGED
@@ -7,6 +7,7 @@ require 'csv'
|
|
7
7
|
require 'nokogiri'
|
8
8
|
require 'ostruct'
|
9
9
|
require 'log4r'
|
10
|
+
require 'memoist'
|
10
11
|
|
11
12
|
# Freedom patches
|
12
13
|
require_relative 'health-data-standards/ext/symbol'
|
@@ -40,6 +41,7 @@ require_relative 'health-data-standards/models/thing_with_codes'
|
|
40
41
|
require_relative 'health-data-standards/models/result_value'
|
41
42
|
require_relative 'health-data-standards/models/coded_result_value'
|
42
43
|
require_relative 'health-data-standards/models/physical_quantity_result_value'
|
44
|
+
require_relative 'health-data-standards/models/cda_identifier'
|
43
45
|
require_relative 'health-data-standards/models/entry'
|
44
46
|
require_relative 'health-data-standards/models/allergy'
|
45
47
|
require_relative 'health-data-standards/models/encounter'
|
@@ -76,6 +78,7 @@ require_relative 'health-data-standards/models/metadata/pedigree'
|
|
76
78
|
require_relative 'health-data-standards/export/qrda/entry_template_resolver'
|
77
79
|
require_relative 'health-data-standards/export/helper/cat1_view_helper'
|
78
80
|
require_relative 'health-data-standards/export/cat_1'
|
81
|
+
require_relative 'health-data-standards/export/cat_3'
|
79
82
|
|
80
83
|
require_relative 'health-data-standards/import/cda/narrative_reference_handler'
|
81
84
|
require_relative 'health-data-standards/import/cda/entry_finder'
|
@@ -135,17 +138,24 @@ require_relative 'health-data-standards/import/green_c32/care_goal_importer'
|
|
135
138
|
|
136
139
|
require_relative 'health-data-standards/import/cat1/gestational_age_importer'
|
137
140
|
require_relative 'health-data-standards/import/cat1/procedure_intolerance_importer'
|
141
|
+
require_relative 'health-data-standards/import/cat1/procedure_performed_importer'
|
138
142
|
require_relative 'health-data-standards/import/cat1/procedure_order_importer'
|
139
143
|
require_relative 'health-data-standards/import/cat1/diagnosis_active_importer'
|
140
144
|
require_relative 'health-data-standards/import/cat1/diagnosis_inactive_importer'
|
141
145
|
require_relative 'health-data-standards/import/cat1/patient_importer'
|
142
146
|
require_relative 'health-data-standards/import/cat1/lab_order_importer'
|
147
|
+
require_relative 'health-data-standards/import/cat1/medication_active_importer'
|
143
148
|
require_relative 'health-data-standards/import/cat1/medication_dispensed_importer'
|
144
149
|
require_relative 'health-data-standards/import/cat1/encounter_order_importer'
|
145
150
|
require_relative 'health-data-standards/import/cat1/diagnostic_study_order_importer'
|
146
151
|
require_relative 'health-data-standards/import/cat1/tobacco_use_importer'
|
147
152
|
require_relative 'health-data-standards/import/cat1/entry_package'
|
153
|
+
require_relative 'health-data-standards/import/cat1/lab_result_importer'
|
154
|
+
require_relative 'health-data-standards/import/cat1/ecog_status_importer'
|
155
|
+
require_relative 'health-data-standards/import/cat1/symptom_active_importer'
|
148
156
|
|
157
|
+
require_relative 'health-data-standards/models/cqm/aggregate_objects'
|
158
|
+
require_relative 'health-data-standards/models/cqm/query_cache'
|
149
159
|
require_relative 'health-data-standards/models/cqm/bundle'
|
150
160
|
require_relative 'health-data-standards/models/cqm/measure'
|
151
161
|
require_relative 'health-data-standards/import/bundle/importer'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module HealthDataStandards
|
2
|
+
module Export
|
3
|
+
class Cat3
|
4
|
+
def initialize
|
5
|
+
template_helper = HealthDataStandards::Export::TemplateHelper.new('cat3', 'cat3')
|
6
|
+
@rendering_context = HealthDataStandards::Export::RenderingContext.new
|
7
|
+
@rendering_context.template_helper = template_helper
|
8
|
+
@cat1_renderer = HealthDataStandards::Export::RenderingContext.new
|
9
|
+
@cat1_renderer.template_helper = HealthDataStandards::Export::TemplateHelper.new('cat1', 'cat1')
|
10
|
+
end
|
11
|
+
|
12
|
+
def export(measures, effective_date, start_date, end_date, test_id=nil)
|
13
|
+
results = {}
|
14
|
+
measures.each do |measure|
|
15
|
+
results[measure['hqmf_id']] = HealthDataStandards::CQM::QueryCache.aggregate_measure(measure['hqmf_id'], effective_date, test_id)
|
16
|
+
end
|
17
|
+
@rendering_context.render(:template => 'show',
|
18
|
+
:locals => {:measures => measures, :start_date => start_date,
|
19
|
+
:end_date => end_date, :cat1_renderer => @cat1_renderer,
|
20
|
+
:results => results})
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -6,10 +6,11 @@ module HealthDataStandards
|
|
6
6
|
@rendering_context = RenderingContext.new
|
7
7
|
@rendering_context.template_helper = template_helper
|
8
8
|
@rendering_context.extensions = [HealthDataStandards::Export::Helper::HTMLViewHelper]
|
9
|
-
@code_map
|
9
|
+
@code_map = nil
|
10
10
|
end
|
11
11
|
|
12
12
|
def export(patient)
|
13
|
+
@code_map ||= self.build_code_map
|
13
14
|
@rendering_context.render(:template => 'show', :locals => {:patient => patient, :code_map => @code_map})
|
14
15
|
end
|
15
16
|
|
@@ -1,148 +1,180 @@
|
|
1
|
+
require 'zip/zipfilesystem'
|
1
2
|
module HealthDataStandards
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
3
|
+
module Import
|
4
|
+
module Bundle
|
5
|
+
|
6
|
+
class Importer
|
7
|
+
|
8
|
+
SOURCE_ROOTS = {bundle: 'bundle.json',
|
9
|
+
libraries: File.join('library_functions','*.js'),
|
10
|
+
measures: 'measures', results: 'results',
|
11
|
+
valuesets: File.join('value_sets','json','*.json'),
|
12
|
+
patients: 'patients'}
|
13
|
+
COLLECTION_NAMES = ["bundles", "records", "measures", "selected_measures", "patient_cache", "query_cache", "system.js"]
|
14
|
+
CLEAR_ONLY_COLLECTIONS = ["system.js"]
|
15
|
+
DEFAULTS = {type: nil,
|
16
|
+
delete_existing: false,
|
17
|
+
update_measures: true,
|
18
|
+
clear_collections: COLLECTION_NAMES
|
19
|
+
}
|
20
|
+
|
21
|
+
# Import a quality bundle into the database. This includes metadata, measures, test patients, supporting JS libraries, and expected results.
|
22
|
+
#
|
23
|
+
# @param [File] zip The bundle zip file.
|
24
|
+
# @param [String] Type of measures to import, either 'ep', 'eh' or nil for all
|
25
|
+
# @param [Boolean] keep_existing If true, delete all current collections related to patients and measures.
|
26
|
+
def self.import(zip, options={})
|
27
|
+
options = DEFAULTS.merge(options)
|
28
|
+
|
29
|
+
bundle = nil
|
30
|
+
Zip::ZipFile.open(zip.path) do |zip_file|
|
31
|
+
|
32
|
+
bundle = unpack_bundle(zip_file)
|
33
|
+
|
34
|
+
bundle_versions = Hash[* HealthDataStandards::CQM::Bundle.where({}).collect{|b| [b._id, b.version]}.flatten]
|
35
|
+
if bundle_versions.invert[bundle.version] && !(options[:delete_existing])
|
36
|
+
raise "A bundle with version #{bundle.version} already exists in the database. "
|
37
|
+
end
|
38
|
+
|
39
|
+
HealthDataStandards::CQM::Bundle.where({:version => bundle.version}).each do |b|
|
40
|
+
puts "deleting existing bundle version: #{b.version}"
|
41
|
+
b.delete
|
42
|
+
end if options[:delete_existing]
|
43
|
+
|
44
|
+
unpack_and_store_system_js(zip_file)
|
45
|
+
|
46
|
+
# Store the bundle metadata.
|
47
|
+
unless bundle.save
|
48
|
+
raise bundle.errors.full_messages.join(",")
|
49
|
+
end
|
50
|
+
puts "bundle metadata unpacked..."
|
51
|
+
|
52
|
+
measure_ids = unpack_and_store_measures(zip_file, options[:type], bundle, options[:update_measures])
|
53
|
+
unpack_and_store_patients(zip_file, options[:type], bundle)
|
54
|
+
unpack_and_store_valuesets(zip_file, bundle)
|
55
|
+
unpack_and_store_results(zip_file, options[:type], measure_ids, bundle)
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
bundle
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
# Save a javascript function into Mongo's system.js collection for measure execution.
|
64
|
+
#
|
65
|
+
# @param [String] name The name by which the function will be referred.
|
66
|
+
# @param [String] fn The body of the function being saved.
|
67
|
+
def self.save_system_js_fn(name, fn)
|
68
|
+
fn = "function () {\n #{fn} \n }"
|
69
|
+
Mongoid.default_session['system.js'].find('_id' => name).upsert(
|
70
|
+
{
|
71
|
+
"_id" => name,
|
72
|
+
"value" => Moped::BSON::Code.new(fn)
|
73
|
+
}
|
74
|
+
)
|
75
|
+
end
|
76
|
+
|
77
|
+
# A utility function for finding files in a bundle. Strip a file path of it's extension and just give the filename.
|
78
|
+
#
|
79
|
+
# @param [String] original A file path.
|
80
|
+
# @param [String] extension A file extension.
|
81
|
+
# @return The filename at the end of the original String path with the extension removed. e.g. "/boo/urns.html" -> "urns"
|
82
|
+
def self.entry_key(original, extension)
|
83
|
+
original.split('/').last.gsub(".#{extension}", '')
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.unpack_bundle(zip)
|
87
|
+
HealthDataStandards::CQM::Bundle.new(JSON.parse(zip.read(SOURCE_ROOTS[:bundle]),max_nesting: 100))
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.unpack_and_store_system_js(zip)
|
91
|
+
zip.glob(SOURCE_ROOTS[:libraries]).each do |entry|
|
92
|
+
name = Pathname.new(entry.name).basename('.js').to_s
|
93
|
+
contents = entry.get_input_stream.read
|
94
|
+
save_system_js_fn(name, contents)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.unpack_and_store_measures(zip, type, bundle, update_measures)
|
99
|
+
measure_ids = []
|
100
|
+
entries = zip.glob(File.join(SOURCE_ROOTS[:measures],type || '**','*.json'))
|
101
|
+
entries.each_with_index do |entry, index|
|
102
|
+
source_measure = unpack_json(entry)
|
103
|
+
# we clone so that we have a source without a bundle id
|
104
|
+
measure = source_measure.clone
|
105
|
+
measure_ids << measure['id']
|
106
|
+
measure['bundle_id'] = bundle.id
|
107
|
+
Mongoid.default_session["measures"].insert(measure)
|
108
|
+
|
109
|
+
if update_measures
|
110
|
+
Mongoid.default_session["measures"].where({hqmf_id: measure["hqmf_id"], sub_id: measure["sub_id"]}).each do |m|
|
111
|
+
b = HealthDataStandards::CQM::Bundle.find(m["bundle_id"])
|
112
|
+
if b.version < bundle.version
|
113
|
+
m.merge!(source_measure)
|
114
|
+
Mongoid.default_session["measures"].where({"_id" => m["_id"]}).update(m)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
report_progress('measures', (index*100/entries.length)) if index%10 == 0
|
119
|
+
end
|
120
|
+
puts "\rLoading: Measures Complete "
|
121
|
+
measure_ids
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.unpack_and_store_patients(zip, type, bundle)
|
125
|
+
entries = zip.glob(File.join(SOURCE_ROOTS[:patients],type || '**','json','*.json'))
|
126
|
+
entries.each_with_index do |entry, index|
|
127
|
+
patient = Record.new(unpack_json(entry))
|
128
|
+
patient['bundle_id'] = bundle.id
|
129
|
+
patient.save
|
130
|
+
report_progress('patients', (index*100/entries.length)) if index%10 == 0
|
131
|
+
end
|
132
|
+
puts "\rLoading: Patients Complete "
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.unpack_and_store_valuesets(zip, bundle)
|
136
|
+
entries = zip.glob(SOURCE_ROOTS[:valuesets])
|
137
|
+
bulk = []
|
138
|
+
entries.each_with_index do |entry, index|
|
139
|
+
vs = HealthDataStandards::SVS::ValueSet.new(unpack_json(entry))
|
140
|
+
vs['bundle_id'] = bundle.id
|
141
|
+
bulk << vs
|
142
|
+
report_progress('Value Sets', (index*100/entries.length)) if index%10 == 0
|
143
|
+
end
|
144
|
+
HealthDataStandards::SVS::ValueSet.collection.insert(bulk.map {|vs| vs.as_document})
|
145
|
+
puts "\rLoading: Value Sets Complete "
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.unpack_and_store_results(zip, type, measure_ids, bundle)
|
149
|
+
zip.glob(File.join(SOURCE_ROOTS[:results],'*.json')).each do |entry|
|
150
|
+
name = Pathname.new(entry.name).basename('.json').to_s
|
151
|
+
collection = (name == "by_patient") ? "patient_cache" : "query_cache"
|
152
|
+
|
153
|
+
contents = unpack_json(entry)
|
154
|
+
|
155
|
+
if (type)
|
156
|
+
contents.select! {|entry| measure_ids.include? entry['measure_id']} if collection == 'query_cache'
|
157
|
+
contents.select! {|entry| measure_ids.include? entry['value']['measure_id']} if collection == 'patient_cache'
|
158
|
+
end
|
159
|
+
|
160
|
+
contents.each do |document|
|
161
|
+
document['bundle_id'] = bundle.id
|
162
|
+
Mongoid.default_session[collection].insert(document)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
puts "\rLoading: Results Complete "
|
166
|
+
end
|
167
|
+
|
168
|
+
def self.unpack_json(entry)
|
169
|
+
JSON.parse(entry.get_input_stream.read,:max_nesting => 100)
|
170
|
+
end
|
171
|
+
|
172
|
+
def self.report_progress(label, percent)
|
173
|
+
print "\rLoading: #{label} #{percent}% complete"
|
174
|
+
STDOUT.flush
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
148
180
|
end
|
@@ -8,11 +8,6 @@ module HealthDataStandards
|
|
8
8
|
@status_xpath = nil # We'll hardcode this to active in create entry because this is from the
|
9
9
|
# diagnosis active template
|
10
10
|
end
|
11
|
-
|
12
|
-
def create_entry(entry_element, nrh = CDA::NarrativeReferenceHandler.new)
|
13
|
-
condition = super
|
14
|
-
condition
|
15
|
-
end
|
16
11
|
end
|
17
12
|
end
|
18
13
|
end
|
@@ -5,14 +5,9 @@ module HealthDataStandards
|
|
5
5
|
|
6
6
|
def initialize
|
7
7
|
super(CDA::EntryFinder.new("//cda:observation[cda:templateId/@root='2.16.840.1.113883.10.20.24.3.13']"))
|
8
|
-
@status_xpath = nil # We'll hardcode this to
|
8
|
+
@status_xpath = nil # We'll hardcode this to inactive in create entry because this is from the
|
9
9
|
# diagnosis active template
|
10
10
|
end
|
11
|
-
|
12
|
-
def create_entry(entry_element, nrh = CDA::NarrativeReferenceHandler.new)
|
13
|
-
condition = super
|
14
|
-
condition
|
15
|
-
end
|
16
11
|
end
|
17
12
|
end
|
18
13
|
end
|