From c830cf7b0499362284dbf07df3c7217546dc2f25 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 12 Jun 2023 01:56:47 +0200 Subject: [PATCH] Add status trend highlights for summarization --- app/models/status.rb | 1 + app/models/status_trend.rb | 1 + app/models/status_trend_highlight.rb | 27 +++++++++++++++++++ app/models/trends/statuses.rb | 15 +++++++++++ ...05085712_create_status_trend_highlights.rb | 13 +++++++++ db/schema.rb | 12 ++++++++- 6 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 app/models/status_trend_highlight.rb create mode 100644 db/migrate/20230605085712_create_status_trend_highlights.rb diff --git a/app/models/status.rb b/app/models/status.rb index 67463b140b..91a3249ff5 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -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? } diff --git a/app/models/status_trend.rb b/app/models/status_trend.rb index b0f1b6942d..ef9e861a71 100644 --- a/app/models/status_trend.rb +++ b/app/models/status_trend.rb @@ -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 diff --git a/app/models/status_trend_highlight.rb b/app/models/status_trend_highlight.rb new file mode 100644 index 0000000000..3c31a6e0b0 --- /dev/null +++ b/app/models/status_trend_highlight.rb @@ -0,0 +1,27 @@ +# 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 + + def self.weekly(account) + Status.joins(:trend_highlight, :account) + .merge(Account.discoverable) + .where(arel_table[:period].gteq(1.week.ago)) + .not_excluded_by_account(account) + .not_domain_blocked_by_account(account) + .reorder(Arel::Nodes::Case.new.when(arel_table[:language].in(account.chosen_languages || account.user_locale)).then(1).else(0).desc, score: :desc) + end +end diff --git a/app/models/trends/statuses.rb b/app/models/trends/statuses.rb index 84bff9c027..a186d90907 100644 --- a/app/models/trends/statuses.rb +++ b/app/models/trends/statuses.rb @@ -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) + store_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 store_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 diff --git a/db/migrate/20230605085712_create_status_trend_highlights.rb b/db/migrate/20230605085712_create_status_trend_highlights.rb new file mode 100644 index 0000000000..09e55183ff --- /dev/null +++ b/db/migrate/20230605085712_create_status_trend_highlights.rb @@ -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 diff --git a/db/schema.rb b/db/schema.rb index 9866b10149..1cd11d5c43 100644 --- a/db/schema.rb +++ b/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