Skip to content
Snippets Groups Projects
Commit b214be49 authored by James Lopez's avatar James Lopez
Browse files

big refactor based on MR feedback

parent daa4f3de
No related branches found
No related tags found
No related merge requests found
Showing
with 107 additions and 112 deletions
Loading
Loading
@@ -32,7 +32,7 @@ class CycleAnalytics
 
def stats_per_stage
STAGES.map do |stage_name|
Gitlab::CycleAnalytics::Stage[stage_name].new(project: @project, options: @options).median_data
self[stage_name].new(project: @project, options: @options).median_data
end
end
end
module Gitlab
module CycleAnalytics
class BaseEvent
class BaseEventFetcher
include MetricsTables
include ClassNameUtil
 
attr_reader :start_time_attrs, :end_time_attrs, :projections, :query
attr_reader :projections, :query, :stage
 
def initialize(fetcher:, options:)
def initialize(fetcher:, options:, stage:)
@fetcher = fetcher
@project = fetcher.project
@options = options
@stage = stage
end
 
def fetch
Loading
Loading
@@ -26,10 +26,6 @@ module Gitlab
@order || @start_time_attrs
end
 
def stage
class_name_for('Event')
end
private
 
def update_author!
Loading
Loading
module Gitlab
module CycleAnalytics
class BaseStage
include ClassNameUtil
attr_accessor :start_time_attrs, :end_time_attrs
 
def initialize(project:, options:)
@project = project
@options = options
@fetcher = Gitlab::CycleAnalytics::MetricsFetcher.new(project: project,
from: options[:from],
branch: options[:branch])
branch: options[:branch],
stage: self)
end
def event
@event ||= Gitlab::CycleAnalytics::Event[stage].new(fetcher: @fetcher, options: @options)
end
 
def events
Gitlab::CycleAnalytics::Event[stage].new(fetcher: @fetcher, options: @options).fetch
event.fetch
end
 
def median_data
Loading
Loading
@@ -24,7 +29,7 @@ module Gitlab
end
 
def median
raise NotImplementedError.new("Expected #{self.name} to implement median")
@fetcher.median
end
 
private
Loading
Loading
module Gitlab
module CycleAnalytics
module ClassNameUtil
def class_name_for(type)
class_name.split(type).first.to_sym
end
def class_name
self.class.name.demodulize
end
end
end
end
module Gitlab
module CycleAnalytics
class ReviewEvent < BaseEvent
class CodeEventFetcher < BaseEventFetcher
include MergeRequestAllowed
 
def initialize(*args)
@start_time_attrs = mr_table[:created_at]
@end_time_attrs = mr_metrics_table[:merged_at]
@projections = [mr_table[:title],
mr_table[:iid],
mr_table[:id],
mr_table[:created_at],
mr_table[:state],
mr_table[:author_id]]
@order = mr_table[:created_at]
 
super(*args)
end
 
private
def serialize(event)
AnalyticsMergeRequestSerializer.new(project: @project).represent(event).as_json
end
Loading
Loading
module Gitlab
module CycleAnalytics
class CodeStage < BaseStage
def description
"Time until first merge request"
def initialize(*args)
@start_time_attrs = issue_metrics_table[:first_mentioned_in_commit_at]
@end_time_attrs = mr_table[:created_at]
super(*args)
end
 
def median
@fetcher.median(:code,
Issue::Metrics.arel_table[:first_mentioned_in_commit_at],
MergeRequest.arel_table[:created_at])
def stage
:code
end
def description
"Time until first merge request"
end
end
end
Loading
Loading
Loading
Loading
@@ -2,7 +2,7 @@ module Gitlab
module CycleAnalytics
module Event
def self.[](stage_name)
const_get("::Gitlab::CycleAnalytics::#{stage_name.to_s.camelize}Event")
CycleAnalytics.const_get("#{stage_name.to_s.camelize}Event")
end
end
end
Loading
Loading
module Gitlab
module CycleAnalytics
class ProductionEvent < BaseEvent
class IssueEventFetcher < BaseEventFetcher
include IssueAllowed
 
def initialize(*args)
@start_time_attrs = issue_table[:created_at]
@end_time_attrs = mr_metrics_table[:first_deployed_to_production_at]
@projections = [issue_table[:title],
issue_table[:iid],
issue_table[:id],
Loading
Loading
module Gitlab
module CycleAnalytics
class IssueStage < BaseStage
def description
"Time before an issue gets scheduled"
def initialize(*args)
@start_time_attrs = issue_table[:created_at]
@end_time_attrs = [issue_metrics_table[:first_associated_with_milestone_at],
issue_metrics_table[:first_added_to_board_at]]
super(*args)
end
 
def median
@fetcher.median(:issue,
Issue.arel_table[:created_at],
[Issue::Metrics.arel_table[:first_associated_with_milestone_at],
Issue::Metrics.arel_table[:first_added_to_board_at]])
def stage
:issue
end
def description
"Time before an issue gets scheduled"
end
end
end
Loading
Loading
Loading
Loading
@@ -9,14 +9,15 @@ module Gitlab
 
DEPLOYMENT_METRIC_STAGES = %i[production staging]
 
def initialize(project:, from:, branch:)
def initialize(project:, from:, branch:, stage:)
@project = project
@from = from
@branch = branch
@stage = stage
end
 
def median(name, start_time_attrs, end_time_attrs)
cte_table = Arel::Table.new("cte_table_for_#{name}")
def median
cte_table = Arel::Table.new("cte_table_for_#{@stage.stage}")
 
# 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).
Loading
Loading
@@ -24,24 +25,26 @@ module Gitlab
# cycle analytics stage.
interval_query = Arel::Nodes::As.new(
cte_table,
subtract_datetimes(base_query_for(name), start_time_attrs, end_time_attrs, name.to_s))
subtract_datetimes(base_query_for(name), @stage.start_time_attrs, @stage.end_time_attrs, @stage.stage.to_s))
 
median_datetime(cte_table, interval_query, name)
end
 
def events(stage_class)
ActiveRecord::Base.connection.exec_query(events_query(stage_class).to_sql)
def events
ActiveRecord::Base.connection.exec_query(events_query.to_sql)
end
 
private
 
def events_query(stage_class)
base_query = base_query_for(stage_class.stage)
diff_fn = subtract_datetimes_diff(base_query, stage_class.start_time_attrs, stage_class.end_time_attrs)
def events_query
base_query = base_query_for(@stage.stage)
event = @stage.event
 
stage_class.custom_query(base_query)
diff_fn = subtract_datetimes_diff(base_query, @stage.start_time_attrs, @stage.end_time_attrs)
 
base_query.project(extract_diff_epoch(diff_fn).as('total_time'), *stage_class.projections).order(stage_class.order.desc)
event_instance.custom_query(base_query)
base_query.project(extract_diff_epoch(diff_fn).as('total_time'), *event.projections).order(event.order.desc)
end
 
# Join table with a row for every <issue,merge_request> pair (where the merge request
Loading
Loading
module Gitlab
module CycleAnalytics
class PlanEvent < BaseEvent
class PlanEventFetcher < BaseEventFetcher
def initialize(*args)
@start_time_attrs = issue_metrics_table[:first_associated_with_milestone_at]
@end_time_attrs = [issue_metrics_table[:first_added_to_board_at],
issue_metrics_table[:first_mentioned_in_commit_at]]
@projections = [mr_diff_table[:st_commits].as('commits'),
issue_metrics_table[:first_mentioned_in_commit_at]]
 
Loading
Loading
module Gitlab
module CycleAnalytics
class PlanStage < BaseStage
def description
"Time before an issue starts implementation"
def initialize(*args)
@start_time_attrs = [issue_metrics_table[:first_associated_with_milestone_at],
issue_metrics_table[:first_added_to_board_at]]
@end_time_attrs = issue_metrics_table[:first_mentioned_in_commit_at]
super(*args)
end
 
def median
@fetcher.median(:plan,
[Issue::Metrics.arel_table[:first_associated_with_milestone_at],
Issue::Metrics.arel_table[:first_added_to_board_at]],
Issue::Metrics.arel_table[:first_mentioned_in_commit_at])
def stage
:code
end
def description
"Time before an issue starts implementation"
end
end
end
Loading
Loading
module Gitlab
module CycleAnalytics
class IssueEvent < BaseEvent
class ProductionEventFetcher < BaseEventFetcher
include IssueAllowed
 
def initialize(*args)
@start_time_attrs = issue_table[:created_at]
@end_time_attrs = [issue_metrics_table[:first_associated_with_milestone_at],
issue_metrics_table[:first_added_to_board_at]]
@projections = [issue_table[:title],
issue_table[:iid],
issue_table[:id],
Loading
Loading
module Gitlab
module CycleAnalytics
class ProductionStage < BaseStage
def description
"From issue creation until deploy to production"
def initialize(*args)
@start_time_attrs = issue_table[:created_at]
@end_time_attrs = mr_metrics_table[:first_deployed_to_production_at]
super(*args)
end
 
def median
@fetcher.median(:production,
Issue.arel_table[:created_at],
MergeRequest::Metrics.arel_table[:first_deployed_to_production_at])
def stage
:production
end
def description
"From issue creation until deploy to production"
end
end
end
Loading
Loading
module Gitlab
module CycleAnalytics
class CodeEvent < BaseEvent
class ReviewEventFetcher < BaseEventFetcher
include MergeRequestAllowed
 
def initialize(*args)
@start_time_attrs = issue_metrics_table[:first_mentioned_in_commit_at]
@end_time_attrs = mr_table[:created_at]
@projections = [mr_table[:title],
mr_table[:iid],
mr_table[:id],
mr_table[:created_at],
mr_table[:state],
mr_table[:author_id]]
@order = mr_table[:created_at]
 
super(*args)
end
 
private
def serialize(event)
AnalyticsMergeRequestSerializer.new(project: @project).represent(event).as_json
end
Loading
Loading
module Gitlab
module CycleAnalytics
class ReviewStage < BaseStage
def description
"Time between merge request creation and merge/close"
def initialize(*args)
@start_time_attrs = mr_table[:created_at]
@end_time_attrs = mr_metrics_table[:merged_at]
super(*args)
end
 
def median
@fetcher.median(:review,
MergeRequest.arel_table[:created_at],
MergeRequest::Metrics.arel_table[:merged_at])
def stage
:review
end
def description
"Time between merge request creation and merge/close"
end
end
end
Loading
Loading
Loading
Loading
@@ -2,7 +2,7 @@ module Gitlab
module CycleAnalytics
module Stage
def self.[](stage_name)
const_get("::Gitlab::CycleAnalytics::#{stage_name.to_s.camelize}Stage")
CycleAnalytics.const_get("#{stage_name.to_s.camelize}Stage")
end
end
end
Loading
Loading
module Gitlab
module CycleAnalytics
class StagingEvent < BaseEvent
class StagingEventFetcher < BaseEventFetcher
def initialize(*args)
@start_time_attrs = mr_metrics_table[:merged_at]
@end_time_attrs = mr_metrics_table[:first_deployed_to_production_at]
@projections = [build_table[:id]]
@order = build_table[:created_at]
 
Loading
Loading
module Gitlab
module CycleAnalytics
class StagingStage < BaseStage
def description
"From merge request merge until deploy to production"
def initialize(*args)
@start_time_attrs = mr_metrics_table[:merged_at]
@end_time_attrs = mr_metrics_table[:first_deployed_to_production_at]
super(*args)
end
 
def median
@fetcher.median(:staging,
MergeRequest::Metrics.arel_table[:merged_at],
MergeRequest::Metrics.arel_table[:first_deployed_to_production_at])
def stage
:staging
end
def description
"From merge request merge until deploy to production"
end
end
end
Loading
Loading
module Gitlab
module CycleAnalytics
class TestEvent < StagingEvent
def initialize(*args)
super(*args)
@start_time_attrs = mr_metrics_table[:latest_build_started_at]
@end_time_attrs = mr_metrics_table[:latest_build_finished_at]
end
end
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment