# desc “Explaining what the task does” # task :umlaut_borrow_direct do # # Task goes here # end namespace :umlaut_borrow_direct do

desc "BD API stats calc"
task :api_stats, [:filename] do |t, args|

  stats = {
    "FindItem" => {
      :times    => [],
      :errors   => [],
      :timeouts => [],
      :count    => 0
    },
    "RequestItem" => {
      :times    => [],
      :errors   => [],
      :timeouts => [],
      :count    => 0
    }
  }

  if args["filename"] == "stdin"
    file = STDIN
  else
    file = File.open(File.expand_path args[:filename])
  end

  file.each_line do |line|

    if line =~ /BD API log\t([^\t]*)\t([^\t]*)\t([^\t]*)\t([^\t]*)/
      action = $1
      result = $2
      timing = $3
      query  = $4

      if timing.present? && (timing.to_f == 0)
        raise "Timing does not look like a float: #{timingf}" 
      end
      timing = timing.to_f

      if ["FindItem", "RequestItem"].include? action
        stats[action][:count] += 1

        if line =~ /(\d\d\d\d-\d\d-\d\d)/

          date = Date.parse($1)

          stats[action][:min_date] = date if stats[action][:min_date].nil? || (date < stats[action][:min_date])
          stats[action][:max_date] = date if stats[action][:max_date].nil? || (date > stats[action][:max_date])
        end

        value_dict = {:action => action, :result => result, :timing => timing, :query => query}

        stats[action][:times] << timing unless timing == 0
        unless result == "SUCCESS"
          (exception_class, exception_message) = result.split("/")
          value_dict[:exception_class]   = exception_class
          value_dict[:exception_message] = exception_message
          if exception_class == "BorrowDirect::HttpTimeoutError"          
            stats[action][:timeouts] << value_dict 
          else
            stats[action][:errors] << value_dict
          end
        end
      else
        puts "Warning, unrecognized action: #{action}"
      end
    end
  end

  ["FindItem", "RequestItem"].each do |action|
    sorted_times = stats[action][:times].sort

    next if sorted_times.empty?

    puts "\n\n#{action} API: #{stats[action][:count]} requests from #{stats[action][:min_date]} to #{stats[action][:max_date]}"
    puts "  Timing (not including timeouts):"
    puts "    Min: #{sorted_times.first}"
    puts "    25th %ile: #{percentile sorted_times, 25}"
    puts "    Median: #{percentile sorted_times, 50}"
    puts "    60th %ile: #{percentile sorted_times, 60}"
    puts "    75th %ile: #{percentile sorted_times, 75}"
    puts "    95th %ile: #{percentile sorted_times, 95}"
    puts "    99th %ile: #{percentile sorted_times, 99}"
    puts "    Max: #{sorted_times.last}"
    puts ""
    puts "  Timeouts: #{stats[action][:timeouts].count}"
    puts "  Errors: #{stats[action][:errors].count}"

    #error_types = stats[action][:errors].group_by {|dict| dict[:exception_class]}
    #error_types.each_pair do |type, list|
    #  puts "    #{type} (#{list.count}) #{list}"
    #end

  end
  puts "\n\n"

end

# pass in a SORTED array a
def percentile a, p
    rank = (p.to_f / 100) * (a.length + 1)

    if a.length == 0
      return nil
    elsif rank.modulo(1) != 0

      sample_0 = a[rank.truncate - 1]
      sample_1 = a[rank.truncate]

      return sample_0 if sample_1.nil?
      return (sample_1 + sample_0) / 2
    else
      return a[rank - 1]
    end    
end

end