93 lines
2.3 KiB
Ruby
93 lines
2.3 KiB
Ruby
class PageViewRollup
|
|
ATTRIBUTES_PRESERVED = %i[article_id created_at user_id].freeze
|
|
ATTRIBUTES_DESTROYED = %i[id domain path referrer updated_at user_agent counts_for_number_of_views
|
|
time_tracked_in_seconds].freeze
|
|
|
|
class ViewAggregator
|
|
Compact = Struct.new(:views, :article_id, :user_id) do
|
|
def to_h
|
|
super.except(:views).merge({
|
|
counts_for_number_of_views: views.sum(&:counts_for_number_of_views),
|
|
time_tracked_in_seconds: views.sum(&:time_tracked_in_seconds)
|
|
})
|
|
end
|
|
end
|
|
|
|
def initialize
|
|
@aggregator = Hash.new do |level1, article_id|
|
|
level1[article_id] = Hash.new do |level2, user_id|
|
|
level2[user_id] = []
|
|
end
|
|
end
|
|
end
|
|
|
|
def <<(view)
|
|
@aggregator[view.article_id][view.user_id] << view
|
|
end
|
|
|
|
def each
|
|
@aggregator.each_pair do |article_id, grouped_by_article_id|
|
|
grouped_by_article_id.each_pair do |user_id, views|
|
|
next unless views.size > 1
|
|
|
|
yield Compact.new(views, article_id, user_id)
|
|
end
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
attr_reader :aggregator
|
|
end
|
|
|
|
def self.rollup(date, relation: PageView)
|
|
new(relation: relation).rollup(date)
|
|
end
|
|
|
|
def initialize(relation:)
|
|
@relation = relation
|
|
end
|
|
|
|
attr_reader :relation
|
|
|
|
def rollup(date)
|
|
created = []
|
|
fixed_date = date.to_datetime.beginning_of_day
|
|
|
|
(0..23).each do |hour|
|
|
start_hour = fixed_date.change(hour: hour)
|
|
end_hour = fixed_date.change(hour: hour + 1)
|
|
rows = relation.where(user_id: nil, created_at: start_hour...end_hour)
|
|
aggregate_into_groups(rows).each do |compacted_views|
|
|
created << compact_records(start_hour, compacted_views)
|
|
end
|
|
end
|
|
|
|
created
|
|
end
|
|
|
|
private
|
|
|
|
def aggregate_into_groups(rows)
|
|
aggregator = ViewAggregator.new
|
|
rows.in_batches.each_record do |event|
|
|
aggregator << event
|
|
end
|
|
|
|
aggregator
|
|
end
|
|
|
|
def compact_records(date, compacted)
|
|
result = nil
|
|
|
|
relation.transaction do
|
|
result = relation.create!(compacted.to_h) do |event|
|
|
event.created_at = date
|
|
end
|
|
|
|
relation.where(id: compacted.views).delete_all
|
|
end
|
|
|
|
result
|
|
end
|
|
end
|