Add page_view model (#1985)
* Add page_view model * Uncomment DJ routes * Add time on site max and don't update article page views every time
This commit is contained in:
parent
a18a4523a1
commit
825aa9be91
10 changed files with 250 additions and 1 deletions
|
|
@ -116,6 +116,34 @@ function trackCustomImpressions() {
|
|||
successLinks[i].addEventListener('click', function() { trackHtmlVariantSuccess(dataBody, csrfToken) });
|
||||
}
|
||||
}
|
||||
|
||||
// page view
|
||||
if (ArticleElement && tokenMeta && !isBot) {
|
||||
var randomNumber = Math.floor(Math.random() * 10); // 1 in 10; Only track 1 in 10 impressions
|
||||
if (!checkUserLoggedIn() && randomNumber != 1) {
|
||||
return;
|
||||
}
|
||||
var dataBody = {
|
||||
article_id: ArticleElement.dataset.articleId,
|
||||
referrer: document.referrer,
|
||||
user_agent: navigator.userAgent,
|
||||
};
|
||||
var csrfToken = tokenMeta.getAttribute('content');
|
||||
trackPageView(dataBody, csrfToken);
|
||||
var timeOnSiteCounter = 0;
|
||||
var timeOnSiteInterval = setInterval(function(){
|
||||
timeOnSiteCounter++
|
||||
var ArticleElement = document.getElementById('article-body');
|
||||
if (ArticleElement && checkUserLoggedIn()) {
|
||||
trackFifteenSecondsOnPage(ArticleElement.dataset.articleId, csrfToken);
|
||||
} else {
|
||||
clearInterval(timeOnSiteInterval);
|
||||
}
|
||||
if ( timeOnSiteCounter > 118 ) {
|
||||
clearInterval(timeOnSiteInterval);
|
||||
}
|
||||
}, 15000)
|
||||
}
|
||||
}, 1500)
|
||||
}
|
||||
|
||||
|
|
@ -144,4 +172,28 @@ function trackHtmlVariantSuccess(dataBody, csrfToken) {
|
|||
body: JSON.stringify(dataBody),
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
}
|
||||
|
||||
function trackPageView(dataBody, csrfToken) {
|
||||
window.fetch('/page_views', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-Token': csrfToken,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(dataBody),
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
}
|
||||
|
||||
function trackFifteenSecondsOnPage(articleId, csrfToken) {
|
||||
window.fetch('/page_views/' + articleId, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'X-CSRF-Token': csrfToken,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
|
||||
}
|
||||
40
app/controllers/page_views_controller.rb
Normal file
40
app/controllers/page_views_controller.rb
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
class PageViewsController < ApplicationController
|
||||
# No policy needed. All views are for all users
|
||||
def create
|
||||
if user_signed_in?
|
||||
PageView.create(user_id: current_user.id,
|
||||
article_id: page_view_params[:article_id],
|
||||
referrer: page_view_params[:referrer],
|
||||
user_agent: page_view_params[:user_agent])
|
||||
else
|
||||
PageView.create(counts_for_number_of_views: 10,
|
||||
article_id: page_view_params[:article_id],
|
||||
referrer: page_view_params[:referrer],
|
||||
user_agent: page_view_params[:user_agent])
|
||||
end
|
||||
update_article_page_views
|
||||
head :ok
|
||||
end
|
||||
|
||||
def update
|
||||
if user_signed_in?
|
||||
page_view = PageView.where(article_id: params[:id], user_id: current_user.id).last
|
||||
page_view.update_column(:time_tracked_in_seconds, page_view.time_tracked_in_seconds + 15)
|
||||
end
|
||||
head :ok
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_article_page_views
|
||||
return if Rails.env.production? && rand(5) != 1 # We don't need to update the article page views every time.
|
||||
|
||||
article = Article.find(page_view_params[:article_id])
|
||||
new_page_views_count = article.page_views.sum(:counts_for_number_of_views)
|
||||
article.update_column(:page_views_count, new_page_views_count) if new_page_views_count > article.page_views_count
|
||||
end
|
||||
|
||||
def page_view_params
|
||||
params.require(:page_view).permit(%i[article_id referrer user_agent])
|
||||
end
|
||||
end
|
||||
|
|
@ -19,6 +19,7 @@ class Article < ApplicationRecord
|
|||
has_many :buffer_updates
|
||||
has_many :notifications, as: :notifiable
|
||||
has_many :rating_votes
|
||||
has_many :page_views
|
||||
|
||||
validates :slug, presence: { if: :published? }, format: /\A[0-9a-z-]*\z/,
|
||||
uniqueness: { scope: :user_id }
|
||||
|
|
|
|||
4
app/models/page_view.rb
Normal file
4
app/models/page_view.rb
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
class PageView < ApplicationRecord
|
||||
belongs_to :user, optional: true
|
||||
belongs_to :article
|
||||
end
|
||||
|
|
@ -37,6 +37,7 @@ class User < ApplicationRecord
|
|||
has_many :feedback_messages
|
||||
has_many :rating_votes
|
||||
has_many :html_variants, dependent: :destroy
|
||||
has_many :page_views
|
||||
has_many :mentor_relationships_as_mentee,
|
||||
class_name: "MentorRelationship", foreign_key: "mentee_id"
|
||||
has_many :mentor_relationships_as_mentor,
|
||||
|
|
|
|||
|
|
@ -131,6 +131,7 @@ Rails.application.routes.draw do
|
|||
resources :push_notification_subscriptions, only: [:create]
|
||||
resources :tag_adjustments, only: [:create]
|
||||
resources :rating_votes, only: [:create]
|
||||
resources :page_views, only: [:create, :update]
|
||||
|
||||
get "/notifications/:filter" => "notifications#index"
|
||||
get "/notifications/:filter/:org_id" => "notifications#index"
|
||||
|
|
|
|||
15
db/migrate/20190305221008_create_page_views.rb
Normal file
15
db/migrate/20190305221008_create_page_views.rb
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
class CreatePageViews < ActiveRecord::Migration[5.1]
|
||||
def change
|
||||
create_table :page_views do |t|
|
||||
t.bigint :user_id
|
||||
t.bigint :article_id
|
||||
t.integer :counts_for_number_of_views, default: 1
|
||||
t.integer :time_tracked_in_seconds, default: 15
|
||||
t.string :referrer
|
||||
t.string :user_agent
|
||||
t.timestamps
|
||||
end
|
||||
add_index :page_views, :user_id
|
||||
add_index :page_views, :article_id
|
||||
end
|
||||
end
|
||||
16
db/schema.rb
16
db/schema.rb
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20190227163803) do
|
||||
ActiveRecord::Schema.define(version: 20190305221008) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
||||
|
|
@ -519,6 +519,19 @@ ActiveRecord::Schema.define(version: 20190227163803) do
|
|||
t.index ["slug"], name: "index_organizations_on_slug", unique: true
|
||||
end
|
||||
|
||||
create_table "page_views", force: :cascade do |t|
|
||||
t.bigint "article_id"
|
||||
t.integer "counts_for_number_of_views", default: 1
|
||||
t.datetime "created_at", null: false
|
||||
t.string "referrer"
|
||||
t.integer "time_tracked_in_seconds", default: 15
|
||||
t.datetime "updated_at", null: false
|
||||
t.string "user_agent"
|
||||
t.bigint "user_id"
|
||||
t.index ["article_id"], name: "index_page_views_on_article_id"
|
||||
t.index ["user_id"], name: "index_page_views_on_user_id"
|
||||
end
|
||||
|
||||
create_table "podcast_episodes", id: :serial, force: :cascade do |t|
|
||||
t.text "body"
|
||||
t.integer "comments_count", default: 0, null: false
|
||||
|
|
@ -776,6 +789,7 @@ ActiveRecord::Schema.define(version: 20190227163803) do
|
|||
t.datetime "github_repos_updated_at", default: "2017-01-01 05:00:00"
|
||||
t.string "github_username"
|
||||
t.string "gitlab_url"
|
||||
t.string "inbox_type", default: "private"
|
||||
t.jsonb "language_settings", default: {}, null: false
|
||||
t.datetime "last_article_at", default: "2017-01-01 05:00:00"
|
||||
t.datetime "last_comment_at", default: "2017-01-01 05:00:00"
|
||||
|
|
|
|||
5
spec/models/page_view_spec.rb
Normal file
5
spec/models/page_view_spec.rb
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe PageView, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
||||
116
spec/requests/page_views_spec.rb
Normal file
116
spec/requests/page_views_spec.rb
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe "PageViews", type: :request do
|
||||
let(:user) { create(:user, :trusted) }
|
||||
let(:article) { create(:article) }
|
||||
|
||||
describe "POST /page_views" do
|
||||
context "when user signed in" do
|
||||
before do
|
||||
sign_in user
|
||||
end
|
||||
|
||||
it "creates a new page view" do
|
||||
post "/page_views", params: {
|
||||
page_view: {
|
||||
article_id: article.id
|
||||
}
|
||||
}
|
||||
expect(article.reload.page_views.size).to eq(1)
|
||||
expect(article.reload.page_views_count).to eq(1)
|
||||
expect(user.reload.page_views.size).to eq(1)
|
||||
expect(PageView.last.counts_for_number_of_views).to eq(1)
|
||||
end
|
||||
|
||||
it "sends referrer" do
|
||||
post "/page_views", params: {
|
||||
page_view: {
|
||||
article_id: article.id,
|
||||
referrer: "test"
|
||||
}
|
||||
}
|
||||
expect(PageView.last.referrer).to eq("test")
|
||||
end
|
||||
|
||||
it "sends user agent" do
|
||||
post "/page_views", params: {
|
||||
page_view: {
|
||||
article_id: article.id,
|
||||
user_agent: "test"
|
||||
}
|
||||
}
|
||||
expect(PageView.last.user_agent).to eq("test")
|
||||
end
|
||||
end
|
||||
|
||||
context "when user not signed in" do
|
||||
it "creates a new page view" do
|
||||
post "/page_views", params: {
|
||||
page_view: {
|
||||
article_id: article.id
|
||||
}
|
||||
}
|
||||
expect(article.reload.page_views.size).to eq(1)
|
||||
expect(article.reload.page_views_count).to eq(10)
|
||||
expect(user.reload.page_views.size).to eq(0)
|
||||
expect(PageView.last.counts_for_number_of_views).to eq(10)
|
||||
end
|
||||
|
||||
it "creates stores aggregate page views" do
|
||||
post "/page_views", params: { page_view: { article_id: article.id } }
|
||||
post "/page_views", params: { page_view: { article_id: article.id } }
|
||||
expect(article.reload.page_views_count).to eq(20)
|
||||
end
|
||||
|
||||
it "sends referrer" do
|
||||
post "/page_views", params: {
|
||||
page_view: {
|
||||
article_id: article.id,
|
||||
referrer: "test"
|
||||
}
|
||||
}
|
||||
expect(PageView.last.referrer).to eq("test")
|
||||
end
|
||||
|
||||
it "sends user agent" do
|
||||
post "/page_views", params: {
|
||||
page_view: {
|
||||
article_id: article.id,
|
||||
user_agent: "test"
|
||||
}
|
||||
}
|
||||
expect(PageView.last.user_agent).to eq("test")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "PUT /page_views/:id" do
|
||||
context "when user is signed in" do
|
||||
before do
|
||||
sign_in user
|
||||
end
|
||||
|
||||
it "updates a new page view time on page by 15" do
|
||||
post "/page_views", params: {
|
||||
page_view: {
|
||||
article_id: article.id
|
||||
}
|
||||
}
|
||||
put "/page_views/" + article.id.to_s
|
||||
expect(PageView.last.time_tracked_in_seconds).to eq(30)
|
||||
end
|
||||
end
|
||||
|
||||
context "when user is not signed in" do
|
||||
it "updates a new page view time on page by 15" do
|
||||
post "/page_views", params: {
|
||||
page_view: {
|
||||
article_id: article.id
|
||||
}
|
||||
}
|
||||
put "/page_views/" + article.id.to_s
|
||||
expect(PageView.last.time_tracked_in_seconds).to eq(15)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Reference in a new issue