quality-measure-engine 2.0.0 → 2.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/.travis.yml +1 -1
- data/Gemfile +3 -1
- data/Gemfile.lock +15 -2
- data/lib/qme/bundle/bundle.rb +14 -0
- data/lib/qme/bundle/eh_measure_sheet.rb +114 -0
- data/lib/qme/bundle/eh_patient_importer.rb +21 -0
- data/lib/qme/bundle/importer.rb +3 -10
- data/lib/qme/tasks/bundle.rake +82 -3
- data/lib/qme/version.rb +1 -1
- data/lib/quality-measure-engine.rb +3 -0
- data/quality-measure-engine.gemspec +2 -0
- data/test/fixtures/bundles/just_measure_0002.zip +0 -0
- data/test/fixtures/delayed_backend_mongoid_jobs/queued_job.json +4 -7
- data/test/fixtures/eh_patient_sheets/results_matrix_eh.xlsx +0 -0
- data/test/fixtures/measures/0142.json +19 -0
- data/test/fixtures/measures/0495a.json +23 -0
- data/test/fixtures/measures/0495b.json +24 -0
- data/test/fixtures/records/barry_berry.json +1 -0
- data/test/fixtures/records/billy_jones_ipp.json +1 -0
- data/test/fixtures/records/jill_jones_denominator.json +1 -0
- data/test/unit/qme/bundle/eh_measure_sheet_test.rb +37 -0
- data/test/unit/qme/bundle/eh_patient_importer_test.rb +20 -0
- data/test/unit/qme/quality_report_test.rb +1 -0
- metadata +48 -2
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
quality-measure-engine (2.
|
4
|
+
quality-measure-engine (2.1.0)
|
5
5
|
delayed_job_mongoid (~> 2.0.0)
|
6
6
|
mongoid (~> 3.0.9)
|
7
7
|
moped (~> 1.2.7)
|
8
|
+
nokogiri (~> 1.5.5)
|
9
|
+
rubyXL (~> 1.2.10)
|
8
10
|
rubyzip (~> 0.9.9)
|
9
11
|
|
10
12
|
GEM
|
@@ -38,6 +40,7 @@ GEM
|
|
38
40
|
i18n (~> 0.6)
|
39
41
|
multi_json (~> 1.0)
|
40
42
|
arel (3.0.2)
|
43
|
+
binding_of_caller (0.6.8)
|
41
44
|
builder (3.0.4)
|
42
45
|
coderay (1.0.8)
|
43
46
|
delayed_job (3.0.3)
|
@@ -48,6 +51,7 @@ GEM
|
|
48
51
|
erubis (2.7.0)
|
49
52
|
hike (1.2.1)
|
50
53
|
i18n (0.6.1)
|
54
|
+
interception (0.3)
|
51
55
|
journey (1.0.4)
|
52
56
|
json (1.7.5)
|
53
57
|
mail (2.4.4)
|
@@ -64,7 +68,8 @@ GEM
|
|
64
68
|
tzinfo (~> 0.3.22)
|
65
69
|
moped (1.2.7)
|
66
70
|
multi_json (1.3.6)
|
67
|
-
|
71
|
+
nokogiri (1.5.5)
|
72
|
+
origin (1.0.10)
|
68
73
|
polyglot (0.3.3)
|
69
74
|
pry (0.9.10)
|
70
75
|
coderay (~> 1.0.5)
|
@@ -72,6 +77,11 @@ GEM
|
|
72
77
|
slop (~> 3.3.1)
|
73
78
|
pry-nav (0.2.2)
|
74
79
|
pry (~> 0.9.10)
|
80
|
+
pry-rescue (0.12)
|
81
|
+
interception (>= 0.3)
|
82
|
+
pry
|
83
|
+
pry-stack_explorer (0.4.7)
|
84
|
+
binding_of_caller (~> 0.6.8)
|
75
85
|
rack (1.4.1)
|
76
86
|
rack-cache (1.2)
|
77
87
|
rack (>= 0.4)
|
@@ -97,6 +107,7 @@ GEM
|
|
97
107
|
rake (0.9.2.2)
|
98
108
|
rdoc (3.12)
|
99
109
|
json (~> 1.4)
|
110
|
+
rubyXL (1.2.10)
|
100
111
|
rubyzip (0.9.9)
|
101
112
|
simplecov (0.7.1)
|
102
113
|
multi_json (~> 1.0)
|
@@ -121,6 +132,8 @@ DEPENDENCIES
|
|
121
132
|
minitest (~> 4.1.0)
|
122
133
|
pry
|
123
134
|
pry-nav
|
135
|
+
pry-rescue
|
136
|
+
pry-stack_explorer
|
124
137
|
quality-measure-engine!
|
125
138
|
rails (~> 3.2.8)
|
126
139
|
simplecov (~> 0.7.1)
|
data/lib/qme/bundle/bundle.rb
CHANGED
@@ -30,5 +30,19 @@ module QME
|
|
30
30
|
def self.entry_key(original, extension)
|
31
31
|
original.split('/').last.gsub(".#{extension}", '')
|
32
32
|
end
|
33
|
+
|
34
|
+
def self.unpack_bundle_contents(zip)
|
35
|
+
bundle_contents = { bundle: nil, measures: {}, patients: {}, extensions: {}, results: {} }
|
36
|
+
Zip::ZipFile.open(zip.path) do |zipfile|
|
37
|
+
zipfile.entries.each do |entry|
|
38
|
+
bundle_contents[:bundle] = zipfile.read(entry.name) if entry.name.include? "bundle"
|
39
|
+
bundle_contents[:measures][Bundle.entry_key(entry.name, "json")] = zipfile.read(entry.name) if entry.name.match /^measures.*\.json$/
|
40
|
+
bundle_contents[:patients][Bundle.entry_key(entry.name, "json")] = zipfile.read(entry.name) if entry.name.match /^patients.*\.json$/ # Only need to import one of the formats
|
41
|
+
bundle_contents[:extensions][Bundle.entry_key(entry.name,"js")] = zipfile.read(entry.name) if entry.name.match /^library_functions.*\.js/
|
42
|
+
bundle_contents[:results][Bundle.entry_key(entry.name,"json")] = zipfile.read(entry.name) if entry.name.match /^results.*\.json/
|
43
|
+
end
|
44
|
+
end
|
45
|
+
bundle_contents
|
46
|
+
end
|
33
47
|
end
|
34
48
|
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module QME
|
2
|
+
module Bundle
|
3
|
+
class EHMeasureSheet
|
4
|
+
attr_reader :query_cache_document, :patient_cache_documents,
|
5
|
+
:patient_updates
|
6
|
+
|
7
|
+
def initialize(db, sheet, effective_date=nil)
|
8
|
+
@db = db
|
9
|
+
@sheet = sheet
|
10
|
+
@effective_date = effective_date
|
11
|
+
@patient_cache_documents = []
|
12
|
+
@patient_updates = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse
|
16
|
+
qc_document = {}
|
17
|
+
measure_info = extract_measure_info
|
18
|
+
qc_document['population_ids'] = measure_info
|
19
|
+
measure_doc = @db['measures'].where('population_ids' => measure_info).first
|
20
|
+
measure_ids = measure_doc.slice('sub_id', 'nqf_id')
|
21
|
+
measure_ids['measure_id'] = measure_doc['hqmf_id']
|
22
|
+
qc_document.merge!(measure_ids)
|
23
|
+
extract_patients(measure_ids)
|
24
|
+
|
25
|
+
qc_document['population'] = extract_data_from_cell("C#{@population_totals_row}")
|
26
|
+
qc_document['considered'] = @population_totals_row - 3 # header row, blank row and totals row
|
27
|
+
if cv_measure?
|
28
|
+
qc_document['msrpopl'] = extract_data_from_cell("E#{@population_totals_row}")
|
29
|
+
else
|
30
|
+
qc_document['denominator'] = extract_data_from_cell("D#{@population_totals_row}")
|
31
|
+
qc_document['numerator'] = extract_data_from_cell("E#{@population_totals_row}")
|
32
|
+
qc_document['antinumerator'] = qc_document['denominator'] - qc_document['numerator']
|
33
|
+
qc_document['exclusions'] = extract_data_from_cell("F#{@population_totals_row}")
|
34
|
+
qc_document['denexcep'] = extract_data_from_cell("G#{@population_totals_row}")
|
35
|
+
end
|
36
|
+
|
37
|
+
qc_document['test_id'] = nil
|
38
|
+
qc_document['filters'] = nil
|
39
|
+
qc_document['execution_time'] = 0
|
40
|
+
qc_document['effective_date'] = @effective_date
|
41
|
+
|
42
|
+
@query_cache_document = qc_document
|
43
|
+
end
|
44
|
+
|
45
|
+
def extract_patients(measure_ids)
|
46
|
+
row = 2
|
47
|
+
medical_record_number = extract_data_from_cell("B#{row}")
|
48
|
+
while medical_record_number.present?
|
49
|
+
patient_document = extract_patient(row, medical_record_number.to_s)
|
50
|
+
patient_document.merge!(measure_ids)
|
51
|
+
@patient_cache_documents << {'value' => patient_document}
|
52
|
+
@patient_updates << {'medical_record_number' => medical_record_number.to_s,
|
53
|
+
'measure_id' => measure_ids['measure_id']}
|
54
|
+
row = row + 1
|
55
|
+
medical_record_number = extract_data_from_cell("B#{row}")
|
56
|
+
end
|
57
|
+
|
58
|
+
@population_totals_row = row + 1
|
59
|
+
end
|
60
|
+
|
61
|
+
def extract_measure_info
|
62
|
+
measure_info = {}
|
63
|
+
measure_info['IPP'] = extract_data_from_cell('I5')
|
64
|
+
if cv_measure?
|
65
|
+
measure_info['MSRPOPL'] = extract_data_from_cell('I10')
|
66
|
+
measure_info['stratification'] = extract_data_from_cell('I11') if extract_data_from_cell('I11').present?
|
67
|
+
else
|
68
|
+
measure_info['DENOM'] = extract_data_from_cell('I6')
|
69
|
+
measure_info['NUMER'] = extract_data_from_cell('I7')
|
70
|
+
measure_info['DENEXCEP'] = extract_data_from_cell('I8') if extract_data_from_cell('I8').present?
|
71
|
+
measure_info['DENEX'] = extract_data_from_cell('I9') if extract_data_from_cell('I9').present?
|
72
|
+
measure_info['stratification'] = extract_data_from_cell('I11') if extract_data_from_cell('I11').present?
|
73
|
+
end
|
74
|
+
|
75
|
+
measure_info
|
76
|
+
end
|
77
|
+
|
78
|
+
def extract_patient(row, medical_record_number)
|
79
|
+
record = @db['records'].where('medical_record_number' => medical_record_number).first
|
80
|
+
patient_document = record.slice('first', 'last', 'gender', 'birthdate', 'race',
|
81
|
+
'ethnicity', 'languages')
|
82
|
+
patient_document['medical_record_id'] = medical_record_number
|
83
|
+
patient_document['patient_id'] = record['_id'].to_s
|
84
|
+
patient_document['population'] = extract_data_from_cell("C#{row}") || 0
|
85
|
+
if cv_measure?
|
86
|
+
patient_document['values'] = [extract_data_from_cell("E#{row}")]
|
87
|
+
else
|
88
|
+
patient_document['denominator'] = extract_data_from_cell("D#{row}") || 0
|
89
|
+
patient_document['numerator'] = extract_data_from_cell("E#{row}") || 0
|
90
|
+
patient_document['exclusions'] = extract_data_from_cell("F#{row}") || 0
|
91
|
+
patient_document['denexcep'] = extract_data_from_cell("G#{row}") || 0
|
92
|
+
patient_document['antinumerator'] = patient_document['denominator'] - patient_document['numerator']
|
93
|
+
|
94
|
+
end
|
95
|
+
patient_document['test_id'] = nil
|
96
|
+
patient_document['effective_date'] = @effective_date
|
97
|
+
|
98
|
+
patient_document
|
99
|
+
end
|
100
|
+
|
101
|
+
def cv_measure?
|
102
|
+
extract_data_from_cell('E1').eql?('MSRPOPL')
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def extract_data_from_cell(cell_name)
|
108
|
+
row, column = RubyXL::Parser.convert_to_index(cell_name)
|
109
|
+
@sheet.sheet_data[row][column].try(:value)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module QME
|
2
|
+
module Bundle
|
3
|
+
class EHPatientImporter
|
4
|
+
def self.load(db, spreadsheet, effective_date=nil)
|
5
|
+
spreadsheet.worksheets.each do |worksheet|
|
6
|
+
ms = EHMeasureSheet.new(db, worksheet, effective_date)
|
7
|
+
ms.parse
|
8
|
+
qc_document = ms.query_cache_document
|
9
|
+
db['query_cache'].insert(qc_document)
|
10
|
+
ms.patient_cache_documents.each do |pcd|
|
11
|
+
db['patient_cache'].insert(pcd)
|
12
|
+
end
|
13
|
+
ms.patient_updates.each do |patient_update|
|
14
|
+
db['records'].find('medical_record_number' => patient_update['medical_record_number']).update(
|
15
|
+
'$push' => {'measure_ids' => patient_update['measure_id']})
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/qme/bundle/importer.rb
CHANGED
@@ -18,16 +18,7 @@ module QME
|
|
18
18
|
Bundle.drop_collections(@db) if delete_existing
|
19
19
|
|
20
20
|
# Unpack content from the bundle.
|
21
|
-
bundle_contents =
|
22
|
-
Zip::ZipFile.open(zip.path) do |zipfile|
|
23
|
-
zipfile.entries.each do |entry|
|
24
|
-
bundle_contents[:bundle] = zipfile.read(entry.name) if entry.name.include? "bundle"
|
25
|
-
bundle_contents[:measures][Bundle.entry_key(entry.name, "json")] = zipfile.read(entry.name) if entry.name.match /^measures.*\.json$/
|
26
|
-
bundle_contents[:patients][Bundle.entry_key(entry.name, "json")] = zipfile.read(entry.name) if entry.name.match /^patients.*\.json$/ # Only need to import one of the formats
|
27
|
-
bundle_contents[:extensions][Bundle.entry_key(entry.name,"js")] = zipfile.read(entry.name) if entry.name.match /^library_functions/
|
28
|
-
bundle_contents[:results][Bundle.entry_key(entry.name,"json")] = zipfile.read(entry.name) if entry.name.match /^results/
|
29
|
-
end
|
30
|
-
end
|
21
|
+
bundle_contents = QME::Bundle.unpack_bundle_contents(zip)
|
31
22
|
|
32
23
|
# Store all JS libraries.
|
33
24
|
bundle_contents[:extensions].each do |key, contents|
|
@@ -63,6 +54,8 @@ module QME
|
|
63
54
|
contents = JSON.parse(contents, {:max_nesting => 100})
|
64
55
|
contents.each {|document| @db[collection].insert(document)}
|
65
56
|
end
|
57
|
+
|
58
|
+
bundle_contents
|
66
59
|
end
|
67
60
|
end
|
68
61
|
end
|
data/lib/qme/tasks/bundle.rake
CHANGED
@@ -4,11 +4,90 @@ db_name = ENV['DB_NAME'] || 'test'
|
|
4
4
|
|
5
5
|
namespace :bundle do
|
6
6
|
desc 'Import a quality bundle into the database.'
|
7
|
-
task :import, :bundle_path, :delete_existing
|
7
|
+
task :import, [:bundle_path, :delete_existing] => [:environment] do |task, args|
|
8
8
|
raise "The path to the measures zip file must be specified" unless args.bundle_path
|
9
9
|
|
10
10
|
bundle = File.open(args.bundle_path)
|
11
11
|
importer = QME::Bundle::Importer.new(db_name)
|
12
|
-
importer.import(bundle, args.delete_existing)
|
12
|
+
bundle_contents = importer.import(bundle, args.delete_existing == "true")
|
13
|
+
|
14
|
+
puts "Successfully imported bundle at: #{args.bundle_path}"
|
15
|
+
puts "\t Imported into environment: #{Rails.env.upcase}" if defined? Rails
|
16
|
+
puts "\t Measures Loaded: #{bundle_contents[:measures].count}"
|
17
|
+
puts "\t Test Patients Loaded: #{bundle_contents[:patients].count}"
|
18
|
+
puts "\t Extensions Loaded: #{bundle_contents[:extensions].count}"
|
19
|
+
|
13
20
|
end
|
14
|
-
|
21
|
+
|
22
|
+
# this task is most likely temporary. Once Bonnie can handle both EP and EH measures together, this would no longer be required.
|
23
|
+
desc 'Merge two bundles into one.'
|
24
|
+
task :merge, [:bundle_one,:bundle_two] do |t, args|
|
25
|
+
raise "Two bundle zip file paths to be merged must be specified" unless args.bundle_one && args.bundle_two
|
26
|
+
|
27
|
+
tmpdir = Dir.mktmpdir
|
28
|
+
['measures','patients','library_functions','results', 'sources'].each do |dir|
|
29
|
+
|
30
|
+
FileUtils.mkdir_p(File.join(tmpdir, 'output', dir))
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
begin
|
35
|
+
|
36
|
+
({'one'=>args.bundle_one,'two'=>args.bundle_two}).each do |key, source|
|
37
|
+
Zip::ZipFile.open(source) do |zip_file|
|
38
|
+
zip_file.each do |f|
|
39
|
+
f_path=File.join(tmpdir, key, f.name)
|
40
|
+
FileUtils.mkdir_p(File.dirname(f_path))
|
41
|
+
zip_file.extract(f, f_path) unless File.exist?(f_path)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
['measures','patients','library_functions', 'sources'].each do |dir|
|
48
|
+
['one','two'].each do |key|
|
49
|
+
FileUtils.mv(Dir.glob(File.join(tmpdir,key,dir,'*')), File.join(tmpdir,'output',dir))
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
Dir.glob(File.join(tmpdir,'one','results','*.json')).each do |result_path_one|
|
54
|
+
json_one = JSON.parse(File.new(result_path_one).read)
|
55
|
+
result_filename = Pathname.new(result_path_one).basename.to_s
|
56
|
+
json_two = JSON.parse(File.new(File.join(tmpdir,'two','results',result_filename)).read)
|
57
|
+
File.open(File.join(tmpdir,'output','results',result_filename), 'w') {|f| f.write(JSON.pretty_generate(json_one + json_two)) }
|
58
|
+
end
|
59
|
+
|
60
|
+
json_one = JSON.parse(File.new(File.join(tmpdir,'one','bundle.json')).read)
|
61
|
+
json_two = JSON.parse(File.new(File.join(tmpdir,'two','bundle.json')).read)
|
62
|
+
json_out = {}
|
63
|
+
|
64
|
+
['title','effective_date','version','license','exported'].each do |key|
|
65
|
+
json_out[key] = json_one[key]
|
66
|
+
end
|
67
|
+
|
68
|
+
['measures','patients','extensions'].each do |key|
|
69
|
+
json_out[key] = (json_one[key] + json_two[key]).uniq
|
70
|
+
end
|
71
|
+
|
72
|
+
File.open(File.join(tmpdir,'output','bundle.json'), 'w') {|f| f.write(JSON.pretty_generate(json_out)) }
|
73
|
+
date_string = Time.now.strftime("%Y-%m-%d")
|
74
|
+
|
75
|
+
out_zip = File.join('tmp','bundles',"bundle-merged-#{date_string}.zip")
|
76
|
+
FileUtils.remove_entry_secure out_zip if File.exists?(out_zip)
|
77
|
+
Zip::ZipFile.open(out_zip, 'w') do |zipfile|
|
78
|
+
path = File.join(tmpdir,'output')
|
79
|
+
Dir[File.join(path,'**','**')].each do |file|
|
80
|
+
zipfile.add(file.sub(path+'/',''),file)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
puts "wrote merged bundle to: #{out_zip}"
|
85
|
+
|
86
|
+
ensure
|
87
|
+
FileUtils.remove_entry_secure tmpdir
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
data/lib/qme/version.rb
CHANGED
@@ -3,6 +3,7 @@ require "bundler/setup"
|
|
3
3
|
require 'moped'
|
4
4
|
require 'delayed_job_mongoid'
|
5
5
|
require 'zip/zip'
|
6
|
+
require 'rubyXL'
|
6
7
|
|
7
8
|
require "qme/version"
|
8
9
|
require 'qme/database_access'
|
@@ -14,6 +15,8 @@ require 'qme/map/measure_calculation_job'
|
|
14
15
|
|
15
16
|
require 'qme/quality_report'
|
16
17
|
|
18
|
+
require 'qme/bundle/eh_measure_sheet'
|
19
|
+
require 'qme/bundle/eh_patient_importer'
|
17
20
|
require 'qme/bundle/bundle'
|
18
21
|
require 'qme/bundle/importer'
|
19
22
|
|
@@ -21,6 +21,8 @@ Gem::Specification.new do |gem|
|
|
21
21
|
gem.add_dependency 'mongoid', '~> 3.0.9'
|
22
22
|
gem.add_dependency 'rubyzip', '~> 0.9.9'
|
23
23
|
gem.add_dependency 'delayed_job_mongoid', '~> 2.0.0'
|
24
|
+
gem.add_dependency 'nokogiri', '~> 1.5.5'
|
25
|
+
gem.add_dependency 'rubyXL', '~> 1.2.10'
|
24
26
|
|
25
27
|
gem.add_development_dependency "minitest", "~> 4.1.0"
|
26
28
|
gem.add_development_dependency "simplecov", "~> 0.7.1"
|
Binary file
|
@@ -1,9 +1,6 @@
|
|
1
1
|
{
|
2
|
-
_id: "508aeff07042f9f88900000d",
|
3
|
-
priority: 0,
|
4
|
-
attempts: 0,
|
5
|
-
handler: "--- !ruby/object:QME::MapReduce::MeasureCalculationJob\ntest_id: \nmeasure_id: test2\nsub_id: b\neffective_date: 1284854400\nfilters: \n"
|
6
|
-
run_at: ISODate("2012-10-26T20:17:52+00:00"),
|
7
|
-
updated_at: ISODate("2012-10-26T20:17:52+00:00"),
|
8
|
-
created_at: ISODate("2012-10-26T20:17:52+00:00")
|
2
|
+
"_id": "508aeff07042f9f88900000d",
|
3
|
+
"priority": 0,
|
4
|
+
"attempts": 0,
|
5
|
+
"handler": "--- !ruby/object:QME::MapReduce::MeasureCalculationJob\ntest_id: \nmeasure_id: test2\nsub_id: b\neffective_date: 1284854400\nfilters: \n"
|
9
6
|
}
|
Binary file
|
@@ -0,0 +1,19 @@
|
|
1
|
+
{
|
2
|
+
"id": "8A4D92B2-3887-5DF3-0139-0D01C6626E46",
|
3
|
+
"nqf_id": "0142",
|
4
|
+
"hqmf_id": "8A4D92B2-3887-5DF3-0139-0D01C6626E46",
|
5
|
+
"hqmf_set_id": "BB481284-30DD-4383-928C-82385BBF1B17",
|
6
|
+
"hqmf_version_number": 1,
|
7
|
+
"endorser": null,
|
8
|
+
"name": "Aspirin Prescribed at Discharge",
|
9
|
+
"description": "Acute myocardial infarction (AMI) patients who are prescribed aspirin at hospital discharge",
|
10
|
+
"type": "eh",
|
11
|
+
"category": "Miscellaneous",
|
12
|
+
"steward": null,
|
13
|
+
"population_ids": {
|
14
|
+
"IPP": "EDD90083-3417-4221-B3B9-52C4E5FAFAF4",
|
15
|
+
"DENOM": "193A17EC-66B4-4C44-9302-192556C78454",
|
16
|
+
"NUMER": "C224FC7D-B0C3-4A7F-BB9C-4480C3095F9F",
|
17
|
+
"DENEX": "FCE06869-6ADE-4A43-9A28-098D808A55BD"
|
18
|
+
}
|
19
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
{
|
2
|
+
"id": "8A4D92B2-3887-5DF3-0139-0C4E41594C98",
|
3
|
+
"nqf_id": "0495",
|
4
|
+
"hqmf_id": "8A4D92B2-3887-5DF3-0139-0C4E41594C98",
|
5
|
+
"hqmf_set_id": "9A033274-3D9B-11E1-8634-00237D5BF174",
|
6
|
+
"hqmf_version_number": 1,
|
7
|
+
"endorser": null,
|
8
|
+
"name": "Median Time from ED Arrival to ED Departure for Admitted ED Patients",
|
9
|
+
"description": "Median time from emergency department arrival to time of departure from the emergency room for patients admitted to the facility from the emergency department.",
|
10
|
+
"type": "eh",
|
11
|
+
"category": "Miscellaneous",
|
12
|
+
"steward": null,
|
13
|
+
"denominator": null,
|
14
|
+
"numerator": null,
|
15
|
+
"exclusions": null,
|
16
|
+
"sub_id": "a",
|
17
|
+
"subtitle": "Population 1",
|
18
|
+
"short_subtitle": "Population 1",
|
19
|
+
"population_ids": {
|
20
|
+
"IPP": "FA8BE62B-09FB-4B01-8CA8-E3CF1F9A16F6",
|
21
|
+
"MSRPOPL": "F3441A34-7FEA-4DAB-9907-A19885CFF92F"
|
22
|
+
}
|
23
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
{
|
2
|
+
"id": "8A4D92B2-3887-5DF3-0139-0C4E41594C98",
|
3
|
+
"nqf_id": "0495",
|
4
|
+
"hqmf_id": "8A4D92B2-3887-5DF3-0139-0C4E41594C98",
|
5
|
+
"hqmf_set_id": "9A033274-3D9B-11E1-8634-00237D5BF174",
|
6
|
+
"hqmf_version_number": 1,
|
7
|
+
"endorser": null,
|
8
|
+
"name": "Median Time from ED Arrival to ED Departure for Admitted ED Patients",
|
9
|
+
"description": "Median time from emergency department arrival to time of departure from the emergency room for patients admitted to the facility from the emergency department.",
|
10
|
+
"type": "eh",
|
11
|
+
"category": "Miscellaneous",
|
12
|
+
"steward": null,
|
13
|
+
"denominator": null,
|
14
|
+
"numerator": null,
|
15
|
+
"exclusions": null,
|
16
|
+
"sub_id": "b",
|
17
|
+
"subtitle": "Population 2",
|
18
|
+
"short_subtitle": "Population 2",
|
19
|
+
"population_ids": {
|
20
|
+
"IPP": "FA8BE62B-09FB-4B01-8CA8-E3CF1F9A16F6",
|
21
|
+
"MSRPOPL": "F3441A34-7FEA-4DAB-9907-A19885CFF92F",
|
22
|
+
"stratification": "1509C6DA-2465-4571-A6D4-AD634BE49BE6"
|
23
|
+
}
|
24
|
+
}
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class EHMeasureSheetTest < MiniTest::Unit::TestCase
|
4
|
+
include QME::DatabaseAccess
|
5
|
+
|
6
|
+
def setup
|
7
|
+
collection_fixtures(get_db(), 'measures')
|
8
|
+
collection_fixtures(get_db(), 'records', '_id')
|
9
|
+
@workbook = RubyXL::Parser.parse(File.join('test', 'fixtures', 'eh_patient_sheets', 'results_matrix_eh.xlsx'))
|
10
|
+
sheet = @workbook.worksheets[0]
|
11
|
+
@ms = QME::Bundle::EHMeasureSheet.new(get_db(), sheet, 12345000)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_extract_measure_info
|
15
|
+
measure_info = @ms.extract_measure_info
|
16
|
+
assert_equal 'EDD90083-3417-4221-B3B9-52C4E5FAFAF4', measure_info['IPP']
|
17
|
+
assert_equal '193A17EC-66B4-4C44-9302-192556C78454', measure_info['DENOM']
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_query_cache_document
|
21
|
+
@ms.parse
|
22
|
+
qcd = @ms.query_cache_document
|
23
|
+
assert_equal '0142', qcd['nqf_id']
|
24
|
+
assert_equal 4, qcd['population']
|
25
|
+
assert_equal 12345000, qcd['effective_date']
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_patient_cache_documents
|
29
|
+
@ms.parse
|
30
|
+
pcd = @ms.patient_cache_documents.first
|
31
|
+
assert_equal 1, pcd['value']['population']
|
32
|
+
assert_equal 0, pcd['value']['numerator']
|
33
|
+
assert_equal 1, pcd['value']['antinumerator']
|
34
|
+
assert_equal '1234', pcd['value']['medical_record_id']
|
35
|
+
assert_equal 12345000, pcd['value']['effective_date']
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class EHPatientImporterTest < MiniTest::Unit::TestCase
|
4
|
+
include QME::DatabaseAccess
|
5
|
+
|
6
|
+
def setup
|
7
|
+
get_db['query_cache'].drop()
|
8
|
+
get_db['patient_cache'].drop()
|
9
|
+
collection_fixtures(get_db(), 'measures')
|
10
|
+
collection_fixtures(get_db(), 'records', '_id')
|
11
|
+
@workbook = RubyXL::Parser.parse(File.join('test', 'fixtures', 'eh_patient_sheets', 'results_matrix_eh.xlsx'))
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_load
|
15
|
+
assert_equal 0, get_db['query_cache'].find().count
|
16
|
+
QME::Bundle::EHPatientImporter.load(get_db, @workbook)
|
17
|
+
assert_equal 3, get_db['query_cache'].find().count
|
18
|
+
assert_equal 12, get_db['patient_cache'].find().count
|
19
|
+
end
|
20
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: quality-measure-engine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2012-
|
15
|
+
date: 2012-11-14 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: moped
|
@@ -78,6 +78,38 @@ dependencies:
|
|
78
78
|
- - ~>
|
79
79
|
- !ruby/object:Gem::Version
|
80
80
|
version: 2.0.0
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: nokogiri
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ~>
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 1.5.5
|
89
|
+
type: :runtime
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
none: false
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 1.5.5
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubyXL
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
none: false
|
101
|
+
requirements:
|
102
|
+
- - ~>
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: 1.2.10
|
105
|
+
type: :runtime
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
none: false
|
109
|
+
requirements:
|
110
|
+
- - ~>
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: 1.2.10
|
81
113
|
- !ruby/object:Gem::Dependency
|
82
114
|
name: minitest
|
83
115
|
requirement: !ruby/object:Gem::Requirement
|
@@ -141,6 +173,8 @@ files:
|
|
141
173
|
- README.md
|
142
174
|
- Rakefile
|
143
175
|
- lib/qme/bundle/bundle.rb
|
176
|
+
- lib/qme/bundle/eh_measure_sheet.rb
|
177
|
+
- lib/qme/bundle/eh_patient_importer.rb
|
144
178
|
- lib/qme/bundle/importer.rb
|
145
179
|
- lib/qme/database_access.rb
|
146
180
|
- lib/qme/map/map_reduce_builder.rb
|
@@ -155,6 +189,10 @@ files:
|
|
155
189
|
- quality-measure-engine.gemspec
|
156
190
|
- test/fixtures/bundles/just_measure_0002.zip
|
157
191
|
- test/fixtures/delayed_backend_mongoid_jobs/queued_job.json
|
192
|
+
- test/fixtures/eh_patient_sheets/results_matrix_eh.xlsx
|
193
|
+
- test/fixtures/measures/0142.json
|
194
|
+
- test/fixtures/measures/0495a.json
|
195
|
+
- test/fixtures/measures/0495b.json
|
158
196
|
- test/fixtures/measures/measure_metadata.json
|
159
197
|
- test/fixtures/records/barry_berry.json
|
160
198
|
- test/fixtures/records/billy_jones_ipp.json
|
@@ -162,6 +200,8 @@ files:
|
|
162
200
|
- test/fixtures/records/jill_jones_denominator.json
|
163
201
|
- test/simplecov_setup.rb
|
164
202
|
- test/test_helper.rb
|
203
|
+
- test/unit/qme/bundle/eh_measure_sheet_test.rb
|
204
|
+
- test/unit/qme/bundle/eh_patient_importer_test.rb
|
165
205
|
- test/unit/qme/map/map_reduce_builder_test.rb
|
166
206
|
- test/unit/qme/map/map_reduce_executor_test.rb
|
167
207
|
- test/unit/qme/map/measure_calculation_job_test.rb
|
@@ -195,6 +235,10 @@ summary: This library can run JavaScript based clinical quality measures on a re
|
|
195
235
|
test_files:
|
196
236
|
- test/fixtures/bundles/just_measure_0002.zip
|
197
237
|
- test/fixtures/delayed_backend_mongoid_jobs/queued_job.json
|
238
|
+
- test/fixtures/eh_patient_sheets/results_matrix_eh.xlsx
|
239
|
+
- test/fixtures/measures/0142.json
|
240
|
+
- test/fixtures/measures/0495a.json
|
241
|
+
- test/fixtures/measures/0495b.json
|
198
242
|
- test/fixtures/measures/measure_metadata.json
|
199
243
|
- test/fixtures/records/barry_berry.json
|
200
244
|
- test/fixtures/records/billy_jones_ipp.json
|
@@ -202,6 +246,8 @@ test_files:
|
|
202
246
|
- test/fixtures/records/jill_jones_denominator.json
|
203
247
|
- test/simplecov_setup.rb
|
204
248
|
- test/test_helper.rb
|
249
|
+
- test/unit/qme/bundle/eh_measure_sheet_test.rb
|
250
|
+
- test/unit/qme/bundle/eh_patient_importer_test.rb
|
205
251
|
- test/unit/qme/map/map_reduce_builder_test.rb
|
206
252
|
- test/unit/qme/map/map_reduce_executor_test.rb
|
207
253
|
- test/unit/qme/map/measure_calculation_job_test.rb
|