quality-measure-engine 2.5.3 → 3.0.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +9 -9
- data/Gemfile.lock +4 -4
- data/lib/qme/database_access.rb +2 -22
- data/lib/qme/manual_exclusion.rb +18 -0
- data/lib/qme/map/map_reduce_builder.rb +10 -10
- data/lib/qme/map/map_reduce_executor.rb +33 -33
- data/lib/qme/map/measure_calculation_job.rb +69 -15
- data/lib/qme/patient_cache.rb +31 -0
- data/lib/qme/quality_measure.rb +11 -55
- data/lib/qme/quality_report.rb +98 -74
- data/lib/qme/version.rb +1 -1
- data/lib/quality-measure-engine.rb +2 -3
- data/test/fixtures/library_functions/map_reduce_utils.js +6 -0
- data/test/fixtures/measures/0002.json +1 -1
- data/test/fixtures/measures/measure_metadata.json +1 -0
- data/test/mongoid.yml +6 -0
- data/test/test_helper.rb +3 -7
- data/test/unit/qme/map/map_reduce_builder_test.rb +2 -2
- data/test/unit/qme/map/map_reduce_executor_test.rb +27 -38
- data/test/unit/qme/map/measure_calculation_job_test.rb +2 -2
- data/test/unit/qme/quality_measure_test.rb +3 -39
- data/test/unit/qme/quality_report_test.rb +12 -9
- metadata +9 -11
- data/lib/qme/bundle/eh_measure_sheet.rb +0 -117
- data/lib/qme/bundle/eh_patient_importer.rb +0 -25
- data/test/unit/qme/bundle/eh_measure_sheet_test.rb +0 -37
- data/test/unit/qme/bundle/eh_patient_importer_test.rb +0 -20
@@ -2,52 +2,16 @@ require 'test_helper'
|
|
2
2
|
|
3
3
|
class QualityMeasureTest < MiniTest::Unit::TestCase
|
4
4
|
include QME::DatabaseAccess
|
5
|
-
|
6
5
|
def setup
|
6
|
+
|
7
7
|
collection_fixtures(get_db(), 'measures')
|
8
8
|
collection_fixtures(get_db(), 'bundles')
|
9
|
-
@bundle_id = get_db['bundles'].find.first['_id']
|
10
|
-
get_db['measures'].find({}).update(:$set => {'bundle_id' => @bundle_id})
|
11
9
|
load_system_js
|
12
10
|
end
|
13
11
|
|
14
|
-
def
|
12
|
+
def test_getting_all_measures
|
15
13
|
all_measures = QME::QualityMeasure.all
|
16
14
|
assert_equal 5, all_measures.size
|
17
|
-
|
18
|
-
assert all_measures["2E679CD2-3FEC-4A75-A75A-61403E5EFEE8.json"]
|
19
|
-
end
|
20
|
-
|
21
|
-
def test_getting_definition_with_bundle_id
|
22
|
-
result = QME::QualityMeasure.all(@bundle_id).to_a.first.last
|
23
|
-
measure = QME::QualityMeasure.new(result['id'], result['sub_id'], @bundle_id)
|
24
|
-
assert measure.definition
|
25
|
-
assert_equal result['id'], measure.definition['id']
|
26
|
-
end
|
27
|
-
|
28
|
-
def test_getting_all_measure_with_bundle_id
|
29
|
-
get_db()['measures']
|
30
|
-
all_measures = QME::QualityMeasure.all(@bundle_id)
|
31
|
-
|
32
|
-
assert_equal 1, all_measures.size
|
15
|
+
assert all_measures.where({"hqmf_id" => "2E679CD2-3FEC-4A75-A75A-61403E5EFEE8"}).first
|
33
16
|
end
|
34
|
-
|
35
|
-
def test_getting_measure_subset
|
36
|
-
measure_ids = get_db['measures'].find({}).map { |m| m['id'] }
|
37
|
-
measure_ids.pop
|
38
|
-
measures = QME::QualityMeasure.get_measures(measure_ids)
|
39
|
-
measure_ids2 = measures.map { |m| m['id'] }
|
40
|
-
assert_equal [], measure_ids - measure_ids2
|
41
|
-
end
|
42
|
-
|
43
|
-
def test_getting_sub_measures
|
44
|
-
measures = QME::QualityMeasure.sub_measures("8A4D92B2-3887-5DF3-0139-0C4E41594C98")
|
45
|
-
assert_equal 2, measures.count
|
46
|
-
end
|
47
|
-
|
48
|
-
def test_getting_sub_measure
|
49
|
-
measure = QME::QualityMeasure.get("8A4D92B2-3887-5DF3-0139-0C4E41594C98", 'a')
|
50
|
-
assert measure
|
51
|
-
end
|
52
|
-
|
53
17
|
end
|
@@ -11,10 +11,12 @@ class QualityReportTest < MiniTest::Unit::TestCase
|
|
11
11
|
get_db()['query_cache'].insert(
|
12
12
|
"measure_id" => "test2",
|
13
13
|
"sub_id" => "b",
|
14
|
+
"status" =>{"state" => "completed"},
|
14
15
|
"initialPopulation" => 4,
|
16
|
+
"result" => {
|
15
17
|
QME::QualityReport::NUMERATOR => 1,
|
16
18
|
QME::QualityReport::DENOMINATOR => 2,
|
17
|
-
QME::QualityReport::EXCLUSIONS => 1,
|
19
|
+
QME::QualityReport::EXCLUSIONS => 1},
|
18
20
|
"effective_date" => Time.gm(2010, 9, 19).to_i
|
19
21
|
)
|
20
22
|
get_db()['patient_cache'].insert(
|
@@ -48,15 +50,15 @@ class QualityReportTest < MiniTest::Unit::TestCase
|
|
48
50
|
end
|
49
51
|
|
50
52
|
def test_calculated
|
51
|
-
qr = QME::QualityReport.
|
53
|
+
qr = QME::QualityReport.find_or_create('test2', 'b', "effective_date" => Time.gm(2010, 9, 19).to_i)
|
52
54
|
assert qr.calculated?
|
53
55
|
|
54
|
-
qr = QME::QualityReport.
|
56
|
+
qr = QME::QualityReport.find_or_create('test2', 'b', "effective_date" => Time.gm(2010, 9, 20).to_i)
|
55
57
|
assert !qr.calculated?
|
56
58
|
end
|
57
59
|
|
58
60
|
def test_result
|
59
|
-
qr = QME::QualityReport.
|
61
|
+
qr = QME::QualityReport.find_or_create('test2', 'b', "effective_date" => Time.gm(2010, 9, 19).to_i)
|
60
62
|
result = qr.result
|
61
63
|
|
62
64
|
assert_equal 1, result[QME::QualityReport::NUMERATOR]
|
@@ -65,24 +67,25 @@ class QualityReportTest < MiniTest::Unit::TestCase
|
|
65
67
|
def test_destroy_all
|
66
68
|
QME::QualityReport.destroy_all
|
67
69
|
|
68
|
-
qr = QME::QualityReport.
|
70
|
+
qr = QME::QualityReport.find_or_create('test2', 'b', "effective_date" => Time.gm(2010, 9, 19).to_i)
|
69
71
|
assert !qr.calculated?
|
70
72
|
end
|
71
73
|
|
72
74
|
def test_update_patient_results
|
73
|
-
qr = QME::QualityReport.
|
75
|
+
qr = QME::QualityReport.find_or_create('test2', 'b', "effective_date" => Time.gm(2010, 9, 19).to_i)
|
74
76
|
assert qr.calculated?
|
75
77
|
assert qr.patients_cached?
|
76
78
|
QME::QualityReport.update_patient_results("0616911582")
|
79
|
+
qr = QME::QualityReport.find_or_create('test2', 'b', "effective_date" => Time.gm(2010, 9, 19).to_i)
|
77
80
|
assert !qr.calculated?
|
78
81
|
assert !qr.patients_cached?
|
79
82
|
end
|
80
83
|
|
81
84
|
def test_status
|
82
|
-
|
83
|
-
status =
|
85
|
+
|
86
|
+
status = QME::MapReduce::MeasureCalculationJob.status('not really a job id')
|
84
87
|
assert_equal :complete, status
|
85
|
-
status =
|
88
|
+
status = QME::MapReduce::MeasureCalculationJob.status("508aeff07042f9f88900000d")
|
86
89
|
assert_equal :queued, status
|
87
90
|
end
|
88
91
|
|
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:
|
4
|
+
version: 3.0.0.beta.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marc Hadley
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2013-12-
|
15
|
+
date: 2013-12-11 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: moped
|
@@ -154,13 +154,13 @@ files:
|
|
154
154
|
- LICENSE.txt
|
155
155
|
- README.md
|
156
156
|
- Rakefile
|
157
|
-
- lib/qme/bundle/eh_measure_sheet.rb
|
158
|
-
- lib/qme/bundle/eh_patient_importer.rb
|
159
157
|
- lib/qme/database_access.rb
|
158
|
+
- lib/qme/manual_exclusion.rb
|
160
159
|
- lib/qme/map/cv_aggregator.rb
|
161
160
|
- lib/qme/map/map_reduce_builder.rb
|
162
161
|
- lib/qme/map/map_reduce_executor.rb
|
163
162
|
- lib/qme/map/measure_calculation_job.rb
|
163
|
+
- lib/qme/patient_cache.rb
|
164
164
|
- lib/qme/quality_measure.rb
|
165
165
|
- lib/qme/quality_report.rb
|
166
166
|
- lib/qme/railtie.rb
|
@@ -184,10 +184,9 @@ files:
|
|
184
184
|
- test/fixtures/records/billy_jones_ipp.json
|
185
185
|
- test/fixtures/records/jane_jones_numerator.json
|
186
186
|
- test/fixtures/records/jill_jones_denominator.json
|
187
|
+
- test/mongoid.yml
|
187
188
|
- test/simplecov_setup.rb
|
188
189
|
- test/test_helper.rb
|
189
|
-
- test/unit/qme/bundle/eh_measure_sheet_test.rb
|
190
|
-
- test/unit/qme/bundle/eh_patient_importer_test.rb
|
191
190
|
- test/unit/qme/map/cv_aggregator_test.rb
|
192
191
|
- test/unit/qme/map/map_reduce_builder_test.rb
|
193
192
|
- test/unit/qme/map/map_reduce_executor_test.rb
|
@@ -208,12 +207,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
208
207
|
version: '0'
|
209
208
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
210
209
|
requirements:
|
211
|
-
- - ! '
|
210
|
+
- - ! '>'
|
212
211
|
- !ruby/object:Gem::Version
|
213
|
-
version:
|
212
|
+
version: 1.3.1
|
214
213
|
requirements: []
|
215
214
|
rubyforge_project:
|
216
|
-
rubygems_version: 2.
|
215
|
+
rubygems_version: 2.0.7
|
217
216
|
signing_key:
|
218
217
|
specification_version: 4
|
219
218
|
summary: This library can run JavaScript based clinical quality measures on a repository
|
@@ -236,10 +235,9 @@ test_files:
|
|
236
235
|
- test/fixtures/records/billy_jones_ipp.json
|
237
236
|
- test/fixtures/records/jane_jones_numerator.json
|
238
237
|
- test/fixtures/records/jill_jones_denominator.json
|
238
|
+
- test/mongoid.yml
|
239
239
|
- test/simplecov_setup.rb
|
240
240
|
- test/test_helper.rb
|
241
|
-
- test/unit/qme/bundle/eh_measure_sheet_test.rb
|
242
|
-
- test/unit/qme/bundle/eh_patient_importer_test.rb
|
243
241
|
- test/unit/qme/map/cv_aggregator_test.rb
|
244
242
|
- test/unit/qme/map/map_reduce_builder_test.rb
|
245
243
|
- test/unit/qme/map/map_reduce_executor_test.rb
|
@@ -1,117 +0,0 @@
|
|
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[QME::QualityReport::POPULATION] = extract_data_from_cell("C#{@population_totals_row}")
|
26
|
-
qc_document[QME::QualityReport::CONSIDERED] = @population_totals_row - 3 # header row, blank row and totals row
|
27
|
-
if cv_measure?
|
28
|
-
qc_document[QME::QualityReport::MSRPOPL] = extract_data_from_cell("E#{@population_totals_row}")
|
29
|
-
qc_document[QME::QualityReport::OBSERVATION] = extract_data_from_cell("F#{@population_totals_row}")
|
30
|
-
else
|
31
|
-
qc_document[QME::QualityReport::DENOMINATOR] = extract_data_from_cell("D#{@population_totals_row}")
|
32
|
-
qc_document[QME::QualityReport::NUMERATOR] = extract_data_from_cell("E#{@population_totals_row}")
|
33
|
-
qc_document[QME::QualityReport::ANTINUMERATOR] = qc_document[QME::QualityReport::DENOMINATOR] - qc_document[QME::QualityReport::NUMERATOR]
|
34
|
-
qc_document[QME::QualityReport::EXCLUSIONS] = extract_data_from_cell("F#{@population_totals_row}")
|
35
|
-
qc_document[QME::QualityReport::EXCEPTIONS] = extract_data_from_cell("G#{@population_totals_row}")
|
36
|
-
end
|
37
|
-
|
38
|
-
qc_document['test_id'] = nil
|
39
|
-
qc_document['filters'] = nil
|
40
|
-
qc_document['execution_time'] = 0
|
41
|
-
qc_document['effective_date'] = @effective_date
|
42
|
-
|
43
|
-
@query_cache_document = qc_document
|
44
|
-
end
|
45
|
-
|
46
|
-
def extract_patients(measure_ids)
|
47
|
-
row = 2
|
48
|
-
medical_record_number = extract_data_from_cell("B#{row}")
|
49
|
-
while medical_record_number.present?
|
50
|
-
patient_document = extract_patient(row, medical_record_number.to_s)
|
51
|
-
patient_document.merge!(measure_ids)
|
52
|
-
@patient_cache_documents << {'value' => patient_document}
|
53
|
-
@patient_updates << {'medical_record_number' => medical_record_number.to_s,
|
54
|
-
'measure_id' => measure_ids['measure_id']}
|
55
|
-
row = row + 1
|
56
|
-
medical_record_number = extract_data_from_cell("B#{row}")
|
57
|
-
end
|
58
|
-
|
59
|
-
@population_totals_row = row + 1
|
60
|
-
end
|
61
|
-
|
62
|
-
def extract_measure_info
|
63
|
-
measure_info = {}
|
64
|
-
measure_info[QME::QualityReport::POPULATION] = extract_data_from_cell('I5')
|
65
|
-
if cv_measure?
|
66
|
-
measure_info[QME::QualityReport::MSRPOPL] = extract_data_from_cell('I10')
|
67
|
-
measure_info[QME::QualityReport::OBSERVATION] = extract_data_from_cell('I11') if extract_data_from_cell('I11').present?
|
68
|
-
measure_info['stratification'] = extract_data_from_cell('I12') if extract_data_from_cell('I12').present?
|
69
|
-
else
|
70
|
-
measure_info['DENOM'] = extract_data_from_cell('I6')
|
71
|
-
measure_info['NUMER'] = extract_data_from_cell('I7')
|
72
|
-
measure_info['DENEXCEP'] = extract_data_from_cell('I8') if extract_data_from_cell('I8').present?
|
73
|
-
measure_info['DENEX'] = extract_data_from_cell('I9') if extract_data_from_cell('I9').present?
|
74
|
-
measure_info['stratification'] = extract_data_from_cell('I12') if extract_data_from_cell('I12').present?
|
75
|
-
end
|
76
|
-
|
77
|
-
measure_info
|
78
|
-
end
|
79
|
-
|
80
|
-
def extract_patient(row, medical_record_number)
|
81
|
-
record = @db['records'].where('medical_record_number' => medical_record_number).first
|
82
|
-
patient_document = record.slice('first', 'last', 'gender', 'birthdate', 'race',
|
83
|
-
'ethnicity', 'languages')
|
84
|
-
patient_document['medical_record_id'] = medical_record_number
|
85
|
-
patient_document['patient_id'] = record['_id'].to_s
|
86
|
-
patient_document[QME::QualityReport::POPULATION] = extract_data_from_cell("C#{row}") || 0
|
87
|
-
if cv_measure?
|
88
|
-
patient_document[QME::QualityReport::MSRPOPL] = extract_data_from_cell("E#{row}") || 0
|
89
|
-
patient_document['values'] = [extract_data_from_cell("F#{row}")]
|
90
|
-
else
|
91
|
-
patient_document[QME::QualityReport::DENOMINATOR] = extract_data_from_cell("D#{row}") || 0
|
92
|
-
patient_document[QME::QualityReport::NUMERATOR] = extract_data_from_cell("E#{row}") || 0
|
93
|
-
patient_document[QME::QualityReport::EXCLUSIONS] = extract_data_from_cell("F#{row}") || 0
|
94
|
-
patient_document[QME::QualityReport::EXCEPTIONS] = extract_data_from_cell("G#{row}") || 0
|
95
|
-
patient_document[QME::QualityReport::ANTINUMERATOR] = patient_document[QME::QualityReport::DENOMINATOR] - patient_document[QME::QualityReport::NUMERATOR]
|
96
|
-
|
97
|
-
end
|
98
|
-
patient_document['test_id'] = nil
|
99
|
-
patient_document['effective_date'] = @effective_date
|
100
|
-
|
101
|
-
patient_document
|
102
|
-
end
|
103
|
-
|
104
|
-
def cv_measure?
|
105
|
-
extract_data_from_cell('E1').eql?('MSRPOPL')
|
106
|
-
end
|
107
|
-
|
108
|
-
private
|
109
|
-
|
110
|
-
def extract_data_from_cell(cell_name)
|
111
|
-
row, column = RubyXL::Parser.convert_to_index(cell_name)
|
112
|
-
@sheet.sheet_data[row][column].try(:value)
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
end
|
117
|
-
end
|
@@ -1,25 +0,0 @@
|
|
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'].find({measure_id: qc_document['measure_id'], sub_id: qc_document['sub_id']}).remove
|
10
|
-
db.command(getLastError: 1)
|
11
|
-
db['query_cache'].insert(qc_document)
|
12
|
-
ms.patient_cache_documents.each do |pcd|
|
13
|
-
db['patient_cache'].find({'value.measure_id'=>pcd['value']['measure_id'], 'value.sub_id'=>pcd['value']['sub_id'],'value.medical_record_id'=>pcd['value']['medical_record_id']}).remove
|
14
|
-
db.command(getLastError: 1)
|
15
|
-
db['patient_cache'].insert(pcd)
|
16
|
-
end
|
17
|
-
ms.patient_updates.each do |patient_update|
|
18
|
-
db['records'].find('medical_record_number' => patient_update['medical_record_number']).update(
|
19
|
-
'$push' => {'measure_ids' => patient_update['measure_id']})
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,37 +0,0 @@
|
|
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[QME::QualityReport::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'][QME::QualityReport::POPULATION]
|
32
|
-
assert_equal 0, pcd['value'][QME::QualityReport::NUMERATOR]
|
33
|
-
assert_equal 1, pcd['value'][QME::QualityReport::ANTINUMERATOR]
|
34
|
-
assert_equal '1234', pcd['value']['medical_record_id']
|
35
|
-
assert_equal 12345000, pcd['value']['effective_date']
|
36
|
-
end
|
37
|
-
end
|
@@ -1,20 +0,0 @@
|
|
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
|