Class: Log::ProgressBar

Inherits:
Object
  • Object
show all
Defined in:
lib/scout/log/progress.rb,
lib/scout/log/progress/util.rb,
lib/scout/log/progress/report.rb

Constant Summary collapse

BAR_MUTEX =
Mutex.new
BARS =
[]
REMOVE =
[]
SILENCED =
[]

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(max = nil, options = {}) ⇒ ProgressBar

Returns a new instance of ProgressBar.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/scout/log/progress.rb', line 23

def initialize(max = nil, options = {})
  depth, desc, file, bytes, frequency, process, callback, severity = 
    IndiferentHash.process_options options, :depth, :desc, :file, :bytes, :frequency, :process, :callback, :severity,
    :depth => 0, :frequency => 2, :severity => Log::ProgressBar.default_severity

  max = nil if TrueClass === max

  @max = max
  @ticks = 0
  @frequency = frequency
  @severity = severity
  @last_time = nil
  @last_count = nil
  @last_percent = nil
  @depth = depth
  @desc = desc.nil? ? "" : desc.gsub(/\n/,' ')
  @file = file
  @bytes = bytes
  @process = process
  @callback = callback
end

Class Attribute Details

.default_fileObject

Returns the value of attribute default_file.



17
18
19
# File 'lib/scout/log/progress.rb', line 17

def default_file
  @default_file
end

.default_severityObject

Returns the value of attribute default_severity.



18
19
20
# File 'lib/scout/log/progress.rb', line 18

def default_severity
  @default_severity
end

Instance Attribute Details

#bytesObject

Returns the value of attribute bytes.



21
22
23
# File 'lib/scout/log/progress.rb', line 21

def bytes
  @bytes
end

#callbackObject

Returns the value of attribute callback.



21
22
23
# File 'lib/scout/log/progress.rb', line 21

def callback
  @callback
end

#depthObject

Returns the value of attribute depth.



21
22
23
# File 'lib/scout/log/progress.rb', line 21

def depth
  @depth
end

#descObject

Returns the value of attribute desc.



21
22
23
# File 'lib/scout/log/progress.rb', line 21

def desc
  @desc
end

#fileObject

Returns the value of attribute file.



21
22
23
# File 'lib/scout/log/progress.rb', line 21

def file
  @file
end

#frequencyObject

Returns the value of attribute frequency.



21
22
23
# File 'lib/scout/log/progress.rb', line 21

def frequency
  @frequency
end

#historyObject

Returns the value of attribute history.



11
12
13
# File 'lib/scout/log/progress/report.rb', line 11

def history
  @history
end

#maxObject

Returns the value of attribute max.



21
22
23
# File 'lib/scout/log/progress.rb', line 21

def max
  @max
end

#max_historyObject

Returns the value of attribute max_history.



11
12
13
# File 'lib/scout/log/progress/report.rb', line 11

def max_history
  @max_history
end

#mean_maxObject

Returns the value of attribute mean_max.



11
12
13
# File 'lib/scout/log/progress/report.rb', line 11

def mean_max
  @mean_max
end

#process(elem) ⇒ Object

Returns the value of attribute process.



21
22
23
# File 'lib/scout/log/progress.rb', line 21

def process
  @process
end

#severityObject

Returns the value of attribute severity.



21
22
23
# File 'lib/scout/log/progress.rb', line 21

def severity
  @severity
end

#ticksObject

Returns the value of attribute ticks.



21
22
23
# File 'lib/scout/log/progress.rb', line 21

def ticks
  @ticks
end

Class Method Details

.add_offset(value = 1) ⇒ Object



9
10
11
12
13
14
# File 'lib/scout/log/progress/util.rb', line 9

def self.add_offset(value = 1)
  value = 1 if TrueClass === value
  @@offset = offset + value.to_i
  @@offset = 0 if @@offset < 0
  @@offset
end

.cleanup_barsObject



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/scout/log/progress/util.rb', line 42

def self.cleanup_bars
  BAR_MUTEX.synchronize do
    REMOVE.each do |bar|
      index = BARS.index bar
      if index
        BARS.delete_at index
        BARS.each_with_index do |bar,i|
          bar.depth = i
        end
      end
      index = SILENCED.index bar
      if index
        SILENCED.delete_at index
        SILENCED.each_with_index do |bar,i|
          bar.depth = i
        end
      end
    end
    REMOVE.clear
    BARS.length
  end
end

.get_obj_bar(obj, bar = nil) ⇒ Object



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/scout/log/progress/util.rb', line 139

def self.get_obj_bar(obj, bar = nil)
  return nil if bar.nil? || bar == false
  case bar
  when String
    max = guess_obj_max(obj)
    Log::ProgressBar.new_bar(max, {:desc => bar}) 
  when TrueClass
    max = guess_obj_max(obj)
    Log::ProgressBar.new_bar(max) 
  when Numeric
    max = guess_obj_max(obj)
    Log::ProgressBar.new_bar(bar) 
  when Hash
    max = IndiferentHash.process_options(bar, :max) || max
    Log::ProgressBar.new_bar(max, bar) 
  when Log::ProgressBar
    bar.max ||= guess_obj_max(obj)
    bar
  else
    if (defined? Step and Step === bar)
      max = guess_obj_max(obj)
      Log::ProgressBar.new_bar(max, {:desc => bar.status, :file => bar.file(:progress)}) 
    else
      bar
    end
  end
end

.guess_obj_max(obj) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/scout/log/progress/util.rb', line 101

def self.guess_obj_max(obj)
  begin
    case obj
    when (defined? Step and Step)
      if obj.done?
        path = obj.path
        path = path.find if path.respond_to? :find
        if File.exist? path
          CMD.cmd("wc -l '#{path}'").read.to_i 
        else
          nil
        end
      else
        nil
      end
    when TSV
      obj.length
    when Array, Hash
      obj.size
    when File
      return nil if Open.gzip?(obj.filename) or Open.bgzip?(obj.filename) or Open.remote?(obj.filename)
      CMD.cmd("wc -l '#{obj.filename}'").read.to_i
    when Path, String
      obj = obj.find if Path === obj
      if File.exist? obj
        return nil if Open.gzip?(obj) or Open.bgzip?(obj)
        CMD.cmd("wc -l '#{obj}'").read.to_i
      else
        nil
      end
    end
  rescue Interrupt
    raise $!
  rescue Exception
    nil
  end
end

.new_bar(max, options = {}) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
# File 'lib/scout/log/progress/util.rb', line 30

def self.new_bar(max, options = {})
  options, max = max, nil if Hash === max
  max = options[:max] if options && max.nil?
  cleanup_bars
  BAR_MUTEX.synchronize do
    Log::LAST.replace "new_bar" if Log::LAST == "progress"
    options = IndiferentHash.add_defaults options, :depth => BARS.length + Log::ProgressBar.offset
    BARS << (bar = ProgressBar.new(max, options))
    bar
  end
end

.offsetObject



24
25
26
27
28
# File 'lib/scout/log/progress/util.rb', line 24

def self.offset
  @@offset ||= 0
  @@offset = 0 if @@offset < 0
  @@offset
end

.remove_bar(bar, error = false) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/scout/log/progress/util.rb', line 65

def self.remove_bar(bar, error = false)
  BAR_MUTEX.synchronize do
    return if REMOVE.include? bar
  end
  if error
    bar.error if bar.respond_to? :error
  else
    bar.done if bar.respond_to? :done
  end
  BAR_MUTEX.synchronize do
    REMOVE << bar
  end
  cleanup_bars
  Log::LAST.replace "remove_bar" if Log::LAST == "progress"
end

.remove_offset(value = 1) ⇒ Object



16
17
18
19
20
21
# File 'lib/scout/log/progress/util.rb', line 16

def self.remove_offset(value = 1)
  value = 1 if TrueClass === value
  @@offset = offset - value.to_i
  @@offset = 0 if @@offset < 0
  @@offset
end

.with_bar(max = nil, options = {}) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/scout/log/progress/util.rb', line 85

def self.with_bar(max = nil, options = {})
  bar = options.include?(:bar) ? options[:bar] : new_bar(max, options)
  begin
    error = false
    keep = false
    yield bar
  rescue KeepBar
    keep = true
  rescue
    error = true
    raise $!
  ensure
    remove_bar(bar, error) if bar && ! keep
  end
end

.with_obj_bar(obj, bar = true, &block) ⇒ Object



167
168
169
170
# File 'lib/scout/log/progress/util.rb', line 167

def self.with_obj_bar(obj, bar = true, &block)
  bar = get_obj_bar(obj, bar)
  with_bar nil, bar: bar, &block
end

Instance Method Details

#done(io = STDERR) ⇒ Object



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/scout/log/progress/report.rb', line 199

def done(io = STDERR)
  done_msg = Log.color(:magenta, "· ") << Log.color(:green, "done")
  if @start
    ellapsed = (Time.now - @start)
  else
    ellapsed = 0
  end
  ellapsed_str = [ellapsed/3600, ellapsed/60 % 60, ellapsed % 60].map{|t| "%02i" % t }.join(':')
  done_msg << " " << Log.color(:blue, (@ticks).to_s) << " #{bytes ? 'bytes' : 'items'} in " << Log.color(:green, ellapsed_str)
  @last_count = 0
  @last_time = @start
  thr = ellapsed > 0 ? (@ticks / ellapsed).to_i.to_s : 0
  done_msg << " - " << Log.color(:blue, thr) << " per second"
  done_msg << Log.color(:magenta, " · " << desc)
  print(io, Log.up_lines(@depth) << done_msg << Log.down_lines(@depth)) 

  FileUtils.rm file if file and File.exist?(file)

  @callback.call self if @callback
end

#error(io = STDERR) ⇒ Object



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/scout/log/progress/report.rb', line 220

def error(io = STDERR)
  done_msg = Log.color(:magenta, "· ") << Log.color(:red, "error")
  if @start
    ellapsed = (Time.now - @start).to_i
  else
    ellapsed = 0
  end
  ellapsed = [ellapsed/3600, ellapsed/60 % 60, ellapsed % 60].map{|t| "%02i" % t }.join(':')
  done_msg << " " << Log.color(:blue, (@ticks).to_s) << " in " << Log.color(:green, ellapsed)
  @last_count = 0
  @last_time = @start
  done_msg << " - " << thr_msg
  done_msg << Log.color(:magenta, " · " << desc)      
  print(io, Log.up_lines(@depth) << done_msg << Log.down_lines(@depth)) 

  FileUtils.rm file if file and File.exist?(file)

  begin
    @callback.call self
  rescue
    Log.debug "Callback failed for filed progress bar: #{$!.message}"
  end if @callback
end

#eta_msgObject



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/scout/log/progress/report.rb', line 88

def eta_msg
  percent = self.percent
  time = Time.now

  indicator = ""
  10.times{|i|
    if i < percent / 10 then
      indicator << Log.color(:yellow, ".")
    else
      indicator << " "
    end
  }

  indicator << " #{Log.color(:blue, percent.to_s << "%")}"

  used = time - @start
  if @mean_max and @mean_max > 0 and @mean > 0
    eta =  (@max - @ticks) / @mean
  else
    eta =  (@max - @ticks) / (@ticks/used)
  end

  used = Misc.format_seconds(used) 
  eta = [eta/3600, eta/60 % 60, eta % 60].map{|t| "%02i" % t }.join(':')

  #indicator << " #{Log.color :yellow, used} used #{Log.color :yellow, eta} left - #{Log.color :yellow, ticks.to_s} of #{Log.color :yellow, @max.to_s} #{bytes ? 'bytes' : 'items'}"
  indicator << " #{Log.color :yellow, eta} => #{Log.color :yellow, used} - #{Log.color :yellow, ticks.to_s} of #{Log.color :yellow, @max.to_s} #{bytes ? 'bytes' : 'items'}"

  indicator
end

#initObject



55
56
57
58
59
60
61
62
# File 'lib/scout/log/progress.rb', line 55

def init
  @ticks, @bytes = 0
  @last_time = @last_count = @last_percent = nil
  @history, @mean_max, @max_history = nil
  @start = @last_time = Time.now
  @last_count = 0
  report
end

#load(info) ⇒ Object



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/scout/log/progress/report.rb', line 138

def load(info)
  info.each do |key, value| 
    case key.to_sym
    when :start 
      @start = value
    when :last_time 
      @last_time = value
    when :last_count 
      @last_count = value
    when :last_percent 
      @last_percent = value
    when :desc 
      @desc = value
    when :ticks 
      @ticks = value
    when :max 
      @max = value
    when :mean 
      @mean = value
    end
  end
end

#percentObject



45
46
47
48
49
# File 'lib/scout/log/progress.rb', line 45

def percent
  return 0 if @ticks == 0
  return 100 if @max == 0
  (@ticks * 100) / @max
end

#pos(pos) ⇒ Object



88
89
90
91
# File 'lib/scout/log/progress.rb', line 88

def pos(pos)
  step = pos - (@ticks || 0)
  tick(step)
end


4
5
6
7
8
9
# File 'lib/scout/log/progress/report.rb', line 4

def print(io, str)
  return if self.severity && self.severity < Log.severity
  return if Log.no_bar
  Log.log_write str
  Log::LAST.replace "progress"
end

#remove(error = false) ⇒ Object



81
82
83
# File 'lib/scout/log/progress/util.rb', line 81

def remove(error = false)
  Log::ProgressBar.remove_bar self, error
end

#report(io = STDERR) ⇒ Object



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/scout/log/progress/report.rb', line 168

def report(io = STDERR)
  if Log::LAST != "progress"
    bars = BARS
    if Log::LAST == "new_bar"
      Log::LAST.replace "progress"
      bar = bars.sort_by{|b| b.depth }.first
      print(io, Log.color(:magenta ,bar.report_msg) << "\n") 
    else
      print(io, Log.color(:magenta, "···Progress\n"))
      bars.sort_by{|b| b.depth }.reverse.each do |bar|
        if SILENCED.include? bar
          print(io, Log.color(:magenta, "·\n")) 
        else
          print(io, Log.color(:magenta ,bar.report_msg) << "\n") 
        end
      end
    end
  else
    bars = BARS
  end
  bars << self unless BARS.include? self

  print(io, Log.up_lines(bars.length) << Log.color(:magenta, "···Progress\n") << Log.down_lines(bars.length+1)) if Log::ProgressBar.offset == 0
  print(io, Log.up_lines(@depth) << report_msg << "\n" << Log.down_lines(@depth - 1)) 
  @last_time = Time.now
  @last_count = ticks
  @last_percent = percent if max and max > 0
  Log::LAST.replace "progress"
  save if file
end

#report_msgObject



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/scout/log/progress/report.rb', line 119

def report_msg
  str = Log.color(:magenta, "·")
  if @ticks == 0
    if @max
      return str << " " << Log.color(:magenta, "waiting on #{@max} #{bytes ? 'bytes' : 'items'}") <<  Log.color(:magenta, " · " << desc)
    else
      return str << " " << Log.color(:magenta, "waiting - PID: #{Process.pid}") <<  Log.color(:magenta, " · " << desc)
    end
  end
  str << " " << thr_msg
  if max
    str << Log.color(:blue, " -- ") << eta_msg
  else
    str << Log.color(:blue, " -- ") << ticks.to_s << " #{bytes ? 'bytes' : 'items'}"
  end
  str <<  Log.color(:magenta, " · " << desc)
  str
end

#saveObject



161
162
163
164
165
166
# File 'lib/scout/log/progress/report.rb', line 161

def save
  info = {:start => @start, :last_time => @last_time, :last_count => @last_count, :last_percent => @last_percent, :desc => @desc, :ticks => @ticks, :max => @max, :mean => @mean}
  info.delete_if{|k,v| v.nil?}
  FileUtils.mkdir_p File.dirname(file) unless File.exist?(File.dirname(file))
  File.write(file, info.to_yaml)
end

#thr_msgObject



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/scout/log/progress/report.rb', line 12

def thr_msg
  if @history.nil?
    @history ||= [[0, @start], [@ticks, Time.now] ]
  elsif @last_ticks != @ticks
    @history << [@ticks, Time.now]
    max_history ||= begin
                      max_history = case 
                                    when @ticks > 20
                                      count = @ticks - @last_count
                                      count = 1 if count == 0
                                      if @max
                                        times = @max / count
                                        num = times / 20
                                        num = 2 if num < 2
                                      else
                                        num = 10
                                      end
                                      count * num
                                    else
                                      20
                                    end
                      max_history = 30 if max_history > 30
                      max_history
                    end
    @history.shift if @history.length > max_history
  end

  @last_ticks = @ticks

  @mean_max ||= 0
  if @history.length > 3

    sticks, stime = @history.first
    ssticks, sstime = @history[-3]
    lticks, ltime = @history.last


    mean = @mean = (lticks - sticks).to_f / (ltime - stime)
    short_mean = (lticks - ssticks).to_f / (ltime - sstime)

    @mean_max = mean if mean > @mean_max
  end

  if short_mean
    thr = short_mean
  else
    thr = begin
            d = Time.now - @start
            if d == 0
              1
            else
              (@ticks || 1) / d
            end
          end
  end

  thr = 0.0000001 if thr == 0
  
  if mean.nil? or mean.to_i > 2
    str = "#{ Log.color :blue, thr.to_i.to_s } per sec."
    #str << " #{ Log.color :yellow, mean.to_i.to_s } avg. #{Log.color :yellow, @mean_max.to_i.to_s} max." if @mean_max > 0
  else
    if 1.0/thr < 1
      str = "#{ Log.color :blue, (1.0/thr).round(2).to_s } secs each"
    elsif 1.0/thr < 2
      str = "#{ Log.color :blue, (1.0/thr).round(1).to_s } secs each"
    else
      str = "#{ Log.color :blue, (1/thr).ceil.to_s } secs each"
    end
    #str << " #{ Log.color :yellow, (1/mean).ceil.to_s } avg. #{Log.color :yellow, (1/@mean_max).ceil.to_s} min." if @mean_max > 0
  end

  str
end

#tick(step = 1) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/scout/log/progress.rb', line 64

def tick(step = 1)
  return if Log.no_bar
  @ticks += step

  time = Time.now
  if @last_time.nil?
    @last_time = time
    @last_count = @ticks
    @start = time
    return
  end

  diff = time - @last_time
  report and return if diff >= @frequency
  return unless max and max > 0

  percent = self.percent
  if @last_percent.nil?
    @last_percent = percent
    return
  end
  report && return if percent > @last_percent and diff > 0.3
end