class Mailings::HourlyThroughput HOUR = 1.hour RECENT = 15.minutes DEFAULT_RANGE = "12h" RANGES = { "12h" => { grain: :hour, periods: 12, short: "12h", label: "ultime 12 ore" }, "24h" => { grain: :hour, periods: 24, short: "24h", label: "ultime 24 ore" }, "7d" => { grain: :day, periods: 7, short: "7g", label: "ultima settimana" } }.freeze def self.normalize_range(value) key = value.to_s RANGES.key?(key) ? key : DEFAULT_RANGE end def initialize(project, now: Time.current, range: DEFAULT_RANGE) @project = project @now = now @range_key = self.class.normalize_range(range) end attr_reader :range_key def range RANGES.fetch(@range_key) end def sent @sent ||= sent_since(HOUR) end def recent_sent @recent_sent ||= sent_since(RECENT) end def failed @failed ||= recipients.failed.where(updated_at: (@now - HOUR)..@now).count end def drained sent + failed end def history @history ||= buckets_for_range end def history_total history.sum { |bucket| bucket[:count] } end def history_max [history.map { |bucket| bucket[:count] }.max, 1].max end private def buckets_for_range from, step = history_origin keyed = recipients.sent .where(sent_at: from..@now) .pluck(:sent_at) .each_with_object(Hash.new(0)) do |sent_at, counts| counts[bucket_at(sent_at)] += 1 end range[:periods].times.map do |index| at = from + (step * index) { at: at, count: keyed[at] || 0, label: bucket_label(at) } end end def history_origin if range[:grain] == :day from = (@now.to_date - (range[:periods] - 1)).in_time_zone.beginning_of_day [from, 1.day] else from = (@now - (range[:periods] - 1).hours).beginning_of_hour [from, 1.hour] end end def bucket_at(sent_at) time = sent_at.in_time_zone range[:grain] == :day ? time.beginning_of_day : time.beginning_of_hour end def bucket_label(at) range[:grain] == :day ? at.strftime("%d/%m") : at.strftime("%H:%M") end def sent_since(window) recipients.sent.where(sent_at: (@now - window)..@now).count end def recipients MailingRecipient.joins(:mailing).merge(Mailing.for_project(@project)) end end