Class: CostAgent
- Inherits:
-
Object
- Object
- CostAgent
- Defined in:
- lib/costagent.rb
Overview
This exposes additional billable tracking functionality around the Freeagent API
Defined Under Namespace
Classes: Base, Invoice, InvoiceItem, Project, Task, Timeslip, User
Class Attribute Summary collapse
-
.cache_provider ⇒ Object
Returns the value of attribute cache_provider.
Instance Attribute Summary collapse
-
#password ⇒ Object
Our configuration for FA access.
-
#subdomain ⇒ Object
Our configuration for FA access.
-
#username ⇒ Object
Our configuration for FA access.
Class Method Summary collapse
-
.usd_rate ⇒ Object
This returns the current USD rate from xe.com (or falls back on 1.6 if there is an error).
Instance Method Summary collapse
-
#api(resource, parameters = {}) ⇒ Object
This calls the FA API for the specified resource.
-
#cache(resource, identifier, reload = false, &block) ⇒ Object
This calls out to the external third party provider for caching.
-
#client(resource, parameters = {}) ⇒ Object
This returns a client ready to query the FA API.
-
#earnt(start_date = DateTime.now, end_date = start_date) ⇒ Object
This returns the amount of GBP earnt in the specified timeframe.
-
#initialize(subdomain, username, password) ⇒ CostAgent
constructor
Initialize and validate input data.
-
#invoice(id) ⇒ Object
This returns the specific invoice by ID.
-
#invoices(reload = false) ⇒ Object
This returns all invoices.
-
#project(id) ⇒ Object
This returns the specified project.
-
#projects(filter = "active", reload = false) ⇒ Object
Returns all projects.
-
#tasks(project_id, reload = false) ⇒ Object
This returns all tasks for the specified project_id.
-
#timeslips(start_date = DateTime.now, end_date = start_date, reload = false) ⇒ Object
This returns all timeslips for the specified date range, with additional cost information.
-
#user(reload = false) ⇒ Object
This contains the logged in user information for the configured credentials.
-
#user_id ⇒ Object
This looks up the user ID using the CostAgent credentials.
-
#worked(start_date = DateTime.now, end_date = start_date) ⇒ Object
This returns the amount of hours worked.
Constructor Details
#initialize(subdomain, username, password) ⇒ CostAgent
Initialize and validate input data
61 62 63 64 65 66 67 68 69 |
# File 'lib/costagent.rb', line 61 def initialize(subdomain, username, password) self.subdomain = subdomain self.username = username self.password = password [:subdomain, :username, :password].each do |f| raise "No #{f} configured!" if self.send(f).nil? || self.send(f).empty? end end |
Class Attribute Details
.cache_provider ⇒ Object
Returns the value of attribute cache_provider.
44 45 46 |
# File 'lib/costagent.rb', line 44 def cache_provider @cache_provider end |
Instance Attribute Details
#password ⇒ Object
Our configuration for FA access
40 41 42 |
# File 'lib/costagent.rb', line 40 def password @password end |
#subdomain ⇒ Object
Our configuration for FA access
40 41 42 |
# File 'lib/costagent.rb', line 40 def subdomain @subdomain end |
#username ⇒ Object
Our configuration for FA access
40 41 42 |
# File 'lib/costagent.rb', line 40 def username @username end |
Class Method Details
.usd_rate ⇒ Object
This returns the current USD rate from xe.com (or falls back on 1.6 if there is an error)
239 240 241 |
# File 'lib/costagent.rb', line 239 def usd_rate @@rate ||= ((Hpricot(Kernel.open("http://www.xe.com"))/"a").detect { |a| a.attributes["id"] == "USDGBP31" }.children.first.to_s.to_f rescue 1.6) end |
Instance Method Details
#api(resource, parameters = {}) ⇒ Object
This calls the FA API for the specified resource
225 226 227 228 229 |
# File 'lib/costagent.rb', line 225 def api(resource, parameters = {}) res = self.client(resource, parameters).get raise "No response from #{url}!" if res.body.nil? && res.body.empty? Hpricot(res.body) end |
#cache(resource, identifier, reload = false, &block) ⇒ Object
This calls out to the external third party provider for caching
48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/costagent.rb', line 48 def cache(resource, identifier, reload = false, &block) if CostAgent.cache_provider.nil? block.call else if (!reload && CostAgent.cache_provider.exists?(self.subdomain, resource, identifier)) CostAgent.cache_provider.get(self.subdomain, resource, identifier) else CostAgent.cache_provider.set(self.subdomain, resource, identifier, block.call) end end end |
#client(resource, parameters = {}) ⇒ Object
This returns a client ready to query the FA API
232 233 234 235 |
# File 'lib/costagent.rb', line 232 def client(resource, parameters = {}) url = "https://#{self.subdomain}.freeagentcentral.com/#{resource}#{parameters.empty? ? "" : "?" + parameters.collect { |p| p.join("=") }.join("&")}" RestClient::Resource.new(url, self.username, self.password) end |
#earnt(start_date = DateTime.now, end_date = start_date) ⇒ Object
This returns the amount of GBP earnt in the specified timeframe
214 215 216 217 218 219 220 221 222 |
# File 'lib/costagent.rb', line 214 def earnt(start_date = DateTime.now, end_date = start_date) self.timeslips(start_date, end_date).collect do |timeslip| if timeslip.project.currency == "GBP" timeslip.cost else timeslip.cost / CostAgent.usd_rate end end.inject(0) { |sum, i| sum += i } end |
#invoice(id) ⇒ Object
This returns the specific invoice by ID
186 187 188 |
# File 'lib/costagent.rb', line 186 def invoice(id) self.invoices.detect { |i| i.id == id } end |
#invoices(reload = false) ⇒ Object
This returns all invoices
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
# File 'lib/costagent.rb', line 150 def invoices(reload = false) self.cache(CostAgent::Invoice, :all, reload) do (self.api("invoices")/"invoice").collect do |invoice| items = (invoice/"invoice-item").collect do |item| price = (item/"price").first.inner_text.to_f quantity = (item/"quantity").first.inner_text.to_f cost = price * quantity project = self.project((item/"project-id").first.inner_text.to_i) InvoiceItem.new( :id => (item/"id").first.inner_text.to_i, :invoice_id => (item/"invoice-id").first.inner_text.to_i, :project_id => project.nil? ? nil : project.id, :project => project, :item_type => (item/"item-type").first.inner_text, :description => (item/"description").first.inner_text, :price => price, :quantity => quantity, :cost => cost) end project = self.project((invoice/"project-id").first.inner_text.to_i) Invoice.new( :id => (invoice/"id").first.inner_text.to_i, :project_id => project.nil? ? nil : project.id, :project => project, :description => (invoice/"description").first.inner_text, :reference => (invoice/"reference").text, :amount => (invoice/"net-value").text.to_f, :status => (invoice/"status").text, :date => DateTime.parse((invoice/"dated-on").text), :due => DateTime.parse((invoice/"due-on").text), :items => items) end end end |
#project(id) ⇒ Object
This returns the specified project
92 93 94 |
# File 'lib/costagent.rb', line 92 def project(id) self.projects("all").detect { |p| p.id == id } end |
#projects(filter = "active", reload = false) ⇒ Object
Returns all projects
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/costagent.rb', line 72 def projects(filter = "active", reload = false) self.cache(CostAgent::Project, filter, reload) do (self.api("projects", {:view => filter})/"project").collect do |project| billing_rate = (project/"normal-billing-rate").text.to_f hours_per_day = (project/"hours-per-day").text.to_f billing_period = (project/"billing-period").text hourly_rate = (billing_period == "hour" ? billing_rate : billing_rate / hours_per_day) daily_rate = (billing_period == "hour" ? billing_rate * hours_per_day : billing_rate) Project.new( :id => (project/"id").text.to_i, :name => (project/"name").text, :currency => (project/"currency").text, :hourly_billing_rate => hourly_rate, :daily_billing_rate => daily_rate, :hours_per_day => hours_per_day) end end end |
#tasks(project_id, reload = false) ⇒ Object
This returns all tasks for the specified project_id
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/costagent.rb', line 126 def tasks(project_id, reload = false) self.cache(CostAgent::Task, project_id, reload) do (self.api("projects/#{project_id}/tasks")/"task").collect do |task| # Find the project for this task project = self.project((task/"project-id").text.to_i) # Calculate rates billing_rate = (task/"billing-rate").text.to_f billing_period = (task/"billing-period").text hourly_rate = (billing_period == "hour" ? billing_rate : billing_rate / project.hours_per_day) daily_rate = (billing_period == "hour" ? billing_rate * project.hours_per_day : billing_rate) # Build the task out using the task data and the project it's tied to Task.new( :id => (task/"id").text.to_i, :name => (task/"name").text, :project_id => project.id, :project => project, :hourly_billing_rate => hourly_rate, :daily_billing_rate => daily_rate, :billable => (task/"is-billable").text == "true") end end end |
#timeslips(start_date = DateTime.now, end_date = start_date, reload = false) ⇒ Object
This returns all timeslips for the specified date range, with additional cost information
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/costagent.rb', line 97 def timeslips(start_date = DateTime.now, end_date = start_date, reload = false) self.cache(CostAgent::Timeslip, "#{start_date.strftime("%Y-%m-%d")}_#{end_date.strftime("%Y-%m-%d")}", reload) do timeslips = (self.api("timeslips", :view => "#{start_date.strftime("%Y-%m-%d")}_#{end_date.strftime("%Y-%m-%d")}")/"timeslip").collect do |timeslip| # Find the project and hours for this timeslip project = self.project((timeslip/"project-id").text.to_i) if project task = self.tasks(project.id).detect { |t| t.id == (timeslip/"task-id").text.to_i } hours = (timeslip/"hours").text.to_f cost = (task.nil? ? project : task).hourly_billing_rate * hours # Build the timeslip out using the timeslip data and the project it's tied to Timeslip.new( :id => (timeslip/"id").text.to_i, :project_id => project.id, :project => project, :task_id => task.nil? ? nil : task.id, :task => task, :hours => hours, :date => DateTime.parse((timeslip/"dated-on").text), :cost => cost, :comment => (timeslip/"comment").text, :status => (timeslip/"status").text) else nil end end - [nil] end end |
#user(reload = false) ⇒ Object
This contains the logged in user information for the configured credentials
191 192 193 194 195 196 197 198 199 |
# File 'lib/costagent.rb', line 191 def user(reload = false) self.cache(CostAgent::User, self.username, reload) do data = self.client("verify").get.headers [User.new( :id => data[:user_id], :permissions => data[:user_permission_level], :company_type => data[:company_type])] end.first end |
#user_id ⇒ Object
This looks up the user ID using the CostAgent credentials
202 203 204 |
# File 'lib/costagent.rb', line 202 def user_id self.user.id end |
#worked(start_date = DateTime.now, end_date = start_date) ⇒ Object
This returns the amount of hours worked
207 208 209 210 211 |
# File 'lib/costagent.rb', line 207 def worked(start_date = DateTime.now, end_date = start_date) self.timeslips(start_date, end_date).collect do |timeslip| timeslip.hours end.inject(0) { |sum, i| sum += i } end |