Pratt 1.5.6
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/.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
|