timetrap 1.5.3 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/README.md +114 -92
- data/VERSION.yml +2 -2
- data/lib/timetrap.rb +1 -61
- data/lib/timetrap/cli.rb +89 -64
- data/lib/timetrap/config.rb +2 -2
- data/lib/timetrap/helpers.rb +14 -6
- data/lib/timetrap/timer.rb +55 -0
- data/spec/spec.opts +1 -0
- data/spec/timetrap_spec.rb +201 -164
- data/timetrap.gemspec +3 -2
- metadata +6 -5
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -4,18 +4,8 @@ Timetrap
|
|
4
4
|
Timetrap is a simple command line time tracker written in ruby. It provides an
|
5
5
|
easy to use command line interface for tracking what you spend your time on.
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
natural language times (e.g. "30 minutes ago"), additional commands such as
|
10
|
-
`archive` and `configure`, and support for rounding.
|
11
|
-
|
12
|
-
Timetrap is also able to export entries to several formats (e.g. ical, csv) and
|
13
|
-
is designed to be easily extended to support additional export formats, by
|
14
|
-
creating a new formatter class (in ruby.)
|
15
|
-
|
16
|
-
Timetrap maintains its state in a sqlite3 database.
|
17
|
-
|
18
|
-
Timetrap is available as a gem on gemcutter (http://gemcutter.org/gems/timetrap)
|
7
|
+
Getting Started
|
8
|
+
---------------
|
19
9
|
|
20
10
|
To install:
|
21
11
|
|
@@ -23,67 +13,88 @@ To install:
|
|
23
13
|
|
24
14
|
This will place a ``t`` executable in your path.
|
25
15
|
|
26
|
-
|
27
|
-
http://bitbucket.org/trevor/timebook/src/
|
16
|
+
### Basic Usage
|
28
17
|
|
18
|
+
$ # get help
|
19
|
+
$ t --help
|
29
20
|
|
30
|
-
|
31
|
-
--------
|
21
|
+
Timetrap maintains a list of *timesheets*.
|
32
22
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
Usage
|
55
|
-
-----
|
56
|
-
|
57
|
-
The basic usage is as follows:
|
58
|
-
|
59
|
-
$ # create the "writing" timesheet
|
60
|
-
$ t switch writing
|
61
|
-
|
62
|
-
$ # check in 5 minutes ago
|
63
|
-
$ t in document timetrap --at "10 minutes ago"
|
64
|
-
|
65
|
-
$ # check out
|
66
|
-
$ t out
|
23
|
+
$ # create the "coding" timesheet
|
24
|
+
$ t sheet coding
|
25
|
+
Switching to sheet coding
|
26
|
+
|
27
|
+
All commands can be abbreviated.
|
28
|
+
|
29
|
+
$ # same as "t sheet coding"
|
30
|
+
$ t s coding
|
31
|
+
Switching to sheet coding
|
32
|
+
|
33
|
+
Each timesheet contains *entries*. Each entry has a start and end time, and a
|
34
|
+
note associated with it. An entry without an end time set is considered to be
|
35
|
+
*running*.
|
36
|
+
|
37
|
+
You check in to the current sheet with the `in` command.
|
38
|
+
|
39
|
+
$ # check in with "document timetrap" note
|
40
|
+
$ t in document timetrap
|
41
|
+
Checked into sheet "coding".
|
42
|
+
|
43
|
+
Commands like `display` and `now` will show you the running entry.
|
67
44
|
|
68
|
-
$ # view current timesheet
|
69
45
|
$ t display
|
46
|
+
Timesheet: coding
|
47
|
+
Day Start End Duration Notes
|
48
|
+
Sun Nov 28, 2010 12:26:10 - 0:00:03 document timetrap
|
49
|
+
0:00:03
|
50
|
+
---------------------------------------------------------
|
51
|
+
Total 0:00:03
|
52
|
+
|
53
|
+
$ t now
|
54
|
+
*coding: 0:01:02 (document timetrap)
|
55
|
+
|
56
|
+
If you make a mistake use the `edit` command.
|
57
|
+
|
58
|
+
$ # edit the running entry's note
|
59
|
+
$ t edit writing readme
|
60
|
+
editing entry #42
|
61
|
+
|
62
|
+
You check out with the `out` command.
|
63
|
+
|
64
|
+
$ t out
|
65
|
+
Checked out of sheet "coding"
|
66
|
+
|
67
|
+
### Natural Language Times
|
68
|
+
|
69
|
+
Commands such as `in`, `out`, `edit`, and `display` have flags that accept
|
70
|
+
times as arguments. Any time you pass Timetrap a time it will try to parse it
|
71
|
+
as a natural language time.
|
72
|
+
|
73
|
+
This is very handy if you start working and forget to start Timetrap. You can
|
74
|
+
check in 5 minutes ago using `in`'s `--at` flag.
|
75
|
+
|
76
|
+
$ t in --at "5 minutes ago"
|
70
77
|
|
71
|
-
|
72
|
-
(or creates it if it does not exist). ``t in document timetrap --at "10 minutes
|
73
|
-
ago"`` creates a new period in the current timesheet, and annotates it with the
|
74
|
-
description "document timetrap". The optional ``--at`` flag can be passed to start
|
75
|
-
the entry at a time other than the present. The ``--at`` flag is able to parse
|
76
|
-
natural language times (via Chronic: http://chronic.rubyforge.org/) and will
|
77
|
-
understand 'friday 13:00', 'mon 2:35', '4pm', etc. (also true of the ``edit``
|
78
|
-
command's ``--start`` and ``--end`` flags.) Note that this command would be in
|
79
|
-
error if the ``writing`` timesheet was already active. Finally, ``t out``
|
80
|
-
records the current time as the end time for the most recent period in the
|
81
|
-
``writing`` timesheet.
|
78
|
+
Command line flags also have short versions.
|
82
79
|
|
83
|
-
|
80
|
+
$ # equivilent to the command above
|
81
|
+
$ t i -a "5 minutes ago"
|
82
|
+
|
83
|
+
You can consult the Chronic gem (http://chronic.rubyforge.org/) for a full
|
84
|
+
list of parsable time formats, but all of these should work.
|
85
|
+
|
86
|
+
$ t out --at "in 30 minutes"
|
87
|
+
$ t edit --start "last monday at 10:30am"
|
88
|
+
$ t edit --end "tomorrow at noon"
|
89
|
+
$ t display --start "10am" --end "2pm"
|
90
|
+
$ t i -a "2010-11-29 12:30:00"
|
91
|
+
|
92
|
+
### Output Formats
|
93
|
+
|
94
|
+
Timetrap supports several output formats. The default is a plain text format.
|
84
95
|
|
85
96
|
$ t display
|
86
|
-
Timesheet:
|
97
|
+
Timesheet: coding
|
87
98
|
Day Start End Duration Notes
|
88
99
|
Mon Apr 13, 2009 15:46:51 - 17:03:50 1:16:59 improved display functionality
|
89
100
|
17:25:59 - 17:26:02 0:00:03
|
@@ -91,20 +102,31 @@ To display the current timesheet, invoke the ``t display`` command::
|
|
91
102
|
22:37:38 - 23:38:43 1:01:05 work on kill
|
92
103
|
2:18:52
|
93
104
|
Tue Apr 14, 2009 00:41:16 - 01:40:19 0:59:03 gem packaging
|
94
|
-
10:20:00 - 10:48:10 0:28:10
|
105
|
+
10:20:00 - 10:48:10 0:28:10 working on readme
|
95
106
|
1:27:13
|
96
107
|
---------------------------------------------------------
|
97
108
|
Total 3:46:05
|
98
109
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
110
|
+
You can also output csv for easy import into a spreadsheet.
|
111
|
+
|
112
|
+
$ t display --format csv
|
113
|
+
start,end,note,sheet
|
114
|
+
"2010-08-21 11:19:05","2010-08-21 12:12:04","migrated site","coding"
|
115
|
+
"2010-08-21 12:44:09","2010-08-21 12:48:46","DNS emails and install email packages","coding"
|
116
|
+
"2010-08-21 12:49:57","2010-08-21 13:10:12","A records","coding"
|
117
|
+
"2010-08-21 15:09:37","2010-08-21 16:32:26","setup for wiki","coding"
|
118
|
+
"2010-08-25 20:42:55","2010-08-25 21:41:49","rewrote index","coding"
|
119
|
+
"2010-08-29 15:44:39","2010-08-29 16:21:53","recaptcha","coding"
|
120
|
+
"2010-08-29 21:15:58","2010-08-29 21:30:31","backups","coding"
|
121
|
+
"2010-08-29 21:40:56","2010-08-29 22:32:26","backups","coding"
|
122
|
+
|
123
|
+
Or to ical format for import into a calendar program (remember commands can be abbreviated).
|
124
|
+
|
125
|
+
$ t d -f ical > MyTimeSheet.ics
|
104
126
|
|
105
127
|
Commands
|
106
128
|
--------
|
107
|
-
**
|
129
|
+
**archive**
|
108
130
|
Archives the selected entries (by moving them to a sheet called ``_[SHEET]``)
|
109
131
|
These entries can be seen by running ``t display _[SHEET]``.
|
110
132
|
usage: ``t archive [--start DATE] [--end DATE] [SHEET]``
|
@@ -145,10 +167,6 @@ Commands
|
|
145
167
|
|
146
168
|
usage: ``t edit [--id ID] [--start TIME] [--end TIME] [--append] [NOTES]``
|
147
169
|
|
148
|
-
**format**
|
149
|
-
Deprecated
|
150
|
-
Alias for display
|
151
|
-
|
152
170
|
**in**
|
153
171
|
Start the timer for the current timesheet. Must be called before out. Notes
|
154
172
|
may be specified for this period. This is exactly equivalent to
|
@@ -168,32 +186,27 @@ Commands
|
|
168
186
|
usage: ``t list``
|
169
187
|
|
170
188
|
**now**
|
171
|
-
Print
|
172
|
-
active and what notes are associated with the current period.
|
189
|
+
Print a description of all running entries.
|
173
190
|
|
174
191
|
usage: ``t now``
|
175
192
|
|
176
193
|
**out**
|
177
194
|
Stop the timer for the current timesheet. Must be called after in. Accepts an
|
178
|
-
optional --at flag.
|
179
|
-
|
180
|
-
usage: ``t out [--at TIME]``
|
181
|
-
|
182
|
-
**running**
|
183
|
-
Print all active sheets and any messages associated with them.
|
195
|
+
optional --at flag. Accepts an optional TIMESHEET name to check out of a
|
196
|
+
running, non-current sheet.
|
184
197
|
|
185
|
-
usage: ``t
|
198
|
+
usage: ``t out [--at TIME] [TIMESHEET]``
|
186
199
|
|
187
|
-
**
|
188
|
-
Switch to a
|
189
|
-
|
200
|
+
**sheet**
|
201
|
+
Switch to a timesheet creating it if necessary. The default timesheet is
|
202
|
+
called "default".
|
190
203
|
|
191
|
-
usage: ``t
|
204
|
+
usage: ``t sheet TIMESHEET``
|
192
205
|
|
193
206
|
**week**
|
194
207
|
Shortcut for display with start date set to monday of this week
|
195
208
|
|
196
|
-
usage: ``t week [--ids] [--end DATE] [--format FMT] [
|
209
|
+
usage: ``t week [--ids] [--end DATE] [--format FMT] [TIMESHEET | all]``
|
197
210
|
|
198
211
|
Global Options
|
199
212
|
--------
|
@@ -217,11 +230,20 @@ Configuration
|
|
217
230
|
Configuration of TimeTrap's behavior can be done through a YAML config file.
|
218
231
|
See ``t configure`` for details. Currently supported options are:
|
219
232
|
|
220
|
-
round_in_seconds
|
233
|
+
``round_in_seconds``: The duration of time to use for rounding with the -r flag
|
221
234
|
|
222
|
-
database_file
|
235
|
+
``database_file``: The file path of the sqlite database
|
223
236
|
|
224
|
-
append_notes_delimiter
|
237
|
+
``append_notes_delimiter``: delimiter used when appending notes via ``t edit --append``
|
238
|
+
|
239
|
+
Special Thanks
|
240
|
+
--------------
|
241
|
+
|
242
|
+
The initial version of Timetrap was heavily inspired by Trevor Caira's
|
243
|
+
Timebook, a small python utility.
|
244
|
+
|
245
|
+
Original Timebook available at:
|
246
|
+
http://bitbucket.org/trevor/timebook/src/
|
225
247
|
|
226
248
|
Bugs and Feature Requests
|
227
249
|
--------
|
data/VERSION.yml
CHANGED
data/lib/timetrap.rb
CHANGED
@@ -7,6 +7,7 @@ require 'Getopt/Declare'
|
|
7
7
|
require File.join(File.dirname(__FILE__), 'timetrap', 'config')
|
8
8
|
require File.join(File.dirname(__FILE__), 'timetrap', 'helpers')
|
9
9
|
require File.join(File.dirname(__FILE__), 'timetrap', 'cli')
|
10
|
+
require File.join(File.dirname(__FILE__), 'timetrap', 'timer')
|
10
11
|
DB_NAME = defined?(TEST_MODE) ? nil : Timetrap::Config['database_file']
|
11
12
|
# connect to database. This will create one if it doesn't exist
|
12
13
|
DB = Sequel.sqlite DB_NAME
|
@@ -16,67 +17,6 @@ Dir["#{File.dirname(__FILE__)}/timetrap/formatters/*.rb"].each do |path|
|
|
16
17
|
end
|
17
18
|
|
18
19
|
module Timetrap
|
19
|
-
extend self
|
20
|
-
|
21
|
-
def current_sheet= sheet
|
22
|
-
m = Meta.find_or_create(:key => 'current_sheet')
|
23
|
-
m.value = sheet
|
24
|
-
m.save
|
25
|
-
end
|
26
|
-
|
27
|
-
def current_sheet
|
28
|
-
unless Meta.find(:key => 'current_sheet')
|
29
|
-
Meta.create(:key => 'current_sheet', :value => 'default')
|
30
|
-
end
|
31
|
-
Meta.find(:key => 'current_sheet').value
|
32
|
-
end
|
33
|
-
|
34
|
-
def entries sheet = nil
|
35
|
-
Entry.filter(:sheet => sheet).order_by(:start)
|
36
|
-
end
|
37
|
-
|
38
|
-
def running?
|
39
|
-
!!active_entry
|
40
|
-
end
|
41
|
-
|
42
|
-
def active_entry
|
43
|
-
Entry.find(:sheet => Timetrap.current_sheet, :end => nil)
|
44
|
-
end
|
45
|
-
|
46
|
-
def stop time = nil
|
47
|
-
while a = active_entry
|
48
|
-
time ||= Time.now
|
49
|
-
a.end = time
|
50
|
-
a.save
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def start note, time = nil
|
55
|
-
raise AlreadyRunning if running?
|
56
|
-
time ||= Time.now
|
57
|
-
Entry.create(:sheet => Timetrap.current_sheet, :note => note, :start => time).save
|
58
|
-
rescue => e
|
59
|
-
CLI.say e.message
|
60
|
-
end
|
61
|
-
|
62
|
-
def switch sheet
|
63
|
-
self.current_sheet = sheet
|
64
|
-
end
|
65
|
-
|
66
|
-
def kill_sheet sheet
|
67
|
-
Entry.filter(:sheet => sheet).destroy
|
68
|
-
end
|
69
|
-
|
70
|
-
def format format_klass, entries
|
71
|
-
format_klass.new(entries).output
|
72
|
-
end
|
73
|
-
|
74
|
-
class AlreadyRunning < StandardError
|
75
|
-
def message
|
76
|
-
"Timetrap is already running"
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
20
|
CLI.args = Getopt::Declare.new(<<-EOF)
|
81
21
|
#{CLI::USAGE}
|
82
22
|
EOF
|
data/lib/timetrap/cli.rb
CHANGED
@@ -64,18 +64,19 @@ COMMAND is one of:
|
|
64
64
|
* list - Show the available timesheets.
|
65
65
|
usage: t list
|
66
66
|
|
67
|
-
* now - Show
|
67
|
+
* now - Show all running entries.
|
68
68
|
usage: t now
|
69
69
|
|
70
|
-
* out - Stop the timer for the
|
71
|
-
usage: t out [--at TIME]
|
70
|
+
* out - Stop the timer for the a timesheet.
|
71
|
+
usage: t out [--at TIME] [TIMESHEET]
|
72
72
|
-a, --at <time:qs> Use this time instead of now
|
73
73
|
|
74
|
-
* running -
|
75
|
-
usage: t running
|
74
|
+
* running - Deprecated: alias for now.
|
76
75
|
|
77
|
-
*
|
78
|
-
usage: t
|
76
|
+
* sheet - Switch to a timesheet creating it if necessary.
|
77
|
+
usage: t sheet TIMESHEET
|
78
|
+
|
79
|
+
* switch - Deprecated: renamed to sheet.
|
79
80
|
|
80
81
|
* week - Shortcut for display with start date set to monday of this week.
|
81
82
|
usage: t week [--ids] [--end DATE] [--format FMT] [SHEET | all]
|
@@ -85,11 +86,12 @@ COMMAND is one of:
|
|
85
86
|
-h, --help Display this help.
|
86
87
|
-r, --round Round output to 15 minute start and end times.
|
87
88
|
-y, --yes Noninteractive, assume yes as answer to all prompts.
|
89
|
+
--debug Display stack traces for errors.
|
88
90
|
|
89
91
|
EXAMPLES
|
90
92
|
|
91
93
|
# create the "MyTimesheet" timesheet
|
92
|
-
$ t
|
94
|
+
$ t sheet MyTimesheet
|
93
95
|
|
94
96
|
# check in 5 minutes ago with a note
|
95
97
|
$ t in --at '5 minutes ago' doing some stuff
|
@@ -108,29 +110,28 @@ COMMAND is one of:
|
|
108
110
|
end
|
109
111
|
|
110
112
|
def invoke
|
111
|
-
args['-h'] ?
|
113
|
+
args['-h'] ? puts(USAGE) : invoke_command_if_valid
|
112
114
|
rescue => e
|
113
|
-
|
114
|
-
|
115
|
+
raise e if args['--debug']
|
116
|
+
warn e.message
|
117
|
+
exit 1 unless defined? TEST_MODE
|
115
118
|
end
|
116
119
|
|
117
120
|
def commands
|
118
121
|
Timetrap::CLI::USAGE.scan(/\* \w+/).map{|s| s.gsub(/\* /, '')}
|
119
122
|
end
|
120
123
|
|
121
|
-
def say *something
|
122
|
-
puts *something
|
123
|
-
end
|
124
|
-
|
125
124
|
def invoke_command_if_valid
|
126
125
|
command = args.unused.shift
|
127
126
|
set_global_options
|
128
127
|
case (valid = commands.select{|name| name =~ %r|^#{command}|}).size
|
129
|
-
when 0 then
|
130
|
-
when 1 then send valid[0]
|
128
|
+
when 0 then puts "Invalid command: #{command}"
|
131
129
|
else
|
132
|
-
|
133
|
-
|
130
|
+
if command
|
131
|
+
send valid[0]
|
132
|
+
else
|
133
|
+
puts USAGE
|
134
|
+
end
|
134
135
|
end
|
135
136
|
end
|
136
137
|
|
@@ -147,25 +148,30 @@ COMMAND is one of:
|
|
147
148
|
e.update :sheet => "_#{e.sheet}"
|
148
149
|
end
|
149
150
|
else
|
150
|
-
|
151
|
+
warn "archive aborted!"
|
151
152
|
end
|
152
153
|
end
|
153
154
|
|
154
155
|
def configure
|
155
156
|
Config.configure!
|
156
|
-
|
157
|
+
puts "Config file is at #{Config::PATH.inspect}"
|
157
158
|
end
|
158
159
|
|
159
160
|
def edit
|
160
|
-
entry = args['-i'] ? Entry[args['-i']] :
|
161
|
-
|
161
|
+
entry = args['-i'] ? Entry[args['-i']] : Timer.active_entry
|
162
|
+
unless entry
|
163
|
+
warn "can't find entry"
|
164
|
+
return
|
165
|
+
else
|
166
|
+
warn "editing entry ##{entry.id.inspect}"
|
167
|
+
end
|
162
168
|
entry.update :start => args['-s'] if args['-s'] =~ /.+/
|
163
169
|
entry.update :end => args['-e'] if args['-e'] =~ /.+/
|
164
170
|
|
165
171
|
# update sheet
|
166
172
|
if args['-m'] =~ /.+/
|
167
|
-
if entry ==
|
168
|
-
|
173
|
+
if entry == Timer.active_entry
|
174
|
+
Timer.current_sheet = args['-m']
|
169
175
|
end
|
170
176
|
entry.update :sheet => args['-m']
|
171
177
|
end
|
@@ -185,11 +191,17 @@ COMMAND is one of:
|
|
185
191
|
end
|
186
192
|
|
187
193
|
def in
|
188
|
-
|
194
|
+
Timer.start unused_args, args['-a']
|
195
|
+
warn "Checked into sheet #{Timer.current_sheet.inspect}."
|
189
196
|
end
|
190
197
|
|
191
198
|
def out
|
192
|
-
|
199
|
+
sheet = sheet_name_from_string(unused_args)
|
200
|
+
if Timer.stop sheet, args['-a']
|
201
|
+
warn "Checked out of sheet #{sheet.inspect}."
|
202
|
+
else
|
203
|
+
warn "No running entry on sheet #{sheet.inspect}."
|
204
|
+
end
|
193
205
|
end
|
194
206
|
|
195
207
|
def kill
|
@@ -198,21 +210,21 @@ COMMAND is one of:
|
|
198
210
|
out << "(#{e.note}) " if e.note.to_s =~ /.+/
|
199
211
|
if ask_user out
|
200
212
|
e.destroy
|
201
|
-
|
213
|
+
warn "it's dead"
|
202
214
|
else
|
203
|
-
|
215
|
+
warn "will not kill"
|
204
216
|
end
|
205
217
|
elsif (sheets = Entry.map{|e| e.sheet }.uniq).include?(sheet = unused_args)
|
206
218
|
victims = Entry.filter(:sheet => sheet).count
|
207
219
|
if ask_user "are you sure you want to delete #{victims} entries on sheet #{sheet.inspect}? "
|
208
|
-
|
209
|
-
|
220
|
+
Entry.filter(:sheet => sheet).destroy
|
221
|
+
warn "killed #{victims} entries"
|
210
222
|
else
|
211
|
-
|
223
|
+
warn "will not kill"
|
212
224
|
end
|
213
225
|
else
|
214
226
|
victim = args['-i'] ? args['-i'].to_s.inspect : sheet.inspect
|
215
|
-
|
227
|
+
warn "can't find #{victim} to kill", 'sheets:', *sheets
|
216
228
|
end
|
217
229
|
end
|
218
230
|
|
@@ -224,38 +236,52 @@ COMMAND is one of:
|
|
224
236
|
Timetrap::Formatters::Text
|
225
237
|
end
|
226
238
|
rescue
|
227
|
-
|
239
|
+
warn "Invalid format #{args['-f'].inspect} specified."
|
228
240
|
return
|
229
241
|
end
|
230
|
-
|
242
|
+
entries = selected_entries.order(:start).all
|
243
|
+
if entries == []
|
244
|
+
warn "No entries were selected to display."
|
245
|
+
else
|
246
|
+
puts fmt_klass.new(entries).output
|
247
|
+
end
|
231
248
|
end
|
232
249
|
alias_method :format, :display
|
233
250
|
|
234
|
-
|
235
|
-
def switch
|
251
|
+
def sheet
|
236
252
|
sheet = unused_args
|
237
|
-
|
238
|
-
|
253
|
+
unless sheet =~ /.+/
|
254
|
+
warn "No sheet specified"
|
255
|
+
else
|
256
|
+
Timer.current_sheet = sheet
|
257
|
+
warn "Switching to sheet #{sheet.inspect}"
|
258
|
+
end
|
239
259
|
end
|
240
260
|
|
261
|
+
alias_method :switch, :sheet
|
262
|
+
|
241
263
|
def list
|
242
|
-
sheets = Entry.sheets.map do |sheet|
|
264
|
+
sheets = ([Timer.current_sheet] | Entry.sheets).map do |sheet|
|
243
265
|
sheet_atts = {:total => 0, :running => 0, :today => 0}
|
244
|
-
Timetrap::Entry.filter(:sheet => sheet)
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
266
|
+
entries = Timetrap::Entry.filter(:sheet => sheet)
|
267
|
+
if entries.empty?
|
268
|
+
sheet_atts.merge(:name => sheet)
|
269
|
+
else
|
270
|
+
entries.inject(sheet_atts) do |m, e|
|
271
|
+
e_end = e.end_or_now
|
272
|
+
m[:name] ||= sheet
|
273
|
+
m[:total] += (e_end.to_i - e.start.to_i)
|
274
|
+
m[:running] += (e_end.to_i - e.start.to_i) unless e.end
|
275
|
+
m[:today] += (e_end.to_i - e.start.to_i) if same_day?(Time.now, e.start)
|
276
|
+
m
|
277
|
+
end
|
251
278
|
end
|
252
|
-
end
|
253
|
-
if sheets.empty? then say "No sheets found"; return end
|
279
|
+
end.sort_by{|sheet| sheet[:name].downcase}
|
254
280
|
width = sheets.sort_by{|h|h[:name].length }.last[:name].length + 4
|
255
|
-
|
281
|
+
puts " %-#{width}s%-12s%-12s%s" % ["Timesheet", "Running", "Today", "Total Time"]
|
256
282
|
sheets.each do |sheet|
|
257
|
-
star = sheet[:name] ==
|
258
|
-
|
283
|
+
star = sheet[:name] == Timer.current_sheet ? '*' : ' '
|
284
|
+
puts "#{star}%-#{width}s%-12s%-12s%s" % [
|
259
285
|
sheet[:running],
|
260
286
|
sheet[:today],
|
261
287
|
sheet[:total]
|
@@ -264,19 +290,18 @@ COMMAND is one of:
|
|
264
290
|
end
|
265
291
|
|
266
292
|
def now
|
267
|
-
if
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
293
|
+
if !Timer.running?
|
294
|
+
puts "*#{Timer.current_sheet}: not running"
|
295
|
+
end
|
296
|
+
Timer.running_entries.each do |entry|
|
297
|
+
current = entry[:sheet] == Timer.current_sheet
|
298
|
+
out = current ? '*' : ' '
|
299
|
+
out << "#{entry[:sheet]}: #{format_duration(entry.start, entry.end_or_now)}".gsub(/ /, ' ')
|
300
|
+
out << " (#{entry.note})" if entry.note =~ /.+/
|
301
|
+
puts out
|
273
302
|
end
|
274
303
|
end
|
275
|
-
|
276
|
-
def running
|
277
|
-
say "Running Timesheets:"
|
278
|
-
say Timetrap::Entry.filter(:end => nil).map{|e| " #{e.sheet}: #{e.note}"}.uniq.sort
|
279
|
-
end
|
304
|
+
alias_method :running, :display
|
280
305
|
|
281
306
|
def week
|
282
307
|
args['-s'] = Date.today.wday == 1 ? Date.today.to_s : Date.parse(Chronic.parse(%q(last monday)).to_s).to_s
|
@@ -291,7 +316,7 @@ COMMAND is one of:
|
|
291
316
|
|
292
317
|
def ask_user question
|
293
318
|
return true if args['-y']
|
294
|
-
print question
|
319
|
+
$stderr.print question
|
295
320
|
$stdin.gets =~ /\Aye?s?\Z/i
|
296
321
|
end
|
297
322
|
|