Skip to content
Snippets Groups Projects
Commit 331080bc authored by Timothy Andrew's avatar Timothy Andrew
Browse files

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.
parent ce6bcdd0
No related branches found
No related tags found
1 merge request!5986Cycle Analytics: first iteration
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
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
Loading
Loading
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
Loading
Loading
%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:
Loading
Loading
Loading
@@ -2,7 +2,8 @@ require 'spec_helper'
Loading
@@ -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
Loading
@@ -26,16 +27,6 @@ describe 'CycleAnalytics#issue', models: true do
Loading
@@ -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
Loading
@@ -80,6 +71,29 @@ describe 'CycleAnalytics#issue', models: true do
Loading
@@ -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
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