2019-05-02 18:17:27 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
class CategoryFeaturedTopic < ActiveRecord::Base
|
|
|
|
belongs_to :category
|
|
|
|
belongs_to :topic
|
|
|
|
|
2017-12-19 21:42:29 -05:00
|
|
|
NEXT_CATEGORY_ID_KEY = 'category-featured-topic:next-category-id'.freeze
|
2017-12-19 21:58:05 -05:00
|
|
|
DEFAULT_BATCH_SIZE = 100
|
2017-12-19 21:42:29 -05:00
|
|
|
|
|
|
|
# Populates the category featured topics.
|
2017-12-19 21:58:05 -05:00
|
|
|
def self.feature_topics(batched: false, batch_size: nil)
|
2016-07-16 01:36:40 -04:00
|
|
|
current = {}
|
|
|
|
CategoryFeaturedTopic.select(:topic_id, :category_id).order(:rank).each do |f|
|
|
|
|
(current[f.category_id] ||= []) << f.topic_id
|
|
|
|
end
|
2017-12-19 21:42:29 -05:00
|
|
|
|
2017-12-19 21:58:05 -05:00
|
|
|
batch_size ||= DEFAULT_BATCH_SIZE
|
|
|
|
|
2019-12-03 04:05:53 -05:00
|
|
|
next_category_id = batched ? Discourse.redis.get(NEXT_CATEGORY_ID_KEY).to_i : 0
|
2017-12-19 21:42:29 -05:00
|
|
|
|
|
|
|
categories = Category.select(:id, :topic_id, :num_featured_topics)
|
|
|
|
.where('id >= ?', next_category_id)
|
|
|
|
.order('id ASC')
|
2017-12-19 21:58:05 -05:00
|
|
|
.limit(batch_size)
|
|
|
|
.to_a
|
2017-12-19 21:42:29 -05:00
|
|
|
|
|
|
|
if batched
|
2017-12-19 21:58:05 -05:00
|
|
|
if categories.length == batch_size
|
2017-12-19 21:42:29 -05:00
|
|
|
next_id = Category.where('id > ?', categories.last.id).order('id asc').limit(1).pluck(:id)[0]
|
2019-12-03 04:05:53 -05:00
|
|
|
next_id ? Discourse.redis.setex(NEXT_CATEGORY_ID_KEY, 1.day, next_id) : clear_batch!
|
2017-12-19 21:42:29 -05:00
|
|
|
else
|
2017-12-19 21:58:05 -05:00
|
|
|
clear_batch!
|
2017-12-19 21:42:29 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-12-19 21:58:05 -05:00
|
|
|
categories.each do |c|
|
2016-07-16 01:36:40 -04:00
|
|
|
CategoryFeaturedTopic.feature_topics_for(c, current[c.id] || [])
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-12-19 21:58:05 -05:00
|
|
|
def self.clear_batch!
|
2019-12-03 04:05:53 -05:00
|
|
|
Discourse.redis.del(NEXT_CATEGORY_ID_KEY)
|
2017-12-19 21:58:05 -05:00
|
|
|
end
|
|
|
|
|
2017-07-27 21:20:09 -04:00
|
|
|
def self.feature_topics_for(c, existing = nil)
|
2013-02-28 13:54:12 -05:00
|
|
|
return if c.blank?
|
2013-02-07 10:45:24 -05:00
|
|
|
|
2015-09-21 15:14:05 -04:00
|
|
|
query_opts = {
|
2017-03-01 12:03:12 -05:00
|
|
|
per_page: c.num_featured_topics,
|
2014-09-10 10:18:28 -04:00
|
|
|
except_topic_ids: [c.topic_id],
|
|
|
|
visible: true,
|
2019-11-18 01:58:35 -05:00
|
|
|
no_definitions: true
|
2015-09-21 15:14:05 -04:00
|
|
|
}
|
2014-09-10 10:18:28 -04:00
|
|
|
|
2017-08-17 15:26:31 -04:00
|
|
|
# It may seem a bit odd that we are running 2 queries here, when admin
|
|
|
|
# can clearly pull out all the topics needed.
|
|
|
|
# We do so, so anonymous will ALWAYS get some topics
|
|
|
|
# If we only fetched as admin we may have a situation where anon can see
|
|
|
|
# no featured topics (all the previous 2x topics are only visible to admins)
|
|
|
|
|
|
|
|
# Add topics, even if they're in secured categories or invisible
|
2015-09-21 15:14:05 -04:00
|
|
|
query = TopicQuery.new(CategoryFeaturedTopic.fake_admin, query_opts)
|
2015-02-23 00:50:52 -05:00
|
|
|
results = query.list_category_topic_ids(c).uniq
|
2014-01-29 18:32:04 -05:00
|
|
|
|
2015-09-21 15:14:05 -04:00
|
|
|
# Add some topics that are visible to everyone:
|
2017-07-27 21:20:09 -04:00
|
|
|
anon_query = TopicQuery.new(nil, query_opts.merge(except_topic_ids: [c.topic_id] + results))
|
2015-09-21 15:14:05 -04:00
|
|
|
results += anon_query.list_category_topic_ids(c).uniq
|
|
|
|
|
2014-01-29 18:32:04 -05:00
|
|
|
return if results == existing
|
2013-09-30 02:59:16 -04:00
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
CategoryFeaturedTopic.transaction do
|
2017-08-31 00:06:56 -04:00
|
|
|
CategoryFeaturedTopic.where(category_id: c.id).delete_all
|
2013-09-30 02:59:16 -04:00
|
|
|
if results
|
|
|
|
results.each_with_index do |topic_id, idx|
|
2016-05-03 14:24:50 -04:00
|
|
|
begin
|
|
|
|
c.category_featured_topics.create(topic_id: topic_id, rank: idx)
|
|
|
|
rescue PG::UniqueViolation
|
|
|
|
# If another process features this topic, just ignore it
|
|
|
|
end
|
2013-05-28 14:54:00 -04:00
|
|
|
end
|
|
|
|
end
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-08-18 21:58:21 -04:00
|
|
|
def self.fake_admin
|
|
|
|
# fake an admin
|
|
|
|
admin = User.new
|
|
|
|
admin.admin = true
|
|
|
|
admin.id = -1
|
|
|
|
admin
|
|
|
|
end
|
2013-08-30 13:39:31 -04:00
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
2013-05-23 22:48:32 -04:00
|
|
|
|
|
|
|
# == Schema Information
|
|
|
|
#
|
|
|
|
# Table name: category_featured_topics
|
|
|
|
#
|
|
|
|
# category_id :integer not null
|
|
|
|
# topic_id :integer not null
|
2014-08-27 01:19:25 -04:00
|
|
|
# created_at :datetime not null
|
|
|
|
# updated_at :datetime not null
|
2013-06-16 20:48:58 -04:00
|
|
|
# rank :integer default(0), not null
|
2013-08-13 16:09:27 -04:00
|
|
|
# id :integer not null, primary key
|
2013-05-23 22:48:32 -04:00
|
|
|
#
|
|
|
|
# Indexes
|
|
|
|
#
|
2013-06-16 20:48:58 -04:00
|
|
|
# cat_featured_threads (category_id,topic_id) UNIQUE
|
|
|
|
# index_category_featured_topics_on_category_id_and_rank (category_id,rank)
|
2013-05-23 22:48:32 -04:00
|
|
|
#
|