quality-measure-engine 0.8.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/js/map_reduce_utils.js +2 -2
- data/lib/qme/importer/code_system_helper.rb +20 -3
- data/lib/qme/importer/entry.rb +41 -6
- data/lib/qme/importer/patient_importer.rb +68 -18
- data/lib/qme/importer/section_importer.rb +41 -7
- data/lib/qme/map/map_reduce_builder.rb +3 -0
- data/lib/qme/map/map_reduce_executor.rb +13 -7
- data/lib/qme/map/measure_calculation_job.rb +4 -3
- data/lib/qme/quality_report.rb +5 -3
- data/lib/qme/randomizer/patient_randomization_job.rb +48 -0
- data/lib/qme/randomizer/patient_randomizer.rb +19 -9
- data/lib/quality-measure-engine.rb +2 -1
- data/lib/tasks/patient_random.rake +1 -1
- metadata +98 -103
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
1.0.1
|
data/js/map_reduce_utils.js
CHANGED
@@ -116,9 +116,9 @@ function() {
|
|
116
116
|
root.map = function(record, population, denominator, numerator, exclusion) {
|
117
117
|
var value = {population: false, denominator: false, numerator: false,
|
118
118
|
exclusions: false, antinumerator: false, patient_id: record._id,
|
119
|
+
medical_record_id: record.patient_id,
|
119
120
|
first: record.first, last: record.last, gender: record.gender,
|
120
|
-
birthdate: record.birthdate};
|
121
|
-
var patient = record._id;
|
121
|
+
birthdate: record.birthdate, test_id: record.test_id};
|
122
122
|
if (population()) {
|
123
123
|
value.population = true;
|
124
124
|
if (denominator()) {
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module QME
|
2
2
|
module Importer
|
3
|
-
|
4
3
|
# General helpers for working with codes and code systems
|
5
4
|
class CodeSystemHelper
|
6
5
|
CODE_SYSTEMS = {
|
@@ -20,9 +19,27 @@ module QME
|
|
20
19
|
# @param [String] oid of a code system
|
21
20
|
# @return [String] the name of the code system as described in the measure definition JSON
|
22
21
|
def self.code_system_for(oid)
|
23
|
-
CODE_SYSTEMS[oid]
|
22
|
+
CODE_SYSTEMS[oid] || "Unknown"
|
23
|
+
end
|
24
|
+
|
25
|
+
@@oid_map = nil
|
26
|
+
|
27
|
+
# Returns the oid for a code system given a codesystem name
|
28
|
+
# @param [String] the name of the code system
|
29
|
+
# @return [String] the oid of the code system
|
30
|
+
def self.oid_for_code_system(codesystem)
|
31
|
+
if(!@@oid_map)
|
32
|
+
@@oid_map = {}
|
33
|
+
CODE_SYSTEMS.each_pair do |oid, codesystem|
|
34
|
+
# STDERR.puts "Adding #{oid}, #{codesystem}"
|
35
|
+
@@oid_map[codesystem] = oid
|
36
|
+
end
|
37
|
+
end
|
38
|
+
# STDERR.puts "@@oid_map[#{codesystem}] = #{@@oid_map[codesystem]}"
|
39
|
+
return @@oid_map[codesystem]
|
24
40
|
end
|
25
41
|
|
26
42
|
end
|
27
43
|
end
|
28
|
-
end
|
44
|
+
end
|
45
|
+
|
data/lib/qme/importer/entry.rb
CHANGED
@@ -2,19 +2,27 @@ module QME
|
|
2
2
|
module Importer
|
3
3
|
# Object that represents a CDA Entry (or act, observation, etc.)
|
4
4
|
class Entry
|
5
|
-
attr_accessor :start_time, :end_time, :time, :status
|
5
|
+
attr_accessor :start_time, :end_time, :time, :status, :description
|
6
6
|
attr_reader :codes, :value
|
7
7
|
|
8
|
-
def initialize
|
8
|
+
def initialize
|
9
9
|
@codes = {}
|
10
10
|
@value = {}
|
11
11
|
end
|
12
12
|
|
13
|
-
def
|
13
|
+
def self.from_event_hash(event)
|
14
14
|
entry = Entry.new
|
15
|
-
|
15
|
+
if event['code']
|
16
|
+
entry.add_code(event['code'], event['code_set'])
|
17
|
+
end
|
16
18
|
entry.time = event['time']
|
17
|
-
|
19
|
+
if event['value']
|
20
|
+
entry.set_value(event['value'], event['unit'])
|
21
|
+
end
|
22
|
+
if event['description']
|
23
|
+
entry.description = event['description']
|
24
|
+
end
|
25
|
+
|
18
26
|
entry
|
19
27
|
end
|
20
28
|
|
@@ -74,6 +82,33 @@ module QME
|
|
74
82
|
def usable?
|
75
83
|
(! @codes.empty?) && ((! @start_time.nil?) || (! @end_time.nil?) || (! @time.nil?))
|
76
84
|
end
|
85
|
+
|
86
|
+
# Creates a Hash for this Entry
|
87
|
+
# @return [Hash] a Hash representing the Entry
|
88
|
+
def to_hash
|
89
|
+
entry_hash = {}
|
90
|
+
entry_hash['codes'] = @codes
|
91
|
+
unless @value.empty?
|
92
|
+
entry_hash['value'] = @value
|
93
|
+
end
|
94
|
+
|
95
|
+
if is_date_range?
|
96
|
+
entry_hash['start_time'] = @start_time
|
97
|
+
entry_hash['end_time'] = @end_time
|
98
|
+
else
|
99
|
+
entry_hash['time'] = as_point_in_time
|
100
|
+
end
|
101
|
+
|
102
|
+
if @status
|
103
|
+
entry_hash['status'] = @status
|
104
|
+
end
|
105
|
+
|
106
|
+
if @description
|
107
|
+
entry_hash['description'] = @description
|
108
|
+
end
|
109
|
+
|
110
|
+
entry_hash
|
111
|
+
end
|
77
112
|
end
|
78
113
|
end
|
79
|
-
end
|
114
|
+
end
|
@@ -50,26 +50,51 @@ module QME
|
|
50
50
|
#
|
51
51
|
# Codes for immunizations are found in the substanceAdministration with the following relative XPath
|
52
52
|
# ./cda:consumable/cda:manufacturedProduct/cda:manufacturedMaterial/cda:code
|
53
|
-
def initialize
|
53
|
+
def initialize (check_usable = true)
|
54
54
|
@measure_importers = {}
|
55
55
|
@section_importers = {}
|
56
|
+
@id_map = {}
|
56
57
|
@section_importers[:encounters] = SectionImporter.new("//cda:section[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.127']/cda:entry/cda:encounter")
|
57
58
|
@section_importers[:procedures] = SectionImporter.new("//cda:procedure[cda:templateId/@root='2.16.840.1.113883.10.20.1.29']")
|
58
59
|
@section_importers[:results] = SectionImporter.new("//cda:observation[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.15.1'] | //cda:observation[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.15']")
|
59
60
|
@section_importers[:vital_signs] = SectionImporter.new("//cda:observation[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.14']")
|
60
61
|
@section_importers[:medications] = SectionImporter.new("//cda:section[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.112']/cda:entry/cda:substanceAdministration",
|
61
|
-
|
62
|
+
"./cda:consumable/cda:manufacturedProduct/cda:manufacturedMaterial/cda:code",
|
63
|
+
nil,
|
64
|
+
"./cda:consumable/cda:manufacturedProduct/cda:manufacturedMaterial/cda:code/cda:originalText/cda:reference[@value]")
|
62
65
|
@section_importers[:conditions] = SectionImporter.new("//cda:section[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.103']/cda:entry/cda:act/cda:entryRelationship/cda:observation",
|
63
|
-
|
64
|
-
|
66
|
+
"./cda:value",
|
67
|
+
"./cda:entryRelationship/cda:observation[cda:templateId/@root='2.16.840.1.1 13883.10.20.1.50']/cda:value",
|
68
|
+
"./cda:text/cda:reference[@value]")
|
65
69
|
@section_importers[:social_history] = SectionImporter.new("//cda:observation[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.19']")
|
66
70
|
@section_importers[:care_goals] = SectionImporter.new("//cda:observation[cda:templateId/@root='2.16.840.1.113883.10.20.1.25']")
|
67
71
|
@section_importers[:medical_equipment] = SectionImporter.new("//cda:section[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.128']/cda:entry/cda:supply",
|
68
|
-
|
72
|
+
"./cda:participant/cda:participantRole/cda:playingDevice/cda:code")
|
69
73
|
@section_importers[:allergies] = SectionImporter.new("//cda:observation[cda:templateId/@root='2.16.840.1.113883.10.20.1.18']",
|
70
|
-
|
71
|
-
@section_importers[:immunizations] = SectionImporter.new("//cda:
|
72
|
-
|
74
|
+
"./cda:participant/cda:participantRole/cda:playingEntity/cda:code")
|
75
|
+
@section_importers[:immunizations] = SectionImporter.new("//cda:section[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.117']/cda:entry/cda:substanceAdministration",
|
76
|
+
"./cda:consumable/cda:manufacturedProduct/cda:manufacturedMaterial/cda:code",
|
77
|
+
nil,
|
78
|
+
"./cda:consumable/cda:manufacturedProduct/cda:manufacturedMaterial/cda:code/cda:originalText/cda:reference[@value]" )
|
79
|
+
end
|
80
|
+
|
81
|
+
def build_id_map(doc)
|
82
|
+
id_map = {}
|
83
|
+
path = "//*[@ID]"
|
84
|
+
ids = doc.xpath(path)
|
85
|
+
ids.each do |id|
|
86
|
+
tag = id['ID']
|
87
|
+
value = id.content
|
88
|
+
id_map[tag] = value
|
89
|
+
end
|
90
|
+
return id_map
|
91
|
+
end
|
92
|
+
|
93
|
+
# @param [boolean] value for check_usable_entries...importer uses true, stats uses false
|
94
|
+
def check_usable(check_usable_entries)
|
95
|
+
@section_importers.each_pair do |section, importer|
|
96
|
+
importer.check_for_usable = check_usable_entries
|
97
|
+
end
|
73
98
|
end
|
74
99
|
|
75
100
|
# Parses a HITSP C32 document and returns a Hash of of the patient.
|
@@ -78,21 +103,23 @@ module QME
|
|
78
103
|
# will have the "cda" namespace registered to "urn:hl7-org:v3"
|
79
104
|
# @return [Hash] a representation of the patient that can be inserted into MongoDB
|
80
105
|
def parse_c32(doc)
|
81
|
-
|
82
|
-
|
83
|
-
get_demographics(
|
84
|
-
process_events(
|
106
|
+
c32_patient = {}
|
107
|
+
entries = create_c32_hash(doc)
|
108
|
+
get_demographics(c32_patient, doc)
|
109
|
+
process_events(c32_patient, entries)
|
85
110
|
end
|
86
111
|
|
87
|
-
# Parses a patient hash containing
|
112
|
+
# Parses a patient hash containing demographic and event information
|
88
113
|
#
|
89
114
|
# @param [Hash] patient_hash patient data
|
90
115
|
# @return [Hash] a representation of the patient that can be inserted into MongoDB
|
91
116
|
def parse_hash(patient_hash)
|
92
117
|
patient_record = {}
|
93
118
|
patient_record['first'] = patient_hash['first']
|
119
|
+
patient_record['patient_id'] = patient_hash['patient_id']
|
94
120
|
patient_record['last'] = patient_hash['last']
|
95
121
|
patient_record['gender'] = patient_hash['gender']
|
122
|
+
patient_record['patient_id'] = patient_hash['patient_id']
|
96
123
|
patient_record['birthdate'] = patient_hash['birthdate']
|
97
124
|
event_hash = {}
|
98
125
|
patient_hash['events'].each do |key, value|
|
@@ -101,11 +128,30 @@ module QME
|
|
101
128
|
process_events(patient_record, event_hash)
|
102
129
|
end
|
103
130
|
|
104
|
-
|
131
|
+
# Adds the entries and denormalized measure information to the patient_record.
|
132
|
+
# Each Entry will be converted to a Hash and stored in an Array under the appropriate
|
133
|
+
# section key, such as medications. Measure information is listed under the measures
|
134
|
+
# key which has a Hash value. The Hash has the measure id as a key, and the denormalized
|
135
|
+
# measure information as a value
|
136
|
+
#
|
137
|
+
# @param patient_record - Hash with basic patient demographic information
|
138
|
+
# @entries - Hash of entries with section names a keys and an Array of Entry values
|
139
|
+
def process_events(patient_record, entries)
|
105
140
|
patient_record['measures'] = {}
|
106
141
|
@measure_importers.each_pair do |measure_id, importer|
|
107
|
-
patient_record['measures'][measure_id] = importer.parse(
|
142
|
+
patient_record['measures'][measure_id] = importer.parse(entries)
|
108
143
|
end
|
144
|
+
|
145
|
+
entries.each_pair do |key, value|
|
146
|
+
patient_record[key] = value.map do |e|
|
147
|
+
if e.usable?
|
148
|
+
e.to_hash
|
149
|
+
else
|
150
|
+
nil
|
151
|
+
end
|
152
|
+
end.compact
|
153
|
+
end
|
154
|
+
|
109
155
|
patient_record
|
110
156
|
end
|
111
157
|
|
@@ -139,10 +185,12 @@ module QME
|
|
139
185
|
# @param [Nokogiri::XML::Document] doc It is expected that the root node of this document
|
140
186
|
# will have the "cda" namespace registered to "urn:hl7-org:v3"
|
141
187
|
# @return [Hash] a represnetation of the patient with symbols as keys for each section
|
142
|
-
def create_c32_hash(doc)
|
188
|
+
def create_c32_hash(doc, check_usable_entries = true)
|
143
189
|
c32_patient = {}
|
190
|
+
id_map = build_id_map(doc)
|
144
191
|
@section_importers.each_pair do |section, importer|
|
145
|
-
|
192
|
+
importer.check_for_usable = check_usable_entries
|
193
|
+
c32_patient[section] = importer.create_entries(doc,id_map)
|
146
194
|
end
|
147
195
|
c32_patient
|
148
196
|
end
|
@@ -160,7 +208,9 @@ module QME
|
|
160
208
|
patient['birthdate'] = HL7Helper.timestamp_to_integer(birthdate_in_hl7ts)
|
161
209
|
gender_node = doc.at_xpath('/cda:ClinicalDocument/cda:recordTarget/cda:patientRole/cda:patient/cda:administrativeGenderCode')
|
162
210
|
patient['gender'] = gender_node['code']
|
211
|
+
id_node = doc.at_xpath('/cda:ClinicalDocument/cda:recordTarget/cda:patientRole/cda:id')
|
212
|
+
patient['patient_id'] = id_node['extension']
|
163
213
|
end
|
164
214
|
end
|
165
215
|
end
|
166
|
-
end
|
216
|
+
end
|
@@ -3,27 +3,47 @@ module QME
|
|
3
3
|
# Class that can be used to create an importer for a section of a HITSP C32 document. It usually
|
4
4
|
# operates by selecting all CDA entries in a section and then creates entries for them.
|
5
5
|
class SectionImporter
|
6
|
-
|
6
|
+
attr_accessor :check_for_usable
|
7
7
|
# Creates a new SectionImporter
|
8
|
+
# @param [Hash] id_map A hash of all ID tags to values for the enclosing document. Used to look up descriptions.
|
8
9
|
# @param [String] entry_xpath An XPath expression that can be used to find the desired entries
|
9
10
|
# @param [String] code_xpath XPath expression to find the code element as a child of the desired CDA entry.
|
10
11
|
# Defaults to "./cda:code"
|
11
12
|
# @param [String] status_xpath XPath expression to find the status element as a child of the desired CDA
|
12
13
|
# entry. Defaults to nil. If not provided, a status will not be checked for since it is not applicable
|
13
14
|
# to all enrty types
|
14
|
-
def initialize(entry_xpath, code_xpath="./cda:code", status_xpath=nil)
|
15
|
+
def initialize(entry_xpath, code_xpath="./cda:code", status_xpath=nil, description_xpath="./cda:code/cda:originalText/cda:reference[@value] | ./cda:text/cda:reference[@value] ")
|
15
16
|
@entry_xpath = entry_xpath
|
16
17
|
@code_xpath = code_xpath
|
17
18
|
@status_xpath = status_xpath
|
19
|
+
@description_xpath = description_xpath
|
20
|
+
@check_for_usable = true # Pilot tools will set this to false
|
21
|
+
@id_map = {}
|
18
22
|
end
|
19
23
|
|
24
|
+
|
25
|
+
# @param [String] tag
|
26
|
+
# @return [String] text description of tag
|
27
|
+
def lookup_tag(tag)
|
28
|
+
value = @id_map[tag]
|
29
|
+
# Not sure why, but sometimes the reference is #<Reference> and the ID value is <Reference>, and
|
30
|
+
# sometimes it is #<Reference>. We look for both.
|
31
|
+
if !value and tag[0] == '#'
|
32
|
+
tag = tag[1,tag.length]
|
33
|
+
value = @id_map[tag]
|
34
|
+
end
|
35
|
+
|
36
|
+
value
|
37
|
+
end
|
38
|
+
|
20
39
|
# Traverses that HITSP C32 document passed in using XPath and creates an Array of Entry
|
21
|
-
# objects based on what it finds
|
40
|
+
# objects based on what it finds
|
22
41
|
# @param [Nokogiri::XML::Document] doc It is expected that the root node of this document
|
23
42
|
# will have the "cda" namespace registered to "urn:hl7-org:v3"
|
24
43
|
# measure definition
|
25
44
|
# @return [Array] will be a list of Entry objects
|
26
|
-
def create_entries(doc)
|
45
|
+
def create_entries(doc,id_map = {})
|
46
|
+
@id_map = id_map
|
27
47
|
entry_list = []
|
28
48
|
entry_elements = doc.xpath(@entry_xpath)
|
29
49
|
entry_elements.each do |entry_element|
|
@@ -34,7 +54,12 @@ module QME
|
|
34
54
|
if @status_xpath
|
35
55
|
extract_status(entry_element, entry)
|
36
56
|
end
|
37
|
-
if
|
57
|
+
if @description_xpath
|
58
|
+
extract_description(entry_element, entry, id_map)
|
59
|
+
end
|
60
|
+
if @check_for_usable
|
61
|
+
entry_list << entry if entry.usable?
|
62
|
+
else
|
38
63
|
entry_list << entry
|
39
64
|
end
|
40
65
|
end
|
@@ -51,12 +76,20 @@ module QME
|
|
51
76
|
entry.status = :active
|
52
77
|
when '73425007'
|
53
78
|
entry.status = :inactive
|
54
|
-
when '413322009'
|
79
|
+
when '413322009'
|
55
80
|
entry.status = :resolved
|
56
81
|
end
|
57
82
|
end
|
58
83
|
end
|
59
84
|
|
85
|
+
def extract_description(parent_element, entry, id_map)
|
86
|
+
code_elements = parent_element.xpath(@description_xpath)
|
87
|
+
code_elements.each do |code_element|
|
88
|
+
tag = code_element['value']
|
89
|
+
entry.description = lookup_tag(tag)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
60
93
|
def extract_codes(parent_element, entry)
|
61
94
|
code_elements = parent_element.xpath(@code_xpath)
|
62
95
|
code_elements.each do |code_element|
|
@@ -101,4 +134,5 @@ module QME
|
|
101
134
|
end
|
102
135
|
end
|
103
136
|
end
|
104
|
-
end
|
137
|
+
end
|
138
|
+
|
@@ -85,6 +85,9 @@ module QME
|
|
85
85
|
"function (key, value) {
|
86
86
|
var patient = value;
|
87
87
|
patient.measure_id = \"#{@measure_def['id']}\";\n"
|
88
|
+
if @params['test_id'] && @params['test_id'].class==BSON::ObjectId
|
89
|
+
reduce += " patient.test_id = new ObjectId(\"#{@params['test_id']}\");\n"
|
90
|
+
end
|
88
91
|
if @measure_def['sub_id']
|
89
92
|
reduce += " patient.sub_id = \"#{@measure_def['sub_id']}\";\n"
|
90
93
|
end
|
@@ -1,11 +1,17 @@
|
|
1
1
|
module QME
|
2
|
+
|
2
3
|
module MapReduce
|
3
4
|
|
4
5
|
# Computes the value of quality measures based on the current set of patient
|
5
6
|
# records in the database
|
6
7
|
class Executor
|
8
|
+
|
7
9
|
include DatabaseAccess
|
8
|
-
|
10
|
+
|
11
|
+
# Create a new Executor for a specific measure, effective date and patient population.
|
12
|
+
# @param [String] measure_id the measure identifier
|
13
|
+
# @param [String] sub_id the measure sub-identifier or null if the measure is single numerator
|
14
|
+
# @param [Hash] parameter_values a hash that may contain the following keys: 'effective_date' the measurement period end date, 'test_id' an identifier for a specific set of patients
|
9
15
|
def initialize(measure_id, sub_id, parameter_values)
|
10
16
|
@measure_id = measure_id
|
11
17
|
@sub_id = sub_id
|
@@ -20,18 +26,17 @@ module QME
|
|
20
26
|
def count_records_in_measure_groups
|
21
27
|
patient_cache = get_db.collection('patient_cache')
|
22
28
|
query = {'value.measure_id' => @measure_id, 'value.sub_id' => @sub_id,
|
23
|
-
'value.effective_date' => @parameter_values['effective_date']
|
29
|
+
'value.effective_date' => @parameter_values['effective_date'],
|
30
|
+
'value.test_id' => @parameter_values['test_id']}
|
24
31
|
result = {:measure_id => @measure_id, :sub_id => @sub_id,
|
25
|
-
:effective_date => @parameter_values['effective_date']
|
26
|
-
|
32
|
+
:effective_date => @parameter_values['effective_date'],
|
33
|
+
:test_id => @parameter_values['test_id']}
|
27
34
|
%w(population denominator numerator antinumerator exclusions).each do |measure_group|
|
28
35
|
patient_cache.find(query.merge("value.#{measure_group}" => true)) do |cursor|
|
29
36
|
result[measure_group] = cursor.count
|
30
37
|
end
|
31
38
|
end
|
32
|
-
|
33
39
|
get_db.collection("query_cache").save(result)
|
34
|
-
|
35
40
|
result
|
36
41
|
end
|
37
42
|
|
@@ -44,7 +49,8 @@ module QME
|
|
44
49
|
records = get_db.collection('records')
|
45
50
|
records.map_reduce(measure.map_function, "function(key, values){return values;}",
|
46
51
|
:out => {:reduce => 'patient_cache'},
|
47
|
-
:finalize => measure.finalize_function
|
52
|
+
:finalize => measure.finalize_function,
|
53
|
+
:query => {:test_id => @parameter_values['test_id']})
|
48
54
|
end
|
49
55
|
end
|
50
56
|
end
|
@@ -2,7 +2,7 @@ module QME
|
|
2
2
|
module MapReduce
|
3
3
|
# A Resque job that allows for measure calculation by a Resque worker. Can be created as follows:
|
4
4
|
#
|
5
|
-
# MapReduce::MeasureCalculationJob.create(:measure_id => '0221', :sub_id => 'a', :effective_date => 1291352400)
|
5
|
+
# MapReduce::MeasureCalculationJob.create(:measure_id => '0221', :sub_id => 'a', :effective_date => 1291352400, :test_id => xyzzy)
|
6
6
|
#
|
7
7
|
# This will return a uuid which can be used to check in on the status of a job. More details on this can be found
|
8
8
|
# at the {Resque Stats project page}[https://github.com/quirkey/resque-status].
|
@@ -15,11 +15,12 @@ module QME
|
|
15
15
|
# the report.
|
16
16
|
class MeasureCalculationJob < Resque::JobWithStatus
|
17
17
|
def perform
|
18
|
-
|
18
|
+
test_id = options['test_id'] ? BSON::ObjectId(options['test_id']) : nil
|
19
|
+
qr = QualityReport.new(options['measure_id'], options['sub_id'], 'effective_date' => options['effective_date'], 'test_id' => test_id)
|
19
20
|
if qr.calculated?
|
20
21
|
completed("#{options['measure_id']}#{options['sub_id']} has already been calculated")
|
21
22
|
else
|
22
|
-
map = QME::MapReduce::Executor.new(options['measure_id'], options['sub_id'], 'effective_date' => options['effective_date'])
|
23
|
+
map = QME::MapReduce::Executor.new(options['measure_id'], options['sub_id'], 'effective_date' => options['effective_date'], 'test_id' => test_id)
|
23
24
|
tick('Starting MapReduce')
|
24
25
|
map.map_records_into_measure_groups
|
25
26
|
tick('MapReduce complete')
|
data/lib/qme/quality_report.rb
CHANGED
@@ -19,7 +19,7 @@ module QME
|
|
19
19
|
# @param [String] sub_id value of the measure's sub_id field, may be nil
|
20
20
|
# for measures with only a single numerator and denominator
|
21
21
|
# @param [Hash] parameter_values slots in the measure definition that need to
|
22
|
-
# be filled in.
|
22
|
+
# be filled in and an optional test_id to identify a sub-population.
|
23
23
|
def initialize(measure_id, sub_id, parameter_values)
|
24
24
|
@measure_id = measure_id
|
25
25
|
@sub_id = sub_id
|
@@ -38,7 +38,8 @@ module QME
|
|
38
38
|
# @return a unique id for the measure calculation job
|
39
39
|
def calculate
|
40
40
|
MapReduce::MeasureCalculationJob.create(:measure_id => @measure_id, :sub_id => @sub_id,
|
41
|
-
:effective_date => @parameter_values['effective_date']
|
41
|
+
:effective_date => @parameter_values['effective_date'],
|
42
|
+
:test_id => @parameter_values['test_id'])
|
42
43
|
end
|
43
44
|
|
44
45
|
# Returns the status of a measure calculation job
|
@@ -54,7 +55,8 @@ module QME
|
|
54
55
|
def result
|
55
56
|
cache = get_db.collection("query_cache")
|
56
57
|
query = {:measure_id => @measure_id, :sub_id => @sub_id,
|
57
|
-
:effective_date => @parameter_values['effective_date']
|
58
|
+
:effective_date => @parameter_values['effective_date'],
|
59
|
+
:test_id => @parameter_values['test_id']}
|
58
60
|
cache.find_one(query)
|
59
61
|
end
|
60
62
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module QME
|
2
|
+
module Randomizer
|
3
|
+
# A Resque job that allows for generation of randomized patients by a Resque worker. Can be created as follows:
|
4
|
+
#
|
5
|
+
# QME::Randomizer::PatientRandomizationJob.create(:template_dir => '/xx/yy', :count => 100, [:test_id => ObjectId])
|
6
|
+
#
|
7
|
+
# This will return a uuid which can be used to check in on the status of a job. More details on this can be found
|
8
|
+
# at the {Resque Stats project page}[https://github.com/quirkey/resque-status].
|
9
|
+
#
|
10
|
+
class PatientRandomizationJob < Resque::JobWithStatus
|
11
|
+
def perform
|
12
|
+
test_id = options['test_id'] ? BSON::ObjectId(options['test_id']) : nil
|
13
|
+
template_dir = options['template_dir']
|
14
|
+
count = options['count']
|
15
|
+
|
16
|
+
tick('Reading templates')
|
17
|
+
templates = []
|
18
|
+
Dir.glob(File.join(template_dir, '*.json.erb')).each do |file|
|
19
|
+
templates << File.read(file)
|
20
|
+
end
|
21
|
+
|
22
|
+
tick('Initializing parser')
|
23
|
+
processed_measures = {}
|
24
|
+
QME::QualityMeasure.all.each_value do |measure_def|
|
25
|
+
measure_id = measure_def['id']
|
26
|
+
if !processed_measures[measure_id]
|
27
|
+
QME::Importer::PatientImporter.instance.add_measure(measure_id, QME::Importer::GenericImporter.new(measure_def))
|
28
|
+
processed_measures[measure_id]=true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
loader = QME::Database::Loader.new()
|
33
|
+
tick('Generating patients')
|
34
|
+
count.times do |i|
|
35
|
+
at(i, count, "Generating patient #{i} of #{count}")
|
36
|
+
template = templates[rand(templates.length)]
|
37
|
+
generator = QME::Randomizer::Patient.new(template)
|
38
|
+
json = JSON.parse(generator.get())
|
39
|
+
patient_record_hash = QME::Importer::PatientImporter.instance.parse_hash(json)
|
40
|
+
patient_record_hash['test_id'] = test_id
|
41
|
+
loader.save('records', patient_record_hash)
|
42
|
+
end
|
43
|
+
|
44
|
+
completed
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -1,13 +1,15 @@
|
|
1
1
|
require 'erb'
|
2
2
|
|
3
3
|
module QME
|
4
|
+
|
4
5
|
module Randomizer
|
5
|
-
|
6
|
+
|
6
7
|
# Provides functionality for randomizing patient records based on erb templates
|
7
8
|
class Patient
|
8
9
|
|
9
10
|
# Utility class used to supply a binding to Erb
|
10
11
|
class Context
|
12
|
+
|
11
13
|
# Create a new context
|
12
14
|
def initialize()
|
13
15
|
@genders = ['M', 'F']
|
@@ -19,26 +21,32 @@ module QME
|
|
19
21
|
# 500 most popular surnames according to US census 1990
|
20
22
|
@surnames = %w{Smith Johnson Williams Jones Brown Davis Miller Wilson Moore Taylor Anderson Thomas Jackson White Harris Martin Thompson Garcia Martinez Robinson Clark Rodriguez Lewis Lee Walker Hall Allen Young Hernandez King Wright Lopez Hill Scott Green Adams Baker Gonzalez Nelson Carter Mitchell Perez Roberts Turner Phillips Campbell Parker Evans Edwards Collins Stewart Sanchez Morris Rogers Reed Cook Morgan Bell Murphy Bailey Rivera Cooper Richardson Cox Howard Ward Torres Peterson Gray Ramirez James Watson Brooks Kelly Sanders Price Bennett Wood Barnes Ross Henderson Coleman Jenkins Perry Powell Long Patterson Hughes Flores Washington Butler Simmons Foster Gonzales Bryant Alexander Russell Griffin Diaz Hayes Myers Ford Hamilton Graham Sullivan Wallace Woods Cole West Jordan Owens Reynolds Fisher Ellis Harrison Gibson Mcdonald Cruz Marshall Ortiz Gomez Murray Freeman Wells Webb Simpson Stevens Tucker Porter Hunter Hicks Crawford Henry Boyd Mason Morales Kennedy Warren Dixon Ramos Reyes Burns Gordon Shaw Holmes Rice Robertson Hunt Black Daniels Palmer Mills Nichols Grant Knight Ferguson Rose Stone Hawkins Dunn Perkins Hudson Spencer Gardner Stephens Payne Pierce Berry Matthews Arnold Wagner Willis Ray Watkins Olson Carroll Duncan Snyder Hart Cunningham Bradley Lane Andrews Ruiz Harper Fox Riley Armstrong Carpenter Weaver Greene Lawrence Elliott Chavez Sims Austin Peters Kelley Franklin Lawson Fields Gutierrez Ryan Schmidt Carr Vasquez Castillo Wheeler Chapman Oliver Montgomery Richards Williamson Johnston Banks Meyer Bishop Mccoy Howell Alvarez Morrison Hansen Fernandez Garza Harvey Little Burton Stanley Nguyen George Jacobs Reid Kim Fuller Lynch Dean Gilbert Garrett Romero Welch Larson Frazier Burke Hanson Day Mendoza Moreno Bowman Medina Fowler Brewer Hoffman Carlson Silva Pearson Holland Douglas Fleming Jensen Vargas Byrd Davidson Hopkins May Terry Herrera Wade Soto Walters Curtis Neal Caldwell Lowe Jennings Barnett Graves Jimenez Horton Shelton Barrett Obrien Castro Sutton Gregory Mckinney Lucas Miles Craig Rodriquez Chambers Holt Lambert Fletcher Watts Bates Hale Rhodes Pena Beck Newman Haynes Mcdaniel Mendez Bush Vaughn Parks Dawson Santiago Norris Hardy Love Steele Curry Powers Schultz Barker Guzman Page Munoz Ball Keller Chandler Weber Leonard Walsh Lyons Ramsey Wolfe Schneider Mullins Benson Sharp Bowen Daniel Barber Cummings Hines Baldwin Griffith Valdez Hubbard Salazar Reeves Warner Stevenson Burgess Santos Tate Cross Garner Mann Mack Moss Thornton Dennis Mcgee Farmer Delgado Aguilar Vega Glover Manning Cohen Harmon Rodgers Robbins Newton Todd Blair Higgins Ingram Reese Cannon Strickland Townsend Potter Goodwin Walton Rowe Hampton Ortega Patton Swanson Joseph Francis Goodman Maldonado Yates Becker Erickson Hodges Rios Conner Adkins Webster Norman Malone Hammond Flowers Cobb Moody Quinn Blake Maxwell Pope Floyd Osborne Paul Mccarthy Guerrero Lindsey Estrada Sandoval Gibbs Tyler Gross Fitzgerald Stokes Doyle Sherman Saunders Wise Colon Gill Alvarado Greer Padilla Simon Waters Nunez Ballard Schwartz Mcbride Houston Christensen Klein Pratt Briggs Parsons Mclaughlin Zimmerman French Buchanan Moran Copeland Roy Pittman Brady Mccormick Holloway Brock Poole Frank Logan Owen Bass Marsh Drake Wong Jefferson Park Morton Abbott Sparks Patrick Norton Huff Clayton Massey Lloyd Figueroa Carson Bowers Roberson Barton Tran Lamb Harrington Casey Boone Cortez Clarke Mathis Singleton Wilkins Cain Bryan Underwood Hogan Mckenzie Collier Luna Phelps Mcguire Allison Bridges Wilkerson Nash Summers Atkins}
|
21
23
|
end
|
22
|
-
|
24
|
+
|
23
25
|
# Pick a gender at random
|
24
26
|
# @return 'M' or 'F'
|
25
27
|
def gender
|
26
28
|
@genders[rand(@genders.length)]
|
27
29
|
end
|
28
|
-
|
30
|
+
|
29
31
|
# Pick a forename at random appropriate for the supplied gender
|
30
32
|
# @param [String] gender the gender 'M' or 'F'
|
31
33
|
# @return a suitable forename
|
32
34
|
def forename(gender)
|
33
35
|
@forenames[gender][rand(@forenames[gender].length)]
|
34
36
|
end
|
35
|
-
|
37
|
+
|
36
38
|
# Pick a surname at random
|
37
39
|
# @return a surname
|
38
40
|
def surname
|
39
41
|
@surnames[rand(@surnames.length)]
|
40
42
|
end
|
41
|
-
|
43
|
+
|
44
|
+
# Pick a patient id, which will be a 10 character string, limited to numeric
|
45
|
+
# values for the string
|
46
|
+
def patient_id
|
47
|
+
(0...10).map{ ('0'..'9').to_a[rand(10)] }.join.to_s
|
48
|
+
end
|
49
|
+
|
42
50
|
# Return a set of randomly selected numbers between two bounds
|
43
51
|
# @param [int] min the lower inclusive bound
|
44
52
|
# @param [int] max the upper inclusive bound
|
@@ -53,7 +61,7 @@ module QME
|
|
53
61
|
end
|
54
62
|
result.to_json
|
55
63
|
end
|
56
|
-
|
64
|
+
|
57
65
|
# Return a randomly selected number between two bounds
|
58
66
|
# @param [int] min the lower inclusive bound
|
59
67
|
# @param [int] max the upper inclusive bound
|
@@ -62,14 +70,14 @@ module QME
|
|
62
70
|
span = max.to_i - min.to_i + 1
|
63
71
|
min.to_i+rand(span)
|
64
72
|
end
|
65
|
-
|
73
|
+
|
66
74
|
# Pick true or false according to the supplied probability
|
67
75
|
# @param [int] probability the probability of getting true as a percentage
|
68
76
|
# @return [boolean] true or false
|
69
77
|
def percent(probability)
|
70
78
|
return rand(100)<probability
|
71
79
|
end
|
72
|
-
|
80
|
+
|
73
81
|
# Get a binding that for the current instance
|
74
82
|
# @return [Binding]
|
75
83
|
def get_binding
|
@@ -82,7 +90,7 @@ module QME
|
|
82
90
|
def initialize(patient)
|
83
91
|
@template = ERB.new(patient)
|
84
92
|
end
|
85
|
-
|
93
|
+
|
86
94
|
# Get a randomized record based on the stored template
|
87
95
|
# @return [String] a randomized patient
|
88
96
|
def get
|
@@ -91,5 +99,7 @@ module QME
|
|
91
99
|
end
|
92
100
|
|
93
101
|
end
|
102
|
+
|
94
103
|
end
|
104
|
+
|
95
105
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require "bundler/setup"
|
2
2
|
|
3
3
|
require 'resque/job_with_status'
|
4
4
|
|
@@ -12,6 +12,7 @@ require_relative 'qme/map/measure_calculation_job'
|
|
12
12
|
require_relative 'qme/quality_report'
|
13
13
|
|
14
14
|
require_relative 'qme/randomizer/patient_randomizer'
|
15
|
+
require_relative 'qme/randomizer/patient_randomization_job'
|
15
16
|
|
16
17
|
require 'singleton'
|
17
18
|
|
@@ -12,7 +12,7 @@ namespace :patient do
|
|
12
12
|
|
13
13
|
desc 'Generate n (default 10) random patient records and save them in the database'
|
14
14
|
task :random, [:n] => ['mongo:drop_records'] do |t, args|
|
15
|
-
n = args.n.to_i>0 ? args.n.to_i :
|
15
|
+
n = args.n.to_i>0 ? args.n.to_i : 10
|
16
16
|
|
17
17
|
templates = []
|
18
18
|
Dir.glob(File.join(patient_template_dir, '*.json.erb')).each do |file|
|
metadata
CHANGED
@@ -1,161 +1,158 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: quality-measure-engine
|
3
|
-
version: !ruby/object:Gem::Version
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
4
5
|
prerelease:
|
5
|
-
version: 0.8.0
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
7
|
+
authors:
|
8
8
|
- Marc Hadley
|
9
9
|
- Andy Gregorowicz
|
10
10
|
- Rob Dingwell
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
|
15
|
-
date: 2011-05-09 00:00:00 -04:00
|
14
|
+
date: 2011-08-04 00:00:00.000000000 -04:00
|
16
15
|
default_executable:
|
17
|
-
dependencies:
|
18
|
-
- !ruby/object:Gem::Dependency
|
16
|
+
dependencies:
|
17
|
+
- !ruby/object:Gem::Dependency
|
19
18
|
name: mongo
|
20
|
-
|
21
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
requirement: &2152409780 !ruby/object:Gem::Requirement
|
22
20
|
none: false
|
23
|
-
requirements:
|
21
|
+
requirements:
|
24
22
|
- - ~>
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version:
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '1.3'
|
27
25
|
type: :runtime
|
28
|
-
version_requirements: *id001
|
29
|
-
- !ruby/object:Gem::Dependency
|
30
|
-
name: rubyzip
|
31
26
|
prerelease: false
|
32
|
-
|
27
|
+
version_requirements: *2152409780
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rubyzip
|
30
|
+
requirement: &2152409300 !ruby/object:Gem::Requirement
|
33
31
|
none: false
|
34
|
-
requirements:
|
32
|
+
requirements:
|
35
33
|
- - ~>
|
36
|
-
- !ruby/object:Gem::Version
|
34
|
+
- !ruby/object:Gem::Version
|
37
35
|
version: 0.9.4
|
38
36
|
type: :runtime
|
39
|
-
version_requirements: *id002
|
40
|
-
- !ruby/object:Gem::Dependency
|
41
|
-
name: nokogiri
|
42
37
|
prerelease: false
|
43
|
-
|
38
|
+
version_requirements: *2152409300
|
39
|
+
- !ruby/object:Gem::Dependency
|
40
|
+
name: nokogiri
|
41
|
+
requirement: &2152408840 !ruby/object:Gem::Requirement
|
44
42
|
none: false
|
45
|
-
requirements:
|
43
|
+
requirements:
|
46
44
|
- - ~>
|
47
|
-
- !ruby/object:Gem::Version
|
45
|
+
- !ruby/object:Gem::Version
|
48
46
|
version: 1.4.4
|
49
47
|
type: :runtime
|
50
|
-
version_requirements: *id003
|
51
|
-
- !ruby/object:Gem::Dependency
|
52
|
-
name: resque
|
53
48
|
prerelease: false
|
54
|
-
|
49
|
+
version_requirements: *2152408840
|
50
|
+
- !ruby/object:Gem::Dependency
|
51
|
+
name: resque
|
52
|
+
requirement: &2152408380 !ruby/object:Gem::Requirement
|
55
53
|
none: false
|
56
|
-
requirements:
|
54
|
+
requirements:
|
57
55
|
- - ~>
|
58
|
-
- !ruby/object:Gem::Version
|
56
|
+
- !ruby/object:Gem::Version
|
59
57
|
version: 1.15.0
|
60
58
|
type: :runtime
|
61
|
-
version_requirements: *id004
|
62
|
-
- !ruby/object:Gem::Dependency
|
63
|
-
name: resque-status
|
64
59
|
prerelease: false
|
65
|
-
|
60
|
+
version_requirements: *2152408380
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: resque-status
|
63
|
+
requirement: &2152407920 !ruby/object:Gem::Requirement
|
66
64
|
none: false
|
67
|
-
requirements:
|
65
|
+
requirements:
|
68
66
|
- - ~>
|
69
|
-
- !ruby/object:Gem::Version
|
67
|
+
- !ruby/object:Gem::Version
|
70
68
|
version: 0.2.3
|
71
69
|
type: :runtime
|
72
|
-
version_requirements: *id005
|
73
|
-
- !ruby/object:Gem::Dependency
|
74
|
-
name: jsonschema
|
75
70
|
prerelease: false
|
76
|
-
|
71
|
+
version_requirements: *2152407920
|
72
|
+
- !ruby/object:Gem::Dependency
|
73
|
+
name: jsonschema
|
74
|
+
requirement: &2152407460 !ruby/object:Gem::Requirement
|
77
75
|
none: false
|
78
|
-
requirements:
|
76
|
+
requirements:
|
79
77
|
- - ~>
|
80
|
-
- !ruby/object:Gem::Version
|
78
|
+
- !ruby/object:Gem::Version
|
81
79
|
version: 2.0.0
|
82
80
|
type: :development
|
83
|
-
version_requirements: *id006
|
84
|
-
- !ruby/object:Gem::Dependency
|
85
|
-
name: rspec
|
86
81
|
prerelease: false
|
87
|
-
|
82
|
+
version_requirements: *2152407460
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: &2152407000 !ruby/object:Gem::Requirement
|
88
86
|
none: false
|
89
|
-
requirements:
|
87
|
+
requirements:
|
90
88
|
- - ~>
|
91
|
-
- !ruby/object:Gem::Version
|
89
|
+
- !ruby/object:Gem::Version
|
92
90
|
version: 2.5.0
|
93
91
|
type: :development
|
94
|
-
version_requirements: *id007
|
95
|
-
- !ruby/object:Gem::Dependency
|
96
|
-
name: awesome_print
|
97
92
|
prerelease: false
|
98
|
-
|
93
|
+
version_requirements: *2152407000
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: awesome_print
|
96
|
+
requirement: &2152406540 !ruby/object:Gem::Requirement
|
99
97
|
none: false
|
100
|
-
requirements:
|
98
|
+
requirements:
|
101
99
|
- - ~>
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version:
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0.3'
|
104
102
|
type: :development
|
105
|
-
version_requirements: *id008
|
106
|
-
- !ruby/object:Gem::Dependency
|
107
|
-
name: roo
|
108
103
|
prerelease: false
|
109
|
-
|
104
|
+
version_requirements: *2152406540
|
105
|
+
- !ruby/object:Gem::Dependency
|
106
|
+
name: roo
|
107
|
+
requirement: &2152406080 !ruby/object:Gem::Requirement
|
110
108
|
none: false
|
111
|
-
requirements:
|
109
|
+
requirements:
|
112
110
|
- - ~>
|
113
|
-
- !ruby/object:Gem::Version
|
111
|
+
- !ruby/object:Gem::Version
|
114
112
|
version: 1.9.3
|
115
113
|
type: :development
|
116
|
-
version_requirements: *id009
|
117
|
-
- !ruby/object:Gem::Dependency
|
118
|
-
name: builder
|
119
114
|
prerelease: false
|
120
|
-
|
115
|
+
version_requirements: *2152406080
|
116
|
+
- !ruby/object:Gem::Dependency
|
117
|
+
name: builder
|
118
|
+
requirement: &2152405620 !ruby/object:Gem::Requirement
|
121
119
|
none: false
|
122
|
-
requirements:
|
120
|
+
requirements:
|
123
121
|
- - ~>
|
124
|
-
- !ruby/object:Gem::Version
|
122
|
+
- !ruby/object:Gem::Version
|
125
123
|
version: 3.0.0
|
126
124
|
type: :development
|
127
|
-
version_requirements: *id010
|
128
|
-
- !ruby/object:Gem::Dependency
|
129
|
-
name: spreadsheet
|
130
125
|
prerelease: false
|
131
|
-
|
126
|
+
version_requirements: *2152405620
|
127
|
+
- !ruby/object:Gem::Dependency
|
128
|
+
name: spreadsheet
|
129
|
+
requirement: &2152405160 !ruby/object:Gem::Requirement
|
132
130
|
none: false
|
133
|
-
requirements:
|
131
|
+
requirements:
|
134
132
|
- - ~>
|
135
|
-
- !ruby/object:Gem::Version
|
133
|
+
- !ruby/object:Gem::Version
|
136
134
|
version: 0.6.5.2
|
137
135
|
type: :development
|
138
|
-
version_requirements: *id011
|
139
|
-
- !ruby/object:Gem::Dependency
|
140
|
-
name: google-spreadsheet-ruby
|
141
136
|
prerelease: false
|
142
|
-
|
137
|
+
version_requirements: *2152405160
|
138
|
+
- !ruby/object:Gem::Dependency
|
139
|
+
name: google-spreadsheet-ruby
|
140
|
+
requirement: &2152404700 !ruby/object:Gem::Requirement
|
143
141
|
none: false
|
144
|
-
requirements:
|
142
|
+
requirements:
|
145
143
|
- - ~>
|
146
|
-
- !ruby/object:Gem::Version
|
144
|
+
- !ruby/object:Gem::Version
|
147
145
|
version: 0.1.2
|
148
146
|
type: :development
|
149
|
-
|
150
|
-
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: *2152404700
|
149
|
+
description: A library for extracting quality measure information from HITSP C32's
|
150
|
+
and ASTM CCR's
|
151
151
|
email: talk@projectpophealth.org
|
152
152
|
executables: []
|
153
|
-
|
154
153
|
extensions: []
|
155
|
-
|
156
154
|
extra_rdoc_files: []
|
157
|
-
|
158
|
-
files:
|
155
|
+
files:
|
159
156
|
- lib/qme/database_access.rb
|
160
157
|
- lib/qme/importer/code_system_helper.rb
|
161
158
|
- lib/qme/importer/entry.rb
|
@@ -173,6 +170,7 @@ files:
|
|
173
170
|
- lib/qme/measure/properties_converter.rb
|
174
171
|
- lib/qme/quality_measure.rb
|
175
172
|
- lib/qme/quality_report.rb
|
173
|
+
- lib/qme/randomizer/patient_randomization_job.rb
|
176
174
|
- lib/qme/randomizer/patient_randomizer.rb
|
177
175
|
- lib/qme_test.rb
|
178
176
|
- lib/quality-measure-engine.rb
|
@@ -189,30 +187,27 @@ files:
|
|
189
187
|
has_rdoc: true
|
190
188
|
homepage: http://github.com/pophealth/quality-measure-engine
|
191
189
|
licenses: []
|
192
|
-
|
193
190
|
post_install_message:
|
194
191
|
rdoc_options: []
|
195
|
-
|
196
|
-
require_paths:
|
192
|
+
require_paths:
|
197
193
|
- lib
|
198
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
194
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
199
195
|
none: false
|
200
|
-
requirements:
|
201
|
-
- -
|
202
|
-
- !ruby/object:Gem::Version
|
203
|
-
version:
|
204
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
196
|
+
requirements:
|
197
|
+
- - ! '>='
|
198
|
+
- !ruby/object:Gem::Version
|
199
|
+
version: '0'
|
200
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
205
201
|
none: false
|
206
|
-
requirements:
|
207
|
-
- -
|
208
|
-
- !ruby/object:Gem::Version
|
209
|
-
version:
|
202
|
+
requirements:
|
203
|
+
- - ! '>='
|
204
|
+
- !ruby/object:Gem::Version
|
205
|
+
version: '0'
|
210
206
|
requirements: []
|
211
|
-
|
212
207
|
rubyforge_project:
|
213
|
-
rubygems_version: 1.
|
208
|
+
rubygems_version: 1.6.2
|
214
209
|
signing_key:
|
215
210
|
specification_version: 3
|
216
|
-
summary: A library for extracting quality measure information from HITSP C32's and
|
211
|
+
summary: A library for extracting quality measure information from HITSP C32's and
|
212
|
+
ASTM CCR's
|
217
213
|
test_files: []
|
218
|
-
|