bcl 0.1.9 → 0.2.2
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 +2 -0
- data/lib/bcl/bcl_xml.rb +130 -0
- data/lib/bcl/component_methods.rb +25 -29
- data/lib/bcl/component_spreadsheet.rb +9 -1
- data/lib/bcl/component_xml.rb +17 -130
- data/lib/bcl/current_taxonomy.json +0 -0
- data/lib/bcl/current_taxonomy.xml +5699 -1283
- data/lib/bcl/master_taxonomy.rb +476 -476
- data/lib/bcl/measure_xml.rb +54 -0
- data/lib/bcl/tar_ball.rb +17 -20
- data/lib/bcl/version.rb +1 -1
- metadata +27 -11
data/lib/bcl.rb
CHANGED
data/lib/bcl/bcl_xml.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'uuid' # gem install uuid
|
2
|
+
|
3
|
+
module BCL
|
4
|
+
ProvStruct = Struct.new(:author, :datetime, :comment)
|
5
|
+
TagsStruct = Struct.new(:descriptor)
|
6
|
+
AttrStruct = Struct.new(:name, :value, :datatype, :units)
|
7
|
+
FileStruct = Struct.new(:version_software_program, :version_id, :fqp_file, :filename, :filetype, :usage_type, :checksum)
|
8
|
+
CostStruct = Struct.new(:cost_name, :cost_type, :category, :value, :interval,
|
9
|
+
:interval_units, :year, :location, :units, :currency, :source,
|
10
|
+
:reference_component_name, :reference_component_id)
|
11
|
+
|
12
|
+
ObjectStruct = Struct.new(:obj_type, :obj_instance)
|
13
|
+
|
14
|
+
class BaseXml
|
15
|
+
attr_accessor :name
|
16
|
+
attr_accessor :description
|
17
|
+
attr_accessor :uuid
|
18
|
+
attr_accessor :vuid
|
19
|
+
|
20
|
+
attr_accessor :attributes
|
21
|
+
attr_accessor :files
|
22
|
+
attr_accessor :costs
|
23
|
+
attr_accessor :tags
|
24
|
+
attr_accessor :provenances
|
25
|
+
|
26
|
+
def initialize(save_path)
|
27
|
+
@name = "" #this is also a unique identifier to the component...
|
28
|
+
@description = ""
|
29
|
+
|
30
|
+
@provenances = []
|
31
|
+
@tags = []
|
32
|
+
@attributes = []
|
33
|
+
@files = []
|
34
|
+
|
35
|
+
@schema_url = "schema.xsd"
|
36
|
+
end
|
37
|
+
|
38
|
+
def generate_uuid()
|
39
|
+
@uuid = UUID.new.generate
|
40
|
+
end
|
41
|
+
|
42
|
+
def generate_vuid()
|
43
|
+
@vuid = UUID.new.generate
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def add_provenance(author, datetime, comment)
|
48
|
+
prov = ProvStruct.new
|
49
|
+
prov.author = author
|
50
|
+
prov.datetime = datetime
|
51
|
+
prov.comment = comment
|
52
|
+
|
53
|
+
@provenances << prov
|
54
|
+
end
|
55
|
+
|
56
|
+
def add_tag(tag_name)
|
57
|
+
tag = TagsStruct.new
|
58
|
+
tag.descriptor = tag_name
|
59
|
+
|
60
|
+
@tags << tag
|
61
|
+
end
|
62
|
+
|
63
|
+
def add_attribute(name, value, units, datatype = nil)
|
64
|
+
attr = AttrStruct.new
|
65
|
+
attr.name = name
|
66
|
+
attr.value = value
|
67
|
+
|
68
|
+
if !datatype.nil?
|
69
|
+
attr.datatype = datatype
|
70
|
+
else
|
71
|
+
attr.datatype = get_datatype(value)
|
72
|
+
end
|
73
|
+
attr.units = units
|
74
|
+
|
75
|
+
@attributes << attr
|
76
|
+
end
|
77
|
+
|
78
|
+
def add_file(version_sp, version_id, fqp_file, filename, filetype, usage_type = nil, checksum = nil)
|
79
|
+
fs = FileStruct.new
|
80
|
+
fs.version_software_program = version_sp
|
81
|
+
fs.version_id = version_id
|
82
|
+
fs.fqp_file = fqp_file
|
83
|
+
fs.filename = filename
|
84
|
+
fs.filetype = filetype
|
85
|
+
fs.usage_type = usage_type if usage_type != nil
|
86
|
+
fs.checksum = checksum if checksum != nil
|
87
|
+
|
88
|
+
@files << fs
|
89
|
+
end
|
90
|
+
|
91
|
+
#return the title case of the string
|
92
|
+
def tc(input)
|
93
|
+
val = input.gsub(/\b\w/){$&.upcase}
|
94
|
+
if val.downcase == "energyplus"
|
95
|
+
val = "EnergyPlus"
|
96
|
+
end
|
97
|
+
return val
|
98
|
+
end
|
99
|
+
|
100
|
+
def get_attribute(attribute_name)
|
101
|
+
result = nil
|
102
|
+
@attributes.each do |attr|
|
103
|
+
if attr.name == attribute_name
|
104
|
+
result = attr
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
result
|
109
|
+
end
|
110
|
+
|
111
|
+
def get_datatype(input_value)
|
112
|
+
dt = 'undefined'
|
113
|
+
|
114
|
+
# simple method to test if the input_value is a string, float, or integer.
|
115
|
+
# First convert the value back to a string for testing (in case it was passed as a float/integer)
|
116
|
+
test = input_value.to_s
|
117
|
+
input_value = test.match('\.').nil? ? Integer(test) : Float(test) rescue test.to_s
|
118
|
+
|
119
|
+
if input_value.is_a?(Fixnum) || input_value.is_a?(Bignum)
|
120
|
+
dt = "int"
|
121
|
+
elsif input_value.is_a?(Float)
|
122
|
+
dt = "float"
|
123
|
+
else
|
124
|
+
dt = "string"
|
125
|
+
end
|
126
|
+
|
127
|
+
dt
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -39,6 +39,7 @@ module BCL
|
|
39
39
|
def initialize()
|
40
40
|
@config = nil
|
41
41
|
@session = nil
|
42
|
+
@api_version = 2.0
|
42
43
|
config_path = File.expand_path('~') + '/.bcl'
|
43
44
|
config_name = 'config.yml'
|
44
45
|
if File.exists?(config_path + "/" + config_name)
|
@@ -79,6 +80,7 @@ module BCL
|
|
79
80
|
session_name = res_j["session_name"]
|
80
81
|
|
81
82
|
@session = { session_name => sessid }
|
83
|
+
|
82
84
|
end
|
83
85
|
|
84
86
|
res
|
@@ -88,7 +90,7 @@ module BCL
|
|
88
90
|
# pushes component to the bcl and sets to published (for now). Username and password and
|
89
91
|
# set in ~/.bcl/config.yml file which determines the permissions and the group to which
|
90
92
|
# the component will be uploaded
|
91
|
-
def
|
93
|
+
def push_content(filename_and_path, write_receipt_file, content_type)
|
92
94
|
raise "Please login before pushing components" if @session.nil?
|
93
95
|
|
94
96
|
valid = false
|
@@ -96,36 +98,24 @@ module BCL
|
|
96
98
|
filename = File.basename(filename_and_path)
|
97
99
|
filepath = File.dirname(filename_and_path) + "/"
|
98
100
|
|
99
|
-
|
100
|
-
file = File.open(filename_and_path, 'r')
|
101
|
+
file = File.open(filename_and_path, 'rb')
|
101
102
|
file_b64 = Base64.encode64(file.read)
|
102
103
|
@data = {"file" =>
|
103
104
|
{
|
104
105
|
"file" => "#{file_b64}",
|
105
106
|
"filesize" => "#{File.size(filename_and_path)}",
|
106
107
|
"filename" => filename
|
107
|
-
}
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
if res.code == 200
|
113
|
-
fid = JSON.parse(res.body)["fid"]
|
114
|
-
|
115
|
-
#post the node now with reference to this fid
|
116
|
-
@data = {"node" =>
|
117
|
-
{"type" => "nrel_component",
|
118
|
-
"status" => 1, #NOTE THIS ONLY WORKS IF YOU ARE ADMIN
|
119
|
-
"field_tar_file" =>
|
120
|
-
{"und" => [
|
121
|
-
{"fid" => fid}
|
122
|
-
]
|
123
|
-
}
|
108
|
+
},
|
109
|
+
"node" =>
|
110
|
+
{
|
111
|
+
"type" => "#{content_type}"#,
|
112
|
+
#"status" => 1 #NOTE THIS ONLY WORKS IF YOU ARE ADMIN
|
124
113
|
}
|
125
|
-
|
126
|
-
|
127
|
-
res = RestClient.post "http://#{@config[:server][:url]}/api/node", @data.to_json, :content_type => :json, :cookies => @session, :accept => :json
|
114
|
+
}
|
128
115
|
|
116
|
+
res = RestClient.post "http://#{@config[:server][:url]}/api/content", @data.to_json, :content_type => :json, :cookies => @session, :accept => :json
|
117
|
+
|
118
|
+
if res.code == 200
|
129
119
|
res_j = JSON.parse(res.body)
|
130
120
|
|
131
121
|
if res.code == 200
|
@@ -153,7 +143,7 @@ module BCL
|
|
153
143
|
[valid, res_j]
|
154
144
|
end
|
155
145
|
|
156
|
-
def
|
146
|
+
def push_contents(array_of_components, skip_files_with_receipts, content_type)
|
157
147
|
logs = []
|
158
148
|
array_of_components.each do |comp|
|
159
149
|
receipt_file = File.dirname(comp) + "/" + File.basename(comp, '.tar.gz') + ".receipt"
|
@@ -163,23 +153,29 @@ module BCL
|
|
163
153
|
else
|
164
154
|
log_message = "pushing component #{comp}: "
|
165
155
|
puts log_message
|
166
|
-
valid, res =
|
156
|
+
valid, res = push_content(comp, true, content_type)
|
167
157
|
log_message += " #{valid} #{res.inspect.chomp}"
|
168
158
|
end
|
169
|
-
puts log_message
|
170
159
|
logs << log_message
|
171
160
|
end
|
172
161
|
|
173
162
|
logs
|
174
163
|
end
|
175
164
|
|
165
|
+
# Simple method to search bcl and return the result as an XML object
|
166
|
+
def search(search_str)
|
167
|
+
full_url = "http://#{@config[:server][:url]}/api/search/#{search_str}&api_version=#{@api_version}"
|
168
|
+
res = RestClient.get "#{full_url}"
|
169
|
+
xml = LibXML::XML::Document.string(res.body)
|
170
|
+
|
171
|
+
xml
|
172
|
+
end
|
173
|
+
|
176
174
|
end
|
177
175
|
|
178
176
|
# TODO make this extend the component_xml class (or create a super class around components)
|
179
177
|
|
180
|
-
|
181
|
-
|
182
|
-
def gather_components(component_dir, chunk_size = 0, delete_previous_gather = false)
|
178
|
+
def BCL.gather_components(component_dir, chunk_size = 0, delete_previous_gather = false)
|
183
179
|
@dest_filename = "components"
|
184
180
|
@dest_file_ext = "tar.gz"
|
185
181
|
|
@@ -178,6 +178,13 @@ end # if $have_win32ole
|
|
178
178
|
filename = values.delete_at(0)
|
179
179
|
filetype = values.delete_at(0)
|
180
180
|
filepath = values.delete_at(0)
|
181
|
+
#not all components(rows) have all files; skip if filename "" or nil
|
182
|
+
next if filename == "" or filename == nil
|
183
|
+
#skip the file if it doesn't exist at the specified location
|
184
|
+
if not File.exists?(filepath)
|
185
|
+
puts "[ComponentSpreadsheet] ERROR #{filepath} -> File does not exist, will not be included in component xml"
|
186
|
+
next #go to the next file
|
187
|
+
end
|
181
188
|
component_xml.add_file(software_program, version, filepath, filename, filetype)
|
182
189
|
|
183
190
|
else
|
@@ -270,7 +277,8 @@ end # if $have_win32ole
|
|
270
277
|
component.uid = xlsx_worksheet.Range("B#{i}").value
|
271
278
|
if component.uid.nil? or component.uid.empty?
|
272
279
|
component.uid = UUID.new.generate
|
273
|
-
xlsx_worksheet.Range("B#{i}").value = component.uid
|
280
|
+
xlsx_worksheet.Range("B#{i}").value = component.uid
|
281
|
+
exit
|
274
282
|
end
|
275
283
|
|
276
284
|
# always write new version id
|
data/lib/bcl/component_xml.rb
CHANGED
@@ -28,51 +28,25 @@ require 'csv'
|
|
28
28
|
|
29
29
|
# required gems
|
30
30
|
require 'builder' #gem install builder (creates xml files)
|
31
|
-
|
31
|
+
|
32
32
|
require 'bcl/tar_ball'
|
33
|
+
require 'bcl/bcl_xml'
|
33
34
|
|
34
35
|
|
35
36
|
module BCL
|
36
|
-
|
37
|
-
SCHEMA_LOCATION = "component.xsd"
|
38
|
-
|
39
|
-
ProvStruct = Struct.new(:author, :datetime, :comment)
|
40
|
-
TagsStruct = Struct.new(:descriptor)
|
41
|
-
AttrStruct = Struct.new(:name, :value, :datatype, :units)
|
42
|
-
FileStruct = Struct.new(:version_software_program, :version_id, :fqp_file, :filename, :filetype)
|
43
|
-
#cost_type is an enumeration (not enforced) of installation, material, operations and maintenance,
|
44
|
-
#variable operations and maintenance, salvage
|
45
|
-
CostStruct = Struct.new(:cost_name, :cost_type, :category, :value, :interval,
|
46
|
-
:interval_units, :year, :location, :units, :currency, :source,
|
47
|
-
:reference_component_name, :reference_component_id)
|
48
|
-
ObjectStruct = Struct.new(:obj_type, :obj_instance)
|
49
|
-
|
50
|
-
class Component
|
51
|
-
attr_accessor :name
|
52
|
-
attr_accessor :uid
|
53
|
-
attr_accessor :comp_version_id
|
54
|
-
attr_accessor :description
|
37
|
+
class Component < BaseXml
|
55
38
|
attr_accessor :comment
|
56
39
|
attr_accessor :source_manufacturer
|
57
40
|
attr_accessor :source_model
|
58
41
|
attr_accessor :source_serial_no
|
59
42
|
attr_accessor :source_year
|
60
43
|
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
44
|
attr_accessor :objects
|
67
45
|
|
68
|
-
public
|
69
|
-
|
70
46
|
#the save path is where the component will be saved
|
71
47
|
def initialize(save_path)
|
72
|
-
|
73
|
-
|
74
|
-
@comp_version_id = UUID.new.generate
|
75
|
-
@description = ""
|
48
|
+
super(save_path)
|
49
|
+
|
76
50
|
@comment = ""
|
77
51
|
@source_manufacturer = ""
|
78
52
|
@source_model = ""
|
@@ -81,23 +55,17 @@ module BCL
|
|
81
55
|
@source_url = ""
|
82
56
|
|
83
57
|
#these items have multiple instances
|
84
|
-
|
85
|
-
@tags = []
|
86
|
-
@attributes = []
|
87
|
-
@files = []
|
58
|
+
|
88
59
|
@costs = []
|
89
60
|
@objects = [] #container for saving the idf/osm snippets
|
90
61
|
|
91
62
|
@path = save_path
|
92
63
|
|
93
|
-
#puts "[ComponentXml] " + @path
|
94
|
-
#need to hit a webservice to validate which tags and attributes are
|
95
|
-
#available (including units?)
|
96
|
-
|
97
64
|
#todo: validate against master taxonomy
|
98
65
|
end
|
99
66
|
|
100
|
-
|
67
|
+
# TODO This isn't implemented at the moment
|
68
|
+
def open_component_xml(filename)
|
101
69
|
read_component_xml(filename)
|
102
70
|
end
|
103
71
|
|
@@ -152,49 +120,6 @@ module BCL
|
|
152
120
|
#puts "[ComponentXml] " + Dir.pwd
|
153
121
|
end
|
154
122
|
|
155
|
-
def add_provenance(author, datetime, comment)
|
156
|
-
prov = ProvStruct.new
|
157
|
-
prov.author = author
|
158
|
-
prov.datetime = datetime
|
159
|
-
prov.comment = comment
|
160
|
-
|
161
|
-
@provenance << prov
|
162
|
-
end
|
163
|
-
|
164
|
-
def add_tag(tag_name)
|
165
|
-
tag = TagsStruct.new
|
166
|
-
tag.descriptor = tag_name
|
167
|
-
|
168
|
-
@tags << tag
|
169
|
-
end
|
170
|
-
|
171
|
-
def add_attribute(name, value, units, datatype = nil)
|
172
|
-
attr = AttrStruct.new
|
173
|
-
attr.name = name
|
174
|
-
attr.value = value
|
175
|
-
|
176
|
-
if !datatype.nil?
|
177
|
-
attr.datatype = datatype
|
178
|
-
else
|
179
|
-
attr.datatype = get_datatype(value)
|
180
|
-
end
|
181
|
-
attr.units = units
|
182
|
-
|
183
|
-
@attributes << attr
|
184
|
-
end
|
185
|
-
|
186
|
-
def add_file(version_sp, version_id, fqp_file, filename, filetype)
|
187
|
-
fs = FileStruct.new
|
188
|
-
fs.version_software_program = version_sp
|
189
|
-
fs.version_id = version_id
|
190
|
-
fs.fqp_file = fqp_file
|
191
|
-
fs.filename = filename
|
192
|
-
fs.filetype = filetype
|
193
|
-
|
194
|
-
@files << fs
|
195
|
-
end
|
196
|
-
|
197
|
-
|
198
123
|
def add_cost(cost_name, cost_type, category, value, units, interval, interval_units, year, location, currency,
|
199
124
|
source, reference_component_name, reference_component_id)
|
200
125
|
cs = CostStruct.new
|
@@ -325,21 +250,26 @@ module BCL
|
|
325
250
|
end
|
326
251
|
|
327
252
|
def save_component_xml(dir_path = resolve_path)
|
253
|
+
FileUtils.mkpath(dir_path) if !File.exists?(dir_path)
|
254
|
+
|
255
|
+
generate_uuid() if @uuid.nil?
|
256
|
+
generate_vuid() if @vuid.nil?
|
257
|
+
|
328
258
|
xmlfile = File.new(dir_path + '/component.xml', 'w')
|
329
259
|
comp_xml = Builder::XmlMarkup.new(:target => xmlfile, :indent=>2)
|
330
260
|
|
331
261
|
#setup the xml file
|
332
262
|
comp_xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
|
333
263
|
comp_xml.component("xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
|
334
|
-
"xsi:noNamespaceSchemaLocation"=>"#{
|
264
|
+
"xsi:noNamespaceSchemaLocation"=>"#{@schema_url}") {
|
335
265
|
comp_xml.name @name
|
336
|
-
comp_xml.uid @
|
337
|
-
comp_xml.version_id @
|
266
|
+
comp_xml.uid @uuid
|
267
|
+
comp_xml.version_id @vuid
|
338
268
|
comp_xml.description @description if @description != ""
|
339
269
|
comp_xml.comment @comment if @comment != ""
|
340
270
|
|
341
271
|
comp_xml.provenances {
|
342
|
-
@
|
272
|
+
@provenances.each do |prov|
|
343
273
|
comp_xml.provenance {
|
344
274
|
comp_xml.author prov.author
|
345
275
|
comp_xml.datetime prov.datetime
|
@@ -428,49 +358,6 @@ module BCL
|
|
428
358
|
|
429
359
|
xmlfile.close
|
430
360
|
end
|
431
|
-
|
432
|
-
def get_attribute(attribute_name)
|
433
|
-
result = nil
|
434
|
-
@attributes.each do |attr|
|
435
|
-
if attr.name == attribute_name
|
436
|
-
result = attr
|
437
|
-
end
|
438
|
-
end
|
439
|
-
|
440
|
-
result
|
441
|
-
|
442
|
-
end
|
443
|
-
|
444
|
-
#return the title case of the string
|
445
|
-
def tc(input)
|
446
|
-
val = input.gsub(/\b\w/){$&.upcase}
|
447
|
-
if val.downcase == "energyplus"
|
448
|
-
val = "EnergyPlus"
|
449
|
-
end
|
450
|
-
return val
|
451
|
-
end
|
452
|
-
|
453
|
-
private
|
454
|
-
|
455
|
-
def get_datatype(input_value)
|
456
|
-
dt = 'undefined'
|
457
|
-
|
458
|
-
# simple method to test if the input_value is a string, float, or integer.
|
459
|
-
# First convert the value back to a string for testing (in case it was passed as a float/integer)
|
460
|
-
test = input_value.to_s
|
461
|
-
input_value = test.match('\.').nil? ? Integer(test) : Float(test) rescue test.to_s
|
462
|
-
|
463
|
-
if input_value.is_a?(Fixnum) || input_value.is_a?(Bignum)
|
464
|
-
dt = "int"
|
465
|
-
elsif input_value.is_a?(Float)
|
466
|
-
dt = "float"
|
467
|
-
else
|
468
|
-
dt = "string"
|
469
|
-
end
|
470
|
-
|
471
|
-
dt
|
472
|
-
end
|
473
|
-
|
474
361
|
end
|
475
362
|
|
476
363
|
end # module BCL
|