Skip to content

Instantly share code, notes, and snippets.

@amkisko
Last active March 27, 2024 12:46
Show Gist options
  • Save amkisko/dda3470b3cd6024bf1658b43a697e626 to your computer and use it in GitHub Desktop.
Save amkisko/dda3470b3cd6024bf1658b43a697e626 to your computer and use it in GitHub Desktop.
Metric record helper
module MetricRecord
extend ActiveSupport::Concern
included do
validates :date, :group, :period, presence: true
validates :date, uniqueness: {scope: %i[group period]}
scope :for_date,
->(date) do
where(date: date_for_period(date, periods.keys.first))
end
scope :for_period_date,
->(period, date) do
where(date: range_for_period(date, period))
end
def self.date_for_period(date, period = periods.keys.first)
date.to_datetime.method(:"beginning_of_#{period}").call.to_date
end
def self.range_for_period(date, period = periods.keys.first)
from_date = date.to_datetime.method(:"beginning_of_#{period}").call.to_date
until_date = from_date.method(:"end_of_#{period}").call.to_date
from_date..until_date
end
def self.increment!(group:, date: Date.current, period: periods.keys.first, value: 1)
group_id = groups.fetch(group.to_s)
period_id = periods.fetch(period.to_s)
period_date = date_for_period(date)
query = <<-SQL
INSERT INTO #{table_name} ("group", "period", "date", "value", "created_at", "updated_at")
VALUES ('#{group_id}', '#{period_id}', '#{period_date}', #{connection.quote(value)}, NOW(), NOW())
ON CONFLICT ("group", "period", "date")
DO UPDATE SET value = #{table_name}.value + EXCLUDED.value, updated_at = NOW()
SQL
connection.execute(query)
end
def self.increment(group, date: Date.current, lock: nil)
lock_updated = false
if lock
lock_key = [name, group, date, lock]
return if Rails.cache.read(lock_key)
Rails.cache.write(lock_key, true, expires_in: 1.day)
lock_updated = true
end
increment!(group: group, date: date)
true
rescue
Rails.cache.delete(lock_key) if lock_updated
ActionReporter.notify("Failed to store metric increase", context: {args: args})
false
end
def self.group_by_periods(date: Date.current, periods: self.periods.keys)
queries = []
periods.each do |period|
range = range_for_period(date, period)
queries << <<-SQL
SELECT "group", #{connection.quote(period)} AS "period", SUM(value) AS value
FROM #{table_name}
WHERE "date" >= '#{range.begin}' AND "date" <= '#{range.end}'
GROUP BY "group"
SQL
end
query = queries.join(" UNION ")
connection.execute(query).each_with_object({}) do |row, result|
group = groups.key(row["group"])
period = row["period"]
result[group] ||= {}
result[group][period] = row["value"]
end
end
end
def range
self.class.range_for_period(date, period)
end
def calculator
raise "Not implemented"
end
def calculate_values
self.value = calculator.calculate
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment