timetrackr 0.1.2 → 0.1.3
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/Gemfile +1 -0
- data/Gemfile.lock +2 -0
- data/README.mkd +9 -5
- data/VERSION +1 -1
- data/bin/timetrackr +17 -17
- data/lib/timetrackr/period.rb +1 -1
- data/lib/timetrackr/sqlite.rb +65 -0
- data/lib/timetrackr.rb +4 -4
- data/test/test_timetrackr.rb +37 -0
- data/timetrackr.gemspec +5 -1
- metadata +21 -9
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/README.mkd
CHANGED
|
@@ -4,19 +4,23 @@ A simple CLI time tracking utility.
|
|
|
4
4
|
|
|
5
5
|
## Example
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
$ tt start work
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
$ tt switch play
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
$ tt
|
|
12
12
|
work 0h 0m 3s
|
|
13
13
|
play * 0h 0m 1s
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
$ tt clear work
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
$ tt time
|
|
18
18
|
play * 0h 0m 5s
|
|
19
19
|
|
|
20
|
+
$ tt log
|
|
21
|
+
2011-05-17 work 08:05 08:20 0h 15m 41s
|
|
22
|
+
something * 09:05 0h 0m 4s
|
|
23
|
+
|
|
20
24
|
|
|
21
25
|
|
|
22
26
|
## Contributing to timetrackr
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.1.
|
|
1
|
+
0.1.3
|
data/bin/timetrackr
CHANGED
|
@@ -6,13 +6,13 @@ require 'timetrackr'
|
|
|
6
6
|
require 'timetrackr/period'
|
|
7
7
|
|
|
8
8
|
DEFAULTS = {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
'backend' => 'yaml',
|
|
10
|
+
'verbose' => false,
|
|
11
|
+
'single_task' => false,
|
|
12
|
+
'path' => File.join(ENV['HOME'],'.timetrackr.db'),
|
|
13
|
+
'relative_format' => "%2<hours>dh %2<minutes>dm %2<seconds>ds",
|
|
14
|
+
'absolute_time' => "%H:%M",
|
|
15
|
+
'absolute_day' => "%Y-%m-%d"
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
def show_help
|
|
@@ -56,7 +56,7 @@ end
|
|
|
56
56
|
# global options
|
|
57
57
|
while (cmd = ARGV.shift) && cmd.start_with?('-')
|
|
58
58
|
if ['-v','--verbose'].include? cmd
|
|
59
|
-
config[
|
|
59
|
+
config['verbose'] = true
|
|
60
60
|
end
|
|
61
61
|
if ['-h','--help'].include? cmd
|
|
62
62
|
show_help
|
|
@@ -65,8 +65,8 @@ while (cmd = ARGV.shift) && cmd.start_with?('-')
|
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
config = DEFAULTS.merge(config || {})
|
|
68
|
-
$verbose = config[
|
|
69
|
-
trackr = TimeTrackr.create(config[
|
|
68
|
+
$verbose = config['verbose']
|
|
69
|
+
trackr = TimeTrackr.create(config['backend'], config)
|
|
70
70
|
|
|
71
71
|
#
|
|
72
72
|
# commands
|
|
@@ -76,7 +76,7 @@ when 'start','in','s'
|
|
|
76
76
|
task = ARGV.shift
|
|
77
77
|
notes = ARGV.join(' ')
|
|
78
78
|
# switch tasks if config says so
|
|
79
|
-
if config[
|
|
79
|
+
if config['single_task'] && trackr.current != task
|
|
80
80
|
trackr.current.each { |t|
|
|
81
81
|
trackr.stop(t) unless t == task
|
|
82
82
|
}
|
|
@@ -118,7 +118,7 @@ when 'time','status',nil
|
|
|
118
118
|
t = t + period.length
|
|
119
119
|
}
|
|
120
120
|
name = trackr.current.include?(task) ? task+' *' : task
|
|
121
|
-
puts name.ljust(15) << format_time(total,config[
|
|
121
|
+
puts name.ljust(15) << format_time(total,config['relative_format'])
|
|
122
122
|
end
|
|
123
123
|
|
|
124
124
|
when 'log'
|
|
@@ -131,16 +131,16 @@ when 'log'
|
|
|
131
131
|
periods = tasks.each.collect{ |t| trackr.history(t) }.flatten
|
|
132
132
|
lastday = nil
|
|
133
133
|
table << periods.sort{|x,y| x.start <=> y.start}.collect{ |period|
|
|
134
|
-
currday = period.start.strftime(config[
|
|
134
|
+
currday = period.start.strftime(config['absolute_day'])
|
|
135
135
|
day = (currday == lastday) ? ' ' : currday
|
|
136
136
|
lastday = currday
|
|
137
137
|
name = period.current? ? period.task+' *' : period.task
|
|
138
|
-
start = period.start.strftime(config[
|
|
139
|
-
stop = period.current? ? ' ' : period.stop.strftime(config[
|
|
140
|
-
length = format_time(period.length, config[
|
|
138
|
+
start = period.start.strftime(config['absolute_time'])
|
|
139
|
+
stop = period.current? ? ' ' : period.stop.strftime(config['absolute_time'])
|
|
140
|
+
length = format_time(period.length, config['relative_format'])
|
|
141
141
|
"#{day.ljust(12)} #{name.ljust(15)} #{start.ljust(7)} #{stop.ljust(7)} #{length}"
|
|
142
142
|
}
|
|
143
|
-
puts table
|
|
143
|
+
puts table
|
|
144
144
|
|
|
145
145
|
|
|
146
146
|
when 'clear','delete','del'
|
data/lib/timetrackr/period.rb
CHANGED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
class SqliteTimeTrackr < TimeTrackr
|
|
2
|
+
|
|
3
|
+
def initialize(path)
|
|
4
|
+
@log_path = path
|
|
5
|
+
if !File.exist? @log_path
|
|
6
|
+
@db = SQLite3::Database.new(@log_path)
|
|
7
|
+
sql_events = "CREATE TABLE events (
|
|
8
|
+
id INTEGER PRIMARY KEY,
|
|
9
|
+
task TEXT,
|
|
10
|
+
start TIME,
|
|
11
|
+
stop TIME,
|
|
12
|
+
notes TEXT);"
|
|
13
|
+
@db.execute(sql_events)
|
|
14
|
+
else
|
|
15
|
+
@db = SQLite3::Database.open(@log_path)
|
|
16
|
+
end
|
|
17
|
+
@db.type_translation = true
|
|
18
|
+
puts "Using DB file '#{@log_path}'" if $verbose
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def current
|
|
22
|
+
sql = "SELECT DISTINCT task FROM events WHERE stop IS NULL;"
|
|
23
|
+
@db.execute(sql).collect{|row|
|
|
24
|
+
row.first
|
|
25
|
+
}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def tasks
|
|
29
|
+
sql = "SELECT DISTINCT task FROM events;"
|
|
30
|
+
@db.execute(sql).collect{ |row|
|
|
31
|
+
row.first
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def start(task, notes)
|
|
36
|
+
sql = "SELECT id FROM events WHERE task = :task AND stop IS NULL;"
|
|
37
|
+
exists = @db.get_first_value(sql, 'task' => task)
|
|
38
|
+
if !exists
|
|
39
|
+
sql = "INSERT INTO events (task,start,notes) VALUES (:task,:start,:notes);"
|
|
40
|
+
@db.execute(sql,'task' => task, 'start' => Time.now.to_s, 'notes' => notes)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def stop(task)
|
|
45
|
+
sql = "SELECT id FROM events WHERE task = :task AND stop IS NULL;"
|
|
46
|
+
exists = @db.get_first_value(sql, 'task' => task)
|
|
47
|
+
if exists
|
|
48
|
+
sql = "UPDATE events SET stop = :stop WHERE id = :current;"
|
|
49
|
+
@db.execute(sql, 'current' => exists, 'stop' => Time.now.to_s)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def history(task, p_begin=nil, p_end=nil)
|
|
54
|
+
sql = "SELECT start, stop, notes FROM events WHERE task = :task ORDER BY start;"
|
|
55
|
+
@db.execute(sql,'task' => task).collect{ |row|
|
|
56
|
+
Period.new(task,row[0],row[1],row[2])
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def clear(task)
|
|
61
|
+
sql = "DELETE FROM events WHERE task = :task;"
|
|
62
|
+
@db.execute(sql, 'task' => task)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
end
|
data/lib/timetrackr.rb
CHANGED
|
@@ -2,12 +2,12 @@ autoload 'YamlTimeTrackr', 'timetrackr/yaml'
|
|
|
2
2
|
autoload 'SqliteTimeTrackr', 'timetrackr/sqlite'
|
|
3
3
|
|
|
4
4
|
class TimeTrackr
|
|
5
|
-
def self.create(type,options={})
|
|
5
|
+
def self.create(type, options={})
|
|
6
6
|
case type.to_s
|
|
7
7
|
when 'yaml'
|
|
8
8
|
begin
|
|
9
9
|
require 'yaml'
|
|
10
|
-
log = YamlTimeTrackr.new(options[
|
|
10
|
+
log = YamlTimeTrackr.new(options['path'])
|
|
11
11
|
puts 'Loaded yaml tracker' if $verbose
|
|
12
12
|
rescue LoadError
|
|
13
13
|
puts 'Yaml not found'
|
|
@@ -15,7 +15,7 @@ class TimeTrackr
|
|
|
15
15
|
when 'sqlite'
|
|
16
16
|
begin
|
|
17
17
|
require 'sqlite3'
|
|
18
|
-
log = SqliteTimeTrackr.new(options[
|
|
18
|
+
log = SqliteTimeTrackr.new(options['path'])
|
|
19
19
|
puts 'Loaded sqlite tracker' if $verbose
|
|
20
20
|
rescue LoadError
|
|
21
21
|
puts 'Sqlite not found'
|
|
@@ -34,7 +34,7 @@ class TimeTrackr
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
#
|
|
37
|
-
# start a period
|
|
37
|
+
# start a period with optional notes
|
|
38
38
|
#
|
|
39
39
|
def start(task,notes)
|
|
40
40
|
raise 'Not implemented'
|
data/test/test_timetrackr.rb
CHANGED
|
@@ -50,4 +50,41 @@ class TestTimetrackr < Test::Unit::TestCase
|
|
|
50
50
|
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
|
+
|
|
54
|
+
context 'an Sqlite based tracker' do
|
|
55
|
+
setup do
|
|
56
|
+
@config = {:path => '/tmp/timetracker.test'}
|
|
57
|
+
@t = TimeTrackr.create('sqlite',@config)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def teardown
|
|
61
|
+
File.unlink(@config[:path]) rescue nil
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
should 'initialise a log file' do
|
|
65
|
+
assert File.exist?(@config[:path])
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
context 'with empty db' do
|
|
69
|
+
|
|
70
|
+
should 'not fail on current command' do
|
|
71
|
+
assert_nothing_raised Exception do
|
|
72
|
+
@t.current
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
should 'not fail on tasks command' do
|
|
77
|
+
assert_nothing_raised Exception do
|
|
78
|
+
@t.tasks
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
should 'not fail on close command' do
|
|
83
|
+
assert_nothing_raised Exception do
|
|
84
|
+
@t.close
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
end
|
|
89
|
+
end
|
|
53
90
|
end
|
data/timetrackr.gemspec
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
Gem::Specification.new do |s|
|
|
7
7
|
s.name = %q{timetrackr}
|
|
8
|
-
s.version = "0.1.
|
|
8
|
+
s.version = "0.1.3"
|
|
9
9
|
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
11
11
|
s.authors = ["Felix Hanley"]
|
|
@@ -28,6 +28,7 @@ Gem::Specification.new do |s|
|
|
|
28
28
|
"bin/timetrackr",
|
|
29
29
|
"lib/timetrackr.rb",
|
|
30
30
|
"lib/timetrackr/period.rb",
|
|
31
|
+
"lib/timetrackr/sqlite.rb",
|
|
31
32
|
"lib/timetrackr/yaml.rb",
|
|
32
33
|
"test/helper.rb",
|
|
33
34
|
"test/test_timetrackr.rb",
|
|
@@ -47,17 +48,20 @@ Gem::Specification.new do |s|
|
|
|
47
48
|
s.specification_version = 3
|
|
48
49
|
|
|
49
50
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
|
51
|
+
s.add_development_dependency(%q<sqlite3>, [">= 0"])
|
|
50
52
|
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
|
51
53
|
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
|
52
54
|
s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
|
|
53
55
|
s.add_development_dependency(%q<rcov>, [">= 0"])
|
|
54
56
|
else
|
|
57
|
+
s.add_dependency(%q<sqlite3>, [">= 0"])
|
|
55
58
|
s.add_dependency(%q<shoulda>, [">= 0"])
|
|
56
59
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
|
57
60
|
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
|
58
61
|
s.add_dependency(%q<rcov>, [">= 0"])
|
|
59
62
|
end
|
|
60
63
|
else
|
|
64
|
+
s.add_dependency(%q<sqlite3>, [">= 0"])
|
|
61
65
|
s.add_dependency(%q<shoulda>, [">= 0"])
|
|
62
66
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
|
63
67
|
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
metadata
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: timetrackr
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease:
|
|
5
|
-
version: 0.1.
|
|
5
|
+
version: 0.1.3
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
8
8
|
- Felix Hanley
|
|
@@ -14,7 +14,7 @@ date: 2011-05-17 00:00:00 +07:00
|
|
|
14
14
|
default_executable: timetrackr
|
|
15
15
|
dependencies:
|
|
16
16
|
- !ruby/object:Gem::Dependency
|
|
17
|
-
name:
|
|
17
|
+
name: sqlite3
|
|
18
18
|
requirement: &id001 !ruby/object:Gem::Requirement
|
|
19
19
|
none: false
|
|
20
20
|
requirements:
|
|
@@ -25,8 +25,19 @@ dependencies:
|
|
|
25
25
|
prerelease: false
|
|
26
26
|
version_requirements: *id001
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
28
|
+
name: shoulda
|
|
29
29
|
requirement: &id002 !ruby/object:Gem::Requirement
|
|
30
|
+
none: false
|
|
31
|
+
requirements:
|
|
32
|
+
- - ">="
|
|
33
|
+
- !ruby/object:Gem::Version
|
|
34
|
+
version: "0"
|
|
35
|
+
type: :development
|
|
36
|
+
prerelease: false
|
|
37
|
+
version_requirements: *id002
|
|
38
|
+
- !ruby/object:Gem::Dependency
|
|
39
|
+
name: bundler
|
|
40
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
|
30
41
|
none: false
|
|
31
42
|
requirements:
|
|
32
43
|
- - ~>
|
|
@@ -34,10 +45,10 @@ dependencies:
|
|
|
34
45
|
version: 1.0.0
|
|
35
46
|
type: :development
|
|
36
47
|
prerelease: false
|
|
37
|
-
version_requirements: *
|
|
48
|
+
version_requirements: *id003
|
|
38
49
|
- !ruby/object:Gem::Dependency
|
|
39
50
|
name: jeweler
|
|
40
|
-
requirement: &
|
|
51
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
|
41
52
|
none: false
|
|
42
53
|
requirements:
|
|
43
54
|
- - ~>
|
|
@@ -45,10 +56,10 @@ dependencies:
|
|
|
45
56
|
version: 1.5.2
|
|
46
57
|
type: :development
|
|
47
58
|
prerelease: false
|
|
48
|
-
version_requirements: *
|
|
59
|
+
version_requirements: *id004
|
|
49
60
|
- !ruby/object:Gem::Dependency
|
|
50
61
|
name: rcov
|
|
51
|
-
requirement: &
|
|
62
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
|
52
63
|
none: false
|
|
53
64
|
requirements:
|
|
54
65
|
- - ">="
|
|
@@ -56,7 +67,7 @@ dependencies:
|
|
|
56
67
|
version: "0"
|
|
57
68
|
type: :development
|
|
58
69
|
prerelease: false
|
|
59
|
-
version_requirements: *
|
|
70
|
+
version_requirements: *id005
|
|
60
71
|
description: A simple time tracking utility
|
|
61
72
|
email: felix@seconddrawer.com.au
|
|
62
73
|
executables:
|
|
@@ -76,6 +87,7 @@ files:
|
|
|
76
87
|
- bin/timetrackr
|
|
77
88
|
- lib/timetrackr.rb
|
|
78
89
|
- lib/timetrackr/period.rb
|
|
90
|
+
- lib/timetrackr/sqlite.rb
|
|
79
91
|
- lib/timetrackr/yaml.rb
|
|
80
92
|
- test/helper.rb
|
|
81
93
|
- test/test_timetrackr.rb
|
|
@@ -94,7 +106,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
94
106
|
requirements:
|
|
95
107
|
- - ">="
|
|
96
108
|
- !ruby/object:Gem::Version
|
|
97
|
-
hash: -
|
|
109
|
+
hash: -4194975739738215101
|
|
98
110
|
segments:
|
|
99
111
|
- 0
|
|
100
112
|
version: "0"
|