-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfetch.rb
More file actions
112 lines (100 loc) · 3.98 KB
/
fetch.rb
File metadata and controls
112 lines (100 loc) · 3.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#!/usr/bin/env ruby
# fetch.rb - Fetches all time data from Basecamp (that you have access to) for a
# given time period and gives you a summarized report in a number of categories.
#
# In order to use this, you must set the following ENV variables:
# BASECAMP_URL BASECAMP_USERNAME BASECAMP_PASSWORD
#
# Instead of launching this directly, put your setting in the 'fetch' shell script
# which is a wrapper to this program. This way, your info is not permanently in
# your shell environment.
#
# Example usage:
# ./fetch.rb 2008-11-01 2008-11-15
#
# You must provide the start date. The end date defaults to today if not provided.
# Yes, this code is not all that pretty - this was primarily a mental exercise.
# Primarily, this refers to outputting all the output in a single expression at the end.
require 'basecamp'
require 'date'
class Object; def with(obj=self) yield(obj); obj; end; end
class Object; def chain(obj=self) yield(obj); end; end
def header(txt = nil, chr = '=')
return chr * 60 if txt.nil?
extra = 60 - (txt.length + 2)
chr * (extra / 2 + (extra.odd? ? 1 : 0)) << " " << txt << " " << chr * (extra / 2)
end
%w[BASECAMP_URL BASECAMP_USERNAME BASECAMP_PASSWORD].each do |setting|
unless ENV.has_key?(setting)
puts "Environment variable #{setting} not set! Aborting..."
exit
end
end
unless ARGV.size >= 1
puts "You must specify the start date for the report as an argument. (End date is optional, defaults to today)"
exit
end
Basecamp.establish_connection!(ENV['BASECAMP_URL'],
ENV['BASECAMP_USERNAME'],
ENV['BASECAMP_PASSWORD'],
true)
start_date, end_date = ARGV.collect { |arg| Date.parse(arg) } << Date.today
basecamp = Basecamp.new
if end_date < start_date
start_date, end_date = [end_date, start_date]
end
totals = { :people => Hash.new(0.0),
:projects => Hash.new(0.0),
:pp => Hash.new { |h, k| h[k] = Hash.new(0.0) } }
cache = { :people => { }, :projects => { } }
cur_start_date = start_date
until cur_start_date > end_date
cur_end_date = [cur_start_date + 180, end_date].min
Basecamp::TimeEntry.report(:from => cur_start_date.strftime('%Y%m%d'), :to => cur_end_date.strftime('%Y%m%d')).each do |entry|
person = cache[:people][entry.person_id] ||= basecamp.person(entry.person_id)
project = cache[:projects][entry.project_id] ||= Basecamp::Project.find(entry.project_id)
totals[:people][person] += entry.hours
totals[:projects][project] += entry.hours
totals[:pp][project][person] += entry.hours
end
# basecamp is inclusive on both ends, so you have to add an additional day here
cur_start_date += 181
end
# Yes, this is one expression - its ugly, but its pretty cool, too.
# No, I don't normally do things like this. :)
puts header("TIME REPORT FOR #{start_date.strftime('%Y-%m-%d')} TO #{end_date.strftime('%Y-%m-%d')}", ' '),
header('By Person'),
totals[:people].
collect { |(p, h)| [ [p['last-name'], p['first-name']], h ] }.
sort.
collect { |(p, h)| sprintf "%-53s%7.02f" % [p.reverse.join(' '), h] }.
join("\n"),
header('By Project'),
totals[:projects].
collect { |(p, h)| [ [p.company.name, p.name].join(' - '), h ] }.
sort.
collect { |(p, h)| sprintf "%-53s%7.02f" % [p, h] }.
join("\n"),
header('By Company'),
totals[:projects].
inject(Hash.new(0.0)) { |hsh, (p, h)|
hsh.with { |hh| hh[p.company.name] += h }
}.
sort.
collect { |(p, h)| sprintf "%-53s%7.02f" % [p, h] }.
join("\n"),
header('By Person per Project'),
totals[:pp].
collect { |(proj, people)|
[
[proj.company.name, proj.name].join(' - '),
people.collect { |(p, h)| [ [p['last-name'], p['first-name'] ], h ] }.
sort.
collect { |(p, h)| sprintf " %-51s%7.02f" % [p.reverse.join(' '), h] }.
join("\n")
]
}.
sort.
flatten.
join("\n"),
header