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:
Timothy Andrew 2016-08-26 16:18:52 +05:30
parent ce6bcdd004
commit 331080bca6
5 changed files with 59 additions and 24 deletions

View file

@ -1,5 +1,21 @@
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

View file

@ -1,46 +1,49 @@
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

View file

@ -1,12 +1,12 @@
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

View file

@ -1,3 +1,5 @@
%h2 Cycle Analytics from #{@cycle_analytics.from} to Today
%ul.list-group
%li.list-group-item
Issue:

View file

@ -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
@ -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
@ -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