amee 2.0.25

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,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