summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfelix <felix@seconddrawer.com.au>2011-05-16 06:22:58 +0000
committerfelix <felix@seconddrawer.com.au>2011-05-16 06:22:58 +0000
commit19f74bb93f68f8e42dd4d7e02d78c214ff9fcdf7 (patch)
treee9c0bc726f8821f33f27be0e183463eb430b1974
parent9d6d2cedf898829a2b9b577964184268e4e79717 (diff)
downloadtimetrackr-19f74bb93f68f8e42dd4d7e02d78c214ff9fcdf7.tar.gz
timetrackr-19f74bb93f68f8e42dd4d7e02d78c214ff9fcdf7.tar.bz2
YAML backend working??
-rw-r--r--Gemfile3
-rw-r--r--README28
-rwxr-xr-xbin/timetrackr115
-rw-r--r--lib/timetrackr.rb103
-rw-r--r--lib/timetrackr/eventlog.rb62
-rw-r--r--lib/timetrackr/yaml.rb (renamed from lib/timetrackr/eventlog/yaml.rb)45
-rw-r--r--timetrackr.gemspec65
7 files changed, 243 insertions, 178 deletions
diff --git a/Gemfile b/Gemfile
index 5293566..a76021a 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,8 +1,5 @@
source 'http://rubygems.org'
-gem 'trollop'
-gem 'sqlite3'
-
group :development do
gem 'shoulda', '>= 0'
gem 'bundler', '~> 1.0.0'
diff --git a/README b/README
index cb82e82..61e63e7 100644
--- a/README
+++ b/README
@@ -16,11 +16,10 @@ A simple CLI time tracking utility.
## timetracker start [task]
-- Start a new task 'task' at this time
-- (config) If another task is already running, make 'task' a subtask of that
+- Start a new task 'task' at this time, 'default' if name not provided
- (config) Stop all other tasks and start 'task'
-## tt switch [task]
+## tt switch <task>
- Stop all other tasks and start 'task'
@@ -29,30 +28,15 @@ A simple CLI time tracking utility.
- Stop 'task' if provided
- Stop all tasks if no 'task'
-## tt list
+## tt time
-- List tasks
+- List tasks and their times
-## tt export [task]
+## tt clear [task]
-- Export 'task'
-- Export all tasks if no 'task'
-
-
-## tt del [task]
-
-- Delete 'task'
-
-## tt query [task] [from date] [until date] [in hours|days|weeks] [by day|week|month]
-
-- Produce report of time spent for 'task'
-- Use task 'all' for global report
-
- tt query all from 2011-05-03 in hours by week
+- Delete 'task' or 'all'
## Copyright
Copyright (c) 2011 Felix Hanley. See LICENSE.txt for
further details.
-
-> vim: set ts=4 sw=2 tw=80 ft=markdown fdm=syntax et fen :
diff --git a/bin/timetrackr b/bin/timetrackr
index 6eed2de..919e714 100755
--- a/bin/timetrackr
+++ b/bin/timetrackr
@@ -4,8 +4,42 @@ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
require 'timetrackr'
-def help
- puts "Help!"
+DEFAULTS = {
+ :backend => 'yaml',
+ :verbose => false,
+ :single_task => false,
+ :time_format => "% -30<task>s %2<hours>dh %2<minutes>dm %2<seconds>ds"
+}
+
+def show_help
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
+ puts "timetrackr version #{version}"
+ puts <<HELP
+
+ timetrackr [command] [options]
+
+ Available commands:
+
+ start [task] start a task (default is 'default')
+ stop TASK stop a task ('all' for all)
+ switch TASK switch tasks
+ time [task] show time for a task ('all' for all)
+
+ Global options
+ -h --help show this help
+ -v --verbose be noisy
+HELP
+end
+
+def format_time(task, time, fmt_str)
+ hours = time.to_i/3600.to_i
+ minutes = (time/60 - hours * 60).to_i
+ seconds = (time - (minutes * 60 + hours * 3600))
+ format(fmt_str,{
+ :task => task,
+ :hours => hours,
+ :minutes => minutes,
+ :seconds => seconds})
end
config = {}
@@ -21,39 +55,80 @@ while (cmd = ARGV.shift) && cmd.start_with?('-')
config[:verbose] = true
end
if ['-h','--help'].include? cmd
- help
+ show_help
+ exit 1
end
end
-if config[:verbose]
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
- puts "timetrackr version #{version}"
-end
-
+config = DEFAULTS.merge(config || {})
+$verbose = config[:verbose]
+trackr = TimeTracker.create(config[:backend])
-trackr = TimeTrackr.new(config)
+#
+# commands
+#
case cmd
when 'start','s'
- trackr.start(ARGV)
+ task = ARGV.shift
+ details = {:action => 'start', :notes => ARGV.join(' ')}
+ if config[:single_task] && trackr.current != task
+ trackr.current.each do |t|
+ trackr.event(t,Time.now,details.merge(:action => 'stop')) unless t == task
+ end
+ trackr.event(task,Time.now,details)
+ puts "Switched to task '#{task}'" if $verbose
+ else
+ trackr.event(task,Time.now, details)
+ puts "Started task '#{task}'" if $verbose
+ end
when 'stop','k'
- trackr.stop(ARGV)
-
-when 'status','current'
- trackr.current
+ task = ARGV.shift
+ tasks = []
+ details = {:action => 'stop', :notes => ARGV.join(' ')}
+ tasks = [*task] if trackr.current.include?(task)
+ tasks = trackr.tasks if task == 'all'
+ tasks.each do |task|
+ trackr.event(task, Time.now, details)
+ puts "Stopped task '#{task}'" if $verbose
+ end
when 'switch','sw'
- trackr.switch(ARGV)
+ task = ARGV.shift
+ details = {:notes => ARGV.join(' ')}
+ trackr.current.each do |t|
+ trackr.event(t, Time.now, details.merge(:action => 'stop')) unless t == task
+ end
+ trackr.event(task, Time.now, details.merge(:action => 'start'))
+ puts "Switched to task '#{task}'" if $verbose
-when 'time'
- puts trackr.time(ARGV[0])
+when 'time', nil
+ task = ARGV.shift
+ if task && trackr.tasks.include?(task)
+ tasks = [*task]
+ else
+ tasks = trackr.tasks.each
+ end
+ tasks.each do |task|
+ name = trackr.current.include?(task) ? task+' *' : task
+ puts format_time(name, trackr.time(task),config[:time_format])
+ end
when 'clear'
- trackr.clear(ARGV[0])
+ task = ARGV.shift
+ tasks = []
+ tasks = [*task] if trackr.current.include?(task)
+ tasks = trackr.tasks if task == 'all'
+ tasks.each do |task|
+ trackr.clear(task)
+ puts "Task '#{task}' cleared" if $verbose
+ end
else
- help
+ puts "'#{cmd}' is not a valid command"
+ show_help
end
-trackr.exit
+
+trackr.close
# vim: set ts=2 sw=2 tw=80 ft=ruby fdm=syntax et fen :
diff --git a/lib/timetrackr.rb b/lib/timetrackr.rb
index f4b9d78..cf64d5c 100644
--- a/lib/timetrackr.rb
+++ b/lib/timetrackr.rb
@@ -1,65 +1,72 @@
-require 'timetrackr/eventlog'
+autoload 'YamlTimeTracker', 'timetrackr/yaml'
+autoload 'SqliteTimeTracker', 'timetrackr/sqlite'
-DEFAULTS = {
- :backend => 'yaml',
- :single_task => false,
- :time_format => "%02dh %02dm %02ds"
-}
-
-class TimeTrackr
-
- def initialize(config)
- @config = DEFAULTS.merge(config || {})
- $verbose = @config[:verbose]
- @log = EventLog.create(@config[:backend])
+class TimeTracker
+ def self.create(type,options={})
+ case type.to_s
+ when 'yaml'
+ begin
+ require 'yaml'
+ log = YamlTimeTracker.new(options)
+ puts 'Loaded yaml tracker' if $verbose
+ rescue LoadError
+ puts 'Yaml not found'
+ end
+ when 'sqlite'
+ begin
+ require 'sqlite3'
+ log = SqliteTimeTracker.new(options)
+ puts 'Loaded sqlite tracker' if $verbose
+ rescue LoadError
+ puts 'Sqlite not found'
+ end
+ else
+ raise "Bad log type: #{type}"
+ end
+ log
end
- def start(args=[])
- return switch(args) if @config[:single_task] && @log.current
-
- task = args.shift
- notes = args.join(' ')
- puts "Starting task '#{task}'" if $verbose
- @log.event(Time.now, 'start', task, notes)
+ #
+ # return an array of current tasks
+ #
+ def current
+ raise 'Not Implemented'
end
- def stop(args=[])
- task = args.shift
- if @log.current.include? task
- notes = args.join(' ')
- puts "Stopping task '#{task}'" if $verbose
- @log.event(Time.now, 'stop', task, notes)
- end
+ #
+ # return an array of all tasks
+ #
+ def tasks
+ raise 'Not Implemented'
end
- def switch(args=[])
- task = args.shift
- notes = args.join(' ')
- puts "Switching to task '#{task}'" if $verbose
- @log.current.each do |t|
- @log.event(Time.now,'stop',t, notes) unless t == task
- end
- @log.event(Time.now,'start',task, notes)
+ #
+ # time in task in seconds
+ # only considers 'start' and 'stop' events
+ #
+ def time(task)
+ raise 'Not Implemented'
end
- def time(task)
- time = @log.time(task).to_i
- hours = time/3600.to_i
- minutes = (time/60 - hours * 60).to_i
- seconds = (time - (minutes * 60 + hours * 3600))
- format(@config[:time_format], hours, minutes, seconds)
+ #
+ # write an event
+ #
+ def event(task, time=Time.now, details={})
+ raise 'Not Implemented'
end
+ #
+ # clear an event
+ #
def clear(task)
- puts "Clearing task '#{task}'" if $verbose
- @log.clear(task)
+ raise 'Not Implemented'
end
- def current
- puts @log.current.inspect
+ #
+ # cleanup and close
+ #
+ def close
end
- def exit
- @log.close
- end
end
+
diff --git a/lib/timetrackr/eventlog.rb b/lib/timetrackr/eventlog.rb
deleted file mode 100644
index 5968adf..0000000
--- a/lib/timetrackr/eventlog.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-autoload 'YamlEventLog', 'timetrackr/eventlog/yaml'
-autoload 'SqliteEventLog', 'timetrackr/eventlog/sqlite'
-
-class EventLog
- def self.create(type,options={})
- case type.to_s
- when 'yaml'
- begin
- require 'yaml'
- puts 'Loading yaml eventlog' if $verbose
- YamlEventLog.new(options)
- rescue LoadError
- puts 'Yaml not found'
- end
- when 'sqlite'
- begin
- require 'sqlite3'
- puts 'Loading sqlite eventlog' if $verbose
- SqliteEventLog.new(options)
- rescue LoadError
- puts 'Sqlite not found'
- end
- else
- raise "Bad log type: #{type}"
- end
- end
-
- #
- # return the list of current tasks
- #
- def current
- raise 'Not Implemented'
- end
-
- #
- # time in task
- #
- def time(task)
- raise 'Not Implemented'
- end
-
- #
- # write an event
- #
- def event(time, action, task='default', notes='')
- raise 'Not Implemented'
- end
-
- #
- # clear an event
- #
- def clear(task)
- raise 'Not Implemented'
- end
-
- #
- # cleanup and close
- #
- def close
- end
-
-end
diff --git a/lib/timetrackr/eventlog/yaml.rb b/lib/timetrackr/yaml.rb
index 1c5ed3c..f9dc6cb 100644
--- a/lib/timetrackr/eventlog/yaml.rb
+++ b/lib/timetrackr/yaml.rb
@@ -1,23 +1,27 @@
-class YamlEventLog < EventLog
+class YamlTimeTracker < TimeTracker
def initialize(options)
@log_path = options[:log_path] || File.join(ENV['HOME'],'.timetrackr.db')
- puts "Using log file '#{@log_path}'" if $verbose
if File.exist? @log_path
@db = YAML.load_file(@log_path)
else
- @db = {:current => []}
+ @db = {:current => [], :tasks => {}}
end
+ puts "Using log file '#{@log_path}'" if $verbose
end
def current
@db[:current]
end
+ def tasks
+ @db[:tasks].keys.compact.uniq || []
+ end
+
def time(task)
total = 0 # seconds
split = nil
- @db[task].each do |event|
+ @db[:tasks][task].sort{|x,y| x[:time] <=> y[:time]}.each do |event|
if event[:action] == 'start'
split = event[:time]
end
@@ -27,29 +31,23 @@ class YamlEventLog < EventLog
end
end
total = (total + (Time.now - split)) if split
- total
+ total.to_i
end
- def event(time, action, task='default', notes='')
-
- if action.to_s == 'stop' && @db[:current].delete(task)
- write_event({:task => task,
- :time => time,
- :action => action,
- :notes => notes })
+ def event(task='default', time=Time.now, details={})
+ details[:action] ||= 'start'
+ details[:time] = time
+ if details[:action].to_s == 'stop' && @db[:current].delete(task)
+ write_event(task,details)
end
- if action.to_s == 'start' && !@db[:current].include?(task)
+ if details[:action].to_s == 'start' && !@db[:current].include?(task)
@db[:current].unshift(task)
- write_event({:task => task,
- :time => time,
- :action => action,
- :notes => notes })
+ write_event(task,details)
end
end
def close
- puts "Closing #{@log_path}" if $verbose
File.open(@log_path,'w') do |fh|
YAML.dump(@db,fh)
end
@@ -57,16 +55,17 @@ class YamlEventLog < EventLog
def clear(task)
@db[:current].delete(task)
- @db.delete(task)
+ @db[:tasks].delete(task)
end
private
- def write_event(details)
- if @db[details[:task]]
- @db[details[:task]].push(details)
+ def write_event(task, details)
+ @db[:tasks] = {} unless @db[:tasks]
+ if @db[:tasks][task]
+ @db[:tasks][task].push(details)
else
- @db[details[:task]] = Array[details]
+ @db[:tasks][task] = Array[details]
end
end
end
diff --git a/timetrackr.gemspec b/timetrackr.gemspec
new file mode 100644
index 0000000..653359b
--- /dev/null
+++ b/timetrackr.gemspec
@@ -0,0 +1,65 @@
+# Generated by jeweler
+# DO NOT EDIT THIS FILE DIRECTLY
+# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+ s.name = %q{timetrackr}
+ s.version = "0.1.0"
+
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+ s.authors = ["felix"]
+ s.date = %q{2011-05-14}
+ s.default_executable = %q{timetrackr}
+ s.description = %q{A simple time tracking utility}
+ s.email = %q{felix@seconddrawer.com.au}
+ s.executables = ["timetrackr"]
+ s.extra_rdoc_files = [
+ "LICENSE.txt",
+ "README"
+ ]
+ s.files = [
+ "Gemfile",
+ "LICENSE.txt",
+ "README",
+ "Rakefile",
+ "VERSION",
+ "bin/timetrackr",
+ "lib/timetrackr.rb",
+ "lib/timetrackr/eventlog.rb",
+ "lib/timetrackr/eventlog/yaml.rb",
+ "test/helper.rb",
+ "test/test_timetrackr.rb"
+ ]
+ s.homepage = %q{http://github.com/felix/timetrackr}
+ s.licenses = ["MIT"]
+ s.require_paths = ["lib"]
+ s.rubygems_version = %q{1.6.2}
+ s.summary = %q{A simple time tracking utility}
+ s.test_files = [
+ "test/helper.rb",
+ "test/test_timetrackr.rb"
+ ]
+
+ if s.respond_to? :specification_version then
+ s.specification_version = 3
+
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
+ s.add_development_dependency(%q<rcov>, [">= 0"])
+ else
+ s.add_dependency(%q<shoulda>, [">= 0"])
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
+ s.add_dependency(%q<rcov>, [">= 0"])
+ end
+ else
+ s.add_dependency(%q<shoulda>, [">= 0"])
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
+ s.add_dependency(%q<rcov>, [">= 0"])
+ end
+end
+