class Datasets::Downloader::ProgressReporter

Public Class Methods

new(base_name, size_max) click to toggle source
# File lib/datasets/downloader.rb, line 98
def initialize(base_name, size_max)
  @base_name = base_name
  @size_max = size_max

  @time_previous = Time.now
  @size_previous = 0

  @need_report = ($stderr == STDERR and $stderr.tty?)
end

Public Instance Methods

report(size_current) click to toggle source
# File lib/datasets/downloader.rb, line 108
def report(size_current)
  return unless @need_report
  return if @size_max.nil?
  return unless foreground?

  done = (size_current == @size_max)
  time_current = Time.now
  if not done and time_current - @time_previous <= 1
    return
  end

  read_bytes = size_current - @size_previous
  throughput = read_bytes.to_f / (time_current - @time_previous)
  @time_previous = time_current
  @size_previous = size_current

  message = build_message(size_current, throughput)
  $stderr.print("\r#{message}") if message
  $stderr.puts if done
end

Private Instance Methods

build_message(size_current, throughput) click to toggle source
# File lib/datasets/downloader.rb, line 130
def build_message(size_current, throughput)
  percent = (size_current / @size_max.to_f) * 100
  formatted_size = "[%s/%s]" % [
    format_size(size_current),
    format_size(@size_max),
  ]
  rest_second = (@size_max - size_current) / throughput
  separator = " - "
  progress = "%05.1f%% %s %s %s" % [
    percent,
    formatted_size,
    format_time_interval(rest_second),
    format_throughput(throughput),
  ]
  base_name = @base_name

  width = guess_terminal_width
  return "#{base_name}#{separator}#{progress}" if width.nil?

  return nil if progress.size > width

  base_name_width = width - progress.size - separator.size
  if base_name.size > base_name_width
    ellipsis = "..."
    shorten_base_name_width = base_name_width - ellipsis.size
    if shorten_base_name_width < 1
      return progress
    else
      base_name = base_name[0, shorten_base_name_width] + ellipsis
    end
  end
  "#{base_name}#{separator}#{progress}"
end
foreground?() click to toggle source
# File lib/datasets/downloader.rb, line 209
def foreground?
  proc_stat_path = "/proc/self/stat"
  ps_path = "/bin/ps"

  if File.exist?(proc_stat_path)
    stat = File.read(proc_stat_path).sub(/\A.+\) /, "").split
    process_group_id = stat[2]
    terminal_process_group_id = stat[5]
    process_group_id == terminal_process_group_id
  elsif File.executable?(ps_path)
    IO.pipe do |input, output|
      pid = spawn(ps_path, "-o", "stat", "-p", Process.pid.to_s,
                  {:out => output, :err => output})
      output.close
      _, status = Process.waitpid2(pid)
      return false unless status.success?

      input.each_line.to_a.last.include?("+")
    end
  else
    false
  end
end
format_size(size) click to toggle source
# File lib/datasets/downloader.rb, line 164
def format_size(size)
  if size < 1000
    "%d" % size
  elsif size < (1000 ** 2)
    "%6.2fKB" % (size.to_f / 1000)
  elsif size < (1000 ** 3)
    "%6.2fMB" % (size.to_f / (1000 ** 2))
  elsif size < (1000 ** 4)
    "%6.2fGB" % (size.to_f / (1000 ** 3))
  else
    "%.2fTB" % (size.to_f / (1000 ** 4))
  end
end
format_throughput(throughput) click to toggle source
# File lib/datasets/downloader.rb, line 196
def format_throughput(throughput)
  throughput_byte = throughput / 8
  if throughput_byte <= 1000
    "%3dB/s" % throughput_byte
  elsif throughput_byte <= (1000 ** 2)
    "%3dKB/s" % (throughput_byte / 1000)
  elsif throughput_byte <= (1000 ** 3)
    "%3dMB/s" % (throughput_byte / (1000 ** 2))
  else
    "%3dGB/s" % (throughput_byte / (1000 ** 3))
  end
end
format_time_interval(interval) click to toggle source
# File lib/datasets/downloader.rb, line 178
def format_time_interval(interval)
  if interval < 60
    "00:00:%02d" % interval
  elsif interval < (60 * 60)
    minute, second = interval.divmod(60)
    "00:%02d:%02d" % [minute, second]
  elsif interval < (60 * 60 * 24)
    minute, second = interval.divmod(60)
    hour, minute = minute.divmod(60)
    "%02d:%02d:%02d" % [hour, minute, second]
  else
    minute, second = interval.divmod(60)
    hour, minute = minute.divmod(60)
    day, hour = hour.divmod(24)
    "%dd %02d:%02d:%02d" % [day, hour, minute, second]
  end
end
guess_terminal_width() click to toggle source
# File lib/datasets/downloader.rb, line 233
def guess_terminal_width
  guess_terminal_width_from_io ||
    guess_terminal_width_from_command ||
    guess_terminal_width_from_env ||
    80
end
guess_terminal_width_from_command() click to toggle source
# File lib/datasets/downloader.rb, line 254
def guess_terminal_width_from_command
  IO.pipe do |input, output|
    begin
      pid = spawn("tput", "cols", {:out => output, :err => output})
    rescue SystemCallError
      return nil
    end

    output.close
    _, status = Process.waitpid2(pid)
    return nil unless status.success?

    result = input.read.chomp
    begin
      Integer(result, 10)
    rescue ArgumentError
      nil
    end
  end
end
guess_terminal_width_from_env() click to toggle source
# File lib/datasets/downloader.rb, line 275
def guess_terminal_width_from_env
  env = ENV["COLUMNS"] || ENV["TERM_WIDTH"]
  return nil if env.nil?

  begin
    Integer(env, 10)
  rescue ArgumentError
    nil
  end
end
guess_terminal_width_from_io() click to toggle source
# File lib/datasets/downloader.rb, line 240
def guess_terminal_width_from_io
  if IO.respond_to?(:console)
    IO.console.winsize[1]
  elsif $stderr.respond_to?(:winsize)
    begin
      $stderr.winsize[1]
    rescue SystemCallError
      nil
    end
  else
    nil
  end
end