bcl 0.6.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f509d5e458bab1b477c6e4695a5333766e6f3e19be91ac2452d87452fcfcd981
4
- data.tar.gz: ccd36a253abd3c64635959edf253ad4aac87789e95958787d11bda1b1e3dfb9c
3
+ metadata.gz: 889eac96a388f104d76ee80dc7406ec047b535e106498425418d7e121b94416d
4
+ data.tar.gz: ae5eba1617aabbe3245975a03d0733afccb5381182071e4a146780b26ad77196
5
5
  SHA512:
6
- metadata.gz: 7ebba6c382b8a2d2576a8ffff66c8571161df38a01c6ff5ff17cc0b834cecd995c96ffb29749cde7302aa0650cb957ff859607fc89247b5594f2e691f423e099
7
- data.tar.gz: 37e1f02ffd32494cbf49f83c04b305e0b7782b6527fe549c758be26ef0f8a683e797e18c1b68a5ecaa809cf750e72cf2184d276e7108f26ce14c3a8025cd3a13
6
+ metadata.gz: 71b0d4004fc2a0dfc20fff6e912adf194262bba8c2b6ba517d264d6700744c25142a036220131de1f19636e8c5fd4702d55a5fd641b8877843aa124178101c68
7
+ data.tar.gz: 4e4e7e94b3851f5a5be2a6dfbcd32c929149ce6e8c5251cfeef143d30546073d093f996d32af4b5949bae48523c87ea1f91b701f9eebf4b8af1e109408d31300
@@ -5,5 +5,4 @@ AllCops:
5
5
  - 'gems/**/*'
6
6
 
7
7
  inherit_from:
8
- - http://s3.amazonaws.com/openstudio-resources/styles/rubocop.yml
9
-
8
+ - http://s3.amazonaws.com/openstudio-resources/styles/rubocop_v3.yml
@@ -1,5 +1,11 @@
1
1
  # Change Log
2
2
 
3
+ ## Version 0.6.1
4
+
5
+ * Remove dependency for winole (`require 'win32ole'`). This affects the reading of the component spreadsheets and the
6
+ the master taxomony.
7
+ * Use new rubocop (v3) from s3 openstudio-resources
8
+
3
9
  ## Version 0.6.0
4
10
 
5
11
  * Support Ruby > 2.5
data/Rakefile CHANGED
@@ -250,7 +250,7 @@ namespace :bcl do
250
250
 
251
251
  paths << file.to_s
252
252
  if file.to_s =~ /^.{0,2}component.xml$/ || file.to_s =~ /^.{0,2}measure.xml$/
253
- if file.to_s =~ /^.{0,2}component.xml$/
253
+ if file.to_s.match?(/^.{0,2}component.xml$/)
254
254
  content_type = 'component'
255
255
  end
256
256
  # extract uuid and vid
data/lib/bcl.rb CHANGED
@@ -27,12 +27,10 @@ require 'spreadsheet'
27
27
 
28
28
  require 'bcl/core_ext'
29
29
  require 'bcl/base_xml'
30
- require 'bcl/component_spreadsheet'
31
30
  require 'bcl/component_from_spreadsheet'
32
31
  require 'bcl/component'
33
32
  require 'bcl/component_methods'
34
33
  require 'bcl/tar_ball'
35
- require 'bcl/master_taxonomy'
36
34
  require 'bcl/version'
37
35
 
38
36
  # Some global structures
@@ -384,4 +384,4 @@ module BCL
384
384
  xmlfile.close
385
385
  end
386
386
  end
387
- end # module BCL
387
+ end
@@ -30,8 +30,6 @@ module BCL
30
30
  class ComponentFromSpreadsheet
31
31
  @@changed = false
32
32
 
33
- public
34
-
35
33
  # initialize with Excel spreadsheet to read
36
34
  # seems to only be working with xls spreadsheets
37
35
  def initialize(xlsx_path, worksheet_names = ['all'])
@@ -80,14 +78,14 @@ module BCL
80
78
 
81
79
  puts " headers: #{component.headers}"
82
80
  component.headers.each do |header|
83
- if /description/i.match(header.name)
81
+ if /description/i.match?(header.name)
84
82
  name = values.delete_at(0) # name, uid already processed
85
83
  uid = values.delete_at(0)
86
84
  component_xml.comp_version_id = values.delete_at(0)
87
85
  description = values.delete_at(0)
88
86
  component_xml.modeler_description = values.delete_at(0)
89
87
  component_xml.description = description
90
- elsif /provenance/i.match(header.name)
88
+ elsif /provenance/i.match?(header.name)
91
89
  author = values.delete_at(0)
92
90
  datetime = values.delete_at(0)
93
91
  if datetime.nil?
@@ -97,10 +95,10 @@ module BCL
97
95
 
98
96
  comment = values.delete_at(0)
99
97
  component_xml.add_provenance(author.to_s, datetime.strftime('%Y-%m-%d'), comment.to_s)
100
- elsif /tag/i.match(header.name)
98
+ elsif /tag/i.match?(header.name)
101
99
  value = values.delete_at(0)
102
100
  component_xml.add_tag(value)
103
- elsif /attribute/i.match(header.name)
101
+ elsif /attribute/i.match?(header.name)
104
102
  value = values.delete_at(0)
105
103
  name = header.children[0]
106
104
  units = ''
@@ -109,7 +107,7 @@ module BCL
109
107
  units = match_data[2].strip
110
108
  end
111
109
  component_xml.add_attribute(name, value, units)
112
- elsif /source/i.match(header.name)
110
+ elsif /source/i.match?(header.name)
113
111
  manufacturer = values.delete_at(0)
114
112
  model = values.delete_at(0)
115
113
  serial_no = values.delete_at(0)
@@ -120,7 +118,7 @@ module BCL
120
118
  component_xml.source_serial_no = serial_no
121
119
  component_xml.source_year = year
122
120
  component_xml.source_url = url
123
- elsif /file/i.match(header.name)
121
+ elsif /file/i.match?(header.name)
124
122
  software_program = values.delete_at(0)
125
123
  version = values.delete_at(0)
126
124
  filename = values.delete_at(0)
@@ -93,13 +93,13 @@ module BCL
93
93
  bni = ''
94
94
  junkout = res['set-cookie'].split(';')
95
95
  junkout.each do |line|
96
- if line =~ /BNES_SESS/
96
+ if line.match?(/BNES_SESS/)
97
97
  bnes = line.match(/(BNES_SESS.*)/)[0]
98
98
  end
99
99
  end
100
100
 
101
101
  junkout.each do |line|
102
- if line =~ /BNI/
102
+ if line.match?(/BNI/)
103
103
  bni = line.match(/(BNI.*)/)[0]
104
104
  end
105
105
  end
@@ -293,13 +293,13 @@ module BCL
293
293
  version_id = nil
294
294
  if uuid.nil?
295
295
  puts File.extname(filename_and_path).downcase
296
- if filename_and_path =~ /^.*.tar.gz$/i
296
+ if filename_and_path.match?(/^.*.tar.gz$/i)
297
297
  uuid, version_id = uuid_vid_from_tarball(filename_and_path)
298
298
  puts "Parsed uuid out of tar.gz file with value #{uuid}"
299
299
  end
300
300
  else
301
301
  # verify the uuid via regex
302
- unless uuid =~ /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/
302
+ unless uuid.match?(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/)
303
303
  raise "uuid of #{uuid} is invalid"
304
304
  end
305
305
  end
@@ -362,7 +362,7 @@ module BCL
362
362
  xml_file = REXML::Document.new entry.read
363
363
 
364
364
  # pull out some information
365
- if entry.name =~ /component/
365
+ if entry.name.match?(/component/)
366
366
  u = xml_file.elements['component/uid']
367
367
  v = xml_file.elements['component/version_id']
368
368
  else
@@ -392,7 +392,7 @@ module BCL
392
392
  xml_to_parse = File.new(path_to_xml)
393
393
  xml_file = REXML::Document.new xml_to_parse
394
394
 
395
- if path_to_xml.to_s.split('/').last =~ /component.xml/
395
+ if path_to_xml.to_s.split('/').last.match?(/component.xml/)
396
396
  u = xml_file.elements['component/uid']
397
397
  v = xml_file.elements['component/version_id']
398
398
  else
@@ -619,7 +619,7 @@ module BCL
619
619
 
620
620
  settings
621
621
  end
622
- end # class ComponentMethods
622
+ end
623
623
 
624
624
  # TODO: make this extend the component_xml class (or create a super class around components)
625
625
 
@@ -681,4 +681,4 @@ module BCL
681
681
 
682
682
  Dir.chdir(current_dir)
683
683
  end
684
- end # module BCL
684
+ end
@@ -18,5 +18,5 @@
18
18
  ######################################################################
19
19
 
20
20
  module BCL
21
- VERSION = '0.6.0'.freeze
21
+ VERSION = '0.6.1'.freeze
22
22
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bcl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Macumber
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: exe
13
13
  cert_chain: []
14
- date: 2020-04-30 00:00:00.000000000 Z
14
+ date: 2020-05-09 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: builder
@@ -174,11 +174,9 @@ files:
174
174
  - lib/bcl/component.rb
175
175
  - lib/bcl/component_from_spreadsheet.rb
176
176
  - lib/bcl/component_methods.rb
177
- - lib/bcl/component_spreadsheet.rb
178
177
  - lib/bcl/core_ext.rb
179
178
  - lib/bcl/current_taxonomy.json
180
179
  - lib/bcl/current_taxonomy.xml
181
- - lib/bcl/master_taxonomy.rb
182
180
  - lib/bcl/tar_ball.rb
183
181
  - lib/bcl/version.rb
184
182
  - lib/files/Components.xls
@@ -1,287 +0,0 @@
1
- ######################################################################
2
- # Copyright (c) 2008-2019, Alliance for Sustainable Energy.
3
- # All rights reserved.
4
- #
5
- # This library is free software; you can redistribute it and/or
6
- # modify it under the terms of the GNU Lesser General Public
7
- # License as published by the Free Software Foundation; either
8
- # version 2.1 of the License, or (at your option) any later version.
9
- #
10
- # This library is distributed in the hope that it will be useful,
11
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
- # Lesser General Public License for more details.
14
- #
15
- # You should have received a copy of the GNU Lesser General Public
16
- # License along with this library; if not, write to the Free Software
17
- # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
- ######################################################################
19
-
20
- # Converts a custom Excel spreadsheet format to BCL components for upload
21
- # Format of the Excel spreadsheet is documented in /doc/ComponentSpreadsheet.docx
22
-
23
- if RUBY_PLATFORM =~ /mswin|mingw|cygwin/
24
- begin
25
- # apparently this is not a gem (todo: need to remove and replace with roo)
26
- require 'win32ole'
27
- mod = WIN32OLE
28
- $have_win32ole = true
29
- rescue NameError
30
- # do not have win32ole
31
- end
32
- end
33
-
34
- module BCL
35
- class ComponentSpreadsheet
36
- public
37
-
38
- # WINDOWS ONLY SECTION BECAUSE THIS USES WIN32OLE
39
- if $have_win32ole
40
-
41
- # initialize with Excel spreadsheet to read
42
- def initialize(xlsx_path, worksheet_names = ['all'])
43
- @xlsx_path = Pathname.new(xlsx_path).realpath.to_s
44
- @worksheets = []
45
-
46
- begin
47
- excel = WIN32OLE.new('Excel.Application')
48
-
49
- xlsx = excel.Workbooks.Open(@xlsx_path)
50
-
51
- # by default, operate on all worksheets
52
- if worksheet_names == ['all']
53
- xlsx.Worksheets.each do |xlsx_worksheet|
54
- parse_xlsx_worksheet(xlsx_worksheet)
55
- end
56
- else # if specific worksheets are specified, operate on them
57
- worksheet_names.each do |worksheet_name|
58
- parse_xlsx_worksheet(xlsx.Worksheets(worksheet_name))
59
- end
60
- end
61
-
62
- # save spreadsheet if changes have been made
63
- if xlsx.saved == true
64
- # puts "[ComponentSpreadsheet] Spreadsheet unchanged; not saving"
65
- else
66
- xlsx.Save
67
- puts '[ComponentSpreadsheet] Spreadsheet changes saved'
68
- end
69
- ensure
70
- excel.Quit
71
- WIN32OLE.ole_free(excel)
72
- excel.ole_free
73
- xlsx = nil
74
- excel = nil
75
- GC.start
76
- end
77
- end
78
-
79
- else # if $have_win32ole
80
-
81
- # parse the master taxonomy document
82
- def initialize(_xlsx_path)
83
- puts "ComponentSpreadsheet class requires 'win32ole' to parse the component spreadsheet."
84
- puts 'ComponentSpreadsheet may also be stored and loaded from JSON if your platform does not support win32ole.'
85
- end
86
-
87
- end # if $have_win32ole
88
-
89
- def save(save_path, chunk_size = 1000, delete_old_gather = false)
90
- # load master taxonomy to validate components
91
- taxonomy = BCL::MasterTaxonomy.new
92
-
93
- # FileUtils.rm_rf(save_path) if File.exists?(save_path) and File.directory?(save_path)
94
-
95
- @worksheets.each do |worksheet|
96
- worksheet.components.each do |component|
97
- component_xml = Component.new("#{save_path}/components")
98
- component_xml.name = component.name
99
- component_xml.uid = component.uid
100
- component_xml.comp_version_id = component.version_id
101
-
102
- # this tag is how we know where this goes in the taxonomy
103
- component_xml.add_tag(worksheet.name)
104
-
105
- values = component.values[0]
106
- component.headers.each do |header|
107
- if /description/i.match(header.name)
108
-
109
- name = values.delete_at(0)
110
- uid = values.delete_at(0)
111
- version_id = values.delete_at(0)
112
- description = values.delete_at(0)
113
- fidelity_level = values.delete_at(0).to_int
114
- # name, uid, and version_id already processed
115
- component_xml.description = description
116
- component_xml.fidelity_level = fidelity_level
117
-
118
- elsif /provenance/i.match(header.name)
119
-
120
- author = values.delete_at(0)
121
- datetime = values.delete_at(0)
122
- if datetime.nil?
123
- # puts "[ComponentSpreadsheet] WARNING missing the date in the datetime column in the spreadsheet - assuming today"
124
- datetime = DateTime.new
125
- else
126
- datetime = DateTime.parse(datetime)
127
- end
128
-
129
- comment = values.delete_at(0)
130
- component_xml.add_provenance(author.to_s, datetime.to_s, comment.to_s)
131
-
132
- elsif /tag/i.match(header.name)
133
-
134
- value = values.delete_at(0)
135
- component_xml.add_tag(value)
136
-
137
- elsif /attribute/i.match(header.name)
138
-
139
- value = values.delete_at(0)
140
- name = header.children[0]
141
- units = ''
142
- if match_data = /(.*)\((.*)\)/.match(name)
143
- name = match_data[1].strip
144
- units = match_data[2].strip
145
- end
146
- component_xml.add_attribute(name, value, units)
147
-
148
- elsif /source/i.match(header.name)
149
-
150
- manufacturer = values.delete_at(0)
151
- model = values.delete_at(0)
152
- serial_no = values.delete_at(0)
153
- year = values.delete_at(0)
154
- url = values.delete_at(0)
155
- component_xml.source_manufacturer = manufacturer
156
- component_xml.source_model = model
157
- component_xml.source_serial_no = serial_no
158
- component_xml.source_year = year
159
- component_xml.source_url = url
160
-
161
- elsif /file/i.match(header.name)
162
-
163
- software_program = values.delete_at(0)
164
- version = values.delete_at(0)
165
- filename = values.delete_at(0)
166
- filetype = values.delete_at(0)
167
- filepath = values.delete_at(0)
168
- # not all components(rows) have all files; skip if filename "" or nil
169
- next if filename == '' || filename.nil?
170
-
171
- # skip the file if it doesn't exist at the specified location
172
- unless File.exist?(filepath)
173
- puts "[ComponentSpreadsheet] ERROR #{filepath} -> File does not exist, will not be included in component xml"
174
- next # go to the next file
175
- end
176
- component_xml.add_file(software_program, version, filepath, filename, filetype)
177
-
178
- else
179
- raise "Unknown section #{header.name}"
180
-
181
- end
182
- end
183
-
184
- taxonomy.check_component(component_xml)
185
-
186
- component_xml.save_tar_gz(false)
187
- end
188
- end
189
-
190
- BCL.gather_components(save_path, chunk_size, delete_old_gather)
191
- end
192
-
193
- private
194
-
195
- def parse_xlsx_worksheet(xlsx_worksheet)
196
- worksheet = WorksheetStruct.new
197
- worksheet.name = xlsx_worksheet.Range('A1').Value
198
- worksheet.components = []
199
- puts "[ComponentSpreadsheet] Starting parsing components of type #{worksheet.name}"
200
-
201
- # find number of rows, first column should be name, should not be empty
202
- num_rows = 1
203
- loop do
204
- test = xlsx_worksheet.Range("A#{num_rows}").Value
205
- if test.nil? || test.empty?
206
- num_rows -= 1
207
- break
208
- end
209
- num_rows += 1
210
- end
211
-
212
- # scan number of columns
213
- headers = []
214
- header = nil
215
- max_col = nil
216
- xlsx_worksheet.Columns.each do |col|
217
- value1 = col.Rows('1').Value
218
- value2 = col.Rows('2').Value
219
-
220
- if !value1.nil? && !value1.empty?
221
- unless header.nil?
222
- headers << header
223
- end
224
- header = HeaderStruct.new
225
- header.name = value1
226
- header.children = []
227
- end
228
-
229
- if !value2.nil? && !value2.empty?
230
- unless header.nil?
231
- header.children << value2
232
- end
233
- end
234
-
235
- if (value1.nil? || value1.empty?) && (value2.nil? || value2.empty?)
236
- break
237
- end
238
-
239
- matchdata = /^\$(.+):/.match(col.Address)
240
- max_col = matchdata[1]
241
- end
242
-
243
- unless header.nil?
244
- headers << header
245
- end
246
-
247
- unless headers.empty?
248
- headers[0].name = 'description'
249
- end
250
-
251
- puts " Found #{num_rows - 2} components"
252
-
253
- components = []
254
- for i in 3..num_rows do
255
- component = ComponentStruct.new
256
- component.row = i
257
-
258
- # get name
259
- component.name = xlsx_worksheet.Range("A#{i}").value
260
-
261
- # get uid, if empty set it
262
- component.uid = xlsx_worksheet.Range("B#{i}").value
263
- if component.uid.nil? || component.uid.empty?
264
- component.uid = UUID.new.generate
265
- puts "#{component.name} uid missing; creating new one"
266
- xlsx_worksheet.Range("B#{i}").value = component.uid
267
- end
268
-
269
- # get version_id, if empty set it
270
- component.version_id = xlsx_worksheet.Range("C#{i}").value
271
- if component.version_id.nil? || component.version_id.empty?
272
- component.version_id = UUID.new.generate
273
- puts "#{component.name} version id missing; creating new one"
274
- xlsx_worksheet.Range("C#{i}").value = component.version_id
275
- end
276
-
277
- component.headers = headers
278
- component.values = xlsx_worksheet.Range("A#{i}:#{max_col}#{i}").value
279
- worksheet.components << component
280
- end
281
-
282
- @worksheets << worksheet
283
-
284
- puts "[ComponentSpreadsheet] Finished parsing components of type #{worksheet.name}"
285
- end
286
- end
287
- end # module BCL
@@ -1,528 +0,0 @@
1
- ######################################################################
2
- # Copyright (c) 2008-2019, Alliance for Sustainable Energy.
3
- # All rights reserved.
4
- #
5
- # This library is free software; you can redistribute it and/or
6
- # modify it under the terms of the GNU Lesser General Public
7
- # License as published by the Free Software Foundation; either
8
- # version 2.1 of the License, or (at your option) any later version.
9
- #
10
- # This library is distributed in the hope that it will be useful,
11
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
- # Lesser General Public License for more details.
14
- #
15
- # You should have received a copy of the GNU Lesser General Public
16
- # License along with this library; if not, write to the Free Software
17
- # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
- ######################################################################
19
-
20
- $have_win32ole = false
21
-
22
- if RUBY_PLATFORM =~ /mswin|mingw|cygwin/
23
- begin
24
- # apparently this is not a gem
25
- require 'win32ole'
26
- mod = WIN32OLE
27
- $have_win32ole = true
28
- rescue NameError
29
- # do not have win32ole
30
- end
31
- end
32
-
33
- module BCL
34
- # each TagStruct represents a node in the taxonomy tree
35
- TagStruct = Struct.new(:level_hierarchy, :name, :description, :parent_tag, :child_tags, :terms)
36
-
37
- # each TermStruct represents a row in the master taxonomy
38
- TermStruct = Struct.new(:first_level, :second_level, :third_level, :level_hierarchy, :name, :description,
39
- :abbr, :data_type, :enums, :ip_written, :ip_symbol, :ip_mask, :si_written, :si_symbol, :si_mask,
40
- :unit_conversion, :default_val, :min_val, :max_val, :allow_multiple, :row, :tp_include,
41
- :tp_required, :tp_use_in_search, :tp_use_in_facets, :tp_show_data_to_data_users, :tp_third_party_testing,
42
- :tp_additional_web_dev_info, :tp_additional_data_user_info, :tp_additional_data_submitter_info)
43
-
44
- # class for parsing, validating, and querying the master taxonomy document
45
- class MasterTaxonomy
46
- # parse the master taxonomy document
47
- def initialize(xlsx_path = nil, sort_alpha = false)
48
- @sort_alphabetical = sort_alpha
49
-
50
- # hash of level_taxonomy to tag
51
- @tag_hash = {}
52
-
53
- if xlsx_path.nil?
54
- # load from the current taxonomy
55
- path = current_taxonomy_path
56
- puts "Loading current taxonomy from #{path}"
57
- File.open(path, 'r') do |file|
58
- @tag_hash = Marshal.load(file)
59
- end
60
- else
61
- xlsx_path = Pathname.new(xlsx_path).realpath.to_s
62
- puts "Loading taxonomy file #{xlsx_path}"
63
-
64
- # WINDOWS ONLY SECTION BECAUSE THIS USES WIN32OLE
65
- if $have_win32ole
66
- begin
67
- excel = WIN32OLE.new('Excel.Application')
68
- xlsx = excel.Workbooks.Open(xlsx_path)
69
- terms_worksheet = xlsx.Worksheets('Terms')
70
- parse_terms(terms_worksheet)
71
- ensure
72
- # not really saving just pretending so don't get prompted on quit
73
- xlsx.saved = true
74
- excel.Quit
75
- WIN32OLE.ole_free(excel)
76
- excel.ole_free
77
- xlsx = nil
78
- excel = nil
79
- GC.start
80
- end
81
- else # if $have_win32ole
82
- puts "MasterTaxonomy class requires 'win32ole' to parse master taxonomy document."
83
- puts 'MasterTaxonomy may also be stored and loaded from JSON if your platform does not support win32ole.'
84
- end # if $have_win32ole
85
- end
86
- end
87
-
88
- # save the current taxonomy
89
- def save_as_current_taxonomy(path = nil)
90
- path ||= current_taxonomy_path
91
- puts "Saving current taxonomy to #{path}"
92
- # this is really not JSON... it is a persisted format of ruby
93
- File.open(path, 'w') do |file|
94
- Marshal.dump(@tag_hash, file)
95
- end
96
- end
97
-
98
- # write taxonomy to xml
99
- def write_xml(path, output_type = 'tpex')
100
- root_tag = @tag_hash['']
101
-
102
- if root_tag.nil?
103
- puts 'Cannot find root tag'
104
- return false
105
- end
106
-
107
- File.open(path, 'w') do |file|
108
- xml = Builder::XmlMarkup.new(target: file, indent: 2)
109
-
110
- # setup the xml file
111
- xml.instruct!(:xml, version: '1.0', encoding: 'UTF-8')
112
- xml.schema('xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance') do
113
- write_tag_to_xml(root_tag, 0, xml, output_type)
114
- end
115
- end
116
- end
117
-
118
- # get all terms for a given tag
119
- # this includes terms that are inherited from parent levels
120
- # e.g. master_taxonomy.get_terms("Space Use.Lighting.Lamp Ballast")
121
- def get_terms(tag)
122
- terms = tag.terms
123
-
124
- parent_tag = tag.parent_tag
125
- until parent_tag.nil?
126
- terms.concat(parent_tag.terms)
127
- parent_tag = parent_tag.parent_tag
128
- end
129
-
130
- # sort the terms as they come out
131
- result = terms.uniq
132
- if !@sort_alphabetical
133
- result = result.sort_by(&:row)
134
- else
135
- result = result.sort_by(&:name)
136
- end
137
-
138
- result
139
- end
140
-
141
- # check that the given component is conforms with the master taxonomy
142
- def check_component(component)
143
- valid = true
144
- tag = nil
145
-
146
- # see if we can find the component's tag in the taxonomy
147
- tags = component.tags
148
- if tags.empty?
149
- puts '[Check Component ERROR] Component does not have any tags'
150
- valid = false
151
- elsif tags.size > 1
152
- puts '[Check Component ERROR] Component has multiple tags'
153
- valid = false
154
- else
155
- tag = @tag_hash[tags[0].descriptor]
156
- unless tag
157
- puts "[Check Component ERROR] Cannot find #{tags[0].descriptor} in the master taxonomy"
158
- valid = false
159
- end
160
- end
161
-
162
- unless tag
163
- return false
164
- end
165
-
166
- terms = get_terms(tag)
167
-
168
- # TODO: check for all required attributes
169
- terms.each do |_term|
170
- # if term.required
171
- # make sure we find attribute
172
- # end
173
- end
174
-
175
- # check that all attributes are allowed
176
- component.attributes.each do |attribute|
177
- term = nil
178
- terms.each do |t|
179
- if t.name == attribute.name
180
- term = t
181
- break
182
- end
183
- end
184
-
185
- unless term
186
- puts "[Check Component ERROR] Cannot find term for #{attribute.name} in #{tag.level_hierarchy}"
187
- valid = false
188
- next
189
- end
190
-
191
- # TODO: validate value, datatype, units
192
- end
193
-
194
- valid
195
- end
196
-
197
- private
198
-
199
- def current_taxonomy_path
200
- File.dirname(__FILE__) + '/current_taxonomy.json'
201
- end
202
-
203
- def parse_terms(terms_worksheet)
204
- # check header
205
- header_error = validate_terms_header(terms_worksheet)
206
- if header_error
207
- raise 'Header Error on Terms Worksheet'
208
- end
209
-
210
- # add root tag
211
- root_terms = []
212
- root_terms << TermStruct.new('', '', '', '', 'OpenStudio Type', 'Type of OpenStudio Object')
213
- root_terms[0].row = 0
214
- # root_terms << TermStruct.new()
215
- root_tag = TagStruct.new('', 'root', 'Root of the taxonomy', nil, [], root_terms)
216
- @tag_hash[''] = root_tag
217
-
218
- ### puts "**** tag hash: #{@tag_hash}"
219
-
220
- # find number of rows by parsing until hit empty value in first column
221
- row_num = 3
222
- loop do
223
- term = parse_term(terms_worksheet, row_num)
224
- if term.nil?
225
- break
226
- end
227
-
228
- add_term(term)
229
-
230
- row_num += 1
231
- end
232
-
233
- # sort the tag tree
234
- sort_tag(root_tag)
235
-
236
- # check the tag tree
237
- check_tag(root_tag)
238
- end
239
-
240
- def validate_terms_header(terms_worksheet)
241
- test_arr = []
242
- test_arr << { 'name' => 'First Level', 'strict' => true }
243
- test_arr << { 'name' => 'Second Level', 'strict' => true }
244
- test_arr << { 'name' => 'Third Level', 'strict' => true }
245
- test_arr << { 'name' => 'Level Hierarchy', 'strict' => true }
246
- test_arr << { 'name' => 'Term', 'strict' => true }
247
- test_arr << { 'name' => 'Abbr', 'strict' => true }
248
- test_arr << { 'name' => 'Description', 'strict' => true }
249
- test_arr << { 'name' => 'Data Type', 'strict' => true }
250
- test_arr << { 'name' => 'Allow Multiple', 'strict' => true }
251
- test_arr << { 'name' => 'Enumerations', 'strict' => true }
252
- test_arr << { 'name' => 'IP Units Written Out', 'strict' => true }
253
- test_arr << { 'name' => 'IP Units Symbol', 'strict' => true }
254
- test_arr << { 'name' => 'IP Display Mask', 'strict' => true }
255
- test_arr << { 'name' => 'SI Units Written Out', 'strict' => true }
256
- test_arr << { 'name' => 'SI Units Symbol', 'strict' => true }
257
- test_arr << { 'name' => 'SI Display Mask', 'strict' => true }
258
- test_arr << { 'name' => 'Unit Conversion', 'strict' => true }
259
- test_arr << { 'name' => 'Default', 'strict' => true }
260
- test_arr << { 'name' => 'Min', 'strict' => true }
261
- test_arr << { 'name' => 'Max', 'strict' => true }
262
- test_arr << { 'name' => 'Source', 'strict' => true }
263
- test_arr << { 'name' => 'Review State', 'strict' => true }
264
- test_arr << { 'name' => 'General Comments', 'strict' => true }
265
- test_arr << { 'name' => 'Requested By / Project', 'strict' => true }
266
- test_arr << { 'name' => 'Include in TPE', 'strict' => false }
267
- test_arr << { 'name' => 'Required for Adding a New Product', 'strict' => false }
268
- test_arr << { 'name' => 'Use as a Column Header in Search Results', 'strict' => false }
269
- test_arr << { 'name' => 'Allow Users to Filter with this Facet', 'strict' => false }
270
- test_arr << { 'name' => 'Show Data to Data Users', 'strict' => false }
271
- test_arr << { 'name' => 'Additional Instructions for Web Developers', 'strict' => false }
272
- test_arr << { 'name' => 'Related Third Party Testing Standards', 'strict' => false }
273
- test_arr << { 'name' => 'Additional Guidance to Data Submitters', 'strict' => false }
274
- test_arr << { 'name' => 'Additional Guidance to Data Users', 'strict' => false }
275
-
276
- parse = true
277
- col = 1
278
- while parse
279
- if terms_worksheet.Columns(col).Rows(2).Value.nil? || col > test_arr.size
280
- parse = false
281
- else
282
- unless terms_worksheet.Columns(col).Rows(2).Value == test_arr[col - 1]['name']
283
- if test_arr[col - 1]['strict']
284
- raise "[ERROR] Header does not match: #{col}: '#{terms_worksheet.Columns(col).Rows(2).Value} <> #{test_arr[col - 1]['name']}'"
285
- else
286
- puts "[WARNING] Header does not match: #{col}: '#{terms_worksheet.Columns(col).Rows(2).Value} <> #{test_arr[col - 1]['name']}'"
287
- end
288
- end
289
- end
290
- col += 1
291
- end
292
- end
293
-
294
- def parse_term(terms_worksheet, row)
295
- term = TermStruct.new
296
- term.row = row
297
- term.first_level = terms_worksheet.Columns(1).Rows(row).Value
298
- term.second_level = terms_worksheet.Columns(2).Rows(row).Value
299
- term.third_level = terms_worksheet.Columns(3).Rows(row).Value
300
- term.level_hierarchy = terms_worksheet.Columns(4).Rows(row).Value
301
- term.name = terms_worksheet.Columns(5).Rows(row).Value
302
- term.abbr = terms_worksheet.Columns(6).Rows(row).Value
303
- term.description = terms_worksheet.Columns(7).Rows(row).Value
304
- term.data_type = terms_worksheet.Columns(8).Rows(row).Value
305
- term.allow_multiple = terms_worksheet.Columns(9).Rows(row).Value
306
- term.enums = terms_worksheet.Columns(10).Rows(row).Value
307
- term.ip_written = terms_worksheet.Columns(11).Rows(row).Value
308
- term.ip_symbol = terms_worksheet.Columns(12).Rows(row).Value
309
- term.ip_mask = terms_worksheet.Columns(13).Rows(row).Value
310
- term.si_written = terms_worksheet.Columns(14).Rows(row).Value
311
- term.si_symbol = terms_worksheet.Columns(15).Rows(row).Value
312
- term.si_mask = terms_worksheet.Columns(16).Rows(row).Value
313
- term.unit_conversion = terms_worksheet.Columns(17).Rows(row).Value
314
- term.default_val = terms_worksheet.Columns(18).Rows(row).Value
315
- term.min_val = terms_worksheet.Columns(19).Rows(row).Value
316
- term.max_val = terms_worksheet.Columns(20).Rows(row).Value
317
-
318
- # custom TPex Columns
319
- term.tp_include = terms_worksheet.Columns(25).Rows(row).Value
320
- term.tp_required = terms_worksheet.Columns(26).Rows(row).Value
321
- term.tp_use_in_search = terms_worksheet.Columns(27).Rows(row).Value
322
- term.tp_use_in_facets = terms_worksheet.Columns(28).Rows(row).Value
323
- term.tp_show_data_to_data_users = terms_worksheet.Columns(29).Rows(row).Value
324
- term.tp_additional_web_dev_info = terms_worksheet.Columns(30).Rows(row).Value
325
- term.tp_third_party_testing = terms_worksheet.Columns(31).Rows(row).Value
326
- term.tp_additional_data_submitter_info = terms_worksheet.Columns(32).Rows(row).Value
327
- term.tp_additional_data_user_info = terms_worksheet.Columns(33).Rows(row).Value
328
-
329
- # trigger to quit parsing the xcel doc
330
- if term.first_level.nil? || term.first_level.empty?
331
- return nil
332
- end
333
-
334
- term
335
- end
336
-
337
- def add_term(term)
338
- level_hierarchy = term.level_hierarchy
339
-
340
- # create the tag
341
- tag = @tag_hash[level_hierarchy]
342
-
343
- if tag.nil?
344
- tag = create_tag(level_hierarchy, term.description)
345
- end
346
-
347
- if term.name.nil? || term.name.strip.empty?
348
- # this row is really about the tag
349
- tag.description = term.description
350
-
351
- else
352
- # this row is about a term
353
- unless validate_term(term)
354
- return nil
355
- end
356
-
357
- tag.terms = [] if tag.terms.nil?
358
- tag.terms << term
359
- end
360
- end
361
-
362
- def create_tag(level_hierarchy, tag_description = '')
363
- # puts "create_tag called for #{level_hierarchy}"
364
-
365
- parts = level_hierarchy.split('.')
366
-
367
- name = parts[-1]
368
- parent_level = parts[0..-2].join('.')
369
-
370
- parent_tag = @tag_hash[parent_level]
371
- if parent_tag.nil?
372
- parent_tag = create_tag(parent_level)
373
- end
374
-
375
- description = tag_description
376
- child_tags = []
377
- terms = []
378
- tag = TagStruct.new(level_hierarchy, name, description, parent_tag, child_tags, terms)
379
-
380
- parent_tag.child_tags << tag
381
-
382
- @tag_hash[level_hierarchy] = tag
383
-
384
- tag
385
- end
386
-
387
- def sort_tag(tag)
388
- # tag.terms = tag.terms.sort {|x, y| x.level_hierarchy <=> y.level_hierarchy}
389
- tag.child_tags = tag.child_tags.sort_by(&:level_hierarchy)
390
- tag.child_tags.each { |child_tag| sort_tag(child_tag) }
391
-
392
- # tag.terms = tag.terms.sort {|x, y| x.name <=> y.name}
393
- # tag.child_tags = tag.child_tags.sort {|x, y| x.name <=> y.name}
394
- # tag.child_tags.each {|child_tag| sort_tag(child_tag) }
395
- end
396
-
397
- def check_tag(tag)
398
- if tag.description.nil? || tag.description.empty?
399
- puts "[check_tag] tag '#{tag.level_hierarchy}' has no description"
400
- end
401
-
402
- tag.terms.each { |term| check_term(term) }
403
- tag.child_tags.each { |child_tag| check_tag(child_tag) }
404
- end
405
-
406
- def validate_term(term)
407
- valid = true
408
-
409
- parts = term.level_hierarchy.split('.')
410
-
411
- if parts.empty?
412
- puts "Hierarchy parts empty, #{term.level_hierarchy}"
413
- valid = false
414
- end
415
-
416
- if parts.size >= 1 && !term.first_level == parts[0]
417
- puts "First level '#{term.first_level}' does not match level hierarchy '#{term.level_hierarchy}', skipping term"
418
- valid = false
419
- end
420
-
421
- if parts.size >= 2 && !term.second_level == parts[1]
422
- puts "Second level '#{term.second_level}' does not match level hierarchy '#{term.level_hierarchy}', skipping term"
423
- valid = false
424
- end
425
-
426
- if parts.size >= 3 && !term.third_level == parts[2]
427
- puts "Third level '#{term.third_level}' does not match level hierarchy '#{term.level_hierarchy}', skipping term"
428
- valid = false
429
- end
430
-
431
- if parts.size > 3
432
- puts "Hierarchy cannot have more than 3 parts '#{term.level_hierarchy}', skipping term"
433
- valid = false
434
- end
435
-
436
- unless term.data_type.nil?
437
- valid_types = ['double', 'integer', 'enum', 'file', 'string', 'autocomplete']
438
- if (term.data_type.downcase != term.data_type) || !valid_types.include?(term.data_type)
439
- puts "[ERROR] Term '#{term.name}' does not have a valid data type with '#{term.data_type}'"
440
- end
441
-
442
- if term.data_type.casecmp('enum').zero?
443
- if term.enums.nil? || term.enums == '' || term.enums.casecmp('no enum found').zero?
444
- puts "[ERROR] Term '#{term.name}' does not have valid enumerations"
445
- end
446
- end
447
- end
448
-
449
- valid
450
- end
451
-
452
- def check_term(term)
453
- if term.description.nil? || term.description.empty?
454
- # puts "[check_term] term '#{term.level_hierarchy}.#{term.name}' has no description"
455
- end
456
- end
457
-
458
- # write term to xml
459
- def write_terms_to_xml(tag, xml, output_type)
460
- terms = get_terms(tag)
461
- unless terms.empty?
462
- terms.each do |term|
463
- xml.term do
464
- xml.name term.name
465
- xml.abbr term.abbr unless term.abbr.nil?
466
- xml.description term.description unless term.description.nil?
467
- xml.data_type term.data_type unless term.data_type.nil?
468
- xml.allow_multiple term.allow_multiple unless term.allow_multiple.nil?
469
-
470
- if !term.enums.nil? && term.enums != ''
471
- xml.enumerations do
472
- out = term.enums.split('|')
473
- out.sort! if @sort_alphabetical
474
- out.each do |enum|
475
- xml.enumeration enum
476
- end
477
- end
478
- end
479
- xml.ip_written term.ip_written unless term.ip_written.nil?
480
- xml.ip_symbol term.ip_symbol unless term.ip_symbol.nil?
481
- xml.ip_mask term.ip_mask unless term.ip_mask.nil?
482
- xml.si_written term.si_written unless term.si_written.nil?
483
- xml.si_symbol term.si_symbol unless term.si_symbol.nil?
484
- xml.si_mask term.si_mask unless term.si_mask.nil?
485
- xml.row term.row unless term.row.nil?
486
- xml.unit_conversion term.unit_conversion unless term.unit_conversion.nil?
487
- xml.default_val term.default_val unless term.default_val.nil?
488
- xml.min_val term.min_val unless term.min_val.nil?
489
- xml.max_val term.max_val unless term.max_val.nil?
490
-
491
- if output_type == 'tpex'
492
- xml.tp_include term.tp_include unless term.tp_include.nil?
493
- xml.tp_required term.tp_required unless term.tp_required.nil?
494
- xml.tp_use_in_search term.tp_use_in_search unless term.tp_use_in_search.nil?
495
- xml.tp_use_in_facets term.tp_use_in_facets unless term.tp_use_in_facets.nil?
496
- xml.tp_show_data_to_data_users term.tp_show_data_to_data_users unless term.tp_show_data_to_data_users.nil?
497
- xml.tp_third_party_testing term.tp_third_party_testing unless term.tp_third_party_testing.nil?
498
- xml.tp_additional_web_dev_info term.tp_additional_web_dev_info unless term.tp_additional_web_dev_info.nil?
499
- xml.tp_additional_data_user_info term.tp_additional_data_user_info unless term.tp_additional_data_user_info.nil?
500
- xml.tp_additional_data_submitter_info term.tp_additional_data_submitter_info unless term.tp_additional_data_submitter_info.nil?
501
- end
502
- end
503
- end
504
- end
505
- end
506
-
507
- # write a tag to xml
508
- def write_tag_to_xml(tag, level, xml, output_type)
509
- level_string = "level_#{level}"
510
- xml.tag!(level_string) do
511
- s_temp = tag.name
512
- xml.name s_temp
513
- xml.description tag.description
514
-
515
- level += 1
516
-
517
- if tag.child_tags.empty?
518
- write_terms_to_xml(tag, xml, output_type)
519
- end
520
-
521
- child_tags = tag.child_tags
522
- child_tags.each do |child_tag|
523
- write_tag_to_xml(child_tag, level, xml, output_type)
524
- end
525
- end
526
- end
527
- end
528
- end # module BCL