bcl 0.1.4
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.
- data/lib/bcl.rb +6 -0
- data/lib/bcl/ComponentSpreadsheet.rb +292 -0
- data/lib/bcl/ComponentXml.rb +451 -0
- data/lib/bcl/GatherComponents.rb +94 -0
- data/lib/bcl/MasterTaxonomy.rb +409 -0
- data/lib/bcl/MongoToComponent.rb +116 -0
- data/lib/bcl/TarBall.rb +51 -0
- data/lib/bcl/current_taxonomy.json +0 -0
- data/lib/bcl/current_taxonomy.xml +451 -0
- metadata +145 -0
data/lib/bcl.rb
ADDED
@@ -0,0 +1,292 @@
|
|
1
|
+
######################################################################
|
2
|
+
# Copyright (c) 2008-2010, 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
|
+
require 'rubygems'
|
24
|
+
|
25
|
+
require 'pathname'
|
26
|
+
require 'fileutils'
|
27
|
+
require 'uuid' # gem install uuid
|
28
|
+
|
29
|
+
require 'bcl/ComponentXml'
|
30
|
+
require 'bcl/GatherComponents'
|
31
|
+
require 'bcl/MasterTaxonomy'
|
32
|
+
|
33
|
+
$have_win32ole = false
|
34
|
+
begin
|
35
|
+
# apparently this is not a gem
|
36
|
+
require 'win32ole'
|
37
|
+
mod = WIN32OLE
|
38
|
+
$have_win32ole = true
|
39
|
+
rescue NameError
|
40
|
+
# do not have win32ole
|
41
|
+
end
|
42
|
+
|
43
|
+
module BCL
|
44
|
+
|
45
|
+
WorksheetStruct = Struct.new(:name, :components)
|
46
|
+
HeaderStruct = Struct.new(:name, :children)
|
47
|
+
ComponentStruct = Struct.new(:row, :name, :uid, :version_id, :headers, :values)
|
48
|
+
|
49
|
+
class ComponentSpreadsheet
|
50
|
+
|
51
|
+
public
|
52
|
+
|
53
|
+
# WINDOWS ONLY SECTION BECAUSE THIS USES WIN32OLE
|
54
|
+
if $have_win32ole
|
55
|
+
|
56
|
+
#initialize with Excel spreadsheet to read
|
57
|
+
def initialize(xlsx_path,worksheet_names = "all")
|
58
|
+
|
59
|
+
@xlsx_path = Pathname.new(xlsx_path).realpath.to_s
|
60
|
+
@worksheets = []
|
61
|
+
|
62
|
+
begin
|
63
|
+
|
64
|
+
excel = WIN32OLE::new('Excel.Application')
|
65
|
+
|
66
|
+
xlsx = excel.Workbooks.Open(@xlsx_path)
|
67
|
+
|
68
|
+
#by default, operate on all worksheets
|
69
|
+
if worksheet_names == "all"
|
70
|
+
xlsx.Worksheets.each do |xlsx_worksheet|
|
71
|
+
parse_xlsx_worksheet(xlsx_worksheet)
|
72
|
+
end
|
73
|
+
else #if specific worksheets are specified, operate on them
|
74
|
+
worksheet_names.each do |worksheet_name|
|
75
|
+
parse_xlsx_worksheet(xlsx.Worksheets(worksheet_name))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
xlsx.saved = true
|
80
|
+
xlsx.Save
|
81
|
+
|
82
|
+
ensure
|
83
|
+
|
84
|
+
excel.Quit
|
85
|
+
WIN32OLE.ole_free(excel)
|
86
|
+
excel.ole_free
|
87
|
+
xlsx=nil
|
88
|
+
excel=nil
|
89
|
+
GC.start
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
else # if $have_win32ole
|
96
|
+
|
97
|
+
# parse the master taxonomy document
|
98
|
+
def initialize(xlsx_path)
|
99
|
+
puts "ComponentSpreadsheet class requires 'win32ole' to parse the component spreadsheet."
|
100
|
+
puts "ComponentSpreadsheet may also be stored and loaded from JSON if your platform does not support win32ole."
|
101
|
+
end
|
102
|
+
|
103
|
+
end # if $have_win32ole
|
104
|
+
|
105
|
+
def save(save_path)
|
106
|
+
|
107
|
+
# load master taxonomy to validate components
|
108
|
+
taxonomy = BCL::MasterTaxonomy.new
|
109
|
+
|
110
|
+
FileUtils.rm_rf(save_path) if File.exists?(save_path) and File.directory?(save_path)
|
111
|
+
|
112
|
+
@worksheets.each do |worksheet|
|
113
|
+
worksheet.components.each do |component|
|
114
|
+
|
115
|
+
component_xml = Component.new(save_path)
|
116
|
+
component_xml.name = component.name
|
117
|
+
component_xml.uid = component.uid
|
118
|
+
component_xml.comp_version_id = component.version_id
|
119
|
+
|
120
|
+
# this tag is how we know where this goes in the taxonomy
|
121
|
+
component_xml.add_tag(worksheet.name)
|
122
|
+
|
123
|
+
values = component.values[0]
|
124
|
+
component.headers.each do |header|
|
125
|
+
|
126
|
+
if /description/i.match(header.name)
|
127
|
+
|
128
|
+
name = values.delete_at(0)
|
129
|
+
uid = values.delete_at(0)
|
130
|
+
version_id = values.delete_at(0)
|
131
|
+
description = values.delete_at(0)
|
132
|
+
fidelity_level = values.delete_at(0).to_int
|
133
|
+
# name, uid, and version_id already processed
|
134
|
+
component_xml.description = description
|
135
|
+
component_xml.fidelity_level = fidelity_level
|
136
|
+
|
137
|
+
elsif /provenance/i.match(header.name)
|
138
|
+
|
139
|
+
author = values.delete_at(0)
|
140
|
+
datetime = values.delete_at(0)
|
141
|
+
comment = values.delete_at(0)
|
142
|
+
component_xml.add_provenance(author.to_s, datetime.to_s, comment.to_s)
|
143
|
+
|
144
|
+
elsif /tag/i.match(header.name)
|
145
|
+
|
146
|
+
value = values.delete_at(0)
|
147
|
+
component_xml.add_tag(value)
|
148
|
+
|
149
|
+
elsif /attribute/i.match(header.name)
|
150
|
+
|
151
|
+
value = values.delete_at(0)
|
152
|
+
name = header.children[0]
|
153
|
+
units = ""
|
154
|
+
if match_data = /(.*)\((.*)\)/.match(name)
|
155
|
+
name = match_data[1].strip
|
156
|
+
units = match_data[2].strip
|
157
|
+
end
|
158
|
+
component_xml.add_attribute(name, value, units)
|
159
|
+
|
160
|
+
elsif /source/i.match(header.name)
|
161
|
+
|
162
|
+
manufacturer = values.delete_at(0)
|
163
|
+
model = values.delete_at(0)
|
164
|
+
serial_no = values.delete_at(0)
|
165
|
+
year = values.delete_at(0)
|
166
|
+
url = values.delete_at(0)
|
167
|
+
component_xml.source_manufacturer = manufacturer
|
168
|
+
component_xml.source_model = model
|
169
|
+
component_xml.source_serial_no = serial_no
|
170
|
+
component_xml.source_year = year
|
171
|
+
component_xml.source_url = url
|
172
|
+
|
173
|
+
elsif /file/i.match(header.name)
|
174
|
+
|
175
|
+
software_program = values.delete_at(0)
|
176
|
+
version = values.delete_at(0)
|
177
|
+
filename = values.delete_at(0)
|
178
|
+
filetype = values.delete_at(0)
|
179
|
+
filepath = values.delete_at(0)
|
180
|
+
component_xml.add_file(software_program, version, filepath, filename, filetype)
|
181
|
+
|
182
|
+
else
|
183
|
+
raise "Unknown section #{header.name}"
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
taxonomy.check_component(component_xml)
|
190
|
+
|
191
|
+
component_xml.save_tar_gz(false)
|
192
|
+
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
196
|
+
|
197
|
+
BCL.gather_components(save_path)
|
198
|
+
|
199
|
+
end
|
200
|
+
|
201
|
+
private
|
202
|
+
|
203
|
+
def parse_xlsx_worksheet(xlsx_worksheet)
|
204
|
+
|
205
|
+
worksheet = WorksheetStruct.new
|
206
|
+
worksheet.name = xlsx_worksheet.Range("A1").Value
|
207
|
+
worksheet.components = []
|
208
|
+
puts "[ComponentSpreadsheet] Starting parsing components of type #{worksheet.name}"
|
209
|
+
|
210
|
+
# find number of rows, first column should be name, should not be empty
|
211
|
+
num_rows = 1
|
212
|
+
while true do
|
213
|
+
test = xlsx_worksheet.Range("A#{num_rows}").Value
|
214
|
+
if test.nil? or test.empty?
|
215
|
+
num_rows -= 1
|
216
|
+
break
|
217
|
+
end
|
218
|
+
num_rows += 1
|
219
|
+
end
|
220
|
+
|
221
|
+
# scan number of columns
|
222
|
+
headers = []
|
223
|
+
header = nil
|
224
|
+
max_col = nil
|
225
|
+
xlsx_worksheet.Columns.each do |col|
|
226
|
+
value1 = col.Rows("1").Value
|
227
|
+
value2 = col.Rows("2").Value
|
228
|
+
|
229
|
+
if not value1.nil? and not value1.empty?
|
230
|
+
if not header.nil?
|
231
|
+
headers << header
|
232
|
+
end
|
233
|
+
header = HeaderStruct.new
|
234
|
+
header.name = value1
|
235
|
+
header.children = []
|
236
|
+
end
|
237
|
+
|
238
|
+
if not value2.nil? and not value2.empty?
|
239
|
+
if not header.nil?
|
240
|
+
header.children << value2
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
if (value1.nil? or value1.empty?) and (value2.nil? or value2.empty?)
|
245
|
+
break
|
246
|
+
end
|
247
|
+
|
248
|
+
matchdata = /^\$(.+):/.match(col.Address)
|
249
|
+
max_col = matchdata[1]
|
250
|
+
end
|
251
|
+
|
252
|
+
if not header.nil?
|
253
|
+
headers << header
|
254
|
+
end
|
255
|
+
|
256
|
+
if not headers.empty?
|
257
|
+
headers[0].name = "description"
|
258
|
+
end
|
259
|
+
|
260
|
+
components = []
|
261
|
+
for i in 3..num_rows do
|
262
|
+
component = ComponentStruct.new
|
263
|
+
component.row = i
|
264
|
+
|
265
|
+
# get name
|
266
|
+
component.name = xlsx_worksheet.Range("A#{i}").value
|
267
|
+
|
268
|
+
# get uid, if empty set it
|
269
|
+
component.uid = xlsx_worksheet.Range("B#{i}").value
|
270
|
+
if component.uid.nil? or component.uid.empty?
|
271
|
+
component.uid = UUID.new.generate
|
272
|
+
xlsx_worksheet.Range("B#{i}").value = component.uid
|
273
|
+
end
|
274
|
+
|
275
|
+
# always write new version id
|
276
|
+
component.version_id = UUID.new.generate
|
277
|
+
xlsx_worksheet.Range("C#{i}").value = component.version_id
|
278
|
+
|
279
|
+
component.headers = headers
|
280
|
+
component.values = xlsx_worksheet.Range("A#{i}:#{max_col}#{i}").value
|
281
|
+
worksheet.components << component
|
282
|
+
end
|
283
|
+
|
284
|
+
@worksheets << worksheet
|
285
|
+
|
286
|
+
puts "[ComponentSpreadsheet] Finished parsing components of type #{worksheet.name}"
|
287
|
+
|
288
|
+
end
|
289
|
+
|
290
|
+
end
|
291
|
+
|
292
|
+
end # module BCL
|
@@ -0,0 +1,451 @@
|
|
1
|
+
######################################################################
|
2
|
+
# Copyright (c) 2008-2010, 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(" ","_").gsub("/","_").gsub("-","_").gsub("___","_").gsub("__","_").gsub("in.","in").gsub(",","").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)
|
173
|
+
attr = AttrStruct.new
|
174
|
+
attr.name = name
|
175
|
+
attr.value = value
|
176
|
+
|
177
|
+
if value.is_a? Fixnum
|
178
|
+
attr.datatype = "int"
|
179
|
+
elsif value.is_a? Float
|
180
|
+
attr.datatype = "float"
|
181
|
+
else
|
182
|
+
attr.datatype = "string"
|
183
|
+
end
|
184
|
+
|
185
|
+
attr.units = units
|
186
|
+
|
187
|
+
@attributes << attr
|
188
|
+
end
|
189
|
+
|
190
|
+
def add_file(version_sp, version_id, fqp_file, filename, filetype)
|
191
|
+
fs = FileStruct.new
|
192
|
+
fs.version_software_program = version_sp
|
193
|
+
fs.version_id = version_id
|
194
|
+
fs.fqp_file = fqp_file
|
195
|
+
fs.filename = filename
|
196
|
+
fs.filetype = filetype
|
197
|
+
|
198
|
+
@files << fs
|
199
|
+
end
|
200
|
+
|
201
|
+
|
202
|
+
def add_cost(cost_name, cost_type, category, value, units, interval, interval_units, year, location, currency,
|
203
|
+
source, reference_component_name, reference_component_id)
|
204
|
+
cs = CostStruct.new
|
205
|
+
cs.cost_name = cost_name
|
206
|
+
cs.cost_type = cost_type
|
207
|
+
cs.category = category
|
208
|
+
cs.value = value
|
209
|
+
cs.interval = interval
|
210
|
+
cs.interval_units = interval_units
|
211
|
+
cs.year = year
|
212
|
+
cs.location = location
|
213
|
+
cs.units = units
|
214
|
+
cs.currency = currency
|
215
|
+
cs.source = source
|
216
|
+
cs.reference_component_name = reference_component_name
|
217
|
+
cs.reference_component_id = reference_component_id
|
218
|
+
|
219
|
+
@costs << cs
|
220
|
+
end
|
221
|
+
|
222
|
+
def add_object(object_type, object_instance)
|
223
|
+
ob = ObjectStruct.new
|
224
|
+
ob.obj_type = object_type
|
225
|
+
ob.obj_instance = object_instance
|
226
|
+
|
227
|
+
@objects << ob
|
228
|
+
end
|
229
|
+
|
230
|
+
def resolve_path
|
231
|
+
FileUtils.mkdir_p(@path) unless File.directory?(@path)
|
232
|
+
new_path = "#{@path}/#{name.gsub(" ","_").gsub("/","_").gsub("-","_").gsub("___","_").gsub("__","_").gsub("in.","in").gsub(",","").strip}"
|
233
|
+
FileUtils.mkdir_p(new_path) unless File.directory?(new_path)
|
234
|
+
result = new_path
|
235
|
+
end
|
236
|
+
|
237
|
+
def osm_resolve_path
|
238
|
+
FileUtils.mkdir_p(@path) unless File.directory?(@path)
|
239
|
+
new_path = "#{@path}/osm_#{name.gsub(" ","_").gsub("/","_").gsub("-","_").gsub("___","_").gsub("__","_").gsub("in.","in").gsub(",","").strip}"
|
240
|
+
FileUtils.mkdir_p(new_path) unless File.directory?(new_path)
|
241
|
+
result = new_path
|
242
|
+
end
|
243
|
+
|
244
|
+
def osc_resolve_path
|
245
|
+
FileUtils.mkdir_p(@path) unless File.directory?(@path)
|
246
|
+
new_path = "#{@path}/osc_#{name.gsub(" ","_").gsub("/","_").gsub("-","_").gsub("___","_").gsub("__","_").gsub("in.","in").gsub(",","").strip}"
|
247
|
+
FileUtils.mkdir_p(new_path) unless File.directory?(new_path)
|
248
|
+
result = new_path
|
249
|
+
end
|
250
|
+
|
251
|
+
def resolve_component_path(component_type)
|
252
|
+
FileUtils.mkdir_p(@path) unless File.directory?(@path)
|
253
|
+
new_path = @path + '/OpenStudio'
|
254
|
+
FileUtils.mkdir_p(new_path) unless File.directory?(new_path)
|
255
|
+
new_path = new_path + "/#{component_type}"
|
256
|
+
FileUtils.mkdir_p(new_path) unless File.directory?(new_path)
|
257
|
+
return new_path
|
258
|
+
end
|
259
|
+
|
260
|
+
def tmp_resolve_path
|
261
|
+
FileUtils.mkdir_p(@path) unless File.directory?(@path)
|
262
|
+
new_path = "#{@path}/tmp_#{name.gsub(" ","_").gsub("/","_").gsub("-","_").gsub("___","_").gsub("__","_").gsub("in.","in").gsub(",","").strip}"
|
263
|
+
FileUtils.mkdir_p(new_path) unless File.directory?(new_path)
|
264
|
+
result = new_path
|
265
|
+
end
|
266
|
+
|
267
|
+
|
268
|
+
def create_os_component(osobj)
|
269
|
+
osobj.getCostLineItems.each do |os|
|
270
|
+
@costs.each do |cost|
|
271
|
+
#figure out costs for constructions
|
272
|
+
os.setMaterialCost(cost.value.to_f) if cost.category == "material"
|
273
|
+
if cost.category == "installation"
|
274
|
+
os.setInstallationCost(cost.value.to_f)
|
275
|
+
os.setExpectedLife(cost.interval.to_i)
|
276
|
+
end
|
277
|
+
os.setFixedOM(cost.value.to_f) if cost.category == "operations and maintenance"
|
278
|
+
os.setVariableOM(cost.value.to_f) if cost.category == "variable operations and maintenance"
|
279
|
+
os.setSalvageCost(cost.value.to_f) if cost.category == "salvage"
|
280
|
+
end
|
281
|
+
end
|
282
|
+
newcomp = osobj.createComponent
|
283
|
+
|
284
|
+
cd = newcomp.componentData
|
285
|
+
cd.setDescription(@description)
|
286
|
+
cd.setFidelityLevel(@fidelity_level)
|
287
|
+
|
288
|
+
at = newcomp.componentData.componentDataAttributes
|
289
|
+
@attributes.each do |attrib|
|
290
|
+
if (attrib.value.to_s != "") and (attrib.name.to_s != "")
|
291
|
+
if attrib.units != ""
|
292
|
+
at.addAttribute(tc(attrib.name), attrib.value, attrib.units)
|
293
|
+
else
|
294
|
+
at.addAttribute(tc(attrib.name), attrib.value)
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
tg = newcomp.componentData.componentDataTags
|
300
|
+
comp_tag = ""
|
301
|
+
@tags.each do |tag|
|
302
|
+
tg.addTag(tc(tag.descriptor))
|
303
|
+
if (tag.descriptor != "energyplus") and (tag.descriptor != "construction")
|
304
|
+
#create a map of component tags to directories
|
305
|
+
comp_tag = tag.descriptor
|
306
|
+
if comp_tag == "interior wall"
|
307
|
+
comp_tag = "interiorwalls"
|
308
|
+
elsif comp_tag == "exterior wall"
|
309
|
+
comp_tag = "exteriorwalls"
|
310
|
+
elsif comp_tag == "exterior slab"
|
311
|
+
comp_tag = "exteriorslabs"
|
312
|
+
elsif comp_tag == "exposed floor"
|
313
|
+
comp_tag = "exposedfloors"
|
314
|
+
elsif comp_tag == "attic floor"
|
315
|
+
comp_tag = "atticfloors"
|
316
|
+
elsif comp_tag == "roof"
|
317
|
+
comp_tag = "roofs"
|
318
|
+
elsif comp_tag == "door"
|
319
|
+
comp_tag = "doors"
|
320
|
+
elsif comp_tag == "skylight"
|
321
|
+
comp_tag = "skylights"
|
322
|
+
elsif comp_tag == "window"
|
323
|
+
comp_tag = "windows"
|
324
|
+
end
|
325
|
+
puts comp_tag
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
return newcomp
|
330
|
+
end
|
331
|
+
|
332
|
+
def save_component_xml(dir_path = resolve_path)
|
333
|
+
xmlfile = File.new(dir_path + '/component.xml', 'w')
|
334
|
+
comp_xml = Builder::XmlMarkup.new(:target => xmlfile, :indent=>2)
|
335
|
+
|
336
|
+
#setup the xml file
|
337
|
+
comp_xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
|
338
|
+
comp_xml.component("xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
|
339
|
+
"xsi:noNamespaceSchemaLocation"=>"#{SCHEMA_LOCATION}") {
|
340
|
+
comp_xml.name @name
|
341
|
+
comp_xml.uid @uid
|
342
|
+
comp_xml.version_id @comp_version_id
|
343
|
+
comp_xml.description @description if @description != ""
|
344
|
+
comp_xml.comment @comment if @comment != ""
|
345
|
+
comp_xml.fidelity_level @fidelity_level
|
346
|
+
|
347
|
+
comp_xml.provenances {
|
348
|
+
@provenance.each do |prov|
|
349
|
+
comp_xml.provenance {
|
350
|
+
comp_xml.author prov.author
|
351
|
+
comp_xml.datetime prov.datetime
|
352
|
+
comp_xml.comment prov.comment
|
353
|
+
}
|
354
|
+
end
|
355
|
+
}
|
356
|
+
|
357
|
+
comp_xml.tags {
|
358
|
+
@tags.each do |tag|
|
359
|
+
comp_xml.tag tag.descriptor
|
360
|
+
end
|
361
|
+
}
|
362
|
+
|
363
|
+
comp_xml.attributes {
|
364
|
+
@attributes.each do |attrib|
|
365
|
+
if (attrib.value.to_s != "") and (attrib.name.to_s != "") then
|
366
|
+
comp_xml.attribute {
|
367
|
+
comp_xml.name attrib.name
|
368
|
+
comp_xml.value attrib.value
|
369
|
+
comp_xml.datatype attrib.datatype
|
370
|
+
comp_xml.units attrib.units if attrib.units != ""
|
371
|
+
}
|
372
|
+
end
|
373
|
+
end
|
374
|
+
}
|
375
|
+
|
376
|
+
comp_xml.source {
|
377
|
+
comp_xml.manufacturer @source_manufacturer if @source_manufacturer != ""
|
378
|
+
comp_xml.model @source_model if @source_model != ""
|
379
|
+
comp_xml.serial_no @source_serial_no if @source_serial_no != ""
|
380
|
+
comp_xml.year @source_year if @source_year != ""
|
381
|
+
comp_xml.url @source_url if @source_url != ""
|
382
|
+
}
|
383
|
+
|
384
|
+
if not @files.nil?
|
385
|
+
comp_xml.files {
|
386
|
+
@files.each do |file|
|
387
|
+
comp_xml.file {
|
388
|
+
comp_xml.version {
|
389
|
+
comp_xml.software_program file.version_software_program
|
390
|
+
comp_xml.identifier file.version_id
|
391
|
+
}
|
392
|
+
|
393
|
+
comp_xml.filename file.filename
|
394
|
+
comp_xml.filetype file.filetype
|
395
|
+
}
|
396
|
+
end
|
397
|
+
}
|
398
|
+
end
|
399
|
+
|
400
|
+
#check if we should write out costs, don't write if all values are 0 or nil
|
401
|
+
#DLM: schema always expects costs
|
402
|
+
write_costs = true
|
403
|
+
#if not @costs.nil?
|
404
|
+
# @costs.each do |cost|
|
405
|
+
# if (cost.value.nil?) && (not cost.value == 0)
|
406
|
+
# write_costs = true
|
407
|
+
# break
|
408
|
+
# end
|
409
|
+
# end
|
410
|
+
#end
|
411
|
+
|
412
|
+
if write_costs
|
413
|
+
comp_xml.costs {
|
414
|
+
@costs.each do |cost|
|
415
|
+
comp_xml.cost {
|
416
|
+
comp_xml.instance_name cost.cost_name
|
417
|
+
comp_xml.cost_type cost.cost_type
|
418
|
+
comp_xml.category cost.category
|
419
|
+
comp_xml.value cost.value
|
420
|
+
comp_xml.units cost.units if cost.units != ""
|
421
|
+
comp_xml.interval cost.interval if cost.interval != ""
|
422
|
+
comp_xml.interval_units cost.interval_units if cost.interval_units != ""
|
423
|
+
comp_xml.year cost.year if cost.year != ""
|
424
|
+
comp_xml.currency cost.currency if cost.currency != ""
|
425
|
+
comp_xml.source cost.source if cost.source != ""
|
426
|
+
comp_xml.reference_component_name cost.reference_component_name if cost.reference_component_name != ""
|
427
|
+
comp_xml.reference_component_id cost.reference_component_id if cost.reference_component_id != ""
|
428
|
+
}
|
429
|
+
end
|
430
|
+
}
|
431
|
+
end
|
432
|
+
|
433
|
+
}
|
434
|
+
|
435
|
+
xmlfile.close
|
436
|
+
end
|
437
|
+
|
438
|
+
private
|
439
|
+
|
440
|
+
#return the title case of the string
|
441
|
+
def tc(input)
|
442
|
+
val = input.gsub(/\b\w/){$&.upcase}
|
443
|
+
if val.downcase == "energyplus"
|
444
|
+
val = "EnergyPlus"
|
445
|
+
end
|
446
|
+
return val
|
447
|
+
end
|
448
|
+
|
449
|
+
end
|
450
|
+
|
451
|
+
end # module BCL
|