amee 2.0.25

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,374 @@
1
+ module AMEE
2
+ module Profile
3
+ class Item < AMEE::Profile::Object
4
+
5
+ def initialize(data = {})
6
+ @values = data ? data[:values] : []
7
+ @total_amount = data[:total_amount]
8
+ @total_amount_unit = data[:total_amount_unit]
9
+ @start_date = data[:start_date] || data[:valid_from]
10
+ @end_date = data[:end_date] || (data[:end] == true ? @start_date : nil )
11
+ @data_item_uid = data[:data_item_uid]
12
+ super
13
+ end
14
+
15
+ attr_reader :values
16
+ attr_reader :total_amount
17
+ attr_reader :total_amount_unit
18
+ attr_reader :start_date
19
+ attr_reader :end_date
20
+ attr_reader :data_item_uid
21
+
22
+ # V1 compatibility
23
+ def valid_from
24
+ start_date
25
+ end
26
+ def end
27
+ end_date.nil? ? false : start_date == end_date
28
+ end
29
+
30
+ def duration
31
+ end_date.nil? ? nil : (end_date - start_date).to_f
32
+ end
33
+
34
+ def self.from_json(json)
35
+ # Parse json
36
+ doc = JSON.parse(json)
37
+ data = {}
38
+ data[:profile_uid] = doc['profile']['uid']
39
+ data[:data_item_uid] = doc['profileItem']['dataItem']['uid']
40
+ data[:uid] = doc['profileItem']['uid']
41
+ data[:name] = doc['profileItem']['name']
42
+ data[:path] = doc['path']
43
+ data[:total_amount] = doc['profileItem']['amountPerMonth']
44
+ data[:total_amount_unit] = "kg/month"
45
+ data[:valid_from] = DateTime.strptime(doc['profileItem']['validFrom'], "%Y%m%d")
46
+ data[:end] = doc['profileItem']['end'] == "false" ? false : true
47
+ data[:values] = []
48
+ doc['profileItem']['itemValues'].each do |item|
49
+ value_data = {}
50
+ item.each_pair do |key,value|
51
+ case key
52
+ when 'name', 'path', 'uid', 'value'
53
+ value_data[key.downcase.to_sym] = value
54
+ end
55
+ end
56
+ data[:values] << value_data
57
+ end
58
+ # Create object
59
+ Item.new(data)
60
+ rescue
61
+ raise AMEE::BadData.new("Couldn't load ProfileItem from JSON data. Check that your URL is correct.\n#{json}")
62
+ end
63
+
64
+ def self.from_v2_json(json)
65
+ # Parse json
66
+ doc = JSON.parse(json)
67
+ data = {}
68
+ data[:profile_uid] = doc['profile']['uid']
69
+ data[:data_item_uid] = doc['profileItem']['dataItem']['uid']
70
+ data[:uid] = doc['profileItem']['uid']
71
+ data[:name] = doc['profileItem']['name']
72
+ data[:path] = doc['path']
73
+ data[:total_amount] = doc['profileItem']['amount']['value'].to_f
74
+ data[:total_amount_unit] = doc['profileItem']['amount']['unit']
75
+ data[:start_date] = DateTime.parse(doc['profileItem']['startDate'])
76
+ data[:end_date] = DateTime.parse(doc['profileItem']['endDate']) rescue nil
77
+ data[:values] = []
78
+ doc['profileItem']['itemValues'].each do |item|
79
+ value_data = {}
80
+ item.each_pair do |key,value|
81
+ case key
82
+ when 'name', 'path', 'uid', 'value', 'unit'
83
+ value_data[key.downcase.to_sym] = value
84
+ when 'perUnit'
85
+ value_data[:per_unit] = value
86
+ end
87
+ end
88
+ data[:values] << value_data
89
+ end
90
+ # Create object
91
+ Item.new(data)
92
+ rescue
93
+ raise AMEE::BadData.new("Couldn't load ProfileItem from V2 JSON data. Check that your URL is correct.\n#{json}")
94
+ end
95
+
96
+ def self.from_xml(xml)
97
+ # Parse XML
98
+ doc = REXML::Document.new(xml)
99
+ data = {}
100
+ data[:profile_uid] = REXML::XPath.first(doc, "/Resources/ProfileItemResource/Profile/@uid").to_s
101
+ data[:data_item_uid] = REXML::XPath.first(doc, "/Resources/ProfileItemResource/DataItem/@uid").to_s
102
+ data[:uid] = REXML::XPath.first(doc, "/Resources/ProfileItemResource/ProfileItem/@uid").to_s
103
+ data[:name] = REXML::XPath.first(doc, '/Resources/ProfileItemResource/ProfileItem/Name').text
104
+ data[:path] = REXML::XPath.first(doc, '/Resources/ProfileItemResource/Path').text || ""
105
+ data[:total_amount] = REXML::XPath.first(doc, '/Resources/ProfileItemResource/ProfileItem/AmountPerMonth').text.to_f rescue nil
106
+ data[:total_amount_unit] = "kg/month"
107
+ data[:valid_from] = DateTime.strptime(REXML::XPath.first(doc, "/Resources/ProfileItemResource/ProfileItem/ValidFrom").text, "%Y%m%d")
108
+ data[:end] = REXML::XPath.first(doc, '/Resources/ProfileItemResource/ProfileItem/End').text == "false" ? false : true
109
+ data[:values] = []
110
+ REXML::XPath.each(doc, '/Resources/ProfileItemResource/ProfileItem/ItemValues/ItemValue') do |item|
111
+ value_data = {}
112
+ item.elements.each do |element|
113
+ key = element.name
114
+ value = element.text
115
+ case key
116
+ when 'Name', 'Path', 'Value'
117
+ value_data[key.downcase.to_sym] = value
118
+ end
119
+ end
120
+ value_data[:uid] = item.attributes['uid'].to_s
121
+ data[:values] << value_data
122
+ end
123
+ # Create object
124
+ Item.new(data)
125
+ rescue
126
+ raise AMEE::BadData.new("Couldn't load ProfileItem from XML data. Check that your URL is correct.\n#{xml}")
127
+ end
128
+
129
+ def self.from_v2_xml(xml)
130
+ # Parse XML
131
+ doc = REXML::Document.new(xml)
132
+ data = {}
133
+ data[:profile_uid] = REXML::XPath.first(doc, "/Resources/ProfileItemResource/Profile/@uid").to_s
134
+ data[:data_item_uid] = REXML::XPath.first(doc, "/Resources/ProfileItemResource/DataItem/@uid").to_s
135
+ data[:uid] = REXML::XPath.first(doc, "/Resources/ProfileItemResource/ProfileItem/@uid").to_s
136
+ data[:name] = REXML::XPath.first(doc, '/Resources/ProfileItemResource/ProfileItem/Name').text
137
+ data[:path] = REXML::XPath.first(doc, '/Resources/ProfileItemResource/Path').text || ""
138
+ data[:total_amount] = REXML::XPath.first(doc, '/Resources/ProfileItemResource/ProfileItem/Amount').text.to_f rescue nil
139
+ data[:total_amount_unit] = REXML::XPath.first(doc, '/Resources/ProfileItemResource/ProfileItem/Amount/@unit').to_s rescue nil
140
+ data[:start_date] = DateTime.parse(REXML::XPath.first(doc, "/Resources/ProfileItemResource/ProfileItem/StartDate").text)
141
+ data[:end_date] = DateTime.parse(REXML::XPath.first(doc, "/Resources/ProfileItemResource/ProfileItem/EndDate").text) rescue nil
142
+ data[:values] = []
143
+ REXML::XPath.each(doc, '/Resources/ProfileItemResource/ProfileItem/ItemValues/ItemValue') do |item|
144
+ value_data = {}
145
+ item.elements.each do |element|
146
+ key = element.name
147
+ value = element.text
148
+ case key
149
+ when 'Name', 'Path', 'Value', 'Unit'
150
+ value_data[key.downcase.to_sym] = value
151
+ when 'PerUnit'
152
+ value_data[:per_unit] = value
153
+ end
154
+ end
155
+ value_data[:uid] = item.attributes['uid'].to_s
156
+ data[:values] << value_data
157
+ end
158
+ # Create object
159
+ Item.new(data)
160
+ rescue
161
+ raise AMEE::BadData.new("Couldn't load ProfileItem from V2 XML data. Check that your URL is correct.\n#{xml}")
162
+ end
163
+
164
+ def self.from_v2_atom(response)
165
+ # Parse XML
166
+ doc = REXML::Document.new(response)
167
+ data = {}
168
+ data[:profile_uid] = REXML::XPath.first(doc, "/entry/@xml:base").to_s.match("/profiles/(.*?)/")[1]
169
+ #data[:data_item_uid] = REXML::XPath.first(doc, "/Resources/ProfileItemResource/DataItem/@uid").to_s
170
+ data[:uid] = REXML::XPath.first(doc, "/entry/id").text.match("urn:item:(.*)")[1]
171
+ data[:name] = REXML::XPath.first(doc, '/entry/title').text
172
+ data[:path] = REXML::XPath.first(doc, "/entry/@xml:base").to_s.match("/profiles/.*?(/.*)")[1]
173
+ data[:total_amount] = REXML::XPath.first(doc, '/entry/amee:amount').text.to_f rescue nil
174
+ data[:total_amount_unit] = REXML::XPath.first(doc, '/entry/amee:amount/@unit').to_s rescue nil
175
+ data[:start_date] = DateTime.parse(REXML::XPath.first(doc, "/entry/amee:startDate").text)
176
+ data[:end_date] = DateTime.parse(REXML::XPath.first(doc, "/entry/amee:endDate").text) rescue nil
177
+ data[:values] = []
178
+ REXML::XPath.each(doc, '/entry/amee:itemValue') do |item|
179
+ value_data = {}
180
+ value_data[:name] = item.elements['amee:name'].text
181
+ value_data[:value] = item.elements['amee:value'].text unless item.elements['amee:value'].text == "N/A"
182
+ value_data[:path] = item.elements['link'].attributes['href'].to_s
183
+ value_data[:unit] = item.elements['amee:unit'].text rescue nil
184
+ value_data[:per_unit] = item.elements['amee:perUnit'].text rescue nil
185
+ data[:values] << value_data
186
+ end
187
+ # Create object
188
+ Item.new(data)
189
+ rescue
190
+ raise AMEE::BadData.new("Couldn't load ProfileItem from V2 ATOM data. Check that your URL is correct.\n#{response}")
191
+ end
192
+
193
+ def self.parse(connection, response)
194
+ # Parse data from response
195
+ if response.is_v2_json?
196
+ item = Item.from_v2_json(response)
197
+ elsif response.is_json?
198
+ item = Item.from_json(response)
199
+ elsif response.is_v2_atom?
200
+ item = Item.from_v2_atom(response)
201
+ elsif response.is_v2_xml?
202
+ item = Item.from_v2_xml(response)
203
+ else
204
+ item = Item.from_xml(response)
205
+ end
206
+ # Store connection in object for future use
207
+ item.connection = connection
208
+ # Done
209
+ return item
210
+ end
211
+
212
+ def self.get(connection, path, options = {})
213
+ unless options.is_a?(Hash)
214
+ raise AMEE::ArgumentError.new("Third argument must be a hash of options!")
215
+ end
216
+ # Convert to AMEE options
217
+ if options[:start_date] && category.connection.version < 2
218
+ options[:profileDate] = options[:start_date].amee1_month
219
+ elsif options[:start_date] && category.connection.version >= 2
220
+ options[:startDate] = options[:start_date].xmlschema
221
+ end
222
+ options.delete(:start_date)
223
+ if options[:end_date] && category.connection.version >= 2
224
+ options[:endDate] = options[:end_date].xmlschema
225
+ end
226
+ options.delete(:end_date)
227
+ if options[:duration] && category.connection.version >= 2
228
+ options[:duration] = "PT#{options[:duration] * 86400}S"
229
+ end
230
+ # Load data from path
231
+ response = connection.get(path, options).body
232
+ return Item.parse(connection, response)
233
+ rescue
234
+ raise AMEE::BadData.new("Couldn't load ProfileItem. Check that your URL is correct.\n#{response}")
235
+ end
236
+
237
+ def self.create(category, data_item_uid, options = {})
238
+ create_without_category(category.connection, category.full_path, data_item_uid, options)
239
+ end
240
+
241
+ def self.create_without_category(connection, path, data_item_uid, options = {})
242
+ # Do we want to automatically fetch the item afterwards?
243
+ get_item = options.delete(:get_item)
244
+ get_item = true if get_item.nil?
245
+ # Store format if set
246
+ format = options[:format]
247
+ unless options.is_a?(Hash)
248
+ raise AMEE::ArgumentError.new("Third argument must be a hash of options!")
249
+ end
250
+ # Set dates
251
+ if options[:start_date] && connection.version < 2
252
+ options[:validFrom] = options[:start_date].amee1_date
253
+ elsif options[:start_date] && connection.version >= 2
254
+ options[:startDate] = options[:start_date].xmlschema
255
+ end
256
+ if options[:end_date] && connection.version >= 2
257
+ options[:endDate] = options[:end_date].xmlschema
258
+ end
259
+ if options[:duration] && connection.version >= 2
260
+ options[:duration] = "PT#{options[:duration] * 86400}S"
261
+ end
262
+ # Send data to path
263
+ options.merge! :dataItemUid => data_item_uid
264
+ response = connection.post(path, options)
265
+ if response['Location']
266
+ location = response['Location'].match("http://.*?(/.*)")[1]
267
+ else
268
+ category = Category.parse(connection, response.body, nil)
269
+ location = category.full_path + "/" + category.items[0][:path]
270
+ end
271
+ if get_item == true
272
+ get_options = {}
273
+ get_options[:returnUnit] = options[:returnUnit] if options[:returnUnit]
274
+ get_options[:returnPerUnit] = options[:returnPerUnit] if options[:returnPerUnit]
275
+ get_options[:format] = format if format
276
+ return AMEE::Profile::Item.get(connection, location, get_options)
277
+ else
278
+ return location
279
+ end
280
+ rescue
281
+ raise AMEE::BadData.new("Couldn't create ProfileItem. Check that your information is correct.\n#{response}")
282
+ end
283
+
284
+ def self.create_batch(category, items, options = {})
285
+ create_batch_without_category(category.connection, category.full_path, items)
286
+ end
287
+
288
+ def self.create_batch_without_category(connection, category_path, items, options = {})
289
+ if connection.format == :json
290
+ options.merge! :profileItems => items
291
+ post_data = options.to_json
292
+ else
293
+ options.merge!({:ProfileItems => items})
294
+ post_data = options.to_xml(:root => "ProfileCategory", :skip_types => true, :skip_nil => true)
295
+ end
296
+ # Post to category
297
+ response = connection.raw_post(category_path, post_data).body
298
+ # Send back a category object containing all the created items
299
+ unless response.empty?
300
+ return AMEE::Profile::Category.parse_batch(connection, response)
301
+ else
302
+ return true
303
+ end
304
+ end
305
+
306
+ def self.update(connection, path, options = {})
307
+ # Do we want to automatically fetch the item afterwards?
308
+ get_item = options.delete(:get_item)
309
+ get_item = true if get_item.nil?
310
+ # Set dates
311
+ if options[:start_date] && connection.version < 2
312
+ options[:validFrom] = options[:start_date].amee1_date
313
+ elsif options[:start_date] && connection.version >= 2
314
+ options[:startDate] = options[:start_date].xmlschema
315
+ end
316
+ options.delete(:start_date)
317
+ if options[:end_date] && connection.version >= 2
318
+ options[:endDate] = options[:end_date].xmlschema
319
+ end
320
+ options.delete(:end_date)
321
+ if options[:duration] && connection.version >= 2
322
+ options[:duration] = "PT#{options[:duration] * 86400}S"
323
+ end
324
+ # Go
325
+ response = connection.put(path, options)
326
+ if get_item
327
+ if response.body.empty?
328
+ return Item.get(connection, path)
329
+ else
330
+ return Item.parse(connection, response.body)
331
+ end
332
+ end
333
+ rescue
334
+ raise AMEE::BadData.new("Couldn't update ProfileItem. Check that your information is correct.\n#{response}")
335
+ end
336
+
337
+ def update(options = {})
338
+ AMEE::Profile::Item.update(connection, full_path, options)
339
+ end
340
+
341
+ def self.update_batch(category, items)
342
+ update_batch_without_category(category.connection, category.full_path, items)
343
+ end
344
+
345
+ def self.update_batch_without_category(connection, category_path, items)
346
+ if connection.format == :json
347
+ put_data = ({:profileItems => items}).to_json
348
+ else
349
+ put_data = ({:ProfileItems => items}).to_xml(:root => "ProfileCategory", :skip_types => true, :skip_nil => true)
350
+ end
351
+ # Post to category
352
+ response = connection.raw_put(category_path, put_data).body
353
+ # Send back a category object containing all the created items
354
+ unless response.empty?
355
+ return AMEE::Profile::Category.parse(connection, response, nil)
356
+ else
357
+ return true
358
+ end
359
+ end
360
+
361
+ def self.delete(connection, path)
362
+ connection.delete(path)
363
+ rescue
364
+ raise AMEE::BadData.new("Couldn't delete ProfileItem. Check that your information is correct.")
365
+ end
366
+
367
+ def value(name_or_path)
368
+ val = values.find{ |x| x[:name] == name_or_path || x[:path] == name_or_path}
369
+ val ? val[:value] : nil
370
+ end
371
+
372
+ end
373
+ end
374
+ end
@@ -0,0 +1,106 @@
1
+ module AMEE
2
+ module Profile
3
+ class ItemValue < AMEE::Profile::Object
4
+
5
+ def initialize(data = {})
6
+ @value = data ? data[:value] : nil
7
+ @type = data ? data[:type] : nil
8
+ @unit = data ? data[:unit] : nil
9
+ @per_unit = data ? data[:per_unit] : nil
10
+ @from_profile = data ? data[:from_profile] : nil
11
+ @from_data = data ? data[:from_data] : nil
12
+ super
13
+ end
14
+
15
+ attr_reader :type
16
+ attr_accessor :unit
17
+ attr_accessor :per_unit
18
+
19
+ def value
20
+ case type
21
+ when "DECIMAL"
22
+ @value.to_f
23
+ else
24
+ @value
25
+ end
26
+ end
27
+
28
+ def value=(val)
29
+ @value = val
30
+ end
31
+
32
+ def from_profile?
33
+ @from_profile
34
+ end
35
+
36
+ def from_data?
37
+ @from_data
38
+ end
39
+
40
+ def self.from_json(json, path)
41
+ # Read JSON
42
+ doc = JSON.parse(json)['itemValue']
43
+ data = {}
44
+ data[:uid] = doc['uid']
45
+ data[:created] = DateTime.parse(doc['created'])
46
+ data[:modified] = DateTime.parse(doc['modified'])
47
+ data[:name] = doc['name']
48
+ data[:path] = path.gsub(/^\/profiles/, '')
49
+ data[:value] = doc['value']
50
+ data[:unit] = doc['unit']
51
+ data[:per_unit] = doc['perUnit']
52
+ data[:type] = doc['itemValueDefinition']['valueDefinition']['valueType']
53
+ # Create object
54
+ ItemValue.new(data)
55
+ rescue
56
+ raise AMEE::BadData.new("Couldn't load ProfileItemValue from JSON. Check that your URL is correct.\n#{json}")
57
+ end
58
+
59
+ def self.from_xml(xml, path)
60
+ # Read XML
61
+ doc = REXML::Document.new(xml)
62
+ data = {}
63
+ data[:uid] = REXML::XPath.first(doc, "/Resources/ProfileItemValueResource/ItemValue/@uid").to_s
64
+ data[:created] = DateTime.parse(REXML::XPath.first(doc, "/Resources/ProfileItemValueResource/ItemValue/@Created").to_s)
65
+ data[:modified] = DateTime.parse(REXML::XPath.first(doc, "/Resources/ProfileItemValueResource/ItemValue/@Modified").to_s)
66
+ data[:name] = REXML::XPath.first(doc, '/Resources/ProfileItemValueResource/ItemValue/Name').text
67
+ data[:path] = path.gsub(/^\/profiles/, '')
68
+ data[:value] = REXML::XPath.first(doc, '/Resources/ProfileItemValueResource/ItemValue/Value').text
69
+ data[:unit] = REXML::XPath.first(doc, '/Resources/ProfileItemValueResource/ItemValue/Unit').text rescue nil
70
+ data[:per_unit] = REXML::XPath.first(doc, '/Resources/ProfileItemValueResource/ItemValue/PerUnit').text rescue nil
71
+ data[:type] = REXML::XPath.first(doc, '/Resources/ProfileItemValueResource/ItemValue/ItemValueDefinition/ValueDefinition/ValueType').text
72
+ data[:from_profile] = REXML::XPath.first(doc, '/Resources/ProfileItemValueResource/ItemValue/ItemValueDefinition/FromProfile').text == "true" ? true : false
73
+ data[:from_data] = REXML::XPath.first(doc, '/Resources/ProfileItemValueResource/ItemValue/ItemValueDefinition/FromData').text == "true" ? true : false
74
+ # Create object
75
+ ItemValue.new(data)
76
+ rescue
77
+ raise AMEE::BadData.new("Couldn't load ProfileItemValue from XML. Check that your URL is correct.\n#{xml}")
78
+ end
79
+
80
+ def self.get(connection, path)
81
+ # Load Profile from path
82
+ response = connection.get(path).body
83
+ # Parse data from response
84
+ if response.is_json?
85
+ value = ItemValue.from_json(response, path)
86
+ else
87
+ value = ItemValue.from_xml(response, path)
88
+ end
89
+ # Store connection in object for future use
90
+ value.connection = connection
91
+ # Done
92
+ return value
93
+ rescue
94
+ raise AMEE::BadData.new("Couldn't load ProfileItemValue. Check that your URL is correct.\n#{response}")
95
+ end
96
+
97
+ def save!
98
+ options = {:value => value}
99
+ options[:unit] = unit if unit
100
+ options[:perUnit] = per_unit if per_unit
101
+ response = @connection.put(full_path, options).body
102
+ end
103
+
104
+ end
105
+ end
106
+ end