timetrackr 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -2,6 +2,7 @@ source 'http://rubygems.org'
2
2
 
3
3
  group :development do
4
4
  gem 'sqlite3'
5
+ gem 'json'
5
6
  gem 'shoulda', '>= 0'
6
7
  gem 'bundler', '~> 1.0.0'
7
8
  gem 'jeweler', '~> 1.5.2'
data/Gemfile.lock CHANGED
@@ -6,6 +6,7 @@ GEM
6
6
  bundler (~> 1.0.0)
7
7
  git (>= 1.2.5)
8
8
  rake
9
+ json (1.5.1)
9
10
  rake (0.8.7)
10
11
  rcov (0.9.9)
11
12
  shoulda (2.11.3)
@@ -17,6 +18,7 @@ PLATFORMS
17
18
  DEPENDENCIES
18
19
  bundler (~> 1.0.0)
19
20
  jeweler (~> 1.5.2)
21
+ json
20
22
  rcov
21
23
  shoulda
22
24
  sqlite3
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.4
1
+ 0.1.5
data/bin/timetrackr CHANGED
@@ -1,168 +1,12 @@
1
1
  #!/usr/bin/env ruby
2
+ # encoding: utf-8
2
3
 
3
4
  lib_dir = File.dirname(__FILE__) + '/../lib'
4
5
  $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
5
6
 
6
7
  require 'timetrackr'
8
+ require 'timetrackr/cli'
7
9
 
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"
16
- }
17
-
18
- def show_help
19
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
20
- puts "timetrackr version #{version}"
21
- puts <<HELP
22
-
23
- timetrackr [command] [options]
24
-
25
- Available commands:
26
-
27
- start [task] start a task
28
- stop [task] stop a task (or 'all')
29
- switch TASK switch tasks
30
- time [task] show time for a task (or 'all')
31
- log [task] show time log for a task (or 'all')
32
-
33
- Global options
34
- -h --help show this help
35
- -v --verbose be noisy
36
- HELP
37
- end
38
-
39
- def format_time(time, fmt_str)
40
- hours = time.to_i/3600.to_i
41
- minutes = (time/60 - hours * 60).to_i
42
- seconds = (time - (minutes * 60 + hours * 3600))
43
- format(fmt_str,{
44
- :hours => hours,
45
- :minutes => minutes,
46
- :seconds => seconds})
47
- end
48
-
49
- config = {}
50
- config_file = File.join(ENV['HOME'],'.timetrackrrc')
51
- if File.exist?(config_file)
52
- require 'yaml'
53
- config = YAML.load_file(config_file)
54
- end
55
-
56
- # global options
57
- while (cmd = ARGV.shift) && cmd.start_with?('-')
58
- if ['-v','--verbose'].include? cmd
59
- config['verbose'] = true
60
- end
61
- if ['-h','--help'].include? cmd
62
- show_help
63
- exit 1
64
- end
65
- end
66
-
67
- config = DEFAULTS.merge(config || {})
68
- $verbose = config['verbose']
69
- trackr = TimeTrackr.create(config['backend'], config)
70
-
71
- #
72
- # commands
73
- #
74
- case cmd
75
- when 'start','in','s'
76
- task = ARGV.shift
77
- notes = ARGV.join(' ')
78
- # switch tasks if config says so
79
- if config['single_task'] && trackr.current != task
80
- trackr.current.each { |t|
81
- trackr.stop(t) unless t == task
82
- }
83
- puts "Switched to task '#{task}'" if $verbose
84
- else
85
- puts "Started task '#{task}'" if $verbose
86
- end
87
- trackr.start(task, notes)
88
-
89
- when 'stop','out','kill','k'
90
- if ARGV[0] == 'all' || ARGV[0].nil?
91
- tasks = trackr.tasks
92
- else
93
- tasks = ARGV
94
- end
95
- tasks.each do |task|
96
- trackr.stop(task)
97
- puts "Stopped task '#{task}'" if $verbose
98
- end
99
-
100
- when 'switch','sw'
101
- task = ARGV.shift
102
- notes = ARGV.join(' ')
103
- trackr.current.each do |t|
104
- trackr.stop(t) unless t == task
105
- end
106
- trackr.start(task, notes)
107
- puts "Switched to task '#{task}'" if $verbose
108
-
109
- when 'time','status',nil
110
- task = ARGV.shift
111
- if task && trackr.tasks.include?(task)
112
- tasks = [*task]
113
- else
114
- tasks = trackr.tasks.each
115
- end
116
- tasks.each do |task|
117
- total = trackr.history(task).reduce(0){ |t, period|
118
- t = t + period.length
119
- }
120
- name = trackr.current.include?(task) ? task+' *' : task
121
- puts name.ljust(15) << format_time(total,config['relative_format'])
122
- end
123
-
124
- when 'log'
125
- if ARGV[0] == 'all' || ARGV[0].nil?
126
- tasks = trackr.tasks
127
- else
128
- tasks = ARGV
129
- end
130
- table = []
131
- periods = tasks.each.collect{ |t| trackr.history(t) }.flatten
132
- lastday = nil
133
- table << periods.sort{|x,y| x.start <=> y.start}.collect{ |period|
134
- currday = period.start.strftime(config['absolute_day'])
135
- day = (currday == lastday) ? ' ' : currday
136
- lastday = currday
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'])
141
- notes = period.notes
142
- "#{day.ljust(12)} #{name.ljust(15)} #{start} - #{stop.ljust(5)} #{length} #{notes}"
143
- }
144
- puts table
145
-
146
-
147
- when 'clear','delete','del'
148
- tasks = ARGV
149
- tasks = trackr.tasks if task == 'all'
150
- tasks.each do |task|
151
- trackr.clear(task)
152
- puts "Task '#{task}' cleared" if $verbose
153
- end
154
-
155
- when 'rename','mv'
156
- from = ARGV.shift
157
- to = ARGV.shift
158
- trackr.rename(from,to)
159
- puts "Renamed '#{from}' to '#{to}'" if $verbose
160
-
161
- else
162
- puts "'#{cmd}' is not a valid command"
163
- show_help
164
- end
165
-
166
- trackr.close
10
+ TimeTrackr::CLI.run(ARGV)
167
11
 
168
12
  # vim: set ts=2 sw=2 tw=80 ft=ruby fdm=syntax et fen :
@@ -0,0 +1,180 @@
1
+ module TimeTrackr
2
+ class CLI
3
+ DEFAULTS = {
4
+ 'backend' => 'yaml',
5
+ 'verbose' => false,
6
+ 'single_task' => false,
7
+ 'path' => File.join(ENV['HOME'],'.timetrackr.db'),
8
+ 'relative_format' => "%2<hours>dh %2<minutes>dm %2<seconds>ds",
9
+ 'absolute_time' => "%H:%M",
10
+ 'absolute_day' => "%Y-%m-%d"
11
+ }
12
+
13
+ #
14
+ # static method to get config file and run the tracker
15
+ #
16
+ def self.run(args)
17
+ config = {}
18
+ config_file = File.join(ENV['HOME'],'.timetrackrrc')
19
+ if File.exist?(config_file)
20
+ require 'yaml'
21
+ config = YAML.load_file(config_file)
22
+ end
23
+
24
+ # global options
25
+ while (cmd = args.shift) && cmd.start_with?('-')
26
+ if ['-v','--verbose'].include? cmd
27
+ config['verbose'] = true
28
+ end
29
+ if ['-h','--help'].include? cmd
30
+ cmd = 'help'
31
+ end
32
+ end
33
+ config = DEFAULTS.merge(config || {})
34
+
35
+ cli = TimeTrackr::CLI.new(config)
36
+ cli.run(cmd, args)
37
+ end
38
+
39
+ def initialize(config)
40
+ @config = config
41
+ @verbose = config['verbose']
42
+ @trackr = TimeTrackr::Database.create(config['backend'], config)
43
+ end
44
+
45
+ #
46
+ # run a command on the tracker
47
+ #
48
+ def run(cmd,args)
49
+ case cmd
50
+ when 'start','in','s'
51
+ task = args.shift
52
+ notes = args.join(' ')
53
+ # switch tasks if config says so
54
+ if @config['single_task'] && @trackr.current != task
55
+ @trackr.current.each { |t|
56
+ @trackr.stop(t) unless t == task
57
+ }
58
+ puts "Switched to task '#{task}'" if @verbose
59
+ else
60
+ puts "Started task '#{task}'" if @verbose
61
+ end
62
+ @trackr.start(task, notes)
63
+
64
+ when 'stop','out','kill','k'
65
+ if args[0] == 'all' || args[0].nil?
66
+ tasks = @trackr.tasks
67
+ else
68
+ tasks = args
69
+ end
70
+ tasks.each do |task|
71
+ @trackr.stop(task)
72
+ puts "Stopped task '#{task}'" if @verbose
73
+ end
74
+
75
+ when 'switch','sw'
76
+ task = args.shift
77
+ notes = args.join(' ')
78
+ @trackr.current.each do |t|
79
+ @trackr.stop(t) unless t == task
80
+ end
81
+ @trackr.start(task, notes)
82
+ puts "Switched to task '#{task}'" if @verbose
83
+
84
+ when 'time','status',nil
85
+ task = args.shift
86
+ if task && @trackr.tasks.include?(task)
87
+ tasks = [*task]
88
+ else
89
+ tasks = @trackr.tasks.each
90
+ end
91
+ tasks.each do |task|
92
+ total = @trackr.history(task).reduce(0){ |t, period|
93
+ t = t + period.length
94
+ }
95
+ name = @trackr.current.include?(task) ? task+' *' : task
96
+ puts name.ljust(15) << format_time(total,@config['relative_format'])
97
+ end
98
+
99
+ when 'log'
100
+ if args[0] == 'all' || args[0].nil?
101
+ tasks = @trackr.tasks
102
+ else
103
+ tasks = args
104
+ end
105
+ table = []
106
+ periods = tasks.each.collect{ |t| @trackr.history(t) }.flatten
107
+ lastday = nil
108
+ table << periods.sort{|x,y| x.start <=> y.start}.collect{ |period|
109
+ currday = period.start.strftime(@config['absolute_day'])
110
+ day = (currday == lastday) ? ' ' : currday
111
+ lastday = currday
112
+ name = period.current? ? period.task+' *' : period.task
113
+ start = period.start.strftime(@config['absolute_time'])
114
+ stop = period.current? ? ' ' : period.stop.strftime(@config['absolute_time'])
115
+ length = format_time(period.length, @config['relative_format'])
116
+ notes = period.notes
117
+ "#{day.ljust(12)} #{name.ljust(15)} #{start} - #{stop.ljust(5)} #{length} #{notes}"
118
+ }
119
+ puts table
120
+
121
+
122
+ when 'clear','delete','del'
123
+ tasks = args
124
+ tasks = @trackr.tasks if task == 'all'
125
+ tasks.each do |task|
126
+ @trackr.clear(task)
127
+ puts "Task '#{task}' cleared" if @verbose
128
+ end
129
+
130
+ when 'rename','mv'
131
+ from = args.shift
132
+ to = args.shift
133
+ @trackr.rename(from,to)
134
+ puts "Renamed '#{from}' to '#{to}'" if @verbose
135
+
136
+ when 'help'
137
+ show_help
138
+
139
+ else
140
+ puts "'#{cmd}' is not a valid command"
141
+ show_help
142
+ end
143
+
144
+ @trackr.close
145
+ end
146
+
147
+ protected
148
+
149
+ def format_time(time, fmt_str)
150
+ hours = time.to_i/3600.to_i
151
+ minutes = (time/60 - hours * 60).to_i
152
+ seconds = (time - (minutes * 60 + hours * 3600))
153
+ format(fmt_str,{
154
+ :hours => hours,
155
+ :minutes => minutes,
156
+ :seconds => seconds})
157
+ end
158
+
159
+ def show_help
160
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
161
+ puts "timetrackr version #{version}"
162
+ puts <<HELP
163
+
164
+ timetrackr [command] [options]
165
+
166
+ Available commands:
167
+
168
+ start [task] start a task
169
+ stop [task] stop a task (or 'all')
170
+ switch TASK switch tasks
171
+ time [task] show time for a task (or 'all')
172
+ log [task] show time log for a task (or 'all')
173
+
174
+ Global options
175
+ -h --help show this help
176
+ -v --verbose be noisy
177
+ HELP
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,103 @@
1
+
2
+ module TimeTrackr
3
+ class Database
4
+ def self.create(type, options={})
5
+ case type.to_s
6
+ when 'yaml'
7
+ begin
8
+ require 'yaml'
9
+ db = TimeTrackr::YamlDatabase.new(options['path'])
10
+ puts 'Loaded YAML tracker' if options['verbose']
11
+ rescue LoadError
12
+ puts 'YAML not found'
13
+ end
14
+
15
+ when 'sqlite'
16
+ begin
17
+ require 'sqlite3'
18
+ db = TimeTrackr::SqliteDatabase.new(options['path'])
19
+ puts 'Loaded sqlite tracker' if options['verbose']
20
+ rescue LoadError
21
+ puts 'Sqlite not found'
22
+ end
23
+
24
+ when 'json'
25
+ begin
26
+ require 'json'
27
+ db = TimeTrackr::JsonDatabase.new(options['path'])
28
+ puts 'Loaded JSON database' if options['verbose']
29
+ rescue LoadError
30
+ puts 'JSON not found'
31
+ end
32
+
33
+ else
34
+ raise "Bad log type: #{type}"
35
+ end
36
+ db
37
+ end
38
+
39
+ #
40
+ # return an array of current tasks
41
+ #
42
+ def current
43
+ raise 'Not Implemented'
44
+ end
45
+
46
+ #
47
+ # start a period with optional notes
48
+ #
49
+ def start(task,notes)
50
+ raise 'Not implemented'
51
+ end
52
+
53
+ #
54
+ # stop a period
55
+ #
56
+ def stop(task)
57
+ raise 'Not implemented'
58
+ end
59
+
60
+ #
61
+ # return an array of all tasks
62
+ #
63
+ def tasks
64
+ raise 'Not Implemented'
65
+ end
66
+
67
+ #
68
+ # time in task in seconds
69
+ #
70
+ def time(task)
71
+ raise 'Not implemented'
72
+ end
73
+
74
+ #
75
+ # get task history as an array of Periods
76
+ #
77
+ def history(task)
78
+ raise 'Not Implemented'
79
+ end
80
+
81
+ #
82
+ # rename a task
83
+ #
84
+ def rename(from, to)
85
+ raise 'Not implemented'
86
+ end
87
+
88
+ #
89
+ # clear an task
90
+ #
91
+ def clear(task)
92
+ raise 'Not Implemented'
93
+ end
94
+
95
+ #
96
+ # cleanup and close
97
+ #
98
+ def close
99
+ end
100
+
101
+ end
102
+ end
103
+
@@ -0,0 +1,68 @@
1
+ module TimeTrackr
2
+ class JsonDatabase < TimeTrackr::Database
3
+
4
+ def initialize(path)
5
+ @log_path = path
6
+ if !File.exist? @log_path
7
+ @db = {'current' => [], 'tasks' => {}}
8
+ write_file
9
+ end
10
+ File.open(@log_path,'r') do |fh|
11
+ @db = JSON.load(fh)
12
+ end
13
+ end
14
+
15
+ def current
16
+ @db['current']
17
+ end
18
+
19
+ def tasks
20
+ @db['tasks'].keys.compact.uniq || []
21
+ end
22
+
23
+ def start(task, notes)
24
+ @db['tasks'][task] = Array[] unless @db['tasks'][task]
25
+ if !@db['current'].include?(task)
26
+ @db['current'].unshift(task)
27
+ @db['tasks'][task].push({'start' => Time.now, 'notes' => notes})
28
+ end
29
+ end
30
+
31
+ def stop(task)
32
+ if @db['current'].include?(task)
33
+ @db['current'].delete(task)
34
+ @db['tasks'][task].last['stop'] = Time.now
35
+ end
36
+ end
37
+
38
+ def history(task, p_begin=nil, p_end=nil)
39
+ @db['tasks'][task].sort{|x,y| x['start'] <=> y['start']}.collect {|p|
40
+ Period.new(task,p['start'],p['stop'],p['notes'])
41
+ } unless !@db['tasks'].include? task
42
+ end
43
+
44
+ def rename(from, to)
45
+ @db['tasks'][to] = @db['tasks'].delete(from)
46
+ if @db['current'].delete(from)
47
+ @db['current'].unshift(to)
48
+ end
49
+ end
50
+
51
+ def close
52
+ write_file
53
+ end
54
+
55
+ def clear(task)
56
+ @db['current'].delete(task)
57
+ @db['tasks'].delete(task)
58
+ end
59
+
60
+ private
61
+
62
+ def write_file
63
+ File.open(@log_path,'w') do |fh|
64
+ JSON.dump(@db,fh)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -9,13 +9,22 @@ class Period
9
9
  @notes = notes
10
10
  end
11
11
 
12
+ def start
13
+ @start.class == Time ? @start : Time.parse(@start)
14
+ end
15
+
16
+ def stop
17
+ return nil if @stop.nil?
18
+ @stop.class == Time ? @stop : Time.parse(@stop)
19
+ end
20
+
12
21
  def length
13
- stop = @stop || Time.now
14
- stop - @start
22
+ stop = self.stop || Time.now
23
+ stop - self.start
15
24
  end
16
25
 
17
26
  def current?
18
- @stop.nil?
27
+ self.stop.nil?
19
28
  end
20
29
 
21
30
  end
@@ -1,70 +1,71 @@
1
- class SqliteTimeTrackr < TimeTrackr
1
+ module TimeTrackr
2
+ class SqliteDatabase < TimeTrackr::Database
2
3
 
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 (
4
+ def initialize(path)
5
+ @log_path = path
6
+ if !File.exist? @log_path
7
+ @db = SQLite3::Database.new(@log_path)
8
+ sql_events = "CREATE TABLE events (
8
9
  id INTEGER PRIMARY KEY,
9
10
  task TEXT,
10
11
  start TIME,
11
12
  stop TIME,
12
13
  notes TEXT);"
13
14
  @db.execute(sql_events)
14
- else
15
- @db = SQLite3::Database.open(@log_path)
15
+ else
16
+ @db = SQLite3::Database.open(@log_path)
17
+ end
18
+ @db.type_translation = true
16
19
  end
17
- @db.type_translation = true
18
- puts "Using DB file '#{@log_path}'" if $verbose
19
- end
20
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
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
27
 
28
- def tasks
29
- sql = "SELECT DISTINCT task FROM events;"
30
- @db.execute(sql).collect{ |row|
31
- row.first
32
- }
33
- end
28
+ def tasks
29
+ sql = "SELECT DISTINCT task FROM events;"
30
+ @db.execute(sql).collect{ |row|
31
+ row.first
32
+ }
33
+ end
34
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)
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
41
42
  end
42
- end
43
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)
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
50
51
  end
51
- end
52
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
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
59
 
60
- def rename(from, to)
61
- sql = "UPDATE events SET task = :to WHERE task = :from;"
62
- @db.execute(sql, 'to' => to, 'from' => from)
63
- end
60
+ def rename(from, to)
61
+ sql = "UPDATE events SET task = :to WHERE task = :from;"
62
+ @db.execute(sql, 'to' => to, 'from' => from)
63
+ end
64
64
 
65
- def clear(task)
66
- sql = "DELETE FROM events WHERE task = :task;"
67
- @db.execute(sql, 'task' => task)
68
- end
65
+ def clear(task)
66
+ sql = "DELETE FROM events WHERE task = :task;"
67
+ @db.execute(sql, 'task' => task)
68
+ end
69
69
 
70
+ end
70
71
  end
@@ -10,68 +10,69 @@
10
10
  # :notes: "blah blah blah"
11
11
  #
12
12
 
13
- class YamlTimeTrackr < TimeTrackr
13
+ module TimeTrackr
14
+ class YamlDatabase < TimeTrackr::Database
14
15
 
15
- def initialize(path)
16
- @log_path = path
17
- if !File.exist? @log_path
18
- @db = {:current => [], :tasks => {}}
19
- write_file
16
+ def initialize(path)
17
+ @log_path = path
18
+ if !File.exist? @log_path
19
+ @db = {:current => [], :tasks => {}}
20
+ write_file
21
+ end
22
+ @db = YAML.load_file(@log_path)
20
23
  end
21
- @db = YAML.load_file(@log_path)
22
- puts "Using log file '#{@log_path}'" if $verbose
23
- end
24
24
 
25
- def current
26
- @db[:current]
27
- end
25
+ def current
26
+ @db[:current]
27
+ end
28
28
 
29
- def tasks
30
- @db[:tasks].keys.compact.uniq || []
31
- end
29
+ def tasks
30
+ @db[:tasks].keys.compact.uniq || []
31
+ end
32
32
 
33
- def start(task, notes)
34
- @db[:tasks][task] = Array[] unless @db[:tasks][task]
35
- if !@db[:current].include?(task)
36
- @db[:current].unshift(task)
37
- @db[:tasks][task].push({:start => Time.now, :notes => notes})
33
+ def start(task, notes)
34
+ @db[:tasks][task] = Array[] unless @db[:tasks][task]
35
+ if !@db[:current].include?(task)
36
+ @db[:current].unshift(task)
37
+ @db[:tasks][task].push({:start => Time.now, :notes => notes})
38
+ end
38
39
  end
39
- end
40
40
 
41
- def stop(task)
42
- if @db[:current].include?(task)
43
- @db[:current].delete(task)
44
- @db[:tasks][task].last[:stop] = Time.now
41
+ def stop(task)
42
+ if @db[:current].include?(task)
43
+ @db[:current].delete(task)
44
+ @db[:tasks][task].last[:stop] = Time.now
45
+ end
45
46
  end
46
- end
47
47
 
48
- def history(task, p_begin=nil, p_end=nil)
49
- @db[:tasks][task].sort{|x,y| x[:start] <=> y[:start]}.collect {|p|
50
- Period.new(task,p[:start],p[:stop],p[:notes])
51
- } unless !@db[:tasks].include? task
52
- end
48
+ def history(task, p_begin=nil, p_end=nil)
49
+ @db[:tasks][task].sort{|x,y| x[:start] <=> y[:start]}.collect {|p|
50
+ Period.new(task,p[:start],p[:stop],p[:notes])
51
+ } unless !@db[:tasks].include? task
52
+ end
53
53
 
54
- def rename(from, to)
55
- @db[:tasks][to] = @db[:tasks].delete(from)
56
- if @db[:current].delete(from)
57
- @db[:current].unshift(to)
54
+ def rename(from, to)
55
+ @db[:tasks][to] = @db[:tasks].delete(from)
56
+ if @db[:current].delete(from)
57
+ @db[:current].unshift(to)
58
+ end
58
59
  end
59
- end
60
60
 
61
- def close
62
- write_file
63
- end
61
+ def close
62
+ write_file
63
+ end
64
64
 
65
- def clear(task)
66
- @db[:current].delete(task)
67
- @db[:tasks].delete(task)
68
- end
65
+ def clear(task)
66
+ @db[:current].delete(task)
67
+ @db[:tasks].delete(task)
68
+ end
69
69
 
70
- private
70
+ private
71
71
 
72
- def write_file
73
- File.open(@log_path,'w') do |fh|
74
- YAML.dump(@db,fh)
72
+ def write_file
73
+ File.open(@log_path,'w') do |fh|
74
+ YAML.dump(@db,fh)
75
+ end
75
76
  end
76
77
  end
77
78
  end
data/lib/timetrackr.rb CHANGED
@@ -1,92 +1,10 @@
1
- autoload 'YamlTimeTrackr', 'timetrackr/yaml'
2
- autoload 'SqliteTimeTrackr', 'timetrackr/sqlite'
3
- autoload 'Period', 'timetrackr/period'
1
+ module TimeTrackr
4
2
 
5
- class TimeTrackr
6
- def self.create(type, options={})
7
- case type.to_s
8
- when 'yaml'
9
- begin
10
- require 'yaml'
11
- log = YamlTimeTrackr.new(options['path'])
12
- puts 'Loaded yaml tracker' if $verbose
13
- rescue LoadError
14
- puts 'Yaml not found'
15
- end
16
- when 'sqlite'
17
- begin
18
- require 'sqlite3'
19
- log = SqliteTimeTrackr.new(options['path'])
20
- puts 'Loaded sqlite tracker' if $verbose
21
- rescue LoadError
22
- puts 'Sqlite not found'
23
- end
24
- else
25
- raise "Bad log type: #{type}"
26
- end
27
- log
28
- end
29
-
30
- #
31
- # return an array of current tasks
32
- #
33
- def current
34
- raise 'Not Implemented'
35
- end
36
-
37
- #
38
- # start a period with optional notes
39
- #
40
- def start(task,notes)
41
- raise 'Not implemented'
42
- end
43
-
44
- #
45
- # stop a period
46
- #
47
- def stop(task)
48
- raise 'Not implemented'
49
- end
50
-
51
- #
52
- # return an array of all tasks
53
- #
54
- def tasks
55
- raise 'Not Implemented'
56
- end
57
-
58
- #
59
- # time in task in seconds
60
- #
61
- def time(task)
62
- end
63
-
64
- #
65
- # get task history as an array of Periods
66
- #
67
- def history(task)
68
- raise 'Not Implemented'
69
- end
70
-
71
- #
72
- # rename a task
73
- #
74
- def rename(from, to)
75
- raise 'Not implemented'
76
- end
77
-
78
- #
79
- # clear an task
80
- #
81
- def clear(task)
82
- raise 'Not Implemented'
83
- end
84
-
85
- #
86
- # cleanup and close
87
- #
88
- def close
89
- end
3
+ require 'time'
4
+ require 'timetrackr/database'
5
+ require 'timetrackr/period'
90
6
 
7
+ autoload 'YamlDatabase', 'timetrackr/yaml'
8
+ autoload 'SqliteDatabase', 'timetrackr/sqlite'
9
+ autoload 'JsonDatabase', 'timetrackr/json'
91
10
  end
92
-
data/timetrackr.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{timetrackr}
8
- s.version = "0.1.4"
8
+ s.version = "0.1.5"
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"]
12
- s.date = %q{2011-05-19}
12
+ s.date = %q{2011-05-23}
13
13
  s.default_executable = %q{timetrackr}
14
14
  s.description = %q{A simple time tracking utility}
15
15
  s.email = %q{felix@seconddrawer.com.au}
@@ -27,6 +27,9 @@ Gem::Specification.new do |s|
27
27
  "VERSION",
28
28
  "bin/timetrackr",
29
29
  "lib/timetrackr.rb",
30
+ "lib/timetrackr/cli.rb",
31
+ "lib/timetrackr/database.rb",
32
+ "lib/timetrackr/json.rb",
30
33
  "lib/timetrackr/period.rb",
31
34
  "lib/timetrackr/sqlite.rb",
32
35
  "lib/timetrackr/yaml.rb",
@@ -49,12 +52,14 @@ Gem::Specification.new do |s|
49
52
 
50
53
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
51
54
  s.add_development_dependency(%q<sqlite3>, [">= 0"])
55
+ s.add_development_dependency(%q<json>, [">= 0"])
52
56
  s.add_development_dependency(%q<shoulda>, [">= 0"])
53
57
  s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
54
58
  s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
55
59
  s.add_development_dependency(%q<rcov>, [">= 0"])
56
60
  else
57
61
  s.add_dependency(%q<sqlite3>, [">= 0"])
62
+ s.add_dependency(%q<json>, [">= 0"])
58
63
  s.add_dependency(%q<shoulda>, [">= 0"])
59
64
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
60
65
  s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
@@ -62,6 +67,7 @@ Gem::Specification.new do |s|
62
67
  end
63
68
  else
64
69
  s.add_dependency(%q<sqlite3>, [">= 0"])
70
+ s.add_dependency(%q<json>, [">= 0"])
65
71
  s.add_dependency(%q<shoulda>, [">= 0"])
66
72
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
67
73
  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.4
5
+ version: 0.1.5
6
6
  platform: ruby
7
7
  authors:
8
8
  - Felix Hanley
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-05-19 00:00:00 +07:00
13
+ date: 2011-05-23 00:00:00 +07:00
14
14
  default_executable: timetrackr
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -25,7 +25,7 @@ dependencies:
25
25
  prerelease: false
26
26
  version_requirements: *id001
27
27
  - !ruby/object:Gem::Dependency
28
- name: shoulda
28
+ name: json
29
29
  requirement: &id002 !ruby/object:Gem::Requirement
30
30
  none: false
31
31
  requirements:
@@ -36,8 +36,19 @@ dependencies:
36
36
  prerelease: false
37
37
  version_requirements: *id002
38
38
  - !ruby/object:Gem::Dependency
39
- name: bundler
39
+ name: shoulda
40
40
  requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ type: :development
47
+ prerelease: false
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: bundler
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.0.0
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: jeweler
51
- requirement: &id004 !ruby/object:Gem::Requirement
62
+ requirement: &id005 !ruby/object:Gem::Requirement
52
63
  none: false
53
64
  requirements:
54
65
  - - ~>
@@ -56,10 +67,10 @@ dependencies:
56
67
  version: 1.5.2
57
68
  type: :development
58
69
  prerelease: false
59
- version_requirements: *id004
70
+ version_requirements: *id005
60
71
  - !ruby/object:Gem::Dependency
61
72
  name: rcov
62
- requirement: &id005 !ruby/object:Gem::Requirement
73
+ requirement: &id006 !ruby/object:Gem::Requirement
63
74
  none: false
64
75
  requirements:
65
76
  - - ">="
@@ -67,7 +78,7 @@ dependencies:
67
78
  version: "0"
68
79
  type: :development
69
80
  prerelease: false
70
- version_requirements: *id005
81
+ version_requirements: *id006
71
82
  description: A simple time tracking utility
72
83
  email: felix@seconddrawer.com.au
73
84
  executables:
@@ -86,6 +97,9 @@ files:
86
97
  - VERSION
87
98
  - bin/timetrackr
88
99
  - lib/timetrackr.rb
100
+ - lib/timetrackr/cli.rb
101
+ - lib/timetrackr/database.rb
102
+ - lib/timetrackr/json.rb
89
103
  - lib/timetrackr/period.rb
90
104
  - lib/timetrackr/sqlite.rb
91
105
  - lib/timetrackr/yaml.rb
@@ -106,7 +120,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
106
120
  requirements:
107
121
  - - ">="
108
122
  - !ruby/object:Gem::Version
109
- hash: 317875573549099611
123
+ hash: -2538018453800347300
110
124
  segments:
111
125
  - 0
112
126
  version: "0"