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