amee 2.0.25

Sign up to get free protection for your applications and to get access to all the features.
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