Floppy-amee 0.4.33 → 2.0.0
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/amee.rb +37 -1
- data/lib/amee/connection.rb +52 -27
- data/lib/amee/exceptions.rb +3 -3
- data/lib/amee/profile_category.rb +217 -10
- data/lib/amee/profile_item.rb +175 -20
- data/lib/amee/version.rb +3 -3
- metadata +2 -2
data/lib/amee.rb
CHANGED
@@ -11,6 +11,18 @@ class String
|
|
11
11
|
def is_json?
|
12
12
|
slice(0,1) == '{'
|
13
13
|
end
|
14
|
+
def is_v2_json?
|
15
|
+
is_json? && match('"apiVersion".*?:.*?"2.0"')
|
16
|
+
end
|
17
|
+
def is_xml?
|
18
|
+
slice(0,5) == '<?xml'
|
19
|
+
end
|
20
|
+
def is_v2_xml?
|
21
|
+
is_xml? && include?('<Resources xmlns="http://schemas.amee.cc/2.0">')
|
22
|
+
end
|
23
|
+
def is_v2_atom?
|
24
|
+
is_xml? && (include?('<feed ') || include?('<entry ')) && include?('xmlns:amee="http://schemas.amee.cc/2.0"')
|
25
|
+
end
|
14
26
|
end
|
15
27
|
|
16
28
|
require 'amee/version'
|
@@ -25,4 +37,28 @@ require 'amee/data_item_value'
|
|
25
37
|
require 'amee/profile'
|
26
38
|
require 'amee/profile_category'
|
27
39
|
require 'amee/profile_item'
|
28
|
-
require 'amee/drill_down'
|
40
|
+
require 'amee/drill_down'
|
41
|
+
|
42
|
+
class Date
|
43
|
+
def amee2schema
|
44
|
+
strftime("%Y-%m-%dT%H:%M+0000")
|
45
|
+
end
|
46
|
+
def amee1_date
|
47
|
+
strftime("%Y%m%d")
|
48
|
+
end
|
49
|
+
def amee1_month
|
50
|
+
strftime("%Y%m")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class Time
|
55
|
+
def amee2schema
|
56
|
+
strftime("%Y-%m-%dT%H:%M+0000")
|
57
|
+
end
|
58
|
+
def amee1_date
|
59
|
+
strftime("%Y%m%d")
|
60
|
+
end
|
61
|
+
def amee1_month
|
62
|
+
strftime("%Y%m")
|
63
|
+
end
|
64
|
+
end
|
data/lib/amee/connection.rb
CHANGED
@@ -3,24 +3,29 @@ require 'net/http'
|
|
3
3
|
module AMEE
|
4
4
|
class Connection
|
5
5
|
|
6
|
-
def initialize(server, username
|
6
|
+
def initialize(server, username, password, options = {})
|
7
|
+
unless options.is_a?(Hash)
|
8
|
+
raise AMEE::ArgumentError.new("Fourth argument must be a hash of options!")
|
9
|
+
end
|
7
10
|
@server = server
|
8
11
|
@username = username
|
9
12
|
@password = password
|
10
13
|
@auth_token = nil
|
11
|
-
@
|
12
|
-
if
|
13
|
-
raise "
|
14
|
+
@format = options[:format] || (defined?(JSON) ? :json : :xml)
|
15
|
+
if !valid?
|
16
|
+
raise "You must supply connection details - server, username and password are all required!"
|
14
17
|
end
|
15
|
-
@enable_caching = enable_caching
|
18
|
+
@enable_caching = options[:enable_caching]
|
16
19
|
if @enable_caching
|
17
20
|
$cache ||= {}
|
18
21
|
end
|
19
22
|
# Make connection to server
|
20
23
|
@http = Net::HTTP.new(@server)
|
21
24
|
@http.read_timeout = 5
|
22
|
-
@http.set_debug_output($stdout) if enable_debug
|
25
|
+
@http.set_debug_output($stdout) if options[:enable_debug]
|
23
26
|
end
|
27
|
+
|
28
|
+
attr_reader :format
|
24
29
|
|
25
30
|
def timeout
|
26
31
|
@http.read_timeout
|
@@ -30,19 +35,21 @@ module AMEE
|
|
30
35
|
@http.read_timeout = t
|
31
36
|
end
|
32
37
|
|
38
|
+
def version
|
39
|
+
@version
|
40
|
+
end
|
41
|
+
|
33
42
|
def valid?
|
34
|
-
|
43
|
+
@username && @password && @server
|
35
44
|
end
|
36
45
|
|
37
|
-
def can_authenticate?
|
38
|
-
!(@username.nil? || @password.nil?)
|
39
|
-
end
|
40
|
-
|
41
46
|
def authenticated?
|
42
47
|
!@auth_token.nil?
|
43
48
|
end
|
44
49
|
|
45
50
|
def get(path, data = {})
|
51
|
+
# Allow format override
|
52
|
+
format = data.delete(:format) || @format
|
46
53
|
# Create URL parameters
|
47
54
|
params = []
|
48
55
|
data.each_pair do |key, value|
|
@@ -53,12 +60,15 @@ module AMEE
|
|
53
60
|
end
|
54
61
|
# Send request
|
55
62
|
return $cache[path] if @enable_caching and $cache[path]
|
56
|
-
response = do_request
|
63
|
+
response = do_request(Net::HTTP::Get.new(path), format)
|
57
64
|
$cache[path] = response if @enable_caching
|
58
65
|
return response
|
59
66
|
end
|
60
67
|
|
61
68
|
def post(path, data = {})
|
69
|
+
# Allow format override
|
70
|
+
format = data.delete(:format) || @format
|
71
|
+
# Clear cache
|
62
72
|
clear_cache
|
63
73
|
# Create POST request
|
64
74
|
post = Net::HTTP::Post.new(path)
|
@@ -68,10 +78,13 @@ module AMEE
|
|
68
78
|
end
|
69
79
|
post.body = body.join '&'
|
70
80
|
# Send request
|
71
|
-
do_request(post)
|
81
|
+
do_request(post, format)
|
72
82
|
end
|
73
83
|
|
74
84
|
def put(path, data = {})
|
85
|
+
# Allow format override
|
86
|
+
format = data.delete(:format) || @format
|
87
|
+
# Clear cache
|
75
88
|
clear_cache
|
76
89
|
# Create PUT request
|
77
90
|
put = Net::HTTP::Put.new(path)
|
@@ -81,7 +94,7 @@ module AMEE
|
|
81
94
|
end
|
82
95
|
put.body = body.join '&'
|
83
96
|
# Send request
|
84
|
-
do_request(put)
|
97
|
+
do_request(put, format)
|
85
98
|
end
|
86
99
|
|
87
100
|
def delete(path)
|
@@ -93,24 +106,36 @@ module AMEE
|
|
93
106
|
end
|
94
107
|
|
95
108
|
def authenticate
|
96
|
-
unless can_authenticate?
|
97
|
-
raise AMEE::AuthRequired.new("Authentication required. Please provide your username and password.")
|
98
|
-
end
|
99
109
|
response = nil
|
100
|
-
post = Net::HTTP::Post.new("/auth")
|
110
|
+
post = Net::HTTP::Post.new("/auth/signIn")
|
101
111
|
post.body = "username=#{@username}&password=#{@password}"
|
102
|
-
post['Accept'] = content_type
|
112
|
+
post['Accept'] = content_type(:xml)
|
103
113
|
response = @http.request(post)
|
104
114
|
@auth_token = response['authToken']
|
105
115
|
unless authenticated?
|
106
116
|
raise AMEE::AuthFailed.new("Authentication failed. Please check your username and password.")
|
107
117
|
end
|
118
|
+
# Detect API version
|
119
|
+
if response.body.is_json?
|
120
|
+
@version = JSON.parse(response.body)["user"]["apiVersion"].to_f
|
121
|
+
elsif response.body.is_xml?
|
122
|
+
@version = REXML::Document.new(response.body).elements['Resources'].elements['SignInResource'].elements['User'].elements['ApiVersion'].text.to_f
|
123
|
+
else
|
124
|
+
@version = 1.0
|
125
|
+
end
|
108
126
|
end
|
109
127
|
|
110
128
|
protected
|
111
129
|
|
112
|
-
def content_type
|
113
|
-
|
130
|
+
def content_type(format = @format)
|
131
|
+
case format
|
132
|
+
when :xml
|
133
|
+
return 'application/xml'
|
134
|
+
when :json
|
135
|
+
return 'application/json'
|
136
|
+
when :atom
|
137
|
+
return 'application/atom+xml'
|
138
|
+
end
|
114
139
|
end
|
115
140
|
|
116
141
|
def redirect?(response)
|
@@ -122,21 +147,21 @@ module AMEE
|
|
122
147
|
when '200'
|
123
148
|
return true
|
124
149
|
when '403'
|
125
|
-
raise AMEE::PermissionDenied.new("You do not have permission to perform the requested operation")
|
150
|
+
raise AMEE::PermissionDenied.new("You do not have permission to perform the requested operation. AMEE Response: #{response.body}")
|
126
151
|
when '401'
|
127
152
|
authenticate
|
128
153
|
return false
|
129
154
|
else
|
130
|
-
raise AMEE::UnknownError.new("An error occurred while talking to AMEE: HTTP response code #{response.code}")
|
155
|
+
raise AMEE::UnknownError.new("An error occurred while talking to AMEE: HTTP response code #{response.code}. AMEE Response: #{response.body}")
|
131
156
|
end
|
132
157
|
end
|
133
158
|
|
134
|
-
def do_request(request)
|
159
|
+
def do_request(request, format = @format)
|
135
160
|
# Open HTTP connection
|
136
161
|
@http.start
|
137
162
|
# Do request
|
138
163
|
begin
|
139
|
-
response = send_request(request)
|
164
|
+
response = send_request(request, format)
|
140
165
|
end while !response_ok?(response)
|
141
166
|
# Return body of response
|
142
167
|
return response.body
|
@@ -147,9 +172,9 @@ module AMEE
|
|
147
172
|
@http.finish if @http.started?
|
148
173
|
end
|
149
174
|
|
150
|
-
def send_request(request)
|
175
|
+
def send_request(request, format = @format)
|
151
176
|
request['authToken'] = @auth_token
|
152
|
-
request['Accept'] = content_type
|
177
|
+
request['Accept'] = content_type(format)
|
153
178
|
response = @http.request(request)
|
154
179
|
# Handle 404s
|
155
180
|
if response.code == '404'
|
data/lib/amee/exceptions.rb
CHANGED
@@ -7,13 +7,15 @@ module AMEE
|
|
7
7
|
def initialize(data = {})
|
8
8
|
@children = data ? data[:children] : []
|
9
9
|
@items = data ? data[:items] : []
|
10
|
-
@
|
10
|
+
@total_amount = data[:total_amount]
|
11
|
+
@total_amount_unit = data[:total_amount_unit]
|
11
12
|
super
|
12
13
|
end
|
13
14
|
|
14
15
|
attr_reader :children
|
15
16
|
attr_reader :items
|
16
|
-
attr_reader :
|
17
|
+
attr_reader :total_amount
|
18
|
+
attr_reader :total_amount_unit
|
17
19
|
|
18
20
|
def self.parse_json_profile_item(item)
|
19
21
|
item_data = {}
|
@@ -22,14 +24,33 @@ module AMEE
|
|
22
24
|
case key
|
23
25
|
when 'dataItemLabel', 'dataItemUid', 'name', 'path', 'uid'
|
24
26
|
item_data[key.to_sym] = value
|
27
|
+
when 'dataItem'
|
28
|
+
item_data[:dataItemLabel] = value['Label']
|
29
|
+
item_data[:dataItemUid] = value['uid']
|
25
30
|
when 'created', 'modified', 'label' # ignore these
|
26
31
|
nil
|
27
32
|
when 'validFrom'
|
28
33
|
item_data[:validFrom] = DateTime.strptime(value, "%Y%m%d")
|
34
|
+
when 'startDate'
|
35
|
+
item_data[:startDate] = DateTime.parse(value)
|
36
|
+
when 'endDate'
|
37
|
+
item_data[:endDate] = DateTime.parse(value) rescue nil
|
29
38
|
when 'end'
|
30
39
|
item_data[:end] = (value == "true")
|
31
40
|
when 'amountPerMonth'
|
32
41
|
item_data[:amountPerMonth] = value.to_f
|
42
|
+
when 'amount'
|
43
|
+
item_data[:amount] = value['value'].to_f
|
44
|
+
item_data[:amount_unit] = value['unit']
|
45
|
+
when 'itemValues'
|
46
|
+
value.each do |itemval|
|
47
|
+
path = itemval['path'].to_sym
|
48
|
+
item_data[:values][path.to_sym] = {}
|
49
|
+
item_data[:values][path.to_sym][:name] = itemval['name']
|
50
|
+
item_data[:values][path.to_sym][:value] = itemval['value']
|
51
|
+
item_data[:values][path.to_sym][:unit] = itemval['unit']
|
52
|
+
item_data[:values][path.to_sym][:per_unit] = itemval['perUnit']
|
53
|
+
end
|
33
54
|
else
|
34
55
|
item_data[:values][key.to_sym] = value
|
35
56
|
end
|
@@ -72,7 +93,8 @@ module AMEE
|
|
72
93
|
data[:profile_date] = DateTime.strptime(doc['profileDate'], "%Y%m")
|
73
94
|
data[:name] = doc['dataCategory']['name']
|
74
95
|
data[:path] = doc['path']
|
75
|
-
data[:
|
96
|
+
data[:total_amount] = doc['totalAmountPerMonth']
|
97
|
+
data[:total_amount_unit] = "kg/month"
|
76
98
|
data[:children] = []
|
77
99
|
if doc['children'] && doc['children']['dataCategories']
|
78
100
|
doc['children']['dataCategories'].each do |child|
|
@@ -92,6 +114,35 @@ module AMEE
|
|
92
114
|
raise AMEE::BadData.new("Couldn't load ProfileCategory from JSON data. Check that your URL is correct.")
|
93
115
|
end
|
94
116
|
|
117
|
+
def self.from_v2_json(json)
|
118
|
+
# Parse json
|
119
|
+
doc = JSON.parse(json)
|
120
|
+
data = {}
|
121
|
+
data[:profile_uid] = doc['profile']['uid']
|
122
|
+
#data[:profile_date] = DateTime.strptime(doc['profileDate'], "%Y%m")
|
123
|
+
data[:name] = doc['dataCategory']['name']
|
124
|
+
data[:path] = doc['path']
|
125
|
+
data[:total_amount] = doc['totalAmount']['value'].to_f rescue nil
|
126
|
+
data[:total_amount_unit] = doc['totalAmount']['unit'] rescue nil
|
127
|
+
data[:children] = []
|
128
|
+
if doc['profileCategories']
|
129
|
+
doc['profileCategories'].each do |child|
|
130
|
+
data[:children] << parse_json_profile_category(child)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
data[:items] = []
|
134
|
+
profile_items = []
|
135
|
+
profile_items.concat doc['profileItems'] rescue nil
|
136
|
+
profile_items << doc['profileItem'] unless doc['profileItem'].nil?
|
137
|
+
profile_items.each do |item|
|
138
|
+
data[:items] << parse_json_profile_item(item)
|
139
|
+
end
|
140
|
+
# Create object
|
141
|
+
Category.new(data)
|
142
|
+
rescue
|
143
|
+
raise AMEE::BadData.new("Couldn't load ProfileCategory from V2 JSON data. Check that your URL is correct.")
|
144
|
+
end
|
145
|
+
|
95
146
|
def self.parse_xml_profile_item(item)
|
96
147
|
item_data = {}
|
97
148
|
item_data[:values] = {}
|
@@ -149,7 +200,8 @@ module AMEE
|
|
149
200
|
data[:profile_date] = DateTime.strptime(REXML::XPath.first(doc, "/Resources/ProfileCategoryResource/ProfileDate").text, "%Y%m")
|
150
201
|
data[:name] = REXML::XPath.first(doc, '/Resources/ProfileCategoryResource/DataCategory/Name').text
|
151
202
|
data[:path] = REXML::XPath.first(doc, '/Resources/ProfileCategoryResource/Path').text || ""
|
152
|
-
data[:
|
203
|
+
data[:total_amount] = REXML::XPath.first(doc, '/Resources/ProfileCategoryResource/TotalAmountPerMonth').text.to_f rescue nil
|
204
|
+
data[:total_amount_unit] = "kg/month"
|
153
205
|
data[:children] = []
|
154
206
|
REXML::XPath.each(doc, '/Resources/ProfileCategoryResource/Children/ProfileCategories/DataCategory') do |child|
|
155
207
|
category_data = {}
|
@@ -174,6 +226,135 @@ module AMEE
|
|
174
226
|
raise AMEE::BadData.new("Couldn't load ProfileCategory from XML data. Check that your URL is correct.")
|
175
227
|
end
|
176
228
|
|
229
|
+
def self.parse_v2_xml_profile_item(item)
|
230
|
+
item_data = {}
|
231
|
+
item_data[:values] = {}
|
232
|
+
item.elements.each do |element|
|
233
|
+
key = element.name
|
234
|
+
case key.downcase
|
235
|
+
when 'name', 'path'
|
236
|
+
item_data[key.downcase.to_sym] = element.text
|
237
|
+
when 'dataitem'
|
238
|
+
item_data[:dataItemLabel] = element.elements['Label'].text
|
239
|
+
item_data[:dataItemUid] = element.attributes['uid'].to_s
|
240
|
+
when 'validfrom'
|
241
|
+
item_data[:validFrom] = DateTime.strptime(element.text, "%Y%m%d")
|
242
|
+
when 'startdate'
|
243
|
+
item_data[:startDate] = DateTime.parse(element.text)
|
244
|
+
when 'enddate'
|
245
|
+
item_data[:endDate] = DateTime.parse(element.text) if element.text
|
246
|
+
when 'end'
|
247
|
+
item_data[:end] = (element.text == "true")
|
248
|
+
when 'amount'
|
249
|
+
item_data[:amount] = element.text.to_f
|
250
|
+
item_data[:amount_unit] = element.attributes['unit'].to_s
|
251
|
+
when 'itemvalues'
|
252
|
+
element.elements.each do |itemvalue|
|
253
|
+
path = itemvalue.elements['Path'].text
|
254
|
+
item_data[:values][path.to_sym] = {}
|
255
|
+
item_data[:values][path.to_sym][:name] = itemvalue.elements['Name'].text
|
256
|
+
item_data[:values][path.to_sym][:value] = itemvalue.elements['Value'].text || "0"
|
257
|
+
item_data[:values][path.to_sym][:unit] = itemvalue.elements['Unit'].text
|
258
|
+
item_data[:values][path.to_sym][:per_unit] = itemvalue.elements['PerUnit'].text
|
259
|
+
end
|
260
|
+
else
|
261
|
+
item_data[:values][key.to_sym] = element.text
|
262
|
+
end
|
263
|
+
end
|
264
|
+
item_data[:uid] = item.attributes['uid'].to_s
|
265
|
+
item_data[:path] ||= item_data[:uid] # Fill in path if not retrieved from response
|
266
|
+
return item_data
|
267
|
+
end
|
268
|
+
|
269
|
+
def self.from_v2_xml(xml)
|
270
|
+
# Parse XML
|
271
|
+
doc = REXML::Document.new(xml)
|
272
|
+
data = {}
|
273
|
+
data[:profile_uid] = REXML::XPath.first(doc, "/Resources/ProfileCategoryResource/Profile/@uid").to_s
|
274
|
+
#data[:profile_date] = DateTime.strptime(REXML::XPath.first(doc, "/Resources/ProfileCategoryResource/ProfileDate").text, "%Y%m")
|
275
|
+
data[:name] = REXML::XPath.first(doc, '/Resources/ProfileCategoryResource/DataCategory/Name').text
|
276
|
+
data[:path] = REXML::XPath.first(doc, '/Resources/ProfileCategoryResource/Path').text || ""
|
277
|
+
data[:total_amount] = REXML::XPath.first(doc, '/Resources/ProfileCategoryResource/TotalAmount').text.to_f rescue nil
|
278
|
+
data[:total_amount_unit] = REXML::XPath.first(doc, '/Resources/ProfileCategoryResource/TotalAmount/@unit').to_s rescue nil
|
279
|
+
data[:children] = []
|
280
|
+
REXML::XPath.each(doc, '/Resources/ProfileCategoryResource/ProfileCategories/DataCategory') do |child|
|
281
|
+
category_data = {}
|
282
|
+
category_data[:name] = child.elements['Name'].text
|
283
|
+
category_data[:path] = child.elements['Path'].text
|
284
|
+
category_data[:uid] = child.attributes['uid'].to_s
|
285
|
+
data[:children] << category_data
|
286
|
+
end
|
287
|
+
REXML::XPath.each(doc, '/Resources/ProfileCategoryResource/Children/ProfileCategories/ProfileCategory') do |child|
|
288
|
+
data[:children] << parse_xml_profile_category(child)
|
289
|
+
end
|
290
|
+
data[:items] = []
|
291
|
+
REXML::XPath.each(doc, '/Resources/ProfileCategoryResource/ProfileItems/ProfileItem') do |item|
|
292
|
+
data[:items] << parse_v2_xml_profile_item(item)
|
293
|
+
end
|
294
|
+
REXML::XPath.each(doc, '/Resources/ProfileCategoryResource/ProfileItem') do |item|
|
295
|
+
data[:items] << parse_v2_xml_profile_item(item)
|
296
|
+
end
|
297
|
+
# Create object
|
298
|
+
Category.new(data)
|
299
|
+
rescue
|
300
|
+
raise AMEE::BadData.new("Couldn't load ProfileCategory from V2 XML data. Check that your URL is correct.")
|
301
|
+
end
|
302
|
+
|
303
|
+
def self.from_v2_atom(response)
|
304
|
+
# Parse XML
|
305
|
+
doc = REXML::Document.new(response)
|
306
|
+
data = {}
|
307
|
+
data[:profile_uid] = REXML::XPath.first(doc, "/feed/@xml:base").to_s.match("/profiles/(.*?)/")[1]
|
308
|
+
# data[:profile_date] = DateTime.strptime(REXML::XPath.first(doc, "/Resources/ProfileCategoryResource/ProfileDate").text, "%Y%m")
|
309
|
+
# data[:name] = REXML::XPath.first(doc, '/feed/title').text
|
310
|
+
data[:path] = REXML::XPath.first(doc, "/feed/@xml:base").to_s.match("/profiles/.*?(/.*)")[1]
|
311
|
+
data[:total_amount] = REXML::XPath.first(doc, '/feed/amee:totalAmount').text.to_f rescue nil
|
312
|
+
data[:total_amount_unit] = REXML::XPath.first(doc, '/feed/amee:totalAmount/@unit').to_s rescue nil
|
313
|
+
data[:children] = []
|
314
|
+
# REXML::XPath.each(doc, '/Resources/ProfileCategoryResource/ProfileCategories/DataCategory') do |child|
|
315
|
+
# category_data = {}
|
316
|
+
# category_data[:name] = child.elements['Name'].text
|
317
|
+
# category_data[:path] = child.elements['Path'].text
|
318
|
+
# category_data[:uid] = child.attributes['uid'].to_s
|
319
|
+
# data[:children] << category_data
|
320
|
+
# end
|
321
|
+
# REXML::XPath.each(doc, '/Resources/ProfileCategoryResource/Children/ProfileCategories/ProfileCategory') do |child|
|
322
|
+
# data[:children] << parse_xml_profile_category(child)
|
323
|
+
# end
|
324
|
+
data[:items] = []
|
325
|
+
REXML::XPath.each(doc, '/feed/entry') do |entry|
|
326
|
+
item = {}
|
327
|
+
item[:uid] = entry.elements['id'].text.match("urn:item:(.*)")[1]
|
328
|
+
item[:name] = entry.elements['title'].text
|
329
|
+
item[:path] = item[:uid]
|
330
|
+
# data[:dataItemLabel].should == "gas"
|
331
|
+
# data[:dataItemUid].should == "66056991EE23"
|
332
|
+
item[:amount] = entry.elements['amee:amount'].text.to_f rescue nil
|
333
|
+
item[:amount_unit] = entry.elements['amee:amount'].attributes['unit'].to_s rescue nil
|
334
|
+
item[:startDate] = DateTime.parse(entry.elements['amee:startDate'].text)
|
335
|
+
item[:endDate] = DateTime.parse(entry.elements['amee:endDate'].text) rescue nil
|
336
|
+
item[:values] = {}
|
337
|
+
entry.elements.each do |itemvalue|
|
338
|
+
if itemvalue.name == 'itemValue'
|
339
|
+
path = itemvalue.elements['link'].attributes['href'].to_s.match(".*/(.*)")[1]
|
340
|
+
x = {}
|
341
|
+
x[:path] = path
|
342
|
+
x[:name] = itemvalue.elements['amee:name'].text
|
343
|
+
x[:value] = itemvalue.elements['amee:value'].text unless itemvalue.elements['amee:value'].text == "N/A"
|
344
|
+
x[:value] ||= "0"
|
345
|
+
x[:unit] = itemvalue.elements['amee:unit'].text rescue nil
|
346
|
+
x[:per_unit] = itemvalue.elements['amee:perUnit'].text rescue nil
|
347
|
+
item[:values][path.to_sym] = x
|
348
|
+
end
|
349
|
+
end
|
350
|
+
data[:items] << item
|
351
|
+
end
|
352
|
+
# Create object
|
353
|
+
Category.new(data)
|
354
|
+
rescue
|
355
|
+
raise AMEE::BadData.new("Couldn't load ProfileCategory from V2 Atom data. Check that your URL is correct.")
|
356
|
+
end
|
357
|
+
|
177
358
|
def self.get_history(connection, path, num_months, end_date = Date.today, items_per_page = 10)
|
178
359
|
month = end_date.month
|
179
360
|
year = end_date.year
|
@@ -201,9 +382,15 @@ module AMEE
|
|
201
382
|
|
202
383
|
def self.parse(connection, response)
|
203
384
|
# Parse data from response
|
204
|
-
if response.
|
385
|
+
if response.is_v2_json?
|
386
|
+
cat = Category.from_v2_json(response)
|
387
|
+
elsif response.is_json?
|
205
388
|
cat = Category.from_json(response)
|
206
|
-
|
389
|
+
elsif response.is_v2_atom?
|
390
|
+
cat = Category.from_v2_atom(response)
|
391
|
+
elsif response.is_v2_xml?
|
392
|
+
cat = Category.from_v2_xml(response)
|
393
|
+
elsif response.is_xml?
|
207
394
|
cat = Category.from_xml(response)
|
208
395
|
end
|
209
396
|
# Store connection in object for future use
|
@@ -213,10 +400,25 @@ module AMEE
|
|
213
400
|
end
|
214
401
|
|
215
402
|
|
216
|
-
def self.get(connection, path,
|
403
|
+
def self.get(connection, path, options = {})
|
404
|
+
unless options.is_a?(Hash)
|
405
|
+
raise AMEE::ArgumentError.new("Third argument must be a hash of options!")
|
406
|
+
end
|
407
|
+
# Convert to AMEE options
|
408
|
+
if options[:start_date] && connection.version < 2
|
409
|
+
options[:profileDate] = options[:start_date].amee1_month
|
410
|
+
elsif options[:start_date] && connection.version >= 2
|
411
|
+
options[:startDate] = options[:start_date].amee2schema
|
412
|
+
end
|
413
|
+
options.delete(:start_date)
|
414
|
+
if options[:end_date] && connection.version >= 2
|
415
|
+
options[:endDate] = options[:end_date].amee2schema
|
416
|
+
end
|
417
|
+
options.delete(:end_date)
|
418
|
+
if options[:duration] && connection.version >= 2
|
419
|
+
options[:duration] = "PT#{options[:duration] * 86400}S"
|
420
|
+
end
|
217
421
|
# Load data from path
|
218
|
-
options = {:profileDate => for_date.strftime("%Y%m"), :itemsPerPage => items_per_page}
|
219
|
-
options[:recurse] = true if recurse == true
|
220
422
|
response = connection.get(path, options)
|
221
423
|
return Category.parse(connection, response)
|
222
424
|
rescue
|
@@ -230,7 +432,12 @@ module AMEE
|
|
230
432
|
def item(options)
|
231
433
|
# Search fields - from most specific to least specific
|
232
434
|
item = items.find{ |x| x[:uid] == options[:uid] || x[:name] == options[:name] || x[:dataItemUid] == options[:dataItemUid] || x[:dataItemLabel] == options[:dataItemLabel] }
|
233
|
-
|
435
|
+
# Pass through some options
|
436
|
+
new_opts = {}
|
437
|
+
new_opts[:returnUnit] = options[:returnUnit] if options[:returnUnit]
|
438
|
+
new_opts[:returnPerUnit] = options[:returnPerUnit] if options[:returnPerUnit]
|
439
|
+
new_opts[:format] = options[:format] if options[:format]
|
440
|
+
item ? AMEE::Profile::Item.get(connection, "#{full_path}/#{item[:path]}", new_opts) : nil
|
234
441
|
end
|
235
442
|
|
236
443
|
end
|
data/lib/amee/profile_item.rb
CHANGED
@@ -4,19 +4,33 @@ module AMEE
|
|
4
4
|
|
5
5
|
def initialize(data = {})
|
6
6
|
@values = data ? data[:values] : []
|
7
|
-
@
|
8
|
-
@
|
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 )
|
9
11
|
@data_item_uid = data[:data_item_uid]
|
10
|
-
@end = data[:end]
|
11
12
|
super
|
12
13
|
end
|
13
14
|
|
14
15
|
attr_reader :values
|
15
|
-
attr_reader :
|
16
|
-
attr_reader :
|
17
|
-
attr_reader :
|
16
|
+
attr_reader :total_amount
|
17
|
+
attr_reader :total_amount_unit
|
18
|
+
attr_reader :start_date
|
19
|
+
attr_reader :end_date
|
18
20
|
attr_reader :data_item_uid
|
19
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
|
+
|
20
34
|
def self.from_json(json)
|
21
35
|
# Parse json
|
22
36
|
doc = JSON.parse(json)
|
@@ -26,7 +40,8 @@ module AMEE
|
|
26
40
|
data[:uid] = doc['profileItem']['uid']
|
27
41
|
data[:name] = doc['profileItem']['name']
|
28
42
|
data[:path] = doc['path']
|
29
|
-
data[:
|
43
|
+
data[:total_amount] = doc['profileItem']['amountPerMonth']
|
44
|
+
data[:total_amount_unit] = "kg/month"
|
30
45
|
data[:valid_from] = DateTime.strptime(doc['profileItem']['validFrom'], "%Y%m%d")
|
31
46
|
data[:end] = doc['profileItem']['end'] == "false" ? false : true
|
32
47
|
data[:values] = []
|
@@ -46,6 +61,38 @@ module AMEE
|
|
46
61
|
raise AMEE::BadData.new("Couldn't load ProfileItem from JSON data. Check that your URL is correct.")
|
47
62
|
end
|
48
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.")
|
94
|
+
end
|
95
|
+
|
49
96
|
def self.from_xml(xml)
|
50
97
|
# Parse XML
|
51
98
|
doc = REXML::Document.new(xml)
|
@@ -55,7 +102,8 @@ module AMEE
|
|
55
102
|
data[:uid] = REXML::XPath.first(doc, "/Resources/ProfileItemResource/ProfileItem/@uid").to_s
|
56
103
|
data[:name] = REXML::XPath.first(doc, '/Resources/ProfileItemResource/ProfileItem/Name').text
|
57
104
|
data[:path] = REXML::XPath.first(doc, '/Resources/ProfileItemResource/Path').text || ""
|
58
|
-
data[:
|
105
|
+
data[:total_amount] = REXML::XPath.first(doc, '/Resources/ProfileItemResource/ProfileItem/AmountPerMonth').text.to_f rescue nil
|
106
|
+
data[:total_amount_unit] = "kg/month"
|
59
107
|
data[:valid_from] = DateTime.strptime(REXML::XPath.first(doc, "/Resources/ProfileItemResource/ProfileItem/ValidFrom").text, "%Y%m%d")
|
60
108
|
data[:end] = REXML::XPath.first(doc, '/Resources/ProfileItemResource/ProfileItem/End').text == "false" ? false : true
|
61
109
|
data[:values] = []
|
@@ -78,32 +126,139 @@ module AMEE
|
|
78
126
|
raise AMEE::BadData.new("Couldn't load ProfileItem from XML data. Check that your URL is correct.")
|
79
127
|
end
|
80
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.")
|
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.")
|
191
|
+
end
|
192
|
+
|
81
193
|
def self.parse(connection, response)
|
82
194
|
# Parse data from response
|
83
|
-
if response.
|
84
|
-
|
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)
|
85
203
|
else
|
86
|
-
|
204
|
+
item = Item.from_xml(response)
|
87
205
|
end
|
88
206
|
# Store connection in object for future use
|
89
|
-
|
207
|
+
item.connection = connection
|
90
208
|
# Done
|
91
|
-
return
|
209
|
+
return item
|
92
210
|
end
|
93
211
|
|
94
|
-
|
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].amee2schema
|
221
|
+
end
|
222
|
+
options.delete(:start_date)
|
223
|
+
if options[:end_date] && category.connection.version >= 2
|
224
|
+
options[:endDate] = options[:end_date].amee2schema
|
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
|
95
230
|
# Load data from path
|
96
|
-
response = connection.get(path,
|
231
|
+
response = connection.get(path, options)
|
97
232
|
return Item.parse(connection, response)
|
98
233
|
rescue
|
99
234
|
raise AMEE::BadData.new("Couldn't load ProfileItem. Check that your URL is correct.")
|
100
235
|
end
|
101
236
|
|
102
|
-
def self.create(
|
237
|
+
def self.create(category, data_item_uid, options = {})
|
238
|
+
# Store format if set
|
239
|
+
format = options[:format]
|
240
|
+
unless options.is_a?(Hash)
|
241
|
+
raise AMEE::ArgumentError.new("Third argument must be a hash of options!")
|
242
|
+
end
|
243
|
+
# Set dates
|
244
|
+
if options[:start_date] && category.connection.version < 2
|
245
|
+
options[:profileDate] = options[:start_date].amee1_month
|
246
|
+
elsif options[:start_date] && category.connection.version >= 2
|
247
|
+
options[:startDate] = options[:start_date].amee2schema
|
248
|
+
end
|
249
|
+
options.delete(:start_date)
|
250
|
+
if options[:end_date] && category.connection.version >= 2
|
251
|
+
options[:endDate] = options[:end_date].amee2schema
|
252
|
+
end
|
253
|
+
options.delete(:end_date)
|
254
|
+
if options[:duration] && category.connection.version >= 2
|
255
|
+
options[:duration] = "PT#{options[:duration] * 86400}S"
|
256
|
+
end
|
103
257
|
# Send data to path
|
104
258
|
options.merge! :dataItemUid => data_item_uid
|
105
|
-
response =
|
106
|
-
category = Category.parse(
|
259
|
+
response = category.connection.post(category.full_path, options)
|
260
|
+
category = Category.parse(category.connection, response)
|
261
|
+
options.merge!(:format => format) if format
|
107
262
|
return category.item(options)
|
108
263
|
rescue
|
109
264
|
raise AMEE::BadData.new("Couldn't create ProfileItem. Check that your information is correct.")
|
@@ -112,8 +267,8 @@ module AMEE
|
|
112
267
|
def self.update(connection, path, options = {})
|
113
268
|
response = connection.put(path, options)
|
114
269
|
return Item.parse(connection, response)
|
115
|
-
|
116
|
-
|
270
|
+
rescue
|
271
|
+
raise AMEE::BadData.new("Couldn't update ProfileItem. Check that your information is correct.")
|
117
272
|
end
|
118
273
|
|
119
274
|
def update(options = {})
|
data/lib/amee/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: Floppy-amee
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Smith
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-02-
|
12
|
+
date: 2009-02-13 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|