quality-measure-engine 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,5 @@
1
1
  {
2
+
2
3
  "id": "8A4D92B2-3887-5DF3-0139-0D01C6626E46",
3
4
  "nqf_id": "0142",
4
5
  "hqmf_id": "8A4D92B2-3887-5DF3-0139-0D01C6626E46",
@@ -1,4 +1,5 @@
1
1
  {
2
+
2
3
  "id": "8A4D92B2-3887-5DF3-0139-0C4E41594C98",
3
4
  "nqf_id": "0495",
4
5
  "hqmf_id": "8A4D92B2-3887-5DF3-0139-0C4E41594C98",
@@ -1,4 +1,5 @@
1
1
  {
2
+
2
3
  "id": "8A4D92B2-3887-5DF3-0139-0C4E41594C98",
3
4
  "nqf_id": "0495",
4
5
  "hqmf_id": "8A4D92B2-3887-5DF3-0139-0C4E41594C98",
@@ -1,4 +1,5 @@
1
1
  {
2
+
2
3
  "id": "0043",
3
4
  "name": "Pneumonia Vaccination Status for Older Adults",
4
5
  "description": "Patients more than 65 years old who received a pneumococcal vaccine.",
data/test/test_helper.rb CHANGED
@@ -9,6 +9,22 @@ Mongoid.configure do |config|
9
9
  end
10
10
 
11
11
  class MiniTest::Unit::TestCase
12
+
13
+ def load_system_js
14
+ Mongoid.default_session['system.js'].drop
15
+ Dir.glob(File.join(File.dirname(__FILE__), 'fixtures', "library_functions", '*.js')).each do |json_fixture_file|
16
+ name = File.basename(json_fixture_file,".*")
17
+ fn = "function () {\n #{File.read(json_fixture_file)} \n }"
18
+ Mongoid.default_session['system.js'].find('_id' => name).upsert(
19
+ {
20
+ "_id" => name,
21
+ "value" => Moped::BSON::Code.new(fn)
22
+ }
23
+ )
24
+ end
25
+
26
+ end
27
+
12
28
  # Add more helper methods to be used by all tests here...
13
29
 
14
30
  def collection_fixtures(db, collection, *id_attributes)
@@ -6,6 +6,7 @@ class MapReduceBuilderTest < MiniTest::Unit::TestCase
6
6
  def setup
7
7
  raw_measure_json = File.read(File.join('test', 'fixtures', 'measures', 'measure_metadata.json'))
8
8
  @measure_json = JSON.parse(raw_measure_json)
9
+ load_system_js
9
10
  end
10
11
 
11
12
  def test_extracting_measure_metadata
@@ -4,10 +4,13 @@ class MapReduceExecutorTest < MiniTest::Unit::TestCase
4
4
  include QME::DatabaseAccess
5
5
 
6
6
  def setup
7
- importer = QME::Bundle::Importer.new
8
- importer.import(File.new('test/fixtures/bundles/just_measure_0002.zip'), true, nil)
9
7
 
8
+ get_db['query_cache'].drop()
9
+ get_db['patient_cache'].drop()
10
+ collection_fixtures(get_db(), 'measures')
10
11
  collection_fixtures(get_db(), 'records', '_id')
12
+ collection_fixtures(get_db(), 'bundles')
13
+ load_system_js
11
14
  end
12
15
 
13
16
  def test_map_records_into_measure_groups
@@ -4,10 +4,12 @@ class MapCalculationJobTest < MiniTest::Unit::TestCase
4
4
  include QME::DatabaseAccess
5
5
 
6
6
  def setup
7
- importer = QME::Bundle::Importer.new
8
- importer.import(File.new('test/fixtures/bundles/just_measure_0002.zip'), true, nil)
9
-
7
+ get_db['query_cache'].drop()
8
+ get_db['patient_cache'].drop()
9
+ collection_fixtures(get_db(), 'measures')
10
10
  collection_fixtures(get_db(), 'records', '_id')
11
+ collection_fixtures(get_db(), 'bundles')
12
+ load_system_js
11
13
 
12
14
  Delayed::Worker.delay_jobs = false
13
15
  end
@@ -1,14 +1,17 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class QualityMeasureTest < MiniTest::Unit::TestCase
4
+ include QME::DatabaseAccess
4
5
  def setup
5
- importer = QME::Bundle::Importer.new
6
- importer.import(File.new('test/fixtures/bundles/just_measure_0002.zip'), true,nil)
6
+
7
+ collection_fixtures(get_db(), 'measures')
8
+ collection_fixtures(get_db(), 'bundles')
9
+ load_system_js
7
10
  end
8
11
 
9
12
  def test_getting_all_measures
10
13
  all_measures = QME::QualityMeasure.all
11
- assert_equal 1, all_measures.size
14
+ assert_equal 5, all_measures.size
12
15
  assert all_measures["2E679CD2-3FEC-4A75-A75A-61403E5EFEE8.json"]
13
16
  end
14
17
  end
@@ -4,6 +4,8 @@ class QualityReportTest < MiniTest::Unit::TestCase
4
4
  include QME::DatabaseAccess
5
5
 
6
6
  def setup
7
+ load_system_js
8
+ collection_fixtures(get_db(), 'bundles')
7
9
  get_db()['query_cache'].drop()
8
10
  get_db()['patient_cache'].drop()
9
11
  get_db()['query_cache'].insert(
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.2.0
4
+ version: 2.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,10 +9,11 @@ authors:
9
9
  - Andy Gregorowicz
10
10
  - Rob Dingwell
11
11
  - Adam Goldstein
12
+ - Andre Quina
12
13
  autorequire:
13
14
  bindir: bin
14
15
  cert_chain: []
15
- date: 2012-12-19 00:00:00.000000000 Z
16
+ date: 2013-02-27 00:00:00.000000000 Z
16
17
  dependencies:
17
18
  - !ruby/object:Gem::Dependency
18
19
  name: moped
@@ -21,7 +22,7 @@ dependencies:
21
22
  requirements:
22
23
  - - ~>
23
24
  - !ruby/object:Gem::Version
24
- version: 1.2.7
25
+ version: 1.4.2
25
26
  type: :runtime
26
27
  prerelease: false
27
28
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +30,7 @@ dependencies:
29
30
  requirements:
30
31
  - - ~>
31
32
  - !ruby/object:Gem::Version
32
- version: 1.2.7
33
+ version: 1.4.2
33
34
  - !ruby/object:Gem::Dependency
34
35
  name: mongoid
35
36
  requirement: !ruby/object:Gem::Requirement
@@ -37,7 +38,7 @@ dependencies:
37
38
  requirements:
38
39
  - - ~>
39
40
  - !ruby/object:Gem::Version
40
- version: 3.0.9
41
+ version: '3.0'
41
42
  type: :runtime
42
43
  prerelease: false
43
44
  version_requirements: !ruby/object:Gem::Requirement
@@ -45,7 +46,7 @@ dependencies:
45
46
  requirements:
46
47
  - - ~>
47
48
  - !ruby/object:Gem::Version
48
- version: 3.0.9
49
+ version: '3.0'
49
50
  - !ruby/object:Gem::Dependency
50
51
  name: rubyzip
51
52
  requirement: !ruby/object:Gem::Requirement
@@ -172,24 +173,28 @@ files:
172
173
  - LICENSE.txt
173
174
  - README.md
174
175
  - Rakefile
175
- - lib/qme/bundle/bundle.rb
176
176
  - lib/qme/bundle/eh_measure_sheet.rb
177
177
  - lib/qme/bundle/eh_patient_importer.rb
178
- - lib/qme/bundle/importer.rb
179
178
  - lib/qme/database_access.rb
179
+ - lib/qme/map/cv_aggregator.rb
180
180
  - lib/qme/map/map_reduce_builder.rb
181
181
  - lib/qme/map/map_reduce_executor.rb
182
182
  - lib/qme/map/measure_calculation_job.rb
183
183
  - lib/qme/quality_measure.rb
184
184
  - lib/qme/quality_report.rb
185
185
  - lib/qme/railtie.rb
186
- - lib/qme/tasks/bundle.rake
187
186
  - lib/qme/version.rb
188
187
  - lib/quality-measure-engine.rb
189
188
  - quality-measure-engine.gemspec
189
+ - test/fixtures/bundles/bundle.json
190
190
  - test/fixtures/bundles/just_measure_0002.zip
191
+ - test/fixtures/bundles/measures_0003_0002.zip
191
192
  - test/fixtures/delayed_backend_mongoid_jobs/queued_job.json
192
193
  - test/fixtures/eh_patient_sheets/results_matrix_eh.xlsx
194
+ - test/fixtures/library_functions/hqmf_utils.js
195
+ - test/fixtures/library_functions/map_reduce_utils.js
196
+ - test/fixtures/library_functions/underscore_min.js
197
+ - test/fixtures/measures/0002.json
193
198
  - test/fixtures/measures/0142.json
194
199
  - test/fixtures/measures/0495a.json
195
200
  - test/fixtures/measures/0495b.json
@@ -233,9 +238,15 @@ specification_version: 3
233
238
  summary: This library can run JavaScript based clinical quality measures on a repository
234
239
  of patients stored in MongoDB
235
240
  test_files:
241
+ - test/fixtures/bundles/bundle.json
236
242
  - test/fixtures/bundles/just_measure_0002.zip
243
+ - test/fixtures/bundles/measures_0003_0002.zip
237
244
  - test/fixtures/delayed_backend_mongoid_jobs/queued_job.json
238
245
  - test/fixtures/eh_patient_sheets/results_matrix_eh.xlsx
246
+ - test/fixtures/library_functions/hqmf_utils.js
247
+ - test/fixtures/library_functions/map_reduce_utils.js
248
+ - test/fixtures/library_functions/underscore_min.js
249
+ - test/fixtures/measures/0002.json
239
250
  - test/fixtures/measures/0142.json
240
251
  - test/fixtures/measures/0495a.json
241
252
  - test/fixtures/measures/0495b.json
@@ -1,51 +0,0 @@
1
- module QME
2
- module Bundle
3
- # Delete a list of collections. By default, this function drops all of collections related to measures and patients.
4
- #
5
- # @param [Array] collection_names Optionally, an array of collection names to be dropped.
6
- def self.drop_collections(db, collection_names=[])
7
- collection_names = ["bundles", "records", "measures", "selected_measures", "patient_cache", "query_cache", "system.js"] if collection_names.empty?
8
- collection_names.each {|collection| db[collection].drop}
9
- end
10
-
11
- # Save a javascript function into Mongo's system.js collection for measure execution.
12
- #
13
- # @param [String] name The name by which the function will be referred.
14
- # @param [String] fn The body of the function being saved.
15
- def self.save_system_js_fn(db, name, fn)
16
- fn = "function () {\n #{fn} \n }"
17
- db['system.js'].find('_id' => name).upsert(
18
- {
19
- "_id" => name,
20
- "value" => Moped::BSON::Code.new(fn)
21
- }
22
- )
23
- end
24
-
25
- # A utility function for finding files in a bundle. Strip a file path of it's extension and just give the filename.
26
- #
27
- # @param [String] original A file path.
28
- # @param [String] extension A file extension.
29
- # @return The filename at the end of the original String path with the extension removed. e.g. "/boo/urns.html" -> "urns"
30
- def self.entry_key(original, extension)
31
- original.split('/').last.gsub(".#{extension}", '')
32
- end
33
-
34
- def self.unpack_bundle_contents(zip, type = nil)
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
- if type.nil? || entry.name.match(Regexp.new("/#{type}/"))
40
- bundle_contents[:measures][Bundle.entry_key(entry.name, "json")] = zipfile.read(entry.name) if entry.name.match /^measures.*\.json$/
41
- 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
42
- bundle_contents[:results][Bundle.entry_key(entry.name,"json")] = zipfile.read(entry.name) if entry.name.match /^results.*\.json/
43
- end
44
- bundle_contents[:extensions][Bundle.entry_key(entry.name,"js")] = zipfile.read(entry.name) if entry.name.match /^library_functions.*\.js/
45
-
46
- end
47
- end
48
- bundle_contents
49
- end
50
- end
51
- end
@@ -1,63 +0,0 @@
1
- module QME
2
- module Bundle
3
- class Importer
4
- include QME::DatabaseAccess
5
-
6
- # Create a new Importer.
7
- # @param [String] db_name the name of the database to use
8
- def initialize(db_name = nil)
9
- determine_connection_information(db_name)
10
- @db = get_db
11
- end
12
-
13
- # Import a quality bundle into the database. This includes metadata, measures, test patients, supporting JS libraries, and expected results.
14
- #
15
- # @param [File] zip The bundle zip file.
16
- # @param [String] Type of measures to import, either 'ep', 'eh' or nil for all
17
- # @param [Boolean] keep_existing If true, delete all current collections related to patients and measures.
18
- def import(zip, delete_existing, type=nil)
19
- Bundle.drop_collections(@db) if delete_existing
20
-
21
- # Unpack content from the bundle.
22
- bundle_contents = QME::Bundle.unpack_bundle_contents(zip, type)
23
-
24
- # Store all JS libraries.
25
- bundle_contents[:extensions].each do |key, contents|
26
- Bundle.save_system_js_fn(@db, key, contents)
27
- end
28
-
29
- # Store the bundle metadata.
30
- bundle_id = Moped::BSON::ObjectId.new()
31
- bundle = JSON.parse(bundle_contents[:bundle])
32
- bundle["_id"] = bundle_id
33
- @db['bundles'].insert(bundle)
34
-
35
- # Store all measures.
36
- bundle_contents[:measures].each do |key, contents|
37
- measure_id = Moped::BSON::ObjectId.new()
38
- measure = JSON.parse(contents, {:max_nesting => 100})
39
- measure['_id'] = measure_id
40
- measure['bundle'] = bundle_id
41
- @db['measures'].insert(measure)
42
- end
43
-
44
- # Store all patients.
45
- bundle_id = Moped::BSON::ObjectId(bundle_id.to_s)
46
- bundle_contents[:patients].each do |key, contents|
47
- patient = JSON.parse(contents, {:max_nesting => 100})
48
- patient['bundle'] = bundle_id
49
- Record.new(patient).save
50
- end
51
-
52
- # Store the expected results into the query and patient caches.
53
- bundle_contents[:results].each do |name, contents|
54
- collection = name == "by_patient" ? "patient_cache" : "query_cache"
55
- contents = JSON.parse(contents, {:max_nesting => 100})
56
- contents.each {|document| @db[collection].insert(document)}
57
- end
58
-
59
- bundle_contents
60
- end
61
- end
62
- end
63
- end
@@ -1,95 +0,0 @@
1
- require 'quality-measure-engine'
2
-
3
- db_name = ENV['DB_NAME'] || 'test'
4
-
5
- namespace :bundle do
6
- desc 'Import a quality bundle into the database.'
7
- task :import, [:bundle_path, :delete_existing, :type] => [:environment] do |task, args|
8
- raise "The path to the measures zip file must be specified" unless args.bundle_path
9
-
10
- bundle = File.open(args.bundle_path)
11
- importer = QME::Bundle::Importer.new(db_name)
12
- bundle_contents = importer.import(bundle, args.delete_existing == "true", args.type)
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 Loaded #{args.type || "all"} measures"
17
- puts "\t Measures Loaded: #{bundle_contents[:measures].count}"
18
- puts "\t Test Patients Loaded: #{bundle_contents[:patients].count}"
19
- puts "\t Extensions Loaded: #{bundle_contents[:extensions].count}"
20
- end
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
- version = json_out['version']
73
-
74
- File.open(File.join(tmpdir,'output','bundle.json'), 'w') {|f| f.write(JSON.pretty_generate(json_out)) }
75
- date_string = Time.now.strftime("%Y-%m-%d")
76
-
77
- out_zip = File.join('tmp','bundles',"bundle-merged-#{date_string}-#{version}.zip")
78
- FileUtils.remove_entry_secure out_zip if File.exists?(out_zip)
79
- Zip::ZipFile.open(out_zip, 'w') do |zipfile|
80
- path = File.join(tmpdir,'output')
81
- Dir[File.join(path,'**','**')].each do |file|
82
- zipfile.add(file.sub(path+'/',''),file)
83
- end
84
- end
85
-
86
- puts "wrote merged bundle to: #{out_zip}"
87
-
88
- ensure
89
- FileUtils.remove_entry_secure tmpdir
90
- end
91
-
92
-
93
- end
94
-
95
- end