2019-02-15 15:39:39 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2018-03-27 19:54:05 +05:30
|
|
|
module Gitlab
|
|
|
|
module CycleAnalytics
|
|
|
|
class UsageData
|
2020-04-08 14:13:33 +05:30
|
|
|
include Gitlab::Utils::StrongMemoize
|
2018-03-27 19:54:05 +05:30
|
|
|
PROJECTS_LIMIT = 10
|
|
|
|
|
2020-04-08 14:13:33 +05:30
|
|
|
attr_reader :options
|
2018-03-27 19:54:05 +05:30
|
|
|
|
|
|
|
def initialize
|
|
|
|
@options = { from: 7.days.ago }
|
|
|
|
end
|
|
|
|
|
2020-04-08 14:13:33 +05:30
|
|
|
def projects
|
|
|
|
strong_memoize(:projects) do
|
|
|
|
projects = Project.where.not(last_activity_at: nil).order(last_activity_at: :desc).limit(10) +
|
|
|
|
Project.where.not(last_repository_updated_at: nil).order(last_repository_updated_at: :desc).limit(10)
|
|
|
|
|
|
|
|
projects = projects.uniq.sort_by do |project|
|
|
|
|
[project.last_activity_at, project.last_repository_updated_at].min
|
|
|
|
end
|
|
|
|
|
|
|
|
if projects.size < 10
|
|
|
|
projects.concat(Project.where(last_activity_at: nil, last_repository_updated_at: nil).limit(10))
|
|
|
|
end
|
|
|
|
|
|
|
|
projects.uniq.first(10)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-03-09 13:42:32 +05:30
|
|
|
def to_json(*)
|
2018-03-27 19:54:05 +05:30
|
|
|
total = 0
|
|
|
|
|
|
|
|
values =
|
|
|
|
medians_per_stage.each_with_object({}) do |(stage_name, medians), hsh|
|
|
|
|
calculations = stage_values(medians)
|
|
|
|
|
|
|
|
total += calculations.values.compact.sum
|
|
|
|
hsh[stage_name] = calculations
|
|
|
|
end
|
|
|
|
|
|
|
|
values[:total] = total
|
|
|
|
|
|
|
|
{ avg_cycle_analytics: values }
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def medians_per_stage
|
|
|
|
projects.each_with_object({}) do |project, hsh|
|
2019-09-30 21:07:59 +05:30
|
|
|
::CycleAnalytics::ProjectLevel.new(project, options: options).all_medians_by_stage.each do |stage_name, median|
|
2018-03-27 19:54:05 +05:30
|
|
|
hsh[stage_name] ||= []
|
|
|
|
hsh[stage_name] << median
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def stage_values(medians)
|
|
|
|
medians = medians.map(&:presence).compact
|
|
|
|
average = calc_average(medians)
|
|
|
|
|
|
|
|
{
|
|
|
|
average: average,
|
|
|
|
sd: standard_deviation(medians, average),
|
|
|
|
missing: projects.length - medians.length
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
def calc_average(values)
|
|
|
|
return if values.empty?
|
|
|
|
|
|
|
|
(values.sum / values.length).to_i
|
|
|
|
end
|
|
|
|
|
|
|
|
def standard_deviation(values, average)
|
|
|
|
Math.sqrt(sample_variance(values, average)).to_i
|
|
|
|
end
|
|
|
|
|
|
|
|
def sample_variance(values, average)
|
|
|
|
return 0 if values.length <= 1
|
|
|
|
|
|
|
|
sum = values.inject(0) do |acc, val|
|
|
|
|
acc + (val - average)**2
|
|
|
|
end
|
|
|
|
|
|
|
|
sum / (values.length - 1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|