Merge pull request #1637 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes
This commit is contained in:
commit
025a2ceb0f
|
@ -84,6 +84,10 @@ class PreviewCard < ApplicationRecord
|
||||||
attributes['trendable'].nil? && (provider.nil? || provider.requires_review_notification?)
|
attributes['trendable'].nil? && (provider.nil? || provider.requires_review_notification?)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def decaying?
|
||||||
|
max_score_at && max_score_at >= Trends.links.options[:max_score_cooldown].ago && max_score_at < 1.day.ago
|
||||||
|
end
|
||||||
|
|
||||||
attr_writer :provider
|
attr_writer :provider
|
||||||
|
|
||||||
def local?
|
def local?
|
||||||
|
|
|
@ -80,6 +80,10 @@ class Tag < ApplicationRecord
|
||||||
requires_review? && !requested_review?
|
requires_review? && !requested_review?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def decaying?
|
||||||
|
max_score_at && max_score_at >= Trends.tags.options[:max_score_cooldown].ago && max_score_at < 1.day.ago
|
||||||
|
end
|
||||||
|
|
||||||
def history
|
def history
|
||||||
@history ||= Trends::History.new('tags', id)
|
@history ||= Trends::History.new('tags', id)
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,19 @@
|
||||||
class Trends::Base
|
class Trends::Base
|
||||||
include Redisable
|
include Redisable
|
||||||
|
|
||||||
|
class_attribute :default_options
|
||||||
|
|
||||||
|
attr_reader :options
|
||||||
|
|
||||||
|
# @param [Hash] options
|
||||||
|
# @option options [Integer] :threshold Minimum amount of uses by unique accounts to begin calculating the score
|
||||||
|
# @option options [Integer] :review_threshold Minimum rank (lower = better) before requesting a review
|
||||||
|
# @option options [ActiveSupport::Duration] :max_score_cooldown For this amount of time, the peak score (if bigger than current score) is decayed-from
|
||||||
|
# @option options [ActiveSupport::Duration] :max_score_halflife How quickly a peak score decays
|
||||||
|
def initialize(options = {})
|
||||||
|
@options = self.class.default_options.merge(options)
|
||||||
|
end
|
||||||
|
|
||||||
def register(_status)
|
def register(_status)
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,17 +3,12 @@
|
||||||
class Trends::Links < Trends::Base
|
class Trends::Links < Trends::Base
|
||||||
PREFIX = 'trending_links'
|
PREFIX = 'trending_links'
|
||||||
|
|
||||||
# Minimum amount of uses by unique accounts to begin calculating the score
|
self.default_options = {
|
||||||
THRESHOLD = 15
|
threshold: 15,
|
||||||
|
review_threshold: 10,
|
||||||
# Minimum rank (lower = better) before requesting a review
|
max_score_cooldown: 2.days.freeze,
|
||||||
REVIEW_THRESHOLD = 10
|
max_score_halflife: 8.hours.freeze,
|
||||||
|
}
|
||||||
# For this amount of time, the peak score (if bigger than current score) is decayed-from
|
|
||||||
MAX_SCORE_COOLDOWN = 2.days.freeze
|
|
||||||
|
|
||||||
# How quickly a peak score decays
|
|
||||||
MAX_SCORE_HALFLIFE = 8.hours.freeze
|
|
||||||
|
|
||||||
def register(status, at_time = Time.now.utc)
|
def register(status, at_time = Time.now.utc)
|
||||||
original_status = status.reblog? ? status.reblog : status
|
original_status = status.reblog? ? status.reblog : status
|
||||||
|
@ -81,10 +76,10 @@ class Trends::Links < Trends::Base
|
||||||
observed = preview_card.history.get(at_time).accounts.to_f
|
observed = preview_card.history.get(at_time).accounts.to_f
|
||||||
max_time = preview_card.max_score_at
|
max_time = preview_card.max_score_at
|
||||||
max_score = preview_card.max_score
|
max_score = preview_card.max_score
|
||||||
max_score = 0 if max_time.nil? || max_time < (at_time - MAX_SCORE_COOLDOWN)
|
max_score = 0 if max_time.nil? || max_time < (at_time - options[:max_score_cooldown])
|
||||||
|
|
||||||
score = begin
|
score = begin
|
||||||
if expected > observed || observed < THRESHOLD
|
if expected > observed || observed < options[:threshold]
|
||||||
0
|
0
|
||||||
else
|
else
|
||||||
((observed - expected)**2) / expected
|
((observed - expected)**2) / expected
|
||||||
|
@ -99,7 +94,7 @@ class Trends::Links < Trends::Base
|
||||||
preview_card.update_columns(max_score: max_score, max_score_at: max_time)
|
preview_card.update_columns(max_score: max_score, max_score_at: max_time)
|
||||||
end
|
end
|
||||||
|
|
||||||
decaying_score = max_score * (0.5**((at_time.to_f - max_time.to_f) / MAX_SCORE_HALFLIFE.to_f))
|
decaying_score = max_score * (0.5**((at_time.to_f - max_time.to_f) / options[:max_score_halflife].to_f))
|
||||||
|
|
||||||
if decaying_score.zero?
|
if decaying_score.zero?
|
||||||
redis.zrem("#{PREFIX}:all", preview_card.id)
|
redis.zrem("#{PREFIX}:all", preview_card.id)
|
||||||
|
@ -117,6 +112,6 @@ class Trends::Links < Trends::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def would_be_trending?(id)
|
def would_be_trending?(id)
|
||||||
score(id) > score_at_rank(REVIEW_THRESHOLD - 1)
|
score(id) > score_at_rank(options[:review_threshold] - 1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,17 +3,12 @@
|
||||||
class Trends::Tags < Trends::Base
|
class Trends::Tags < Trends::Base
|
||||||
PREFIX = 'trending_tags'
|
PREFIX = 'trending_tags'
|
||||||
|
|
||||||
# Minimum amount of uses by unique accounts to begin calculating the score
|
self.default_options = {
|
||||||
THRESHOLD = 15
|
threshold: 15,
|
||||||
|
review_threshold: 10,
|
||||||
# Minimum rank (lower = better) before requesting a review
|
max_score_cooldown: 2.days.freeze,
|
||||||
REVIEW_THRESHOLD = 10
|
max_score_halflife: 4.hours.freeze,
|
||||||
|
}
|
||||||
# For this amount of time, the peak score (if bigger than current score) is decayed-from
|
|
||||||
MAX_SCORE_COOLDOWN = 2.days.freeze
|
|
||||||
|
|
||||||
# How quickly a peak score decays
|
|
||||||
MAX_SCORE_HALFLIFE = 4.hours.freeze
|
|
||||||
|
|
||||||
def register(status, at_time = Time.now.utc)
|
def register(status, at_time = Time.now.utc)
|
||||||
original_status = status.reblog? ? status.reblog : status
|
original_status = status.reblog? ? status.reblog : status
|
||||||
|
@ -75,10 +70,10 @@ class Trends::Tags < Trends::Base
|
||||||
observed = tag.history.get(at_time).accounts.to_f
|
observed = tag.history.get(at_time).accounts.to_f
|
||||||
max_time = tag.max_score_at
|
max_time = tag.max_score_at
|
||||||
max_score = tag.max_score
|
max_score = tag.max_score
|
||||||
max_score = 0 if max_time.nil? || max_time < (at_time - MAX_SCORE_COOLDOWN)
|
max_score = 0 if max_time.nil? || max_time < (at_time - options[:max_score_cooldown])
|
||||||
|
|
||||||
score = begin
|
score = begin
|
||||||
if expected > observed || observed < THRESHOLD
|
if expected > observed || observed < options[:threshold]
|
||||||
0
|
0
|
||||||
else
|
else
|
||||||
((observed - expected)**2) / expected
|
((observed - expected)**2) / expected
|
||||||
|
@ -93,7 +88,7 @@ class Trends::Tags < Trends::Base
|
||||||
tag.update_columns(max_score: max_score, max_score_at: max_time)
|
tag.update_columns(max_score: max_score, max_score_at: max_time)
|
||||||
end
|
end
|
||||||
|
|
||||||
decaying_score = max_score * (0.5**((at_time.to_f - max_time.to_f) / MAX_SCORE_HALFLIFE.to_f))
|
decaying_score = max_score * (0.5**((at_time.to_f - max_time.to_f) / options[:max_score_halflife].to_f))
|
||||||
|
|
||||||
if decaying_score.zero?
|
if decaying_score.zero?
|
||||||
redis.zrem("#{PREFIX}:all", tag.id)
|
redis.zrem("#{PREFIX}:all", tag.id)
|
||||||
|
@ -111,6 +106,6 @@ class Trends::Tags < Trends::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def would_be_trending?(id)
|
def would_be_trending?(id)
|
||||||
score(id) > score_at_rank(REVIEW_THRESHOLD - 1)
|
score(id) > score_at_rank(options[:review_threshold] - 1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
•
|
•
|
||||||
%abbr{ title: t('admin.trends.tags.current_score', score: Trends.links.score(preview_card.id)) }= t('admin.trends.tags.trending_rank', rank: rank + 1)
|
%abbr{ title: t('admin.trends.tags.current_score', score: Trends.links.score(preview_card.id)) }= t('admin.trends.tags.trending_rank', rank: rank + 1)
|
||||||
|
|
||||||
- if preview_card.max_score_at && preview_card.max_score_at >= Trends::Links::MAX_SCORE_COOLDOWN.ago && preview_card.max_score_at < 1.day.ago
|
- if preview_card.decaying?
|
||||||
•
|
•
|
||||||
= t('admin.trends.tags.peaked_on_and_decaying', date: l(preview_card.max_score_at.to_date, format: :short))
|
= t('admin.trends.tags.peaked_on_and_decaying', date: l(preview_card.max_score_at.to_date, format: :short))
|
||||||
- elsif preview_card.provider&.requires_review?
|
- elsif preview_card.provider&.requires_review?
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
•
|
•
|
||||||
%abbr{ title: t('admin.trends.tags.current_score', score: Trends.tags.score(tag.id)) }= t('admin.trends.tags.trending_rank', rank: rank + 1)
|
%abbr{ title: t('admin.trends.tags.current_score', score: Trends.tags.score(tag.id)) }= t('admin.trends.tags.trending_rank', rank: rank + 1)
|
||||||
|
|
||||||
- if tag.max_score_at && tag.max_score_at >= Trends::Tags::MAX_SCORE_COOLDOWN.ago && tag.max_score_at < 1.day.ago
|
- if tag.decaying?
|
||||||
•
|
•
|
||||||
= t('admin.trends.tags.peaked_on_and_decaying', date: l(tag.max_score_at.to_date, format: :short))
|
= t('admin.trends.tags.peaked_on_and_decaying', date: l(tag.max_score_at.to_date, format: :short))
|
||||||
- elsif tag.requires_review?
|
- elsif tag.requires_review?
|
||||||
|
|
|
@ -1,15 +1,10 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe Trends::Tags do
|
RSpec.describe Trends::Tags do
|
||||||
subject { described_class.new }
|
subject { described_class.new(threshold: 5, review_threshold: 10) }
|
||||||
|
|
||||||
let!(:at_time) { DateTime.new(2021, 11, 14, 10, 15, 0) }
|
let!(:at_time) { DateTime.new(2021, 11, 14, 10, 15, 0) }
|
||||||
|
|
||||||
before do
|
|
||||||
stub_const 'Trends::Tags::THRESHOLD', 5
|
|
||||||
stub_const 'Trends::Tags::REVIEW_THRESHOLD', 10
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#add' do
|
describe '#add' do
|
||||||
let(:tag) { Fabricate(:tag) }
|
let(:tag) { Fabricate(:tag) }
|
||||||
|
|
||||||
|
@ -64,7 +59,7 @@ RSpec.describe Trends::Tags do
|
||||||
subject.refresh(yesterday + 12.hours)
|
subject.refresh(yesterday + 12.hours)
|
||||||
original_score = subject.score(tag3.id)
|
original_score = subject.score(tag3.id)
|
||||||
expect(original_score).to eq 144.0
|
expect(original_score).to eq 144.0
|
||||||
subject.refresh(yesterday + 12.hours + described_class::MAX_SCORE_HALFLIFE)
|
subject.refresh(yesterday + 12.hours + subject.options[:max_score_halflife])
|
||||||
decayed_score = subject.score(tag3.id)
|
decayed_score = subject.score(tag3.id)
|
||||||
expect(decayed_score).to be <= original_score / 2
|
expect(decayed_score).to be <= original_score / 2
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue