forked from fedi/mastodon
Add a weekly digest
This commit is contained in:
parent
dfaf59d99a
commit
453c88f3f1
31
app/mailers/digest_mailer.rb
Normal file
31
app/mailers/digest_mailer.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class DigestMailer < ApplicationMailer
|
||||
helper :accounts
|
||||
helper :statuses
|
||||
helper :routing
|
||||
|
||||
before_action do
|
||||
@account = params[:account]
|
||||
end
|
||||
|
||||
def weekly
|
||||
@weekly_highlights = weekly_highlights(@account)
|
||||
|
||||
locale_for_account @account do
|
||||
mail to: email_address_with_name(@account.user_email, @account.username)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def weekly_highlights(account)
|
||||
Status.joins(:trend_highlight, :account)
|
||||
.merge(Account.discoverable)
|
||||
.where(StatusTrendHighlight.arel_table[:period].gteq(10.days.ago))
|
||||
.not_excluded_by_account(account)
|
||||
.not_domain_blocked_by_account(account)
|
||||
.reorder(Arel::Nodes::Case.new.when(StatusTrendHighlight.arel_table[:language].in(account.chosen_languages || account.user_locale)).then(1).else(0).desc, score: :desc)
|
||||
.limit(20)
|
||||
end
|
||||
end
|
|
@ -77,6 +77,7 @@ class Status < ApplicationRecord
|
|||
has_one :status_stat, inverse_of: :status
|
||||
has_one :poll, inverse_of: :status, dependent: :destroy
|
||||
has_one :trend, class_name: 'StatusTrend', inverse_of: :status
|
||||
has_one :trend_highlight, class_name: 'StatusTrendHighlight', inverse_of: :status
|
||||
|
||||
validates :uri, uniqueness: true, presence: true, unless: :local?
|
||||
validates :text, presence: true, unless: -> { with_media? || reblog? }
|
||||
|
|
|
@ -18,4 +18,5 @@ class StatusTrend < ApplicationRecord
|
|||
belongs_to :account
|
||||
|
||||
scope :allowed, -> { joins('INNER JOIN (SELECT account_id, MAX(score) AS max_score FROM status_trends GROUP BY account_id) AS grouped_status_trends ON status_trends.account_id = grouped_status_trends.account_id AND status_trends.score = grouped_status_trends.max_score').where(allowed: true) }
|
||||
scope :below_rank, ->(rank) { where(arel_table[:rank].lteq(rank)) }
|
||||
end
|
||||
|
|
18
app/models/status_trend_highlight.rb
Normal file
18
app/models/status_trend_highlight.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: status_trend_highlights
|
||||
#
|
||||
# id :bigint(8) not null, primary key
|
||||
# period :datetime not null
|
||||
# status_id :bigint(8) not null
|
||||
# account_id :bigint(8) not null
|
||||
# score :float default(0.0), not null
|
||||
# language :string
|
||||
#
|
||||
|
||||
class StatusTrendHighlight < ApplicationRecord
|
||||
belongs_to :status
|
||||
belongs_to :account
|
||||
end
|
|
@ -60,6 +60,7 @@ class Trends::Statuses < Trends::Base
|
|||
def refresh(at_time = Time.now.utc)
|
||||
statuses = Status.where(id: (recently_used_ids(at_time) + StatusTrend.pluck(:status_id)).uniq).includes(:status_stat, :account)
|
||||
calculate_scores(statuses, at_time)
|
||||
update_weekly_highlights(at_time)
|
||||
end
|
||||
|
||||
def request_review
|
||||
|
@ -123,4 +124,18 @@ class Trends::Statuses < Trends::Base
|
|||
StatusTrend.connection.exec_update('UPDATE status_trends SET rank = t0.calculated_rank FROM (SELECT id, row_number() OVER w AS calculated_rank FROM status_trends WINDOW w AS (PARTITION BY language ORDER BY score DESC)) t0 WHERE status_trends.id = t0.id')
|
||||
end
|
||||
end
|
||||
|
||||
def update_weekly_highlights(at_time)
|
||||
highlights = StatusTrend.allowed.below_rank(20)
|
||||
|
||||
highlights.find_each do |trend|
|
||||
# Forced to resort to this monstrosity because upsert_all's on_duplicate option is only
|
||||
# available starting with Rails 7...
|
||||
StatusTrendHighlight.connection.exec_insert(<<~SQL.squish, nil, [[nil, at_time.beginning_of_day], [nil, trend.status_id], [nil, trend.account_id], [nil, trend.score], [nil, trend.language]])
|
||||
INSERT INTO status_trend_highlights(period, status_id, account_id, score, language)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
ON CONFLICT (status_id) DO UPDATE SET score = GREATEST(status_trend_highlights.score, EXCLUDED.score)
|
||||
SQL
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
19
app/views/digest_mailer/weekly.html.haml
Normal file
19
app/views/digest_mailer/weekly.html.haml
Normal file
|
@ -0,0 +1,19 @@
|
|||
%table.email-table{ cellspacing: 0, cellpadding: 0 }
|
||||
%tbody
|
||||
%tr
|
||||
%td.email-body
|
||||
.email-container
|
||||
%table.content-section{ cellspacing: 0, cellpadding: 0 }
|
||||
%tbody
|
||||
%tr
|
||||
%td.content-cell.hero
|
||||
.email-row
|
||||
.col-6
|
||||
%table.column{ cellspacing: 0, cellpadding: 0 }
|
||||
%tbody
|
||||
%tr
|
||||
%td.column-cell.text-center.padded
|
||||
%h1 Your weekly digest
|
||||
|
||||
- @weekly_highlights.each_with_index do |status, i|
|
||||
= render 'notification_mailer/status', status: status, i: i + 1, highlighted: true, time_zone: @account.user_time_zone
|
|
@ -66,6 +66,8 @@ Rails.application.configure do
|
|||
|
||||
config.action_mailer.default_options = { from: 'notifications@localhost' }
|
||||
|
||||
config.action_mailer.preview_path = Rails.root.join('spec', 'mailers', 'previews')
|
||||
|
||||
# If using a Heroku, Vagrant or generic remote development environment,
|
||||
# use letter_opener_web, accessible at /letter_opener.
|
||||
# Otherwise, use letter_opener, which launches a browser window to view sent mail.
|
||||
|
|
13
db/migrate/20230605085712_create_status_trend_highlights.rb
Normal file
13
db/migrate/20230605085712_create_status_trend_highlights.rb
Normal file
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CreateStatusTrendHighlights < ActiveRecord::Migration[6.1]
|
||||
def change
|
||||
create_table :status_trend_highlights do |t| # rubocop:disable Rails/CreateTableWithTimestamps
|
||||
t.datetime :period, null: false
|
||||
t.bigint :status_id, null: false, index: { unique: true }
|
||||
t.bigint :account_id, null: false, index: true
|
||||
t.float :score, null: false, default: 0.0
|
||||
t.string :language
|
||||
end
|
||||
end
|
||||
end
|
12
db/schema.rb
12
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: 2023_06_05_085711) do
|
||||
ActiveRecord::Schema.define(version: 2023_06_05_085712) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
@ -934,6 +934,16 @@ ActiveRecord::Schema.define(version: 2023_06_05_085711) do
|
|||
t.index ["status_id"], name: "index_status_stats_on_status_id", unique: true
|
||||
end
|
||||
|
||||
create_table "status_trend_highlights", force: :cascade do |t|
|
||||
t.datetime "period", null: false
|
||||
t.bigint "status_id", null: false
|
||||
t.bigint "account_id", null: false
|
||||
t.float "score", default: 0.0, null: false
|
||||
t.string "language"
|
||||
t.index ["account_id"], name: "index_status_trend_highlights_on_account_id"
|
||||
t.index ["status_id"], name: "index_status_trend_highlights_on_status_id", unique: true
|
||||
end
|
||||
|
||||
create_table "status_trends", force: :cascade do |t|
|
||||
t.bigint "status_id", null: false
|
||||
t.bigint "account_id", null: false
|
||||
|
|
10
spec/mailers/previews/digest_mailer_preview.rb
Normal file
10
spec/mailers/previews/digest_mailer_preview.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Preview all emails at http://localhost:3000/rails/mailers/digest_mailer
|
||||
|
||||
class DigestMailerPreview < ActionMailer::Preview
|
||||
# Preview this email at http://localhost:3000/rails/mailers/digest_mailer/weekly
|
||||
def weekly
|
||||
DigestMailer.with(account: Account.local.first).weekly
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue