clockout 0.1 → 0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/clock +73 -51
- data/lib/clockout.rb +80 -32
- metadata +2 -2
data/bin/clock
CHANGED
@@ -3,16 +3,16 @@
|
|
3
3
|
require 'clockout'
|
4
4
|
|
5
5
|
HELP_BANNER = <<-EOS
|
6
|
-
Clockout v0.
|
6
|
+
Clockout v0.2
|
7
7
|
Usage:
|
8
|
-
|
8
|
+
$ clock [options]
|
9
9
|
|
10
10
|
Options:
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
--estimations, -e: Show estimations made for first commit of each block
|
12
|
+
--condensed, -c: Condense output (don't show the timeline for each day)
|
13
|
+
--generate-clock, -g: Generate .clock file
|
14
|
+
--see-clock, -s: See options specified in .clock file
|
15
|
+
--help, -h: Show this message
|
16
16
|
EOS
|
17
17
|
|
18
18
|
TEMPLATE_CLOCKFILE = <<-EOF
|
@@ -41,7 +41,7 @@ TEMPLATE_CLOCKFILE = <<-EOF
|
|
41
41
|
; that you don't want to report as your own work.
|
42
42
|
; Below is an example regex that will only calculate changes made to files with those extensions
|
43
43
|
|
44
|
-
;my_files =
|
44
|
+
;my_files = /\\.(m|h|rb|txt)$/
|
45
45
|
|
46
46
|
; Diffs of files matched by this regex will NOT be included in commit time estimation
|
47
47
|
; Type: Regex (Ruby)
|
@@ -50,7 +50,7 @@ TEMPLATE_CLOCKFILE = <<-EOF
|
|
50
50
|
; You also have the option of defining a negative regex match, to ignore certain files.
|
51
51
|
; For example, if you added an external library or something, you should ignore those additions
|
52
52
|
|
53
|
-
;not_my_files = /(ThisFile
|
53
|
+
;not_my_files = /(ThisFile\\.cpp | SomeOtherClass\\.*)/
|
54
54
|
|
55
55
|
; Completion time overrides for commit estimations
|
56
56
|
; Type: Int (in minutes)
|
@@ -76,8 +76,8 @@ def parse_options(args)
|
|
76
76
|
opts[:condensed] = true
|
77
77
|
elsif (arg == "-g" || arg == "--generate-clock")
|
78
78
|
opts[:generate_clock] = true
|
79
|
-
|
80
|
-
puts "Error: invalid option '#{arg}'."
|
79
|
+
else
|
80
|
+
puts "#{colorize("Error:", RED)} invalid option '#{arg}'."
|
81
81
|
puts "Try --help for help."
|
82
82
|
exit
|
83
83
|
end
|
@@ -86,51 +86,73 @@ def parse_options(args)
|
|
86
86
|
opts
|
87
87
|
end
|
88
88
|
|
89
|
+
def generate_clock_file(path)
|
90
|
+
clock_path = Clockout.clock_path(path)
|
91
|
+
if (File.exists?(clock_path))
|
92
|
+
false
|
93
|
+
else
|
94
|
+
File.open(clock_path, "w") do |file|
|
95
|
+
file.write(TEMPLATE_CLOCKFILE)
|
96
|
+
end
|
97
|
+
true
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
89
101
|
path = Dir.pwd
|
90
102
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
if opts[:generate_clock]
|
96
|
-
clock_path = path+"/.clock"
|
97
|
-
if (File.exists?(clock_path))
|
98
|
-
puts "#{colorize("Error:", RED)} .clock file already exists, ignoring --generate-clock option.\n"
|
99
|
-
else
|
100
|
-
File.open(clock_path, "w") do |file|
|
101
|
-
file.write(TEMPLATE_CLOCKFILE)
|
102
|
-
end
|
103
|
-
puts "Generated .clock file at #{clock_path}.\n"
|
104
|
-
end
|
105
|
-
end
|
103
|
+
if (ARGV[0] == "in" || ARGV[0] == "out")
|
104
|
+
# Generate a clock file if one doesn't already exist
|
105
|
+
generate_clock_file(path)
|
106
106
|
|
107
|
-
|
107
|
+
# Append "in <current date>" or "out <current date>" to the clockfile
|
108
|
+
File.open(Clockout.clock_path(path), "a") do |file|
|
109
|
+
file.puts ARGV[0] + " " + Time.new.to_s
|
110
|
+
end
|
111
|
+
else
|
112
|
+
opts = parse_options(ARGV)
|
113
|
+
|
114
|
+
if opts[:help]
|
115
|
+
puts HELP_BANNER
|
116
|
+
exit
|
117
|
+
end
|
108
118
|
|
109
|
-
if opts[:
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
puts "Clock options:"
|
117
|
-
clock.clock_opts.each do |k, v|
|
118
|
-
key = k[0..19]
|
119
|
-
puts " #{key}:#{' '*(20-key.length)}#{v}"
|
120
|
-
end
|
121
|
-
end
|
119
|
+
if opts[:generate_clock]
|
120
|
+
if generate_clock_file(path)
|
121
|
+
puts "Generated .clock file at #{Clockout.clock_path(path)}.\n"
|
122
|
+
else
|
123
|
+
puts "#{colorize("Error:", RED)} .clock file already exists for this repo.\n"
|
124
|
+
end
|
125
|
+
exit
|
122
126
|
end
|
123
|
-
exit
|
124
|
-
end
|
125
127
|
|
126
|
-
if opts[:
|
127
|
-
|
128
|
-
|
129
|
-
|
128
|
+
if opts[:see_clock]
|
129
|
+
clock_opts = Clockout.parse_clockfile(Clockout.clock_path(path))
|
130
|
+
if !clock_opts
|
131
|
+
puts "No .clock file found. Run `clock -g` to generate one."
|
132
|
+
else
|
133
|
+
if clock_opts.size == 0
|
134
|
+
puts "No clock options."
|
135
|
+
else
|
136
|
+
puts "Clock options:"
|
137
|
+
clock_opts.each do |k, v|
|
138
|
+
space = 23
|
139
|
+
width = space-6
|
140
|
+
key = k[0..width]
|
141
|
+
key += "..." if k.length > width
|
142
|
+
# For clockins/outs, display the number of them instead of all the dates
|
143
|
+
v = v.length if k == :clockins || k == :clockouts
|
144
|
+
puts " #{key}:#{' '*(space-key.length)}#{v}"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
exit
|
149
|
+
end
|
130
150
|
|
131
|
-
|
132
|
-
clock.print_estimations
|
133
|
-
else
|
134
|
-
clock.print_chart
|
135
|
-
end
|
151
|
+
clock = Clockout.new(path)
|
136
152
|
|
153
|
+
if (opts[:estimations])
|
154
|
+
clock.print_estimations
|
155
|
+
else
|
156
|
+
clock.print_chart(opts[:condensed])
|
157
|
+
end
|
158
|
+
end
|
data/lib/clockout.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'grit'
|
2
|
+
require 'time'
|
2
3
|
|
3
4
|
class Commit
|
4
5
|
attr_accessor :message, :minutes, :date, :diffs, :sha
|
@@ -16,7 +17,7 @@ class Clockout
|
|
16
17
|
COLS = 80
|
17
18
|
DAY_FORMAT = '%B %e, %Y'
|
18
19
|
|
19
|
-
attr_accessor :blocks
|
20
|
+
attr_accessor :blocks
|
20
21
|
|
21
22
|
def diffs(commit)
|
22
23
|
plus, minus = 0, 0
|
@@ -43,6 +44,7 @@ class Clockout
|
|
43
44
|
|
44
45
|
total_diffs, total_mins = 0, 0
|
45
46
|
|
47
|
+
prev = nil
|
46
48
|
commits.each do |commit|
|
47
49
|
|
48
50
|
c = Commit.new
|
@@ -52,7 +54,17 @@ class Clockout
|
|
52
54
|
c.sha = commit.id[0..7]
|
53
55
|
|
54
56
|
if block.size > 0
|
55
|
-
|
57
|
+
last_date = block.last.date
|
58
|
+
|
59
|
+
@clockins.each do |clockin|
|
60
|
+
if clockin > last_date && clockin < c.date
|
61
|
+
last_date = clockin
|
62
|
+
@clockins.delete(clockin)
|
63
|
+
break
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
time_since_last = (last_date - commit.committed_date).abs/60
|
56
68
|
|
57
69
|
if (time_since_last > $opts[:time_cutoff])
|
58
70
|
blocks << block
|
@@ -82,22 +94,21 @@ class Clockout
|
|
82
94
|
elsif ($opts[:ignore_initial] && block == blocks.first) || total_diffs == 0
|
83
95
|
first.minutes = 0
|
84
96
|
else
|
85
|
-
|
86
|
-
first.minutes = 0.9*first.diffs*(1.0*total_mins/total_diffs)
|
97
|
+
first.minutes = $opts[:estimation_factor]*first.diffs*(1.0*total_mins/total_diffs)
|
87
98
|
end
|
88
99
|
|
89
100
|
@time_per_day[first.date.strftime(DAY_FORMAT)] += first.minutes
|
90
101
|
end
|
91
102
|
end
|
92
103
|
|
93
|
-
def print_chart
|
94
|
-
cols = (
|
104
|
+
def print_chart(condensed)
|
105
|
+
cols = (condensed ? 30 : COLS)
|
95
106
|
total_sum = 0
|
96
107
|
current_day = nil
|
97
108
|
@blocks.each do |block|
|
98
109
|
date = block.first.date.strftime(DAY_FORMAT)
|
99
110
|
if date != current_day
|
100
|
-
puts if (
|
111
|
+
puts if (!condensed)
|
101
112
|
|
102
113
|
current_day = date
|
103
114
|
|
@@ -111,7 +122,7 @@ class Clockout
|
|
111
122
|
puts
|
112
123
|
end
|
113
124
|
|
114
|
-
print_timeline(block) if (
|
125
|
+
print_timeline(block) if (!condensed)
|
115
126
|
end
|
116
127
|
|
117
128
|
puts " "*(cols-10) + colorize("-"*10,MAGENTA)
|
@@ -184,15 +195,16 @@ class Clockout
|
|
184
195
|
begin
|
185
196
|
return Grit::Repo.new(path)
|
186
197
|
rescue Exception => e
|
198
|
+
print colorize("Error: ", RED)
|
187
199
|
if e.class == Grit::NoSuchPathError
|
188
|
-
puts "
|
200
|
+
puts "Path '#{path}' could not be found."
|
189
201
|
else
|
190
|
-
puts "
|
202
|
+
puts "'#{path}' is not a Git repository."
|
191
203
|
end
|
192
204
|
end
|
193
205
|
end
|
194
206
|
|
195
|
-
def parse_clockfile(file)
|
207
|
+
def self.parse_clockfile(file)
|
196
208
|
return nil if !File.exists?(file)
|
197
209
|
|
198
210
|
opts = {}
|
@@ -200,42 +212,78 @@ class Clockout
|
|
200
212
|
line_num = 0
|
201
213
|
File.foreach(file) do |line|
|
202
214
|
line_num += 1
|
215
|
+
#Strip whitespace
|
203
216
|
line.strip!
|
217
|
+
#Strip comments
|
218
|
+
line = line.split(";",2)[0]
|
204
219
|
|
205
|
-
next if line
|
220
|
+
next if !line || line.length == 0
|
206
221
|
|
207
222
|
sides = line.split("=",2)
|
208
223
|
|
209
|
-
|
210
|
-
|
211
|
-
puts " #{line}"
|
212
|
-
puts ""
|
213
|
-
puts "Line must be of form:"
|
214
|
-
puts " KEY = VALUE"
|
224
|
+
clock_split = sides[0].split(" ",2)
|
225
|
+
if (clock_split[0] == "in" || clock_split[0] == "out")
|
215
226
|
|
216
|
-
|
217
|
-
|
227
|
+
begin
|
228
|
+
date = Time.parse(clock_split[1])
|
229
|
+
rescue Exception => e
|
230
|
+
puts "#{colorize("Error:", RED)} invalid date for '#{clock_split[0]}' on line #{line_num} of .clock file:"
|
231
|
+
puts " #{line}"
|
218
232
|
|
219
|
-
|
220
|
-
|
233
|
+
exit
|
234
|
+
end
|
221
235
|
|
222
|
-
|
223
|
-
right = (right != "0")
|
224
|
-
elsif left == "time_cutoff"
|
225
|
-
right = right.to_i
|
226
|
-
end
|
236
|
+
key = (clock_split[0] == "out") ? :clockouts : :clockins
|
227
237
|
|
228
|
-
|
238
|
+
opts[key] ||= []
|
239
|
+
opts[key] << date
|
240
|
+
else
|
241
|
+
if sides.length != 2
|
242
|
+
puts "#{colorize("Error:", RED)} bad syntax on line #{line_num} of .clock file:"
|
243
|
+
puts " #{line}"
|
244
|
+
puts ""
|
245
|
+
puts "Line must be of form:"
|
246
|
+
puts " KEY = VALUE"
|
247
|
+
|
248
|
+
exit
|
249
|
+
end
|
250
|
+
|
251
|
+
left = sides[0].strip
|
252
|
+
right = sides[1].strip
|
253
|
+
|
254
|
+
if left == "ignore_initial"
|
255
|
+
right = (right != "0")
|
256
|
+
elsif left == "time_cutoff"
|
257
|
+
right = right.to_i
|
258
|
+
elsif left == "estimation_factor"
|
259
|
+
right = right.to_f
|
260
|
+
end
|
261
|
+
|
262
|
+
opts[left.to_sym] = right
|
263
|
+
end
|
229
264
|
end
|
230
265
|
|
231
266
|
opts
|
232
267
|
end
|
233
268
|
|
234
|
-
def
|
235
|
-
|
269
|
+
def self.clock_path(path)
|
270
|
+
path+"/.clock"
|
271
|
+
end
|
272
|
+
|
273
|
+
def initialize(path)
|
274
|
+
# Default options
|
275
|
+
$opts = {time_cutoff:120, my_files:"/.*/", estimation_factor:0.9}
|
276
|
+
|
277
|
+
# Parse .clock options
|
278
|
+
clock_opts = Clockout.parse_clockfile(Clockout.clock_path(path))
|
236
279
|
|
237
|
-
|
238
|
-
|
280
|
+
if clock_opts
|
281
|
+
@clockins = clock_opts[:clockins] || []
|
282
|
+
@clockouts = clock_opts[:clockouts] || []
|
283
|
+
|
284
|
+
# Merge with .clock override options
|
285
|
+
$opts.merge!(clock_opts)
|
286
|
+
end
|
239
287
|
|
240
288
|
repo = get_repo(path) || exit
|
241
289
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: clockout
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.2'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-05-
|
12
|
+
date: 2013-05-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: grit
|