lib/rcs-common/stats.rb
#
# Class to keep statistics by minute, hour, day, week and so on...
# to be subclassed and customized
#
module RCS
class Stats
def initialize
@stats = {}
# the template can be provided by the subclass
@template ||= Hash.new(0)
# total statistics ever
@stats[:total] = @template.dup
# remember the time when the total count was started
@stats[:total][:start] = Time.now.getutc
# sections to keep stats for
# the values are the tipping point
@sections ||= {:minutes => 0, :hours => 60, :days => 24, :weeks => 7}
# create the templates for each section
@sections.each_key do |section|
@stats[section] = {}
@stats[section][:last] = Array.new(5) { @template.dup }
@stats[section][:average] = @template.dup
@stats[section][:average][:samples] = 0
end
end
def stats(section = nil)
section ? @stats[section] : @stats
end
def add(hash)
# for each key sum up the counters in the total and in the current minute
hash.each_pair do |k, v|
next if @stats[:total][k].nil? or v.nil?
@stats[:total][k] += v
@sections.keys.each do |section|
add_to_section section, k, v
end
end
end
def add_to_section(section, k, v)
@stats[section][:last][0][k] ||= 0
@stats[section][:last][0][k] += v
end
def calculate
calculate_section @sections.keys.first
# each value in the @sections is the number of element of the previous section
# needed to form the current section
@sections.keys.each_with_index do |section, index|
calculate_section(section) if sample_limit(index)
end
end
def calculate_section(section)
# calculate the average for this section
@stats[section][:last].first.each_pair do |k, v|
@stats[section][:average][k] = ((@stats[section][:average][k].to_f * @stats[section][:average][:samples] + v) / (@stats[section][:average][:samples] + 1)).round(2)
end
@stats[section][:average][:samples] += 1
# initialize a new element for the current section
@stats[section][:last].insert(0, @template.dup)
# remove the last one
@stats[section][:last].pop
end
def sample_limit(index)
# tipping point of 0 means never tip
return false if @sections[@sections.keys[index]] == 0
# all the previous must be on the tipping point
values = []
index.downto(1) do |i|
prev = @sections.keys[i - 1]
values[i] = @stats[prev][:average][:samples] != 0 && @stats[prev][:average][:samples] % @sections[@sections.keys[i]] == 0
end
values.compact.inject(:&)
end
end
end #RCS