Traccia i movimenti aggregati, overlay a gradienti sull’iframe della pagina e toggle click/move in admin. Co-authored-by: Cursor <cursoragent@cursor.com>
87 lines
3.0 KiB
Ruby
87 lines
3.0 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Analytics
|
|
class Aggregate
|
|
BATCH = 500
|
|
|
|
def call
|
|
loop do
|
|
ids = AnalyticsEvent.order(:occurred_at).limit(BATCH).pluck(:id)
|
|
break if ids.empty?
|
|
|
|
events = AnalyticsEvent.where(id: ids).to_a
|
|
apply_points(events.select { |e| e.event_type == "click" }, :click_count)
|
|
apply_points(events.select { |e| e.event_type == "move" }, :move_count)
|
|
apply_stats(events.select { |e| %w[pageview scroll].include?(e.event_type) })
|
|
AnalyticsEvent.where(id: ids).delete_all
|
|
end
|
|
end
|
|
|
|
def purge_old!(retention: 30.days)
|
|
AnalyticsEvent.where("occurred_at < ?", retention.ago).delete_all
|
|
AnalyticsPageCell.where("day < ?", retention.ago.to_date).delete_all
|
|
AnalyticsPageStat.where("day < ?", retention.ago.to_date).delete_all
|
|
end
|
|
|
|
private
|
|
|
|
def apply_points(events, counter_attr)
|
|
return if events.empty?
|
|
|
|
grid = AnalyticsPageCell::GRID_SIZE
|
|
grouped = Hash.new(0)
|
|
events.each do |e|
|
|
next if e.x_pct.nil? || e.y_pct.nil?
|
|
|
|
cell_x = [[(e.x_pct.to_f / 100 * grid).floor, grid - 1].min, 0].max
|
|
cell_y = [[(e.y_pct.to_f / 100 * grid).floor, grid - 1].min, 0].max
|
|
key = [e.occurred_at.in_time_zone.to_date, e.page_path, e.device, cell_x, cell_y]
|
|
grouped[key] += e.weight.to_i.clamp(1, 500)
|
|
end
|
|
|
|
now = Time.current
|
|
grouped.each do |(day, page_path, device, cell_x, cell_y), count|
|
|
cell = AnalyticsPageCell.find_or_initialize_by(
|
|
day: day, page_path: page_path, device: device, cell_x: cell_x, cell_y: cell_y
|
|
)
|
|
cell[counter_attr] = cell[counter_attr].to_i + count
|
|
cell.created_at ||= now
|
|
cell.updated_at = now
|
|
cell.save!
|
|
end
|
|
end
|
|
|
|
def apply_stats(events)
|
|
return if events.empty?
|
|
|
|
grouped = {}
|
|
events.each do |e|
|
|
day = e.occurred_at.in_time_zone.to_date
|
|
key = [day, e.page_path, e.device]
|
|
bucket = grouped[key] ||= { pageviews: 0, scroll_samples: 0, scroll_sum: 0, max_scroll: 0 }
|
|
case e.event_type
|
|
when "pageview"
|
|
bucket[:pageviews] += e.weight.to_i.clamp(1, 500)
|
|
when "scroll"
|
|
pct = e.scroll_pct.to_f.round
|
|
bucket[:scroll_samples] += 1
|
|
bucket[:scroll_sum] += pct
|
|
bucket[:max_scroll] = [bucket[:max_scroll], pct].max
|
|
end
|
|
end
|
|
|
|
now = Time.current
|
|
grouped.each do |(day, page_path, device), vals|
|
|
stat = AnalyticsPageStat.find_or_initialize_by(day: day, page_path: page_path, device: device)
|
|
stat.pageview_count = stat.pageview_count.to_i + vals[:pageviews]
|
|
stat.scroll_samples = stat.scroll_samples.to_i + vals[:scroll_samples]
|
|
stat.scroll_sum_pct = stat.scroll_sum_pct.to_i + vals[:scroll_sum]
|
|
stat.max_scroll_pct = [stat.max_scroll_pct.to_i, vals[:max_scroll]].max
|
|
stat.created_at ||= now
|
|
stat.updated_at = now
|
|
stat.save!
|
|
end
|
|
end
|
|
end
|
|
end
|