timetrackr 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,6 +1,7 @@
1
1
  source 'http://rubygems.org'
2
2
 
3
3
  group :development do
4
+ gem 'sqlite3'
4
5
  gem 'shoulda', '>= 0'
5
6
  gem 'bundler', '~> 1.0.0'
6
7
  gem 'jeweler', '~> 1.5.2'
data/Gemfile.lock CHANGED
@@ -9,6 +9,7 @@ GEM
9
9
  rake (0.8.7)
10
10
  rcov (0.9.9)
11
11
  shoulda (2.11.3)
12
+ sqlite3 (1.3.3)
12
13
 
13
14
  PLATFORMS
14
15
  ruby
@@ -18,3 +19,4 @@ DEPENDENCIES
18
19
  jeweler (~> 1.5.2)
19
20
  rcov
20
21
  shoulda
22
+ sqlite3
data/README.mkd CHANGED
@@ -4,19 +4,23 @@ A simple CLI time tracking utility.
4
4
 
5
5
  ## Example
6
6
 
7
- >tt start work
7
+ $ tt start work
8
8
 
9
- >tt switch play
9
+ $ tt switch play
10
10
 
11
- >tt
11
+ $ tt
12
12
  work 0h 0m 3s
13
13
  play * 0h 0m 1s
14
14
 
15
- >tt clear work
15
+ $ tt clear work
16
16
 
17
- >tt time
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.2
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
- :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"
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[:verbose] = true
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[:verbose]
69
- trackr = TimeTrackr.create(config[:backend], 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[:single_task] && trackr.current != task
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[:relative_format])
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[:absolute_day])
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[:absolute_time])
139
- stop = period.current? ? ' ' : period.stop.strftime(config[:absolute_time])
140
- length = format_time(period.length, config[:relative_format])
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.join("\n")
143
+ puts table
144
144
 
145
145
 
146
146
  when 'clear','delete','del'
@@ -15,7 +15,7 @@ class Period
15
15
  end
16
16
 
17
17
  def current?
18
- stop.nil?
18
+ @stop.nil?
19
19
  end
20
20
 
21
21
  end
@@ -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[:path])
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[:path])
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'
@@ -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.2"
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.2
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: shoulda
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: bundler
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: *id002
48
+ version_requirements: *id003
38
49
  - !ruby/object:Gem::Dependency
39
50
  name: jeweler
40
- requirement: &id003 !ruby/object:Gem::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: *id003
59
+ version_requirements: *id004
49
60
  - !ruby/object:Gem::Dependency
50
61
  name: rcov
51
- requirement: &id004 !ruby/object:Gem::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: *id004
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: -3115143978170493963
109
+ hash: -4194975739738215101
98
110
  segments:
99
111
  - 0
100
112
  version: "0"