Merge pull request #1699 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes
This commit is contained in:
commit
2c8615fbf8
|
@ -661,18 +661,6 @@ body,
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&--inactive {
|
|
||||||
.log-entry__title {
|
|
||||||
text-decoration: line-through;
|
|
||||||
}
|
|
||||||
|
|
||||||
a,
|
|
||||||
.username,
|
|
||||||
.target {
|
|
||||||
color: $darker-text-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a.name-tag,
|
a.name-tag,
|
||||||
|
@ -1208,17 +1196,6 @@ a.sparkline {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
|
||||||
color: $ui-highlight-color;
|
|
||||||
text-decoration: none;
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus,
|
|
||||||
&:active {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&--horizontal {
|
&--horizontal {
|
||||||
|
@ -1306,6 +1283,30 @@ a.sparkline {
|
||||||
background: linear-gradient(to left, $ui-base-color, transparent);
|
background: linear-gradient(to left, $ui-base-color, transparent);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $secondary-text-color;
|
||||||
|
text-decoration: none;
|
||||||
|
unicode-bidi: isolate;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
color: lighten($dark-text-color, 7%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mention {
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
span {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__actions {
|
&__actions {
|
||||||
|
@ -1513,6 +1514,25 @@ a.sparkline {
|
||||||
&:last-child {
|
&:last-child {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__rules {
|
||||||
|
list-style: disc;
|
||||||
|
padding-left: 15px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: $darker-text-color;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
color: $primary-text-color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__statuses-list {
|
&__statuses-list {
|
||||||
|
|
|
@ -661,18 +661,6 @@ body,
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&--inactive {
|
|
||||||
.log-entry__title {
|
|
||||||
text-decoration: line-through;
|
|
||||||
}
|
|
||||||
|
|
||||||
a,
|
|
||||||
.username,
|
|
||||||
.target {
|
|
||||||
color: $darker-text-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a.name-tag,
|
a.name-tag,
|
||||||
|
@ -1208,17 +1196,6 @@ a.sparkline {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
|
||||||
color: $ui-highlight-color;
|
|
||||||
text-decoration: none;
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus,
|
|
||||||
&:active {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&--horizontal {
|
&--horizontal {
|
||||||
|
@ -1306,6 +1283,30 @@ a.sparkline {
|
||||||
background: linear-gradient(to left, $ui-base-color, transparent);
|
background: linear-gradient(to left, $ui-base-color, transparent);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $secondary-text-color;
|
||||||
|
text-decoration: none;
|
||||||
|
unicode-bidi: isolate;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
color: lighten($dark-text-color, 7%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mention {
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
span {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__actions {
|
&__actions {
|
||||||
|
@ -1513,6 +1514,25 @@ a.sparkline {
|
||||||
&:last-child {
|
&:last-child {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__rules {
|
||||||
|
list-style: disc;
|
||||||
|
padding-left: 15px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: $darker-text-color;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
color: $primary-text-color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__statuses-list {
|
&__statuses-list {
|
||||||
|
|
|
@ -1,23 +1,34 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Admin::Metrics::Dimension::BaseDimension
|
class Admin::Metrics::Dimension::BaseDimension
|
||||||
|
CACHE_TTL = 5.minutes.freeze
|
||||||
|
|
||||||
def self.with_params?
|
def self.with_params?
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
attr_reader :loaded
|
||||||
|
|
||||||
|
alias loaded? loaded
|
||||||
|
|
||||||
def initialize(start_at, end_at, limit, params)
|
def initialize(start_at, end_at, limit, params)
|
||||||
@start_at = start_at&.to_datetime
|
@start_at = start_at&.to_datetime
|
||||||
@end_at = end_at&.to_datetime
|
@end_at = end_at&.to_datetime
|
||||||
@limit = limit&.to_i
|
@limit = limit&.to_i
|
||||||
@params = params
|
@params = params
|
||||||
|
@loaded = false
|
||||||
end
|
end
|
||||||
|
|
||||||
def key
|
def key
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def cache_key
|
||||||
|
["metrics/dimension/#{key}", @start_at, @end_at, @limit, canonicalized_params].join(';')
|
||||||
|
end
|
||||||
|
|
||||||
def data
|
def data
|
||||||
raise NotImplementedError
|
load
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.model_name
|
def self.model_name
|
||||||
|
@ -30,11 +41,28 @@ class Admin::Metrics::Dimension::BaseDimension
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
|
def load
|
||||||
|
unless loaded?
|
||||||
|
@values = Rails.cache.fetch(cache_key, expires_in: CACHE_TTL) { perform_query }
|
||||||
|
@loaded = true
|
||||||
|
end
|
||||||
|
|
||||||
|
@values
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform_query
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
def time_period
|
def time_period
|
||||||
(@start_at..@end_at)
|
(@start_at..@end_at)
|
||||||
end
|
end
|
||||||
|
|
||||||
def params
|
def params
|
||||||
raise NotImplementedError
|
{}
|
||||||
|
end
|
||||||
|
|
||||||
|
def canonicalized_params
|
||||||
|
params.to_h.to_a.sort_by { |k, _v| k.to_s }.map { |k, v| "#{k}=#{v}" }.join(';')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,7 +7,9 @@ class Admin::Metrics::Dimension::LanguagesDimension < Admin::Metrics::Dimension:
|
||||||
'languages'
|
'languages'
|
||||||
end
|
end
|
||||||
|
|
||||||
def data
|
protected
|
||||||
|
|
||||||
|
def perform_query
|
||||||
sql = <<-SQL.squish
|
sql = <<-SQL.squish
|
||||||
SELECT locale, count(*) AS value
|
SELECT locale, count(*) AS value
|
||||||
FROM users
|
FROM users
|
||||||
|
|
|
@ -5,7 +5,9 @@ class Admin::Metrics::Dimension::ServersDimension < Admin::Metrics::Dimension::B
|
||||||
'servers'
|
'servers'
|
||||||
end
|
end
|
||||||
|
|
||||||
def data
|
protected
|
||||||
|
|
||||||
|
def perform_query
|
||||||
sql = <<-SQL.squish
|
sql = <<-SQL.squish
|
||||||
SELECT accounts.domain, count(*) AS value
|
SELECT accounts.domain, count(*) AS value
|
||||||
FROM statuses
|
FROM statuses
|
||||||
|
|
|
@ -7,12 +7,12 @@ class Admin::Metrics::Dimension::SoftwareVersionsDimension < Admin::Metrics::Dim
|
||||||
'software_versions'
|
'software_versions'
|
||||||
end
|
end
|
||||||
|
|
||||||
def data
|
protected
|
||||||
|
|
||||||
|
def perform_query
|
||||||
[mastodon_version, ruby_version, postgresql_version, redis_version]
|
[mastodon_version, ruby_version, postgresql_version, redis_version]
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def mastodon_version
|
def mastodon_version
|
||||||
value = Mastodon::Version.to_s
|
value = Mastodon::Version.to_s
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,9 @@ class Admin::Metrics::Dimension::SourcesDimension < Admin::Metrics::Dimension::B
|
||||||
'sources'
|
'sources'
|
||||||
end
|
end
|
||||||
|
|
||||||
def data
|
protected
|
||||||
|
|
||||||
|
def perform_query
|
||||||
sql = <<-SQL.squish
|
sql = <<-SQL.squish
|
||||||
SELECT oauth_applications.name, count(*) AS value
|
SELECT oauth_applications.name, count(*) AS value
|
||||||
FROM users
|
FROM users
|
||||||
|
|
|
@ -8,12 +8,12 @@ class Admin::Metrics::Dimension::SpaceUsageDimension < Admin::Metrics::Dimension
|
||||||
'space_usage'
|
'space_usage'
|
||||||
end
|
end
|
||||||
|
|
||||||
def data
|
protected
|
||||||
|
|
||||||
|
def perform_query
|
||||||
[postgresql_size, redis_size, media_size]
|
[postgresql_size, redis_size, media_size]
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def postgresql_size
|
def postgresql_size
|
||||||
value = ActiveRecord::Base.connection.execute('SELECT pg_database_size(current_database())').first['pg_database_size']
|
value = ActiveRecord::Base.connection.execute('SELECT pg_database_size(current_database())').first['pg_database_size']
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,9 @@ class Admin::Metrics::Dimension::TagLanguagesDimension < Admin::Metrics::Dimensi
|
||||||
'tag_languages'
|
'tag_languages'
|
||||||
end
|
end
|
||||||
|
|
||||||
def data
|
protected
|
||||||
|
|
||||||
|
def perform_query
|
||||||
sql = <<-SQL.squish
|
sql = <<-SQL.squish
|
||||||
SELECT COALESCE(statuses.language, 'und') AS language, count(*) AS value
|
SELECT COALESCE(statuses.language, 'und') AS language, count(*) AS value
|
||||||
FROM statuses
|
FROM statuses
|
||||||
|
@ -28,8 +30,6 @@ class Admin::Metrics::Dimension::TagLanguagesDimension < Admin::Metrics::Dimensi
|
||||||
rows.map { |row| { key: row['language'], human_key: standard_locale_name(row['language']), value: row['value'].to_s } }
|
rows.map { |row| { key: row['language'], human_key: standard_locale_name(row['language']), value: row['value'].to_s } }
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def params
|
def params
|
||||||
@params.permit(:id)
|
@params.permit(:id)
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,7 +9,9 @@ class Admin::Metrics::Dimension::TagServersDimension < Admin::Metrics::Dimension
|
||||||
'tag_servers'
|
'tag_servers'
|
||||||
end
|
end
|
||||||
|
|
||||||
def data
|
protected
|
||||||
|
|
||||||
|
def perform_query
|
||||||
sql = <<-SQL.squish
|
sql = <<-SQL.squish
|
||||||
SELECT accounts.domain, count(*) AS value
|
SELECT accounts.domain, count(*) AS value
|
||||||
FROM statuses
|
FROM statuses
|
||||||
|
@ -27,8 +29,6 @@ class Admin::Metrics::Dimension::TagServersDimension < Admin::Metrics::Dimension
|
||||||
rows.map { |row| { key: row['domain'] || Rails.configuration.x.local_domain, human_key: row['domain'] || Rails.configuration.x.local_domain, value: row['value'].to_s } }
|
rows.map { |row| { key: row['domain'] || Rails.configuration.x.local_domain, human_key: row['domain'] || Rails.configuration.x.local_domain, value: row['value'].to_s } }
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def params
|
def params
|
||||||
@params.permit(:id)
|
@params.permit(:id)
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,20 +5,20 @@ class Admin::Metrics::Measure::ActiveUsersMeasure < Admin::Metrics::Measure::Bas
|
||||||
'active_users'
|
'active_users'
|
||||||
end
|
end
|
||||||
|
|
||||||
def total
|
protected
|
||||||
|
|
||||||
|
def perform_total_query
|
||||||
activity_tracker.sum(time_period.first, time_period.last)
|
activity_tracker.sum(time_period.first, time_period.last)
|
||||||
end
|
end
|
||||||
|
|
||||||
def previous_total
|
def perform_previous_total_query
|
||||||
activity_tracker.sum(previous_time_period.first, previous_time_period.last)
|
activity_tracker.sum(previous_time_period.first, previous_time_period.last)
|
||||||
end
|
end
|
||||||
|
|
||||||
def data
|
def perform_data_query
|
||||||
activity_tracker.get(time_period.first, time_period.last).map { |date, value| { date: date.to_time(:utc).iso8601, value: value.to_s } }
|
activity_tracker.get(time_period.first, time_period.last).map { |date, value| { date: date.to_time(:utc).iso8601, value: value.to_s } }
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def activity_tracker
|
def activity_tracker
|
||||||
@activity_tracker ||= ActivityTracker.new('activity:logins', :unique)
|
@activity_tracker ||= ActivityTracker.new('activity:logins', :unique)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,14 +1,25 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Admin::Metrics::Measure::BaseMeasure
|
class Admin::Metrics::Measure::BaseMeasure
|
||||||
|
CACHE_TTL = 5.minutes.freeze
|
||||||
|
|
||||||
def self.with_params?
|
def self.with_params?
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
attr_reader :loaded
|
||||||
|
|
||||||
|
alias loaded? loaded
|
||||||
|
|
||||||
def initialize(start_at, end_at, params)
|
def initialize(start_at, end_at, params)
|
||||||
@start_at = start_at&.to_datetime
|
@start_at = start_at&.to_datetime
|
||||||
@end_at = end_at&.to_datetime
|
@end_at = end_at&.to_datetime
|
||||||
@params = params
|
@params = params
|
||||||
|
@loaded = false
|
||||||
|
end
|
||||||
|
|
||||||
|
def cache_key
|
||||||
|
["metrics/measure/#{key}", @start_at, @end_at, canonicalized_params].join(';')
|
||||||
end
|
end
|
||||||
|
|
||||||
def key
|
def key
|
||||||
|
@ -16,15 +27,15 @@ class Admin::Metrics::Measure::BaseMeasure
|
||||||
end
|
end
|
||||||
|
|
||||||
def total
|
def total
|
||||||
raise NotImplementedError
|
load[:total]
|
||||||
end
|
end
|
||||||
|
|
||||||
def previous_total
|
def previous_total
|
||||||
raise NotImplementedError
|
load[:previous_total]
|
||||||
end
|
end
|
||||||
|
|
||||||
def data
|
def data
|
||||||
raise NotImplementedError
|
load[:data]
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.model_name
|
def self.model_name
|
||||||
|
@ -37,6 +48,35 @@ class Admin::Metrics::Measure::BaseMeasure
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
|
def load
|
||||||
|
unless loaded?
|
||||||
|
@values = Rails.cache.fetch(cache_key, expires_in: CACHE_TTL) { perform_queries }.with_indifferent_access
|
||||||
|
@loaded = true
|
||||||
|
end
|
||||||
|
|
||||||
|
@values
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform_queries
|
||||||
|
{
|
||||||
|
total: perform_total_query,
|
||||||
|
previous_total: perform_previous_total_query,
|
||||||
|
data: perform_data_query,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform_total_query
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform_previous_total_query
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform_data_query
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
def time_period
|
def time_period
|
||||||
(@start_at..@end_at)
|
(@start_at..@end_at)
|
||||||
end
|
end
|
||||||
|
@ -50,6 +90,10 @@ class Admin::Metrics::Measure::BaseMeasure
|
||||||
end
|
end
|
||||||
|
|
||||||
def params
|
def params
|
||||||
raise NotImplementedError
|
{}
|
||||||
|
end
|
||||||
|
|
||||||
|
def canonicalized_params
|
||||||
|
params.to_h.to_a.sort_by { |k, _v| k.to_s }.map { |k, v| "#{k}=#{v}" }.join(';')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,20 +5,20 @@ class Admin::Metrics::Measure::InteractionsMeasure < Admin::Metrics::Measure::Ba
|
||||||
'interactions'
|
'interactions'
|
||||||
end
|
end
|
||||||
|
|
||||||
def total
|
protected
|
||||||
|
|
||||||
|
def perform_total_query
|
||||||
activity_tracker.sum(time_period.first, time_period.last)
|
activity_tracker.sum(time_period.first, time_period.last)
|
||||||
end
|
end
|
||||||
|
|
||||||
def previous_total
|
def perform_previous_total_query
|
||||||
activity_tracker.sum(previous_time_period.first, previous_time_period.last)
|
activity_tracker.sum(previous_time_period.first, previous_time_period.last)
|
||||||
end
|
end
|
||||||
|
|
||||||
def data
|
def perform_data_query
|
||||||
activity_tracker.get(time_period.first, time_period.last).map { |date, value| { date: date.to_time(:utc).iso8601, value: value.to_s } }
|
activity_tracker.get(time_period.first, time_period.last).map { |date, value| { date: date.to_time(:utc).iso8601, value: value.to_s } }
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def activity_tracker
|
def activity_tracker
|
||||||
@activity_tracker ||= ActivityTracker.new('activity:interactions', :basic)
|
@activity_tracker ||= ActivityTracker.new('activity:interactions', :basic)
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,15 +5,17 @@ class Admin::Metrics::Measure::NewUsersMeasure < Admin::Metrics::Measure::BaseMe
|
||||||
'new_users'
|
'new_users'
|
||||||
end
|
end
|
||||||
|
|
||||||
def total
|
protected
|
||||||
|
|
||||||
|
def perform_total_query
|
||||||
User.where(created_at: time_period).count
|
User.where(created_at: time_period).count
|
||||||
end
|
end
|
||||||
|
|
||||||
def previous_total
|
def perform_previous_total_query
|
||||||
User.where(created_at: previous_time_period).count
|
User.where(created_at: previous_time_period).count
|
||||||
end
|
end
|
||||||
|
|
||||||
def data
|
def perform_data_query
|
||||||
sql = <<-SQL.squish
|
sql = <<-SQL.squish
|
||||||
SELECT axis.*, (
|
SELECT axis.*, (
|
||||||
WITH new_users AS (
|
WITH new_users AS (
|
||||||
|
|
|
@ -5,15 +5,17 @@ class Admin::Metrics::Measure::OpenedReportsMeasure < Admin::Metrics::Measure::B
|
||||||
'opened_reports'
|
'opened_reports'
|
||||||
end
|
end
|
||||||
|
|
||||||
def total
|
protected
|
||||||
|
|
||||||
|
def perform_total_query
|
||||||
Report.where(created_at: time_period).count
|
Report.where(created_at: time_period).count
|
||||||
end
|
end
|
||||||
|
|
||||||
def previous_total
|
def perform_previous_total_query
|
||||||
Report.where(created_at: previous_time_period).count
|
Report.where(created_at: previous_time_period).count
|
||||||
end
|
end
|
||||||
|
|
||||||
def data
|
def perform_data_query
|
||||||
sql = <<-SQL.squish
|
sql = <<-SQL.squish
|
||||||
SELECT axis.*, (
|
SELECT axis.*, (
|
||||||
WITH new_reports AS (
|
WITH new_reports AS (
|
||||||
|
|
|
@ -5,15 +5,17 @@ class Admin::Metrics::Measure::ResolvedReportsMeasure < Admin::Metrics::Measure:
|
||||||
'resolved_reports'
|
'resolved_reports'
|
||||||
end
|
end
|
||||||
|
|
||||||
def total
|
protected
|
||||||
|
|
||||||
|
def perform_total_query
|
||||||
Report.resolved.where(action_taken_at: time_period).count
|
Report.resolved.where(action_taken_at: time_period).count
|
||||||
end
|
end
|
||||||
|
|
||||||
def previous_total
|
def perform_previous_total_query
|
||||||
Report.resolved.where(action_taken_at: previous_time_period).count
|
Report.resolved.where(action_taken_at: previous_time_period).count
|
||||||
end
|
end
|
||||||
|
|
||||||
def data
|
def perform_data_query
|
||||||
sql = <<-SQL.squish
|
sql = <<-SQL.squish
|
||||||
SELECT axis.*, (
|
SELECT axis.*, (
|
||||||
WITH resolved_reports AS (
|
WITH resolved_reports AS (
|
||||||
|
|
|
@ -9,20 +9,20 @@ class Admin::Metrics::Measure::TagAccountsMeasure < Admin::Metrics::Measure::Bas
|
||||||
'tag_accounts'
|
'tag_accounts'
|
||||||
end
|
end
|
||||||
|
|
||||||
def total
|
protected
|
||||||
|
|
||||||
|
def perform_total_query
|
||||||
tag.history.aggregate(time_period).accounts
|
tag.history.aggregate(time_period).accounts
|
||||||
end
|
end
|
||||||
|
|
||||||
def previous_total
|
def perform_previous_total_query
|
||||||
tag.history.aggregate(previous_time_period).accounts
|
tag.history.aggregate(previous_time_period).accounts
|
||||||
end
|
end
|
||||||
|
|
||||||
def data
|
def perform_data_query
|
||||||
time_period.map { |date| { date: date.to_time(:utc).iso8601, value: tag.history.get(date).accounts.to_s } }
|
time_period.map { |date| { date: date.to_time(:utc).iso8601, value: tag.history.get(date).accounts.to_s } }
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def tag
|
def tag
|
||||||
@tag ||= Tag.find(params[:id])
|
@tag ||= Tag.find(params[:id])
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,15 +9,17 @@ class Admin::Metrics::Measure::TagServersMeasure < Admin::Metrics::Measure::Base
|
||||||
'tag_servers'
|
'tag_servers'
|
||||||
end
|
end
|
||||||
|
|
||||||
def total
|
protected
|
||||||
|
|
||||||
|
def perform_total_query
|
||||||
tag.statuses.where('statuses.id BETWEEN ? AND ?', Mastodon::Snowflake.id_at(@start_at, with_random: false), Mastodon::Snowflake.id_at(@end_at, with_random: false)).joins(:account).count('distinct accounts.domain')
|
tag.statuses.where('statuses.id BETWEEN ? AND ?', Mastodon::Snowflake.id_at(@start_at, with_random: false), Mastodon::Snowflake.id_at(@end_at, with_random: false)).joins(:account).count('distinct accounts.domain')
|
||||||
end
|
end
|
||||||
|
|
||||||
def previous_total
|
def perform_previous_total_query
|
||||||
tag.statuses.where('statuses.id BETWEEN ? AND ?', Mastodon::Snowflake.id_at(@start_at - length_of_period, with_random: false), Mastodon::Snowflake.id_at(@end_at - length_of_period, with_random: false)).joins(:account).count('distinct accounts.domain')
|
tag.statuses.where('statuses.id BETWEEN ? AND ?', Mastodon::Snowflake.id_at(@start_at - length_of_period, with_random: false), Mastodon::Snowflake.id_at(@end_at - length_of_period, with_random: false)).joins(:account).count('distinct accounts.domain')
|
||||||
end
|
end
|
||||||
|
|
||||||
def data
|
def perform_data_query
|
||||||
sql = <<-SQL.squish
|
sql = <<-SQL.squish
|
||||||
SELECT axis.*, (
|
SELECT axis.*, (
|
||||||
SELECT count(distinct accounts.domain) AS value
|
SELECT count(distinct accounts.domain) AS value
|
||||||
|
@ -38,8 +40,6 @@ class Admin::Metrics::Measure::TagServersMeasure < Admin::Metrics::Measure::Base
|
||||||
rows.map { |row| { date: row['day'], value: row['value'].to_s } }
|
rows.map { |row| { date: row['day'], value: row['value'].to_s } }
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def tag
|
def tag
|
||||||
@tag ||= Tag.find(params[:id])
|
@tag ||= Tag.find(params[:id])
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,20 +9,20 @@ class Admin::Metrics::Measure::TagUsesMeasure < Admin::Metrics::Measure::BaseMea
|
||||||
'tag_uses'
|
'tag_uses'
|
||||||
end
|
end
|
||||||
|
|
||||||
def total
|
protected
|
||||||
|
|
||||||
|
def perform_total_query
|
||||||
tag.history.aggregate(time_period).uses
|
tag.history.aggregate(time_period).uses
|
||||||
end
|
end
|
||||||
|
|
||||||
def previous_total
|
def perform_previous_total_query
|
||||||
tag.history.aggregate(previous_time_period).uses
|
tag.history.aggregate(previous_time_period).uses
|
||||||
end
|
end
|
||||||
|
|
||||||
def data
|
def perform_data_query
|
||||||
time_period.map { |date| { date: date.to_time(:utc).iso8601, value: tag.history.get(date).uses.to_s } }
|
time_period.map { |date| { date: date.to_time(:utc).iso8601, value: tag.history.get(date).uses.to_s } }
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def tag
|
def tag
|
||||||
@tag ||= Tag.find(params[:id])
|
@tag ||= Tag.find(params[:id])
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Admin::Metrics::Retention
|
class Admin::Metrics::Retention
|
||||||
|
CACHE_TTL = 5.minutes.freeze
|
||||||
|
|
||||||
class Cohort < ActiveModelSerializers::Model
|
class Cohort < ActiveModelSerializers::Model
|
||||||
attributes :period, :frequency, :data
|
attributes :period, :frequency, :data
|
||||||
end
|
end
|
||||||
|
@ -9,13 +11,37 @@ class Admin::Metrics::Retention
|
||||||
attributes :date, :rate, :value
|
attributes :date, :rate, :value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
attr_reader :loaded
|
||||||
|
|
||||||
|
alias loaded? loaded
|
||||||
|
|
||||||
def initialize(start_at, end_at, frequency)
|
def initialize(start_at, end_at, frequency)
|
||||||
@start_at = start_at&.to_date
|
@start_at = start_at&.to_date
|
||||||
@end_at = end_at&.to_date
|
@end_at = end_at&.to_date
|
||||||
@frequency = %w(day month).include?(frequency) ? frequency : 'day'
|
@frequency = %w(day month).include?(frequency) ? frequency : 'day'
|
||||||
|
@loaded = false
|
||||||
|
end
|
||||||
|
|
||||||
|
def cache_key
|
||||||
|
['metrics/retention', @start_at, @end_at, @frequency].join(';')
|
||||||
end
|
end
|
||||||
|
|
||||||
def cohorts
|
def cohorts
|
||||||
|
load
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def load
|
||||||
|
unless loaded?
|
||||||
|
@values = Rails.cache.fetch(cache_key, expires_in: CACHE_TTL) { perform_query }
|
||||||
|
@loaded = true
|
||||||
|
end
|
||||||
|
|
||||||
|
@values
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform_query
|
||||||
sql = <<-SQL.squish
|
sql = <<-SQL.squish
|
||||||
SELECT axis.*, (
|
SELECT axis.*, (
|
||||||
WITH new_users AS (
|
WITH new_users AS (
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
class VideoMetadataExtractor
|
class VideoMetadataExtractor
|
||||||
attr_reader :duration, :bitrate, :video_codec, :audio_codec,
|
attr_reader :duration, :bitrate, :video_codec, :audio_codec,
|
||||||
:colorspace, :width, :height, :frame_rate
|
:colorspace, :width, :height, :frame_rate, :r_frame_rate
|
||||||
|
|
||||||
def initialize(path)
|
def initialize(path)
|
||||||
@path = path
|
@path = path
|
||||||
|
@ -42,6 +42,7 @@ class VideoMetadataExtractor
|
||||||
@width = video_stream[:width]
|
@width = video_stream[:width]
|
||||||
@height = video_stream[:height]
|
@height = video_stream[:height]
|
||||||
@frame_rate = video_stream[:avg_frame_rate] == '0/0' ? nil : Rational(video_stream[:avg_frame_rate])
|
@frame_rate = video_stream[:avg_frame_rate] == '0/0' ? nil : Rational(video_stream[:avg_frame_rate])
|
||||||
|
@r_frame_rate = video_stream[:r_frame_rate] == '0/0' ? nil : Rational(video_stream[:r_frame_rate])
|
||||||
end
|
end
|
||||||
|
|
||||||
if (audio_stream = audio_streams.first)
|
if (audio_stream = audio_streams.first)
|
||||||
|
|
|
@ -38,6 +38,12 @@ class MediaAttachment < ApplicationRecord
|
||||||
|
|
||||||
MAX_DESCRIPTION_LENGTH = 1_500
|
MAX_DESCRIPTION_LENGTH = 1_500
|
||||||
|
|
||||||
|
IMAGE_LIMIT = (ENV['MAX_IMAGE_SIZE'] || 10.megabytes).to_i
|
||||||
|
VIDEO_LIMIT = (ENV['MAX_VIDEO_SIZE'] || 40.megabytes).to_i
|
||||||
|
|
||||||
|
MAX_VIDEO_MATRIX_LIMIT = 2_304_000 # 1920x1200px
|
||||||
|
MAX_VIDEO_FRAME_RATE = 60
|
||||||
|
|
||||||
IMAGE_FILE_EXTENSIONS = %w(.jpg .jpeg .png .gif).freeze
|
IMAGE_FILE_EXTENSIONS = %w(.jpg .jpeg .png .gif).freeze
|
||||||
VIDEO_FILE_EXTENSIONS = %w(.webm .mp4 .m4v .mov).freeze
|
VIDEO_FILE_EXTENSIONS = %w(.webm .mp4 .m4v .mov).freeze
|
||||||
AUDIO_FILE_EXTENSIONS = %w(.ogg .oga .mp3 .wav .flac .opus .aac .m4a .3gp .wma).freeze
|
AUDIO_FILE_EXTENSIONS = %w(.ogg .oga .mp3 .wav .flac .opus .aac .m4a .3gp .wma).freeze
|
||||||
|
@ -75,6 +81,7 @@ class MediaAttachment < ApplicationRecord
|
||||||
VIDEO_FORMAT = {
|
VIDEO_FORMAT = {
|
||||||
format: 'mp4',
|
format: 'mp4',
|
||||||
content_type: 'video/mp4',
|
content_type: 'video/mp4',
|
||||||
|
vfr_frame_rate_threshold: MAX_VIDEO_FRAME_RATE,
|
||||||
convert_options: {
|
convert_options: {
|
||||||
output: {
|
output: {
|
||||||
'loglevel' => 'fatal',
|
'loglevel' => 'fatal',
|
||||||
|
@ -152,12 +159,6 @@ class MediaAttachment < ApplicationRecord
|
||||||
all: '-quality 90 -strip +set modify-date +set create-date',
|
all: '-quality 90 -strip +set modify-date +set create-date',
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
IMAGE_LIMIT = (ENV['MAX_IMAGE_SIZE'] || 10.megabytes).to_i
|
|
||||||
VIDEO_LIMIT = (ENV['MAX_VIDEO_SIZE'] || 40.megabytes).to_i
|
|
||||||
|
|
||||||
MAX_VIDEO_MATRIX_LIMIT = 2_304_000 # 1920x1200px
|
|
||||||
MAX_VIDEO_FRAME_RATE = 60
|
|
||||||
|
|
||||||
belongs_to :account, inverse_of: :media_attachments, optional: true
|
belongs_to :account, inverse_of: :media_attachments, optional: true
|
||||||
belongs_to :status, inverse_of: :media_attachments, optional: true
|
belongs_to :status, inverse_of: :media_attachments, optional: true
|
||||||
belongs_to :scheduled_status, inverse_of: :media_attachments, optional: true
|
belongs_to :scheduled_status, inverse_of: :media_attachments, optional: true
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
= link_to disputes_strike_path(account_warning), class: ['log-entry', account_warning.overruled? && 'log-entry--inactive'] do
|
= link_to disputes_strike_path(account_warning), class: 'log-entry' do
|
||||||
.log-entry__header
|
.log-entry__header
|
||||||
.log-entry__avatar
|
.log-entry__avatar
|
||||||
= image_tag account_warning.target_account.avatar.url(:original), alt: '', width: 40, height: 40, class: 'avatar'
|
.indicator-icon{ class: account_warning.overruled? ? 'success' : 'failure' }
|
||||||
|
= fa_icon 'warning'
|
||||||
.log-entry__content
|
.log-entry__content
|
||||||
.log-entry__title
|
.log-entry__title
|
||||||
= t(account_warning.action, scope: 'admin.strikes.actions', name: content_tag(:span, account_warning.account.username, class: 'username'), target: content_tag(:span, account_warning.target_account.acct, class: 'target')).html_safe
|
= t(account_warning.action, scope: 'admin.strikes.actions', name: content_tag(:span, account_warning.account.username, class: 'username'), target: content_tag(:span, account_warning.target_account.acct, class: 'target')).html_safe
|
||||||
|
@ -11,7 +12,7 @@
|
||||||
|
|
||||||
- if account_warning.report_id.present?
|
- if account_warning.report_id.present?
|
||||||
·
|
·
|
||||||
= t('admin.reports.title', id: account_warning.report_id)
|
= t('admin.reports.report', id: account_warning.report_id)
|
||||||
|
|
||||||
- if account_warning.overruled?
|
- if account_warning.overruled?
|
||||||
·
|
·
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
.report-header__card
|
.report-header__card
|
||||||
.strike-card
|
.strike-card
|
||||||
- unless @strike.none_action?
|
- unless @strike.none_action?
|
||||||
%p= t "user_mailer.warning.explanation.#{@strike.action}"
|
%p= t "user_mailer.warning.explanation.#{@strike.action}", instance: Rails.configuration.x.local_domain
|
||||||
|
|
||||||
- unless @strike.text.blank?
|
- unless @strike.text.blank?
|
||||||
= Formatter.instance.linkify(@strike.text)
|
= Formatter.instance.linkify(@strike.text)
|
||||||
|
@ -34,9 +34,10 @@
|
||||||
= t("user_mailer.warning.categories.#{@strike.report.category}")
|
= t("user_mailer.warning.categories.#{@strike.report.category}")
|
||||||
|
|
||||||
- if @strike.report.violation? && @strike.report.rule_ids.present?
|
- if @strike.report.violation? && @strike.report.rule_ids.present?
|
||||||
%ul.rules-list
|
%ul.strike-card__rules
|
||||||
- @strike.report.rules.each do |rule|
|
- @strike.report.rules.each do |rule|
|
||||||
%li= rule.text
|
%li
|
||||||
|
%span.strike-card__rules__text= rule.text
|
||||||
|
|
||||||
- if @strike.status_ids.present? && !@strike.status_ids.empty?
|
- if @strike.status_ids.present? && !@strike.status_ids.empty?
|
||||||
%p
|
%p
|
||||||
|
@ -75,7 +76,7 @@
|
||||||
.report-header__details__item__header
|
.report-header__details__item__header
|
||||||
%strong= t('disputes.strikes.recipient')
|
%strong= t('disputes.strikes.recipient')
|
||||||
.report-header__details__item__content
|
.report-header__details__item__content
|
||||||
= admin_account_link_to @strike.target_account, path: can?(:show, @strike.target_account) ? admin_account_path(@strike.target_account_id) : ActivityPub::TagManager.instance.url_for(@strike.target_account)
|
= link_to @strike.target_account.username, can?(:show, @strike.target_account) ? admin_account_path(@strike.target_account_id) : ActivityPub::TagManager.instance.url_for(@strike.target_account), class: 'table-action-link'
|
||||||
.report-header__details__item
|
.report-header__details__item
|
||||||
.report-header__details__item__header
|
.report-header__details__item__header
|
||||||
%strong= t('disputes.strikes.action_taken')
|
%strong= t('disputes.strikes.action_taken')
|
||||||
|
@ -89,7 +90,7 @@
|
||||||
.report-header__details__item__header
|
.report-header__details__item__header
|
||||||
%strong= t('disputes.strikes.associated_report')
|
%strong= t('disputes.strikes.associated_report')
|
||||||
.report-header__details__item__content
|
.report-header__details__item__content
|
||||||
= link_to t('admin.reports.report', id: @strike.report.id), admin_report_path(@strike.report)
|
= link_to t('admin.reports.report', id: @strike.report.id), admin_report_path(@strike.report), class: 'table-action-link'
|
||||||
- if @appeal.persisted?
|
- if @appeal.persisted?
|
||||||
.report-header__details__item
|
.report-header__details__item
|
||||||
.report-header__details__item__header
|
.report-header__details__item__header
|
||||||
|
|
|
@ -13,6 +13,7 @@ module Paperclip
|
||||||
@time = options[:time] || 3
|
@time = options[:time] || 3
|
||||||
@passthrough_options = options[:passthrough_options]
|
@passthrough_options = options[:passthrough_options]
|
||||||
@convert_options = options[:convert_options].dup
|
@convert_options = options[:convert_options].dup
|
||||||
|
@vfr_threshold = options[:vfr_frame_rate_threshold]
|
||||||
end
|
end
|
||||||
|
|
||||||
def make
|
def make
|
||||||
|
@ -41,6 +42,11 @@ module Paperclip
|
||||||
when 'mp4'
|
when 'mp4'
|
||||||
@output_options['acodec'] = 'aac'
|
@output_options['acodec'] = 'aac'
|
||||||
@output_options['strict'] = 'experimental'
|
@output_options['strict'] = 'experimental'
|
||||||
|
|
||||||
|
if high_vfr?(metadata) && !eligible_to_passthrough?(metadata)
|
||||||
|
@output_options['vsync'] = 'vfr'
|
||||||
|
@output_options['r'] = @vfr_threshold
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
command_arguments, interpolations = prepare_command(destination)
|
command_arguments, interpolations = prepare_command(destination)
|
||||||
|
@ -88,13 +94,21 @@ module Paperclip
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_options_from_metadata(metadata)
|
def update_options_from_metadata(metadata)
|
||||||
return unless @passthrough_options && @passthrough_options[:video_codecs].include?(metadata.video_codec) && @passthrough_options[:audio_codecs].include?(metadata.audio_codec) && @passthrough_options[:colorspaces].include?(metadata.colorspace)
|
return unless eligible_to_passthrough?(metadata)
|
||||||
|
|
||||||
@format = @passthrough_options[:options][:format] || @format
|
@format = @passthrough_options[:options][:format] || @format
|
||||||
@time = @passthrough_options[:options][:time] || @time
|
@time = @passthrough_options[:options][:time] || @time
|
||||||
@convert_options = @passthrough_options[:options][:convert_options].dup
|
@convert_options = @passthrough_options[:options][:convert_options].dup
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def high_vfr?(metadata)
|
||||||
|
@vfr_threshold && metadata.r_frame_rate && metadata.r_frame_rate > @vfr_threshold
|
||||||
|
end
|
||||||
|
|
||||||
|
def eligible_to_passthrough?(metadata)
|
||||||
|
@passthrough_options && @passthrough_options[:video_codecs].include?(metadata.video_codec) && @passthrough_options[:audio_codecs].include?(metadata.audio_codec) && @passthrough_options[:colorspaces].include?(metadata.colorspace)
|
||||||
|
end
|
||||||
|
|
||||||
def update_attachment_type(metadata)
|
def update_attachment_type(metadata)
|
||||||
@attachment.instance.type = MediaAttachment.types[:gifv] unless metadata.audio_codec
|
@attachment.instance.type = MediaAttachment.types[:gifv] unless metadata.audio_codec
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue