Created
May 28, 2009 04:14
-
-
Save justinperkins/119085 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
require 'rubygems' | |
=begin | |
TimeTracker is a simple way to track time on tasks | |
Start tracking time: ./time_tracker.rb -s some-task-name | |
Stop tracking time: ./time_tracker.rb -f | |
View recorded time for a task: ./time_tracker.rb -h some-task-name | |
View all tasks and their | |
respective recorded time: ./time_tracker.rb -l | |
Add a task: ./time_tracker.rb -a some-task-name | |
Remove a task: ./time_tracker.rb -d some-task-name | |
Reset recorded time for a task: ./time_tracker.rb -r some-task-name | |
Test everything: ./time_tracker.rb -t | |
=end | |
class TimeTracker | |
DATA = '~/bin/.time_tracker.yml' | |
def initialize | |
@data = File.open(File.expand_path(DATA)) { |f| YAML::load(f) } rescue nil | |
@data ||= {} | |
@data[:tasks] ||= [] | |
end | |
def persist | |
File.open(File.expand_path(DATA), 'w+') { |f| f.write(@data.to_yaml) } | |
end | |
def list_tasks | |
puts('no tasks') and return if @data[:tasks].empty? | |
results = @data[:tasks].inject([]) do |memo, task| | |
memo << "#{ task } (#{ hours_for(task) })" | |
memo | |
end | |
puts results.join(', ') | |
end | |
def add_task(task) | |
puts('task already exists') || return if @data[:tasks].include?(task) | |
@data[:tasks] << task | |
puts "added task #{ task }" | |
end | |
def remove_task(task) | |
deleted = @data[:tasks].delete(task) | |
puts "deleted #{ deleted || 'nothing' }" | |
end | |
def start_timer(task) | |
add_task(task) unless @data[:tasks].include?(task) | |
active = @data[:active_timer] | |
finish_timer if active | |
@data[:active_timer] = {:start => Time.now, :task => task} | |
puts "started timer for #{ task }" | |
end | |
def finish_timer | |
active = @data[:active_timer] | |
puts('no timer running') || return unless active | |
task = active[:task] | |
start = active[:start] | |
stop = Time.now | |
puts "stopping timer for #{ active[:task] }" | |
difference_in_seconds = stop - start | |
difference_in_hours = difference_in_seconds / 60 / 60 | |
difference_in_hours_rounded = round_hours(difference_in_hours) | |
@data[:recorded_time] ||= {} | |
@data[:recorded_time][task.to_sym] ||= 0 | |
@data[:recorded_time][task.to_sym] = round_hours(@data[:recorded_time][task.to_sym] + difference_in_hours_rounded) | |
puts "worked #{ difference_in_hours_rounded } hours, for a total of #{ @data[:recorded_time][task.to_sym] }" | |
@data.delete(:active_timer) | |
end | |
def hours_for(task) | |
@data[:recorded_time] ||= {} | |
@data[:recorded_time][task.to_sym] || 0 | |
end | |
def reset_recorded_hours(task) | |
@data[:recorded_time].delete(task.to_sym) if @data[:recorded_time][task.to_sym] | |
end | |
def test | |
# make sure time calculations are working correctly, that's sorta important | |
# keep track of an active task if it's running, too clever? | |
running_task = @data[:active_timer][:task] if active? | |
test_task = 'test-task' | |
# reset the recorded time to zero if we have anything | |
reset_recorded_hours(test_task) | |
# make sure we get the zero we're expecting since we just reset the recorded hours for this task | |
puts_failure "hours for test task should be zero" if hours_for(test_task) != 0 | |
# kick off a timer | |
start_timer(test_task) | |
# make sure the correct task has started | |
puts_failure "task is not correct" if @data[:active_timer][:task] != test_task | |
# hack the start time to something in the past | |
five_hours_ago = Time.now - (60*60*5) | |
@data[:active_timer][:start] = five_hours_ago | |
puts '*** imagine 5 hours has lapsed ***' | |
finish_timer | |
puts_failure 'task was not stopped like expected' if @data[:active_timer] | |
puts_failure 'recorded hours not correct, expected 5' if hours_for(test_task).to_i != 5 | |
# start again, gonna verify hours are added up correctly | |
start_timer(test_task) | |
# hack the start time once again | |
three_hours_ago = Time.now - (60*60*3) | |
@data[:active_timer][:start] = three_hours_ago | |
puts '*** imagine 3 hours has lapsed ***' | |
finish_timer | |
puts_failure 'recorded hours not correct, expected 8' if hours_for(test_task).to_i != 8 | |
reset_recorded_hours(test_task) | |
puts_failure 'hours for test task should be zero' if hours_for(test_task) != 0 | |
remove_task(test_task) | |
# restart the previously running task if we've got one | |
start_timer(running_task) if running_task | |
end | |
def active? | |
!@data[:active_timer].nil? | |
end | |
private | |
def puts_failure(string) | |
puts "TEST FAILURE: #{ string }" | |
end | |
def round_hours(hours) | |
(hours * 10 ** 2).round.to_f / 10 ** 2 | |
end | |
end | |
if $0 == __FILE__ | |
time = TimeTracker.new | |
command = ARGV.first | |
if %w{ -a -d -s -h -r }.include?(command) | |
task = ARGV[1] | |
unless task | |
puts 'expected task name' | |
exit(0) | |
end | |
end | |
case command | |
when '-l' | |
time.list_tasks | |
when '-a' | |
time.add_task(task) | |
when '-d' | |
time.remove_task(task) | |
when '-s' | |
time.start_timer(task) | |
when '-h' | |
puts "total hours for task #{ task } is: #{ time.hours_for(task) }" | |
when '-f' | |
time.finish_timer | |
when '-r' | |
time.reset_recorded_hours(task) | |
when '-t' | |
time.test | |
else | |
puts 'nothing to do, use -l (list), -a [task name] (add task), -d [task name] (delete task), -s [task name] (start timer for task), -h [task name] (recorded hours for task), -f (finish active timer), -r [task name] (reset recorded hours for given task), -t (test logic)' | |
end | |
time.persist | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment