bcl 0.3.3 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,561 +0,0 @@
1
- ######################################################################
2
- # Copyright (c) 2008-2013, 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
- require 'rubygems'
21
- require 'pathname'
22
- require 'fileutils'
23
- require 'builder' #gem install builder (creates xml files)
24
- require 'rbconfig'
25
-
26
- $have_win32ole = false
27
-
28
- if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
29
- begin
30
- # apparently this is not a gem
31
- require 'win32ole'
32
- mod = WIN32OLE
33
- $have_win32ole = true
34
- rescue NameError
35
- # do not have win32ole
36
- end
37
- end
38
-
39
- module BCL
40
-
41
- # each TagStruct represents a node in the taxonomy tree
42
- TagStruct = Struct.new(:level_hierarchy, :name, :description, :parent_tag, :child_tags, :terms)
43
-
44
- # each TermStruct represents a row in the master taxonomy
45
- TermStruct = Struct.new(:first_level, :second_level, :third_level, :level_hierarchy, :name, :description,
46
- :abbr, :data_type, :enums, :ip_written, :ip_symbol, :ip_mask, :si_written, :si_symbol, :si_mask, :unit_conversion, :default_val, :min_val, :max_val, :allow_multiple, :row, :tp_include, :tp_required, :tp_use_in_search, :tp_use_in_facets, :tp_show_data_to_data_users, :tp_third_party_testing, :tp_additional_web_dev_info, :tp_additional_data_user_info, :tp_additional_data_submitter_info)
47
-
48
-
49
- # class for parsing, validating, and querying the master taxonomy document
50
- class MasterTaxonomy
51
-
52
- # parse the master taxonomy document
53
- def initialize(xlsx_path = nil, sort_alpha = false)
54
- @sort_alphabetical = sort_alpha
55
-
56
- # hash of level_taxonomy to tag
57
- @tag_hash = Hash.new
58
-
59
- if xlsx_path.nil?
60
- # load from the current taxonomy
61
- path = current_taxonomy_path
62
- puts "Loading current taxonomy from #{path}"
63
- File.open(path, 'r') do |file|
64
- @tag_hash = Marshal.load(file)
65
- end
66
- else
67
- xlsx_path = Pathname.new(xlsx_path).realpath.to_s
68
- puts "Loading taxonomy file #{xlsx_path}"
69
-
70
- # WINDOWS ONLY SECTION BECAUSE THIS USES WIN32OLE
71
- if $have_win32ole
72
- begin
73
- excel = WIN32OLE::new('Excel.Application')
74
- xlsx = excel.Workbooks.Open(xlsx_path)
75
- terms_worksheet = xlsx.Worksheets("Terms")
76
- parse_terms(terms_worksheet)
77
- ensure
78
- # not really saving just pretending so don't get prompted on quit
79
- xlsx.saved = true
80
- excel.Quit
81
- WIN32OLE.ole_free(excel)
82
- excel.ole_free
83
- xlsx=nil
84
- excel=nil
85
- GC.start
86
- end
87
- else # if $have_win32ole
88
- puts "MasterTaxonomy class requires 'win32ole' to parse master taxonomy document."
89
- puts "MasterTaxonomy may also be stored and loaded from JSON if your platform does not support win32ole."
90
- end # if $have_win32ole
91
- end
92
- end
93
-
94
- # save the current taxonomy
95
- def save_as_current_taxonomy(path = nil)
96
- if not path
97
- path = current_taxonomy_path
98
- end
99
- puts "Saving current taxonomy to #{path}"
100
- # this is really not JSON... it is a persisted format of ruby
101
- File.open(path, 'w') do |file|
102
- Marshal.dump(@tag_hash, file)
103
- end
104
- end
105
-
106
- # write taxonomy to xml
107
- def write_xml(path, output_type = 'tpex')
108
-
109
- root_tag = @tag_hash[""]
110
-
111
- if root_tag.nil?
112
- puts "Cannot find root tag"
113
- return false
114
- end
115
-
116
- File.open(path, 'w') do |file|
117
- xml = Builder::XmlMarkup.new(:target => file, :indent=>2)
118
-
119
- #setup the xml file
120
- xml.instruct!(:xml, :version=>"1.0", :encoding=>"UTF-8")
121
- xml.schema("xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance") {
122
- write_tag_to_xml(root_tag, 0, xml, output_type)
123
- }
124
- end
125
-
126
- end
127
-
128
- # get all terms for a given tag
129
- # this includes terms that are inherited from parent levels
130
- # e.g. master_taxonomy.get_terms("Space Use.Lighting.Lamp Ballast")
131
- def get_terms(tag)
132
-
133
- terms = tag.terms
134
-
135
- parent_tag = tag.parent_tag
136
- while not parent_tag.nil?
137
- terms.concat(parent_tag.terms)
138
- parent_tag = parent_tag.parent_tag
139
- end
140
-
141
-
142
- #sort the terms as they come out
143
- result = terms.uniq
144
- if !@sort_alphabetical
145
- result = result.sort {|x, y| x.row <=> y.row}
146
- else
147
- result = result.sort {|x, y| x.name <=> y.name}
148
- end
149
-
150
- return result
151
- end
152
-
153
- # check that the given component is conforms with the master taxonomy
154
- def check_component(component)
155
- valid = true
156
- tag = nil
157
-
158
- # see if we can find the component's tag in the taxonomy
159
- tags = component.tags
160
- if tags.empty?
161
- puts "[Check Component ERROR] Component does not have any tags"
162
- valid = false
163
- elsif tags.size > 1
164
- puts "[Check Component ERROR] Component has multiple tags"
165
- valid = false
166
- else
167
- tag = @tag_hash[tags[0].descriptor]
168
- if not tag
169
- puts "[Check Component ERROR] Cannot find #{tags[0].descriptor} in the master taxonomy"
170
- valid = false
171
- end
172
- end
173
-
174
- if not tag
175
- return false
176
- end
177
-
178
- terms = get_terms(tag)
179
-
180
- # todo: check for all required attributes
181
- terms.each do |term|
182
- #if term.required
183
- # make sure we find attribute
184
- #end
185
- end
186
-
187
- # check that all attributes are allowed
188
- component.attributes.each do |attribute|
189
-
190
- term = nil
191
- terms.each do |t|
192
- if t.name == attribute.name
193
- term = t
194
- break
195
- end
196
- end
197
-
198
- if not term
199
- puts "[Check Component ERROR] Cannot find term for #{attribute.name} in #{tag.level_hierarchy}"
200
- valid = false
201
- next
202
- end
203
-
204
- # todo: validate value, datatype, units
205
-
206
- end
207
-
208
- return valid
209
- end
210
-
211
- private
212
-
213
- def current_taxonomy_path
214
- return File.dirname(__FILE__) + "/current_taxonomy.json"
215
- end
216
-
217
- def parse_terms(terms_worksheet)
218
-
219
- # check header
220
- header_error = validate_terms_header(terms_worksheet)
221
- if header_error
222
- raise "Header Error on Terms Worksheet"
223
- end
224
-
225
- # add root tag
226
- root_terms = []
227
- root_terms << TermStruct.new("", "", "", "", "OpenStudio Type", "Type of OpenStudio Object")
228
- root_terms[0].row = 0
229
- #root_terms << TermStruct.new()
230
- root_tag = TagStruct.new("", "root", "Root of the taxonomy", nil, [], root_terms)
231
- @tag_hash[""] = root_tag
232
-
233
- ### puts "**** tag hash: #{@tag_hash}"
234
-
235
- # find number of rows by parsing until hit empty value in first column
236
- row_num = 3
237
- while true do
238
- term = parse_term(terms_worksheet, row_num)
239
- if term.nil?
240
- break
241
- end
242
-
243
- add_term(term)
244
-
245
- row_num += 1
246
- end
247
-
248
- # sort the tag tree
249
- sort_tag(root_tag)
250
-
251
- # check the tag tree
252
- check_tag(root_tag)
253
-
254
- end
255
-
256
-
257
- def validate_terms_header(terms_worksheet)
258
- test_arr = []
259
- test_arr << {"name"=>"First Level", "strict"=>true}
260
- test_arr << {"name"=>"Second Level", "strict"=>true}
261
- test_arr << {"name"=>"Third Level", "strict"=>true}
262
- test_arr << {"name"=>"Level Hierarchy", "strict"=>true}
263
- test_arr << {"name"=>"Term", "strict"=>true}
264
- test_arr << {"name"=>"Abbr", "strict"=>true}
265
- test_arr << {"name"=>"Description", "strict"=>true}
266
- test_arr << {"name"=>"Data Type", "strict"=>true}
267
- test_arr << {"name"=>"Allow Multiple", "strict"=>true}
268
- test_arr << {"name"=>"Enumerations", "strict"=>true}
269
- test_arr << {"name"=>"IP Units Written Out", "strict"=>true}
270
- test_arr << {"name"=>"IP Units Symbol", "strict"=>true}
271
- test_arr << {"name"=>"IP Display Mask", "strict"=>true}
272
- test_arr << {"name"=>"SI Units Written Out", "strict"=>true}
273
- test_arr << {"name"=>"SI Units Symbol", "strict"=>true}
274
- test_arr << {"name"=>"SI Display Mask", "strict"=>true}
275
- test_arr << {"name"=>"Unit Conversion", "strict"=>true}
276
- test_arr << {"name"=>"Default", "strict"=>true}
277
- test_arr << {"name"=>"Min", "strict"=>true}
278
- test_arr << {"name"=>"Max", "strict"=>true}
279
- test_arr << {"name"=>"Source", "strict"=>true}
280
- test_arr << {"name"=>"Review State", "strict"=>true}
281
- test_arr << {"name"=>"General Comments", "strict"=>true}
282
- test_arr << {"name"=>"Requested By / Project", "strict"=>true}
283
- test_arr << {"name"=>"Include in TPE", "strict"=>false}
284
- test_arr << {"name"=>"Required for Adding a New Product", "strict"=>false}
285
- <<<<<<< .mine
286
- test_arr << {"name"=>"Use as a Column Header in Search Results", "strict"=>false}
287
- test_arr << {"name"=>"Allow Users to Filter with this Facet", "strict"=>false}
288
- test_arr << {"name"=>"Show/Hide Data from Data Users", "strict"=>false}
289
- =======
290
- test_arr << {"name"=>"Use as a Column Header in Search Results", "strict"=>false}
291
- test_arr << {"name"=>"Allow Users to Filter with this Facet", "strict"=>false}
292
- test_arr << {"name"=>"Show Data to Data Users", "strict"=>false}
293
- >>>>>>> .r727
294
- test_arr << {"name"=>"Additional Instructions for Web Developers", "strict"=>false}
295
- test_arr << {"name"=>"Related Third Party Testing Standards", "strict"=>false}
296
- test_arr << {"name"=>"Additional Guidance to Data Submitters", "strict"=>false}
297
- test_arr << {"name"=>"Additional Guidance to Data Users", "strict"=>false}
298
-
299
-
300
- parse = true
301
- col = 1
302
- while parse
303
- if terms_worksheet.Columns(col).Rows(2).Value.nil? || col > test_arr.size
304
- parse = false
305
- else
306
- if not terms_worksheet.Columns(col).Rows(2).Value == test_arr[col-1]["name"]
307
- if test_arr[col-1]["strict"]
308
- raise "[ERROR] Header does not match: #{col}: '#{terms_worksheet.Columns(col).Rows(2).Value} <> #{test_arr[col-1]["name"]}'"
309
- else
310
- puts "[WARNING] Header does not match: #{col}: '#{terms_worksheet.Columns(col).Rows(2).Value} <> #{test_arr[col-1]["name"]}'"
311
- end
312
- end
313
- end
314
- col += 1
315
- end
316
- end
317
-
318
- def parse_term(terms_worksheet, row)
319
-
320
- term = TermStruct.new
321
- term.row = row
322
- term.first_level = terms_worksheet.Columns(1).Rows(row).Value
323
- term.second_level = terms_worksheet.Columns(2).Rows(row).Value
324
- term.third_level = terms_worksheet.Columns(3).Rows(row).Value
325
- term.level_hierarchy = terms_worksheet.Columns(4).Rows(row).Value
326
- term.name = terms_worksheet.Columns(5).Rows(row).Value
327
- term.abbr = terms_worksheet.Columns(6).Rows(row).Value
328
- term.description = terms_worksheet.Columns(7).Rows(row).Value
329
- term.data_type = terms_worksheet.Columns(8).Rows(row).Value
330
- term.allow_multiple = terms_worksheet.Columns(9).Rows(row).Value
331
- term.enums = terms_worksheet.Columns(10).Rows(row).Value
332
- term.ip_written = terms_worksheet.Columns(11).Rows(row).Value
333
- term.ip_symbol = terms_worksheet.Columns(12).Rows(row).Value
334
- term.ip_mask = terms_worksheet.Columns(13).Rows(row).Value
335
- term.si_written = terms_worksheet.Columns(14).Rows(row).Value
336
- term.si_symbol = terms_worksheet.Columns(15).Rows(row).Value
337
- term.si_mask = terms_worksheet.Columns(16).Rows(row).Value
338
- term.unit_conversion = terms_worksheet.Columns(17).Rows(row).Value
339
- term.default_val = terms_worksheet.Columns(18).Rows(row).Value
340
- term.min_val = terms_worksheet.Columns(19).Rows(row).Value
341
- term.max_val = terms_worksheet.Columns(20).Rows(row).Value
342
-
343
- #custom TPex Columns
344
- term.tp_include = terms_worksheet.Columns(25).Rows(row).Value
345
- term.tp_required = terms_worksheet.Columns(26).Rows(row).Value
346
- term.tp_use_in_search = terms_worksheet.Columns(27).Rows(row).Value
347
- term.tp_use_in_facets = terms_worksheet.Columns(28).Rows(row).Value
348
- term.tp_show_data_to_data_users = terms_worksheet.Columns(29).Rows(row).Value
349
- term.tp_additional_web_dev_info = terms_worksheet.Columns(30).Rows(row).Value
350
- term.tp_third_party_testing = terms_worksheet.Columns(31).Rows(row).Value
351
- term.tp_additional_data_submitter_info = terms_worksheet.Columns(32).Rows(row).Value
352
- term.tp_additional_data_user_info = terms_worksheet.Columns(33).Rows(row).Value
353
-
354
- # trigger to quit parsing the xcel doc
355
- if term.first_level.nil? or term.first_level.empty?
356
- return nil
357
- end
358
-
359
- return term
360
- end
361
-
362
- def add_term(term)
363
-
364
- level_hierarchy = term.level_hierarchy
365
-
366
- # create the tag
367
- tag = @tag_hash[level_hierarchy]
368
-
369
- if tag.nil?
370
- tag = create_tag(level_hierarchy, term.description)
371
- end
372
-
373
- if term.name.nil? or term.name.strip.empty?
374
- # this row is really about the tag
375
- tag.description = term.description
376
-
377
- else
378
- # this row is about a term
379
- if not validate_term(term)
380
- return nil
381
- end
382
-
383
- tag.terms = [] if tag.terms.nil?
384
- tag.terms << term
385
- end
386
- end
387
-
388
- def create_tag(level_hierarchy, tag_description="")
389
-
390
- #puts "create_tag called for #{level_hierarchy}"
391
-
392
- parts = level_hierarchy.split('.')
393
-
394
- name = parts[-1]
395
- parent_level = parts[0..-2].join('.')
396
-
397
- parent_tag = @tag_hash[parent_level]
398
- if parent_tag.nil?
399
- parent_tag = create_tag(parent_level)
400
- end
401
-
402
- description = tag_description
403
- child_tags = []
404
- terms = []
405
- tag = TagStruct.new(level_hierarchy, name, description, parent_tag, child_tags, terms)
406
-
407
- parent_tag.child_tags << tag
408
-
409
- @tag_hash[level_hierarchy] = tag
410
-
411
- return tag
412
- end
413
-
414
- def sort_tag(tag)
415
- #tag.terms = tag.terms.sort {|x, y| x.level_hierarchy <=> y.level_hierarchy}
416
- tag.child_tags = tag.child_tags.sort {|x, y| x.level_hierarchy <=> y.level_hierarchy}
417
- tag.child_tags.each {|child_tag| sort_tag(child_tag) }
418
-
419
- #tag.terms = tag.terms.sort {|x, y| x.name <=> y.name}
420
- #tag.child_tags = tag.child_tags.sort {|x, y| x.name <=> y.name}
421
- #tag.child_tags.each {|child_tag| sort_tag(child_tag) }
422
- end
423
-
424
- def check_tag(tag)
425
-
426
- if tag.description.nil? or tag.description.empty?
427
- puts "[check_tag] tag '#{tag.level_hierarchy}' has no description"
428
- end
429
-
430
- tag.terms.each {|term| check_term(term) }
431
- tag.child_tags.each {|child_tag| check_tag(child_tag) }
432
- end
433
-
434
- def validate_term(term)
435
- valid = true
436
-
437
- parts = term.level_hierarchy.split('.')
438
-
439
- if parts.empty?
440
- puts "Hierarchy parts empty, #{term.level_hierarchy}"
441
- valid = false
442
- end
443
-
444
- if parts.size >= 1 and not term.first_level == parts[0]
445
- puts "First level '#{term.first_level}' does not match level hierarchy '#{term.level_hierarchy}', skipping term"
446
- valid = false
447
- end
448
-
449
- if parts.size >= 2 and not term.second_level == parts[1]
450
- puts "Second level '#{term.second_level}' does not match level hierarchy '#{term.level_hierarchy}', skipping term"
451
- valid = false
452
- end
453
-
454
- if parts.size >= 3 and not term.third_level == parts[2]
455
- puts "Third level '#{term.third_level}' does not match level hierarchy '#{term.level_hierarchy}', skipping term"
456
- valid = false
457
- end
458
-
459
- if parts.size > 3
460
- puts "Hierarchy cannot have more than 3 parts '#{term.level_hierarchy}', skipping term"
461
- valid = false
462
- end
463
-
464
- if !term.data_type.nil?
465
- valid_types = ["double", "integer", "enum", "file", "string", "autocomplete"]
466
- if (term.data_type.downcase != term.data_type) || !valid_types.include?(term.data_type)
467
- puts "[ERROR] Term '#{term.name}' does not have a valid data type with '#{term.data_type}'"
468
- end
469
-
470
- if term.data_type.downcase == "enum"
471
- if term.enums.nil? || term.enums == "" || term.enums.downcase == "no enum found"
472
- puts "[ERROR] Term '#{term.name}' does not have valid enumerations"
473
- end
474
- end
475
- end
476
-
477
- return valid
478
- end
479
-
480
- def check_term(term)
481
- if term.description.nil? or term.description.empty?
482
- #puts "[check_term] term '#{term.level_hierarchy}.#{term.name}' has no description"
483
- end
484
- end
485
-
486
- # write term to xml
487
- def write_terms_to_xml(tag, xml, output_type)
488
- terms = get_terms(tag)
489
- if terms.size > 0
490
- terms.each do |term|
491
- xml.term {
492
- xml.name term.name
493
- xml.abbr term.abbr if !term.abbr.nil?
494
- xml.description term.description if !term.description.nil?
495
- xml.data_type term.data_type if !term.data_type.nil?
496
- xml.allow_multiple term.allow_multiple if !term.allow_multiple.nil?
497
-
498
- if !term.enums.nil? && term.enums != ""
499
- xml.enumerations {
500
- out = term.enums.split("|")
501
- out.sort! if @sort_alphabetical
502
- out.each do |enum|
503
- xml.enumeration enum
504
- end
505
- }
506
- end
507
- xml.ip_written term.ip_written if !term.ip_written.nil?
508
- xml.ip_symbol term.ip_symbol if !term.ip_symbol.nil?
509
- xml.ip_mask term.ip_mask if !term.ip_mask.nil?
510
- xml.si_written term.si_written if !term.si_written.nil?
511
- xml.si_symbol term.si_symbol if !term.si_symbol.nil?
512
- xml.si_mask term.si_mask if !term.si_mask.nil?
513
- xml.row term.row if !term.row.nil?
514
- xml.unit_conversion term.unit_conversion if !term.unit_conversion.nil?
515
- xml.default_val term.default_val if !term.default_val.nil?
516
- xml.min_val term.min_val if !term.min_val.nil?
517
- xml.max_val term.max_val if !term.max_val.nil?
518
-
519
- if output_type == 'tpex'
520
- xml.tp_include term.tp_include if !term.tp_include.nil?
521
- xml.tp_required term.tp_required if !term.tp_required.nil?
522
- xml.tp_use_in_search term.tp_use_in_search if !term.tp_use_in_search.nil?
523
- xml.tp_use_in_facets term.tp_use_in_facets if !term.tp_use_in_facets.nil?
524
- xml.tp_show_data_to_data_users term.tp_show_data_to_data_users if !term.tp_show_data_to_data_users.nil?
525
- xml.tp_third_party_testing term.tp_third_party_testing if !term.tp_third_party_testing.nil?
526
- xml.tp_additional_web_dev_info term.tp_additional_web_dev_info if !term.tp_additional_web_dev_info.nil?
527
- xml.tp_additional_data_user_info term.tp_additional_data_user_info if !term.tp_additional_data_user_info.nil?
528
- xml.tp_additional_data_submitter_info term.tp_additional_data_submitter_info if !term.tp_additional_data_submitter_info.nil?
529
- end
530
- }
531
- end
532
- end
533
- end
534
-
535
- # write a tag to xml
536
- def write_tag_to_xml(tag, level, xml, output_type)
537
- level_string = "level_#{level}"
538
- xml.tag!(level_string) {
539
- s_temp = tag.name
540
- xml.name s_temp
541
- xml.description tag.description
542
-
543
- level += 1
544
-
545
- if tag.child_tags.size == 0
546
- write_terms_to_xml(tag, xml, output_type)
547
- end
548
-
549
- child_tags = tag.child_tags
550
- child_tags.each do |child_tag|
551
- write_tag_to_xml(child_tag, level, xml, output_type)
552
- end
553
-
554
- }
555
- end
556
-
557
- end
558
-
559
- end # module BCL
560
-
561
-