Commit b115baff authored by Adam Hegyi's avatar Adam Hegyi

Move lead and cycle time classes out from Group NS

parent 77d8e925
...@@ -47,3 +47,4 @@ module Analytics ...@@ -47,3 +47,4 @@ module Analytics
end end
end end
end end
Analytics::CycleAnalytics::ProjectLevel.prepend_mod_with('Analytics::CycleAnalytics::ProjectLevel')
# frozen_string_literal: true # frozen_string_literal: true
module EE::Projects::Analytics::CycleAnalytics::SummaryController module EE::Projects::Analytics::CycleAnalytics::SummaryController
extend ActiveSupport::Concern
extend ::Gitlab::Utils::Override 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 private
override :allowed_params override :allowed_params
def 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 request_params.to_data_collector_params
end end
......
...@@ -18,11 +18,14 @@ module Analytics ...@@ -18,11 +18,14 @@ module Analytics
end end
def time_summary def time_summary
@time_summary ||= @time_summary ||= begin
Gitlab::Analytics::CycleAnalytics::Summary::Group::StageTimeSummary stage = ::Analytics::CycleAnalytics::GroupStage.new(group: group)
.new(group, options: options)
Gitlab::Analytics::CycleAnalytics::Summary::StageTimeSummary
.new(stage, options: options)
.data .data
end end
end end
end 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 ...@@ -95,6 +95,10 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resources :code_reviews, only: [:index] resources :code_reviews, only: [:index]
resource :issues_analytics, only: [:show] resource :issues_analytics, only: [:show]
resource :merge_request_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 end
resources :approvers, only: :destroy 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,7 +4,6 @@ module Gitlab ...@@ -4,7 +4,6 @@ module Gitlab
module Analytics module Analytics
module CycleAnalytics module CycleAnalytics
module Summary module Summary
module Group
class CycleTime < BaseTime class CycleTime < BaseTime
def title def title
_('Cycle Time') _('Cycle Time')
...@@ -21,5 +20,4 @@ module Gitlab ...@@ -21,5 +20,4 @@ module Gitlab
end end
end end
end end
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,12 +4,11 @@ module Gitlab ...@@ -4,12 +4,11 @@ module Gitlab
module Analytics module Analytics
module CycleAnalytics module CycleAnalytics
module Summary module Summary
module Group
class StageTimeSummary class StageTimeSummary
attr_reader :group, :current_user, :options attr_reader :stage, :current_user, :options
def initialize(group, options:) def initialize(stage, options:)
@group = group @stage = stage
@current_user = options[:current_user] @current_user = options[:current_user]
@options = options @options = options
end end
...@@ -22,8 +21,8 @@ module Gitlab ...@@ -22,8 +21,8 @@ module Gitlab
def lead_time def lead_time
serialize( serialize(
Summary::Group::LeadTime.new( Summary::LeadTime.new(
group: group, current_user: current_user, options: options stage: stage, current_user: current_user, options: options
), ),
with_unit: true with_unit: true
) )
...@@ -31,8 +30,8 @@ module Gitlab ...@@ -31,8 +30,8 @@ module Gitlab
def cycle_time def cycle_time
serialize( serialize(
Summary::Group::CycleTime.new( Summary::CycleTime.new(
group: group, current_user: current_user, options: options stage: stage, current_user: current_user, options: options
), ),
with_unit: true with_unit: true
) )
...@@ -46,5 +45,4 @@ module Gitlab ...@@ -46,5 +45,4 @@ module Gitlab
end end
end end
end end
end
end end
...@@ -4,7 +4,6 @@ module Gitlab ...@@ -4,7 +4,6 @@ module Gitlab
module Analytics module Analytics
module CycleAnalytics module CycleAnalytics
module Summary module Summary
module Group
class LeadTime < BaseTime class LeadTime < BaseTime
def title def title
_('Lead Time') _('Lead Time')
...@@ -21,5 +20,4 @@ module Gitlab ...@@ -21,5 +20,4 @@ module Gitlab
end end
end end
end end
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 ...@@ -20,15 +20,16 @@ RSpec.describe Projects::Analytics::CycleAnalytics::SummaryController do
subject { get :show, params: params } subject { get :show, params: params }
before do before do
project.add_reporter(user)
params[:author_username] = issue_with_author.author.username params[:author_username] = issue_with_author.author.username
project.add_reporter(user)
end end
context 'when cycle_analytics_for_projects feature is available' do context 'when cycle_analytics_for_projects feature is available' do
before do before do
stub_licensed_features(cycle_analytics_for_projects: true) stub_licensed_features(cycle_analytics_for_projects: true)
end end
it 'filters by author username' do it 'filters by author username' do
subject subject
...@@ -50,4 +51,68 @@ RSpec.describe Projects::Analytics::CycleAnalytics::SummaryController do ...@@ -50,4 +51,68 @@ RSpec.describe Projects::Analytics::CycleAnalytics::SummaryController do
end end
end 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 end
# frozen_string_literal: true # frozen_string_literal: true
require 'spec_helper' 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(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, namespace: group) } let_it_be(:project) { create(:project, :repository, namespace: group) }
let_it_be(:project_2) { create(:project, :repository, namespace: group) } let_it_be(:project_2) { create(:project, :repository, namespace: group) }
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let(:from) { 1.day.ago } let(:from) { 1.day.ago }
let(:to) { nil } let(:to) { nil }
let(:options) { { from: from, to: to, current_user: user } } 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| around do |example|
freeze_time { example.run } freeze_time { example.run }
...@@ -101,7 +103,7 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::Summary::Group::StageTimeSumma ...@@ -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)) create(:closed_issue, created_at: 3.days.ago, closed_at: Time.zone.now, project: create(:project, namespace: group))
end 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 it 'finds the lead time of issues from those projects' do
# Median of 1, 2, 4, not including new issue # Median of 1, 2, 4, not including new issue
...@@ -178,7 +180,7 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::Summary::Group::StageTimeSumma ...@@ -178,7 +180,7 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::Summary::Group::StageTimeSumma
issue_4.metrics.update!(first_mentioned_in_commit_at: 3.days.ago) issue_4.metrics.update!(first_mentioned_in_commit_at: 3.days.ago)
end 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 it 'finds the cycle time of issues from those projects' do
# Median of 1, 2, 4, not including new issue # 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