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.
data/COPYING ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2008 James Smith (james@floppy.org.uk)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,106 @@
1
+ == AMEE-Ruby
2
+
3
+ A gem to provide a Ruby interface to the AMEE carbon calculator (http://amee.cc)
4
+
5
+ Licensed under the MIT license (See COPYING file for details)
6
+
7
+ Author: James Smith (james@floppy.org.uk / http://www.floppy.org.uk)
8
+
9
+ Homepage: http://github.com/Floppy/amee-ruby
10
+
11
+ Documentation: http://docs.github.com/Floppy/amee-ruby
12
+
13
+ == INSTALLATION
14
+
15
+ 1) Enable gems from github, if you haven't already done so (rubygems >= 1.2):
16
+ > sudo gem sources -a http://gems.github.com
17
+
18
+ 2) Install gem
19
+ > sudo gem install Floppy-amee
20
+
21
+ == IMPORTANT CHANGES in 2.0.25
22
+
23
+ If you are using the $amee connection in your Rails apps, this is now deprecated
24
+ and will be removed in future releases. See the "Rails" section below for details
25
+ of what you should use instead.
26
+
27
+ == USAGE
28
+
29
+ Currently, you can read DataCategories, DataItems and DataItemValues. See
30
+ examples/view_data_*.rb for simple usage examples. You can also get the list
31
+ of available Profiles, and create and delete them. See examples/list_profiles.rb
32
+ and examples/create_profile.rb for details. You can also load ProfileCategories,
33
+ and load, create and update ProfileItems.
34
+
35
+ The gem will use the AMEE JSON API if the JSON gem is installed on the local
36
+ system. Otherwise the XML API will be used.
37
+
38
+ == SUPPORT
39
+ Create Read Update Delete
40
+ DataCategories N Y N N
41
+ DataItems N Y N N
42
+ DataItemValues N Y N Y
43
+ Profile List - Y - -
44
+ Profiles Y - - Y
45
+ ProfileCategories - Y - -
46
+ - drilldown - Y - -
47
+ ProfileItems Y Y Y Y
48
+
49
+ == INTERACTIVE SHELL
50
+
51
+ You can use the 'ameesh' app to interactively explore the AMEE data area. Run
52
+ 'ameesh -u USERNAME -p PASSWORD -s SERVER' to try it out. Source code for this
53
+ tool is in bin/ameesh and lib/amee/shell.rb. Profiles are not accessible through
54
+ this interface yet.
55
+
56
+ == RAILS
57
+
58
+ This gem can also be used as a Rails plugin. You can either extract it into
59
+ vendor/plugins, or use the new-style config.gem command in environment.rb. For
60
+ example:
61
+
62
+ config.gem "Floppy-amee", :lib => "amee", :source => "http://gems.github.com", :version => '>= 0.3.0'
63
+
64
+ If you copy amee.example.yml from the gem source directory to amee.yml in your
65
+ app's config directory, a persistent AMEE connection will be available from
66
+ AMEE::Rails#connection, which you can use anywhere. In your controllers, you can
67
+ also use the global_amee_connection function to access the same global connection.
68
+
69
+ data = AMEE::Data::Category.root(global_amee_connection)
70
+
71
+ If you do not use this facility, you will have to create your own connection
72
+ objects and manage them yourself, which you can do using AMEE::Connection#new
73
+
74
+ There is a helper for ActiveRecord models which should be linked to an AMEE profile.
75
+ By adding:
76
+
77
+ has_amee_profile
78
+
79
+ to your model, and by adding an amee_profile:string field to the model in the
80
+ database, an AMEE profile will be automatically created and destroyed with your
81
+ model. By overriding the function amee_save in your model, you can store data in
82
+ AMEE when your model is saved.
83
+
84
+ == CACHING
85
+
86
+ The AMEE::Connection object implements an optional cache for GET requests. This is
87
+ currently a versy simple implementation which caches the result of all GET requests
88
+ until a POST, PUT, or DELETE is executed, at which point the cache is cleared. To
89
+ enable caching, set the enable_caching parameter of AMEE::Connection.new to true.
90
+ Caching is disabled by default.
91
+
92
+ == UPGRADING TO VERSION > 2
93
+
94
+ There are a few changes to the API exposed by this gem for version 2. The main
95
+ ones are:
96
+
97
+ 1) AMEE::Connection#new takes a hash of options instead of an explicit parameter list.
98
+ Whereas before you would have used new(server, username, password, use_json, enable_cache, enable_debug)
99
+ you would now use new(server, username, password, :format => :json, :enable_caching => true, :enable_debug => true)
100
+
101
+ 2) Many get functions take a hash of options instead of explicit date and itemsPerPage parameters.
102
+ get(... , :start_date => {your_date}, :itemsPerPage => 20)
103
+
104
+ 3) total_amount_per_month functions have been replaced with total_amount. There are also
105
+ total_amount_unit and total_amount_per_unit functions which give the units that the total
106
+ amount is in.
@@ -0,0 +1,12 @@
1
+ development:
2
+ server: stage.amee.com
3
+ username: your_amee_username
4
+ password: your_amee_password
5
+ test:
6
+ server: stage.amee.com
7
+ username: your_amee_username
8
+ password: your_amee_password
9
+ production:
10
+ server: live.amee.com
11
+ username: your_amee_username
12
+ password: your_amee_password
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ shell_lib = File.dirname(__FILE__) + '/../lib/amee/shell'
4
+ irb_name = RUBY_PLATFORM =~ /mswin32/ ? 'irb.bat' : 'irb'
5
+
6
+ require 'optparse'
7
+ # Command-line options - get username, password, and server
8
+ options = {}
9
+ OptionParser.new do |opts|
10
+ opts.on("-u USERNAME", "AMEE username") do |u|
11
+ options[:username] = u
12
+ end
13
+ opts.on("-p PASSWORD", "AMEE password") do |p|
14
+ options[:password] = p
15
+ end
16
+ opts.on("-s SERVER", "AMEE server") do |s|
17
+ options[:server] = s
18
+ end
19
+ end.parse!
20
+
21
+ # Set environment variables for irb
22
+ ENV['AMEE_SERVER'] = options[:server]
23
+ ENV['AMEE_USERNAME'] = options[:username]
24
+ ENV['AMEE_PASSWORD'] = options[:password]
25
+
26
+ if options[:server].nil? || options[:username].nil? || options[:password].nil?
27
+ puts "Please provide connection details. Run 'ameesh --help' for details."
28
+ else
29
+ exec "#{irb_name} -r #{shell_lib} --simple-prompt"
30
+ end
@@ -0,0 +1,27 @@
1
+ dir = File.dirname(__FILE__) + '/../lib'
2
+ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
3
+
4
+ #require 'rubygems'
5
+ require 'amee'
6
+ require 'optparse'
7
+
8
+ # Command-line options - get username, password, and server
9
+ options = {}
10
+ OptionParser.new do |opts|
11
+ opts.on("-u", "--username USERNAME", "AMEE username") do |u|
12
+ options[:username] = u
13
+ end
14
+ opts.on("-p", "--password PASSWORD", "AMEE password") do |p|
15
+ options[:password] = p
16
+ end
17
+ opts.on("-s", "--server SERVER", "AMEE server") do |s|
18
+ options[:server] = s
19
+ end
20
+ end.parse!
21
+
22
+ # Connect
23
+ connection = AMEE::Connection.new(options[:server], options[:username], options[:password])
24
+
25
+ # Create a new profile
26
+ profile = AMEE::Profile::Profile.create(connection)
27
+ puts "#{profile.uid} created"
@@ -0,0 +1,33 @@
1
+ dir = File.dirname(__FILE__) + '/../lib'
2
+ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
3
+
4
+ #require 'rubygems'
5
+ require 'amee'
6
+ require 'optparse'
7
+
8
+ # Command-line options - get username, password, and server
9
+ options = {}
10
+ OptionParser.new do |opts|
11
+ opts.on("-u", "--username USERNAME", "AMEE username") do |u|
12
+ options[:username] = u
13
+ end
14
+ opts.on("-p", "--password PASSWORD", "AMEE password") do |p|
15
+ options[:password] = p
16
+ end
17
+ opts.on("-s", "--server SERVER", "AMEE server") do |s|
18
+ options[:server] = s
19
+ end
20
+ end.parse!
21
+
22
+ # Connect
23
+ connection = AMEE::Connection.new(options[:server], options[:username], options[:password])
24
+
25
+ # Create a new profile item
26
+ category = AMEE::Profile::Category.get(connection, ARGV[0])
27
+ puts "loaded category #{category.name}"
28
+ item = AMEE::Profile::Item.create(category, ARGV[1])
29
+ if item
30
+ puts "created item in #{category.name} OK"
31
+ else
32
+ puts "error creating item in #{category.name}"
33
+ end
@@ -0,0 +1,29 @@
1
+ require 'rubygems'
2
+ require 'amee'
3
+ require 'optparse'
4
+
5
+ # Command-line options - get username, password, and server
6
+ options = {}
7
+ OptionParser.new do |opts|
8
+ opts.on("-u", "--username USERNAME", "AMEE username") do |u|
9
+ options[:username] = u
10
+ end
11
+ opts.on("-p", "--password PASSWORD", "AMEE password") do |p|
12
+ options[:password] = p
13
+ end
14
+ opts.on("-s", "--server SERVER", "AMEE server") do |s|
15
+ options[:server] = s
16
+ end
17
+ end.parse!
18
+
19
+ # Connect
20
+ connection = AMEE::Connection.new(options[:server], options[:username], options[:password])
21
+
22
+ # List all available profiles
23
+ profiles = AMEE::Profile::Profile.list(connection)
24
+ puts "#{profiles.size} #{profiles.size == 1 ? "profile" : "profiles"} available in AMEE:"
25
+ profiles.each do |p|
26
+ puts p.uid
27
+ end
28
+
29
+
@@ -0,0 +1,40 @@
1
+ require 'rubygems'
2
+ require 'amee'
3
+ require 'optparse'
4
+
5
+ # Command-line options - get username, password, and server
6
+ options = {}
7
+ OptionParser.new do |opts|
8
+ opts.on("-u", "--username USERNAME", "AMEE username") do |u|
9
+ options[:username] = u
10
+ end
11
+ opts.on("-p", "--password PASSWORD", "AMEE password") do |p|
12
+ options[:password] = p
13
+ end
14
+ opts.on("-s", "--server SERVER", "AMEE server") do |s|
15
+ options[:server] = s
16
+ end
17
+ end.parse!
18
+
19
+ # Connect
20
+ connection = AMEE::Connection.new(options[:server], options[:username], options[:password])
21
+
22
+ # For each path in arg list, show details
23
+ ARGV.each do |path|
24
+ cat = AMEE::Data::Category.get(connection, path)
25
+ puts "---------------------"
26
+ puts "Category: #{cat.name}"
27
+ puts "Path: #{cat.full_path}"
28
+ puts "UID: #{cat.uid}"
29
+ puts "Subcategories:"
30
+ cat.children.each do |c|
31
+ puts " - #{c[:path]} (#{c[:name]})"
32
+ end
33
+ puts "Items:"
34
+ cat.items.each do |i|
35
+ puts " - #{i[:path]} (#{i[:label]})"
36
+ end
37
+
38
+ end
39
+
40
+
@@ -0,0 +1,37 @@
1
+ require 'rubygems'
2
+ require 'amee'
3
+ require 'optparse'
4
+
5
+ # Command-line options - get username, password, and server
6
+ options = {}
7
+ OptionParser.new do |opts|
8
+ opts.on("-u", "--username USERNAME", "AMEE username") do |u|
9
+ options[:username] = u
10
+ end
11
+ opts.on("-p", "--password PASSWORD", "AMEE password") do |p|
12
+ options[:password] = p
13
+ end
14
+ opts.on("-s", "--server SERVER", "AMEE server") do |s|
15
+ options[:server] = s
16
+ end
17
+ end.parse!
18
+
19
+ # Connect
20
+ connection = AMEE::Connection.new(options[:server], options[:username], options[:password])
21
+
22
+ # For each path in arg list, show details
23
+ ARGV.each do |path|
24
+ cat = AMEE::Data::Item.get(connection, path)
25
+ puts "---------------------"
26
+ puts "Name: #{cat.name}"
27
+ puts "Path: #{cat.full_path}"
28
+ puts "Label: #{cat.label}"
29
+ puts "UID: #{cat.uid}"
30
+ puts "Values:"
31
+ cat.values.each do |v|
32
+ puts " - #{v[:name]}: #{v[:value]}"
33
+ end
34
+
35
+ end
36
+
37
+
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + "/rails/init"
@@ -0,0 +1,63 @@
1
+ require 'rexml/document'
2
+ require 'activesupport'
3
+
4
+ # We don't NEED the JSON gem, but if it's available, use it.
5
+ begin
6
+ require 'json'
7
+ rescue LoadError
8
+ nil
9
+ end
10
+
11
+ class String
12
+ def is_json?
13
+ slice(0,1) == '{'
14
+ end
15
+ def is_v2_json?
16
+ is_json? && match('"apiVersion"\s?:\s?"2.0"')
17
+ end
18
+ def is_xml?
19
+ slice(0,5) == '<?xml'
20
+ end
21
+ def is_v2_xml?
22
+ is_xml? && include?('<Resources xmlns="http://schemas.amee.cc/2.0">')
23
+ end
24
+ def is_v2_atom?
25
+ is_xml? && (include?('<feed ') || include?('<entry ')) && include?('xmlns:amee="http://schemas.amee.cc/2.0"')
26
+ end
27
+ end
28
+
29
+ require 'amee/version'
30
+ require 'amee/exceptions'
31
+ require 'amee/connection'
32
+ require 'amee/object'
33
+ require 'amee/data_object'
34
+ require 'amee/profile_object'
35
+ require 'amee/data_category'
36
+ require 'amee/data_item'
37
+ require 'amee/data_item_value'
38
+ require 'amee/profile'
39
+ require 'amee/profile_category'
40
+ require 'amee/profile_item'
41
+ require 'amee/profile_item_value'
42
+ require 'amee/drill_down'
43
+ require 'amee/pager'
44
+ require 'amee/item_definition'
45
+ require 'amee/user'
46
+
47
+ class Date
48
+ def amee1_date
49
+ strftime("%Y%m%d")
50
+ end
51
+ def amee1_month
52
+ strftime("%Y%m")
53
+ end
54
+ end
55
+
56
+ class Time
57
+ def amee1_date
58
+ strftime("%Y%m%d")
59
+ end
60
+ def amee1_month
61
+ strftime("%Y%m")
62
+ end
63
+ end
@@ -0,0 +1,236 @@
1
+ require 'net/http'
2
+
3
+ module AMEE
4
+ class Connection
5
+
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
10
+ @server = server
11
+ @username = username
12
+ @password = password
13
+ @auth_token = nil
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!"
17
+ end
18
+ @enable_caching = options[:enable_caching]
19
+ if @enable_caching
20
+ $cache ||= {}
21
+ end
22
+ # Make connection to server
23
+ @http = Net::HTTP.new(@server)
24
+ @http.read_timeout = 5
25
+ @http.set_debug_output($stdout) if options[:enable_debug]
26
+ end
27
+
28
+ attr_reader :format
29
+ attr_reader :server
30
+ attr_reader :username
31
+ attr_reader :password
32
+
33
+ def timeout
34
+ @http.read_timeout
35
+ end
36
+
37
+ def timeout=(t)
38
+ @http.read_timeout = t
39
+ end
40
+
41
+ def version
42
+ authenticate if @version.nil?
43
+ @version
44
+ end
45
+
46
+ def valid?
47
+ @username && @password && @server
48
+ end
49
+
50
+ def authenticated?
51
+ !@auth_token.nil?
52
+ end
53
+
54
+ def get(path, data = {})
55
+ # Allow format override
56
+ format = data.delete(:format) || @format
57
+ # Create URL parameters
58
+ params = []
59
+ data.each_pair do |key, value|
60
+ params << "#{CGI::escape(key.to_s)}=#{CGI::escape(value.to_s)}"
61
+ end
62
+ if params.size > 0
63
+ path += "?#{params.join('&')}"
64
+ end
65
+ # Send request
66
+ return $cache[path] if @enable_caching and $cache[path]
67
+ response = do_request(Net::HTTP::Get.new(path), format)
68
+ $cache[path] = response if @enable_caching
69
+ return response
70
+ end
71
+
72
+ def post(path, data = {})
73
+ # Allow format override
74
+ format = data.delete(:format) || @format
75
+ # Clear cache
76
+ clear_cache
77
+ # Create POST request
78
+ post = Net::HTTP::Post.new(path)
79
+ body = []
80
+ data.each_pair do |key, value|
81
+ body << "#{CGI::escape(key.to_s)}=#{CGI::escape(value.to_s)}"
82
+ end
83
+ post.body = body.join '&'
84
+ # Send request
85
+ do_request(post, format)
86
+ end
87
+
88
+ def raw_post(path, body, options = {})
89
+ # Allow format override
90
+ format = options.delete(:format) || @format
91
+ # Clear cache
92
+ clear_cache
93
+ # Create POST request
94
+ post = Net::HTTP::Post.new(path)
95
+ post['Content-type'] = options[:content_type] || content_type(format)
96
+ post.body = body
97
+ # Send request
98
+ do_request(post, format)
99
+ end
100
+
101
+ def put(path, data = {})
102
+ # Allow format override
103
+ format = data.delete(:format) || @format
104
+ # Clear cache
105
+ clear_cache
106
+ # Create PUT request
107
+ put = Net::HTTP::Put.new(path)
108
+ body = []
109
+ data.each_pair do |key, value|
110
+ body << "#{CGI::escape(key.to_s)}=#{CGI::escape(value.to_s)}"
111
+ end
112
+ put.body = body.join '&'
113
+ # Send request
114
+ do_request(put, format)
115
+ end
116
+
117
+ def raw_put(path, body, options = {})
118
+ # Allow format override
119
+ format = options.delete(:format) || @format
120
+ # Clear cache
121
+ clear_cache
122
+ # Create PUT request
123
+ put = Net::HTTP::Put.new(path)
124
+ put['Content-type'] = options[:content_type] || content_type(format)
125
+ put.body = body
126
+ # Send request
127
+ do_request(put, format)
128
+ end
129
+
130
+ def delete(path)
131
+ clear_cache
132
+ # Create DELETE request
133
+ delete = Net::HTTP::Delete.new(path)
134
+ # Send request
135
+ do_request(delete)
136
+ end
137
+
138
+ def authenticate
139
+ response = nil
140
+ post = Net::HTTP::Post.new("/auth/signIn")
141
+ post.body = "username=#{@username}&password=#{@password}"
142
+ post['Accept'] = content_type(:xml)
143
+ response = @http.request(post)
144
+ @auth_token = response['authToken']
145
+ unless authenticated?
146
+ raise AMEE::AuthFailed.new("Authentication failed. Please check your username and password.")
147
+ end
148
+ # Detect API version
149
+ if response.body.is_json?
150
+ @version = JSON.parse(response.body)["user"]["apiVersion"].to_f
151
+ elsif response.body.is_xml?
152
+ @version = REXML::Document.new(response.body).elements['Resources'].elements['SignInResource'].elements['User'].elements['ApiVersion'].text.to_f
153
+ else
154
+ @version = 1.0
155
+ end
156
+ end
157
+
158
+ protected
159
+
160
+ def content_type(format = @format)
161
+ case format
162
+ when :xml
163
+ return 'application/xml'
164
+ when :json
165
+ return 'application/json'
166
+ when :atom
167
+ return 'application/atom+xml'
168
+ end
169
+ end
170
+
171
+ def redirect?(response)
172
+ response.code == '301' || response.code == '302'
173
+ end
174
+
175
+ def response_ok?(response)
176
+ case response.code
177
+ when '200', '201'
178
+ return true
179
+ when '403'
180
+ raise AMEE::PermissionDenied.new("You do not have permission to perform the requested operation. AMEE Response: #{response.body}")
181
+ when '401'
182
+ authenticate
183
+ return false
184
+ when '400'
185
+ if response.body.include? "would have resulted in a duplicate resource being created"
186
+ raise AMEE::DuplicateResource.new("The specified resource already exists. This is most often caused by creating an item that overlaps another in time. AMEE Response: #{response.body}")
187
+ else
188
+ raise AMEE::UnknownError.new("An error occurred while talking to AMEE: HTTP response code #{response.code}. AMEE Response: #{response.body}")
189
+ end
190
+ else
191
+ raise AMEE::UnknownError.new("An error occurred while talking to AMEE: HTTP response code #{response.code}. AMEE Response: #{response.body}")
192
+ end
193
+ end
194
+
195
+ def do_request(request, format = @format)
196
+ # Open HTTP connection
197
+ @http.start
198
+ # Do request
199
+ begin
200
+ response = send_request(request, format)
201
+ end while !response_ok?(response)
202
+ # Return response
203
+ return response
204
+ rescue SocketError
205
+ raise AMEE::ConnectionFailed.new("Connection failed. Check server name or network connection.")
206
+ ensure
207
+ # Close HTTP connection
208
+ @http.finish if @http.started?
209
+ end
210
+
211
+ def send_request(request, format = @format)
212
+ # Set auth token in cookie (and header just in case someone's stripping cookies)
213
+ request['Cookie'] = "authToken=#{@auth_token}"
214
+ request['authToken'] = @auth_token
215
+ # Set accept header
216
+ request['Accept'] = content_type(format)
217
+ # Do the business
218
+ response = @http.request(request)
219
+ # Handle 404s
220
+ if response.code == '404'
221
+ raise AMEE::NotFound.new("URL doesn't exist on server.")
222
+ end
223
+ # Done
224
+ response
225
+ end
226
+
227
+ public
228
+
229
+ def clear_cache
230
+ if @enable_caching
231
+ $cache = {}
232
+ end
233
+ end
234
+
235
+ end
236
+ end