2019-05-02 17:17:27 -05:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2014-08-11 15:59:00 -05:00
|
|
|
class About
|
FEATURE: Add plugin API to register About stat group (#17442)
This commit introduces a new plugin API to register
a group of stats that will be included in about.json
and also conditionally in the site about UI at /about.
The usage is like this:
```ruby
register_about_stat_group("chat_messages", show_in_ui: true) do
{
last_day: 1,
"7_days" => 10,
"30_days" => 100,
count: 1000,
previous_30_days: 120
}
end
```
In reality the stats will be generated any way the implementer
chooses within the plugin. The `last_day`, `7_days`, `30_days,` and `count`
keys must be present but apart from that additional stats may be added.
Only those core 4 stat keys will be shown in the UI, but everything will be shown
in about.json.
The stat group name is used to prefix the stats in about.json like so:
```json
"chat_messages_last_day": 2322,
"chat_messages_7_days": 2322,
"chat_messages_30_days": 2322,
"chat_messages_count": 2322,
```
The `show_in_ui` option (default false) is used to determine whether the
group of stats is shown on the site About page in the Site Statistics
table. Some stats may be needed purely for reporting purposes and thus
do not need to be shown in the UI to admins/users. An extension to the Site
serializer, `displayed_about_plugin_stat_groups`, has been added so this
can be inspected on the client-side.
2022-07-14 22:16:00 -05:00
|
|
|
def self.displayed_plugin_stat_groups
|
2023-11-09 14:44:05 -06:00
|
|
|
DiscoursePluginRegistry.stats.select { |stat| stat.show_in_ui }.map { |stat| stat.name }
|
FEATURE: Add plugin API to register About stat group (#17442)
This commit introduces a new plugin API to register
a group of stats that will be included in about.json
and also conditionally in the site about UI at /about.
The usage is like this:
```ruby
register_about_stat_group("chat_messages", show_in_ui: true) do
{
last_day: 1,
"7_days" => 10,
"30_days" => 100,
count: 1000,
previous_30_days: 120
}
end
```
In reality the stats will be generated any way the implementer
chooses within the plugin. The `last_day`, `7_days`, `30_days,` and `count`
keys must be present but apart from that additional stats may be added.
Only those core 4 stat keys will be shown in the UI, but everything will be shown
in about.json.
The stat group name is used to prefix the stats in about.json like so:
```json
"chat_messages_last_day": 2322,
"chat_messages_7_days": 2322,
"chat_messages_30_days": 2322,
"chat_messages_count": 2322,
```
The `show_in_ui` option (default false) is used to determine whether the
group of stats is shown on the site About page in the Site Statistics
table. Some stats may be needed purely for reporting purposes and thus
do not need to be shown in the UI to admins/users. An extension to the Site
serializer, `displayed_about_plugin_stat_groups`, has been added so this
can be inspected on the client-side.
2022-07-14 22:16:00 -05:00
|
|
|
end
|
|
|
|
|
2019-07-31 08:46:58 -05:00
|
|
|
class CategoryMods
|
|
|
|
include ActiveModel::Serialization
|
2024-04-05 01:38:54 -05:00
|
|
|
attr_reader :category, :moderators
|
2019-07-31 08:46:58 -05:00
|
|
|
|
2024-04-05 01:38:54 -05:00
|
|
|
def initialize(category, moderators)
|
|
|
|
@category = category
|
2019-07-31 08:46:58 -05:00
|
|
|
@moderators = moderators
|
|
|
|
end
|
2024-05-10 12:11:43 -05:00
|
|
|
|
|
|
|
def parent_category
|
|
|
|
category.parent_category
|
|
|
|
end
|
2019-07-31 08:46:58 -05:00
|
|
|
end
|
|
|
|
|
2014-08-11 15:59:00 -05:00
|
|
|
include ActiveModel::Serialization
|
2015-07-06 23:52:19 -05:00
|
|
|
include StatsCacheable
|
2014-08-11 15:59:00 -05:00
|
|
|
|
2015-07-06 23:52:19 -05:00
|
|
|
def self.stats_cache_key
|
2023-01-09 06:20:10 -06:00
|
|
|
"about-stats"
|
2015-07-06 23:52:19 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def self.fetch_stats
|
2023-11-09 14:44:05 -06:00
|
|
|
Stat.api_stats
|
2015-07-06 23:52:19 -05:00
|
|
|
end
|
|
|
|
|
2019-07-31 08:46:58 -05:00
|
|
|
def initialize(user = nil)
|
|
|
|
@user = user
|
|
|
|
end
|
|
|
|
|
2014-08-11 17:15:35 -05:00
|
|
|
def version
|
|
|
|
Discourse::VERSION::STRING
|
|
|
|
end
|
|
|
|
|
2014-11-24 00:54:17 -06:00
|
|
|
def https
|
2016-06-27 04:26:43 -05:00
|
|
|
SiteSetting.force_https
|
2014-11-24 00:54:17 -06:00
|
|
|
end
|
|
|
|
|
2014-08-11 17:15:35 -05:00
|
|
|
def title
|
|
|
|
SiteSetting.title
|
|
|
|
end
|
|
|
|
|
|
|
|
def locale
|
|
|
|
SiteSetting.default_locale
|
|
|
|
end
|
|
|
|
|
|
|
|
def description
|
|
|
|
SiteSetting.site_description
|
|
|
|
end
|
|
|
|
|
2024-07-22 17:35:18 -05:00
|
|
|
def extended_site_description
|
|
|
|
SiteSetting.extended_site_description_cooked
|
|
|
|
end
|
|
|
|
|
|
|
|
def banner_image
|
|
|
|
url = SiteSetting.about_banner_image&.url
|
|
|
|
return if url.blank?
|
|
|
|
GlobalPath.full_cdn_url(url)
|
|
|
|
end
|
|
|
|
|
2024-08-07 03:11:41 -05:00
|
|
|
def site_creation_date
|
|
|
|
Discourse.site_creation_date
|
|
|
|
end
|
|
|
|
|
2014-08-11 15:59:00 -05:00
|
|
|
def moderators
|
2024-09-10 06:43:41 -05:00
|
|
|
@moderators ||=
|
|
|
|
apply_excluded_groups(
|
|
|
|
User.where(moderator: true, admin: false).human_users.order(last_seen_at: :desc),
|
|
|
|
)
|
2014-08-11 15:59:00 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def admins
|
2024-05-30 10:44:53 -05:00
|
|
|
@admins ||=
|
|
|
|
DiscoursePluginRegistry.apply_modifier(
|
|
|
|
:about_admins,
|
2024-09-10 06:43:41 -05:00
|
|
|
apply_excluded_groups(User.where(admin: true).human_users.order(last_seen_at: :desc)),
|
2024-05-30 10:44:53 -05:00
|
|
|
)
|
2014-08-11 15:59:00 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def stats
|
2024-08-29 05:16:57 -05:00
|
|
|
@stats ||= About.fetch_cached_stats
|
2014-08-11 15:59:00 -05:00
|
|
|
end
|
|
|
|
|
2019-07-31 08:46:58 -05:00
|
|
|
def category_moderators
|
2019-10-03 13:48:56 -05:00
|
|
|
allowed_cats = Guardian.new(@user).allowed_category_ids
|
|
|
|
return [] if allowed_cats.blank?
|
2021-03-28 03:25:30 -05:00
|
|
|
|
2024-09-03 20:38:46 -05:00
|
|
|
cats_with_mods = Category.joins(:category_moderation_groups).distinct.pluck(:id)
|
2021-03-28 03:25:30 -05:00
|
|
|
|
2019-10-03 13:48:56 -05:00
|
|
|
category_ids = cats_with_mods & allowed_cats
|
2019-07-31 08:46:58 -05:00
|
|
|
return [] if category_ids.blank?
|
2019-10-03 13:48:56 -05:00
|
|
|
|
|
|
|
per_cat_limit = category_mods_limit / category_ids.size
|
|
|
|
per_cat_limit = 1 if per_cat_limit < 1
|
2021-03-28 03:25:30 -05:00
|
|
|
|
2024-09-03 20:38:46 -05:00
|
|
|
results = DB.query(<<~SQL, category_ids:)
|
|
|
|
WITH moderator_users AS (
|
|
|
|
SELECT
|
|
|
|
cmg.category_id AS category_id,
|
|
|
|
u.id AS user_id,
|
|
|
|
u.last_seen_at,
|
|
|
|
ROW_NUMBER() OVER (PARTITION BY cmg.category_id, u.id ORDER BY u.last_seen_at DESC) as rn
|
|
|
|
FROM category_moderation_groups cmg
|
|
|
|
INNER JOIN group_users gu
|
|
|
|
ON cmg.group_id = gu.group_id
|
|
|
|
INNER JOIN users u
|
|
|
|
ON gu.user_id = u.id
|
|
|
|
WHERE cmg.category_id IN (:category_ids)
|
|
|
|
)
|
|
|
|
SELECT id AS category_id, user_ids
|
|
|
|
FROM categories
|
|
|
|
INNER JOIN (
|
|
|
|
SELECT
|
|
|
|
category_id,
|
|
|
|
(ARRAY_AGG(user_id ORDER BY last_seen_at DESC))[:#{per_cat_limit}] AS user_ids
|
|
|
|
FROM moderator_users
|
|
|
|
WHERE rn = 1
|
|
|
|
GROUP BY category_id
|
|
|
|
) X
|
|
|
|
ON X.category_id = id
|
|
|
|
ORDER BY position
|
2019-07-31 08:46:58 -05:00
|
|
|
SQL
|
2021-03-28 03:25:30 -05:00
|
|
|
|
2024-04-05 01:38:54 -05:00
|
|
|
cats = Category.where(id: results.map(&:category_id)).index_by(&:id)
|
2021-03-30 16:12:53 -05:00
|
|
|
mods = User.where(id: results.map(&:user_ids).flatten.uniq).index_by(&:id)
|
2021-03-28 03:25:30 -05:00
|
|
|
|
2024-04-05 01:38:54 -05:00
|
|
|
results.map { |row| CategoryMods.new(cats[row.category_id], mods.values_at(*row.user_ids)) }
|
2019-07-31 08:46:58 -05:00
|
|
|
end
|
2019-10-03 13:48:56 -05:00
|
|
|
|
|
|
|
def category_mods_limit
|
|
|
|
@category_mods_limit || 100
|
|
|
|
end
|
|
|
|
|
|
|
|
def category_mods_limit=(number)
|
|
|
|
@category_mods_limit = number
|
|
|
|
end
|
2024-09-10 06:43:41 -05:00
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def apply_excluded_groups(query)
|
|
|
|
group_ids = SiteSetting.about_page_hidden_groups_map
|
|
|
|
return query if group_ids.blank?
|
|
|
|
|
|
|
|
query.joins(
|
|
|
|
DB.sql_fragment(
|
|
|
|
"LEFT JOIN group_users ON group_id IN (:group_ids) AND user_id = users.id",
|
|
|
|
group_ids:,
|
|
|
|
),
|
|
|
|
).where("group_users.id": nil)
|
|
|
|
end
|
2014-08-11 15:59:00 -05:00
|
|
|
end
|