Skip to content

Commit

Permalink
feat: add analytics to video table
Browse files Browse the repository at this point in the history
  • Loading branch information
danirod committed Jul 7, 2024
1 parent 278ad19 commit d3a2cbd
Show file tree
Hide file tree
Showing 14 changed files with 163 additions and 1 deletion.
7 changes: 7 additions & 0 deletions app/jobs/application_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class ApplicationJob < ActiveJob::Base
# Automatically retry jobs that encountered a deadlock
# retry_on ActiveRecord::Deadlocked

# Most jobs are safe to ignore if the underlying records are no longer available
# discard_on ActiveJob::DeserializationError
end
18 changes: 18 additions & 0 deletions app/jobs/materialize_video_analytics_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class MaterializeVideoAnalyticsJob < ApplicationJob
queue_as :default

def video_path(video)
Rails.application.routes.url_helpers.playlist_video_path(id: video.slug, playlist_id: video.playlist.slug)
end

def perform(*args)
total_data = IngestedAnalytic.recordset.group_by_page
recent_data = IngestedAnalytic.recordset.where('day >= ?', 30.days.ago).group_by_page
Video.includes(:playlist).find_each do |video|
url = video_path(video)
total = total_data[url]
recent = recent_data[url]
video.update(views_total: total, views_recent: recent)
end
end
end
33 changes: 33 additions & 0 deletions app/models/ingested_analytic.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# == Schema Information
#
# Table name: ingested_analytics
#
# id :bigint not null, primary key
# day :date not null
# document :jsonb not null
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_ingested_analytics_on_day (day)
#
class IngestedAnalytic < ApplicationRecord
validates :day, presence: true, uniqueness: true
validates :document, presence: true

# SELECT ...
# FROM ingested_analytics
# JOIN jsonb_to_recordset(ingested_analytics.document)
# AS row(page text, pageviews int)
# ON true
scope :recordset, -> { joins('JOIN jsonb_to_recordset(document) as row(page text, pageviews int) ON TRUE') }
scope :by_page, ->(page) { recordset.where(row: { page: page }) }
scope :group_by_page, -> {
result = select('page', 'sum(pageviews) AS sum').group('page')
result.map { |r| [r.page, r.sum] }.to_h
}

scope :total, -> { sum('pageviews') }
scope :data, -> { pluck('day', 'page', 'pageviews') }
end
2 changes: 2 additions & 0 deletions app/models/video.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
# slug :string not null
# tags :string default([]), is an Array
# title :string not null
# views_recent :integer default(0)
# views_total :integer default(0)
# created_at :datetime not null
# updated_at :datetime not null
# playlist_id :integer not null
Expand Down
27 changes: 27 additions & 0 deletions app/services/plausible/downloader.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# frozen_string_literal: true

module Plausible
class Downloader
class << self
def entity_for_day(day)
new(day).entity
end
end

def initialize(day)
@day = day
end

def entity
@entity ||= IngestedAnalytic.new(day: @day, document: analytics)
end

def analytics
@analytics ||= client.breakdown(@day)
end

def client
Plausible::Integration.client
end
end
end
2 changes: 2 additions & 0 deletions app/views/dashboard/videos/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
<th><%= link_to dashboard_derive_video_list_params(sort: @sorter.flip('updated')) do %>
<%= t('.last_update') %> <%= @sorter.arrow('updated') %>
<% end %></th>
<th>Views</th>
<th colspan="3"><%= t('.aspect') %></th>
</tr>
</thead>
Expand Down Expand Up @@ -65,6 +66,7 @@
<td><%= link_to video.playlist.title, [:dashboard, video.playlist] %></td>
<td><%= l video.published_at, format: :long %></td>
<td><%= distance_of_time_in_words video.updated_at, DateTime.now %></td>
<td>T <%= video.views_total %><br>R <%= video.views_recent %></td>
<td>
<% if video.transcription.present? %>
<span class="bi bi-badge-cc" aria-hidden="true"></span>
Expand Down
9 changes: 9 additions & 0 deletions db/migrate/20240623161219_create_ingested_analytics.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class CreateIngestedAnalytics < ActiveRecord::Migration[7.0]
def change
create_table :ingested_analytics do |t|
t.timestamps
t.date :day, null: false, index: true, unique: true
t.jsonb :document, null: false # , index: true
end
end
end
6 changes: 6 additions & 0 deletions db/migrate/20240707160915_add_views_to_videos.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class AddViewsToVideos < ActiveRecord::Migration[7.0]
def change
add_column :videos, :views_total, :integer, default: 0
add_column :videos, :views_recent, :integer, default: 0
end
end
12 changes: 11 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.0].define(version: 2024_06_17_100108) do
ActiveRecord::Schema[7.0].define(version: 2024_07_07_160915) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"

Expand Down Expand Up @@ -80,6 +80,14 @@
t.index ["sluggable_type", "sluggable_id"], name: "index_friendly_id_slugs_on_sluggable_type_and_sluggable_id"
end

create_table "ingested_analytics", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.date "day", null: false
t.jsonb "document", null: false
t.index ["day"], name: "index_ingested_analytics_on_day"
end

create_table "links", force: :cascade do |t|
t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", precision: nil, null: false
Expand Down Expand Up @@ -186,6 +194,8 @@
t.boolean "early_access", default: false, null: false
t.integer "old_playlist_ids", default: [], null: false, array: true
t.text "excerpt"
t.integer "views_total", default: 0
t.integer "views_recent", default: 0
t.index ["early_access"], name: "index_videos_on_early_access"
t.index ["old_playlist_ids"], name: "index_videos_on_old_playlist_ids"
t.index ["slug"], name: "index_videos_on_slug"
Expand Down
20 changes: 20 additions & 0 deletions spec/factories/ingested_analytics.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# == Schema Information
#
# Table name: ingested_analytics
#
# id :bigint not null, primary key
# day :date not null
# document :jsonb not null
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_ingested_analytics_on_day (day)
#
FactoryBot.define do
factory :ingested_analytic do
day { '2024-06-23' }
document { [{ "page" => "/series/cocina/como-cocinar-estofado", "pageviews" => 5 }] }
end
end
2 changes: 2 additions & 0 deletions spec/factories/videos.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
# slug :string not null
# tags :string default([]), is an Array
# title :string not null
# views_recent :integer default(0)
# views_total :integer default(0)
# created_at :datetime not null
# updated_at :datetime not null
# playlist_id :integer not null
Expand Down
5 changes: 5 additions & 0 deletions spec/jobs/materialize_video_analytics_job_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
require 'rails_helper'

RSpec.describe MaterializeVideoAnalyticsJob, type: :job do
pending "add some examples to (or delete) #{__FILE__}"
end
19 changes: 19 additions & 0 deletions spec/models/ingested_analytic_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# == Schema Information
#
# Table name: ingested_analytics
#
# id :bigint not null, primary key
# day :date not null
# document :jsonb not null
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_ingested_analytics_on_day (day)
#
require 'rails_helper'

RSpec.describe IngestedAnalytic, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end
2 changes: 2 additions & 0 deletions spec/models/video_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
# slug :string not null
# tags :string default([]), is an Array
# title :string not null
# views_recent :integer default(0)
# views_total :integer default(0)
# created_at :datetime not null
# updated_at :datetime not null
# playlist_id :integer not null
Expand Down

0 comments on commit d3a2cbd

Please sign in to comment.