mirror of
https://github.com/discourse/discourse.git
synced 2024-12-28 12:23:50 +08:00
196 lines
7.5 KiB
Ruby
196 lines
7.5 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class DirectoryItem < ActiveRecord::Base
|
|
belongs_to :user
|
|
has_one :user_stat, foreign_key: :user_id, primary_key: :user_id
|
|
|
|
@@plugin_queries = []
|
|
|
|
def self.period_types
|
|
@types ||= Enum.new(all: 1, yearly: 2, monthly: 3, weekly: 4, daily: 5, quarterly: 6)
|
|
end
|
|
|
|
def self.refresh!
|
|
period_types.each_key { |p| refresh_period!(p) }
|
|
end
|
|
|
|
def self.last_updated_at(period_type)
|
|
val = Discourse.redis.get("directory_#{period_type}")
|
|
return nil if val.nil?
|
|
|
|
Time.zone.at(val.to_i)
|
|
end
|
|
|
|
def self.add_plugin_query(details)
|
|
@@plugin_queries << details
|
|
end
|
|
|
|
def self.clear_plugin_queries
|
|
@@plugin_queries = []
|
|
end
|
|
|
|
def self.refresh_period!(period_type, force: false)
|
|
DiscourseEvent.trigger("before_directory_refresh")
|
|
Discourse.redis.set("directory_#{period_types[period_type]}", Time.zone.now.to_i)
|
|
|
|
# Don't calculate it if the user directory is disabled
|
|
return unless SiteSetting.enable_user_directory? || force
|
|
|
|
since =
|
|
case period_type
|
|
when :daily
|
|
1.day.ago
|
|
when :weekly
|
|
1.week.ago
|
|
when :monthly
|
|
1.month.ago
|
|
when :quarterly
|
|
3.months.ago
|
|
when :yearly
|
|
1.year.ago
|
|
else
|
|
1000.years.ago
|
|
end
|
|
|
|
ActiveRecord::Base.transaction do
|
|
# Delete records that belonged to users who have been deleted
|
|
DB.exec(
|
|
"DELETE FROM directory_items
|
|
USING directory_items di
|
|
LEFT JOIN users u ON (u.id = user_id AND u.active AND u.silenced_till IS NULL AND u.id > 0)
|
|
WHERE di.id = directory_items.id AND
|
|
u.id IS NULL AND
|
|
di.period_type = :period_type",
|
|
period_type: period_types[period_type],
|
|
)
|
|
|
|
# Create new records for users who don't have one yet
|
|
|
|
column_names =
|
|
DirectoryColumn.automatic_column_names + DirectoryColumn.plugin_directory_columns
|
|
DB.exec(
|
|
"INSERT INTO directory_items(period_type, user_id, #{column_names.map(&:to_s).join(", ")})
|
|
SELECT
|
|
:period_type,
|
|
u.id,
|
|
#{Array.new(column_names.count) { |_| 0 }.join(", ")}
|
|
FROM users u
|
|
LEFT JOIN directory_items di ON di.user_id = u.id AND di.period_type = :period_type
|
|
WHERE di.id IS NULL AND u.id > 0 AND u.silenced_till IS NULL AND u.active
|
|
#{SiteSetting.must_approve_users ? "AND u.approved" : ""}
|
|
",
|
|
period_type: period_types[period_type],
|
|
)
|
|
|
|
# Calculate new values and update records
|
|
#
|
|
#
|
|
# TODO
|
|
# WARNING: post_count is a wrong name, it should be reply_count (excluding topic post)
|
|
#
|
|
#
|
|
query_args = {
|
|
period_type: period_types[period_type],
|
|
since: since,
|
|
like_type: UserAction::LIKE,
|
|
was_liked_type: UserAction::WAS_LIKED,
|
|
new_topic_type: UserAction::NEW_TOPIC,
|
|
reply_type: UserAction::REPLY,
|
|
regular_post_type: Post.types[:regular],
|
|
}
|
|
|
|
DB.exec(
|
|
"WITH x AS (SELECT
|
|
u.id user_id,
|
|
SUM(CASE WHEN p.id IS NOT NULL AND t.id IS NOT NULL AND ua.action_type = :was_liked_type THEN 1 ELSE 0 END) likes_received,
|
|
SUM(CASE WHEN p.id IS NOT NULL AND t.id IS NOT NULL AND ua.action_type = :like_type THEN 1 ELSE 0 END) likes_given,
|
|
COALESCE((SELECT COUNT(topic_id) FROM topic_views AS v WHERE v.user_id = u.id AND v.viewed_at > :since), 0) topics_entered,
|
|
COALESCE((SELECT COUNT(id) FROM user_visits AS uv WHERE uv.user_id = u.id AND uv.visited_at > :since), 0) days_visited,
|
|
COALESCE((SELECT SUM(posts_read) FROM user_visits AS uv2 WHERE uv2.user_id = u.id AND uv2.visited_at > :since), 0) posts_read,
|
|
SUM(CASE WHEN t2.id IS NOT NULL AND ua.action_type = :new_topic_type THEN 1 ELSE 0 END) topic_count,
|
|
SUM(CASE WHEN p.id IS NOT NULL AND t.id IS NOT NULL AND ua.action_type = :reply_type THEN 1 ELSE 0 END) post_count
|
|
FROM users AS u
|
|
LEFT OUTER JOIN user_actions AS ua ON ua.user_id = u.id AND COALESCE(ua.created_at, :since) > :since
|
|
LEFT OUTER JOIN posts AS p ON ua.target_post_id = p.id AND p.deleted_at IS NULL AND p.post_type = :regular_post_type AND NOT p.hidden
|
|
LEFT OUTER JOIN topics AS t ON p.topic_id = t.id AND t.archetype = 'regular' AND t.deleted_at IS NULL AND t.visible
|
|
LEFT OUTER JOIN topics AS t2 ON t2.id = ua.target_topic_id AND t2.archetype = 'regular' AND t2.deleted_at IS NULL AND t2.visible
|
|
LEFT OUTER JOIN categories AS c ON t.category_id = c.id
|
|
WHERE u.active
|
|
AND u.silenced_till IS NULL
|
|
AND u.id > 0
|
|
GROUP BY u.id)
|
|
UPDATE directory_items di SET
|
|
likes_received = x.likes_received,
|
|
likes_given = x.likes_given,
|
|
topics_entered = x.topics_entered,
|
|
days_visited = x.days_visited,
|
|
posts_read = x.posts_read,
|
|
topic_count = x.topic_count,
|
|
post_count = x.post_count
|
|
FROM x
|
|
WHERE
|
|
x.user_id = di.user_id AND
|
|
di.period_type = :period_type AND (
|
|
di.likes_received <> x.likes_received OR
|
|
di.likes_given <> x.likes_given OR
|
|
di.topics_entered <> x.topics_entered OR
|
|
di.days_visited <> x.days_visited OR
|
|
di.posts_read <> x.posts_read OR
|
|
di.topic_count <> x.topic_count OR
|
|
di.post_count <> x.post_count )
|
|
|
|
",
|
|
query_args,
|
|
)
|
|
|
|
@@plugin_queries.each { |plugin_query| DB.exec(plugin_query, query_args) }
|
|
|
|
DB.exec <<~SQL if period_type == :all
|
|
UPDATE user_stats s
|
|
SET likes_given = d.likes_given,
|
|
likes_received = d.likes_received,
|
|
topic_count = d.topic_count,
|
|
post_count = d.post_count
|
|
|
|
FROM directory_items d
|
|
WHERE s.user_id = d.user_id AND
|
|
d.period_type = 1 AND
|
|
( s.likes_given <> d.likes_given OR
|
|
s.likes_received <> d.likes_received OR
|
|
s.topic_count <> d.topic_count OR
|
|
s.post_count <> d.post_count
|
|
)
|
|
SQL
|
|
end
|
|
end
|
|
end
|
|
|
|
# == Schema Information
|
|
#
|
|
# Table name: directory_items
|
|
#
|
|
# id :integer not null, primary key
|
|
# period_type :integer not null
|
|
# user_id :integer not null
|
|
# likes_received :integer not null
|
|
# likes_given :integer not null
|
|
# topics_entered :integer not null
|
|
# topic_count :integer not null
|
|
# post_count :integer not null
|
|
# created_at :datetime
|
|
# updated_at :datetime
|
|
# days_visited :integer default(0), not null
|
|
# posts_read :integer default(0), not null
|
|
#
|
|
# Indexes
|
|
#
|
|
# index_directory_items_on_days_visited (days_visited)
|
|
# index_directory_items_on_likes_given (likes_given)
|
|
# index_directory_items_on_likes_received (likes_received)
|
|
# index_directory_items_on_period_type_and_user_id (period_type,user_id) UNIQUE
|
|
# index_directory_items_on_post_count (post_count)
|
|
# index_directory_items_on_posts_read (posts_read)
|
|
# index_directory_items_on_topic_count (topic_count)
|
|
# index_directory_items_on_topics_entered (topics_entered)
|
|
#
|