Connecting the Harvest API to Beeminder
This is a little script I wrote to send my logged Harvest hours to a Beeminder goal. It’s possible to do this via IFTTT or Zapier, but I had a couple of additional requirements:
- Should only submit data at the end of the day
- Only wanted a single data-point in my Beeminder graph
- Wanted each entry to be rounded up to the nearest 15 minutes
Let’s take them in order.
1 - Only submit data at the end of the day
I use cron to run the script every night at 10:55pm. I thought about running it at midnight, but the deadline stops me from working too late.
55 22 * * * cd <script directory> && ./log_time > /<script directory>/bee.log
2 - Only want a single data point in my graph
Nothing too complex here, the script fetches all individual entries for the day and adds them together.
client = Harvest.client(
subdomain: ENV['HARVEST_SUBDOMAIN'],
username: ENV['HARVEST_USERNAME'],
password: ENV['HARVEST_PASSWORD']
)
todays_entries = client.time.all
total_time = 0
todays_entries.each do |entry|
total_time += entry.hours
end
I’d like to replace the each
loop with something more Ruby-ish, but for now it works.
3 - Round all entries up to the nearest 15 minutes
# Round up a floating point time value to the nearest quarter hour.
def round_to_quarter_hour(time)
(time * 4).ceil / 4.0
end
# Update the sum loop
todays_entries.each do |entry|
total_time += round_to_quarter_hour(entry.hours)
end
The finished script
All configuration details are stored in a .env
file in the script directory.
# frozen_string_literal: true
require 'beeminder'
require 'dotenv/load'
require 'harvested'
# Round up a floating point time value to the nearest quarter hour.
def round_to_quarter_hour(time)
(time * 4).ceil / 4.0
end
puts 'Fetching data...'
client = Harvest.client(
subdomain: ENV['HARVEST_SUBDOMAIN'],
username: ENV['HARVEST_USERNAME'],
password: ENV['HARVEST_PASSWORD']
)
# Get all entries for today.
todays_entries = client.time.all
total_time = 0
# Round up each entry to nearest 0.25.
todays_entries.each do |entry|
total_time += round_to_quarter_hour(entry.hours)
end
# Submit the value to beeminder.
beeminder = Beeminder::User.new(
ENV['BEEMINDER_TOKEN'],
auth_type: :personal
)
puts 'Sending data...'
beeminder.send(
ENV['BEEMINDER_GOAL'],
total_time,
"AUTO: #{total_time} hours billed for day"
)
puts 'Done'