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.
- 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
|