gitlab-org--gitlab-foss/app/services/cohorts_service.rb

88 lines
2.5 KiB
Ruby
Raw Normal View History

2017-04-05 10:24:15 +00:00
class CohortsService
MONTHS_INCLUDED = 12
# Get a hash that looks like:
#
# {
# month => {
# months: [3, 2, 1],
# total: 3
# inactive: 0
# },
# etc.
#
# The `months` array is always from oldest to newest, so it's always
# non-strictly decreasing from left to right.
#
def execute
cohorts = {}
months = Array.new(MONTHS_INCLUDED) { |i| i.months.ago.beginning_of_month.to_date }
MONTHS_INCLUDED.times do
created_at_month = months.last
activity_months = running_totals(months, created_at_month)
# Even if no users registered in this month, we always want to have a
# value to fill in the table.
inactive = counts_by_month[[created_at_month, nil]].to_i
cohorts[created_at_month] = {
months: activity_months,
total: activity_months.first,
inactive: inactive
}
months.pop
end
cohorts
end
private
# Calculate a running sum of active users, so users active in later months
# count as active in this month, too. Start with the most recent month first,
# for calculating the running totals, and then reverse for displaying in the
# table.
def running_totals(all_months, created_at_month)
all_months
.map { |activity_month| counts_by_month[[created_at_month, activity_month]] }
.reduce([]) { |result, total| result << result.last.to_i + total.to_i }
.reverse
end
# Get a hash that looks like:
#
# {
# [created_at_month, current_sign_in_at_month] => count,
# [created_at_month, current_sign_in_at_month_2] => count_2,
# # etc.
# }
#
# created_at_month can never be nil, but current_sign_in_at_month can (when a
# user has never logged in, just been created). This covers the last twelve
# months.
#
def counts_by_month
@counts_by_month ||=
begin
created_at_month = column_to_date('created_at')
2017-04-05 09:49:33 +00:00
current_sign_in_at_month = column_to_date('current_sign_in_at')
User
.where('created_at > ?', MONTHS_INCLUDED.months.ago.end_of_month)
.group(created_at_month, current_sign_in_at_month)
.reorder("#{created_at_month} ASC", "#{current_sign_in_at_month} ASC")
.count
end
end
def column_to_date(column)
if Gitlab::Database.postgresql?
"CAST(DATE_TRUNC('month', #{column}) AS date)"
elsif Gitlab::Database.mysql?
"STR_TO_DATE(DATE_FORMAT(#{column}, '%Y-%m-01'), '%Y-%m-%d')"
end
end
end