bcl 0.2.3 → 0.2.9

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.
@@ -0,0 +1,94 @@
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 'enumerator'
24
+
25
+ require 'bcl/TarBall'
26
+
27
+ module BCL
28
+ module_function
29
+
30
+ def gather_components(component_dir)
31
+ #store the starting directory
32
+ current_dir = Dir.pwd
33
+
34
+ #an array to hold reporting info about the batches
35
+ gather_components_report = []
36
+
37
+ #go to the directory containing the components
38
+ Dir.chdir(component_dir)
39
+
40
+ #delete old gather files first
41
+ gather_dest_base = "components.tar.gz"
42
+ #File.delete("./gather/" + gather_dest_base) if File.exists?("./0_Package/" + gather_dest_base)
43
+
44
+ #copy all the components' tar.gz files into a single directory
45
+ targzs = Pathname.glob("./**/*.tar.gz")
46
+ targzs.each do |targz|
47
+ destination = "./0_Package/#{File.basename(targz.to_s)}"
48
+ #puts "copying #{targz.to_s} to #{destination}"
49
+ Dir.mkdir("./0_Package") unless File.directory?("./0_Package") #named so it will be at top of directory list
50
+ File.delete(destination) if File.exists?(destination)
51
+ FileUtils.cp(targz.to_s, destination)
52
+ end
53
+
54
+ #go into that directory
55
+ Dir.chdir("./0_Package")
56
+
57
+ #get a list of all the tar.gz files in the new directory
58
+ targzs = Pathname.glob("*.tar.gz")
59
+
60
+ #report the total number of components in the directory
61
+ gather_components_report << "Total components = #{targzs.length}"
62
+
63
+ #define an iterator to keep track of the number of batches
64
+ batch_num = 1
65
+
66
+ #package all the tar.gzs in the directory into a few master tar.gz files of 1000 components or less
67
+ targzs.each_slice(100) do |batch|
68
+
69
+ gather_components_report << " batch #{batch_num} contains #{batch.length} components"
70
+
71
+ #put all the paths in the batch into an array
72
+ paths = []
73
+ batch.each do |targz|
74
+ paths << File.basename(targz.to_s)
75
+ end
76
+
77
+ #path where the batch tarball is going
78
+ gather_dest = "0_Package_#{batch_num}_#{gather_dest_base}" #prefix to move to top of directory
79
+
80
+ #tar up the batch
81
+ tarball(gather_dest, paths)
82
+
83
+ batch_num += 1
84
+ end
85
+
86
+ #report out
87
+ puts gather_components_report
88
+
89
+ #change back to the directory where we started
90
+ Dir.chdir(current_dir)
91
+
92
+ end
93
+
94
+ end # module BCL
@@ -0,0 +1,461 @@
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
+ # Provides programmatic access to the component.xsd schema needed for
21
+ # generating the component information that will be uploaded to
22
+ # the Building Component Library.
23
+
24
+ require 'rubygems'
25
+
26
+ require 'pathname'
27
+ require 'csv'
28
+ require 'builder' #gem install builder (creates xml files)
29
+ require 'uuid' # gem install uuid
30
+ require 'fileutils'
31
+
32
+ require 'bcl/TarBall'
33
+
34
+ module BCL
35
+
36
+ SCHEMA_LOCATION = "component.xsd"
37
+
38
+ ProvStruct = Struct.new(:author, :datetime, :comment)
39
+ TagsStruct = Struct.new(:descriptor)
40
+ AttrStruct = Struct.new(:name, :value, :datatype, :units)
41
+ FileStruct = Struct.new(:version_software_program, :version_id, :fqp_file, :filename, :filetype)
42
+ #cost_type is an enumeration (not enforced) of installation, material, operations and maintenance,
43
+ #variable operations and maintenance, salvage
44
+ CostStruct = Struct.new(:cost_name, :cost_type, :category, :value, :interval,
45
+ :interval_units, :year, :location, :units, :currency, :source,
46
+ :reference_component_name, :reference_component_id)
47
+ ObjectStruct = Struct.new(:obj_type, :obj_instance)
48
+
49
+ class Component
50
+ attr_accessor :name
51
+ attr_accessor :uid
52
+ attr_accessor :comp_version_id
53
+ attr_accessor :description
54
+ attr_accessor :comment
55
+ attr_accessor :fidelity_level
56
+ attr_accessor :source_manufacturer
57
+ attr_accessor :source_model
58
+ attr_accessor :source_serial_no
59
+ attr_accessor :source_year
60
+ attr_accessor :source_url
61
+ attr_accessor :tags
62
+ attr_accessor :provenance
63
+ attr_accessor :attributes
64
+ attr_accessor :files
65
+ attr_accessor :costs
66
+ attr_accessor :objects
67
+
68
+ public
69
+
70
+ #the save path is where the component will be saved
71
+ def initialize(save_path)
72
+ @name = "" #this is also a unique identifier to the component...
73
+ @uid = UUID.new.generate
74
+ @comp_version_id = UUID.new.generate
75
+ @description = ""
76
+ @comment = ""
77
+ @fidelity_level = 0 #restricted to level_1 to level_5
78
+ @source_manufacturer = ""
79
+ @source_model = ""
80
+ @source_serial_no = ""
81
+ @source_year = ""
82
+ @source_url = ""
83
+
84
+ #these items have multiple instances
85
+ @provenance = []
86
+ @tags = []
87
+ @attributes = []
88
+ @files = []
89
+ @costs = []
90
+ @objects = [] #container for saving the idf/osm snippets
91
+
92
+ @path = save_path
93
+
94
+ #puts "[ComponentXml] " + @path
95
+ #need to hit a webservice to validate which tags and attributes are
96
+ #available (including units?)
97
+
98
+ #todo: validate against master taxonomy
99
+ end
100
+
101
+ def open_component(filename)
102
+ read_component_xml(filename)
103
+ end
104
+
105
+ # savefile, save the component xml along with
106
+ # the files that have been added to the object
107
+ def save_tar_gz(delete_files = true)
108
+ current_d = Dir.pwd
109
+ paths = []
110
+
111
+ save_component_xml
112
+
113
+ paths << "./component.xml"
114
+
115
+ #copy over the files to the directory
116
+ @files.each do |file|
117
+ src_path = Pathname.new(file.fqp_file)
118
+ dest_path = Pathname.new("#{resolve_path}/#{file.filename}")
119
+ if File.exists?(src_path)
120
+ if src_path == dest_path
121
+ #do nothing, file is already where it needs to be
122
+ else
123
+ #move the file where it needs to go
124
+ FileUtils.cp(src_path, dest_path)
125
+ end
126
+ else
127
+ puts "#{src_path} -> File does not exist"
128
+ end
129
+ paths << "./#{file.filename}"
130
+ end
131
+
132
+ #take all the files and tar.gz them -- name the file the same as
133
+ #the directory
134
+
135
+ Dir.chdir("#{resolve_path}")
136
+ destination = "#{@name.gsub(/\W/,'_').gsub(/___/,'_').gsub(/__/,'_').chomp('_').strip}.tar.gz"
137
+
138
+ File.delete(destination) if File.exists?(destination)
139
+
140
+ BCL.tarball(destination, paths)
141
+
142
+ Dir.chdir(current_d)
143
+
144
+ if (delete_files)
145
+ @files.each do |file|
146
+ if File.exists?(File.dirname(file.fqp_file))
147
+ puts "[ComponentXml] Deleting: #{File.dirname(file.fqp_file)}"
148
+ FileUtils.rm_rf(File.dirname(file.fqp_file))
149
+ end
150
+ end
151
+ end
152
+
153
+ #puts "[ComponentXml] " + Dir.pwd
154
+ end
155
+
156
+ def add_provenance(author, datetime, comment)
157
+ prov = ProvStruct.new
158
+ prov.author = author
159
+ prov.datetime = datetime
160
+ prov.comment = comment
161
+
162
+ @provenance << prov
163
+ end
164
+
165
+ def add_tag(tag_name)
166
+ tag = TagsStruct.new
167
+ tag.descriptor = tag_name
168
+
169
+ @tags << tag
170
+ end
171
+
172
+ def add_attribute(name, value, units, datatype = nil)
173
+ attr = AttrStruct.new
174
+ attr.name = name
175
+ attr.value = value
176
+
177
+ if !datatype.nil?
178
+ attribute.datatype = datatype
179
+ else
180
+ isint = true if Integer(value) rescue false
181
+ isfloat = true if Float(value) rescue false
182
+
183
+ if value.is_a? Fixnum
184
+ attr.datatype = "int"
185
+ elsif value.is_a? Float
186
+ attr.datatype = "float"
187
+ elsif isfloat
188
+ attr.datatype = "float"
189
+ elsif isint
190
+ attr.datatype = "int"
191
+ else
192
+ attr.datatype = "string"
193
+ end
194
+ end
195
+ attr.units = units
196
+
197
+ @attributes << attr
198
+ end
199
+
200
+ def add_file(version_sp, version_id, fqp_file, filename, filetype)
201
+ fs = FileStruct.new
202
+ fs.version_software_program = version_sp
203
+ fs.version_id = version_id
204
+ fs.fqp_file = fqp_file
205
+ fs.filename = filename
206
+ fs.filetype = filetype
207
+
208
+ @files << fs
209
+ end
210
+
211
+
212
+ def add_cost(cost_name, cost_type, category, value, units, interval, interval_units, year, location, currency,
213
+ source, reference_component_name, reference_component_id)
214
+ cs = CostStruct.new
215
+ cs.cost_name = cost_name
216
+ cs.cost_type = cost_type
217
+ cs.category = category
218
+ cs.value = value
219
+ cs.interval = interval
220
+ cs.interval_units = interval_units
221
+ cs.year = year
222
+ cs.location = location
223
+ cs.units = units
224
+ cs.currency = currency
225
+ cs.source = source
226
+ cs.reference_component_name = reference_component_name
227
+ cs.reference_component_id = reference_component_id
228
+
229
+ @costs << cs
230
+ end
231
+
232
+ def add_object(object_type, object_instance)
233
+ ob = ObjectStruct.new
234
+ ob.obj_type = object_type
235
+ ob.obj_instance = object_instance
236
+
237
+ @objects << ob
238
+ end
239
+
240
+ def resolve_path
241
+ FileUtils.mkdir_p(@path) unless File.directory?(@path)
242
+ new_path = "#{@path}/#{name.gsub(/\W/,'_').gsub(/___/,'_').gsub(/__/,'_').chomp('_').strip}"
243
+ FileUtils.mkdir_p(new_path) unless File.directory?(new_path)
244
+ result = new_path
245
+ end
246
+
247
+ def osm_resolve_path
248
+ FileUtils.mkdir_p(@path) unless File.directory?(@path)
249
+ new_path = "#{@path}/osm_#{name.gsub(/\W/,'_').gsub(/___/,'_').gsub(/__/,'_').chomp('_').strip}"
250
+ FileUtils.mkdir_p(new_path) unless File.directory?(new_path)
251
+ result = new_path
252
+ end
253
+
254
+ def osc_resolve_path
255
+ FileUtils.mkdir_p(@path) unless File.directory?(@path)
256
+ new_path = "#{@path}/osc_#{name.gsub(/\W/,'_').gsub(/___/,'_').gsub(/__/,'_').chomp('_').strip}"
257
+ FileUtils.mkdir_p(new_path) unless File.directory?(new_path)
258
+ result = new_path
259
+ end
260
+
261
+ def resolve_component_path(component_type)
262
+ FileUtils.mkdir_p(@path) unless File.directory?(@path)
263
+ new_path = @path + '/OpenStudio'
264
+ FileUtils.mkdir_p(new_path) unless File.directory?(new_path)
265
+ new_path = new_path + "/#{component_type}"
266
+ FileUtils.mkdir_p(new_path) unless File.directory?(new_path)
267
+ return new_path
268
+ end
269
+
270
+ def tmp_resolve_path
271
+ FileUtils.mkdir_p(@path) unless File.directory?(@path)
272
+ new_path = "#{@path}/tmp_#{name.gsub(/\W/,'_').gsub(/___/,'_').gsub(/__/,'_').chomp('_').strip}"
273
+ FileUtils.mkdir_p(new_path) unless File.directory?(new_path)
274
+ result = new_path
275
+ end
276
+
277
+
278
+ def create_os_component(osobj)
279
+ osobj.getCostLineItems.each do |os|
280
+ @costs.each do |cost|
281
+ #figure out costs for constructions
282
+ os.setMaterialCost(cost.value.to_f) if cost.category == "material"
283
+ if cost.category == "installation"
284
+ os.setInstallationCost(cost.value.to_f)
285
+ os.setExpectedLife(cost.interval.to_i)
286
+ end
287
+ os.setFixedOM(cost.value.to_f) if cost.category == "operations and maintenance"
288
+ os.setVariableOM(cost.value.to_f) if cost.category == "variable operations and maintenance"
289
+ os.setSalvageCost(cost.value.to_f) if cost.category == "salvage"
290
+ end
291
+ end
292
+ newcomp = osobj.createComponent
293
+
294
+ cd = newcomp.componentData
295
+ cd.setDescription(@description)
296
+ cd.setFidelityLevel(@fidelity_level)
297
+
298
+ at = newcomp.componentData.componentDataAttributes
299
+ @attributes.each do |attrib|
300
+ if (attrib.value.to_s != "") and (attrib.name.to_s != "")
301
+ if attrib.units != ""
302
+ at.addAttribute(tc(attrib.name), attrib.value, attrib.units)
303
+ else
304
+ at.addAttribute(tc(attrib.name), attrib.value)
305
+ end
306
+ end
307
+ end
308
+
309
+ tg = newcomp.componentData.componentDataTags
310
+ comp_tag = ""
311
+ @tags.each do |tag|
312
+ tg.addTag(tc(tag.descriptor))
313
+ if (tag.descriptor != "energyplus") and (tag.descriptor != "construction")
314
+ #create a map of component tags to directories
315
+ comp_tag = tag.descriptor
316
+ if comp_tag == "interior wall"
317
+ comp_tag = "interiorwalls"
318
+ elsif comp_tag == "exterior wall"
319
+ comp_tag = "exteriorwalls"
320
+ elsif comp_tag == "exterior slab"
321
+ comp_tag = "exteriorslabs"
322
+ elsif comp_tag == "exposed floor"
323
+ comp_tag = "exposedfloors"
324
+ elsif comp_tag == "attic floor"
325
+ comp_tag = "atticfloors"
326
+ elsif comp_tag == "roof"
327
+ comp_tag = "roofs"
328
+ elsif comp_tag == "door"
329
+ comp_tag = "doors"
330
+ elsif comp_tag == "skylight"
331
+ comp_tag = "skylights"
332
+ elsif comp_tag == "window"
333
+ comp_tag = "windows"
334
+ end
335
+ puts comp_tag
336
+ end
337
+ end
338
+
339
+ return newcomp
340
+ end
341
+
342
+ def save_component_xml(dir_path = resolve_path)
343
+ xmlfile = File.new(dir_path + '/component.xml', 'w')
344
+ comp_xml = Builder::XmlMarkup.new(:target => xmlfile, :indent=>2)
345
+
346
+ #setup the xml file
347
+ comp_xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
348
+ comp_xml.component("xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
349
+ "xsi:noNamespaceSchemaLocation"=>"#{SCHEMA_LOCATION}") {
350
+ comp_xml.name @name
351
+ comp_xml.uid @uid
352
+ comp_xml.version_id @comp_version_id
353
+ comp_xml.description @description if @description != ""
354
+ comp_xml.comment @comment if @comment != ""
355
+ comp_xml.fidelity_level @fidelity_level
356
+
357
+ comp_xml.provenances {
358
+ @provenance.each do |prov|
359
+ comp_xml.provenance {
360
+ comp_xml.author prov.author
361
+ comp_xml.datetime prov.datetime
362
+ comp_xml.comment prov.comment
363
+ }
364
+ end
365
+ }
366
+
367
+ comp_xml.tags {
368
+ @tags.each do |tag|
369
+ comp_xml.tag tag.descriptor
370
+ end
371
+ }
372
+
373
+ comp_xml.attributes {
374
+ @attributes.each do |attrib|
375
+ if (attrib.value.to_s != "") and (attrib.name.to_s != "") then
376
+ comp_xml.attribute {
377
+ comp_xml.name attrib.name
378
+ comp_xml.value attrib.value
379
+ comp_xml.datatype attrib.datatype
380
+ comp_xml.units attrib.units if attrib.units != ""
381
+ }
382
+ end
383
+ end
384
+ }
385
+
386
+ comp_xml.source {
387
+ comp_xml.manufacturer @source_manufacturer if @source_manufacturer != ""
388
+ comp_xml.model @source_model if @source_model != ""
389
+ comp_xml.serial_no @source_serial_no if @source_serial_no != ""
390
+ comp_xml.year @source_year if @source_year != ""
391
+ comp_xml.url @source_url if @source_url != ""
392
+ }
393
+
394
+ if not @files.nil?
395
+ comp_xml.files {
396
+ @files.each do |file|
397
+ comp_xml.file {
398
+ comp_xml.version {
399
+ comp_xml.software_program file.version_software_program
400
+ comp_xml.identifier file.version_id
401
+ }
402
+
403
+ comp_xml.filename file.filename
404
+ comp_xml.filetype file.filetype
405
+ }
406
+ end
407
+ }
408
+ end
409
+
410
+ #check if we should write out costs, don't write if all values are 0 or nil
411
+ #DLM: schema always expects costs
412
+ write_costs = true
413
+ #if not @costs.nil?
414
+ # @costs.each do |cost|
415
+ # if (cost.value.nil?) && (not cost.value == 0)
416
+ # write_costs = true
417
+ # break
418
+ # end
419
+ # end
420
+ #end
421
+
422
+ if write_costs
423
+ comp_xml.costs {
424
+ @costs.each do |cost|
425
+ comp_xml.cost {
426
+ comp_xml.instance_name cost.cost_name
427
+ comp_xml.cost_type cost.cost_type
428
+ comp_xml.category cost.category
429
+ comp_xml.value cost.value
430
+ comp_xml.units cost.units if cost.units != ""
431
+ comp_xml.interval cost.interval if cost.interval != ""
432
+ comp_xml.interval_units cost.interval_units if cost.interval_units != ""
433
+ comp_xml.year cost.year if cost.year != ""
434
+ comp_xml.currency cost.currency if cost.currency != ""
435
+ comp_xml.source cost.source if cost.source != ""
436
+ comp_xml.reference_component_name cost.reference_component_name if cost.reference_component_name != ""
437
+ comp_xml.reference_component_id cost.reference_component_id if cost.reference_component_id != ""
438
+ }
439
+ end
440
+ }
441
+ end
442
+
443
+ }
444
+
445
+ xmlfile.close
446
+ end
447
+
448
+ private
449
+
450
+ #return the title case of the string
451
+ def tc(input)
452
+ val = input.gsub(/\b\w/){$&.upcase}
453
+ if val.downcase == "energyplus"
454
+ val = "EnergyPlus"
455
+ end
456
+ return val
457
+ end
458
+
459
+ end
460
+
461
+ end # module BCL