Module: Fasten::Support::Stats

Included in:
Runner
Defined in:
lib/fasten/support/stats.rb

Constant Summary collapse

FLOAT_FORMATTER =
->(f) { format('%7.3f', f) }

Instance Method Summary collapse

Instance Method Details

#hformat(time, total = nil) ⇒ Object



106
107
108
109
110
111
112
113
# File 'lib/fasten/support/stats.rb', line 106

def hformat(time, total = nil)
  sign, hours, mins, secs, msecs = split_time time

  str = hours.zero? ? format('%.1s%02d:%02d.%03d', sign, mins, secs, msecs) : format('%.1s%02d:%02d:%02d.%03d', sign, hours, mins, secs, msecs)
  str += format(' (%.1f%%)', 100.0 * time / total) if total

  str
end

#initialize_statsObject

attr_writer :stats_data, :stats_entries attr_reader :stats_path



11
12
13
14
15
16
17
18
# File 'lib/fasten/support/stats.rb', line 11

def initialize_stats
  return unless stats

  @stats_path = "#{ENV['HOME']}/.fasten/stats/#{name}.csv" if ENV['HOME']
  FileUtils.mkdir_p File.dirname(@stats_path)
rescue StandardError
  @stats_path = nil
end

#load_statsObject



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/fasten/support/stats.rb', line 20

def load_stats
  return unless @stats_path && File.exist?(@stats_path)

  @stats_data = []
  CSV.foreach(@stats_path, headers: true, converters: [:all]) do |row|
    stats_data << row.to_h
  end

  @tasks.each do |task|
    task.runner = self
    task.last_stat
  end
  self.runner = self
  last_stat

  @tasks.waiting = nil
rescue StandardError
  nil
end

#save_statsObject



40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/fasten/support/stats.rb', line 40

def save_stats
  return unless @stats_path && stats_data

  keys = %w[state kind name run cnt avg std err ini fin deps]

  CSV.open(@stats_path, 'wb') do |csv|
    csv << keys

    stats_data.each do |data|
      csv << keys.map { |i| data[i] }
    end
  end
end

#split_time(time) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
# File 'lib/fasten/support/stats.rb', line 94

def split_time(time)
  sign = time.negative? ? '-' : ''
  time = -time if time.negative?

  hours, seconds = time.divmod(3600)
  minutes, seconds = seconds.divmod(60)
  seconds, decimal = seconds.divmod(1)
  milliseconds, _ignored = (decimal.round(4) * 1000).divmod(1)

  [sign, hours, minutes, seconds, milliseconds]
end

#stats_add_entry(state, target) ⇒ Object



73
74
75
76
77
78
79
80
81
82
83
# File 'lib/fasten/support/stats.rb', line 73

def stats_add_entry(state, target)
  return unless target.ini && target.fin

  entry = stats_create_entry(state, target)
  stats_data << entry
  stats_entries << entry

  history = stats_run_history(entry)

  update_stats(history, entry)
end

#stats_create_entry(state, target) ⇒ Object



54
55
56
57
58
59
60
61
62
63
# File 'lib/fasten/support/stats.rb', line 54

def stats_create_entry(state, target)
  { 'state'  => state.to_s,
    'kind'   => target.kind,
    'name'   => target.name,
    'ini'    => target.ini.to_f,
    'fin'    => target.fin.to_f,
    'run'    => target.fin - target.ini,
    'deps'   => target.deps,
    'worker' => target.respond_to?(:worker) ? target.worker.name : nil }
end

#stats_dataObject



65
66
67
# File 'lib/fasten/support/stats.rb', line 65

def stats_data
  @stats_data ||= []
end

#stats_entriesObject



69
70
71
# File 'lib/fasten/support/stats.rb', line 69

def stats_entries
  @stats_entries ||= []
end

#stats_last(target) ⇒ Object



131
132
133
# File 'lib/fasten/support/stats.rb', line 131

def stats_last(target)
  stats_data.select { |item| %w[kind name deps].all? { |key| item[key] == target.send(key) } }.last || {}
end

#stats_run_history(entry) ⇒ Object



127
128
129
# File 'lib/fasten/support/stats.rb', line 127

def stats_run_history(entry)
  stats_data.select { |item| %w[state kind name deps].all? { |key| item[key] == entry[key] } }.map { |item| item['run'] }
end

#stats_summaryObject



115
116
117
118
119
120
121
122
123
124
125
# File 'lib/fasten/support/stats.rb', line 115

def stats_summary
  sub, tot = stats_summary_data

  Hirb::Console.render_output(stats_entries,
                              fields: %w[state kind name run cnt avg std err worker], unicode: true, class: 'Hirb::Helpers::AutoTable',
                              filters: { 'run' => FLOAT_FORMATTER, 'avg' => FLOAT_FORMATTER, 'std' => FLOAT_FORMATTER, 'err' => FLOAT_FORMATTER },
                              description: false)

  puts format('∑tasks: %<task>s runner: %<runner>s saved: %<saved>s jobs: %<jobs>s',
              task: hformat(sub), runner: hformat(tot, sub), saved: hformat(sub - tot, sub), jobs: jobs.to_s)
end

#stats_summary_dataObject



87
88
89
90
91
92
# File 'lib/fasten/support/stats.rb', line 87

def stats_summary_data
  sub = stats_entries.select { |x| x['kind'] == 'task' }.map { |x| x['run'] }.sum
  tot = stats_entries.select { |x| x['kind'] == 'runner' }.map { |x| x['run'] }.sum

  [sub, tot]
end

#update_stats(history, entry) ⇒ Object



135
136
137
138
139
140
# File 'lib/fasten/support/stats.rb', line 135

def update_stats(history, entry)
  entry['cnt'] = cnt = history.size
  entry['avg'] = avg = history.sum.to_f / cnt
  entry['std'] = std = Math.sqrt(history.map { |x| (x - avg)**2 }.sum / cnt)
  entry['err'] = std / Math.sqrt(cnt) if cnt.positive?
end