mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-06-19 00:48:08 +08:00
The "Daily engaged" KPI on the admin dashboard could show an upward trend while engagement was actually falling, because the current period was summed while the previous period it compared against was a daily average. This commit drives the average-versus-sum choice off each report's `average` flag instead of a hardcoded check, so average reports like `daily_engaged_users` average both periods while cumulative reports keep summing both.
136 lines
4 KiB
Ruby
Vendored
136 lines
4 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
class AdminDashboardEngagement
|
|
include AdminDashboardKpis
|
|
|
|
DEFAULT_RANGE_DAYS = 30
|
|
|
|
KPI_REPORTS = {
|
|
dau_mau: "dau_by_mau",
|
|
daily_engaged_users: "daily_engaged_users",
|
|
new_signups: "signups",
|
|
}.freeze
|
|
|
|
def self.build(start_date:, end_date:, current_user: nil)
|
|
new(start_date: start_date, end_date: end_date, current_user: current_user).build
|
|
end
|
|
|
|
def initialize(start_date:, end_date:, current_user: nil)
|
|
@start_date = parse_date(start_date) || DEFAULT_RANGE_DAYS.days.ago.beginning_of_day
|
|
@end_date = parse_date(end_date)&.end_of_day || Time.zone.now.end_of_day
|
|
@current_user = current_user
|
|
end
|
|
|
|
def build
|
|
kpis = build_kpis
|
|
{
|
|
kpis: kpis,
|
|
headline: build_headline(kpis),
|
|
trust_level_pipeline: build_trust_level_pipeline,
|
|
posters: build_posters,
|
|
activity_by_category: build_activity_by_category,
|
|
}
|
|
end
|
|
|
|
private
|
|
|
|
attr_reader :start_date, :end_date, :current_user
|
|
|
|
def parse_date(value)
|
|
return nil if value.blank?
|
|
Time.zone.parse(value.to_s)&.beginning_of_day
|
|
rescue ArgumentError, TypeError
|
|
nil
|
|
end
|
|
|
|
def build_kpis
|
|
KPI_REPORTS.filter_map { |type, report| build_kpi(type, report) }
|
|
end
|
|
|
|
HEADLINE_KEYS = {
|
|
healthy_growth: "admin.dashboard.sections.engagement.headline.healthy_growth",
|
|
declining: "admin.dashboard.sections.engagement.headline.declining",
|
|
engaged_but_shrinking: "admin.dashboard.sections.engagement.headline.engaged_but_shrinking",
|
|
growing_but_distracted: "admin.dashboard.sections.engagement.headline.growing_but_distracted",
|
|
mixed: "admin.dashboard.sections.engagement.headline.mixed",
|
|
no_signal: "admin.dashboard.sections.engagement.headline.no_signal",
|
|
}.freeze
|
|
|
|
def build_headline(kpis)
|
|
stickiness = sign_of(kpi_change(kpis, :dau_mau))
|
|
signups = sign_of(kpi_change(kpis, :new_signups))
|
|
engaged = sign_of(kpi_change(kpis, :daily_engaged_users))
|
|
|
|
key =
|
|
if [stickiness, signups, engaged].all?(&:zero?)
|
|
:no_signal
|
|
elsif stickiness >= 0 && signups >= 0 && engaged >= 0
|
|
:healthy_growth
|
|
elsif stickiness <= 0 && signups <= 0 && engaged <= 0
|
|
:declining
|
|
elsif stickiness >= 0 && (signups < 0 || engaged < 0)
|
|
:engaged_but_shrinking
|
|
elsif stickiness < 0 && signups > 0
|
|
:growing_but_distracted
|
|
else
|
|
:mixed
|
|
end
|
|
|
|
{ key: HEADLINE_KEYS[key] }
|
|
end
|
|
|
|
def kpi_change(kpis, type)
|
|
kpis.find { |k| k[:type] == type }&.dig(:percent_change)
|
|
end
|
|
|
|
def sign_of(value)
|
|
return 0 if value.nil? || value.zero?
|
|
value.positive? ? 1 : -1
|
|
end
|
|
|
|
def build_trust_level_pipeline
|
|
args = { start_date: start_date, end_date: end_date }
|
|
|
|
report = Report.find_cached("trust_level_pipeline", args)
|
|
if report.nil?
|
|
report = Report.find("trust_level_pipeline", args)
|
|
Report.cache(report) if report && report.error.blank?
|
|
end
|
|
|
|
return nil if report.nil? || report_error?(report)
|
|
|
|
{
|
|
rows: report_data(report),
|
|
trend: report_prev_period(report),
|
|
total_members: report.is_a?(Hash) ? report[:total] : report.total,
|
|
}
|
|
end
|
|
|
|
def build_posters
|
|
args = { start_date: start_date, end_date: end_date, current_user: current_user }
|
|
|
|
report = Report.find_cached("posters_by_member_type", args)
|
|
if report.nil?
|
|
report = Report.find("posters_by_member_type", args)
|
|
Report.cache(report) if report && report.error.blank?
|
|
end
|
|
|
|
return nil if report.nil? || report_error?(report)
|
|
|
|
{ rows: report_data(report), total: report.is_a?(Hash) ? report[:total] : report.total }
|
|
end
|
|
|
|
def build_activity_by_category
|
|
args = { start_date: start_date, end_date: end_date, current_user: current_user }
|
|
|
|
report = Report.find_cached("activity_by_category", args)
|
|
if report.nil?
|
|
report = Report.find("activity_by_category", args)
|
|
Report.cache(report) if report && report.error.blank?
|
|
end
|
|
|
|
return nil if report.nil? || report_error?(report)
|
|
|
|
{ rows: report_data(report), total: report.is_a?(Hash) ? report[:total] : report.total }
|
|
end
|
|
end
|