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
|
|
|
|
PROJECTS_LIMIT = 10
|
|
|
|
|
|
|
|
attr_reader :projects, :options
|
|
|
|
|
|
|
|
def initialize
|
|
|
|
@projects = Project.sorted_by_activity.limit(PROJECTS_LIMIT)
|
|
|
|
@options = { from: 7.days.ago }
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_json
|
|
|
|
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|
|
|
|
|
::CycleAnalytics.new(project, options).all_medians_per_stage.each do |stage_name, median|
|
|
|
|
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
|