lib/timetrap/cli.rb in timetrap-1.5.3 vs lib/timetrap/cli.rb in timetrap-1.6.0

- old
+ new

@@ -62,36 +62,38 @@ -i, --id <id:i> Alter entry with id <id> instead of the running entry * list - Show the available timesheets. usage: t list - * now - Show the status of the current timesheet. + * now - Show all running entries. usage: t now - * out - Stop the timer for the current timesheet. - usage: t out [--at TIME] + * out - Stop the timer for the a timesheet. + usage: t out [--at TIME] [TIMESHEET] -a, --at <time:qs> Use this time instead of now - * running - Show all running timesheets. - usage: t running + * running - Deprecated: alias for now. - * switch - Switch to a new timesheet. - usage: t switch TIMESHEET + * sheet - Switch to a timesheet creating it if necessary. + usage: t sheet TIMESHEET + * switch - Deprecated: renamed to sheet. + * week - Shortcut for display with start date set to monday of this week. usage: t week [--ids] [--end DATE] [--format FMT] [SHEET | all] OTHER OPTIONS -h, --help Display this help. -r, --round Round output to 15 minute start and end times. -y, --yes Noninteractive, assume yes as answer to all prompts. + --debug Display stack traces for errors. EXAMPLES # create the "MyTimesheet" timesheet - $ t switch MyTimesheet + $ t sheet MyTimesheet # check in 5 minutes ago with a note $ t in --at '5 minutes ago' doing some stuff # check out @@ -106,33 +108,32 @@ def parse arguments args.parse arguments end def invoke - args['-h'] ? say(USAGE) : invoke_command_if_valid + args['-h'] ? puts(USAGE) : invoke_command_if_valid rescue => e - say e.message - exit 1 + raise e if args['--debug'] + warn e.message + exit 1 unless defined? TEST_MODE end def commands Timetrap::CLI::USAGE.scan(/\* \w+/).map{|s| s.gsub(/\* /, '')} end - def say *something - puts *something - end - def invoke_command_if_valid command = args.unused.shift set_global_options case (valid = commands.select{|name| name =~ %r|^#{command}|}).size - when 0 then say "Invalid command: #{command}" - when 1 then send valid[0] + when 0 then puts "Invalid command: #{command}" else - say "Ambiguous command: #{command}" if command - say(USAGE) + if command + send valid[0] + else + puts USAGE + end end end # currently just sets whether output should be rounded to 15 min intervals def set_global_options @@ -145,29 +146,34 @@ ee.all.each do |e| next unless e.end e.update :sheet => "_#{e.sheet}" end else - say "archive aborted!" + warn "archive aborted!" end end def configure Config.configure! - say "Config file is at #{Config::PATH.inspect}" + puts "Config file is at #{Config::PATH.inspect}" end def edit - entry = args['-i'] ? Entry[args['-i']] : Timetrap.active_entry - say "can't find entry" && return unless entry + entry = args['-i'] ? Entry[args['-i']] : Timer.active_entry + unless entry + warn "can't find entry" + return + else + warn "editing entry ##{entry.id.inspect}" + end entry.update :start => args['-s'] if args['-s'] =~ /.+/ entry.update :end => args['-e'] if args['-e'] =~ /.+/ # update sheet if args['-m'] =~ /.+/ - if entry == Timetrap.active_entry - Timetrap.current_sheet = args['-m'] + if entry == Timer.active_entry + Timer.current_sheet = args['-m'] end entry.update :sheet => args['-m'] end # update notes @@ -183,38 +189,44 @@ def backend exec "sqlite3 #{DB_NAME}" end def in - Timetrap.start unused_args, args['-a'] + Timer.start unused_args, args['-a'] + warn "Checked into sheet #{Timer.current_sheet.inspect}." end def out - Timetrap.stop args['-a'] + sheet = sheet_name_from_string(unused_args) + if Timer.stop sheet, args['-a'] + warn "Checked out of sheet #{sheet.inspect}." + else + warn "No running entry on sheet #{sheet.inspect}." + end end def kill if e = Entry[args['-i']] out = "are you sure you want to delete entry #{e.id}? " out << "(#{e.note}) " if e.note.to_s =~ /.+/ if ask_user out e.destroy - say "it's dead" + warn "it's dead" else - say "will not kill" + warn "will not kill" end elsif (sheets = Entry.map{|e| e.sheet }.uniq).include?(sheet = unused_args) victims = Entry.filter(:sheet => sheet).count if ask_user "are you sure you want to delete #{victims} entries on sheet #{sheet.inspect}? " - Timetrap.kill_sheet sheet - say "killed #{victims} entries" + Entry.filter(:sheet => sheet).destroy + warn "killed #{victims} entries" else - say "will not kill" + warn "will not kill" end else victim = args['-i'] ? args['-i'].to_s.inspect : sheet.inspect - say "can't find #{victim} to kill", 'sheets:', *sheets + warn "can't find #{victim} to kill", 'sheets:', *sheets end end def display begin @@ -222,64 +234,77 @@ Timetrap::Formatters.const_get("#{args['-f'].classify}") else Timetrap::Formatters::Text end rescue - say "Invalid format specified `#{args['-f']}'" + warn "Invalid format #{args['-f'].inspect} specified." return end - say Timetrap.format(fmt_klass, selected_entries.order(:start).all) + entries = selected_entries.order(:start).all + if entries == [] + warn "No entries were selected to display." + else + puts fmt_klass.new(entries).output + end end alias_method :format, :display - - def switch + def sheet sheet = unused_args - if not sheet =~ /.+/ then say "No sheet specified"; return end - say "Switching to sheet " + Timetrap.switch(sheet) + unless sheet =~ /.+/ + warn "No sheet specified" + else + Timer.current_sheet = sheet + warn "Switching to sheet #{sheet.inspect}" + end end + alias_method :switch, :sheet + def list - sheets = Entry.sheets.map do |sheet| + sheets = ([Timer.current_sheet] | Entry.sheets).map do |sheet| sheet_atts = {:total => 0, :running => 0, :today => 0} - Timetrap::Entry.filter(:sheet => sheet).inject(sheet_atts) do |m, e| - e_end = e.end_or_now - m[:name] ||= sheet - m[:total] += (e_end.to_i - e.start.to_i) - m[:running] += (e_end.to_i - e.start.to_i) unless e.end - m[:today] += (e_end.to_i - e.start.to_i) if same_day?(Time.now, e.start) - m + entries = Timetrap::Entry.filter(:sheet => sheet) + if entries.empty? + sheet_atts.merge(:name => sheet) + else + entries.inject(sheet_atts) do |m, e| + e_end = e.end_or_now + m[:name] ||= sheet + m[:total] += (e_end.to_i - e.start.to_i) + m[:running] += (e_end.to_i - e.start.to_i) unless e.end + m[:today] += (e_end.to_i - e.start.to_i) if same_day?(Time.now, e.start) + m + end end - end - if sheets.empty? then say "No sheets found"; return end + end.sort_by{|sheet| sheet[:name].downcase} width = sheets.sort_by{|h|h[:name].length }.last[:name].length + 4 - say " %-#{width}s%-12s%-12s%s" % ["Timesheet", "Running", "Today", "Total Time"] + puts " %-#{width}s%-12s%-12s%s" % ["Timesheet", "Running", "Today", "Total Time"] sheets.each do |sheet| - star = sheet[:name] == Timetrap.current_sheet ? '*' : ' ' - say "#{star}%-#{width}s%-12s%-12s%s" % [ + star = sheet[:name] == Timer.current_sheet ? '*' : ' ' + puts "#{star}%-#{width}s%-12s%-12s%s" % [ sheet[:running], sheet[:today], sheet[:total] ].map(&method(:format_seconds)).unshift(sheet[:name]) end end def now - if Timetrap.running? - out = "#{Timetrap.current_sheet}: #{format_duration(Timetrap.active_entry.start, Timetrap.active_entry.end_or_now)}".gsub(/ /, ' ') - out << " (#{Timetrap.active_entry.note})" if Timetrap.active_entry.note =~ /.+/ - say out - else - say "#{Timetrap.current_sheet}: not running" + if !Timer.running? + puts "*#{Timer.current_sheet}: not running" end + Timer.running_entries.each do |entry| + current = entry[:sheet] == Timer.current_sheet + out = current ? '*' : ' ' + out << "#{entry[:sheet]}: #{format_duration(entry.start, entry.end_or_now)}".gsub(/ /, ' ') + out << " (#{entry.note})" if entry.note =~ /.+/ + puts out + end end + alias_method :running, :display - def running - say "Running Timesheets:" - say Timetrap::Entry.filter(:end => nil).map{|e| " #{e.sheet}: #{e.note}"}.uniq.sort - end - def week args['-s'] = Date.today.wday == 1 ? Date.today.to_s : Date.parse(Chronic.parse(%q(last monday)).to_s).to_s display end @@ -289,10 +314,10 @@ args.unused.join(' ') end def ask_user question return true if args['-y'] - print question + $stderr.print question $stdin.gets =~ /\Aye?s?\Z/i end end end