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
No related merge requests found
class Projects::CycleAnalyticsController < Projects::ApplicationController
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
class CycleAnalytics
def initialize(project)
attr_reader :from
def initialize(project, from:)
@project = project
@from = from
end
 
def issue
calculate_metric(Queries::issues(@project),
calculate_metric(Queries::issues(@project, created_after: @from),
-> (data_point) { data_point[:issue].created_at },
[Queries::issue_first_associated_with_milestone_at, Queries::issue_first_added_to_list_label_at])
end
 
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_closing_merge_request_opened_at)
end
 
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 },
[Queries::merge_request_first_assigned_to_user_other_than_author_at, Queries::merge_request_wip_flag_first_removed_at])
end
 
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_finished_at)
end
 
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_closed_at, Queries::merge_request_merged_at])
end
 
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_deployed_to_any_environment_at)
end
 
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 },
Queries::merge_request_deployed_to_production_at)
end
Loading
Loading
class CycleAnalytics
module Queries
class << self
def issues(project)
project.issues.map { |issue| { issue: issue } }
def issues(project, created_after:)
project.issues.where("created_at >= ?", created_after).map { |issue| { issue: issue } }
end
 
def merge_requests_closing_issues(project)
issues(project).map do |data_point|
def merge_requests_closing_issues(project, options = {})
issues(project, options).map do |data_point|
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 } }
end.flatten
Loading
Loading
%h2 Cycle Analytics from #{@cycle_analytics.from} to Today
%ul.list-group
%li.list-group-item
Issue:
Loading
Loading
Loading
Loading
@@ -2,7 +2,8 @@ require 'spec_helper'
 
describe 'CycleAnalytics#issue', models: true do
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:
start: issue created_at
Loading
Loading
@@ -26,16 +27,6 @@ describe 'CycleAnalytics#issue', models: true do
median_start_time, median_end_time = start_and_end_times[2]
expect(subject.issue).to eq(median_end_time - median_start_time)
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
 
context "when a label is added to the issue" do
Loading
Loading
@@ -80,6 +71,29 @@ describe 'CycleAnalytics#issue', models: true do
 
expect(subject.issue).to eq(milestone_add_time - start_time)
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
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