timetrap 1.7.3 → 1.7.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CONTRIBUTORS +1 -0
- data/README.md +23 -3
- data/VERSION.yml +1 -1
- data/lib/timetrap/cli.rb +17 -7
- data/lib/timetrap/formatters/factor.rb +19 -0
- data/lib/timetrap/formatters/text.rb +1 -2
- data/lib/timetrap/helpers.rb +4 -7
- data/lib/timetrap/models.rb +7 -0
- data/lib/timetrap/timer.rb +9 -0
- data/spec/timetrap_spec.rb +60 -0
- data/timetrap.gemspec +3 -2
- metadata +5 -4
data/CONTRIBUTORS
CHANGED
data/README.md
CHANGED
@@ -118,9 +118,9 @@ list of parsable time formats, but all of these should work.
|
|
118
118
|
|
119
119
|
#### Built-in Formatters
|
120
120
|
|
121
|
-
Timetrap has built-in support for
|
121
|
+
Timetrap has built-in support for 6 output formats.
|
122
122
|
|
123
|
-
These are **text**, **csv**, **ical**, **json**, and **ids**
|
123
|
+
These are **text**, **csv**, **ical**, **json**, **factor** and **ids**
|
124
124
|
|
125
125
|
The default is a plain **text** format. (You can change the default format using
|
126
126
|
`t configure`).
|
@@ -167,10 +167,29 @@ from one sheet to another sheet. You could do something like this:
|
|
167
167
|
editing entry #44
|
168
168
|
editing entry #46
|
169
169
|
|
170
|
-
A *json* formatter is
|
170
|
+
A *json* formatter is provided because hackers love json.
|
171
171
|
|
172
172
|
$ t d -fjson
|
173
173
|
|
174
|
+
The *factor* formatter is like the default *text* formatter, except it reads special
|
175
|
+
notes in your entry descriptions, and multiplies the entry's duration by them.
|
176
|
+
A note like *f:2* will multiply the entry's duration by two in the output.
|
177
|
+
See https://github.com/samg/timetrap/issues#issue/13 for more details.
|
178
|
+
|
179
|
+
$ # note durations are multiplications of start and end times, based on notes
|
180
|
+
$ t d -ffactor
|
181
|
+
Timesheet: nopoconi
|
182
|
+
Day Start End Duration Notes
|
183
|
+
Mon Mar 07, 2011 19:56:06 - 20:18:37 0:22:31 merge factor in timetrap, f:3
|
184
|
+
20:19:04 - 20:23:02 0:01:59 document factor formatter f:0.5
|
185
|
+
|
186
|
+
0:22:34
|
187
|
+
---------------------------------------------------------
|
188
|
+
Total 0:22:34
|
189
|
+
|
190
|
+
|
191
|
+
|
192
|
+
|
174
193
|
#### Custom Formatters
|
175
194
|
|
176
195
|
Timetrap tries to make it easy to define custom output formats.
|
@@ -302,6 +321,7 @@ Commands
|
|
302
321
|
**sheet**
|
303
322
|
Switch to a timesheet creating it if necessary. The default timesheet is
|
304
323
|
called "default". When no sheet is specified list all existing sheets.
|
324
|
+
The special timesheet name '-' will switch to the last active sheet.
|
305
325
|
|
306
326
|
usage: ``t sheet [TIMESHEET]``
|
307
327
|
|
data/VERSION.yml
CHANGED
data/lib/timetrap/cli.rb
CHANGED
@@ -40,7 +40,7 @@ COMMAND is one of:
|
|
40
40
|
-s, --start <date:qs> Include entries that start on this date or later
|
41
41
|
-e, --end <date:qs> Include entries that start on this date or earlier
|
42
42
|
-f, --format <format> The output format. Valid built-in formats are
|
43
|
-
ical, csv, json, ids, and text (default).
|
43
|
+
ical, csv, json, ids, factor, and text (default).
|
44
44
|
Documentation on defining custom formats can be
|
45
45
|
found in the README included in this
|
46
46
|
distribution.
|
@@ -79,7 +79,8 @@ COMMAND is one of:
|
|
79
79
|
-a, --at <time:qs> Use this time instead of now
|
80
80
|
|
81
81
|
* sheet - Switch to a timesheet creating it if necessary. When no sheet is
|
82
|
-
specified list all sheets.
|
82
|
+
specified list all sheets. The special sheetname '-' will switch to the
|
83
|
+
last active sheet.
|
83
84
|
usage: t sheet [TIMESHEET]
|
84
85
|
|
85
86
|
* week - Shortcut for display with start date set to monday of this week.
|
@@ -270,12 +271,21 @@ COMMAND is one of:
|
|
270
271
|
|
271
272
|
def sheet
|
272
273
|
sheet = unused_args
|
273
|
-
|
274
|
+
case sheet
|
275
|
+
when nil, ''
|
274
276
|
list
|
275
|
-
|
276
|
-
|
277
|
-
|
277
|
+
return
|
278
|
+
when '-'
|
279
|
+
if Timer.last_sheet
|
280
|
+
sheet = Timer.last_sheet
|
281
|
+
else
|
282
|
+
warn 'LAST_SHEET is not set'
|
283
|
+
return
|
284
|
+
end
|
278
285
|
end
|
286
|
+
|
287
|
+
Timer.current_sheet = sheet
|
288
|
+
warn "Switching to sheet #{sheet.inspect}"
|
279
289
|
end
|
280
290
|
|
281
291
|
def list
|
@@ -314,7 +324,7 @@ COMMAND is one of:
|
|
314
324
|
Timer.running_entries.each do |entry|
|
315
325
|
current = entry[:sheet] == Timer.current_sheet
|
316
326
|
out = current ? '*' : ' '
|
317
|
-
out << "#{entry[:sheet]}: #{format_duration(entry.
|
327
|
+
out << "#{entry[:sheet]}: #{format_duration(entry.duration)}".gsub(/ /, ' ')
|
318
328
|
out << " (#{entry.note})" if entry.note =~ /.+/
|
319
329
|
puts out
|
320
330
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Timetrap
|
2
|
+
module Formatters
|
3
|
+
require File.join( File.dirname(__FILE__), 'text' )
|
4
|
+
|
5
|
+
class Factor < Text
|
6
|
+
def initialize entries
|
7
|
+
entries.map! do |e|
|
8
|
+
factor = 1
|
9
|
+
if e.note =~ /\bf(actor)?:([\d\.]+)\b/
|
10
|
+
factor = $2.to_f
|
11
|
+
end
|
12
|
+
e.duration = (e.end_or_now.to_i - e.start.to_i) * factor
|
13
|
+
e
|
14
|
+
end
|
15
|
+
super
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -19,13 +19,12 @@ module Timetrap
|
|
19
19
|
from_current_day = []
|
20
20
|
sheets[sheet].each_with_index do |e, i|
|
21
21
|
from_current_day << e
|
22
|
-
e_end = e.end_or_now
|
23
22
|
self.output << "%-4s%16s%11s -%9s%10s %s\n" % [
|
24
23
|
(Timetrap::CLI.args['-v'] ? e.id : ''),
|
25
24
|
format_date_if_new(e.start, last_start),
|
26
25
|
format_time(e.start),
|
27
26
|
format_time(e.end),
|
28
|
-
format_duration(e.
|
27
|
+
format_duration(e.duration),
|
29
28
|
e.note
|
30
29
|
]
|
31
30
|
|
data/lib/timetrap/helpers.rb
CHANGED
@@ -60,18 +60,15 @@ module Timetrap
|
|
60
60
|
format_date(time) == format_date(other_time)
|
61
61
|
end
|
62
62
|
|
63
|
-
def format_duration stime, etime
|
64
|
-
return '' unless stime and etime
|
65
|
-
secs = etime.to_i - stime.to_i
|
66
|
-
format_seconds secs
|
67
|
-
end
|
68
|
-
|
69
63
|
def format_seconds secs
|
70
64
|
"%2s:%02d:%02d" % [secs/3600, (secs%3600)/60, secs%60]
|
71
65
|
end
|
66
|
+
alias :format_duration :format_seconds
|
72
67
|
|
73
68
|
def format_total entries
|
74
|
-
secs = entries.inject(0)
|
69
|
+
secs = entries.inject(0) do |m, e|
|
70
|
+
m += e.duration
|
71
|
+
end
|
75
72
|
"%2s:%02d:%02d" % [secs/3600, (secs%3600)/60, secs%60]
|
76
73
|
end
|
77
74
|
|
data/lib/timetrap/models.rb
CHANGED
@@ -29,6 +29,13 @@ module Timetrap
|
|
29
29
|
round? ? rounded_end : self[:end]
|
30
30
|
end
|
31
31
|
|
32
|
+
def duration
|
33
|
+
@duration ||= self.end_or_now.to_i - self.start.to_i
|
34
|
+
end
|
35
|
+
def duration=( nd )
|
36
|
+
@duration = nd.to_i
|
37
|
+
end
|
38
|
+
|
32
39
|
def end_or_now
|
33
40
|
self.end || (round? ? round(Time.now) : Time.now)
|
34
41
|
end
|
data/lib/timetrap/timer.rb
CHANGED
@@ -43,6 +43,10 @@ module Timetrap
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def current_sheet= sheet
|
46
|
+
last = Meta.find_or_create(:key => 'last_sheet')
|
47
|
+
last.value = current_sheet
|
48
|
+
last.save
|
49
|
+
|
46
50
|
m = Meta.find_or_create(:key => 'current_sheet')
|
47
51
|
m.value = sheet
|
48
52
|
m.save
|
@@ -55,6 +59,11 @@ module Timetrap
|
|
55
59
|
Meta.find(:key => 'current_sheet').value
|
56
60
|
end
|
57
61
|
|
62
|
+
def last_sheet
|
63
|
+
m = Meta.find(:key => 'last_sheet')
|
64
|
+
m and m.value
|
65
|
+
end
|
66
|
+
|
58
67
|
def entries sheet = nil
|
59
68
|
Entry.filter(:sheet => sheet).order_by(:start)
|
60
69
|
end
|
data/spec/timetrap_spec.rb
CHANGED
@@ -323,6 +323,38 @@ Grand Total 10:00:00
|
|
323
323
|
end
|
324
324
|
end
|
325
325
|
|
326
|
+
describe "factor" do
|
327
|
+
before do
|
328
|
+
Timetrap::Entry.create( :sheet => 'SpecSheet',
|
329
|
+
:note => 'entry f:2', :start => '2008-10-03 16:00:00', :end => '2008-10-03 18:00:00'
|
330
|
+
)
|
331
|
+
Timetrap::Entry.create( :sheet => 'SpecSheet',
|
332
|
+
:note => 'entry f:0.5', :start => '2008-10-04 16:00:00', :end => '2008-10-04 18:00:00'
|
333
|
+
)
|
334
|
+
Timetrap::Entry.create( :sheet => 'SpecSheet',
|
335
|
+
:note => 'entry', :start => '2008-10-04 19:00:00'
|
336
|
+
)
|
337
|
+
Time.stub!(:now).and_return local_time('2008-10-04 20:00:00')
|
338
|
+
@desired_output = <<-OUTPUT
|
339
|
+
Timesheet: SpecSheet
|
340
|
+
Day Start End Duration Notes
|
341
|
+
Fri Oct 03, 2008 16:00:00 - 18:00:00 4:00:00 entry f:2
|
342
|
+
4:00:00
|
343
|
+
Sat Oct 04, 2008 16:00:00 - 18:00:00 1:00:00 entry f:0.5
|
344
|
+
19:00:00 - 1:00:00 entry
|
345
|
+
2:00:00
|
346
|
+
---------------------------------------------------------
|
347
|
+
Total 6:00:00
|
348
|
+
OUTPUT
|
349
|
+
end
|
350
|
+
|
351
|
+
it "should correctly handle factors in notes" do
|
352
|
+
Timetrap::Timer.current_sheet = 'SpecSheet'
|
353
|
+
invoke 'display --format factor'
|
354
|
+
$stdout.string.should == @desired_output
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
326
358
|
describe "default" do
|
327
359
|
before do
|
328
360
|
create_entry(:start => '2008-10-03 12:00:00', :end => '2008-10-03 14:00:00')
|
@@ -733,6 +765,34 @@ END:VCALENDAR
|
|
733
765
|
invoke 'sheet'
|
734
766
|
$stdout.string.should == " Timesheet Running Today Total Time\n*sheet 1 0:00:00 0:00:00 0:00:00\n"
|
735
767
|
end
|
768
|
+
|
769
|
+
describe "using - to switch to the last sheet" do
|
770
|
+
it "should warn if there isn't a sheet set" do
|
771
|
+
lambda do
|
772
|
+
invoke 'sheet -'
|
773
|
+
end.should_not change(Timetrap::Timer, :current_sheet)
|
774
|
+
$stderr.string.should include 'LAST_SHEET is not set'
|
775
|
+
end
|
776
|
+
|
777
|
+
it "should switch to the last active sheet" do
|
778
|
+
invoke 'sheet second'
|
779
|
+
lambda do
|
780
|
+
invoke 'sheet -'
|
781
|
+
end.should change(Timetrap::Timer, :current_sheet).
|
782
|
+
from('second').to('default')
|
783
|
+
end
|
784
|
+
|
785
|
+
it "should toggle back and forth" do
|
786
|
+
invoke 'sheet first'
|
787
|
+
invoke 'sheet second'
|
788
|
+
5.times do
|
789
|
+
invoke 's -'
|
790
|
+
Timetrap::Timer.current_sheet.should == 'first'
|
791
|
+
invoke 's -'
|
792
|
+
Timetrap::Timer.current_sheet.should == 'second'
|
793
|
+
end
|
794
|
+
end
|
795
|
+
end
|
736
796
|
end
|
737
797
|
end
|
738
798
|
end
|
data/timetrap.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{timetrap}
|
8
|
-
s.version = "1.7.
|
8
|
+
s.version = "1.7.4"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Sam Goldstein"]
|
12
|
-
s.date = %q{2011-
|
12
|
+
s.date = %q{2011-03-07}
|
13
13
|
s.default_executable = %q{t}
|
14
14
|
s.description = %q{Command line time tracker}
|
15
15
|
s.email = %q{sgrock@gmail.com}
|
@@ -34,6 +34,7 @@ Gem::Specification.new do |s|
|
|
34
34
|
"lib/timetrap/config.rb",
|
35
35
|
"lib/timetrap/formatters.rb",
|
36
36
|
"lib/timetrap/formatters/csv.rb",
|
37
|
+
"lib/timetrap/formatters/factor.rb",
|
37
38
|
"lib/timetrap/formatters/ical.rb",
|
38
39
|
"lib/timetrap/formatters/ids.rb",
|
39
40
|
"lib/timetrap/formatters/json.rb",
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: timetrap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 3
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 7
|
9
|
-
-
|
10
|
-
version: 1.7.
|
9
|
+
- 4
|
10
|
+
version: 1.7.4
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Sam Goldstein
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-03-07 00:00:00 -08:00
|
19
19
|
default_executable: t
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -106,6 +106,7 @@ files:
|
|
106
106
|
- lib/timetrap/config.rb
|
107
107
|
- lib/timetrap/formatters.rb
|
108
108
|
- lib/timetrap/formatters/csv.rb
|
109
|
+
- lib/timetrap/formatters/factor.rb
|
109
110
|
- lib/timetrap/formatters/ical.rb
|
110
111
|
- lib/timetrap/formatters/ids.rb
|
111
112
|
- lib/timetrap/formatters/json.rb
|