bcl 0.6.0 → 0.6.1

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