Fetch cycle analytics data for a specific date range.
1. Supported date ranges are 30 / 90 days ago. The default is 90 days ago. 2. All issues created before "x days ago" are filtered out, even if they have other related data (test runs, merge requests) within the filter range.
This commit is contained in:
parent
ce6bcdd004
commit
331080bca6
|
@ -1,5 +1,21 @@
|
||||||
class Projects::CycleAnalyticsController < Projects::ApplicationController
|
class Projects::CycleAnalyticsController < Projects::ApplicationController
|
||||||
def show
|
def show
|
||||||
@cycle_analytics = CycleAnalytics.new(@project)
|
@cycle_analytics = CycleAnalytics.new(@project, from: parse_start_date)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def parse_start_date
|
||||||
|
case cycle_analytics_params[:start_date]
|
||||||
|
when '30' then 30.days.ago
|
||||||
|
when '90' then 90.days.ago
|
||||||
|
else 90.days.ago
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def cycle_analytics_params
|
||||||
|
return {} unless params[:cycle_analytics].present?
|
||||||
|
|
||||||
|
{ start_date: params[:cycle_analytics][:start_date] }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,46 +1,49 @@
|
||||||
class CycleAnalytics
|
class CycleAnalytics
|
||||||
def initialize(project)
|
attr_reader :from
|
||||||
|
|
||||||
|
def initialize(project, from:)
|
||||||
@project = project
|
@project = project
|
||||||
|
@from = from
|
||||||
end
|
end
|
||||||
|
|
||||||
def issue
|
def issue
|
||||||
calculate_metric(Queries::issues(@project),
|
calculate_metric(Queries::issues(@project, created_after: @from),
|
||||||
-> (data_point) { data_point[:issue].created_at },
|
-> (data_point) { data_point[:issue].created_at },
|
||||||
[Queries::issue_first_associated_with_milestone_at, Queries::issue_first_added_to_list_label_at])
|
[Queries::issue_first_associated_with_milestone_at, Queries::issue_first_added_to_list_label_at])
|
||||||
end
|
end
|
||||||
|
|
||||||
def plan
|
def plan
|
||||||
calculate_metric(Queries::issues(@project),
|
calculate_metric(Queries::issues(@project, created_after: @from),
|
||||||
[Queries::issue_first_associated_with_milestone_at, Queries::issue_first_added_to_list_label_at],
|
[Queries::issue_first_associated_with_milestone_at, Queries::issue_first_added_to_list_label_at],
|
||||||
Queries::issue_closing_merge_request_opened_at)
|
Queries::issue_closing_merge_request_opened_at)
|
||||||
end
|
end
|
||||||
|
|
||||||
def code
|
def code
|
||||||
calculate_metric(Queries::merge_requests_closing_issues(@project),
|
calculate_metric(Queries::merge_requests_closing_issues(@project, created_after: @from),
|
||||||
-> (data_point) { data_point[:merge_request].created_at },
|
-> (data_point) { data_point[:merge_request].created_at },
|
||||||
[Queries::merge_request_first_assigned_to_user_other_than_author_at, Queries::merge_request_wip_flag_first_removed_at])
|
[Queries::merge_request_first_assigned_to_user_other_than_author_at, Queries::merge_request_wip_flag_first_removed_at])
|
||||||
end
|
end
|
||||||
|
|
||||||
def test
|
def test
|
||||||
calculate_metric(Queries::merge_requests_closing_issues(@project),
|
calculate_metric(Queries::merge_requests_closing_issues(@project, created_after: @from),
|
||||||
Queries::merge_request_build_started_at,
|
Queries::merge_request_build_started_at,
|
||||||
Queries::merge_request_build_finished_at)
|
Queries::merge_request_build_finished_at)
|
||||||
end
|
end
|
||||||
|
|
||||||
def review
|
def review
|
||||||
calculate_metric(Queries::merge_requests_closing_issues(@project),
|
calculate_metric(Queries::merge_requests_closing_issues(@project, created_after: @from),
|
||||||
[Queries::merge_request_first_assigned_to_user_other_than_author_at, Queries::merge_request_wip_flag_first_removed_at],
|
[Queries::merge_request_first_assigned_to_user_other_than_author_at, Queries::merge_request_wip_flag_first_removed_at],
|
||||||
[Queries::merge_request_first_closed_at, Queries::merge_request_merged_at])
|
[Queries::merge_request_first_closed_at, Queries::merge_request_merged_at])
|
||||||
end
|
end
|
||||||
|
|
||||||
def staging
|
def staging
|
||||||
calculate_metric(Queries::merge_requests_closing_issues(@project),
|
calculate_metric(Queries::merge_requests_closing_issues(@project, created_after: @from),
|
||||||
Queries::merge_request_merged_at,
|
Queries::merge_request_merged_at,
|
||||||
Queries::merge_request_deployed_to_any_environment_at)
|
Queries::merge_request_deployed_to_any_environment_at)
|
||||||
end
|
end
|
||||||
|
|
||||||
def production
|
def production
|
||||||
calculate_metric(Queries::merge_requests_closing_issues(@project),
|
calculate_metric(Queries::merge_requests_closing_issues(@project, created_after: @from),
|
||||||
-> (data_point) { data_point[:issue].created_at },
|
-> (data_point) { data_point[:issue].created_at },
|
||||||
Queries::merge_request_deployed_to_production_at)
|
Queries::merge_request_deployed_to_production_at)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
class CycleAnalytics
|
class CycleAnalytics
|
||||||
module Queries
|
module Queries
|
||||||
class << self
|
class << self
|
||||||
def issues(project)
|
def issues(project, created_after:)
|
||||||
project.issues.map { |issue| { issue: issue } }
|
project.issues.where("created_at >= ?", created_after).map { |issue| { issue: issue } }
|
||||||
end
|
end
|
||||||
|
|
||||||
def merge_requests_closing_issues(project)
|
def merge_requests_closing_issues(project, options = {})
|
||||||
issues(project).map do |data_point|
|
issues(project, options).map do |data_point|
|
||||||
merge_requests = data_point[:issue].closed_by_merge_requests(nil, check_if_open: false)
|
merge_requests = data_point[:issue].closed_by_merge_requests(nil, check_if_open: false)
|
||||||
merge_requests.map { |merge_request| { issue: data_point[:issue], merge_request: merge_request } }
|
merge_requests.map { |merge_request| { issue: data_point[:issue], merge_request: merge_request } }
|
||||||
end.flatten
|
end.flatten
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
%h2 Cycle Analytics from #{@cycle_analytics.from} to Today
|
||||||
|
|
||||||
%ul.list-group
|
%ul.list-group
|
||||||
%li.list-group-item
|
%li.list-group-item
|
||||||
Issue:
|
Issue:
|
||||||
|
|
|
@ -2,7 +2,8 @@ require 'spec_helper'
|
||||||
|
|
||||||
describe 'CycleAnalytics#issue', models: true do
|
describe 'CycleAnalytics#issue', models: true do
|
||||||
let(:project) { create(:project) }
|
let(:project) { create(:project) }
|
||||||
subject { CycleAnalytics.new(project) }
|
let(:from_date) { 10.days.ago }
|
||||||
|
subject { CycleAnalytics.new(project, from: from_date) }
|
||||||
|
|
||||||
context "when calculating the median of times between:
|
context "when calculating the median of times between:
|
||||||
start: issue created_at
|
start: issue created_at
|
||||||
|
@ -26,16 +27,6 @@ describe 'CycleAnalytics#issue', models: true do
|
||||||
median_start_time, median_end_time = start_and_end_times[2]
|
median_start_time, median_end_time = start_and_end_times[2]
|
||||||
expect(subject.issue).to eq(median_end_time - median_start_time)
|
expect(subject.issue).to eq(median_end_time - median_start_time)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "does not include issues from other projects" do
|
|
||||||
5.times do
|
|
||||||
milestone = create(:milestone, project: project)
|
|
||||||
issue = create(:issue)
|
|
||||||
issue.update(milestone: milestone)
|
|
||||||
end
|
|
||||||
|
|
||||||
expect(subject.issue).to be_nil
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when a label is added to the issue" do
|
context "when a label is added to the issue" do
|
||||||
|
@ -80,6 +71,29 @@ describe 'CycleAnalytics#issue', models: true do
|
||||||
|
|
||||||
expect(subject.issue).to eq(milestone_add_time - start_time)
|
expect(subject.issue).to eq(milestone_add_time - start_time)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
it "does not include issues from other projects" do
|
||||||
|
milestone = create(:milestone, project: project)
|
||||||
|
list_label = create(:label, lists: [create(:list)])
|
||||||
|
issue = create(:issue)
|
||||||
|
issue.update(milestone: milestone)
|
||||||
|
issue.update(label_ids: [list_label.id])
|
||||||
|
|
||||||
|
expect(subject.issue).to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "excludes issues created before the 'from' date" do
|
||||||
|
before_from_date = from_date - 5.days
|
||||||
|
|
||||||
|
milestone = create(:milestone, project: project)
|
||||||
|
list_label = create(:label, lists: [create(:list)])
|
||||||
|
issue = Timecop.freeze(before_from_date) { create(:issue, project: project)}
|
||||||
|
issue.update(milestone: milestone)
|
||||||
|
issue.update(label_ids: [list_label.id])
|
||||||
|
|
||||||
|
expect(subject.issue).to be_nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue