openstudio_measure_tester 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +25 -0
- data/.rspec +3 -0
- data/.rubocop.yml +5 -0
- data/.travis.yml +15 -0
- data/Gemfile +8 -0
- data/LICENSE.md +13 -0
- data/README.md +61 -0
- data/Rakefile +11 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/dashboard/css/bootstrap-grid.css +2050 -0
- data/dashboard/css/bootstrap-grid.min.css +7 -0
- data/dashboard/css/bootstrap-reboot.css +330 -0
- data/dashboard/css/bootstrap.css +8975 -0
- data/dashboard/css/bootstrap.min.css +7 -0
- data/dashboard/css/dashboard.css +191 -0
- data/dashboard/index.html +558 -0
- data/dashboard/js/bootstrap.js +3894 -0
- data/dashboard/js/bootstrap.min.js +7 -0
- data/dashboard/js/jquery-3.3.1.min.js +2 -0
- data/lib/openstudio_measure_tester.rb +49 -0
- data/lib/openstudio_measure_tester/core_ext.rb +69 -0
- data/lib/openstudio_measure_tester/coverage.rb +174 -0
- data/lib/openstudio_measure_tester/dashboard.rb +33 -0
- data/lib/openstudio_measure_tester/minitest_result.rb +144 -0
- data/lib/openstudio_measure_tester/openstudio_style.rb +502 -0
- data/lib/openstudio_measure_tester/openstudio_testing_result.rb +167 -0
- data/lib/openstudio_measure_tester/rake_task.rb +258 -0
- data/lib/openstudio_measure_tester/rubocop_result.rb +213 -0
- data/lib/openstudio_measure_tester/templates/dashboard.html.erb +566 -0
- data/lib/openstudio_measure_tester/test_helper.rb +44 -0
- data/lib/openstudio_measure_tester/version.rb +31 -0
- data/openstudio_measure_tester.gemspec +35 -0
- metadata +230 -0
@@ -0,0 +1,502 @@
|
|
1
|
+
########################################################################################################################
|
2
|
+
# OpenStudio(R), Copyright (c) 2008-2018, Alliance for Sustainable Energy, LLC. All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
|
5
|
+
# following conditions are met:
|
6
|
+
#
|
7
|
+
# (1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following
|
8
|
+
# disclaimer.
|
9
|
+
#
|
10
|
+
# (2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
|
11
|
+
# following disclaimer in the documentation and/or other materials provided with the distribution.
|
12
|
+
#
|
13
|
+
# (3) Neither the name of the copyright holder nor the names of any contributors may be used to endorse or promote
|
14
|
+
# products derived from this software without specific prior written permission from the respective party.
|
15
|
+
#
|
16
|
+
# (4) Other than as required in clauses (1) and (2), distributions in any form of modifications or other derivative
|
17
|
+
# works may not use the "OpenStudio" trademark, "OS", "os", or any other confusingly similar designation without
|
18
|
+
# specific prior written permission from Alliance for Sustainable Energy, LLC.
|
19
|
+
#
|
20
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
21
|
+
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
22
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER, THE UNITED STATES GOVERNMENT, OR ANY CONTRIBUTORS BE LIABLE FOR
|
23
|
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
24
|
+
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
25
|
+
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
26
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
27
|
+
########################################################################################################################
|
28
|
+
|
29
|
+
module OpenStudioMeasureTester
|
30
|
+
class OpenStudioStyle
|
31
|
+
attr_reader :results
|
32
|
+
attr_reader :measure_messages
|
33
|
+
|
34
|
+
CHECKS = [
|
35
|
+
{
|
36
|
+
regex: /OpenStudio::Ruleset::ModelUserScript/,
|
37
|
+
check_type: :if_exists,
|
38
|
+
message: 'OpenStudio::Ruleset::ModelUserScript is deprecated, use OpenStudio::Measure::ModelMeasure instead.',
|
39
|
+
type: :deprecated,
|
40
|
+
severity: :error,
|
41
|
+
file_type: :measure
|
42
|
+
}, {
|
43
|
+
regex: /OpenStudio::Ruleset::OSRunner/,
|
44
|
+
check_type: :if_exists,
|
45
|
+
message: 'OpenStudio::Ruleset::OSRunner is deprecated, use OpenStudio::Measure::OSRunner.new(OpenStudio::WorkflowJSON.new) instead.',
|
46
|
+
type: :deprecated,
|
47
|
+
severity: :error,
|
48
|
+
file_type: :measure
|
49
|
+
}, {
|
50
|
+
regex: /OpenStudio::Ruleset::OSRunner/,
|
51
|
+
check_type: :if_exists,
|
52
|
+
message: 'OpenStudio::Ruleset::OSRunner is deprecated, use OpenStudio::Measure::OSRunner.new(OpenStudio::WorkflowJSON.new) instead.',
|
53
|
+
type: :deprecated,
|
54
|
+
severity: :error,
|
55
|
+
file_type: :test
|
56
|
+
}, {
|
57
|
+
regex: /OpenStudio::Ruleset::OSArgumentVector/,
|
58
|
+
check_type: :if_exists,
|
59
|
+
message: 'OpenStudio::Ruleset::OSArgumentVector is deprecated, use OpenStudio::Measure::OSArgumentVector instead.',
|
60
|
+
type: :deprecated,
|
61
|
+
severity: :error,
|
62
|
+
file_type: :measure
|
63
|
+
}, {
|
64
|
+
regex: /OpenStudio::Ruleset::OSArgument/,
|
65
|
+
check_type: :if_exists,
|
66
|
+
message: 'OpenStudio::Ruleset::OSArgument is deprecated, use OpenStudio::Measure::OSArgument instead.',
|
67
|
+
type: :deprecated,
|
68
|
+
severity: :error,
|
69
|
+
file_type: :measure
|
70
|
+
}, {
|
71
|
+
regex: /OpenStudio::Ruleset::OSArgumentMap/,
|
72
|
+
check_type: :if_exists,
|
73
|
+
message: 'OpenStudio::Ruleset::OSArgumentMap is deprecated, use OpenStudio::Measure.convertOSArgumentVectorToMap(arguments) instead.',
|
74
|
+
type: :deprecated,
|
75
|
+
severity: :error,
|
76
|
+
file_type: :measure
|
77
|
+
}, {
|
78
|
+
regex: /def description(.*?)end/m,
|
79
|
+
check_type: :if_missing,
|
80
|
+
message: '\'def description\' is missing.',
|
81
|
+
type: :syntax,
|
82
|
+
severity: :error,
|
83
|
+
file_type: :measure
|
84
|
+
}, {
|
85
|
+
regex: /def modeler_description(.*?)end/m,
|
86
|
+
check_type: :if_missing,
|
87
|
+
message: '\'def modeler_description\' is missing.',
|
88
|
+
type: :syntax,
|
89
|
+
severity: :error,
|
90
|
+
file_type: :measure
|
91
|
+
}, {
|
92
|
+
regex: /require .openstudio_measure_tester\/test_helper./,
|
93
|
+
check_type: :if_missing,
|
94
|
+
message: "Must include 'require 'openstudio_measure_tester/test_helper'' in Test file to report coverage correctly.",
|
95
|
+
type: :syntax,
|
96
|
+
severity: :error,
|
97
|
+
file_type: :test
|
98
|
+
}, {
|
99
|
+
regex: /MiniTest::Unit::TestCase/,
|
100
|
+
check_type: :if_exists,
|
101
|
+
message: "MiniTest::Unit::TestCase is deprecated. Use MiniTest::Test.",
|
102
|
+
type: :syntax,
|
103
|
+
severity: :warning,
|
104
|
+
file_type: :test
|
105
|
+
}, {
|
106
|
+
regex: /require .openstudio\/ruleset /,
|
107
|
+
check_type: :if_exists,
|
108
|
+
message: "Require openstudio/ruleset/* is deprecated. Use require 'openstudio/measure/*'",
|
109
|
+
type: :syntax,
|
110
|
+
severity: :warning,
|
111
|
+
file_type: :test
|
112
|
+
}
|
113
|
+
].freeze
|
114
|
+
|
115
|
+
# Pass in the measures_glob with the filename (typically measure.rb)
|
116
|
+
def initialize(measures_glob)
|
117
|
+
@measures_glob = measures_glob
|
118
|
+
@results = {by_measure: {}}
|
119
|
+
|
120
|
+
# Individual measure messages
|
121
|
+
@measure_messages = []
|
122
|
+
|
123
|
+
# Load in the method infoExtractor which will load measure info (helpful comment huh?)
|
124
|
+
# https://github.com/NREL/OpenStudio/blob/e7aa6be05a714814983d68ea840ca61383e9ef54/openstudiocore/src/measure/OSMeasureInfoGetter.cpp#L254
|
125
|
+
eval(::OpenStudio::Measure.infoExtractorRubyFunction)
|
126
|
+
|
127
|
+
Dir[@measures_glob].each do |measure|
|
128
|
+
measure_dir = File.dirname(measure)
|
129
|
+
|
130
|
+
# initialize the measure name from the directory until the measure_hash is loaded.
|
131
|
+
# The test_measure method can fail but still report errors, so we need a place to store the results.
|
132
|
+
@results[:by_measure].merge!(test_measure(measure_dir))
|
133
|
+
end
|
134
|
+
|
135
|
+
aggregate_results
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_measure(measure_dir)
|
139
|
+
# reset the instance variables for this measure
|
140
|
+
@measure_messages.clear
|
141
|
+
# initialize the measure name from the directory until the measure_hash is loaded.
|
142
|
+
# The test_measure method can fail but still report errors, so we need a place to store the results.
|
143
|
+
@measure_classname = measure_dir.split('/').last
|
144
|
+
|
145
|
+
measure_missing = false
|
146
|
+
unless Dir.exist? measure_dir
|
147
|
+
log_message("Could not find measure directory: '#{measure_dir}'.", :general, :error)
|
148
|
+
measure_missing = true
|
149
|
+
end
|
150
|
+
|
151
|
+
unless File.exist? "#{measure_dir}/measure.rb"
|
152
|
+
log_message("Could not find measure.rb in '#{measure_dir}'.", :general, :error)
|
153
|
+
measure_missing = true
|
154
|
+
end
|
155
|
+
|
156
|
+
unless File.exist? "#{measure_dir}/measure.xml"
|
157
|
+
log_message("Could not find measure.xml in '#{measure_dir}'.", :general, :error)
|
158
|
+
measure_missing = true
|
159
|
+
end
|
160
|
+
|
161
|
+
unless measure_missing
|
162
|
+
measure = OpenStudio::BCLMeasure.load(measure_dir)
|
163
|
+
if measure.empty?
|
164
|
+
log_message("Failed to load measure '#{measure_dir}'", :general, :error)
|
165
|
+
else
|
166
|
+
measure = measure.get
|
167
|
+
measure_info = infoExtractor(measure, OpenStudio::Model::OptionalModel.new, OpenStudio::OptionalWorkspace.new)
|
168
|
+
|
169
|
+
measure_hash = generate_measure_hash(measure_dir, measure, measure_info)
|
170
|
+
measure_hash.merge!(get_attributes_from_measure(measure_dir, measure_hash[:class_name]))
|
171
|
+
|
172
|
+
@measure_classname = measure_hash[:class_name]
|
173
|
+
# At this point, the measure.rb file is ensured to exist
|
174
|
+
|
175
|
+
# run static checks on the files in the measure directory
|
176
|
+
run_regex_checks(measure_dir)
|
177
|
+
|
178
|
+
validate_measure_hash(measure_hash)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# pp @measure_messages
|
183
|
+
|
184
|
+
# calculate the info, warnings, errors and return the measure data
|
185
|
+
# TODO: break out the issues by file
|
186
|
+
return {
|
187
|
+
@measure_classname.to_sym => {
|
188
|
+
measure_info: @measure_messages.nil? ? 0 : @measure_messages.count{|h| h[:severity] == :info},
|
189
|
+
measure_warnings: @measure_messages.nil? ? 0 : @measure_messages.count{|h| h[:severity] == :warning},
|
190
|
+
measure_errors: @measure_messages.nil? ? 0 : @measure_messages.count{|h| h[:severity] == :error},
|
191
|
+
issues: @measure_messages.clone
|
192
|
+
}
|
193
|
+
}
|
194
|
+
end
|
195
|
+
|
196
|
+
def log_message(message, type = :syntax, severity = :info)
|
197
|
+
new_message = {
|
198
|
+
message: message,
|
199
|
+
type: type,
|
200
|
+
severity: severity
|
201
|
+
}
|
202
|
+
@measure_messages << new_message
|
203
|
+
end
|
204
|
+
|
205
|
+
# read the data in the results and sum up the total number of issues for all measures
|
206
|
+
def aggregate_results
|
207
|
+
total_info = 0
|
208
|
+
total_warnings = 0
|
209
|
+
total_errors = 0
|
210
|
+
@results[:by_measure].each_pair do |k, v|
|
211
|
+
total_info += v[:measure_info]
|
212
|
+
total_warnings += v[:measure_warnings]
|
213
|
+
total_errors += v[:measure_errors]
|
214
|
+
end
|
215
|
+
@results[:total_info] = total_info
|
216
|
+
@results[:total_warnings] = total_warnings
|
217
|
+
@results[:total_errors] = total_errors
|
218
|
+
end
|
219
|
+
|
220
|
+
def save_results
|
221
|
+
FileUtils.mkdir 'openstudio_style' unless Dir.exist? 'openstudio_style'
|
222
|
+
File.open("openstudio_style/openstudio_style.json", 'w') do |file|
|
223
|
+
file << JSON.pretty_generate(@results)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def run_regex_checks(measure_dir)
|
228
|
+
def check(data, check)
|
229
|
+
if check[:check_type] == :if_exists
|
230
|
+
if data =~ check[:regex]
|
231
|
+
log_message(check[:message], check[:type], check[:severity])
|
232
|
+
end
|
233
|
+
elsif check[:check_type] == :if_missing
|
234
|
+
if data !~ check[:regex]
|
235
|
+
log_message(check[:message], check[:type], check[:severity])
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
file_data = File.read("#{measure_dir}/measure.rb")
|
241
|
+
test_files = Dir["#{measure_dir}/**/*_[tT]est.rb"]
|
242
|
+
CHECKS.each do |check|
|
243
|
+
if check[:file_type] == :test
|
244
|
+
test_files.each do |test_file|
|
245
|
+
puts test_file
|
246
|
+
test_filedata = File.read(test_file)
|
247
|
+
check(test_filedata, check)
|
248
|
+
end
|
249
|
+
elsif check[:file_type] == :measure
|
250
|
+
check(file_data, check)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
# check the name of the measure and make sure that there are no unwanted characters
|
256
|
+
#
|
257
|
+
# @param name_type [String] type of name that is being validated (e.g. Display Name, Class Name, etc)
|
258
|
+
# @param name [String] name to validate
|
259
|
+
# @param name [Symbol] severity, [:info, :warning, :error]
|
260
|
+
# @param options [Hash] Additional checks
|
261
|
+
# @option options [Boolean] :ensure_camelcase
|
262
|
+
# @option options [Boolean] :ensure_snakecase
|
263
|
+
def validate_name(name_type, name, severity = :info, options = {})
|
264
|
+
clean_name = name
|
265
|
+
|
266
|
+
# Check for parenthetical names
|
267
|
+
if clean_name =~ /\(.+?\)/
|
268
|
+
log_message("#{name_type} '#{name}' appears to have units. Set units in the setUnits method.", severity)
|
269
|
+
end
|
270
|
+
|
271
|
+
if clean_name =~ /\?|\.|\#/
|
272
|
+
log_message("#{name_type} '#{name}' cannot contain ?#.[] characters.", :syntax, severity)
|
273
|
+
end
|
274
|
+
|
275
|
+
if options[:ensure_camelcase]
|
276
|
+
# convert to snake and then back to camelcase to check if formatted correctly
|
277
|
+
if clean_name != clean_name.strip
|
278
|
+
log_message("#{name_type} '#{name}' has leading or trailing spaces.", :syntax, severity)
|
279
|
+
end
|
280
|
+
|
281
|
+
if clean_name != clean_name.to_snakecase.to_camelcase
|
282
|
+
log_message("#{name_type} '#{name}' is not CamelCase.", :syntax, severity)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
if options[:ensure_snakecase]
|
287
|
+
# no leading/trailing spaces
|
288
|
+
if clean_name != clean_name.strip
|
289
|
+
log_message("#{name_type} '#{name}' has leading or trailing spaces.", :syntax, severity)
|
290
|
+
end
|
291
|
+
|
292
|
+
if clean_name != clean_name.to_snakecase
|
293
|
+
log_message("#{name_type} '#{name}' is not snake_case.", :syntax, severity)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
# Validate the measure hash to make sure that it is meets the style guide. This will also perform the selection
|
299
|
+
# of which data to use for the "actual metadata"
|
300
|
+
def validate_measure_hash(measure_hash)
|
301
|
+
validate_name('Measure name', measure_hash[:name], :error, ensure_snakecase: true)
|
302
|
+
validate_name('Class name', measure_hash[:class_name], :error, ensure_camelcase: true)
|
303
|
+
# check if @measure_name (which is the directory name) is snake cased
|
304
|
+
|
305
|
+
validate_name('Measure directory name',
|
306
|
+
measure_hash[:measure_dir].split('/').last,
|
307
|
+
:warning,
|
308
|
+
ensure_snakecase: true)
|
309
|
+
|
310
|
+
log_message('Could not find measure description in XML.', :structure, :warning) if measure_hash[:description].empty?
|
311
|
+
log_message('Could not find modeler description in XML.', :structure, :warning) unless measure_hash[:modeler_description]
|
312
|
+
log_message('Could not find display_name in measure.', :structure, :warning) unless measure_hash[:display_name]
|
313
|
+
log_message('Could not find measure name in measure.', :structure, :warning) unless measure_hash[:name]
|
314
|
+
|
315
|
+
# def name is the display name in the XML!
|
316
|
+
if measure_hash[:values_from_file][:display_name].empty?
|
317
|
+
log_message('Could not find "def name" in measure.rb', :structure, :error)
|
318
|
+
elsif measure_hash[:display_name] != measure_hash[:values_from_file][:display_name]
|
319
|
+
log_message("'def name' in measure.rb differs from <display_name> in XML. Run openstudio measure -u .", :structure, :error)
|
320
|
+
end
|
321
|
+
|
322
|
+
if measure_hash[:values_from_file][:name] != measure_hash[:name]
|
323
|
+
log_message("Measure class as snake_case name does not match <name> in XML. Run openstudio measure -u .", :structure, :error)
|
324
|
+
end
|
325
|
+
|
326
|
+
if measure_hash[:values_from_file][:description].empty?
|
327
|
+
log_message('Could not find "def description" in measure.rb', :structure, :error)
|
328
|
+
elsif measure_hash[:description] != measure_hash[:values_from_file][:description]
|
329
|
+
log_message('Description in measure.rb differs from description in XML', :structure, :error)
|
330
|
+
end
|
331
|
+
|
332
|
+
if measure_hash[:values_from_file][:description].empty?
|
333
|
+
log_message('Could not find "def modeler_description" in measure.rb', :structure, :error) if measure_hash[:values_from_file][:description].empty?
|
334
|
+
elsif measure_hash[:description] != measure_hash[:values_from_file][:description]
|
335
|
+
log_message('Modeler description in measure.rb differs from modeler description in XML', :structure, :error)
|
336
|
+
end
|
337
|
+
|
338
|
+
measure_hash[:arguments].each do |arg|
|
339
|
+
validate_name('Argument display name', arg[:display_name], :error)
|
340
|
+
# {
|
341
|
+
# :name => "relative_building_rotation",
|
342
|
+
# :display_name =>
|
343
|
+
# "Number of Degrees to Rotate Building (positive value is clockwise).",
|
344
|
+
# :description => "",
|
345
|
+
# :type => "Double",
|
346
|
+
# :required => true,
|
347
|
+
# :model_dependent => false,
|
348
|
+
# :default_value => 90.0
|
349
|
+
# }
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
def get_attributes_from_measure(measure_dir, class_name)
|
354
|
+
result = {
|
355
|
+
values_from_file: {
|
356
|
+
name: '',
|
357
|
+
display_name: '',
|
358
|
+
description: '',
|
359
|
+
modeler_description: ''
|
360
|
+
}
|
361
|
+
}
|
362
|
+
|
363
|
+
begin
|
364
|
+
# file exists because the init checks for its existence
|
365
|
+
require "#{measure_dir}/measure.rb"
|
366
|
+
measure = Object.const_get(class_name).new
|
367
|
+
rescue LoadError => e
|
368
|
+
log_message("Could not load measure into memory with error #{e.message}", :initialize, :error)
|
369
|
+
return result
|
370
|
+
end
|
371
|
+
|
372
|
+
result[:values_from_file][:name] = class_name.to_snakecase
|
373
|
+
result[:values_from_file][:display_name] = measure.name
|
374
|
+
result[:values_from_file][:description] = measure.description
|
375
|
+
result[:values_from_file][:modeler_description] = measure.modeler_description
|
376
|
+
|
377
|
+
result
|
378
|
+
end
|
379
|
+
|
380
|
+
###################################################################################################################
|
381
|
+
# These methods are copied from the measure_manager.rb file in OpenStudio. Once the measure_manager.rb file
|
382
|
+
# is shipped with OpenStudio, we can deprecate the copying over.
|
383
|
+
#
|
384
|
+
# https://github.com/NREL/OpenStudio/blob/7865ba413ef52e8c41b8b95d6643d68eb949f1c4/openstudiocore/src/cli/measure_manager.rb#L355
|
385
|
+
###################################################################################################################
|
386
|
+
def generate_measure_hash(measure_dir, measure, measure_info)
|
387
|
+
result = {}
|
388
|
+
result[:measure_dir] = measure_dir
|
389
|
+
result[:name] = measure.name
|
390
|
+
result[:directory] = measure.directory.to_s
|
391
|
+
if measure.error.is_initialized
|
392
|
+
result[:error] = measure.error.get
|
393
|
+
end
|
394
|
+
result[:uid] = measure.uid
|
395
|
+
result[:uuid] = measure.uuid.to_s
|
396
|
+
result[:version_id] = measure.versionId
|
397
|
+
result[:version_uuid] = measure.versionUUID.to_s
|
398
|
+
version_modified = measure.versionModified
|
399
|
+
if version_modified.is_initialized
|
400
|
+
result[:version_modified] = version_modified.get.toISO8601
|
401
|
+
else
|
402
|
+
result[:version_modified] = nil
|
403
|
+
end
|
404
|
+
result[:xml_checksum] = measure.xmlChecksum
|
405
|
+
result[:name] = measure.name
|
406
|
+
result[:display_name] = measure.displayName
|
407
|
+
result[:class_name] = measure.className
|
408
|
+
result[:description] = measure.description
|
409
|
+
result[:modeler_description] = measure.modelerDescription
|
410
|
+
result[:tags] = []
|
411
|
+
measure.tags.each {|tag| result[:tags] << tag}
|
412
|
+
|
413
|
+
result[:outputs] = []
|
414
|
+
begin
|
415
|
+
# this is an OS 2.0 only method
|
416
|
+
measure.outputs.each do |output|
|
417
|
+
out = {}
|
418
|
+
out[:name] = output.name
|
419
|
+
out[:display_name] = output.displayName
|
420
|
+
out[:short_name] = output.shortName.get if output.shortName.is_initialized
|
421
|
+
out[:description] = output.description
|
422
|
+
out[:type] = output.type
|
423
|
+
out[:units] = output.units.get if output.units.is_initialized
|
424
|
+
out[:model_dependent] = output.modelDependent
|
425
|
+
result[:outputs] << out
|
426
|
+
end
|
427
|
+
rescue StandardError
|
428
|
+
end
|
429
|
+
|
430
|
+
attributes = []
|
431
|
+
measure.attributes.each do |a|
|
432
|
+
value_type = a.valueType
|
433
|
+
if value_type == 'Boolean'.to_AttributeValueType
|
434
|
+
attributes << {name: a.name, display_name: a.displayName(true).get, value: a.valueAsBoolean}
|
435
|
+
elsif value_type == 'Double'.to_AttributeValueType
|
436
|
+
attributes << {name: a.name, display_name: a.displayName(true).get, value: a.valueAsDouble}
|
437
|
+
elsif value_type == 'Integer'.to_AttributeValueType
|
438
|
+
attributes << {name: a.name, display_name: a.displayName(true).get, value: a.valueAsInteger}
|
439
|
+
elsif value_type == 'Unsigned'.to_AttributeValueType
|
440
|
+
attributes << {name: a.name, display_name: a.displayName(true).get, value: a.valueAsUnsigned}
|
441
|
+
elsif value_type == 'String'.to_AttributeValueType
|
442
|
+
attributes << {name: a.name, display_name: a.displayName(true).get, value: a.valueAsString}
|
443
|
+
end
|
444
|
+
end
|
445
|
+
result[:attributes] = attributes
|
446
|
+
|
447
|
+
result[:arguments] = measure_info ? get_arguments_from_measure_info(measure_info) : []
|
448
|
+
|
449
|
+
result
|
450
|
+
end
|
451
|
+
|
452
|
+
def get_arguments_from_measure_info(measure_info)
|
453
|
+
result = []
|
454
|
+
|
455
|
+
measure_info.arguments.each do |argument|
|
456
|
+
type = argument.type
|
457
|
+
|
458
|
+
arg = {}
|
459
|
+
arg[:name] = argument.name
|
460
|
+
arg[:display_name] = argument.displayName
|
461
|
+
arg[:description] = argument.description.to_s
|
462
|
+
arg[:type] = argument.type.valueName
|
463
|
+
arg[:required] = argument.required
|
464
|
+
arg[:model_dependent] = argument.modelDependent
|
465
|
+
|
466
|
+
if type == 'Boolean'.to_OSArgumentType
|
467
|
+
arg[:default_value] = argument.defaultValueAsBool if argument.hasDefaultValue
|
468
|
+
|
469
|
+
elsif type == 'Double'.to_OSArgumentType
|
470
|
+
arg[:units] = argument.units.get if argument.units.is_initialized
|
471
|
+
arg[:default_value] = argument.defaultValueAsDouble if argument.hasDefaultValue
|
472
|
+
|
473
|
+
elsif type == 'Quantity'.to_OSArgumentType
|
474
|
+
arg[:units] = argument.units.get if argument.units.is_initialized
|
475
|
+
arg[:default_value] = argument.defaultValueAsQuantity.value if argument.hasDefaultValue
|
476
|
+
|
477
|
+
elsif type == 'Integer'.to_OSArgumentType
|
478
|
+
arg[:units] = argument.units.get if argument.units.is_initialized
|
479
|
+
arg[:default_value] = argument.defaultValueAsInteger if argument.hasDefaultValue
|
480
|
+
|
481
|
+
elsif type == 'String'.to_OSArgumentType
|
482
|
+
arg[:default_value] = argument.defaultValueAsString if argument.hasDefaultValue
|
483
|
+
|
484
|
+
elsif type == 'Choice'.to_OSArgumentType
|
485
|
+
arg[:default_value] = argument.defaultValueAsString if argument.hasDefaultValue
|
486
|
+
arg[:choice_values] = []
|
487
|
+
argument.choiceValues.each {|value| arg[:choice_values] << value}
|
488
|
+
arg[:choice_display_names] = []
|
489
|
+
argument.choiceValueDisplayNames.each {|value| arg[:choice_display_names] << value}
|
490
|
+
|
491
|
+
elsif type == 'Path'.to_OSArgumentType
|
492
|
+
arg[:default_value] = argument.defaultValueAsPath.to_s if argument.hasDefaultValue
|
493
|
+
|
494
|
+
end
|
495
|
+
|
496
|
+
result << arg
|
497
|
+
end
|
498
|
+
|
499
|
+
result
|
500
|
+
end
|
501
|
+
end
|
502
|
+
end
|