Pratt 1.6.2 → 1.6.4
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/.gitignore +2 -0
- data/Pratt.gemspec +38 -12
- data/README.html +85 -0
- data/README.txt +6 -3
- data/TODO +6 -3
- data/VERSION +1 -1
- data/config.rb +3 -17
- data/db/zips.csv.zip +0 -0
- data/lib/models.rb +8 -0
- data/lib/pratt.rb +58 -293
- data/lib/pratt.rb.orig +626 -0
- data/lib/pratt/core_ext.rb +6 -0
- data/lib/pratt/{array.rb → core_ext/array.rb} +5 -0
- data/lib/pratt/core_ext/float.rb +28 -0
- data/lib/pratt/core_ext/nil.rb +5 -0
- data/lib/pratt/core_ext/numeric.rb +9 -0
- data/lib/pratt/{string.rb → core_ext/string.rb} +13 -0
- data/lib/pratt/core_ext/time.rb +20 -0
- data/lib/pratt/dialogs.rb +81 -0
- data/lib/pratt/formatting.rb +24 -0
- data/lib/pratt/project_actions.rb +25 -0
- data/lib/pratt/reports.rb +127 -0
- data/models/app.rb +1 -2
- data/models/customer.rb +22 -0
- data/models/invoice.rb +28 -0
- data/models/invoice_whence.rb +18 -0
- data/models/payment.rb +1 -1
- data/models/pratt.rb +0 -9
- data/models/project.rb +16 -22
- data/models/whence.rb +10 -1
- data/models/zip.rb +27 -0
- data/spec/fixtures/graph.expectation +0 -5
- data/spec/fixtures/proportions.expectation +4 -0
- data/spec/float_spec.rb +24 -0
- data/spec/numeric_spec.rb +30 -0
- data/spec/pratt_spec.rb +5 -4
- data/spec/project_spec.rb +8 -2
- data/spec/spec.opts +1 -1
- data/spec/spec_helper.rb +31 -1
- data/spec/string_ext_spec.rb +33 -0
- data/views/current.eruby +5 -0
- data/views/general-invoice.eruby +538 -0
- data/views/graph.eruby +2 -7
- data/views/invoice.eruby +3 -3
- data/views/main.rb +8 -6
- data/views/proportions.eruby +4 -0
- data/views/raw.eruby +1 -1
- metadata +86 -33
- data/lib/pratt/time.rb +0 -12
- data/views/env.rb +0 -22
data/lib/pratt.rb.orig
ADDED
@@ -0,0 +1,626 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'fileutils'
|
3
|
+
require 'optparse'
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
require 'colored'
|
8
|
+
require 'chronic'
|
9
|
+
require 'active_record'
|
10
|
+
require 'erubis'
|
11
|
+
require 'hoe'
|
12
|
+
require 'mocha'
|
13
|
+
require 'config'
|
14
|
+
require 'shifty_week'
|
15
|
+
require 'shifty_week/time'
|
16
|
+
require 'shifty_week/date'
|
17
|
+
|
18
|
+
require 'lib/pratt/array'
|
19
|
+
require 'lib/pratt/string'
|
20
|
+
require 'lib/pratt/time'
|
21
|
+
|
22
|
+
require 'models/app'
|
23
|
+
require 'models/customer'
|
24
|
+
require 'models/whence'
|
25
|
+
require 'models/project'
|
26
|
+
require 'models/payment'
|
27
|
+
|
28
|
+
class Pratt
|
29
|
+
|
30
|
+
NAME = 'Pratt'
|
31
|
+
URL = 'http://www.frogstarr78.com/projects/pratt'
|
32
|
+
AUTHORS = ['Scott Noel-Hemming']
|
33
|
+
SUMMARY = "Pro/Re-Active Time Tracker. Track time based on what you expect to be working on, with frequent prompts to ensure accuracy."
|
34
|
+
DESCRIPTION = %q|
|
35
|
+
Need a way to keep track of your time, but get caught up in work? Or constant interruptions?
|
36
|
+
Yeah you know who I'm talking about. Those people from the [abc] department always "NEEDING xyz FEATURE NOW!!!".
|
37
|
+
Seriously though. I'm usually just loose track of time. I wanted an app that I could start with a task I think
|
38
|
+
I'll be working on, but that get's in my face constantly to ensure I'm still working on it. And if I'm not any longer,
|
39
|
+
provides an easy way of changing to another task, or if I have changed tasks and not already updated the app, would
|
40
|
+
provide an easy way of changing the task of the previously recorded interval. That's what this is intended to do.
|
41
|
+
|
42
|
+
Time Tracking.
|
43
|
+
Proactively set what you expect to work on.
|
44
|
+
Reactively modify what you are no longer working on.
|
45
|
+
|
|
46
|
+
DEPENDENCIES = ["activerecord >=2.1.1", "sqlite3-ruby >=1.2.4", "rspec >=1.2.6", "mocha >=0.9.5"]
|
47
|
+
VERSION = File.open( File.join(Dir.pwd, 'VERSION') ).read.strip
|
48
|
+
|
49
|
+
PID_FILE='pratt.pid'
|
50
|
+
FMT = "%a %X %b %d %Y"
|
51
|
+
INVOICE_FMT = "%x"
|
52
|
+
@@color = true
|
53
|
+
|
54
|
+
attr_accessor :when_to, :scale, :color, :show_all, :env, :raw_conditions, :template, :week_day_start
|
55
|
+
attr_reader :project, :todo
|
56
|
+
def initialize proj = nil
|
57
|
+
@when_to = DateTime.now
|
58
|
+
@week = false
|
59
|
+
@day = false
|
60
|
+
@todo = []
|
61
|
+
@scale = nil
|
62
|
+
@show_all = false
|
63
|
+
@template = nil
|
64
|
+
@env = :development
|
65
|
+
@raw_conditions = ''
|
66
|
+
self.project = proj unless proj.nil?
|
67
|
+
end
|
68
|
+
|
69
|
+
# Set the project to something (Project/String)
|
70
|
+
# Conditionally creating a new project if the project
|
71
|
+
# named by the parameter isn't found
|
72
|
+
def project= proj
|
73
|
+
if proj.is_a?(Project)
|
74
|
+
@project = proj
|
75
|
+
else
|
76
|
+
@project = Project.find_or_create_by_name( { :name => proj } )
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# We should act like an array
|
81
|
+
def << what
|
82
|
+
@todo << what
|
83
|
+
end
|
84
|
+
|
85
|
+
# Singleton Accessor for @app
|
86
|
+
def app
|
87
|
+
@app ||= App.last
|
88
|
+
@app
|
89
|
+
end
|
90
|
+
|
91
|
+
# TODO Rename
|
92
|
+
def graph
|
93
|
+
@primary = @off_total = @rest_total = 0.0
|
94
|
+
self.template = 'graph'
|
95
|
+
|
96
|
+
if project?
|
97
|
+
@projects = [project]
|
98
|
+
|
99
|
+
@primary = project.time_spent(scale, when_to)
|
100
|
+
@scaled_total = project.whences.time_spent(scale, when_to)
|
101
|
+
else
|
102
|
+
@projects = Project.all
|
103
|
+
|
104
|
+
@projects.each do |proj|
|
105
|
+
@primary = proj.time_spent(scale, when_to) if proj.name == Project.primary.name
|
106
|
+
@off_total = proj.time_spent(scale, when_to) if proj.name == Project.off.name
|
107
|
+
@rest_total += proj.time_spent(scale, when_to) if Project.rest.collect(&:name).include?(proj.name)
|
108
|
+
end
|
109
|
+
@scaled_total = Whence.time_spent(scale, when_to)-@off_total
|
110
|
+
end
|
111
|
+
|
112
|
+
if @primary + @off_total + @rest_total > 0.0
|
113
|
+
process_template!
|
114
|
+
else
|
115
|
+
"No data to report"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Generate an invoice for a given time period
|
120
|
+
def invoice
|
121
|
+
self.template = 'invoice'
|
122
|
+
|
123
|
+
if project?
|
124
|
+
@projects = [project]
|
125
|
+
|
126
|
+
@total = project.time_spent(scale, when_to)
|
127
|
+
else
|
128
|
+
@projects = (Project.all - [Project.primary, Project.off])
|
129
|
+
@projects.select! {|proj| show_all or ( !show_all and proj.time_spent(scale, when_to) != 0.0 ) }
|
130
|
+
|
131
|
+
@total = @projects.inject 0.0 do |total, proj|
|
132
|
+
total += proj.time_spent(scale, when_to)
|
133
|
+
total
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
@projects.each do |project|
|
138
|
+
puts "#{project.name} #{project.payment.inspect}"
|
139
|
+
end
|
140
|
+
if @total > 0.0
|
141
|
+
puts process_template!
|
142
|
+
else
|
143
|
+
puts "No data to report"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def console options = []
|
148
|
+
options << %w(-r\ irb/completion -r\ lib/pratt --simple-prompt)
|
149
|
+
exec "irb #{options.join ' '}"
|
150
|
+
end
|
151
|
+
|
152
|
+
def current
|
153
|
+
project_names = ([Project.primary, Project.off] | Project.rest).collect(&:name)
|
154
|
+
|
155
|
+
if last_whence = Whence.last_unended || Whence.last
|
156
|
+
puts " projects: " << (
|
157
|
+
project_names.collect {|project_name| "'#{project_name.send(last_whence.end_at.nil? && last_whence.project.name == project_name ? :green : :magenta)}'" }
|
158
|
+
) * ' '
|
159
|
+
if last_whence.end_at.nil?
|
160
|
+
puts " started: #{last_whence.start_at.strftime(FMT).send(:blue)}"
|
161
|
+
time_til = ( app.interval - ( Time.now - last_whence.start_at ) )
|
162
|
+
puts "next prompt: %s %s"% [Pratt.fmt_i( time_til / 60.0, 'min', :yellow ), Pratt.fmt_i( time_til % 60, 'sec', :yellow ) ], ''
|
163
|
+
end
|
164
|
+
else
|
165
|
+
puts " projects: " << (
|
166
|
+
project_names.collect do |project_name|
|
167
|
+
"'#{project_name.magenta}'"
|
168
|
+
end
|
169
|
+
) * ' '
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def begin
|
174
|
+
self.project.start! when_to
|
175
|
+
end
|
176
|
+
def restart
|
177
|
+
if project?
|
178
|
+
project.restart! when_to
|
179
|
+
else
|
180
|
+
Whence.last_unended.project.restart! when_to
|
181
|
+
end
|
182
|
+
end
|
183
|
+
def end
|
184
|
+
if project?
|
185
|
+
project.stop! when_to
|
186
|
+
else
|
187
|
+
Whence.last_unended.stop! when_to
|
188
|
+
end
|
189
|
+
end
|
190
|
+
def change
|
191
|
+
Whence.last.change! project.name
|
192
|
+
end
|
193
|
+
def destroy
|
194
|
+
project.destroy
|
195
|
+
end
|
196
|
+
|
197
|
+
def cpid
|
198
|
+
`pgrep -fl 'bin/pratt'`.chomp.split(' ').first
|
199
|
+
end
|
200
|
+
|
201
|
+
def pid
|
202
|
+
self.template = 'pid'
|
203
|
+
puts process_template!
|
204
|
+
end
|
205
|
+
|
206
|
+
def raw
|
207
|
+
self.template = 'raw'
|
208
|
+
|
209
|
+
if project?
|
210
|
+
@whences = project.whences.all
|
211
|
+
else
|
212
|
+
case raw_conditions
|
213
|
+
when 'all'
|
214
|
+
@whences = Whence.find raw_conditions.to_sym
|
215
|
+
when /^last$/, 'first'
|
216
|
+
@whences = [Whence.find raw_conditions.to_sym]
|
217
|
+
when /last[\(\s]?(\d+)[\)\s]?/
|
218
|
+
@whences = Whence.all.last($1.to_i)
|
219
|
+
else
|
220
|
+
@whences = Whence.all :conditions => ["start_at > ?", Chronic.parse("today 00:00")]
|
221
|
+
end
|
222
|
+
end
|
223
|
+
@whences.sort_by(&:id)
|
224
|
+
puts process_template!
|
225
|
+
end
|
226
|
+
|
227
|
+
def quit
|
228
|
+
project.stop! if project? and project.whences.last_unended
|
229
|
+
Whence.last_unended.stop! if Whence.last_unended
|
230
|
+
begin
|
231
|
+
Process.kill("KILL", app.pid.to_i)
|
232
|
+
rescue Errno::ESRCH
|
233
|
+
end
|
234
|
+
app.pid = ''
|
235
|
+
app.gui = ''
|
236
|
+
app.save!
|
237
|
+
end
|
238
|
+
|
239
|
+
def gui
|
240
|
+
if Whence.last_unended
|
241
|
+
pop
|
242
|
+
else
|
243
|
+
main
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
def show_env
|
248
|
+
defork { system("ruby views/env.rb ") }
|
249
|
+
end
|
250
|
+
|
251
|
+
def detect
|
252
|
+
if self.daemonized?
|
253
|
+
gui
|
254
|
+
else
|
255
|
+
daemonize!
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
<<<<<<< HEAD
|
260
|
+
def unlock
|
261
|
+
self.app.unlock
|
262
|
+
end
|
263
|
+
=======
|
264
|
+
def run
|
265
|
+
# Pratt.connect self.env
|
266
|
+
|
267
|
+
self.begin if i_should?(:begin)
|
268
|
+
self.change if i_should?(:change)
|
269
|
+
self.restart if i_should?(:restart)
|
270
|
+
self.end if i_should?(:end)
|
271
|
+
|
272
|
+
self.destroy if i_should?(:destroy)
|
273
|
+
|
274
|
+
self.pid if i_should?(:pid)
|
275
|
+
self.raw if i_should?(:raw)
|
276
|
+
self.current if i_should?(:current)
|
277
|
+
self.graph if i_should?(:graph)
|
278
|
+
self.gui if i_should?(:gui)
|
279
|
+
self.detect if i_should?(:detect)
|
280
|
+
self.tray_menu if i_should?(:tray_menu)
|
281
|
+
self.app.unlock if i_should?(:unlock)
|
282
|
+
>>>>>>> 08f31db4a4500d2f6950d31ca6646c6b08ac7432
|
283
|
+
|
284
|
+
def run
|
285
|
+
self.when_to.week_day_start = self.week_day_start
|
286
|
+
puts self.when_to.inspect, self.when_to.week_day_start
|
287
|
+
# must happen before any actions but after all cli argument parsing
|
288
|
+
|
289
|
+
self.begin if i_should? :begin
|
290
|
+
self.change if i_should? :change
|
291
|
+
self.restart if i_should? :restart
|
292
|
+
self.end if i_should? :end
|
293
|
+
|
294
|
+
self.destroy if i_should? :destroy
|
295
|
+
|
296
|
+
self.pid if i_should? :pid
|
297
|
+
self.raw if i_should? :raw
|
298
|
+
self.current if i_should? :current
|
299
|
+
puts self.graph if i_should? :graph
|
300
|
+
self.invoice if i_should? :invoice
|
301
|
+
self.console if i_should? :console
|
302
|
+
self.gui if i_should? :gui
|
303
|
+
self.show_env if i_should? :env
|
304
|
+
self.detect if i_should? :detect
|
305
|
+
self.unlock if i_should? :unlock
|
306
|
+
|
307
|
+
self.quit if i_should? :quit
|
308
|
+
self.daemonize! if i_should? :daemonize and not self.daemonized?
|
309
|
+
end
|
310
|
+
|
311
|
+
def daemonized?
|
312
|
+
!app.pid.blank? and ( cpid.to_i == app.pid )
|
313
|
+
end
|
314
|
+
def daemonize!
|
315
|
+
<<<<<<< HEAD
|
316
|
+
defork {
|
317
|
+
puts "pratt (#{Process.pid.to_s.yellow})"
|
318
|
+
app.pid = Process.pid
|
319
|
+
app.save!
|
320
|
+
|
321
|
+
gui
|
322
|
+
while(daemonized?)
|
323
|
+
sleep(app.interval)
|
324
|
+
gui
|
325
|
+
end
|
326
|
+
quit
|
327
|
+
}
|
328
|
+
=======
|
329
|
+
# self.class.connect :development
|
330
|
+
puts "pratt (#{Process.pid.to_s.yellow})"
|
331
|
+
app.pid = Process.pid
|
332
|
+
app.save!
|
333
|
+
|
334
|
+
tray_icon
|
335
|
+
gui
|
336
|
+
while(daemonized?)
|
337
|
+
sleep(app.interval)
|
338
|
+
gui
|
339
|
+
end
|
340
|
+
quit
|
341
|
+
end
|
342
|
+
|
343
|
+
def gui
|
344
|
+
if Whence.last_unended
|
345
|
+
pop
|
346
|
+
else
|
347
|
+
main
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
def tray_icon
|
352
|
+
Process.detach(
|
353
|
+
fork { system("ruby views/tray_icon.rb") }
|
354
|
+
)
|
355
|
+
>>>>>>> 08f31db4a4500d2f6950d31ca6646c6b08ac7432
|
356
|
+
end
|
357
|
+
|
358
|
+
def tray_menu
|
359
|
+
self.app.reload
|
360
|
+
return if self.app.gui?('tray_menu', true)
|
361
|
+
self.app.log('tray_menu')
|
362
|
+
Process.detach(
|
363
|
+
fork { system("ruby views/tray_menu.rb '#{Whence.last_unended.project.name}' 1197 2") }
|
364
|
+
)
|
365
|
+
end
|
366
|
+
|
367
|
+
private
|
368
|
+
def main
|
369
|
+
self.app.reload
|
370
|
+
return if self.app.gui?('main', true)
|
371
|
+
self.app.log('main')
|
372
|
+
projects = ([Project.primary, Project.off] | Project.rest).collect(&:name)
|
373
|
+
if Whence.count == 0
|
374
|
+
# first run
|
375
|
+
project = Whence.new(:project => Project.primary)
|
376
|
+
else
|
377
|
+
project = Whence.last_unended || Whence.last
|
378
|
+
end
|
379
|
+
defork { system("ruby views/main.rb --projects '#{projects*"','"}' --current '#{project.project.name}'") }
|
380
|
+
end
|
381
|
+
def pop
|
382
|
+
self.app.reload
|
383
|
+
return if self.app.gui?('pop', true)
|
384
|
+
self.app.log('pop')
|
385
|
+
self.project = Whence.last_unended.project
|
386
|
+
defork { system("ruby views/pop.rb --project '#{project.name}' --start '#{project.whences.last_unended.start_at}' --project_time '#{Pratt.totals(project.time_spent)}'") }
|
387
|
+
end
|
388
|
+
|
389
|
+
def i_should? what
|
390
|
+
@todo.include?(what)
|
391
|
+
end
|
392
|
+
|
393
|
+
def project?
|
394
|
+
!@project.nil? and @project.name?
|
395
|
+
end
|
396
|
+
|
397
|
+
def process_template!
|
398
|
+
input = File.open(Pratt.root("views", "#{template}.eruby").first).read
|
399
|
+
erubis = Erubis::Eruby.new(input)
|
400
|
+
erubis.evaluate(self)
|
401
|
+
end
|
402
|
+
|
403
|
+
def padded_to_max string
|
404
|
+
self.class.padded_to_max string
|
405
|
+
end
|
406
|
+
|
407
|
+
def defork &block
|
408
|
+
Process.detach(
|
409
|
+
fork &block
|
410
|
+
)
|
411
|
+
end
|
412
|
+
|
413
|
+
class << self
|
414
|
+
|
415
|
+
|
416
|
+
def max
|
417
|
+
# TODO Fix me
|
418
|
+
@max ||= Project.all.inject(0) {|x,p| x = p.name.length if p.name.length > x; x }
|
419
|
+
end
|
420
|
+
|
421
|
+
def color
|
422
|
+
@@color
|
423
|
+
end
|
424
|
+
|
425
|
+
def color= c
|
426
|
+
@@color = c
|
427
|
+
end
|
428
|
+
|
429
|
+
def color?
|
430
|
+
@@color == true
|
431
|
+
end
|
432
|
+
|
433
|
+
|
434
|
+
def parse args
|
435
|
+
me = Pratt.new
|
436
|
+
|
437
|
+
# There are aa few things we're parsing here
|
438
|
+
# Pratt config arguments (Ideally that should be all we do)
|
439
|
+
# Pratt actions. These may require ordering or not. They also may require an argument value.
|
440
|
+
#
|
441
|
+
# TODO: Redo the cli parsing...
|
442
|
+
# In some cases we require arguments to be run in a certain order, but we don't want some to be run concurrently w/ others.
|
443
|
+
# Sometimes it may be unexpected but helpful to allow that behavior.
|
444
|
+
opt = OptionParser.new do |opt|
|
445
|
+
Pratt.connect! ENV['PRATT_ENV'] || 'development' unless Pratt.connected?
|
446
|
+
|
447
|
+
# Actionable options
|
448
|
+
opt.on('-b', "--begin", String, "Begin project tracking.") do
|
449
|
+
me << :begin
|
450
|
+
end
|
451
|
+
opt.on('-r', "--restart", String, "Restart project log (stop last log and start a new one).
|
452
|
+
Applies to last un-ended project, unless a specific project is provided.") do
|
453
|
+
me << :restart
|
454
|
+
end
|
455
|
+
opt.on('-e', "--end", String, "Stop tracking interval for last project or supplied project if provided.") do
|
456
|
+
me << :end
|
457
|
+
end
|
458
|
+
opt.on('-c', "--change", String, "Change last time interval to this project.") do
|
459
|
+
me << :change
|
460
|
+
end
|
461
|
+
opt.on('-g', "--graph", String, "Display time spent on supplied project or all projects without argument value.") do
|
462
|
+
me << :graph
|
463
|
+
end
|
464
|
+
opt.on('-I', "--invoice", "Create an invoice.") do
|
465
|
+
me << :invoice
|
466
|
+
end
|
467
|
+
opt.on('-p', '--pid', "Process id display. (Is it still running)") do
|
468
|
+
me << :pid
|
469
|
+
end
|
470
|
+
opt.on('-R', '--raw [CONDITIONS]', "Dump logs (semi-)raw") do |conditions|
|
471
|
+
me << :raw
|
472
|
+
me.raw_conditions = conditions
|
473
|
+
end
|
474
|
+
opt.on('-C', "--current", "Show available projects and current project (if there is one)") do
|
475
|
+
me << :current
|
476
|
+
end
|
477
|
+
opt.on("-d", "--daemonize", "Start daemon.") do
|
478
|
+
me << :daemonize
|
479
|
+
end
|
480
|
+
opt.on('-D', '--detect', 'Detect appropriate behavior. (Daemonize or Graphical).') do
|
481
|
+
me << :detect
|
482
|
+
end
|
483
|
+
opt.on('-G', '--gui', 'Show "smart" gui.') do
|
484
|
+
me << :gui
|
485
|
+
end
|
486
|
+
opt.on('-q', "--quit", "Stop daemon.") do
|
487
|
+
me << :quit
|
488
|
+
end
|
489
|
+
opt.on('-U', '--unlock', "Manually unlock a gui that has died but left it's lock around.") do
|
490
|
+
me << :unlock
|
491
|
+
end
|
492
|
+
opt.on '-V', '--version' do
|
493
|
+
puts "Pro-Reactive Time Tracker [Pratt] (#{VERSION})"
|
494
|
+
end
|
495
|
+
opt.on('--destroy', String, "Remove a project.") do
|
496
|
+
me << :destroy
|
497
|
+
end
|
498
|
+
|
499
|
+
# Strictly configuration options
|
500
|
+
opt.on('-P', "--project PROJECT_NAME", String, "Set project.") do |proj|
|
501
|
+
me.project = proj
|
502
|
+
end
|
503
|
+
|
504
|
+
templates = []
|
505
|
+
Pratt.root("views", "*.eruby") {|view| templates << File.basename(view, '.eruby') }
|
506
|
+
opt.on('-t', "--template TEMPLATE", templates, "Template to use for displaying work done.
|
507
|
+
Available templates are #{templates.to_sentence('or')}.") do |template|
|
508
|
+
me.template = template
|
509
|
+
end
|
510
|
+
opt.on '-w', '--when_to TIME', String, 'When to do something.
|
511
|
+
(e.g. log time start|stop, or what time interval to graph)
|
512
|
+
If graphing, silently ignored w/out scale argument.' do |when_to|
|
513
|
+
me.when_to = Chronic.parse(when_to).to_datetime
|
514
|
+
end
|
515
|
+
scales = %w(day week month quarter year)
|
516
|
+
opt.on('-l', '--scale SCALE', scales, "Granularity of time argument
|
517
|
+
Available scales are #{scales.to_sentence('or')}.
|
518
|
+
Only applies to graphing.") do |scale|
|
519
|
+
me.scale = scale
|
520
|
+
end
|
521
|
+
opt.on('-L', '--log', "Redirect errors") do
|
522
|
+
FileUtils.mkdir 'log' unless File.exists? 'log'
|
523
|
+
$stderr.reopen('log/pratt.log', 'a')
|
524
|
+
$stderr.sync = true
|
525
|
+
end
|
526
|
+
opt.on('-N', '--no-color', "Display output without color or special characters.") do
|
527
|
+
Pratt.color = false
|
528
|
+
end
|
529
|
+
opt.on('-A', '--all', "Display all project regardless of other options.") do
|
530
|
+
me.show_all = true
|
531
|
+
end
|
532
|
+
opt.on '-s', '--start-day WEEK_DAY_START', String, "" do |wday_start|
|
533
|
+
me.week_day_start = wday_start
|
534
|
+
end
|
535
|
+
opt.on('-i', "--interval INTERVAL", Float, "Set the remind interval/min (Only applies to daemonized process).") do |interval|
|
536
|
+
me.app.interval = interval
|
537
|
+
me.app.save
|
538
|
+
end
|
539
|
+
<<<<<<< HEAD
|
540
|
+
=======
|
541
|
+
opt.on('-D', '--detect', 'Detect appropriate behavior. (Daemonize or Graphical).') do
|
542
|
+
me << :detect
|
543
|
+
end
|
544
|
+
opt.on('-G', '--gui', 'Show "smart" gui.') do
|
545
|
+
me << :gui
|
546
|
+
end
|
547
|
+
opt.on("-d", "--daemonize", "Start daemon.") do
|
548
|
+
me << :daemonize
|
549
|
+
end
|
550
|
+
opt.on('-q', "--quit", "Stop daemon.") do
|
551
|
+
me << :quit
|
552
|
+
end
|
553
|
+
opt.on('-G', '--gui', 'Show "smart" gui.') do
|
554
|
+
me << :gui
|
555
|
+
end
|
556
|
+
opt.on('-y', '--tray', 'Show tray') do
|
557
|
+
me << :tray_menu
|
558
|
+
end
|
559
|
+
opt.on('--last MODEL', %w(app project whence log), 'Show the last entry for supplied model') do |model|
|
560
|
+
puts send(model.classify, last).inspect
|
561
|
+
end
|
562
|
+
opt.on('-U', '--unlock', "Manually unlock a gui that has died but left it's lock around.") do
|
563
|
+
me.app.unlock
|
564
|
+
# me << :unlock
|
565
|
+
end
|
566
|
+
>>>>>>> 08f31db4a4500d2f6950d31ca6646c6b08ac7432
|
567
|
+
|
568
|
+
opt.parse!
|
569
|
+
end
|
570
|
+
|
571
|
+
me << :env if args.include? 'env'
|
572
|
+
me << :console if args.include? 'console'
|
573
|
+
|
574
|
+
me.run
|
575
|
+
end
|
576
|
+
|
577
|
+
# Calculate totals. I think this should be an instance method on Projects/?/Whences
|
578
|
+
def totals hr, fmt = false
|
579
|
+
"#{fmt_i(hr / 24, 'day', :cyan)} #{fmt_i(hr % 24, 'hour', :yellow)} #{fmt_i((60*(hr -= hr.to_i)), 'min', :green)}"
|
580
|
+
end
|
581
|
+
|
582
|
+
def percent label, off, total, color
|
583
|
+
percent = "%0.2f"% ((off/total)*100)
|
584
|
+
padded_to_max(label) << " #{percent}%".send(color)
|
585
|
+
end
|
586
|
+
|
587
|
+
# Where is Pratt installed.
|
588
|
+
# We've already chdir'd to it's base dir in the bin file
|
589
|
+
def root *globs, &block
|
590
|
+
root = Dir.pwd
|
591
|
+
if globs.empty?
|
592
|
+
subdir = root
|
593
|
+
else
|
594
|
+
subdir = File.join(root, *globs)
|
595
|
+
end
|
596
|
+
|
597
|
+
if block_given?
|
598
|
+
Pathname.glob(subdir) {|dir_files| yield dir_files }
|
599
|
+
else
|
600
|
+
Pathname.glob(subdir)
|
601
|
+
end
|
602
|
+
end
|
603
|
+
|
604
|
+
# Pad the output string to the maximum Project name
|
605
|
+
def padded_to_max string
|
606
|
+
"%#{max}.#{max}s"% string
|
607
|
+
end
|
608
|
+
|
609
|
+
# Migrate schema.
|
610
|
+
def migrate
|
611
|
+
Pratt.root( 'models', '*.rb' ) do |model_file|
|
612
|
+
klass = File.basename( model_file, '.rb' ).capitalize.constantize
|
613
|
+
begin
|
614
|
+
ActiveRecord::Base.connection.table_structure(model_file)
|
615
|
+
rescue ActiveRecord::StatementInvalid
|
616
|
+
klass.migrate :up if klass.superclass == ActiveRecord::Base
|
617
|
+
end
|
618
|
+
end
|
619
|
+
end
|
620
|
+
|
621
|
+
def fmt_i int, label, color
|
622
|
+
"%s #{label}"% [("%02i"% int).send(color), label]
|
623
|
+
end
|
624
|
+
|
625
|
+
end
|
626
|
+
end
|