costagent 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +77 -0
- data/lib/costagent.rb +74 -0
- 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
|
+
|