quality-measure-engine 1.1.5 → 2.0.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.
Files changed (66) hide show
  1. data/.gitignore +12 -0
  2. data/.travis.yml +16 -0
  3. data/Gemfile +5 -21
  4. data/Gemfile.lock +126 -0
  5. data/LICENSE.txt +13 -0
  6. data/README.md +23 -44
  7. data/Rakefile +6 -29
  8. data/lib/qme/bundle/bundle.rb +34 -0
  9. data/lib/qme/bundle/importer.rb +69 -0
  10. data/lib/qme/database_access.rb +7 -11
  11. data/lib/qme/map/map_reduce_builder.rb +4 -1
  12. data/lib/qme/map/map_reduce_executor.rb +55 -43
  13. data/lib/qme/map/measure_calculation_job.rb +24 -23
  14. data/lib/qme/quality_measure.rb +5 -5
  15. data/lib/qme/quality_report.rb +37 -19
  16. data/lib/qme/railtie.rb +7 -0
  17. data/lib/qme/tasks/bundle.rake +14 -0
  18. data/lib/qme/version.rb +3 -0
  19. data/lib/quality-measure-engine.rb +13 -25
  20. data/quality-measure-engine.gemspec +28 -0
  21. data/test/fixtures/bundles/just_measure_0002.zip +0 -0
  22. data/test/fixtures/delayed_backend_mongoid_jobs/queued_job.json +9 -0
  23. data/test/fixtures/measures/measure_metadata.json +52 -0
  24. data/test/fixtures/records/barry_berry.json +471 -0
  25. data/test/fixtures/records/billy_jones_ipp.json +78 -0
  26. data/test/fixtures/records/jane_jones_numerator.json +120 -0
  27. data/test/fixtures/records/jill_jones_denominator.json +78 -0
  28. data/test/simplecov_setup.rb +18 -0
  29. data/test/test_helper.rb +26 -0
  30. data/test/unit/qme/map/map_reduce_builder_test.rb +38 -0
  31. data/test/unit/qme/map/map_reduce_executor_test.rb +56 -0
  32. data/test/unit/qme/map/measure_calculation_job_test.rb +22 -0
  33. data/test/unit/qme/quality_measure_test.rb +14 -0
  34. data/{spec/qme/quality_report_spec.rb → test/unit/qme/quality_report_test.rb} +32 -20
  35. metadata +91 -115
  36. data/VERSION +0 -1
  37. data/js/map_reduce_utils.js +0 -173
  38. data/js/underscore_min.js +0 -25
  39. data/lib/qme/ext/record.rb +0 -43
  40. data/lib/qme/importer/entry.rb +0 -126
  41. data/lib/qme/importer/generic_importer.rb +0 -117
  42. data/lib/qme/importer/measure_properties_generator.rb +0 -39
  43. data/lib/qme/importer/property_matcher.rb +0 -110
  44. data/lib/qme/measure/database_loader.rb +0 -83
  45. data/lib/qme/measure/measure_loader.rb +0 -174
  46. data/lib/qme/measure/properties_builder.rb +0 -184
  47. data/lib/qme/measure/properties_converter.rb +0 -27
  48. data/lib/qme/randomizer/patient_randomization_job.rb +0 -47
  49. data/lib/qme/randomizer/patient_randomizer.rb +0 -250
  50. data/lib/qme/randomizer/random_patient_creator.rb +0 -47
  51. data/lib/qme_test.rb +0 -13
  52. data/lib/tasks/fixtures.rake +0 -91
  53. data/lib/tasks/measure.rake +0 -110
  54. data/lib/tasks/mongo.rake +0 -68
  55. data/lib/tasks/patient_random.rake +0 -45
  56. data/spec/qme/bundle_spec.rb +0 -37
  57. data/spec/qme/importer/generic_importer_spec.rb +0 -73
  58. data/spec/qme/importer/measure_properties_generator_spec.rb +0 -15
  59. data/spec/qme/importer/property_matcher_spec.rb +0 -174
  60. data/spec/qme/map/map_reduce_builder_spec.rb +0 -38
  61. data/spec/qme/map/measures_spec.rb +0 -38
  62. data/spec/qme/map/patient_mapper_spec.rb +0 -11
  63. data/spec/qme/measure_loader_spec.rb +0 -12
  64. data/spec/qme/properties_builder_spec.rb +0 -61
  65. data/spec/spec_helper.rb +0 -120
  66. data/spec/validate_measures_spec.rb +0 -21
@@ -0,0 +1,78 @@
1
+ {
2
+ "_id": "507eb5977042f9362c000001",
3
+ "birthdate": 1059723000,
4
+ "conditions": [
5
+ {
6
+ "codes": {
7
+ "ICD-9-CM": [
8
+ "034.0"
9
+ ],
10
+ "SNOMED-CT": [
11
+ "140004.0"
12
+ ]
13
+ },
14
+ "mood_code": "EVN",
15
+ "_type": "Condition",
16
+ "description": "Diagnosis, Active: pharyngitis (Code List: 2.16.840.1.113883.3.464.0001.369)",
17
+ "start_time": 1295067600,
18
+ "end_time": 1295067600,
19
+ "status_code": {
20
+ "SNOMED-CT": [
21
+ "55561003"
22
+ ],
23
+ "HL7 ActStatus": [
24
+ "active"
25
+ ]
26
+ }
27
+ }
28
+ ],
29
+ "description": "",
30
+ "description_category": "DENOM",
31
+ "elimination_population": null,
32
+ "elimination_reason": null,
33
+ "encounters": [
34
+ {
35
+ "codes": {
36
+ "CPT": [
37
+ "99201"
38
+ ],
39
+ "ICD-9-CM": [
40
+ "V70.0"
41
+ ]
42
+ },
43
+ "mood_code": "EVN",
44
+ "_type": "Encounter",
45
+ "description": "Encounter: Encounter ambulatory including pediatrics (Code List: 2.16.840.1.113883.3.464.0001.231)",
46
+ "start_time": 1295067600,
47
+ "end_time": 1295067600
48
+ }
49
+ ],
50
+ "expired": false,
51
+ "first": "Billy",
52
+ "gender": "M",
53
+ "last": "Jones",
54
+ "measure_period_end": 1325394000000,
55
+ "measure_period_start": 1293858000000,
56
+ "medications": [
57
+ {
58
+ "codes": {
59
+ "RxNorm": [
60
+ "197450"
61
+ ]
62
+ },
63
+ "mood_code": "EVN",
64
+ "_type": "Medication",
65
+ "description": "Medication, Active: pharyngitis antibiotics (Code List: 2.16.840.1.113883.3.464.0001.373)",
66
+ "start_time": 1295154000,
67
+ "end_time": 1295154000,
68
+ "status_code": {
69
+ "SNOMED-CT": [
70
+ "55561003"
71
+ ],
72
+ "HL7 ActStatus": [
73
+ "active"
74
+ ]
75
+ }
76
+ }
77
+ ]
78
+ }
@@ -0,0 +1,120 @@
1
+ {
2
+ "_id": "507ec2077042f9362c00001b",
3
+ "birthdate": 946715400,
4
+ "medical_record_number": "12345",
5
+ "conditions": [
6
+ {
7
+ "codes": {
8
+ "ICD-9-CM": [
9
+ "034.0"
10
+ ],
11
+ "SNOMED-CT": [
12
+ "140004.0"
13
+ ]
14
+ },
15
+ "mood_code": "EVN",
16
+ "_id": {
17
+ "$oid": "507ecc2b7042f9362c000061"
18
+ },
19
+ "_type": "Condition",
20
+ "description": "Diagnosis, Active: pharyngitis (Code List: 2.16.840.1.113883.3.464.0001.369)",
21
+ "start_time": 1293253200,
22
+ "end_time": 1293253200,
23
+ "status_code": {
24
+ "SNOMED-CT": [
25
+ "55561003"
26
+ ],
27
+ "HL7 ActStatus": [
28
+ "active"
29
+ ]
30
+ }
31
+ }
32
+ ],
33
+ "description": "",
34
+ "description_category": "NUMER",
35
+ "elimination_population": null,
36
+ "elimination_reason": null,
37
+ "encounters": [
38
+ {
39
+ "codes": {
40
+ "CPT": [
41
+ "99201"
42
+ ],
43
+ "ICD-9-CM": [
44
+ "V70.0"
45
+ ]
46
+ },
47
+ "mood_code": "EVN",
48
+ "_type": "Encounter",
49
+ "description": "Encounter: Encounter ambulatory including pediatrics (Code List: 2.16.840.1.113883.3.464.0001.231)",
50
+ "start_time": 1293253200,
51
+ "end_time": 1293253200
52
+ }
53
+ ],
54
+ "expired": false,
55
+ "first": "Jane",
56
+ "gender": "F",
57
+ "last": "Jones",
58
+ "measure_period_end": 1325307600000,
59
+ "measure_period_start": 1293858000000,
60
+ "medications": [
61
+ {
62
+ "codes": {
63
+ "RxNorm": [
64
+ "197450"
65
+ ]
66
+ },
67
+ "mood_code": "EVN",
68
+ "_type": "Medication",
69
+ "description": "Medication, Active: pharyngitis antibiotics (Code List: 2.16.840.1.113883.3.464.0001.373)",
70
+ "start_time": 1293339600,
71
+ "end_time": 1293339600,
72
+ "status_code": {
73
+ "SNOMED-CT": [
74
+ "55561003"
75
+ ],
76
+ "HL7 ActStatus": [
77
+ "active"
78
+ ]
79
+ }
80
+ }
81
+ ],
82
+ "vital_signs": [
83
+ {
84
+ "codes": {
85
+ "CPT": [
86
+ "87070"
87
+ ],
88
+ "LOINC": [
89
+ "11268-0"
90
+ ],
91
+ "SNOMED-CT": [
92
+ "89634005.0"
93
+ ]
94
+ },
95
+ "mood_code": "EVN",
96
+ "_type": "VitalSign",
97
+ "description": "Laboratory Test, Performed: Group A Streptococcus Test (Code List: 2.16.840.1.113883.3.464.0001.250)",
98
+ "start_time": 1293166800,
99
+ "end_time": 1293166800
100
+ },
101
+ {
102
+ "codes": {
103
+ "CPT": [
104
+ "87070"
105
+ ],
106
+ "LOINC": [
107
+ "11268-0"
108
+ ],
109
+ "SNOMED-CT": [
110
+ "89634005.0"
111
+ ]
112
+ },
113
+ "mood_code": "EVN",
114
+ "_type": "VitalSign",
115
+ "description": "Laboratory Test, Performed: Group A Streptococcus Test (Code List: 2.16.840.1.113883.3.464.0001.250)",
116
+ "start_time": 1293339600,
117
+ "end_time": 1293339600
118
+ }
119
+ ]
120
+ }
@@ -0,0 +1,78 @@
1
+ {
2
+ "_id": "507ec1407042f9362c000017",
3
+ "birthdate": 946715400,
4
+ "conditions": [
5
+ {
6
+ "codes": {
7
+ "ICD-9-CM": [
8
+ "034.0"
9
+ ],
10
+ "SNOMED-CT": [
11
+ "140004.0"
12
+ ]
13
+ },
14
+ "mood_code": "EVN",
15
+ "_type": "Condition",
16
+ "description": "Diagnosis, Active: pharyngitis (Code List: 2.16.840.1.113883.3.464.0001.369)",
17
+ "start_time": 1293858000,
18
+ "end_time": 1293858000,
19
+ "status_code": {
20
+ "SNOMED-CT": [
21
+ "55561003"
22
+ ],
23
+ "HL7 ActStatus": [
24
+ "active"
25
+ ]
26
+ }
27
+ }
28
+ ],
29
+ "description": "",
30
+ "description_category": "DENOM",
31
+ "elimination_population": null,
32
+ "elimination_reason": null,
33
+ "encounters": [
34
+ {
35
+ "codes": {
36
+ "CPT": [
37
+ "99201"
38
+ ],
39
+ "ICD-9-CM": [
40
+ "V70.0"
41
+ ]
42
+ },
43
+ "mood_code": "EVN",
44
+ "_type": "Encounter",
45
+ "description": "Encounter: Encounter ambulatory including pediatrics (Code List: 2.16.840.1.113883.3.464.0001.231)",
46
+ "start_time": 1293858000,
47
+ "end_time": 1293858000
48
+ }
49
+ ],
50
+ "expired": false,
51
+ "first": "Jill",
52
+ "gender": "F",
53
+ "last": "Jones",
54
+ "measure_period_end": 1325307600000,
55
+ "measure_period_start": 1293858000000,
56
+ "medications": [
57
+ {
58
+ "codes": {
59
+ "RxNorm": [
60
+ "197450"
61
+ ]
62
+ },
63
+ "mood_code": "EVN",
64
+ "_type": "Medication",
65
+ "description": "Medication, Active: pharyngitis antibiotics (Code List: 2.16.840.1.113883.3.464.0001.373)",
66
+ "start_time": 1293944400,
67
+ "end_time": 1293944400,
68
+ "status_code": {
69
+ "SNOMED-CT": [
70
+ "55561003"
71
+ ],
72
+ "HL7 ActStatus": [
73
+ "active"
74
+ ]
75
+ }
76
+ }
77
+ ]
78
+ }
@@ -0,0 +1,18 @@
1
+ require 'simplecov'
2
+ SimpleCov.command_name 'Unit Tests'
3
+ SimpleCov.start do
4
+ add_filter "test/"
5
+ add_group "Map Reduce", "lib/qme/map"
6
+ add_group "Bundles", "lib/qme/bundle"
7
+ end
8
+
9
+ class SimpleCov::Formatter::QualityFormatter
10
+ def format(result)
11
+ SimpleCov::Formatter::HTMLFormatter.new.format(result)
12
+ File.open("coverage/covered_percent", "w") do |f|
13
+ f.puts result.source_files.covered_percent.to_f
14
+ end
15
+ end
16
+ end
17
+
18
+ SimpleCov.formatter = SimpleCov::Formatter::QualityFormatter
@@ -0,0 +1,26 @@
1
+ require 'simplecov_setup'
2
+ require 'minitest/autorun'
3
+ require 'quality-measure-engine'
4
+
5
+ db_host = ENV['TEST_DB_HOST'] || 'localhost'
6
+
7
+ Mongoid.configure do |config|
8
+ config.sessions = { default: { hosts: [ "#{db_host}:27017" ], database: 'test' }}
9
+ end
10
+
11
+ class MiniTest::Unit::TestCase
12
+ # Add more helper methods to be used by all tests here...
13
+
14
+ def collection_fixtures(db, collection, *id_attributes)
15
+ db[collection].drop
16
+ Dir.glob(File.join(File.dirname(__FILE__), 'fixtures', collection, '*.json')).each do |json_fixture_file|
17
+ #puts "Loading #{json_fixture_file}"
18
+ fixture_json = JSON.parse(File.read(json_fixture_file))
19
+ id_attributes.each do |attr|
20
+ fixture_json[attr] = Moped::BSON::ObjectId.from_string(fixture_json[attr])
21
+ end
22
+
23
+ db[collection].insert(fixture_json)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,38 @@
1
+ require 'test_helper'
2
+
3
+ class MapReduceBuilderTest < MiniTest::Unit::TestCase
4
+ include QME::DatabaseAccess
5
+
6
+ def setup
7
+ raw_measure_json = File.read(File.join('test', 'fixtures', 'measures', 'measure_metadata.json'))
8
+ @measure_json = JSON.parse(raw_measure_json)
9
+ end
10
+
11
+ def test_extracting_measure_metadata
12
+ measure = QME::MapReduce::Builder.new(get_db(), @measure_json, 'effective_date' => Time.gm(2010, 9, 19).to_i)
13
+ assert_equal '0043', measure.id
14
+ end
15
+
16
+ def test_extracting_parameters
17
+ time = Time.gm(2010, 9, 19).to_i
18
+ measure = QME::MapReduce::Builder.new(get_db(), @measure_json, 'effective_date'=>time)
19
+ assert_equal 1, measure.params.size
20
+ assert measure.params.keys.include?('effective_date')
21
+ assert_equal time, measure.params['effective_date']
22
+ end
23
+
24
+ def test_raise_error_when_no_params_provided
25
+ rte = assert_raises(RuntimeError) do
26
+ QME::MapReduce::Builder.new(get_db(), @measure_json, {})
27
+ end
28
+ assert_equal "No value supplied for measure parameter: effective_date", rte.message
29
+ end
30
+
31
+ def test_context_building
32
+ vars = {'a'=>10, 'b'=>20}
33
+ context = QME::MapReduce::Builder::Context.new(get_db(), vars)
34
+ binding = context.get_binding
35
+ assert_equal 10, eval("a",binding)
36
+ assert_equal 20, eval("b",binding)
37
+ end
38
+ end
@@ -0,0 +1,56 @@
1
+ require 'test_helper'
2
+
3
+ class MapReduceExecutorTest < MiniTest::Unit::TestCase
4
+ include QME::DatabaseAccess
5
+
6
+ def setup
7
+ importer = QME::Bundle::Importer.new
8
+ importer.import(File.new('test/fixtures/bundles/just_measure_0002.zip'), true)
9
+
10
+ collection_fixtures(get_db(), 'records', '_id')
11
+ end
12
+
13
+ def test_map_records_into_measure_groups
14
+ executor = QME::MapReduce::Executor.new("2E679CD2-3FEC-4A75-A75A-61403E5EFEE8", nil,
15
+ 'effective_date' => Time.gm(2011, 1, 15).to_i)
16
+ executor.map_records_into_measure_groups
17
+
18
+ assert_equal 4, get_db['patient_cache'].find().count
19
+ assert_equal 3, get_db['patient_cache'].find('value.population' => 1).count
20
+ assert_equal 1, get_db['patient_cache'].find('value.population' => 0).count
21
+ assert_equal 2, get_db['patient_cache'].find('value.denominator' => 1).count
22
+ assert_equal 1, get_db['patient_cache'].find('value.numerator' => 1).count
23
+ end
24
+
25
+ def test_count_records_in_measure_groups
26
+ executor = QME::MapReduce::Executor.new("2E679CD2-3FEC-4A75-A75A-61403E5EFEE8", nil,
27
+ 'effective_date' => Time.gm(2011, 1, 15).to_i)
28
+ executor.map_records_into_measure_groups
29
+ executor.count_records_in_measure_groups
30
+ assert_equal 1, get_db['query_cache'].find().count
31
+ doc = get_db['query_cache'].find().first
32
+ assert_equal 3, doc['population']
33
+ assert_equal 2, doc['denominator']
34
+ assert_equal 1, doc['numerator']
35
+ end
36
+
37
+ def test_map_record_into_measure_groups
38
+ executor = QME::MapReduce::Executor.new("2E679CD2-3FEC-4A75-A75A-61403E5EFEE8", nil,
39
+ 'effective_date' => Time.gm(2011, 1, 15).to_i)
40
+ executor.map_record_into_measure_groups("12345")
41
+
42
+ assert_equal 1, get_db['patient_cache'].find().count
43
+ assert_equal 1, get_db['patient_cache'].find('value.population' => 1).count
44
+ assert_equal 0, get_db['patient_cache'].find('value.population' => 0).count
45
+ assert_equal 1, get_db['patient_cache'].find('value.denominator' => 1).count
46
+ assert_equal 1, get_db['patient_cache'].find('value.numerator' => 1).count
47
+ end
48
+
49
+ def test_get_patient_result
50
+ executor = QME::MapReduce::Executor.new("2E679CD2-3FEC-4A75-A75A-61403E5EFEE8", nil,
51
+ 'effective_date' => Time.gm(2011, 1, 15).to_i)
52
+ result = executor.get_patient_result("12345")
53
+ assert_equal 0, get_db['patient_cache'].find().count
54
+ assert result['numerator']
55
+ end
56
+ end
@@ -0,0 +1,22 @@
1
+ require 'test_helper'
2
+
3
+ class MapCalculationJobTest < MiniTest::Unit::TestCase
4
+ include QME::DatabaseAccess
5
+
6
+ def setup
7
+ importer = QME::Bundle::Importer.new
8
+ importer.import(File.new('test/fixtures/bundles/just_measure_0002.zip'), true)
9
+
10
+ collection_fixtures(get_db(), 'records', '_id')
11
+
12
+ Delayed::Worker.delay_jobs = false
13
+ end
14
+
15
+ def test_perform
16
+ options = {'measure_id' => "2E679CD2-3FEC-4A75-A75A-61403E5EFEE8",
17
+ 'effective_date' => Time.gm(2011, 1, 15).to_i}
18
+
19
+ job = Delayed::Job.enqueue(QME::MapReduce::MeasureCalculationJob.new(options))
20
+ assert job
21
+ end
22
+ end