diff options
| author | felix <felix@seconddrawer.com.au> | 2011-05-16 06:22:58 +0000 |
|---|---|---|
| committer | felix <felix@seconddrawer.com.au> | 2011-05-16 06:22:58 +0000 |
| commit | 19f74bb93f68f8e42dd4d7e02d78c214ff9fcdf7 (patch) | |
| tree | e9c0bc726f8821f33f27be0e183463eb430b1974 | |
| parent | 9d6d2cedf898829a2b9b577964184268e4e79717 (diff) | |
| download | timetrackr-19f74bb93f68f8e42dd4d7e02d78c214ff9fcdf7.tar.gz timetrackr-19f74bb93f68f8e42dd4d7e02d78c214ff9fcdf7.tar.bz2 | |
YAML backend working??
| -rw-r--r-- | Gemfile | 3 | ||||
| -rw-r--r-- | README | 28 | ||||
| -rwxr-xr-x | bin/timetrackr | 115 | ||||
| -rw-r--r-- | lib/timetrackr.rb | 103 | ||||
| -rw-r--r-- | lib/timetrackr/eventlog.rb | 62 | ||||
| -rw-r--r-- | lib/timetrackr/yaml.rb (renamed from lib/timetrackr/eventlog/yaml.rb) | 45 | ||||
| -rw-r--r-- | timetrackr.gemspec | 65 |
7 files changed, 243 insertions, 178 deletions
@@ -1,8 +1,5 @@ source 'http://rubygems.org' -gem 'trollop' -gem 'sqlite3' - group :development do gem 'shoulda', '>= 0' gem 'bundler', '~> 1.0.0' @@ -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 + |
