costagent 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/README.rdoc +77 -0
  2. data/lib/costagent.rb +74 -0
  3. metadata +115 -0
data/README.rdoc ADDED
@@ -0,0 +1,77 @@
1
+ = costagent
2
+
3
+ * http://github.com/ejdraper/costagent
4
+
5
+ == DESCRIPTION:
6
+
7
+ costagent is a Ruby gem that provides lightweight access to the projects/timeslips part of the FreeAgent API, with a view to tracking billable hours and figures.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ This isn't meant to be an entire Ruby library for the FA API - this is simply supposed to be a simple to use read-only view of timeslip data for keeping track of billable hours and calculating money earnt.
12
+
13
+ This assumes that when calculating money earnt, you want it in GBP, and it only copes with calculating timeslips either in GBP, or in USD (which is converted using xe.com where possible).
14
+
15
+ == SYNOPSIS:
16
+
17
+ To initialize:
18
+
19
+ costagent = CostAgent.new subdomain, username, password
20
+
21
+ (remember you need to enable API access from within your FreeAgent account)
22
+
23
+ To see all projects:
24
+
25
+ projects = costagent.projects
26
+
27
+ To find a specific project:
28
+
29
+ project = costagent.project(id)
30
+
31
+ To find timeslips:
32
+
33
+ timeslips = costagent.timeslips(start_date, end_date)
34
+
35
+ And to return a GBP figure of the money earnt during a specific timeframe based on billable hours:
36
+
37
+ total = costagent.earnt(start_date, end_date)
38
+
39
+ You can see the USD rate used for conversion:
40
+
41
+ CostAgent.usd_rate
42
+
43
+ This returns 1.6 if there is any error reaching or parsing xe.com. It's also stored once retrieved in a class variable so it doesn't keep querying everytime it's needed.
44
+
45
+ == REQUIREMENTS:
46
+
47
+ rest-client (1.5.0)
48
+ hpricot (0.8.2)
49
+
50
+ == INSTALL:
51
+
52
+ gem install costagent
53
+
54
+ == LICENSE:
55
+
56
+ (The MIT License)
57
+
58
+ Copyright (c) 2010 Elliott Draper <el@ejdraper.com>
59
+
60
+ Permission is hereby granted, free of charge, to any person obtaining
61
+ a copy of this software and associated documentation files (the
62
+ 'Software'), to deal in the Software without restriction, including
63
+ without limitation the rights to use, copy, modify, merge, publish,
64
+ distribute, sublicense, and/or sell copies of the Software, and to
65
+ permit persons to whom the Software is furnished to do so, subject to
66
+ the following conditions:
67
+
68
+ The above copyright notice and this permission notice shall be
69
+ included in all copies or substantial portions of the Software.
70
+
71
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
72
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
73
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
74
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
75
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
76
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
77
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/lib/costagent.rb ADDED
@@ -0,0 +1,74 @@
1
+ require "rubygems"
2
+ require "date"
3
+ gem "rest-client"; require "rest_client"
4
+ gem "hpricot"; require "hpricot"
5
+ require "open-uri"
6
+
7
+ # This exposes additional billable tracking functionality around the Freeagent API
8
+ class CostAgent
9
+ Project = Struct.new(:id, :name, :currency, :hourly_billing_rate)
10
+ Timeslip = Struct.new(:id, :project, :hours, :date, :cost)
11
+
12
+ attr_accessor :subdomain, :username, :password
13
+
14
+ def initialize(subdomain, username, password)
15
+ self.subdomain = subdomain
16
+ self.username = username
17
+ self.password = password
18
+
19
+ [:subdomain, :username, :password].each do |f|
20
+ raise "No #{f} configured!" if self.send(f).nil? || self.send(f).empty?
21
+ end
22
+ end
23
+
24
+ # Returns all projects
25
+ def projects
26
+ @projects ||= (self.api("projects")/"project").collect { |project| Project.new((project/"id").text.to_i, (project/"name").text, (project/"currency").text, (project/"normal-billing-rate").text.to_f) }
27
+ end
28
+
29
+ # This returns the specified project
30
+ def project(id)
31
+ self.projects.detect { |p| p.id == id }
32
+ end
33
+
34
+ # This returns all timeslips for the specified date range, with additional cost information
35
+ def timeslips(start_date = DateTime.now, end_date = start_date)
36
+ (self.api("timeslips", :view => "#{start_date.strftime("%Y-%m-%d")}_#{end_date.strftime("%Y-%m-%d")}")/"timeslip").collect do |timeslip|
37
+ # Find the project and hours for this timeslip
38
+ project = self.project((timeslip/"project-id").text.to_i)
39
+ hours = (timeslip/"hours").text.to_f
40
+ # Build the timeslip out using the timeslip data and the project it's tied to
41
+ Timeslip.new((timeslip/"id").text.to_i,
42
+ project,
43
+ hours,
44
+ DateTime.parse((timeslip/"updated-at").text),
45
+ project.hourly_billing_rate * hours)
46
+ end
47
+ end
48
+
49
+ # This returns the amount of GBP earnt in the specified timeframe
50
+ def earnt(start_date = DateTime.now, end_date = start_date)
51
+ self.timeslips(start_date, end_date).collect do |timeslip|
52
+ if timeslip.project.currency == "GBP"
53
+ timeslip.cost
54
+ else
55
+ timeslip.cost / CostAgent.usd_rate
56
+ end
57
+ end.inject(0) { |sum, i| sum += i }
58
+ end
59
+
60
+ # This calls the FA API for the specified resource
61
+ def api(resource, parameters = {})
62
+ url = "https://#{self.subdomain}.freeagentcentral.com/#{resource}#{parameters.empty? ? "" : "?" + parameters.collect { |p| p.join("=") }.join("&")}"
63
+ res = RestClient::Resource.new(url, self.username, self.password).get
64
+ raise "No response from #{url}!" if res.body.nil? && res.body.empty?
65
+ Hpricot(res.body)
66
+ end
67
+
68
+ class << self
69
+ # This returns the current USD rate from xe.com (or falls back on 1.6 if there is an error)
70
+ def usd_rate
71
+ @@rate ||= ((Hpricot(Kernel.open("http://www.xe.com"))/"a").detect { |a| a.attributes["id"] == "USDGBP31" }.children.first.to_s.to_f rescue 1.6)
72
+ end
73
+ end
74
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: costagent
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Elliott Draper
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-05-11 00:00:00 +01:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rest-client
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 5
30
+ - 0
31
+ version: 1.5.0
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: hpricot
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 0
43
+ - 8
44
+ - 2
45
+ version: 0.8.2
46
+ type: :runtime
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: shoulda
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ segments:
56
+ - 0
57
+ version: "0"
58
+ type: :development
59
+ version_requirements: *id003
60
+ - !ruby/object:Gem::Dependency
61
+ name: mocha
62
+ prerelease: false
63
+ requirement: &id004 !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ type: :development
71
+ version_requirements: *id004
72
+ description:
73
+ email: el@ejdraper.com
74
+ executables: []
75
+
76
+ extensions: []
77
+
78
+ extra_rdoc_files:
79
+ - README.rdoc
80
+ files:
81
+ - README.rdoc
82
+ - lib/costagent.rb
83
+ has_rdoc: true
84
+ homepage: http://github.com/ejdraper/costagent
85
+ licenses: []
86
+
87
+ post_install_message:
88
+ rdoc_options:
89
+ - --main
90
+ - README.rdoc
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ segments:
98
+ - 0
99
+ version: "0"
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ segments:
105
+ - 0
106
+ version: "0"
107
+ requirements: []
108
+
109
+ rubyforge_project:
110
+ rubygems_version: 1.3.6
111
+ signing_key:
112
+ specification_version: 3
113
+ summary: costagent is a Ruby gem that provides lightweight access to the projects/timeslips part of the FreeAgent API, with a view to tracking billable hours and figures.
114
+ test_files: []
115
+