2018-11-06 04:45:35 +00:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2016-11-18 15:38:02 +00:00
|
|
|
module Gitlab
|
|
|
|
module CycleAnalytics
|
|
|
|
class BaseStage
|
2016-12-09 11:41:15 +00:00
|
|
|
include BaseQuery
|
2019-07-18 14:07:36 +00:00
|
|
|
include GroupProjectsProvider
|
2016-11-21 16:15:25 +00:00
|
|
|
|
2019-07-04 14:42:31 +00:00
|
|
|
attr_reader :options
|
2019-07-10 11:22:29 +00:00
|
|
|
|
2019-07-04 14:42:31 +00:00
|
|
|
def initialize(options:)
|
2016-11-18 15:38:02 +00:00
|
|
|
@options = options
|
2016-12-02 16:09:29 +00:00
|
|
|
end
|
|
|
|
|
2016-11-18 15:38:02 +00:00
|
|
|
def events
|
2017-01-12 11:48:01 +00:00
|
|
|
event_fetcher.fetch
|
2016-11-18 15:38:02 +00:00
|
|
|
end
|
|
|
|
|
2019-07-04 11:40:32 +00:00
|
|
|
def as_json(serializer: AnalyticsStageSerializer)
|
|
|
|
serializer.new.represent(self)
|
2016-11-22 09:33:19 +00:00
|
|
|
end
|
|
|
|
|
2016-12-01 10:21:24 +00:00
|
|
|
def title
|
2017-05-04 02:05:38 +00:00
|
|
|
raise NotImplementedError.new("Expected #{self.name} to implement title")
|
2016-12-01 10:21:24 +00:00
|
|
|
end
|
|
|
|
|
2019-07-04 14:42:31 +00:00
|
|
|
def project_median
|
2019-07-10 11:22:29 +00:00
|
|
|
return if project.nil?
|
2019-07-08 15:04:08 +00:00
|
|
|
|
2019-07-10 11:22:29 +00:00
|
|
|
BatchLoader.for(project.id).batch(key: name) do |project_ids, loader|
|
2018-02-21 12:13:56 +00:00
|
|
|
if project_ids.one?
|
2019-07-10 11:22:29 +00:00
|
|
|
loader.call(project.id, median_query(project_ids))
|
2018-02-15 13:23:39 +00:00
|
|
|
else
|
2018-02-21 12:13:56 +00:00
|
|
|
begin
|
2019-07-04 11:40:32 +00:00
|
|
|
median_datetimes(cte_table, interval_query(project_ids), name, :project_id)&.each do |project_id, median|
|
2018-02-21 12:13:56 +00:00
|
|
|
loader.call(project_id, median)
|
|
|
|
end
|
|
|
|
rescue NotSupportedError
|
|
|
|
{}
|
2018-02-15 13:23:39 +00:00
|
|
|
end
|
2018-02-12 12:24:42 +00:00
|
|
|
end
|
|
|
|
end
|
2016-12-09 11:41:15 +00:00
|
|
|
end
|
|
|
|
|
2019-07-04 14:42:31 +00:00
|
|
|
def group_median
|
|
|
|
median_query(projects.map(&:id))
|
|
|
|
end
|
|
|
|
|
2019-07-04 11:40:32 +00:00
|
|
|
def median_query(project_ids)
|
|
|
|
# Build a `SELECT` query. We find the first of the `end_time_attrs` that isn't `NULL` (call this end_time).
|
|
|
|
# Next, we find the first of the start_time_attrs that isn't `NULL` (call this start_time).
|
|
|
|
# We compute the (end_time - start_time) interval, and give it an alias based on the current
|
2020-02-07 21:08:39 +00:00
|
|
|
# value stream analytics stage.
|
2019-07-04 11:40:32 +00:00
|
|
|
|
|
|
|
median_datetime(cte_table, interval_query(project_ids), name)
|
|
|
|
end
|
|
|
|
|
2016-12-09 11:41:15 +00:00
|
|
|
def name
|
|
|
|
raise NotImplementedError.new("Expected #{self.name} to implement name")
|
2016-12-01 10:21:24 +00:00
|
|
|
end
|
|
|
|
|
2019-07-04 11:40:32 +00:00
|
|
|
def cte_table
|
|
|
|
Arel::Table.new("cte_table_for_#{name}")
|
|
|
|
end
|
|
|
|
|
|
|
|
def interval_query(project_ids)
|
|
|
|
Arel::Nodes::As.new(cte_table,
|
|
|
|
subtract_datetimes(stage_query(project_ids), start_time_attrs, end_time_attrs, name.to_s))
|
|
|
|
end
|
|
|
|
|
2016-11-18 15:38:02 +00:00
|
|
|
private
|
|
|
|
|
2017-01-12 11:48:01 +00:00
|
|
|
def event_fetcher
|
2019-07-04 14:42:31 +00:00
|
|
|
@event_fetcher ||= Gitlab::CycleAnalytics::EventFetcher[name].new(stage: name,
|
2017-01-12 13:32:30 +00:00
|
|
|
options: event_options)
|
2017-01-12 11:48:01 +00:00
|
|
|
end
|
|
|
|
|
2016-12-09 11:41:15 +00:00
|
|
|
def event_options
|
2019-07-10 11:22:29 +00:00
|
|
|
options.merge(start_time_attrs: start_time_attrs, end_time_attrs: end_time_attrs)
|
2016-11-18 15:38:02 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|