this repo has no description
at main 3.1 kB view raw
1#!/usr/bin/env ruby 2 3# Cloud City needs 1200 hours to be in the black on employing me. With 10 weeks 4# of PTO and 2 weeks of vacation per year, that means I need to bill an average 5# of 30 hours per week. To reach 30 billable hours, I need to average 6 hours 6# per workday. 7 8# Unfortunately, Cloud City doesn't always provide billable work. As a result, 9# the only way to track that I have figured out that makes sense is to assume 10# 30 logged hours per week, both billed and unbilled, as the ultimate goal. 11 12# 1365 hours worked/40 weeks is 34.125 hours/week or 6.825 hours/day 13YEARLY_HOURS = 1365.to_f 14EXPECTED_HOURS_PER_DAY = 6.825 15 16require "bundler/inline" 17gemfile do 18 source "https://rubygems.org" 19 gem "http" 20 gem "pry" 21end 22 23 24def harvest_req(url) 25 @acct ||= %x(security find-internet-password -s "ccd.harvestapp.com")[/"acct"<blob>="(.*)"/, 1] || "" 26 @pass ||= %x(security find-internet-password -s "ccd.harvestapp.com" -w).chomp 27 28 if @acct.empty? || @pass.empty? 29 puts "security add-internet-password -s 'ccd.harvestapp.com' -a '202855' -w" 30 abort "Add your Harvest personal access token to the keychain as an internet password!" 31 end 32 33 HTTP.auth("Bearer #{@pass}"). 34 headers("Harvest-Account-ID" => @acct). 35 headers("Content-Type" => "application/json"). 36 get(url).parse 37end 38 39def api_url(path) 40 "https://api.harvestapp.com/api/v2/#{path}" 41end 42 43def time_entries(user, year_start, year_end) 44 entries = [] 45 page = 1 46 47 while page 48 next_page = api_url("time_entries.json?user_id=#{user}&page=#{page}&per_page=100" + 49 "&from=#{year_start.iso8601}&to=#{year_end.iso8601}") 50 response = harvest_req(next_page) 51 entries.push(*response["time_entries"]) 52 page = response["next_page"] 53 end 54 55 entries 56end 57 58today = Date.today 59year_end = Date.new(today.year, 12, 31) 60remaining_weekdays = (today..year_end).reject{|d| [0,6].include?(d.wday) }.size 61year_start = Date.new(today.year, 1, 1) 62 63user = harvest_req(api_url("users/me"))["id"] 64report = time_entries(user, year_start, year_end) 65 66# We've added a Harvest "project" to track PTO. It's not getting used super 67# consistently, but there are at least a few entries in there, so let's make 68# sure any hours entered as PTO will not be counted as worked time, either 69# billed or unbilled. 70PTO_PROJECT_ID = 11169073 71report.reject!{|e| e["project"]["id"] == PTO_PROJECT_ID } 72 73hours_logged = report.map{|e| e["hours"] }.inject(0, :+) 74hours_billed = report.select{|e| e["billable"] }.map{|e| e["hours"] }.inject(0, :+) 75hours_unbilled = report.reject{|e| e["billable"] }.map{|e| e["hours"] }.inject(0, :+) 76remaining_hours = YEARLY_HOURS - hours_billed - hours_unbilled 77remaining_work_days = remaining_hours / EXPECTED_HOURS_PER_DAY 78remaining_days_off = (remaining_weekdays - remaining_work_days).round(2) 79 80puts "In calendar year #{today.year}, you have:" 81puts "#{hours_logged.round} hours logged" 82puts "#{hours_billed.round} hours of billed work" 83puts "#{hours_unbilled.round} hours of unbilled work" 84puts "#{remaining_work_days.round} expected workdays left" 85puts "#{remaining_days_off.round} days of PTO left"