Pratt 1.5.6
Sign up to get free protection for your applications and to get access to all the features.
- data/.exrc +61 -0
- data/.gitignore +4 -0
- data/History.txt +6 -0
- data/Manifest.txt +46 -0
- data/Pratt.gemspec +351 -0
- data/README.txt +66 -0
- data/Rakefile +85 -0
- data/TODO +54 -0
- data/VERSION +1 -0
- data/bin/pratt.rb +13 -0
- data/config.rb +34 -0
- data/lib/pratt.rb +527 -0
- data/lib/pratt/array.rb +11 -0
- data/lib/pratt/string.rb +18 -0
- data/models/app.rb +40 -0
- data/models/customer.rb +24 -0
- data/models/payment.rb +22 -0
- data/models/pratt.rb +19 -0
- data/models/project.rb +82 -0
- data/models/whence.rb +70 -0
- data/pkgs/tile-0.8.2.tar.gz +0 -0
- data/pkgs/tile-0.8.2/ANNOUNCE.txt +95 -0
- data/pkgs/tile-0.8.2/ChangeLog +4651 -0
- data/pkgs/tile-0.8.2/Makefile +250 -0
- data/pkgs/tile-0.8.2/Makefile.in +250 -0
- data/pkgs/tile-0.8.2/README.txt +86 -0
- data/pkgs/tile-0.8.2/aclocal.m4 +2 -0
- data/pkgs/tile-0.8.2/altTheme.o +0 -0
- data/pkgs/tile-0.8.2/blink.o +0 -0
- data/pkgs/tile-0.8.2/button.o +0 -0
- data/pkgs/tile-0.8.2/cache.o +0 -0
- data/pkgs/tile-0.8.2/clamTheme.o +0 -0
- data/pkgs/tile-0.8.2/classicTheme.o +0 -0
- data/pkgs/tile-0.8.2/config.log +1330 -0
- data/pkgs/tile-0.8.2/config.status +795 -0
- data/pkgs/tile-0.8.2/configure +15248 -0
- data/pkgs/tile-0.8.2/configure.in +89 -0
- data/pkgs/tile-0.8.2/demos/autocomplete.tcl +59 -0
- data/pkgs/tile-0.8.2/demos/demo.tcl +870 -0
- data/pkgs/tile-0.8.2/demos/dirbrowser.tcl +167 -0
- data/pkgs/tile-0.8.2/demos/dlgtest.tcl +97 -0
- data/pkgs/tile-0.8.2/demos/iconlib.tcl +110 -0
- data/pkgs/tile-0.8.2/demos/repeater.tcl +117 -0
- data/pkgs/tile-0.8.2/demos/toolbutton.tcl +101 -0
- data/pkgs/tile-0.8.2/doc/Geometry.3 +230 -0
- data/pkgs/tile-0.8.2/doc/INDEX.MAP +153 -0
- data/pkgs/tile-0.8.2/doc/Makefile +36 -0
- data/pkgs/tile-0.8.2/doc/TILE.XML +45 -0
- data/pkgs/tile-0.8.2/doc/Theme.3 +34 -0
- data/pkgs/tile-0.8.2/doc/button.n +75 -0
- data/pkgs/tile-0.8.2/doc/checkbutton.n +61 -0
- data/pkgs/tile-0.8.2/doc/combobox.n +98 -0
- data/pkgs/tile-0.8.2/doc/converting.txt +97 -0
- data/pkgs/tile-0.8.2/doc/dialog.n +122 -0
- data/pkgs/tile-0.8.2/doc/entry.n +438 -0
- data/pkgs/tile-0.8.2/doc/frame.n +43 -0
- data/pkgs/tile-0.8.2/doc/html/Geometry.html +304 -0
- data/pkgs/tile-0.8.2/doc/html/Theme.html +48 -0
- data/pkgs/tile-0.8.2/doc/html/button.html +120 -0
- data/pkgs/tile-0.8.2/doc/html/category-index.html +18 -0
- data/pkgs/tile-0.8.2/doc/html/checkbutton.html +94 -0
- data/pkgs/tile-0.8.2/doc/html/combobox.html +164 -0
- data/pkgs/tile-0.8.2/doc/html/converting.txt +97 -0
- data/pkgs/tile-0.8.2/doc/html/dialog.html +159 -0
- data/pkgs/tile-0.8.2/doc/html/entry.html +613 -0
- data/pkgs/tile-0.8.2/doc/html/frame.html +76 -0
- data/pkgs/tile-0.8.2/doc/html/image.html +100 -0
- data/pkgs/tile-0.8.2/doc/html/index.html +25 -0
- data/pkgs/tile-0.8.2/doc/html/keyword-index.html +228 -0
- data/pkgs/tile-0.8.2/doc/html/label.html +133 -0
- data/pkgs/tile-0.8.2/doc/html/labelframe.html +91 -0
- data/pkgs/tile-0.8.2/doc/html/manpage.css +212 -0
- data/pkgs/tile-0.8.2/doc/html/menubutton.html +63 -0
- data/pkgs/tile-0.8.2/doc/html/notebook.html +280 -0
- data/pkgs/tile-0.8.2/doc/html/paned.html +149 -0
- data/pkgs/tile-0.8.2/doc/html/progressbar.html +138 -0
- data/pkgs/tile-0.8.2/doc/html/radiobutton.html +89 -0
- data/pkgs/tile-0.8.2/doc/html/scrollbar.html +221 -0
- data/pkgs/tile-0.8.2/doc/html/separator.html +48 -0
- data/pkgs/tile-0.8.2/doc/html/sizegrip.html +62 -0
- data/pkgs/tile-0.8.2/doc/html/style.html +172 -0
- data/pkgs/tile-0.8.2/doc/html/tile-intro.html +164 -0
- data/pkgs/tile-0.8.2/doc/html/treeview.html +634 -0
- data/pkgs/tile-0.8.2/doc/html/widget.html +342 -0
- data/pkgs/tile-0.8.2/doc/image.n +81 -0
- data/pkgs/tile-0.8.2/doc/internals.txt +409 -0
- data/pkgs/tile-0.8.2/doc/label.n +75 -0
- data/pkgs/tile-0.8.2/doc/labelframe.n +64 -0
- data/pkgs/tile-0.8.2/doc/man.macros +239 -0
- data/pkgs/tile-0.8.2/doc/menubutton.n +41 -0
- data/pkgs/tile-0.8.2/doc/notebook.n +188 -0
- data/pkgs/tile-0.8.2/doc/paned.n +95 -0
- data/pkgs/tile-0.8.2/doc/progressbar.n +79 -0
- data/pkgs/tile-0.8.2/doc/radiobutton.n +57 -0
- data/pkgs/tile-0.8.2/doc/scrollbar.n +160 -0
- data/pkgs/tile-0.8.2/doc/separator.n +30 -0
- data/pkgs/tile-0.8.2/doc/sizegrip.n +53 -0
- data/pkgs/tile-0.8.2/doc/style.n +119 -0
- data/pkgs/tile-0.8.2/doc/tile-intro.n +165 -0
- data/pkgs/tile-0.8.2/doc/tmml.options +4 -0
- data/pkgs/tile-0.8.2/doc/treeview.n +415 -0
- data/pkgs/tile-0.8.2/doc/widget.n +227 -0
- data/pkgs/tile-0.8.2/doc/xml/Geometry.tmml +379 -0
- data/pkgs/tile-0.8.2/doc/xml/INDEX.MAP +153 -0
- data/pkgs/tile-0.8.2/doc/xml/Theme.tmml +63 -0
- data/pkgs/tile-0.8.2/doc/xml/button.tmml +134 -0
- data/pkgs/tile-0.8.2/doc/xml/checkbutton.tmml +119 -0
- data/pkgs/tile-0.8.2/doc/xml/combobox.tmml +184 -0
- data/pkgs/tile-0.8.2/doc/xml/dialog.tmml +195 -0
- data/pkgs/tile-0.8.2/doc/xml/entry.tmml +630 -0
- data/pkgs/tile-0.8.2/doc/xml/frame.tmml +98 -0
- data/pkgs/tile-0.8.2/doc/xml/image.tmml +101 -0
- data/pkgs/tile-0.8.2/doc/xml/label.tmml +154 -0
- data/pkgs/tile-0.8.2/doc/xml/labelframe.tmml +116 -0
- data/pkgs/tile-0.8.2/doc/xml/menubutton.tmml +80 -0
- data/pkgs/tile-0.8.2/doc/xml/notebook.tmml +306 -0
- data/pkgs/tile-0.8.2/doc/xml/paned.tmml +154 -0
- data/pkgs/tile-0.8.2/doc/xml/progressbar.tmml +151 -0
- data/pkgs/tile-0.8.2/doc/xml/radiobutton.tmml +109 -0
- data/pkgs/tile-0.8.2/doc/xml/scrollbar.tmml +233 -0
- data/pkgs/tile-0.8.2/doc/xml/separator.tmml +59 -0
- data/pkgs/tile-0.8.2/doc/xml/sizegrip.tmml +82 -0
- data/pkgs/tile-0.8.2/doc/xml/style.tmml +171 -0
- data/pkgs/tile-0.8.2/doc/xml/tile-intro.tmml +192 -0
- data/pkgs/tile-0.8.2/doc/xml/treeview.tmml +604 -0
- data/pkgs/tile-0.8.2/doc/xml/widget.tmml +372 -0
- data/pkgs/tile-0.8.2/entry.o +0 -0
- data/pkgs/tile-0.8.2/frame.o +0 -0
- data/pkgs/tile-0.8.2/generic/Makefile.in +221 -0
- data/pkgs/tile-0.8.2/generic/TODO +493 -0
- data/pkgs/tile-0.8.2/generic/altTheme.c +1172 -0
- data/pkgs/tile-0.8.2/generic/blink.c +168 -0
- data/pkgs/tile-0.8.2/generic/button.c +858 -0
- data/pkgs/tile-0.8.2/generic/cache.c +354 -0
- data/pkgs/tile-0.8.2/generic/clamTheme.c +974 -0
- data/pkgs/tile-0.8.2/generic/classicTheme.c +518 -0
- data/pkgs/tile-0.8.2/generic/configure +10334 -0
- data/pkgs/tile-0.8.2/generic/configure.in +100 -0
- data/pkgs/tile-0.8.2/generic/entry.c +1922 -0
- data/pkgs/tile-0.8.2/generic/frame.c +648 -0
- data/pkgs/tile-0.8.2/generic/gunk.h +44 -0
- data/pkgs/tile-0.8.2/generic/image.c +416 -0
- data/pkgs/tile-0.8.2/generic/label.c +663 -0
- data/pkgs/tile-0.8.2/generic/layout.c +1215 -0
- data/pkgs/tile-0.8.2/generic/manager.c +554 -0
- data/pkgs/tile-0.8.2/generic/manager.h +91 -0
- data/pkgs/tile-0.8.2/generic/notebook.c +1380 -0
- data/pkgs/tile-0.8.2/generic/paned.c +958 -0
- data/pkgs/tile-0.8.2/generic/pkgIndex.tcl.in +7 -0
- data/pkgs/tile-0.8.2/generic/progress.c +549 -0
- data/pkgs/tile-0.8.2/generic/scale.c +526 -0
- data/pkgs/tile-0.8.2/generic/scroll.c +253 -0
- data/pkgs/tile-0.8.2/generic/scrollbar.c +346 -0
- data/pkgs/tile-0.8.2/generic/separator.c +132 -0
- data/pkgs/tile-0.8.2/generic/square.c +306 -0
- data/pkgs/tile-0.8.2/generic/tagset.c +147 -0
- data/pkgs/tile-0.8.2/generic/tile.c +296 -0
- data/pkgs/tile-0.8.2/generic/tkElements.c +1280 -0
- data/pkgs/tile-0.8.2/generic/tkTheme.c +1708 -0
- data/pkgs/tile-0.8.2/generic/tkTheme.h +419 -0
- data/pkgs/tile-0.8.2/generic/tkThemeInt.h +45 -0
- data/pkgs/tile-0.8.2/generic/tkstate.c +268 -0
- data/pkgs/tile-0.8.2/generic/trace.c +145 -0
- data/pkgs/tile-0.8.2/generic/track.c +174 -0
- data/pkgs/tile-0.8.2/generic/treeview.c +3211 -0
- data/pkgs/tile-0.8.2/generic/ttk.decls +154 -0
- data/pkgs/tile-0.8.2/generic/ttkDecls.h +340 -0
- data/pkgs/tile-0.8.2/generic/ttkStubInit.c +61 -0
- data/pkgs/tile-0.8.2/generic/ttkStubLib.c +70 -0
- data/pkgs/tile-0.8.2/generic/widget.c +785 -0
- data/pkgs/tile-0.8.2/generic/widget.h +263 -0
- data/pkgs/tile-0.8.2/image.o +0 -0
- data/pkgs/tile-0.8.2/label.o +0 -0
- data/pkgs/tile-0.8.2/layout.o +0 -0
- data/pkgs/tile-0.8.2/library/altTheme.tcl +101 -0
- data/pkgs/tile-0.8.2/library/aquaTheme.tcl +62 -0
- data/pkgs/tile-0.8.2/library/button.tcl +85 -0
- data/pkgs/tile-0.8.2/library/clamTheme.tcl +139 -0
- data/pkgs/tile-0.8.2/library/classicTheme.tcl +108 -0
- data/pkgs/tile-0.8.2/library/combobox.tcl +439 -0
- data/pkgs/tile-0.8.2/library/cursors.tcl +36 -0
- data/pkgs/tile-0.8.2/library/defaults.tcl +118 -0
- data/pkgs/tile-0.8.2/library/dialog.tcl +274 -0
- data/pkgs/tile-0.8.2/library/entry.tcl +580 -0
- data/pkgs/tile-0.8.2/library/fonts.tcl +153 -0
- data/pkgs/tile-0.8.2/library/icons.tcl +105 -0
- data/pkgs/tile-0.8.2/library/keynav.tcl +192 -0
- data/pkgs/tile-0.8.2/library/menubutton.tcl +171 -0
- data/pkgs/tile-0.8.2/library/notebook.tcl +193 -0
- data/pkgs/tile-0.8.2/library/paned.tcl +87 -0
- data/pkgs/tile-0.8.2/library/progress.tcl +51 -0
- data/pkgs/tile-0.8.2/library/scale.tcl +54 -0
- data/pkgs/tile-0.8.2/library/scrollbar.tcl +125 -0
- data/pkgs/tile-0.8.2/library/sizegrip.tcl +77 -0
- data/pkgs/tile-0.8.2/library/tile.tcl +211 -0
- data/pkgs/tile-0.8.2/library/treeview.tcl +382 -0
- data/pkgs/tile-0.8.2/library/utils.tcl +254 -0
- data/pkgs/tile-0.8.2/library/winTheme.tcl +77 -0
- data/pkgs/tile-0.8.2/library/xpTheme.tcl +63 -0
- data/pkgs/tile-0.8.2/libtile0.8.2.so +0 -0
- data/pkgs/tile-0.8.2/libttkstub.a +0 -0
- data/pkgs/tile-0.8.2/license.terms +24 -0
- data/pkgs/tile-0.8.2/macosx/aquaTheme.c +1076 -0
- data/pkgs/tile-0.8.2/manager.o +0 -0
- data/pkgs/tile-0.8.2/notebook.o +0 -0
- data/pkgs/tile-0.8.2/paned.o +0 -0
- data/pkgs/tile-0.8.2/pkgIndex.tcl +3 -0
- data/pkgs/tile-0.8.2/progress.o +0 -0
- data/pkgs/tile-0.8.2/scale.o +0 -0
- data/pkgs/tile-0.8.2/scroll.o +0 -0
- data/pkgs/tile-0.8.2/scrollbar.o +0 -0
- data/pkgs/tile-0.8.2/separator.o +0 -0
- data/pkgs/tile-0.8.2/tagset.o +0 -0
- data/pkgs/tile-0.8.2/tclconfig/install-sh +119 -0
- data/pkgs/tile-0.8.2/tclconfig/tcl.m4 +4069 -0
- data/pkgs/tile-0.8.2/tclconfig/teax.m4 +109 -0
- data/pkgs/tile-0.8.2/tests/all.tcl +18 -0
- data/pkgs/tile-0.8.2/tests/bwidget.test +103 -0
- data/pkgs/tile-0.8.2/tests/cbtest.tcl +125 -0
- data/pkgs/tile-0.8.2/tests/combobox.test +51 -0
- data/pkgs/tile-0.8.2/tests/compound.tcl +92 -0
- data/pkgs/tile-0.8.2/tests/entry.test +285 -0
- data/pkgs/tile-0.8.2/tests/entrytest.tcl +78 -0
- data/pkgs/tile-0.8.2/tests/image.test +94 -0
- data/pkgs/tile-0.8.2/tests/labelframe.tcl +41 -0
- data/pkgs/tile-0.8.2/tests/labelframe.test +137 -0
- data/pkgs/tile-0.8.2/tests/layout.test +33 -0
- data/pkgs/tile-0.8.2/tests/misc.test +35 -0
- data/pkgs/tile-0.8.2/tests/nbtest.tcl +66 -0
- data/pkgs/tile-0.8.2/tests/notebook.test +500 -0
- data/pkgs/tile-0.8.2/tests/paned.test +298 -0
- data/pkgs/tile-0.8.2/tests/progress.test +92 -0
- data/pkgs/tile-0.8.2/tests/pwtest.tcl +90 -0
- data/pkgs/tile-0.8.2/tests/sbtest.tcl +79 -0
- data/pkgs/tile-0.8.2/tests/scrollbar.test +77 -0
- data/pkgs/tile-0.8.2/tests/sgtest.tcl +52 -0
- data/pkgs/tile-0.8.2/tests/testutils.tcl +20 -0
- data/pkgs/tile-0.8.2/tests/tile.test +674 -0
- data/pkgs/tile-0.8.2/tests/treetags.test +78 -0
- data/pkgs/tile-0.8.2/tests/treeview.test +563 -0
- data/pkgs/tile-0.8.2/tests/tvtest.tcl +332 -0
- data/pkgs/tile-0.8.2/tests/validate.test +278 -0
- data/pkgs/tile-0.8.2/tile.o +0 -0
- data/pkgs/tile-0.8.2/tkElements.o +0 -0
- data/pkgs/tile-0.8.2/tkTheme.o +0 -0
- data/pkgs/tile-0.8.2/tkstate.o +0 -0
- data/pkgs/tile-0.8.2/tools/genStubs.tcl +861 -0
- data/pkgs/tile-0.8.2/trace.o +0 -0
- data/pkgs/tile-0.8.2/track.o +0 -0
- data/pkgs/tile-0.8.2/treeview.o +0 -0
- data/pkgs/tile-0.8.2/ttkStubInit.o +0 -0
- data/pkgs/tile-0.8.2/ttkStubLib.o +0 -0
- data/pkgs/tile-0.8.2/widget.o +0 -0
- data/pkgs/tile-0.8.2/win/Tile.dsp +261 -0
- data/pkgs/tile-0.8.2/win/makefile.vc +527 -0
- data/pkgs/tile-0.8.2/win/monitor.c +164 -0
- data/pkgs/tile-0.8.2/win/nmakehlp.c +483 -0
- data/pkgs/tile-0.8.2/win/rules.vc +512 -0
- data/pkgs/tile-0.8.2/win/tile.rc +40 -0
- data/pkgs/tile-0.8.2/win/winTheme.c +734 -0
- data/pkgs/tile-0.8.2/win/xpTheme.c +1029 -0
- data/spec/app_spec.rb +48 -0
- data/spec/customer_spec.rb +31 -0
- data/spec/fixtures/graph.expectation +18 -0
- data/spec/payment_spec.rb +19 -0
- data/spec/pratt_spec.rb +148 -0
- data/spec/project_spec.rb +163 -0
- data/spec/rcov.opts +0 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/whence_spec.rb +54 -0
- data/tasks/pratt.rb +84 -0
- data/templates/model.eruby +12 -0
- data/templates/spec.eruby +8 -0
- data/views/env.rb +22 -0
- data/views/graph.eruby +20 -0
- data/views/invoice.eruby +148 -0
- data/views/main.rb +92 -0
- data/views/pid.eruby +3 -0
- data/views/pop.rb +94 -0
- data/views/pop2.rb +75 -0
- data/views/raw.eruby +11 -0
- metadata +390 -0
data/README.txt
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
= pratt
|
2
|
+
|
3
|
+
* Pratt (url)
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Pratt (describe your package)
|
8
|
+
(Pro/Re)-Active Time Tracker
|
9
|
+
|
10
|
+
== FEATURES/PROBLEMS:
|
11
|
+
|
12
|
+
* (list of features or problems)
|
13
|
+
Currently you'll need to manually fix the project data since I haven't
|
14
|
+
fully integrated the support for Projects to have customer's and my current
|
15
|
+
implementation is shoddy.
|
16
|
+
|
17
|
+
== SYNOPSIS:
|
18
|
+
|
19
|
+
* pratt (code sample of usage)
|
20
|
+
|
21
|
+
** PRATT_ENV=production pratt -D # daemonize (or if already daemonized init the
|
22
|
+
gui)
|
23
|
+
** pratt -h (as you'd expect lists available commands)
|
24
|
+
|
25
|
+
== REQUIREMENTS:
|
26
|
+
|
27
|
+
* pratt
|
28
|
+
debian: libtcltk-ruby tk-tile libsqlite3-dev
|
29
|
+
gems: activerecord sqlite3-ruby rspec mocha
|
30
|
+
tk: http://sourceforge.net/project/showfiles.php?group_id=11464&package_id=107795&release_id=562098
|
31
|
+
|
32
|
+
== INSTALL:
|
33
|
+
|
34
|
+
* sudo gem install
|
35
|
+
|
36
|
+
Credits
|
37
|
+
|
38
|
+
Michael Goff
|
39
|
+
Thanks for helping me with some of the workflow details
|
40
|
+
Michael and Kent Krenrich
|
41
|
+
Thanks for the UI input, suggestions, and testing.
|
42
|
+
|
43
|
+
== LICENSE:
|
44
|
+
|
45
|
+
(The MIT License)
|
46
|
+
|
47
|
+
Copyright (c) 2009 QualitySmith LLC
|
48
|
+
|
49
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
50
|
+
a copy of this software and associated documentation files (the
|
51
|
+
'Software'), to deal in the Software without restriction, including
|
52
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
53
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
54
|
+
permit persons to whom the Software is furnished to do so, subject to
|
55
|
+
the following conditions:
|
56
|
+
|
57
|
+
The above copyright notice and this permission notice shall be
|
58
|
+
included in all copies or substantial portions of the Software.
|
59
|
+
|
60
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
61
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
62
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
63
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
64
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
65
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
66
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rake'
|
5
|
+
require 'lib/pratt'
|
6
|
+
require 'tasks/pratt'
|
7
|
+
|
8
|
+
task :default => :spec
|
9
|
+
|
10
|
+
begin
|
11
|
+
require 'jeweler'
|
12
|
+
Jeweler::Tasks.new do |gem|
|
13
|
+
gem.name = Pratt::NAME
|
14
|
+
gem.summary = Pratt::SUMMARY
|
15
|
+
gem.description = Pratt::DESCRIPTION
|
16
|
+
gem.email = "frogstarr78@gmail.com"
|
17
|
+
gem.homepage = "http://github.com/frogstarr78/pratt"
|
18
|
+
gem.authors = Pratt::AUTHORS
|
19
|
+
gem.add_development_dependency 'rspec', '>=1.2.6'
|
20
|
+
gem.add_development_dependency 'mocha', '>=0.9.5'
|
21
|
+
gem.add_runtime_dependency 'activerecord', '>=2.1.1'
|
22
|
+
gem.add_runtime_dependency 'sqlite3-ruby', '>=1.2.4'
|
23
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
24
|
+
end
|
25
|
+
Jeweler::GemcutterTasks.new
|
26
|
+
rescue LoadError
|
27
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
28
|
+
end
|
29
|
+
|
30
|
+
require 'spec/rake/spectask'
|
31
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
32
|
+
spec.libs << 'lib' << 'spec'
|
33
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
34
|
+
end
|
35
|
+
|
36
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
37
|
+
spec.libs << 'lib' << 'spec'
|
38
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
39
|
+
spec.rcov = true
|
40
|
+
end
|
41
|
+
|
42
|
+
task :spec => :check_dependencies
|
43
|
+
|
44
|
+
begin
|
45
|
+
require 'reek/rake_task'
|
46
|
+
Reek::RakeTask.new do |t|
|
47
|
+
t.fail_on_error = true
|
48
|
+
t.verbose = false
|
49
|
+
t.source_files = 'lib/**/*.rb'
|
50
|
+
end
|
51
|
+
rescue LoadError
|
52
|
+
task :reek do
|
53
|
+
abort "Reek is not available. In order to run reek, you must: sudo gem install reek"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
begin
|
58
|
+
require 'roodi'
|
59
|
+
require 'roodi_task'
|
60
|
+
RoodiTask.new do |t|
|
61
|
+
t.verbose = false
|
62
|
+
end
|
63
|
+
rescue LoadError
|
64
|
+
task :roodi do
|
65
|
+
abort "Roodi is not available. In order to run roodi, you must: sudo gem install roodi"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
task :default => :spec
|
70
|
+
|
71
|
+
require 'rake/rdoctask'
|
72
|
+
Rake::RDocTask.new do |rdoc|
|
73
|
+
if File.exist?('VERSION')
|
74
|
+
version = File.read('VERSION')
|
75
|
+
else
|
76
|
+
version = ""
|
77
|
+
end
|
78
|
+
|
79
|
+
rdoc.rdoc_dir = 'rdoc'
|
80
|
+
rdoc.title = "pratt #{version}"
|
81
|
+
rdoc.rdoc_files.include('README*')
|
82
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
83
|
+
end
|
84
|
+
|
85
|
+
# vim: syntax=Ruby
|
data/TODO
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
General:
|
2
|
+
[ ] Add environment ability
|
3
|
+
[ ] Change to use raw command line arguments / Remove optparse lib
|
4
|
+
[ ] Update general package setup (It's messy)
|
5
|
+
[?] Add file watching like merb
|
6
|
+
[?] Move to Observer Pattern for reporting functionality
|
7
|
+
|
8
|
+
Invoicing:
|
9
|
+
[ ] Add shifty week plugin
|
10
|
+
[ ] Add support for variable rates.
|
11
|
+
|
12
|
+
Templating:
|
13
|
+
[ ] Invoices
|
14
|
+
[ ] Move other output to template based
|
15
|
+
|
16
|
+
Data:
|
17
|
+
[ ] Change to Use DataMapper
|
18
|
+
[?] Put everything in the db? (logs, app locks, etc)
|
19
|
+
|
20
|
+
GUI:
|
21
|
+
[ ] Add TrayIcon.
|
22
|
+
[ ] Customer CRUD
|
23
|
+
[ ] Payment CRUD
|
24
|
+
[ ] Re-design prompts
|
25
|
+
[ ] Remove fork in favor of ( eval OR (!?!) GUI classes )
|
26
|
+
|
27
|
+
Test:
|
28
|
+
[ ] Add more I'm not testing everything
|
29
|
+
[ ] Flog
|
30
|
+
[ ] Flay
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
Done:
|
35
|
+
[x] Whence.last should be last unended.
|
36
|
+
[x] Add date range argument to timed report.
|
37
|
+
[x] Add validation for start/end at.
|
38
|
+
[x] Manual data manipulation date/time.
|
39
|
+
[x] Change pop prompt to show helpful time.
|
40
|
+
[x] Change Start/Continue interface.
|
41
|
+
[x] Add directoryless agnostic runner.
|
42
|
+
[x] Log output.
|
43
|
+
[x] Add Quiet option??
|
44
|
+
[x] Allow only one prompt at a time.
|
45
|
+
[x] Handle stop when there isn't a running project w/out errors
|
46
|
+
[x] fix gui so if last_unended is detected app knows which gui to display
|
47
|
+
[x] Intelligently detect correctly running stored pid.
|
48
|
+
[x] Fix pid directory access.
|
49
|
+
|
50
|
+
[x] Add support for Customer's model
|
51
|
+
[x] Add Template command
|
52
|
+
[x] Add Invoice command
|
53
|
+
[x] Fix colorability
|
54
|
+
[x] Add console command
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.5.6
|
data/bin/pratt.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
bin_file = __FILE__
|
5
|
+
bin_file = File.symlink?(bin_file) ? File.readlink(bin_file) : bin_file
|
6
|
+
|
7
|
+
bin_path = File.expand_path( '..', bin_file )
|
8
|
+
pratt_dir = File.dirname( bin_path )
|
9
|
+
FileUtils.chdir pratt_dir
|
10
|
+
|
11
|
+
require "lib/pratt"
|
12
|
+
|
13
|
+
Pratt.parse(ARGV)
|
data/config.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
PRATT_ENV = ENV["PRATT_ENV"] || 'development' unless Object.const_defined? :PRATT_ENV
|
3
|
+
|
4
|
+
class Pratt
|
5
|
+
@@connected_to = nil
|
6
|
+
module Test
|
7
|
+
end
|
8
|
+
|
9
|
+
include FileUtils
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def connected_to?
|
13
|
+
@@connected_to
|
14
|
+
end
|
15
|
+
|
16
|
+
def connect! to_env = :development
|
17
|
+
puts "Connecting to: #{to_env}"
|
18
|
+
@@connected_to = "#{to_env}.sqlite3"
|
19
|
+
ActiveRecord::Base.establish_connection( :adapter => 'sqlite3', :database => connected_to? )
|
20
|
+
unless File.exists? connected_to?
|
21
|
+
Pratt.migrate
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def connected?
|
26
|
+
ActiveRecord::Base.connected?
|
27
|
+
end
|
28
|
+
|
29
|
+
def config= env
|
30
|
+
include Pratt::Test
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
data/lib/pratt.rb
ADDED
@@ -0,0 +1,527 @@
|
|
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
|
+
|
15
|
+
require 'lib/pratt/array'
|
16
|
+
require 'lib/pratt/string'
|
17
|
+
|
18
|
+
require 'models/app'
|
19
|
+
require 'models/customer'
|
20
|
+
require 'models/whence'
|
21
|
+
require 'models/project'
|
22
|
+
require 'models/payment'
|
23
|
+
|
24
|
+
class Pratt
|
25
|
+
|
26
|
+
NAME = 'Pratt'
|
27
|
+
URL = 'http://www.frogstarr78.com/projects/pratt'
|
28
|
+
AUTHORS = ['Scott Noel-Hemming']
|
29
|
+
SUMMARY = "Pro/Re-Active Time Tracker. Track time based on what you expect to be working on, with frequent prompts to ensure accuracy."
|
30
|
+
DESCRIPTION = %q|
|
31
|
+
Need a way to keep track of your time, but get caught up in work? Or constant interruptions?
|
32
|
+
Yeah you know who I'm talking about. Those people from the [abc] department always "NEEDING xyz FEATURE NOW!!!".
|
33
|
+
Seriously though. I'm usually just loose track of time. I wanted an app that I could start with a task I think
|
34
|
+
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,
|
35
|
+
provides an easy way of changing to another task, or if I have changed tasks and not already updated the app, would
|
36
|
+
provide an easy way of changing the task of the previously recorded interval. That's what this is intended to do.
|
37
|
+
|
38
|
+
Time Tracking.
|
39
|
+
Proactively set what you expect to work on.
|
40
|
+
Reactively modify what you are no longer working on.
|
41
|
+
|
|
42
|
+
DEPENDENCIES = ["activerecord >=2.1.1", "sqlite3-ruby >=1.2.4", "rspec >=1.2.6", "mocha >=0.9.5"]
|
43
|
+
VERSION = File.open( File.join(Dir.pwd, 'VERSION') ).read.strip
|
44
|
+
|
45
|
+
PID_FILE='pratt.pid'
|
46
|
+
FMT = "%a %X %b %d %Y"
|
47
|
+
INVOICE_FMT = "%x"
|
48
|
+
@@color = true
|
49
|
+
|
50
|
+
attr_accessor :when_to, :scale, :color, :show_all, :env, :raw_conditions, :template
|
51
|
+
attr_reader :project, :todo
|
52
|
+
def initialize proj = nil #, when_to
|
53
|
+
@when_to = Time.now
|
54
|
+
@week = false
|
55
|
+
@day = false
|
56
|
+
@todo = []
|
57
|
+
@scale = nil
|
58
|
+
@show_all = false
|
59
|
+
@template = nil
|
60
|
+
@env = :development
|
61
|
+
@raw_conditions = ''
|
62
|
+
self.project = proj unless proj.nil?
|
63
|
+
end
|
64
|
+
|
65
|
+
def project= proj
|
66
|
+
if proj.is_a?(Project)
|
67
|
+
@project = proj
|
68
|
+
else
|
69
|
+
@project = Project.find_or_create_by_name( { :name => proj } )
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def << what
|
74
|
+
@todo << what
|
75
|
+
end
|
76
|
+
|
77
|
+
def app
|
78
|
+
@app ||= App.last
|
79
|
+
@app
|
80
|
+
end
|
81
|
+
|
82
|
+
def graph
|
83
|
+
@primary = @off_total = @rest_total = 0.0
|
84
|
+
self.template = 'graph'
|
85
|
+
|
86
|
+
if project?
|
87
|
+
@projects = [project]
|
88
|
+
|
89
|
+
@primary = project.time_spent(scale, when_to)
|
90
|
+
@scaled_total = project.whences.time_spent(scale, when_to)
|
91
|
+
else
|
92
|
+
@projects = Project.all
|
93
|
+
|
94
|
+
@projects.each do |proj|
|
95
|
+
@primary = proj.time_spent(scale, when_to) if proj.name == Project.primary.name
|
96
|
+
@off_total = proj.time_spent(scale, when_to) if proj.name == Project.off.name
|
97
|
+
@rest_total += proj.time_spent(scale, when_to) if Project.rest.collect(&:name).include?(proj.name)
|
98
|
+
end
|
99
|
+
@scaled_total = Whence.time_spent(scale, when_to)-@off_total
|
100
|
+
end
|
101
|
+
|
102
|
+
if @primary + @off_total + @rest_total > 0.0
|
103
|
+
process_template!
|
104
|
+
else
|
105
|
+
"No data to report"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def invoice
|
110
|
+
self.template = 'invoice'
|
111
|
+
|
112
|
+
if project?
|
113
|
+
@projects = [project]
|
114
|
+
|
115
|
+
@total = project.time_spent(scale, when_to)
|
116
|
+
else
|
117
|
+
@projects = (Project.all - [Project.primary, Project.off])
|
118
|
+
@projects.select! {|proj| show_all or ( !show_all and proj.time_spent(scale, when_to) != 0.0 ) }
|
119
|
+
|
120
|
+
@total = @projects.inject 0.0 do |total, proj|
|
121
|
+
total += proj.time_spent(scale, when_to)
|
122
|
+
total
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
@projects.each do |project|
|
127
|
+
puts "#{project.name} #{project.payment.inspect}"
|
128
|
+
end
|
129
|
+
if @total > 0.0
|
130
|
+
puts process_template!
|
131
|
+
else
|
132
|
+
puts "No data to report"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def console options = []
|
137
|
+
options << %w(-r\ irb/completion -r\ lib/pratt --simple-prompt)
|
138
|
+
exec "irb #{options.join ' '}"
|
139
|
+
end
|
140
|
+
|
141
|
+
def current
|
142
|
+
project_names = ([Project.primary, Project.off] | Project.rest).collect(&:name)
|
143
|
+
|
144
|
+
if last_whence = Whence.last_unended || Whence.last
|
145
|
+
puts " projects: " << (
|
146
|
+
project_names.collect {|project_name| "'#{project_name.send(last_whence.end_at.nil? && last_whence.project.name == project_name ? :green : :magenta)}'" }
|
147
|
+
) * ' '
|
148
|
+
if last_whence.end_at.nil?
|
149
|
+
puts " started: #{last_whence.start_at.strftime(FMT).send(:blue)}"
|
150
|
+
time_til = ( app.interval - ( Time.now - last_whence.start_at ) )
|
151
|
+
puts "next prompt: %s %s"% [Pratt.send( :fmt_i, time_til / 60.0, 'min', :yellow ), Pratt.send( :fmt_i, time_til % 60, 'sec', :yellow ), ], ''
|
152
|
+
end
|
153
|
+
else
|
154
|
+
puts " projects: " << (
|
155
|
+
project_names.collect do |project_name|
|
156
|
+
"'#{project_name.magenta}'"
|
157
|
+
end
|
158
|
+
) * ' '
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def begin
|
163
|
+
self.project.start! when_to
|
164
|
+
end
|
165
|
+
def restart
|
166
|
+
if project?
|
167
|
+
project.restart! when_to
|
168
|
+
else
|
169
|
+
Whence.last_unended.project.restart! when_to
|
170
|
+
end
|
171
|
+
end
|
172
|
+
def end
|
173
|
+
if project?
|
174
|
+
project.stop! when_to
|
175
|
+
else
|
176
|
+
Whence.last_unended.stop! when_to
|
177
|
+
end
|
178
|
+
end
|
179
|
+
def change
|
180
|
+
Whence.last.change! project.name
|
181
|
+
end
|
182
|
+
def destroy
|
183
|
+
project.destroy
|
184
|
+
end
|
185
|
+
|
186
|
+
def cpid
|
187
|
+
`pgrep -fl 'bin/pratt'`.chomp.split(' ').first
|
188
|
+
end
|
189
|
+
|
190
|
+
def pid
|
191
|
+
self.template = 'pid'
|
192
|
+
puts process_template!
|
193
|
+
end
|
194
|
+
|
195
|
+
def raw
|
196
|
+
self.template = 'raw'
|
197
|
+
|
198
|
+
if project = Project.find_by_name( raw_conditions )
|
199
|
+
@whences = project.whences.all
|
200
|
+
else
|
201
|
+
case raw_conditions
|
202
|
+
when 'all'
|
203
|
+
@whences = Whence.find raw_conditions.to_sym
|
204
|
+
when /^last$/, 'first'
|
205
|
+
@whences = [Whence.find raw_conditions.to_sym]
|
206
|
+
when /last[\(\s]?(\d+)[\)\s]?/
|
207
|
+
@whences = Whence.all.last($1.to_i)
|
208
|
+
else
|
209
|
+
@whences = Whence.all :conditions => ["start_at > ?", Chronic.parse("today 00:00")]
|
210
|
+
end
|
211
|
+
end
|
212
|
+
@whences.sort_by(&:id)
|
213
|
+
puts process_template!
|
214
|
+
end
|
215
|
+
|
216
|
+
def quit
|
217
|
+
project.stop! if project? and project.whences.last_unended
|
218
|
+
Whence.last_unended.stop! if Whence.last_unended
|
219
|
+
begin
|
220
|
+
Process.kill("KILL", app.pid.to_i)
|
221
|
+
rescue Errno::ESRCH
|
222
|
+
end
|
223
|
+
app.pid = ''
|
224
|
+
app.gui = ''
|
225
|
+
app.save!
|
226
|
+
end
|
227
|
+
|
228
|
+
def gui
|
229
|
+
if Whence.last_unended
|
230
|
+
pop
|
231
|
+
else
|
232
|
+
main
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def show_env
|
237
|
+
defork { system("ruby views/env.rb ") }
|
238
|
+
end
|
239
|
+
|
240
|
+
def detect
|
241
|
+
if self.daemonized?
|
242
|
+
gui
|
243
|
+
else
|
244
|
+
daemonize!
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def unlock
|
249
|
+
self.app.unlock
|
250
|
+
end
|
251
|
+
|
252
|
+
def run
|
253
|
+
self.begin if i_should? :begin
|
254
|
+
self.change if i_should? :change
|
255
|
+
self.restart if i_should? :restart
|
256
|
+
self.end if i_should? :end
|
257
|
+
|
258
|
+
self.destroy if i_should? :destroy
|
259
|
+
|
260
|
+
self.pid if i_should? :pid
|
261
|
+
self.raw if i_should? :raw
|
262
|
+
self.current if i_should? :current
|
263
|
+
puts self.graph if i_should? :graph
|
264
|
+
self.invoice if i_should? :invoice
|
265
|
+
self.console if i_should? :console
|
266
|
+
self.gui if i_should? :gui
|
267
|
+
self.show_env if i_should? :env
|
268
|
+
self.detect if i_should? :detect
|
269
|
+
self.unlock if i_should? :unlock
|
270
|
+
|
271
|
+
self.quit if i_should? :quit
|
272
|
+
self.daemonize! if i_should? :daemonize and not self.daemonized?
|
273
|
+
end
|
274
|
+
|
275
|
+
def daemonized?
|
276
|
+
!app.pid.blank? and ( cpid.to_i == app.pid )
|
277
|
+
end
|
278
|
+
def daemonize!
|
279
|
+
defork {
|
280
|
+
puts "pratt (#{Process.pid.to_s.yellow})"
|
281
|
+
app.pid = Process.pid
|
282
|
+
app.save!
|
283
|
+
|
284
|
+
gui
|
285
|
+
while(daemonized?)
|
286
|
+
sleep(app.interval)
|
287
|
+
gui
|
288
|
+
end
|
289
|
+
quit
|
290
|
+
}
|
291
|
+
end
|
292
|
+
|
293
|
+
def max
|
294
|
+
self.class.max
|
295
|
+
end
|
296
|
+
|
297
|
+
private
|
298
|
+
def main
|
299
|
+
self.app.reload
|
300
|
+
return if self.app.gui?('main', true)
|
301
|
+
self.app.log('main')
|
302
|
+
projects = ([Project.primary, Project.off] | Project.rest).collect(&:name)
|
303
|
+
if Whence.count == 0
|
304
|
+
# first run
|
305
|
+
project = Whence.new(:project => Project.primary)
|
306
|
+
else
|
307
|
+
project = Whence.last_unended || Whence.last
|
308
|
+
end
|
309
|
+
defork { system("ruby views/main.rb --projects '#{projects*"','"}' --current '#{project.project.name}'") }
|
310
|
+
end
|
311
|
+
def pop
|
312
|
+
self.app.reload
|
313
|
+
return if self.app.gui?('pop', true)
|
314
|
+
self.app.log('pop')
|
315
|
+
self.project = Whence.last_unended.project
|
316
|
+
defork { system("ruby views/pop.rb --project '#{project.name}' --start '#{project.whences.last_unended.start_at}' --project_time '#{Pratt.totals(project.time_spent)}'") }
|
317
|
+
end
|
318
|
+
|
319
|
+
def i_should? what
|
320
|
+
@todo.include?(what)
|
321
|
+
end
|
322
|
+
|
323
|
+
def project?
|
324
|
+
!@project.nil? and @project.name?
|
325
|
+
end
|
326
|
+
|
327
|
+
def process_template!
|
328
|
+
input = File.open(Pratt.root("views", "#{template}.eruby").first).read
|
329
|
+
erubis = Erubis::Eruby.new(input)
|
330
|
+
erubis.evaluate(self)
|
331
|
+
end
|
332
|
+
|
333
|
+
def padded_to_max string
|
334
|
+
self.class.padded_to_max string
|
335
|
+
end
|
336
|
+
|
337
|
+
def defork &block
|
338
|
+
Process.detach(
|
339
|
+
fork &block
|
340
|
+
)
|
341
|
+
end
|
342
|
+
|
343
|
+
class << self
|
344
|
+
|
345
|
+
|
346
|
+
def max
|
347
|
+
# TODO Fix me
|
348
|
+
@max ||= Project.all.inject(0) {|x,p| x = p.name.length if p.name.length > x; x }
|
349
|
+
end
|
350
|
+
|
351
|
+
def color
|
352
|
+
@@color
|
353
|
+
end
|
354
|
+
|
355
|
+
def color= c
|
356
|
+
@@color = c
|
357
|
+
end
|
358
|
+
|
359
|
+
def color?
|
360
|
+
@@color == true
|
361
|
+
end
|
362
|
+
|
363
|
+
|
364
|
+
def parse args
|
365
|
+
me = Pratt.new
|
366
|
+
|
367
|
+
opt = OptionParser.new do |opt|
|
368
|
+
Pratt.connect! ENV['PRATT_ENV'] || 'development' unless Pratt.connected?
|
369
|
+
|
370
|
+
opt.on('-b', "--begin PROJECT_NAME", String, "Begin project tracking.") do |proj|
|
371
|
+
me.project = proj
|
372
|
+
me << :begin
|
373
|
+
end
|
374
|
+
opt.on('-r', "--restart [PROJECT_NAME]", String, "Restart project log (stop last log and start a new one).
|
375
|
+
Applies to last un-ended project, unless a specific project is provided.") do |proj|
|
376
|
+
me.project = proj
|
377
|
+
me << :restart
|
378
|
+
end
|
379
|
+
opt.on('-e', "--end [PROJECT_NAME]", String, "Stop tracking interval for last project or supplied project if provided.") do |proj|
|
380
|
+
me.project = proj
|
381
|
+
me << :end
|
382
|
+
end
|
383
|
+
opt.on('-c', "--change PROJECT_NAME", String, "Change last time interval to this project.") do |proj|
|
384
|
+
me.project = proj
|
385
|
+
me << :change
|
386
|
+
end
|
387
|
+
|
388
|
+
opt.on('-g', "--graph [PROJECT_NAME]", String, "Display time spent on supplied project or all projects without argument value.") do |proj|
|
389
|
+
me.project = proj
|
390
|
+
me << :graph
|
391
|
+
end
|
392
|
+
|
393
|
+
opt.on('-I', "--invoice", "Create an invoice.") do
|
394
|
+
me << :invoice
|
395
|
+
end
|
396
|
+
|
397
|
+
templates = []
|
398
|
+
Pratt.root("views", "*.eruby") {|view| templates << File.basename(view, '.eruby') }
|
399
|
+
opt.on('-t', "--template TEMPLATE", templates, "Template to use for displaying work done.
|
400
|
+
Available templates are #{templates.to_sentence('or')}.") do |template|
|
401
|
+
me.template = template
|
402
|
+
end
|
403
|
+
opt.on('--destroy PROJECT_NAME', String, "Remove a project.") do |proj|
|
404
|
+
me.project = proj
|
405
|
+
me << :destroy
|
406
|
+
end
|
407
|
+
|
408
|
+
opt.on '-w', '--when_to TIME', String, 'When to do something.
|
409
|
+
(e.g. log time start|stop, or what time interval to graph)
|
410
|
+
If graphing, silently ignored w/out scale argument.' do |when_to|
|
411
|
+
me.when_to = Chronic.parse(when_to)
|
412
|
+
end
|
413
|
+
scales = %w(day week month quarter year)
|
414
|
+
opt.on('-l', '--scale SCALE', scales, "Granularity of time argument
|
415
|
+
Available scales are #{scales.to_sentence('or')}.
|
416
|
+
Only applies to graphing.") do |scale|
|
417
|
+
me.scale = scale
|
418
|
+
end
|
419
|
+
|
420
|
+
opt.on('-P', '--pid', "Process id display. (Is it still running)") do
|
421
|
+
me << :pid
|
422
|
+
end
|
423
|
+
opt.on('-R', '--raw [CONDITIONS]', "Dump logs (semi-)raw") do |conditions|
|
424
|
+
me << :raw
|
425
|
+
me.raw_conditions = conditions
|
426
|
+
end
|
427
|
+
opt.on('-L', '--log', "Redirect errors") do
|
428
|
+
FileUtils.mkdir 'log' unless File.exists? 'log'
|
429
|
+
$stderr.reopen('log/pratt.log', 'a')
|
430
|
+
$stderr.sync = true
|
431
|
+
end
|
432
|
+
|
433
|
+
opt.on('-N', '--no-color', "Display output without color or special characters.") do
|
434
|
+
Pratt.color = false
|
435
|
+
end
|
436
|
+
opt.on('-A', '--show-all', "Display all project regardless of other options.") do
|
437
|
+
me.show_all = true
|
438
|
+
end
|
439
|
+
|
440
|
+
opt.on('-C', "--current", "Show available projects and current project (if there is one)") do
|
441
|
+
me << :current
|
442
|
+
end
|
443
|
+
|
444
|
+
opt.on('-i', "--interval INTERVAL", Float, "Set the remind interval/min (Only applies to daemonized process).") do |interval|
|
445
|
+
me.app.interval = interval
|
446
|
+
me.app.save
|
447
|
+
end
|
448
|
+
opt.on('-D', '--detect', 'Detect appropriate behavior. (Daemonize or Graphical).') do
|
449
|
+
me << :detect
|
450
|
+
end
|
451
|
+
opt.on('-G', '--gui', 'Show "smart" gui.') do
|
452
|
+
me << :gui
|
453
|
+
end
|
454
|
+
opt.on("-d", "--daemonize", "Start daemon.") do
|
455
|
+
me << :daemonize
|
456
|
+
end
|
457
|
+
opt.on('-q', "--quit", "Stop daemon.") do
|
458
|
+
me << :quit
|
459
|
+
end
|
460
|
+
opt.on('-U', '--unlock', "Manually unlock a gui that has died but left it's lock around.") do
|
461
|
+
me << :unlock
|
462
|
+
end
|
463
|
+
|
464
|
+
opt.on '-V', '--version' do
|
465
|
+
puts "Pro-Reactive Time Tracker [Pratt] (#{VERSION})"
|
466
|
+
end
|
467
|
+
|
468
|
+
opt.parse!
|
469
|
+
end
|
470
|
+
|
471
|
+
me << :env if args.include? 'env'
|
472
|
+
me << :console if args.include? 'console'
|
473
|
+
|
474
|
+
me.run
|
475
|
+
end
|
476
|
+
|
477
|
+
def totals hr, fmt = false
|
478
|
+
"#{fmt_i(hr / 24, 'day', :cyan)} #{fmt_i(hr % 24, 'hour', :yellow)} #{fmt_i((60*(hr -= hr.to_i)), 'min', :green)}"
|
479
|
+
end
|
480
|
+
|
481
|
+
def percent label, off, total, color
|
482
|
+
fmt_f((off/total)*100, label, color)
|
483
|
+
end
|
484
|
+
|
485
|
+
def root *globs, &block
|
486
|
+
root = Dir.pwd
|
487
|
+
if globs.empty?
|
488
|
+
subdir = root
|
489
|
+
else
|
490
|
+
subdir = File.join(root, *globs)
|
491
|
+
end
|
492
|
+
|
493
|
+
if block_given?
|
494
|
+
Pathname.glob(subdir) {|dir_files| yield dir_files }
|
495
|
+
else
|
496
|
+
Pathname.glob(subdir)
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
|
501
|
+
def padded_to_max string
|
502
|
+
"%#{max}.#{max}s"% string
|
503
|
+
end
|
504
|
+
|
505
|
+
def migrate
|
506
|
+
Pratt.root( 'models', '*.rb' ) do |model_file|
|
507
|
+
klass = File.basename( model_file, '.rb' ).capitalize.constantize
|
508
|
+
begin
|
509
|
+
ActiveRecord::Base.connection.table_structure(model_file)
|
510
|
+
rescue ActiveRecord::StatementInvalid
|
511
|
+
klass.migrate :up if klass.superclass == ActiveRecord::Base
|
512
|
+
end
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
516
|
+
private
|
517
|
+
|
518
|
+
def fmt_f flt, label, color
|
519
|
+
padded_to_max(label) << " " << (("%0.2f"% flt) << '%').send(color)
|
520
|
+
end
|
521
|
+
|
522
|
+
def fmt_i int, label, color
|
523
|
+
"%s #{label}"% [("%02i"% int).send(color), label]
|
524
|
+
end
|
525
|
+
|
526
|
+
end
|
527
|
+
end
|