From bd53dd521064b12261b82105624cf5f8b9ca9d69 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 9 Mar 2022 08:52:32 +0100 Subject: Change design of federation pages in admin UI (#17704) * Change design of federation pages in admin UI * Fix query performance in instance media attachments measure * Fix reblogs being included in instance languages dimension --- app/lib/admin/metrics/dimension.rb | 2 + .../dimension/instance_accounts_dimension.rb | 35 +++++++++++ .../dimension/instance_languages_dimension.rb | 37 ++++++++++++ app/lib/admin/metrics/measure.rb | 6 ++ app/lib/admin/metrics/measure/base_measure.rb | 8 +++ .../metrics/measure/instance_accounts_measure.rb | 58 ++++++++++++++++++ .../metrics/measure/instance_followers_measure.rb | 59 ++++++++++++++++++ .../metrics/measure/instance_follows_measure.rb | 59 ++++++++++++++++++ .../measure/instance_media_attachments_measure.rb | 69 ++++++++++++++++++++++ .../metrics/measure/instance_reports_measure.rb | 59 ++++++++++++++++++ .../metrics/measure/instance_statuses_measure.rb | 60 +++++++++++++++++++ app/lib/delivery_failure_tracker.rb | 2 +- 12 files changed, 453 insertions(+), 1 deletion(-) create mode 100644 app/lib/admin/metrics/dimension/instance_accounts_dimension.rb create mode 100644 app/lib/admin/metrics/dimension/instance_languages_dimension.rb create mode 100644 app/lib/admin/metrics/measure/instance_accounts_measure.rb create mode 100644 app/lib/admin/metrics/measure/instance_followers_measure.rb create mode 100644 app/lib/admin/metrics/measure/instance_follows_measure.rb create mode 100644 app/lib/admin/metrics/measure/instance_media_attachments_measure.rb create mode 100644 app/lib/admin/metrics/measure/instance_reports_measure.rb create mode 100644 app/lib/admin/metrics/measure/instance_statuses_measure.rb (limited to 'app/lib') diff --git a/app/lib/admin/metrics/dimension.rb b/app/lib/admin/metrics/dimension.rb index d8392ddfc..81b89d9b3 100644 --- a/app/lib/admin/metrics/dimension.rb +++ b/app/lib/admin/metrics/dimension.rb @@ -9,6 +9,8 @@ class Admin::Metrics::Dimension software_versions: Admin::Metrics::Dimension::SoftwareVersionsDimension, tag_servers: Admin::Metrics::Dimension::TagServersDimension, tag_languages: Admin::Metrics::Dimension::TagLanguagesDimension, + instance_accounts: Admin::Metrics::Dimension::InstanceAccountsDimension, + instance_languages: Admin::Metrics::Dimension::InstanceLanguagesDimension, }.freeze def self.retrieve(dimension_keys, start_at, end_at, limit, params) diff --git a/app/lib/admin/metrics/dimension/instance_accounts_dimension.rb b/app/lib/admin/metrics/dimension/instance_accounts_dimension.rb new file mode 100644 index 000000000..4eac8e611 --- /dev/null +++ b/app/lib/admin/metrics/dimension/instance_accounts_dimension.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +class Admin::Metrics::Dimension::InstanceAccountsDimension < Admin::Metrics::Dimension::BaseDimension + include LanguagesHelper + + def self.with_params? + true + end + + def key + 'instance_accounts' + end + + protected + + def perform_query + sql = <<-SQL.squish + SELECT accounts.username, count(follows.*) AS value + FROM accounts + LEFT JOIN follows ON follows.target_account_id = accounts.id + WHERE accounts.domain = $1 + GROUP BY accounts.id, follows.target_account_id + ORDER BY value DESC + LIMIT $2 + SQL + + rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, params[:domain]], [nil, @limit]]) + + rows.map { |row| { key: row['username'], human_key: row['username'], value: row['value'].to_s } } + end + + def params + @params.permit(:domain) + end +end diff --git a/app/lib/admin/metrics/dimension/instance_languages_dimension.rb b/app/lib/admin/metrics/dimension/instance_languages_dimension.rb new file mode 100644 index 000000000..1ede1a56e --- /dev/null +++ b/app/lib/admin/metrics/dimension/instance_languages_dimension.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +class Admin::Metrics::Dimension::InstanceLanguagesDimension < Admin::Metrics::Dimension::BaseDimension + include LanguagesHelper + + def self.with_params? + true + end + + def key + 'instance_languages' + end + + protected + + def perform_query + sql = <<-SQL.squish + SELECT COALESCE(statuses.language, 'und') AS language, count(*) AS value + FROM statuses + INNER JOIN accounts ON accounts.id = statuses.account_id + WHERE accounts.domain = $1 + AND statuses.id BETWEEN $2 AND $3 + AND statuses.reblog_of_id IS NULL + GROUP BY COALESCE(statuses.language, 'und') + ORDER BY count(*) DESC + LIMIT $4 + SQL + + rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, params[:domain]], [nil, Mastodon::Snowflake.id_at(@start_at, with_random: false)], [nil, Mastodon::Snowflake.id_at(@end_at, with_random: false)], [nil, @limit]]) + + rows.map { |row| { key: row['language'], human_key: standard_locale_name(row['language']), value: row['value'].to_s } } + end + + def params + @params.permit(:domain) + end +end diff --git a/app/lib/admin/metrics/measure.rb b/app/lib/admin/metrics/measure.rb index a839498a1..0b510eb25 100644 --- a/app/lib/admin/metrics/measure.rb +++ b/app/lib/admin/metrics/measure.rb @@ -10,6 +10,12 @@ class Admin::Metrics::Measure tag_accounts: Admin::Metrics::Measure::TagAccountsMeasure, tag_uses: Admin::Metrics::Measure::TagUsesMeasure, tag_servers: Admin::Metrics::Measure::TagServersMeasure, + instance_accounts: Admin::Metrics::Measure::InstanceAccountsMeasure, + instance_media_attachments: Admin::Metrics::Measure::InstanceMediaAttachmentsMeasure, + instance_reports: Admin::Metrics::Measure::InstanceReportsMeasure, + instance_statuses: Admin::Metrics::Measure::InstanceStatusesMeasure, + instance_follows: Admin::Metrics::Measure::InstanceFollowsMeasure, + instance_followers: Admin::Metrics::Measure::InstanceFollowersMeasure, }.freeze def self.retrieve(measure_keys, start_at, end_at, params) diff --git a/app/lib/admin/metrics/measure/base_measure.rb b/app/lib/admin/metrics/measure/base_measure.rb index ed1df9c7d..e33a6c494 100644 --- a/app/lib/admin/metrics/measure/base_measure.rb +++ b/app/lib/admin/metrics/measure/base_measure.rb @@ -26,6 +26,14 @@ class Admin::Metrics::Measure::BaseMeasure raise NotImplementedError end + def unit + nil + end + + def total_in_time_range? + true + end + def total load[:total] end diff --git a/app/lib/admin/metrics/measure/instance_accounts_measure.rb b/app/lib/admin/metrics/measure/instance_accounts_measure.rb new file mode 100644 index 000000000..4c61a064a --- /dev/null +++ b/app/lib/admin/metrics/measure/instance_accounts_measure.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +class Admin::Metrics::Measure::InstanceAccountsMeasure < Admin::Metrics::Measure::BaseMeasure + def self.with_params? + true + end + + def key + 'instance_accounts' + end + + def total_in_time_range? + false + end + + protected + + def perform_total_query + Account.where(domain: params[:domain]).count + end + + def perform_previous_total_query + nil + end + + def perform_data_query + sql = <<-SQL.squish + SELECT axis.*, ( + WITH new_accounts AS ( + SELECT accounts.id + FROM accounts + WHERE date_trunc('day', accounts.created_at)::date = axis.period + AND accounts.domain = $3::text + ) + SELECT count(*) FROM new_accounts + ) AS value + FROM ( + SELECT generate_series(date_trunc('day', $1::timestamp)::date, date_trunc('day', $2::timestamp)::date, interval '1 day') AS period + ) AS axis + SQL + + rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, @start_at], [nil, @end_at], [nil, params[:domain]]]) + + rows.map { |row| { date: row['period'], value: row['value'].to_s } } + end + + def time_period + (@start_at.to_date..@end_at.to_date) + end + + def previous_time_period + ((@start_at.to_date - length_of_period)..(@end_at.to_date - length_of_period)) + end + + def params + @params.permit(:domain) + end +end diff --git a/app/lib/admin/metrics/measure/instance_followers_measure.rb b/app/lib/admin/metrics/measure/instance_followers_measure.rb new file mode 100644 index 000000000..caa60013b --- /dev/null +++ b/app/lib/admin/metrics/measure/instance_followers_measure.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +class Admin::Metrics::Measure::InstanceFollowersMeasure < Admin::Metrics::Measure::BaseMeasure + def self.with_params? + true + end + + def key + 'instance_followers' + end + + def total_in_time_range? + false + end + + protected + + def perform_total_query + Follow.joins(:account).merge(Account.where(domain: params[:domain])).count + end + + def perform_previous_total_query + nil + end + + def perform_data_query + sql = <<-SQL.squish + SELECT axis.*, ( + WITH new_followers AS ( + SELECT follows.id + FROM follows + INNER JOIN accounts ON follows.account_id = accounts.id + WHERE date_trunc('day', follows.created_at)::date = axis.period + AND accounts.domain = $3::text + ) + SELECT count(*) FROM new_followers + ) AS value + FROM ( + SELECT generate_series(date_trunc('day', $1::timestamp)::date, date_trunc('day', $2::timestamp)::date, interval '1 day') AS period + ) AS axis + SQL + + rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, @start_at], [nil, @end_at], [nil, params[:domain]]]) + + rows.map { |row| { date: row['period'], value: row['value'].to_s } } + end + + def time_period + (@start_at.to_date..@end_at.to_date) + end + + def previous_time_period + ((@start_at.to_date - length_of_period)..(@end_at.to_date - length_of_period)) + end + + def params + @params.permit(:domain) + end +end diff --git a/app/lib/admin/metrics/measure/instance_follows_measure.rb b/app/lib/admin/metrics/measure/instance_follows_measure.rb new file mode 100644 index 000000000..b026c7e6d --- /dev/null +++ b/app/lib/admin/metrics/measure/instance_follows_measure.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +class Admin::Metrics::Measure::InstanceFollowsMeasure < Admin::Metrics::Measure::BaseMeasure + def self.with_params? + true + end + + def key + 'instance_follows' + end + + def total_in_time_range? + false + end + + protected + + def perform_total_query + Follow.joins(:target_account).merge(Account.where(domain: params[:domain])).count + end + + def perform_previous_total_query + nil + end + + def perform_data_query + sql = <<-SQL.squish + SELECT axis.*, ( + WITH new_follows AS ( + SELECT follows.id + FROM follows + INNER JOIN accounts ON follows.target_account_id = accounts.id + WHERE date_trunc('day', follows.created_at)::date = axis.period + AND accounts.domain = $3::text + ) + SELECT count(*) FROM new_follows + ) AS value + FROM ( + SELECT generate_series(date_trunc('day', $1::timestamp)::date, date_trunc('day', $2::timestamp)::date, interval '1 day') AS period + ) AS axis + SQL + + rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, @start_at], [nil, @end_at], [nil, params[:domain]]]) + + rows.map { |row| { date: row['period'], value: row['value'].to_s } } + end + + def time_period + (@start_at.to_date..@end_at.to_date) + end + + def previous_time_period + ((@start_at.to_date - length_of_period)..(@end_at.to_date - length_of_period)) + end + + def params + @params.permit(:domain) + end +end diff --git a/app/lib/admin/metrics/measure/instance_media_attachments_measure.rb b/app/lib/admin/metrics/measure/instance_media_attachments_measure.rb new file mode 100644 index 000000000..2e2154c92 --- /dev/null +++ b/app/lib/admin/metrics/measure/instance_media_attachments_measure.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +class Admin::Metrics::Measure::InstanceMediaAttachmentsMeasure < Admin::Metrics::Measure::BaseMeasure + include ActionView::Helpers::NumberHelper + + def self.with_params? + true + end + + def key + 'instance_media_attachments' + end + + def unit + 'bytes' + end + + def value_to_human_value(value) + number_to_human_size(value) + end + + def total_in_time_range? + false + end + + protected + + def perform_total_query + MediaAttachment.joins(:account).merge(Account.where(domain: params[:domain])).sum('COALESCE(file_file_size, 0) + COALESCE(thumbnail_file_size, 0)') + end + + def perform_previous_total_query + nil + end + + def perform_data_query + sql = <<-SQL.squish + SELECT axis.*, ( + WITH new_media_attachments AS ( + SELECT COALESCE(media_attachments.file_file_size, 0) + COALESCE(media_attachments.thumbnail_file_size, 0) AS size + FROM media_attachments + INNER JOIN accounts ON accounts.id = media_attachments.account_id + WHERE date_trunc('day', media_attachments.created_at)::date = axis.period + AND accounts.domain = $3::text + ) + SELECT SUM(size) FROM new_media_attachments + ) AS value + FROM ( + SELECT generate_series(date_trunc('day', $1::timestamp)::date, date_trunc('day', $2::timestamp)::date, interval '1 day') AS period + ) AS axis + SQL + + rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, @start_at], [nil, @end_at], [nil, params[:domain]]]) + + rows.map { |row| { date: row['period'], value: row['value'].to_s } } + end + + def time_period + (@start_at.to_date..@end_at.to_date) + end + + def previous_time_period + ((@start_at.to_date - length_of_period)..(@end_at.to_date - length_of_period)) + end + + def params + @params.permit(:domain) + end +end diff --git a/app/lib/admin/metrics/measure/instance_reports_measure.rb b/app/lib/admin/metrics/measure/instance_reports_measure.rb new file mode 100644 index 000000000..6b3f35067 --- /dev/null +++ b/app/lib/admin/metrics/measure/instance_reports_measure.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +class Admin::Metrics::Measure::InstanceReportsMeasure < Admin::Metrics::Measure::BaseMeasure + def self.with_params? + true + end + + def key + 'instance_reports' + end + + def total_in_time_range? + false + end + + protected + + def perform_total_query + Report.where(target_account: Account.where(domain: params[:domain])).count + end + + def perform_previous_total_query + nil + end + + def perform_data_query + sql = <<-SQL.squish + SELECT axis.*, ( + WITH new_reports AS ( + SELECT reports.id + FROM reports + INNER JOIN accounts ON accounts.id = reports.target_account_id + WHERE date_trunc('day', reports.created_at)::date = axis.period + AND accounts.domain = $3::text + ) + SELECT count(*) FROM new_reports + ) AS value + FROM ( + SELECT generate_series(date_trunc('day', $1::timestamp)::date, date_trunc('day', $2::timestamp)::date, interval '1 day') AS period + ) AS axis + SQL + + rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, @start_at], [nil, @end_at], [nil, params[:domain]]]) + + rows.map { |row| { date: row['period'], value: row['value'].to_s } } + end + + def time_period + (@start_at.to_date..@end_at.to_date) + end + + def previous_time_period + ((@start_at.to_date - length_of_period)..(@end_at.to_date - length_of_period)) + end + + def params + @params.permit(:domain) + end +end diff --git a/app/lib/admin/metrics/measure/instance_statuses_measure.rb b/app/lib/admin/metrics/measure/instance_statuses_measure.rb new file mode 100644 index 000000000..86b10da6c --- /dev/null +++ b/app/lib/admin/metrics/measure/instance_statuses_measure.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +class Admin::Metrics::Measure::InstanceStatusesMeasure < Admin::Metrics::Measure::BaseMeasure + def self.with_params? + true + end + + def key + 'instance_statuses' + end + + def total_in_time_range? + false + end + + protected + + def perform_total_query + Status.joins(:account).merge(Account.where(domain: params[:domain])).count + end + + def perform_previous_total_query + nil + end + + def perform_data_query + sql = <<-SQL.squish + SELECT axis.*, ( + WITH new_statuses AS ( + SELECT statuses.id + FROM statuses + INNER JOIN accounts ON accounts.id = statuses.account_id + WHERE statuses.id BETWEEN $3 AND $4 + AND accounts.domain = $5::text + AND date_trunc('day', statuses.created_at)::date = axis.period + ) + SELECT count(*) FROM new_statuses + ) AS value + FROM ( + SELECT generate_series(date_trunc('day', $1::timestamp)::date, date_trunc('day', $2::timestamp)::date, interval '1 day') AS period + ) AS axis + SQL + + rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, @start_at], [nil, @end_at], [nil, Mastodon::Snowflake.id_at(@start_at, with_random: false)], [nil, Mastodon::Snowflake.id_at(@end_at, with_random: false)], [nil, params[:domain]]]) + + rows.map { |row| { date: row['period'], value: row['value'].to_s } } + end + + def time_period + (@start_at.to_date..@end_at.to_date) + end + + def previous_time_period + ((@start_at.to_date - length_of_period)..(@end_at.to_date - length_of_period)) + end + + def params + @params.permit(:domain) + end +end diff --git a/app/lib/delivery_failure_tracker.rb b/app/lib/delivery_failure_tracker.rb index 8907ade4c..7b800fc0b 100644 --- a/app/lib/delivery_failure_tracker.rb +++ b/app/lib/delivery_failure_tracker.rb @@ -30,7 +30,7 @@ class DeliveryFailureTracker end def exhausted_deliveries_days - Redis.current.smembers(exhausted_deliveries_key).sort.map { |date| Date.new(date.slice(0, 4).to_i, date.slice(4, 2).to_i, date.slice(6, 2).to_i) } + @exhausted_deliveries_days ||= Redis.current.smembers(exhausted_deliveries_key).sort.map { |date| Date.new(date.slice(0, 4).to_i, date.slice(4, 2).to_i, date.slice(6, 2).to_i) } end alias reset! track_success! -- cgit