Add feed event for extended page view (#20198)
This commit is contained in:
parent
b67d5c4358
commit
8268a59ae5
4 changed files with 52 additions and 12 deletions
|
|
@ -12,7 +12,8 @@ class FeedEvent < ApplicationRecord
|
|||
impression: 0,
|
||||
click: 1,
|
||||
reaction: 2,
|
||||
comment: 3
|
||||
comment: 3,
|
||||
extended_pageview: 4
|
||||
}
|
||||
|
||||
CONTEXT_TYPE_HOME = "home".freeze
|
||||
|
|
@ -25,8 +26,8 @@ class FeedEvent < ApplicationRecord
|
|||
].freeze
|
||||
DEFAULT_TIMEBOX = 5.minutes.freeze
|
||||
|
||||
REACTION_SCORE_MULTIPLIER = 5
|
||||
COMMENT_SCORE_MULTIPLIER = 10
|
||||
REACTION_SCORE_MULTIPLIER = 6
|
||||
COMMENT_SCORE_MULTIPLIER = 12
|
||||
|
||||
validates :article_position, numericality: { only_integer: true, greater_than: 0 }
|
||||
validates :context_type, inclusion: { in: VALID_CONTEXT_TYPES }, presence: true
|
||||
|
|
@ -35,7 +36,7 @@ class FeedEvent < ApplicationRecord
|
|||
validates :user_id, numericality: { only_integer: true }, allow_nil: true
|
||||
|
||||
def self.record_journey_for(user, article:, category:)
|
||||
return unless %i[reaction comment].include?(category)
|
||||
return unless %i[reaction comment extended_pageview].include?(category)
|
||||
|
||||
last_click = where(user: user, category: :click).last
|
||||
return unless last_click&.article_id == article.id
|
||||
|
|
@ -67,19 +68,22 @@ class FeedEvent < ApplicationRecord
|
|||
clicks = FeedEvent.where(article_id: article_id, category: "click")
|
||||
reactions = FeedEvent.where(article_id: article_id, category: "reaction")
|
||||
comments = FeedEvent.where(article_id: article_id, category: "comment")
|
||||
pageviews = FeedEvent.where(article_id: article_id, category: "extended_pageview")
|
||||
|
||||
# Count the distinct users for impressions and each event type
|
||||
distinct_impressions_users = impressions.distinct.pluck(:user_id)
|
||||
distinct_clicks_users = clicks.distinct.pluck(:user_id)
|
||||
distinct_reactions_users = reactions.distinct.pluck(:user_id)
|
||||
distinct_comments_users = comments.distinct.pluck(:user_id)
|
||||
distinct_pageviews_users = pageviews.distinct.pluck(:user_id)
|
||||
|
||||
# Calculate score based on distinct users
|
||||
reactions_score = distinct_reactions_users.size * REACTION_SCORE_MULTIPLIER
|
||||
clicks_score = distinct_clicks_users.size # 1x multiplier for clicks
|
||||
comments_score = distinct_comments_users.size * COMMENT_SCORE_MULTIPLIER
|
||||
pageviews_score = distinct_pageviews_users.size # 1x multiplier for extended pageviews
|
||||
|
||||
score = (clicks_score + reactions_score + comments_score).to_f / distinct_impressions_users.size
|
||||
score = (clicks_score + pageviews_score + reactions_score + comments_score).to_f / distinct_impressions_users.size
|
||||
|
||||
# Update the article counters
|
||||
Article.where(id: article_id).update_all(
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ module Articles
|
|||
# @see Articles::UpdatePageViewsWorker for the sibling that's responsible for recording page
|
||||
# views.
|
||||
module PageViewUpdater
|
||||
EXTENDED_PAGEVIEW_NUMBER = 60
|
||||
# @param article_id [Integer]
|
||||
# @param user_id [Integer]
|
||||
#
|
||||
|
|
@ -22,10 +23,13 @@ module Articles
|
|||
|
||||
page_view = PageView.order(created_at: :desc)
|
||||
.find_or_create_by(article_id: article_id, user_id: user_id)
|
||||
|
||||
return true if page_view.new_record?
|
||||
|
||||
page_view.update_column(:time_tracked_in_seconds, page_view.time_tracked_in_seconds + 15)
|
||||
new_time_mark = page_view.time_tracked_in_seconds + 15
|
||||
page_view.update_column(:time_tracked_in_seconds, new_time_mark)
|
||||
if new_time_mark == EXTENDED_PAGEVIEW_NUMBER
|
||||
FeedEvent.record_journey_for(page_view.user, article: page_view.article, category: :extended_pageview)
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ require "rails_helper"
|
|||
RSpec.describe FeedEvent do
|
||||
let(:reaction_multiplier) { FeedEvent::REACTION_SCORE_MULTIPLIER }
|
||||
let(:comment_multiplier) { FeedEvent::COMMENT_SCORE_MULTIPLIER }
|
||||
let(:valid_categories) { %w[impression click reaction comment extended_pageview] }
|
||||
|
||||
describe "validations" do
|
||||
it { is_expected.to belong_to(:article).optional }
|
||||
|
|
@ -10,7 +11,7 @@ RSpec.describe FeedEvent do
|
|||
it { is_expected.to belong_to(:user).optional }
|
||||
it { is_expected.to validate_numericality_of(:user_id).only_integer.allow_nil }
|
||||
|
||||
it { is_expected.to define_enum_for(:category).with_values(%i[impression click reaction comment]) }
|
||||
it { is_expected.to define_enum_for(:category).with_values(valid_categories) }
|
||||
it { is_expected.to validate_numericality_of(:article_position).is_greater_than(0).only_integer }
|
||||
it { is_expected.to validate_inclusion_of(:context_type).in_array(%w[home search tag]) }
|
||||
end
|
||||
|
|
@ -113,7 +114,7 @@ RSpec.describe FeedEvent do
|
|||
|
||||
article.reload
|
||||
|
||||
expect(article.feed_success_score).to eq(5.0) # One reaction by one distinct user
|
||||
expect(article.feed_success_score).to eq(reaction_multiplier) # One reaction by one distinct user
|
||||
expect(article.feed_impressions_count).to eq(1) # One impression
|
||||
expect(article.feed_clicks_count).to eq(0) # Zero clicks
|
||||
end
|
||||
|
|
@ -123,13 +124,14 @@ RSpec.describe FeedEvent do
|
|||
create_list(:feed_event, 4, category: "click", article: article, user: user1)
|
||||
create_list(:feed_event, 3, category: "reaction", article: article, user: user2)
|
||||
create_list(:feed_event, 2, category: "comment", article: article, user: user2)
|
||||
create_list(:feed_event, 3, category: "extended_pageview", article: article, user: user2)
|
||||
|
||||
# Trigger the after_save
|
||||
create(:feed_event, category: "impression", article: article, user: user2)
|
||||
|
||||
article.reload
|
||||
|
||||
expect(article.feed_success_score).to eq((1 + reaction_multiplier + comment_multiplier) / 2.0) # Calculated score
|
||||
expect(article.feed_success_score).to eq((1 + 1 + reaction_multiplier + comment_multiplier) / 2.0)
|
||||
expect(article.feed_clicks_count).to eq(4)
|
||||
expect(article.feed_impressions_count).to eq(3)
|
||||
end
|
||||
|
|
@ -149,6 +151,7 @@ RSpec.describe FeedEvent do
|
|||
create(:feed_event, category: "click", article: article1, user: user1)
|
||||
create(:feed_event, category: "reaction", article: article1, user: user1)
|
||||
create(:feed_event, category: "comment", article: article1, user: user2)
|
||||
create(:feed_event, category: "extended_pageview", article: article1, user: user2)
|
||||
|
||||
# Create some feed events for article2
|
||||
create(:feed_event, category: "impression", article: article2, user: user2)
|
||||
|
|
@ -160,11 +163,11 @@ RSpec.describe FeedEvent do
|
|||
article1.reload
|
||||
article2.reload
|
||||
|
||||
expect(article1.feed_success_score).to eq((1 + reaction_multiplier + comment_multiplier) / 2.0) # Calculated score
|
||||
expect(article1.feed_success_score).to eq((1 + 1 + reaction_multiplier + comment_multiplier) / 2.0)
|
||||
expect(article1.feed_impressions_count).to eq(3)
|
||||
expect(article1.feed_clicks_count).to eq(1)
|
||||
|
||||
expect(article2.feed_success_score).to eq((1 + 5) / 1.0) # Calculated score
|
||||
expect(article2.feed_success_score).to eq((1 + reaction_multiplier) / 1.0) # Calculated score
|
||||
expect(article2.feed_impressions_count).to eq(1)
|
||||
expect(article2.feed_clicks_count).to eq(1)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -29,5 +29,34 @@ RSpec.describe Articles::PageViewUpdater do
|
|||
expect { method_call }.not_to change(PageView, :count)
|
||||
end
|
||||
end
|
||||
|
||||
context "when time count equals EXTENDED_PAGEVIEW_NUMBER" do
|
||||
let(:article) { create(:article, user: create(:user)) }
|
||||
|
||||
before do
|
||||
create(:feed_event, user: user, article: article, category: :click)
|
||||
end
|
||||
|
||||
it "sends a feed event journey when it receives a page view length of 60" do
|
||||
4.times do
|
||||
described_class.call(article_id: article.id, user_id: user.id)
|
||||
end
|
||||
expect(FeedEvent.last.category).to eq("extended_pageview")
|
||||
end
|
||||
|
||||
it "does not send feed event journey when it receives a page view length of less than 60" do
|
||||
2.times do
|
||||
described_class.call(article_id: article.id, user_id: user.id)
|
||||
end
|
||||
expect(FeedEvent.all.size).to be 1
|
||||
end
|
||||
|
||||
it "only sends one event when it passes through the 60 range" do
|
||||
8.times do
|
||||
described_class.call(article_id: article.id, user_id: user.id)
|
||||
end
|
||||
expect(FeedEvent.all.size).to be 2
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue