module Timetrap::CLI

Constants

USAGE

Attributes

args[RW]

Public Instance Methods

archive() click to toggle source
# File lib/timetrap/cli.rb, line 202
def archive
  ee = selected_entries
  if ask_user "Archive #{ee.count} entries? "
    ee.each do |e|
      next unless e.end
      e.update :sheet => "_#{e.sheet}"
    end
  else
    warn "archive aborted!"
  end
end
backend() click to toggle source
# File lib/timetrap/cli.rb, line 270
def backend
  exec "sqlite3 #{DB_NAME}"
end
commands() click to toggle source
# File lib/timetrap/cli.rb, line 156
def commands
  Timetrap::CLI::USAGE.scan(/\* \w+/).map{|s| s.gsub(/\* /, '')}
end
configure() click to toggle source
# File lib/timetrap/cli.rb, line 214
def configure
  Config.configure!
  puts "Config file is at #{Config::PATH.inspect}"
end
deprecated_commands() click to toggle source
# File lib/timetrap/cli.rb, line 160
def deprecated_commands
  {
    'switch' => 'sheet',
    'running' => 'now',
    'format' => 'display'
  }
end
display() click to toggle source
# File lib/timetrap/cli.rb, line 366
def display
  entries = selected_entries
  if entries == []
    warn "No entries were selected to display."
  else
    puts format_entries(entries)
  end
end
edit() click to toggle source
# File lib/timetrap/cli.rb, line 219
def edit
  entry = case
          when args['-i']
            warn "Editing entry with id #{args['-i'].inspect}"
            Entry[args['-i']]
          when Timer.active_entry
            warn "Editing running entry"
            Timer.active_entry
          when Timer.last_checkout
            warn  "Editing last entry you checked out of"
            Timer.last_checkout
          end

  unless entry
    warn "Can't find entry"
    return
  end
  warn ""

  entry.update :start => args['-s'] if args['-s'] =~ /.+/
  entry.update :end => args['-e'] if args['-e'] =~ /.+/

  # update sheet
  if args['-m'] =~ /.+/
    if entry == Timer.active_entry
      Timer.current_sheet = args['-m']
    end
    entry.update :sheet => args['-m']
  end

  if Config['note_editor']
    if args['-z']
      note = [entry.note, get_note_from_external_editor].join(Config['append_notes_delimiter'])
      entry.update :note => note
    elsif editing_a_note?
      entry.update :note => get_note_from_external_editor(entry.note)
    end
  else
    if unused_args =~ /.+/
      note = unused_args
      if args['-z']
        note = [entry.note, note].join(Config['append_notes_delimiter'])
      end
      entry.update :note => note
    end
  end


  puts format_entries(entry)
end
handle_invalid_command(command) click to toggle source
# File lib/timetrap/cli.rb, line 185
def handle_invalid_command(command)
  if !command
    puts USAGE
  elsif mapping = deprecated_commands.detect{|(k,v)| k =~ %r|^#{command}|}
    deprecated, current = *mapping
    warn "The #{deprecated.inspect} command is deprecated in favor of #{current.inspect}. Sorry for the inconvenience."
    send current
  else
    warn "Invalid command: #{command.inspect}"
  end
end
in() click to toggle source
# File lib/timetrap/cli.rb, line 274
def in
  if Config['auto_checkout']
    Timer.stop_all(args['-a']).each do |checked_out_of|
      warn "Checked out of sheet #{checked_out_of.sheet.inspect}."
    end
  end

  note = unused_args
  if Config['require_note'] && !Timer.running? && unused_args.empty?
    if Config['note_editor']
      note = get_note_from_external_editor
    else
      $stderr.print("Please enter a note for this entry:\n> ")
      note = $stdin.gets.strip
    end
  end

  Timer.start note, args['-a']
  warn "Checked into sheet #{Timer.current_sheet.inspect}."
end
invoke() click to toggle source
# File lib/timetrap/cli.rb, line 148
def invoke
  args['-h'] ? puts(USAGE) : invoke_command_if_valid
rescue StandardError, LoadError => e
  raise e if args['--debug']
  warn e.message
  exit 1 unless defined? TEST_MODE
end
invoke_command_if_valid() click to toggle source
# File lib/timetrap/cli.rb, line 168
def invoke_command_if_valid
  if args.unused.empty? && Timetrap::Config['default_command']
    self.args = Getopt::Declare.new(USAGE.dup, Timetrap::Config['default_command'])
  end
  command = args.unused.shift
  set_global_options
  case (valid = commands.select{|name| name =~ %r|^#{command}|}).size
  when 1 then send valid[0]
  else
    handle_invalid_command(command)
  end
end
kill() click to toggle source
# File lib/timetrap/cli.rb, line 342
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
      warn "it's dead"
    else
      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}? "
      Entry.filter(:sheet => sheet).destroy
      warn "killed #{victims} entries"
    else
      warn "will not kill"
    end
  else
    victim = args['-i'] ? args['-i'].to_s.inspect : sheet.inspect
    warn ["can't find #{victim} to kill", 'sheets:', *sheets].join("\n")
  end
end
list() click to toggle source
# File lib/timetrap/cli.rb, line 398
def list
  sheets = ([Timer.current_sheet] | Entry.sheets).map do |sheet|
    sheet_atts = {:total => 0, :running => 0, :today => 0}
    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.sort_by{|sheet| sheet[:name].downcase}
  width = sheets.sort_by{|h|h[:name].length }.last[:name].length + 4
  width = 10 if width < 10
  puts " %-#{width}s%-12s%-12s%s" % ["Timesheet", "Running", "Today", "Total Time"]
  sheets.each do |sheet|
    star = sheet[:name] == Timer.current_sheet ? '*' : sheet[:name] == Timer.last_sheet ? '-' : ' '
    puts "#{star}%-#{width}s%-12s%-12s%s" % [
      sheet[:running],
      sheet[:today],
      sheet[:total]
    ].map(&method(:format_seconds)).unshift(sheet[:name])
  end
end
month() click to toggle source
# File lib/timetrap/cli.rb, line 464
def month
  d = Chronic.parse( args['-s'] || Date.today )

  beginning_of_month = Date.new( d.year, d.month )
  end_of_month = if d.month == 12 # handle edgecase
    Date.new( d.year + 1, 1) - 1
  else
    Date.new( d.year, d.month+1 ) - 1
  end
  args['-s'] = beginning_of_month.to_s
  args['-e'] = end_of_month.to_s
  display
end
now() click to toggle source
# File lib/timetrap/cli.rb, line 428
def now
  if !Timer.running?
    warn "*#{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.duration)}".gsub(/  /, ' ')
    out << " (#{entry.note})" if entry.note =~ /.+/
    puts out
  end
end
out() click to toggle source
# File lib/timetrap/cli.rb, line 324
def out
  if Config['auto_checkout']
    stopped = Timer.stop_all(args['-a']).each do |checked_out_of|
      warn "Checked out of sheet #{checked_out_of.sheet.inspect}."
    end
    if stopped.empty?
      warn "No running entries to stop."
    end
  else
    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
end
parse(arguments) click to toggle source
# File lib/timetrap/cli.rb, line 144
def parse arguments
  args.parse arguments
end
resume() click to toggle source
# File lib/timetrap/cli.rb, line 295
def resume
  entry = case
          when args['-i']
            entry = Entry[args['-i']]
            unless entry
              warn "No such entry (id #{args['-i'].inspect})!"
              return
            end
            warn "Resuming entry with id #{args['-i'].inspect} (#{entry.note})"
            entry
          else
            last_entry = Timer.entries(Timer.current_sheet).order(:id).last
            last_entry ||= Timer.entries("_#{Timer.current_sheet}").order(:id).last
            warn "No entry yet on this sheet yet. Started a new entry." unless last_entry
            note = (last_entry ? last_entry.note : nil)
            warn "Resuming #{note.inspect} from entry ##{last_entry.id}" if note
            last_entry
          end

  unless entry
    warn "Can't find entry"
    return
  end

  self.unused_args = entry.note || unused_args

  self.in
end
set_global_options() click to toggle source

currently just sets whether output should be rounded to 15 min intervals

# File lib/timetrap/cli.rb, line 198
def set_global_options
  Timetrap::Entry.round = true if args['-r']
end
sheet() click to toggle source
# File lib/timetrap/cli.rb, line 375
def sheet
  sheet = unused_args
  case sheet
  when nil, ''
    list
    return
  when '-'
    if Timer.last_sheet
      sheet = Timer.last_sheet
    else
      warn 'LAST_SHEET is not set'
      return
    end
  end

  Timer.current_sheet = sheet
  if Timer.last_sheet == sheet
    warn "Already on sheet #{sheet.inspect}"
  else
    warn "Switching to sheet #{sheet.inspect}"
  end
end
today() click to toggle source
# File lib/timetrap/cli.rb, line 441
def today
    args['-s'] = Date.today.to_s
    display
end
valid_command(command) click to toggle source
# File lib/timetrap/cli.rb, line 181
def valid_command(command)
   return commands.include?(command)
end
week() click to toggle source
# File lib/timetrap/cli.rb, line 453
def week
  d = Chronic.parse( args['-s'] || Date.today )

  today = Date.new( d.year, d.month, d.day )
  end_of_week = today + 6
  last_week_start = Date.parse(Chronic.parse('last '.concat(Config['week_start']).to_s, :now => today).to_s)
  args['-s'] = today.wday == Date.parse(Config['week_start']).wday ? today.to_s : last_week_start.to_s
  args['-e'] = end_of_week.to_s
  display
end
yesterday() click to toggle source
# File lib/timetrap/cli.rb, line 446
def yesterday
  yesterday = (Date.today - 1).to_s
  args['-s'] = yesterday
  args['-e'] = yesterday
  display
end

Private Instance Methods

ask_user(question) click to toggle source
# File lib/timetrap/cli.rb, line 488
def ask_user question
  return true if args['-y']
  $stderr.print question
  $stdin.gets =~ /\Aye?s?\Z/i
end
editing_a_note?() click to toggle source
# File lib/timetrap/cli.rb, line 509
def editing_a_note?
  return true if args.size == 0

  args.each do |(k,_v)|
    return false unless ["--id", "-i"].include?(k)
  end
  true
end
format_entries(entries) click to toggle source
# File lib/timetrap/cli.rb, line 519
def format_entries(entries)
  load_formatter(args['-f'] || Config['default_formatter']).new(Array(entries)).output
end
get_note_from_external_editor(contents = "") click to toggle source
# File lib/timetrap/cli.rb, line 494
def get_note_from_external_editor(contents = "")
  file = Tempfile.new('get_note')
  unless contents.empty?
    file.open
    file.write(contents)
    file.close
  end

  system("#{Config['note_editor']} #{file.path}")
  file.open.read
ensure
 file.close
 file.unlink
end
unused_args() click to toggle source
# File lib/timetrap/cli.rb, line 480
def unused_args
  args.unused.join(' ')
end
unused_args=(str) click to toggle source
# File lib/timetrap/cli.rb, line 484
def unused_args=(str)
  args.unused = str.split
end