class TheFox::Timr::Command::ReportCommand

This Command is very similar to LogCommand. By default it prints all [Tasks](TheFox::Timr::Model::Task) of the current month.

Man page: [timr-report(1)](../../../../man/timr-report.1.html)

Constants

MAN_PATH

Path to man page.

Public Class Methods

new(argv = Array.new) click to toggle source
# File lib/timr/command/report_command.rb, line 19
def initialize(argv = Array.new)
        super()
        
        @help_opt = false
        @tasks_opt = false
        @tracks_opt = false
        @from_opt = nil
        @to_opt = nil
        @billed_opt = false
        @unbilled_opt = false
        @format_opt = nil
        @csv_opt = nil
        @force_opt = false
        
        loop_c = 0 # Limit the loop.
        while loop_c < 1024 && argv.length > 0
                loop_c += 1
                arg = argv.shift
                
                case arg
                when '-h', '--help'
                        @help_opt = true
                
                when '-d', '--day'
                        @from_opt, @to_opt = DateTimeHelper.parse_day_argv(argv)
                when '-m', '--month'
                        @from_opt, @to_opt = DateTimeHelper.parse_month_argv(argv)
                when '-y', '--year'
                        @from_opt, @to_opt = DateTimeHelper.parse_year_argv(argv)
                
                when '-a', '--all'
                        @from_opt = Time.parse('1970-01-01 00:00:00')
                        @to_opt   = Time.parse('2099-12-31 23:59:59')
                
                when '--tasks'
                        @tasks_opt = true
                when '-t', '--tracks'
                        @tracks_opt = true
                
                when '--billed'
                        @billed_opt = true
                when '--unbilled'
                        @unbilled_opt = true
                
                when '--format'
                        @format_opt = argv.shift
                
                when '--csv'
                        @csv_opt = argv.shift
                        if !@csv_opt
                                raise ReportCommandError, 'Invalid value for --csv option.'
                        end
                when '-f', '--force' # -f inofficial, maybe used for --file?
                        @force_opt = true
                
                else
                        raise ReportCommandError, "Unknown argument '#{arg}'. See 'timr report --help'."
                end
        end
        
        today = Date.today
        unless @from_opt
                @from_opt = Time.new(today.year, today.month, 1, 0, 0, 0)
        end
        unless @to_opt
                month_end = Date.new(today.year, today.month, -1)
                @to_opt = Time.new(today.year, today.month, month_end.day, 23, 59, 59)
        end
        
        @billed_resolved_opt = nil
        if @billed_opt || @unbilled_opt
                if @billed_opt
                        @billed_resolved_opt = true
                elsif @unbilled_opt
                        @billed_resolved_opt = false
                end
        end
        
        @filter_options = {
                :format => '%y-%m-%d %H:%M',
                :from => @from_opt,
                :to => @to_opt,
                :billed => @billed_resolved_opt,
        }
        @csv_filter_options = {
                :format => '%F %T %z',
                :from => @from_opt,
                :to => @to_opt,
        }
        
        # Used by
        #  print_formatted_task_list
        #  print_formatted_track_list
        @format_options = {
                :format => @format_opt,
                :billed => @billed_resolved_opt,
        }
        
        if @csv_opt
                if @csv_opt == '-'
                        # OK
                else
                        @csv_opt = Pathname.new(@csv_opt).expand_path
                end
        end
end

Public Instance Methods

run() click to toggle source

See BasicCommand#run.

# File lib/timr/command/report_command.rb, line 127
def run
        if @help_opt
                help
                return
        end
        
        @timr = Timr.new(@cwd)
        
        if @csv_opt
                if @tasks_opt
                        export_tasks_csv
                elsif @tracks_opt
                        export_tracks_csv
                else
                        export_tasks_csv
                end
        elsif @format_opt
                if @tasks_opt
                        print_formatted_task_list
                elsif @tracks_opt
                        print_formatted_track_list
                else
                        print_formatted_task_list
                end
        else
                if @tasks_opt
                        print_task_table
                elsif @tracks_opt
                        print_track_table
                else
                        print_task_table
                end
        end
end

Private Instance Methods

export_tasks_csv() click to toggle source
# File lib/timr/command/report_command.rb, line 363
def export_tasks_csv
        if @csv_opt == '-'
                csv_file_handle = STDOUT
        else
                if @csv_opt.exist? && !@force_opt
                        raise ReportCommandError, "File '#{@csv_opt}' already exist. Use --force to overwrite it."
                end
                #csv_file_handle = @csv_opt.to_s
                csv_file_handle = File.open(@csv_opt, 'wb')
        end
        
        totals = {
                :row_c => 0,
                
                :duration => Duration.new,
                :estimation => Duration.new,
                :remaining_time => Duration.new,
                :billed_duration => Duration.new,
                :unbilled_duration => Duration.new,
                
                :tracks_c => 0,
                :billed_tracks_c => 0,
                :unbilled_tracks_c => 0,
                
                :begin_datetime => nil,
                :end_datetime   => nil,
        }
        
        csv_options = {
                :headers => [
                        'ROW_NO',
                        
                        'TASK_ID',
                        'TASK_FOREIGN_ID',
                        'TASK_NAME',
                        
                        'TASK_BEGIN_DATETIME',
                        'TASK_END_DATETIME',
                        
                        'TASK_DURATION_HUMAN',
                        'TASK_DURATION_SECONDS',
                        
                        'TASK_ESTIMATION_HUMAN',
                        'TASK_ESTIMATION_SECONDS',
                        'TASK_REMAINING_TIME_HUMAN',
                        'TASK_REMAINING_TIME_SECONDS',
                        
                        'TASK_BILLED_DURATION_HUMAN',
                        'TASK_BILLED_DURATION_SECONDS',
                        'TASK_UNBILLED_DURATION_HUMAN',
                        'TASK_UNBILLED_DURATION_SECONDS',
                        
                        'TASK_TRACK_COUNT',
                        'TASK_BILLED_TRACK_COUNT',
                        'TASK_UNBILLED_TRACK_COUNT',
                        
                        # 'TASK_SHORTEST_TRACK_DURATION_HUMAN', # @TODO shortest longest track duration
                        # 'TASK_SHORTEST_TRACK_DURATION_SECONDS',
                        # 'TASK_LONGEST_TRACK_DURATION_HUMAN',
                        # 'TASK_LONGEST_TRACK_DURATION_SECONDS',
                ],
                :write_headers => true,
                :skip_blanks => true,
                #:force_quotes => true,
        }
        csv = CSV.new(csv_file_handle, csv_options)
        filtered_tasks.each do |task|
                totals[:row_c] += 1
                
                tracks = task.tracks
                
                # Task Tracks Count
                tracks_c = tracks.count
                billed_tracks_c = task.tracks({:billed => true}).count
                unbilled_tracks_c = task.tracks({:billed => false}).count
                
                # Global Tracks Count
                totals[:tracks_c] += tracks_c
                totals[:billed_tracks_c] += billed_tracks_c
                totals[:unbilled_tracks_c] += unbilled_tracks_c
                
                # Task Duration
                duration = task.duration(@csv_filter_options)
                estimation = task.estimation
                remaining_time = task.remaining_time
                billed_duration = task.billed_duration(@csv_filter_options)
                unbilled_duration = task.unbilled_duration(@csv_filter_options)
                
                # Global Duration Sum
                if duration
                        totals[:duration] += duration
                end
                if estimation
                        totals[:estimation] += estimation
                end
                if remaining_time
                        totals[:remaining_time] += remaining_time
                end
                if billed_duration
                        totals[:billed_duration] += billed_duration
                end
                if unbilled_duration
                        totals[:unbilled_duration] += unbilled_duration
                end
                
                # Task Begin DateTime
                bdt = task.begin_datetime(@csv_filter_options)
                
                # Task End DateTime
                edt = task.end_datetime(@csv_filter_options)
                
                # Determine First Begin DateTime of the table.
                if bdt && (!totals[:begin_datetime] || bdt < totals[:begin_datetime])
                        totals[:begin_datetime] = bdt
                end
                
                # Determine Last End DateTime of the table.
                if edt && (!totals[:end_datetime] || edt > totals[:end_datetime])
                        totals[:end_datetime] = edt
                end
                
                row = [
                        totals[:row_c],
                        
                        task.id,
                        task.foreign_id,
                        task.name,
                        
                        task.begin_datetime_s(@csv_filter_options),
                        task.end_datetime_s(@csv_filter_options),
                        
                        duration.to_human_s,
                        duration.to_i,
                ]
                
                if estimation
                        row << estimation.to_human_s
                        row << estimation.to_i
                else
                        row << '---'
                        row << 0
                end
                
                if remaining_time
                        row << remaining_time.to_human_s
                        row << remaining_time.to_i
                else
                        row << '---'
                        row << 0
                end
                
                row << billed_duration.to_human_s
                row << billed_duration.to_i
                row << unbilled_duration.to_human_s
                row << unbilled_duration.to_i
                
                row << tracks_c
                row << billed_tracks_c
                row << unbilled_tracks_c
                
                csv << row
        end
        
        totals[:begin_datetime_s] = totals[:begin_datetime] ? totals[:begin_datetime].localtime.strftime(@csv_filter_options[:format]) : '---'
        
        totals[:end_datetime_s] = totals[:end_datetime] ? totals[:end_datetime].localtime.strftime(@csv_filter_options[:format]) : '---'
        
        totals[:row_c] += 1
        csv << [
                totals[:row_c],
                
                'TOTAL',
                nil,
                nil,
                
                totals[:begin_datetime_s],
                totals[:end_datetime_s],
                
                totals[:duration].to_human_s,
                totals[:duration].to_i,
                
                totals[:estimation].to_human_s,
                totals[:estimation].to_i,
                
                totals[:remaining_time].to_human_s,
                totals[:remaining_time].to_i,
                
                totals[:billed_duration].to_human_s,
                totals[:billed_duration].to_i,
                
                totals[:unbilled_duration].to_human_s,
                totals[:unbilled_duration].to_i,
                
                totals[:tracks_c],
                
                totals[:billed_tracks_c],
                totals[:unbilled_tracks_c],
        ]
        
        csv.close
end
export_tracks_csv() click to toggle source
# File lib/timr/command/report_command.rb, line 565
def export_tracks_csv
        if @csv_opt == '-'
                csv_file_handle = STDOUT
        else
                if @csv_opt.exist? && !@force_opt
                        raise ReportCommandError, "File '#{@csv_opt}' already exist. Use --force to overwrite it."
                end
                #csv_file_handle = @csv_opt.to_s
                csv_file_handle = File.open(@csv_opt, 'wb')
        end
        
        totals = {
                :duration => Duration.new,
                :billed_duration => Duration.new,
                :unbilled_duration => Duration.new,
                :row_c => 0,
                
                :begin_datetime => nil,
                :end_datetime   => nil,
        }
        
        csv_options = {
                :headers => [
                        'ROW_NO',
                        
                        'TASK_ID',
                        'TASK_FOREIGN_ID',
                        'TASK_NAME',
                        
                        'TRACK_ID',
                        'TRACK_TITLE',
                        
                        'TRACK_BEGIN_DATETIME',
                        'TRACK_END_DATETIME',
                        
                        'TRACK_DURATION_HUMAN',
                        'TRACK_DURATION_SECONDS',
                        'TRACK_BILLED_DURATION_HUMAN',
                        'TRACK_BILLED_DURATION_SECONDS',
                        'TRACK_UNBILLED_DURATION_HUMAN',
                        'TRACK_UNBILLED_DURATION_SECONDS',
                        'TRACK_IS_BILLED',
                ],
                :write_headers => true,
                :skip_blanks => true,
                #:force_quotes => true,
        }
        csv = CSV.new(csv_file_handle, csv_options)
        @timr.tracks(@csv_filter_options).each do |track_id, track|
                totals[:row_c] += 1
                
                # Track Duration
                duration = track.duration(@csv_filter_options)
                billed_duration = track.billed_duration(@csv_filter_options)
                unbilled_duration = track.unbilled_duration(@csv_filter_options)
                
                # Global Duration Sum
                if duration
                        totals[:duration] += duration
                end
                if billed_duration
                        totals[:billed_duration] += billed_duration
                end
                if unbilled_duration
                        totals[:unbilled_duration] += unbilled_duration
                end
                
                # Get Task from Track.
                task = track.task
                
                # Track Begin DateTime
                bdt = track.begin_datetime(@csv_filter_options)
                
                # Track End DateTime
                edt = track.end_datetime(@csv_filter_options)
                
                # Determine First Begin DateTime of the table.
                if bdt && (!totals[:begin_datetime] || bdt < totals[:begin_datetime])
                        totals[:begin_datetime] = bdt
                end
                
                # Determine Last End DateTime of the table.
                if edt && (!totals[:end_datetime] || edt > totals[:end_datetime])
                        totals[:end_datetime] = edt
                end
                
                row = [
                        totals[:row_c],
                        
                        task.id,
                        task.foreign_id,
                        task.name,
                        
                        track.id,
                        track.title,
                        track.begin_datetime_s(@csv_filter_options),
                        track.end_datetime_s(@csv_filter_options),
                        
                        duration.to_human_s,
                        duration.to_i,
                        
                        billed_duration.to_human_s,
                        billed_duration.to_i,
                        
                        unbilled_duration.to_human_s,
                        unbilled_duration.to_i,
                        
                        track.is_billed.to_i,
                ]
                
                csv << row
        end
        
        totals[:begin_datetime_s] = totals[:begin_datetime] ? totals[:begin_datetime].localtime.strftime(@csv_filter_options[:format]) : '---'
        
        totals[:end_datetime_s] = totals[:end_datetime] ? totals[:end_datetime].localtime.strftime(@csv_filter_options[:format]) : '---'
        
        totals[:row_c] += 1
        row = [
                totals[:row_c],
                'TOTAL',
                
                nil,
                nil,
                nil,
                
                nil,
                
                totals[:begin_datetime_s],
                totals[:end_datetime_s],
                
                totals[:duration].to_human_s,
                totals[:duration].to_i,
                
                totals[:billed_duration].to_human_s,
                totals[:billed_duration].to_i,
                
                totals[:unbilled_duration].to_human_s,
                totals[:unbilled_duration].to_i,
        ]
        
        csv << row
        
        csv.close
end
filtered_tasks() click to toggle source
# File lib/timr/command/report_command.rb, line 711
def filtered_tasks
        # Get Tracks.
        tracks = @timr.tracks(@filter_options)
        
        # Convert Tracks to Tasks. Convert the Array to a Set removes all duplicates.
        tracks.map{ |track_id, track| track.task }.to_set
end
help() click to toggle source
# File lib/timr/command/report_command.rb, line 719
def help
        puts 'usage: timr report [-d|--day <date>] [-m|--month <[YYYY-]MM>]'
        puts '                   [-y|--year [<YYYY>]] [-a|--all] [--tasks|--tracks]'
        puts '                   [--billed|--unbilled]'
        puts '                   [--csv <path>] [--force] [--format <str>]'
        puts '   or: timr report [-h|--help]'
        puts
        puts 'Options'
        puts '    -d, --day   [<date>]        A single day from 00:00 to 23:59.'
        puts '    -m, --month <[YYYY-]MM>     A single month from 01 to 31.'
        puts '    -y, --year  [<YYYY>]        A single year from 01-01 to 12-31.'
        puts '    -a, --all                   All.'
        puts '    --tasks                     Export Tasks (default)'
        puts '    --tracks                    Export Tracks'
        puts '    --billed                    Filter only Tasks/Tracks which are billed.'
        puts '    --unbilled                  Filter only Tasks/Tracks which are not billed.'
        puts '    --format <str>              Format Tasks and Tracks output.'
        puts "    --csv <path>                Export as CSV file. Use '--csv -' to use STDOUT."
        puts "    --force                     Force overwrite file."
        puts
        puts 'For column descriptions see man page:'
        puts "'timr help report'"
        puts
        HelpCommand.print_datetime_help
        puts
        puts 'Task Format'
        puts '    %id     ID'
        puts '    %sid    Short ID'
        puts '    %fid    Foreign ID'
        puts '    %n      Name'
        puts '    %d      Description'
        puts '    %ds     Duration Seconds'
        puts '    %dh     Duration Human Format'
        puts
        puts 'Track Format'
        puts '    %id     ID'
        puts '    %sid    Short ID'
        puts '    %m      Message'
        puts '    %bdt    Begin DateTime'
        puts '    %bd     Begin Date'
        puts '    %bt     Begin Time'
        puts '    %edt    End DateTime'
        puts '    %ed     End Date'
        puts '    %et     End Time'
        puts '    %ds     Duration Seconds'
        puts '    %dh     Duration Human Format'
        puts '    %bi     Billed Integer'
        puts '    %bh     Billed Human Format (YES, NO)'
        puts
        puts "Use '%T' prefix for each Task attribute for Track formatting."
        puts "For example use '%Tid' to use the Task ID."
        puts
end
print_formatted_task_list() click to toggle source
print_formatted_track_list() click to toggle source
print_task_table() click to toggle source
print_track_table() click to toggle source