quality-measure-engine 1.1.5 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +12 -0
- data/.travis.yml +16 -0
- data/Gemfile +5 -21
- data/Gemfile.lock +126 -0
- data/LICENSE.txt +13 -0
- data/README.md +23 -44
- data/Rakefile +6 -29
- data/lib/qme/bundle/bundle.rb +34 -0
- data/lib/qme/bundle/importer.rb +69 -0
- data/lib/qme/database_access.rb +7 -11
- data/lib/qme/map/map_reduce_builder.rb +4 -1
- data/lib/qme/map/map_reduce_executor.rb +55 -43
- data/lib/qme/map/measure_calculation_job.rb +24 -23
- data/lib/qme/quality_measure.rb +5 -5
- data/lib/qme/quality_report.rb +37 -19
- data/lib/qme/railtie.rb +7 -0
- data/lib/qme/tasks/bundle.rake +14 -0
- data/lib/qme/version.rb +3 -0
- data/lib/quality-measure-engine.rb +13 -25
- data/quality-measure-engine.gemspec +28 -0
- data/test/fixtures/bundles/just_measure_0002.zip +0 -0
- data/test/fixtures/delayed_backend_mongoid_jobs/queued_job.json +9 -0
- data/test/fixtures/measures/measure_metadata.json +52 -0
- data/test/fixtures/records/barry_berry.json +471 -0
- data/test/fixtures/records/billy_jones_ipp.json +78 -0
- data/test/fixtures/records/jane_jones_numerator.json +120 -0
- data/test/fixtures/records/jill_jones_denominator.json +78 -0
- data/test/simplecov_setup.rb +18 -0
- data/test/test_helper.rb +26 -0
- data/test/unit/qme/map/map_reduce_builder_test.rb +38 -0
- data/test/unit/qme/map/map_reduce_executor_test.rb +56 -0
- data/test/unit/qme/map/measure_calculation_job_test.rb +22 -0
- data/test/unit/qme/quality_measure_test.rb +14 -0
- data/{spec/qme/quality_report_spec.rb → test/unit/qme/quality_report_test.rb} +32 -20
- metadata +91 -115
- data/VERSION +0 -1
- data/js/map_reduce_utils.js +0 -173
- data/js/underscore_min.js +0 -25
- data/lib/qme/ext/record.rb +0 -43
- data/lib/qme/importer/entry.rb +0 -126
- data/lib/qme/importer/generic_importer.rb +0 -117
- data/lib/qme/importer/measure_properties_generator.rb +0 -39
- data/lib/qme/importer/property_matcher.rb +0 -110
- data/lib/qme/measure/database_loader.rb +0 -83
- data/lib/qme/measure/measure_loader.rb +0 -174
- data/lib/qme/measure/properties_builder.rb +0 -184
- data/lib/qme/measure/properties_converter.rb +0 -27
- data/lib/qme/randomizer/patient_randomization_job.rb +0 -47
- data/lib/qme/randomizer/patient_randomizer.rb +0 -250
- data/lib/qme/randomizer/random_patient_creator.rb +0 -47
- data/lib/qme_test.rb +0 -13
- data/lib/tasks/fixtures.rake +0 -91
- data/lib/tasks/measure.rake +0 -110
- data/lib/tasks/mongo.rake +0 -68
- data/lib/tasks/patient_random.rake +0 -45
- data/spec/qme/bundle_spec.rb +0 -37
- data/spec/qme/importer/generic_importer_spec.rb +0 -73
- data/spec/qme/importer/measure_properties_generator_spec.rb +0 -15
- data/spec/qme/importer/property_matcher_spec.rb +0 -174
- data/spec/qme/map/map_reduce_builder_spec.rb +0 -38
- data/spec/qme/map/measures_spec.rb +0 -38
- data/spec/qme/map/patient_mapper_spec.rb +0 -11
- data/spec/qme/measure_loader_spec.rb +0 -12
- data/spec/qme/properties_builder_spec.rb +0 -61
- data/spec/spec_helper.rb +0 -120
- data/spec/validate_measures_spec.rb +0 -21
@@ -1,83 +0,0 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__),'measure_loader')
|
2
|
-
|
3
|
-
module QME
|
4
|
-
module Database
|
5
|
-
|
6
|
-
# Utility class for working with JSON files and the database
|
7
|
-
class Loader
|
8
|
-
include DatabaseAccess
|
9
|
-
# Create a new Loader.
|
10
|
-
# @param [String] db_name the name of the database to use
|
11
|
-
def initialize(db_name = nil)
|
12
|
-
determine_connection_information(db_name)
|
13
|
-
end
|
14
|
-
|
15
|
-
# Load a measure from the filesystem and save it in the database.
|
16
|
-
# @param [String] measure_dir path to the directory containing a measure or measure collection document
|
17
|
-
# @param [String] collection_name name of the database collection to save the measure into.
|
18
|
-
# @return [Array] the stroed measures as an array of JSON measure hashes
|
19
|
-
def save_measure(measure_dir, collection_name)
|
20
|
-
measures = QME::Measure::Loader.load_measure(measure_dir)
|
21
|
-
measures.each do |measure|
|
22
|
-
save(collection_name, measure)
|
23
|
-
end
|
24
|
-
measures
|
25
|
-
end
|
26
|
-
|
27
|
-
# Save a JSON hash to the specified collection, creates the
|
28
|
-
# collection if it doesn't already exist.
|
29
|
-
# @param [String] collection_name name of the database collection
|
30
|
-
# @param [Hash] json the JSON hash to save in the database
|
31
|
-
def save(collection_name, json)
|
32
|
-
get_db[collection_name].save(json)
|
33
|
-
end
|
34
|
-
|
35
|
-
# Drop a database collection
|
36
|
-
# @param [String] collection_name name of the database collection
|
37
|
-
def drop_collection(collection_name)
|
38
|
-
get_db.drop_collection(collection_name)
|
39
|
-
end
|
40
|
-
|
41
|
-
def save_system_js_fn(name, fn)
|
42
|
-
get_db['system.js'].save(
|
43
|
-
{
|
44
|
-
"_id" => name,
|
45
|
-
"value" => BSON::Code.new(fn)
|
46
|
-
}
|
47
|
-
)
|
48
|
-
end
|
49
|
-
|
50
|
-
# Save a bundle to the db, this will save the bundle meta data, javascript extension functions and measures to
|
51
|
-
# the db in their respective locations
|
52
|
-
# @param [String] bundle_dir the bundle directory
|
53
|
-
# @param [String] bundle_collection the collection to save the bundle meta_data and extension functions to
|
54
|
-
# @param [String] measure_collection the collection to save the measures to, defaults to measures
|
55
|
-
def save_bundle(bundle_dir, measure_dir, bundle_collection='bundles', measure_collection = 'measures')
|
56
|
-
bundle = QME::Measure::Loader.load_bundle(bundle_dir, measure_dir)
|
57
|
-
bundle[:bundle_data][:measures] = []
|
58
|
-
b_id = save(bundle_collection,bundle[:bundle_data])
|
59
|
-
measures = bundle[:measures]
|
60
|
-
measures.each do |measure|
|
61
|
-
measure[:bundle] = b_id
|
62
|
-
m_id = save(measure_collection,measure)
|
63
|
-
bundle[:bundle_data][:measures] << m_id
|
64
|
-
end
|
65
|
-
save(bundle_collection,bundle[:bundle_data])
|
66
|
-
bundle[:extensions].each do |name, fn|
|
67
|
-
save_system_js_fn(name, fn)
|
68
|
-
end
|
69
|
-
bundle
|
70
|
-
end
|
71
|
-
|
72
|
-
|
73
|
-
def remove_bundle(bundle_id, bundle_collection = 'bundles', measure_collection = 'measures')
|
74
|
-
bundle = get_db[bundle_collection].find_one(bundle_id)
|
75
|
-
bundle['measures'].each do |measure|
|
76
|
-
mes = get_db[measure_collection].find_one(measure)
|
77
|
-
get_db[measure_collection].remove(mes)
|
78
|
-
end
|
79
|
-
get_db[bundle_collection].remove(bundle)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
@@ -1,174 +0,0 @@
|
|
1
|
-
gem 'rubyzip'
|
2
|
-
require 'json'
|
3
|
-
require 'zip/zip'
|
4
|
-
require 'zip/zipfilesystem'
|
5
|
-
require File.join(File.dirname(__FILE__),'properties_builder')
|
6
|
-
|
7
|
-
module QME
|
8
|
-
module Measure
|
9
|
-
|
10
|
-
# Utility class for working with JSON measure definition files
|
11
|
-
class Loader
|
12
|
-
|
13
|
-
# Load a measure from the filesystem
|
14
|
-
# @param [String] measure_dir path to the directory containing a measure or measure collection document
|
15
|
-
# @return [Array] the measures as an array of JSON measure hashes
|
16
|
-
def self.load_measure(measure_dir)
|
17
|
-
measures = []
|
18
|
-
Dir.glob(File.join(measure_dir, '*.col')).each do |collection_file|
|
19
|
-
component_dir = File.join(measure_dir, 'components')
|
20
|
-
load_collection(collection_file, component_dir).each do |measure|
|
21
|
-
measures << measure
|
22
|
-
end
|
23
|
-
end
|
24
|
-
Dir.glob(File.join(measure_dir, '*.json')).each do |measure_file|
|
25
|
-
files = Dir.glob(File.join(measure_dir,'*.js'))
|
26
|
-
if files.length!=1
|
27
|
-
raise "Unexpected number of map functions in #{measure_dir}, expected 1"
|
28
|
-
end
|
29
|
-
map_file = files[0]
|
30
|
-
measure = load_measure_file(measure_file, map_file)
|
31
|
-
measures << measure
|
32
|
-
end
|
33
|
-
measures
|
34
|
-
end
|
35
|
-
|
36
|
-
# Load a collection of measures definitions by processing a collection
|
37
|
-
# definition file.
|
38
|
-
# @param [String] collection_file path of the collection definition file
|
39
|
-
# @param [String] component_dir path to the directory that contains the measure components
|
40
|
-
# @return [Array] an array of measure definition JSON hashes
|
41
|
-
def self.load_collection(collection_file, component_dir)
|
42
|
-
collection_def = JSON.parse(File.read(collection_file))
|
43
|
-
measure_file = File.join(component_dir, collection_def['root'])
|
44
|
-
measures = []
|
45
|
-
collection_def['combinations'].each do |combination|
|
46
|
-
combination['metadata'] ||= {}
|
47
|
-
if !combination['map_fn']
|
48
|
-
raise "Missing map function in #{collection_file} (sub_id #{combination['metadata']['sub_id']})"
|
49
|
-
end
|
50
|
-
map_file = File.join(component_dir, combination['map_fn'])
|
51
|
-
measure = load_measure_file(measure_file, map_file)
|
52
|
-
|
53
|
-
# add inline metadata to top level of definition
|
54
|
-
combination['metadata'].each do |key, value|
|
55
|
-
measure[key] = value
|
56
|
-
end
|
57
|
-
if measure['properties'] && !measure['measure']
|
58
|
-
# load the measure properties if they weren't already added
|
59
|
-
measure['measure'] = get_measure_properties(measure)
|
60
|
-
end
|
61
|
-
|
62
|
-
# add inline measure-specific properties to definition
|
63
|
-
combination['measure'] ||= {}
|
64
|
-
measure['measure'] ||= {}
|
65
|
-
combination['measure'].each do |key, value|
|
66
|
-
measure['measure'][key] = value
|
67
|
-
end
|
68
|
-
['population', 'denominator', 'numerator', 'exclusions'].each do |component|
|
69
|
-
if combination[component]
|
70
|
-
measure[component] = load_json(component_dir, combination[component])
|
71
|
-
end
|
72
|
-
end
|
73
|
-
measures << measure
|
74
|
-
end
|
75
|
-
measures
|
76
|
-
end
|
77
|
-
|
78
|
-
# For ease of development and change mananegment, measure definition JSON
|
79
|
-
# files, property lists and JavaScript map functions are stored separately
|
80
|
-
# in the file system, this function combines the three components and
|
81
|
-
# returns the result.
|
82
|
-
# @param [String] measure_file path to the measure file
|
83
|
-
# @param [String] map_fn_file path to the map function file
|
84
|
-
# @return [Hash] a JSON hash of the measure with embedded map function.
|
85
|
-
def self.load_measure_file(measure_file, map_fn_file)
|
86
|
-
map_fn = File.read(map_fn_file)
|
87
|
-
measure = JSON.parse(File.read(measure_file))
|
88
|
-
measure['map_fn'] = map_fn
|
89
|
-
if measure['properties']
|
90
|
-
measure['measure'] = get_measure_properties(measure)
|
91
|
-
end
|
92
|
-
measure
|
93
|
-
end
|
94
|
-
|
95
|
-
# Load the measure properties from an external file, converting from JSONified XLS if
|
96
|
-
# necessary
|
97
|
-
def self.get_measure_properties(measure)
|
98
|
-
merged_props = {}
|
99
|
-
# normalize field value to be an array of file names
|
100
|
-
if !(measure['properties'].respond_to?(:each))
|
101
|
-
measure['properties'] = [measure['properties']]
|
102
|
-
end
|
103
|
-
measure['properties'].each do |file|
|
104
|
-
measure_props_file = File.join(ENV['MEASURE_PROPS'], file)
|
105
|
-
measure_props = JSON.parse(File.read(measure_props_file))
|
106
|
-
if measure_props['measure']
|
107
|
-
# copy measure properties over
|
108
|
-
merged_props.merge!(measure_props['measure'])
|
109
|
-
else
|
110
|
-
# convert JSONified XLS to properties format and add to measure
|
111
|
-
converted_props = QME::Measure::PropertiesBuilder.build_properties(measure_props, measure_props_file)['measure']
|
112
|
-
merged_props.merge!(converted_props)
|
113
|
-
end
|
114
|
-
end
|
115
|
-
merged_props
|
116
|
-
end
|
117
|
-
|
118
|
-
# Load a JSON file from the specified directory
|
119
|
-
# @param [String] dir_path path to the directory containing the JSON file
|
120
|
-
# @param [String] filename the JSON file
|
121
|
-
# @return [Hash] the parsed JSON hash
|
122
|
-
def self.load_json(dir_path, filename)
|
123
|
-
file_path = File.join(dir_path, filename)
|
124
|
-
JSON.parse(File.read(file_path))
|
125
|
-
end
|
126
|
-
|
127
|
-
|
128
|
-
#Load a bundle from a directory
|
129
|
-
# @param [String] bundle_path path to directory containing the bundle information
|
130
|
-
def self.load_bundle(bundle_path, measure_dir)
|
131
|
-
begin
|
132
|
-
bundle = {};
|
133
|
-
bundle_file = File.join(bundle_path,'bundle.json')
|
134
|
-
license_file = File.join(bundle_path, 'license.html')
|
135
|
-
|
136
|
-
bundle[:bundle_data] = File.exists?(bundle_file) ? JSON.parse(File.read(bundle_file)) : JSON.parse("{}")
|
137
|
-
bundle[:bundle_data][:license] = File.exists?(license_file) ? File.read(license_file) : ""
|
138
|
-
bundle[:extensions] = load_bundle_extensions(bundle_path)
|
139
|
-
bundle[:bundle_data][:extensions] = bundle[:extensions].keys
|
140
|
-
bundle[:measures] = []
|
141
|
-
Dir.glob(File.join(bundle_path, measure_dir, '*')).each do |measure_dir|
|
142
|
-
load_measure(measure_dir).each do |measure|
|
143
|
-
bundle[:measures] << measure
|
144
|
-
end
|
145
|
-
end
|
146
|
-
bundle
|
147
|
-
rescue Exception => e
|
148
|
-
print e.backtrace.join("\n")
|
149
|
-
throw e
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
|
154
|
-
# Load all of the extenson functions that will be available to map reduce functions from the bundle dir
|
155
|
-
# This will load from bundle_path/js and from ext directories in the individule measures directories
|
156
|
-
# like bundle_path/measures/0001/ext/
|
157
|
-
# @param [String] bundle_path the path to the bundle directory
|
158
|
-
# @return [Hash] name, function
|
159
|
-
def self.load_bundle_extensions(bundle_path)
|
160
|
-
extensions = {}
|
161
|
-
process_extension = lambda do |js_file|
|
162
|
-
fn_name = File.basename(js_file, ".js")
|
163
|
-
raw_js = File.read(js_file)
|
164
|
-
extensions[fn_name] = raw_js
|
165
|
-
end
|
166
|
-
Dir.glob(File.join(File.dirname(__FILE__), '../../..', 'js', '*.js')).each &process_extension
|
167
|
-
Dir.glob(File.join(bundle_path, 'js', '*.js')).each &process_extension
|
168
|
-
Dir.glob(File.join(bundle_path, 'measures', '*','ext', '*.js')).each &process_extension
|
169
|
-
extensions
|
170
|
-
end
|
171
|
-
|
172
|
-
end
|
173
|
-
end
|
174
|
-
end
|
@@ -1,184 +0,0 @@
|
|
1
|
-
module QME
|
2
|
-
module Measure
|
3
|
-
|
4
|
-
# Utility class for converting JSONified XLS file to measure properties
|
5
|
-
class PropertiesBuilder
|
6
|
-
|
7
|
-
GROUPING = 'GROUPING'
|
8
|
-
STANDARD_TAXONOMY = 'standard_taxonomy'
|
9
|
-
STANDARD_CODE_LIST = 'standard_code_list'
|
10
|
-
STANDARD_CONCEPT = 'standard_concept'
|
11
|
-
STANDARD_CONCEPT_ID = 'standard_concept_id'
|
12
|
-
STANDARD_CATEGORY = 'standard_category'
|
13
|
-
QDS_DATA_TYPE = 'QDS_data_type'
|
14
|
-
PROPERTIES_TO_IGNORE = %w(birthdate measurement_enddate measurement_period)
|
15
|
-
|
16
|
-
# Convert JSONified measure XLS to measure properties format
|
17
|
-
def self.build_properties(xls_json, xls_file)
|
18
|
-
xls_json = patch_properties(xls_json, xls_file)
|
19
|
-
|
20
|
-
# build groupings
|
21
|
-
grouped_json = build_groups(xls_json)
|
22
|
-
|
23
|
-
properties = {}
|
24
|
-
grouped_json.each do |value|
|
25
|
-
property_value = build_property(value)
|
26
|
-
if !PROPERTIES_TO_IGNORE.include?(property_value[STANDARD_CONCEPT])
|
27
|
-
properties[property_name(value)] = property_value
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
{'measure' => properties}
|
32
|
-
end
|
33
|
-
|
34
|
-
# Build the top-level JSON hash for a measure property
|
35
|
-
def self.build_property(parent)
|
36
|
-
property = {}
|
37
|
-
property[STANDARD_CONCEPT] = build_underscore_separated_name(parent[STANDARD_CONCEPT])
|
38
|
-
property[STANDARD_CATEGORY] = build_underscore_separated_name(parent[STANDARD_CATEGORY])
|
39
|
-
property[STANDARD_CONCEPT_ID] = parent[STANDARD_CONCEPT_ID]
|
40
|
-
property['qds_data_type'] = build_underscore_separated_name(parent[QDS_DATA_TYPE])
|
41
|
-
property['qds_id'] = parent['QDS_id']
|
42
|
-
property['type'] = 'array'
|
43
|
-
property['items'] = build_item_def(parent)
|
44
|
-
property['codes'] = build_code_list(parent)
|
45
|
-
property
|
46
|
-
end
|
47
|
-
|
48
|
-
# Build the definition of a property item
|
49
|
-
def self.build_item_def(property)
|
50
|
-
if property['value']
|
51
|
-
{
|
52
|
-
'type' => 'object',
|
53
|
-
'properties' => {
|
54
|
-
'value' => {
|
55
|
-
'description' => property['value']['description'],
|
56
|
-
'type' => property['value']['type']
|
57
|
-
},
|
58
|
-
'date' => {
|
59
|
-
'description' => property['value']['date_description'],
|
60
|
-
'type' => 'number',
|
61
|
-
'format' => 'utc-sec'
|
62
|
-
}
|
63
|
-
}
|
64
|
-
}
|
65
|
-
elsif property['range']
|
66
|
-
{
|
67
|
-
'type' => 'object',
|
68
|
-
'properties' => {
|
69
|
-
'start' => {
|
70
|
-
'description' => property['range']['start_description'],
|
71
|
-
'type' => 'number',
|
72
|
-
'format' => 'utc-sec'
|
73
|
-
},
|
74
|
-
'end' => {
|
75
|
-
'description' => property['range']['end_description'],
|
76
|
-
'type' => 'number',
|
77
|
-
'format' => 'utc-sec'
|
78
|
-
}
|
79
|
-
}
|
80
|
-
}
|
81
|
-
else
|
82
|
-
{
|
83
|
-
'type' => 'number',
|
84
|
-
'format' => 'utc-sec'
|
85
|
-
}
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
# Build the set of code lists for a property
|
90
|
-
def self.build_code_list(property)
|
91
|
-
if property['child_nodes']
|
92
|
-
property['child_nodes'].collect { |item| build_code_def(item) }
|
93
|
-
else
|
94
|
-
[build_code_def(property)]
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
# Build a single code list JSON hash for a property
|
99
|
-
def self.build_code_def(property)
|
100
|
-
{
|
101
|
-
'set' => property['standard_taxonomy'],
|
102
|
-
'version' => property['standard_taxonomy_version'],
|
103
|
-
STANDARD_CONCEPT => build_underscore_separated_name(property[STANDARD_CONCEPT]),
|
104
|
-
STANDARD_CONCEPT_ID => property[STANDARD_CONCEPT_ID],
|
105
|
-
'qds_id' => property['QDS_id'],
|
106
|
-
'values' => extract_code_values(property['standard_code_list'], property['standard_taxonomy'])
|
107
|
-
}
|
108
|
-
end
|
109
|
-
|
110
|
-
# Extract the code values from their string form into an array of strings, one per code
|
111
|
-
def self.extract_code_values(string_list, set)
|
112
|
-
return [] if string_list==nil || string_list.length==0
|
113
|
-
string_list.split(',').collect do |entry|
|
114
|
-
if set=='CPT' && entry.include?('-')
|
115
|
-
# special handling for ranges in CPT code sets, e.g. 10010-10015
|
116
|
-
eval(entry.strip.gsub('-','..')).to_a.collect { |i| i.to_s }
|
117
|
-
else
|
118
|
-
entry.strip
|
119
|
-
end
|
120
|
-
end.flatten
|
121
|
-
end
|
122
|
-
|
123
|
-
# Generate a property name from the standard concept and qds data type
|
124
|
-
def self.property_name(property)
|
125
|
-
build_underscore_separated_name(property[STANDARD_CONCEPT], property[QDS_DATA_TYPE])
|
126
|
-
end
|
127
|
-
|
128
|
-
# Break all the supplied strings into separate words and return the resulting list as a
|
129
|
-
# new string with each word separated with '_'
|
130
|
-
def self.build_underscore_separated_name(*components)
|
131
|
-
name = []
|
132
|
-
components.each do |component|
|
133
|
-
name.concat component.gsub(/\W/,' ').split.collect { |word| word.strip.downcase }
|
134
|
-
end
|
135
|
-
name.join '_'
|
136
|
-
end
|
137
|
-
|
138
|
-
# build an array of grouped properties
|
139
|
-
def self.build_groups(xls_json)
|
140
|
-
grouped_json = []
|
141
|
-
exclude_child_ids = []
|
142
|
-
|
143
|
-
# add grouped members
|
144
|
-
groups = xls_json.values.select { |value| value[STANDARD_TAXONOMY]!=nil && value[STANDARD_TAXONOMY].upcase==GROUPING }
|
145
|
-
groups.each do |group|
|
146
|
-
group_child_ids = group[STANDARD_CODE_LIST].split(',').collect { |id| id.strip }
|
147
|
-
children = xls_json.values.select do |child|
|
148
|
-
group_child_ids.include?(child[STANDARD_CONCEPT_ID].strip)
|
149
|
-
end
|
150
|
-
group['child_nodes'] = children
|
151
|
-
grouped_json << group
|
152
|
-
if group['group_type']==nil || group['group_type']!='abstract'
|
153
|
-
exclude_child_ids.concat group_child_ids
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
# add remaining non-grouped members
|
158
|
-
xls_json.values.each do |value|
|
159
|
-
if value[STANDARD_TAXONOMY]!=GROUPING && !exclude_child_ids.include?(value[STANDARD_CONCEPT_ID].strip)
|
160
|
-
grouped_json<<value
|
161
|
-
end
|
162
|
-
end
|
163
|
-
grouped_json
|
164
|
-
end
|
165
|
-
|
166
|
-
# Patch the supplied property_json hash with the contents of a patch file named
|
167
|
-
# "#{property_file}.patch" and return the result
|
168
|
-
def self.patch_properties(property_json, property_file)
|
169
|
-
patch_file = "#{property_file}.patch"
|
170
|
-
|
171
|
-
if File.exists? patch_file
|
172
|
-
patch_json = JSON.parse(File.read(patch_file))
|
173
|
-
|
174
|
-
property_json = property_json.merge(patch_json) do |key, old, new|
|
175
|
-
old.merge(new)
|
176
|
-
end
|
177
|
-
end
|
178
|
-
property_json
|
179
|
-
end
|
180
|
-
|
181
|
-
end
|
182
|
-
|
183
|
-
end
|
184
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
require 'roo'
|
2
|
-
|
3
|
-
module QME
|
4
|
-
module Measure
|
5
|
-
|
6
|
-
# Utility class for converting NQF XLS files to JSON
|
7
|
-
class Converter
|
8
|
-
|
9
|
-
#Convert an NQF XLS file to a hash
|
10
|
-
def self.from_xls(file)
|
11
|
-
xls =Excelx.new(file)
|
12
|
-
xls.default_sheet='Measure_QDS'
|
13
|
-
result = {}
|
14
|
-
(xls.header_line+1..xls.last_row).each do |row|
|
15
|
-
entry = {}
|
16
|
-
(xls.first_column..xls.last_column).each do |column|
|
17
|
-
entry[xls.cell(xls.header_line, column)] = xls.cell(row, column)
|
18
|
-
end
|
19
|
-
result[entry['QDS_id']] = entry
|
20
|
-
end
|
21
|
-
result
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
end
|
27
|
-
end
|