Commit 7ac4827e authored by Robert Speicher's avatar Robert Speicher

Merge branch '327570-project-level-lead-and-cycle-time' into 'master'

Move lead and cycle time classes out from Group NS

See merge request gitlab-org/gitlab!63977
parents 98a555f4 b115baff
......@@ -47,3 +47,4 @@ module Analytics
end
end
end
Analytics::CycleAnalytics::ProjectLevel.prepend_mod_with('Analytics::CycleAnalytics::ProjectLevel')
# frozen_string_literal: true
module EE::Projects::Analytics::CycleAnalytics::SummaryController
extend ActiveSupport::Concern
extend ::Gitlab::Utils::Override
def time_summary
if project.licensed_feature_available?(:cycle_analytics_for_projects)
render json: project_level.time_summary
else
render_404
end
end
private
override :allowed_params
def allowed_params
return super unless @project.licensed_feature_available?(:cycle_analytics_for_projects) # rubocop: disable Gitlab/ModuleWithInstanceVariables
return super unless project.licensed_feature_available?(:cycle_analytics_for_projects)
request_params.to_data_collector_params
end
......
......@@ -18,10 +18,13 @@ module Analytics
end
def time_summary
@time_summary ||=
Gitlab::Analytics::CycleAnalytics::Summary::Group::StageTimeSummary
.new(group, options: options)
.data
@time_summary ||= begin
stage = ::Analytics::CycleAnalytics::GroupStage.new(group: group)
Gitlab::Analytics::CycleAnalytics::Summary::StageTimeSummary
.new(stage, options: options)
.data
end
end
end
end
......
# frozen_string_literal: true
module EE
module Analytics
module CycleAnalytics
module ProjectLevel
def time_summary
@time_summary ||= begin
stage = ::Analytics::CycleAnalytics::ProjectStage.new(project: project)
::Gitlab::Analytics::CycleAnalytics::Summary::StageTimeSummary
.new(stage, options: options)
.data
end
end
end
end
end
end
......@@ -95,6 +95,10 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resources :code_reviews, only: [:index]
resource :issues_analytics, only: [:show]
resource :merge_request_analytics, only: :show
scope module: :cycle_analytics, as: 'cycle_analytics', path: 'value_stream_analytics' do
get '/time_summary' => 'summary#time_summary'
end
end
resources :approvers, only: :destroy
......
# frozen_string_literal: true
module Gitlab
module Analytics
module CycleAnalytics
module Summary
class BaseTime
def initialize(stage:, current_user:, options:)
@stage = stage
@current_user = current_user
@options = options
assign_event_identifiers
end
def value
@value ||= Gitlab::CycleAnalytics::Summary::Value::PrettyNumeric.new(data_collector.median.days&.round(1))
end
def unit
n_('day', 'days', value)
end
def start_event_identifier
raise NotImplementedError, "Expected #{self.name} to implement start_event_identifier"
end
def end_event_identifier
raise NotImplementedError, "Expected #{self.name} to implement end_event_identifier"
end
private
def assign_event_identifiers
@stage.start_event_identifier = start_event_identifier
@stage.end_event_identifier = end_event_identifier
end
def data_collector
Gitlab::Analytics::CycleAnalytics::DataCollector.new(
stage: @stage,
params: {
from: @options[:from],
to: @options[:to] || DateTime.now,
project_ids: @options[:projects],
end_event_filter: @options[:end_event_filter],
current_user: @current_user
}.merge(@options.slice(*::Gitlab::Analytics::CycleAnalytics::RequestParams::FINDER_PARAM_NAMES))
)
end
end
end
end
end
end
......@@ -4,19 +4,17 @@ module Gitlab
module Analytics
module CycleAnalytics
module Summary
module Group
class CycleTime < BaseTime
def title
_('Cycle Time')
end
class CycleTime < BaseTime
def title
_('Cycle Time')
end
def start_event_identifier
:issue_first_mentioned_in_commit
end
def start_event_identifier
:issue_first_mentioned_in_commit
end
def end_event_identifier
:issue_closed
end
def end_event_identifier
:issue_closed
end
end
end
......
# frozen_string_literal: true
module Gitlab
module Analytics
module CycleAnalytics
module Summary
module Group
class BaseTime < Group::Base
def initialize(group:, current_user:, options:)
@group = group
@current_user = current_user
@options = options
end
def value
@value ||= Gitlab::CycleAnalytics::Summary::Value::PrettyNumeric.new(data_collector.median.days&.round(1))
end
def unit
n_('day', 'days', value)
end
def start_event_identifier
raise NotImplementedError, "Expected #{self.name} to implement start_event_identifier"
end
def end_event_identifier
raise NotImplementedError, "Expected #{self.name} to implement end_event_identifier"
end
private
def stage
::Analytics::CycleAnalytics::GroupStage.new(
group: @group,
start_event_identifier: start_event_identifier,
end_event_identifier: end_event_identifier)
end
def data_collector
Gitlab::Analytics::CycleAnalytics::DataCollector.new(
stage: stage,
params: {
from: @options[:from],
to: @options[:to] || DateTime.now,
project_ids: @options[:projects],
end_event_filter: @options[:end_event_filter],
current_user: @current_user
}.merge(@options.slice(*::Gitlab::Analytics::CycleAnalytics::RequestParams::FINDER_PARAM_NAMES))
)
end
end
end
end
end
end
end
......@@ -4,44 +4,42 @@ module Gitlab
module Analytics
module CycleAnalytics
module Summary
module Group
class StageTimeSummary
attr_reader :group, :current_user, :options
class StageTimeSummary
attr_reader :stage, :current_user, :options
def initialize(group, options:)
@group = group
@current_user = options[:current_user]
@options = options
end
def initialize(stage, options:)
@stage = stage
@current_user = options[:current_user]
@options = options
end
def data
[lead_time, cycle_time]
end
def data
[lead_time, cycle_time]
end
private
private
def lead_time
serialize(
Summary::Group::LeadTime.new(
group: group, current_user: current_user, options: options
),
with_unit: true
)
end
def lead_time
serialize(
Summary::LeadTime.new(
stage: stage, current_user: current_user, options: options
),
with_unit: true
)
end
def cycle_time
serialize(
Summary::Group::CycleTime.new(
group: group, current_user: current_user, options: options
),
with_unit: true
)
end
def cycle_time
serialize(
Summary::CycleTime.new(
stage: stage, current_user: current_user, options: options
),
with_unit: true
)
end
def serialize(summary_object, with_unit: false)
AnalyticsSummarySerializer.new.represent(
summary_object, with_unit: with_unit)
end
def serialize(summary_object, with_unit: false)
AnalyticsSummarySerializer.new.represent(
summary_object, with_unit: with_unit)
end
end
end
......
......@@ -4,19 +4,17 @@ module Gitlab
module Analytics
module CycleAnalytics
module Summary
module Group
class LeadTime < BaseTime
def title
_('Lead Time')
end
class LeadTime < BaseTime
def title
_('Lead Time')
end
def start_event_identifier
:issue_created
end
def start_event_identifier
:issue_created
end
def end_event_identifier
:issue_closed
end
def end_event_identifier
:issue_closed
end
end
end
......
# frozen_string_literal: true
module Gitlab
module Analytics
module CycleAnalytics
module Summary
class StageTimeSummary
attr_reader :stage, :current_user, :options
def initialize(stage, options:)
@stage = stage
@current_user = options[:current_user]
@options = options
end
def data
[lead_time, cycle_time]
end
private
def lead_time
serialize(
Summary::LeadTime.new(
stage: stage, current_user: current_user, options: options
),
with_unit: true
)
end
def cycle_time
serialize(
Summary::CycleTime.new(
stage: stage, current_user: current_user, options: options
),
with_unit: true
)
end
def serialize(summary_object, with_unit: false)
AnalyticsSummarySerializer.new.represent(
summary_object, with_unit: with_unit)
end
end
end
end
end
end
......@@ -20,15 +20,16 @@ RSpec.describe Projects::Analytics::CycleAnalytics::SummaryController do
subject { get :show, params: params }
before do
project.add_reporter(user)
params[:author_username] = issue_with_author.author.username
project.add_reporter(user)
end
context 'when cycle_analytics_for_projects feature is available' do
before do
stub_licensed_features(cycle_analytics_for_projects: true)
end
it 'filters by author username' do
subject
......@@ -50,4 +51,68 @@ RSpec.describe Projects::Analytics::CycleAnalytics::SummaryController do
end
end
end
describe 'GET "time_summary"' do
let_it_be(:first_mentioned_in_commit_at) { Date.new(2015, 1, 1) }
let_it_be(:closed_at) { Date.new(2015, 2, 1) }
let_it_be(:closed_issue) do
create(:issue, project: project, created_at: closed_at, closed_at: closed_at).tap do |issue|
issue.metrics.update!(first_mentioned_in_commit_at: first_mentioned_in_commit_at)
end
end
subject { get :time_summary, params: params }
context 'when cycle_analytics_for_projects feature is available' do
before do
stub_licensed_features(cycle_analytics_for_projects: true)
project.add_reporter(user)
end
it 'succeeds' do
subject
expect(response).to be_successful
end
it 'returns correct value' do
expected_cycle_time = (closed_at - first_mentioned_in_commit_at).to_i
subject
expect(json_response.last["value"].to_i).to eq(expected_cycle_time)
end
context 'when analytics_disabled features are disabled' do
it 'renders 404' do
project.add_reporter(user)
project.project_feature.update!(analytics_access_level: ProjectFeature::DISABLED)
subject
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
context 'when user is not part of the project' do
it 'renders 404' do
subject
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when the feature is not available' do
it 'renders 404' do
project.add_reporter(user)
subject
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Analytics::CycleAnalytics::Summary::Group::StageTimeSummary do
RSpec.describe Gitlab::Analytics::CycleAnalytics::Summary::StageTimeSummary do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, namespace: group) }
let_it_be(:project_2) { create(:project, :repository, namespace: group) }
let_it_be(:user) { create(:user) }
let(:from) { 1.day.ago }
let(:to) { nil }
let(:options) { { from: from, to: to, current_user: user } }
let(:stage) { Analytics::CycleAnalytics::GroupStage.new(group: group) }
subject { described_class.new(group, options: options).data }
subject { described_class.new(stage, options: options).data }
around do |example|
freeze_time { example.run }
......@@ -101,7 +103,7 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::Summary::Group::StageTimeSumma
create(:closed_issue, created_at: 3.days.ago, closed_at: Time.zone.now, project: create(:project, namespace: group))
end
subject { described_class.new(group, options: { from: from, current_user: user, projects: [project.id, project_2.id] }).data }
subject { described_class.new(stage, options: { from: from, current_user: user, projects: [project.id, project_2.id] }).data }
it 'finds the lead time of issues from those projects' do
# Median of 1, 2, 4, not including new issue
......@@ -178,7 +180,7 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::Summary::Group::StageTimeSumma
issue_4.metrics.update!(first_mentioned_in_commit_at: 3.days.ago)
end
subject { described_class.new(group, options: { from: from, current_user: user, projects: [project.id, project_2.id] }).data }
subject { described_class.new(stage, options: { from: from, current_user: user, projects: [project.id, project_2.id] }).data }
it 'finds the cycle time of issues from those projects' do
# Median of 1, 2, 4, not including new issue
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment