clitt 0.1.0

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.
Files changed (10) hide show
  1. data/LICENSE +20 -0
  2. data/README +24 -0
  3. data/TODO +1 -0
  4. data/bin/clitt +24 -0
  5. data/bin/pi +6 -0
  6. data/bin/po +6 -0
  7. data/lib/tt.rb +132 -0
  8. data/lib/tt/smrty.rb +4 -0
  9. data/spec/spec_helper.rb +2 -0
  10. metadata +105 -0
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 François Beausoleil
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,24 @@
1
+ TT - Time Tracker
2
+ ================
3
+
4
+ Track your time locally, offline.
5
+
6
+ Usage
7
+ =====
8
+
9
+ $ pi # punch in, stores current working directory, date, time (UTC)
10
+ $ po # punch out: stops current task
11
+ $ cd my-project
12
+ $ pi
13
+ $ # code furiously
14
+ $ cd ../my-other-project
15
+ $ pi talking about stuff # stops first instance, records new CWD, and comment
16
+ $ po did this and that # stops tracking time, appends comment to time entry
17
+ $ tt list # lists your time entries in a nice format
18
+ $ tt drop # drops your last time entry
19
+ $ tt clear # DESTROYS all time entries, no turning back, nor is there any confirmation
20
+
21
+ Data
22
+ ====
23
+
24
+ The time tracker database is stored as ~/.tt/entries.csv
data/TODO ADDED
@@ -0,0 +1 @@
1
+ TODO:
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ require "rubygems"
3
+ $: << File.dirname(__FILE__) + "/../lib"
4
+ require "tt"
5
+
6
+ case command = ARGV.shift
7
+ when "list", "ls"
8
+ Tt.report
9
+ when "clear"
10
+ Tt.clear
11
+ when "drop"
12
+ Tt.drop
13
+ else
14
+ puts <<EOF
15
+ WARNING: No command given
16
+
17
+ tt - Time Tracker
18
+
19
+ list: lists the time entries
20
+ clear: deletes all time entries
21
+ drop: drops the last time entry from the file
22
+ EOF
23
+ exit 1
24
+ end
data/bin/pi ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require "rubygems"
3
+ $: << File.dirname(__FILE__) + "/../lib"
4
+ require "tt"
5
+
6
+ Tt.punch_in(Dir.pwd, ARGV.empty? ? nil : ARGV.join(" "))
data/bin/po ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require "rubygems"
3
+ $: << File.dirname(__FILE__) + "/../lib"
4
+ require "tt"
5
+
6
+ Tt.punch_out(Dir.pwd, ARGV.empty? ? nil : ARGV.join(" "))
@@ -0,0 +1,132 @@
1
+ require "pathname"
2
+ require "time"
3
+
4
+ module Tt
5
+ FORMAT = "%Y-%m-%dT%H:%MZ"
6
+ SCREEN_FORMAT = "%Y-%m-%d %H:%M"
7
+
8
+ def self.csv_engine
9
+ if RUBY_VERSION =~ /^1\.8/ then
10
+ require "fastercsv"
11
+ FasterCSV
12
+ else
13
+ require "csv"
14
+ CSV
15
+ end
16
+ end
17
+
18
+ def self.root
19
+ @root ||= ENV["CLITT_ROOT"].to_s.empty? ? ENV["HOME"] : ENV["CLITT_ROOT"]
20
+ end
21
+
22
+ def self.tt_dir
23
+ Pathname.new(root) + ".tt"
24
+ end
25
+
26
+ def self.tt_path
27
+ tt_dir + "entries.csv"
28
+ end
29
+
30
+ def self.punch_in(cwd, comment=nil)
31
+ tt_dir.mkdir unless tt_dir.directory?
32
+ csv_engine.open(tt_path, "ab") do |io|
33
+ io << ["in", cwd, Time.now.utc.strftime(FORMAT), comment]
34
+ end
35
+ end
36
+
37
+ def self.punch_out(cwd, comment=nil)
38
+ tt_dir.mkdir unless tt_dir.directory?
39
+ csv_engine.open(tt_path, "ab") do |io|
40
+ io << ["out", cwd, Time.now.utc.strftime(FORMAT), comment]
41
+ end
42
+ end
43
+
44
+ def self.report_line(dir, intime, outtime, comment, stream=STDOUT)
45
+ ltime = Time.now
46
+ intime = intime + ltime.utc_offset
47
+ endtime = (outtime.nil? ? Time.now.utc : outtime) + ltime.utc_offset
48
+ outtime = outtime + ltime.utc_offset if outtime
49
+
50
+ duration_in_seconds = endtime - intime
51
+ duration_in_minutes = duration_in_seconds / 60
52
+ duration_as_human_string = "%02d:%02d" % [duration_in_minutes / 60, duration_in_minutes % 60]
53
+
54
+ stream.print "%-40s %16s %16s %5s" % [dir, intime.strftime(SCREEN_FORMAT), outtime ? outtime.strftime(SCREEN_FORMAT) : "", duration_as_human_string]
55
+ stream.print " - %s" % comment if comment && !comment.empty?
56
+ stream.puts
57
+ end
58
+
59
+ def self.each_line
60
+ tt_dir.mkdir unless tt_dir.directory?
61
+ line, state, dir, intime, outtime, comment = 0, :out, nil, nil, nil, nil
62
+
63
+ csv_engine.foreach(tt_path) do |row|
64
+ line += 1
65
+
66
+ case row[0]
67
+ when "in"
68
+ case state
69
+ when :out # punch in
70
+ dir = row[1]
71
+ intime = Time.parse(row[2])
72
+ outtime = nil
73
+ comment = row[3]
74
+ state = :in
75
+ when :in # switch task
76
+ outtime = Time.parse(row[2])
77
+ yield(dir, intime, outtime, [comment, row[3]].compact.join("; "))
78
+
79
+ dir = row[1]
80
+ intime = Time.parse(row[2])
81
+ outtime = nil
82
+ comment = nil
83
+ else
84
+ raise ArgumentError, "Unknown processing state: #{state}, expected one of in/out"
85
+ end
86
+ when "out"
87
+ case state
88
+ when :in # punch out
89
+ outtime = Time.parse(row[2])
90
+ yield(dir, intime, outtime, [comment, row[3]].compact.join("; "))
91
+
92
+ dir, intime, outtime, comment = nil
93
+ state = :out
94
+ when :out
95
+ $stderr.puts "WARNING: out when already out on line #{line}"
96
+ else
97
+ raise ArgumentError, "Unknown processing state: #{state}, expected one of in/out"
98
+ end
99
+ else
100
+ raise SyntaxError, "Error on line \##{line} of #{tt_path}: unknown reference in column 0: #{row[0]}"
101
+ end
102
+ end if tt_path.file?
103
+
104
+ case state
105
+ when :in
106
+ yield(dir, intime, nil, comment)
107
+ end
108
+ end
109
+
110
+ def self.report(stream=STDOUT)
111
+ each_line do |dir, intime, outtime, comment|
112
+ report_line(dir, intime, outtime, comment, stream)
113
+ end
114
+ end
115
+
116
+ def self.drop
117
+ csv_engine.open(tt_path.to_s + ".new", "wb") do |io|
118
+ lastrow = nil
119
+ csv_engine.foreach(tt_path, "rb") do |row|
120
+ io << lastrow if lastrow
121
+ lastrow = row
122
+ end
123
+ end
124
+
125
+ tt_path.unlink
126
+ File.rename(tt_path.to_s + ".new", tt_path)
127
+ end
128
+
129
+ def self.clear
130
+ tt_path.unlink
131
+ end
132
+ end
@@ -0,0 +1,4 @@
1
+ module Tt
2
+ class Smrty
3
+ end
4
+ end
@@ -0,0 +1,2 @@
1
+ $TESTING=true
2
+ $:.push File.join(File.dirname(__FILE__), '..', 'lib')
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: clitt
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Francois Beausoleil
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-30 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: fastercsv
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: "1"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: active_support
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: timecop
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: rspec
47
+ type: :development
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ description: Track your time locally, offline, using this simple tool.
56
+ email: francois@teksol.info
57
+ executables:
58
+ - clitt
59
+ - pi
60
+ - po
61
+ extensions: []
62
+
63
+ extra_rdoc_files:
64
+ - LICENSE
65
+ - README
66
+ - TODO
67
+ files:
68
+ - bin/clitt
69
+ - bin/pi
70
+ - bin/po
71
+ - lib/tt.rb
72
+ - lib/tt/smrty.rb
73
+ - LICENSE
74
+ - README
75
+ - TODO
76
+ has_rdoc: false
77
+ homepage: http://francois.github.com/tt
78
+ licenses: []
79
+
80
+ post_install_message:
81
+ rdoc_options:
82
+ - --charset=UTF-8
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: "0"
90
+ version:
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: "0"
96
+ version:
97
+ requirements: []
98
+
99
+ rubyforge_project:
100
+ rubygems_version: 1.3.5
101
+ signing_key:
102
+ specification_version: 3
103
+ summary: Track your time locally, offline, using this simple tool.
104
+ test_files:
105
+ - spec/spec_helper.rb