From f932bb8e41852d7bdcd66fe15a56277074df3aa3 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Thu, 25 Aug 2016 10:32:27 +0530 Subject: [PATCH] Add the "Plan" Cycle Analytics query. 1. Move from raw SQL to ActiveRecord. 2. Add a non-persisted `CycleAnalytics` model to store all the queries. --- .../projects/cycle_analytics_controller.rb | 29 +------------- app/models/cycle_analytics.rb | 40 +++++++++++++++++++ .../projects/cycle_analytics/show.html.haml | 9 ++++- 3 files changed, 49 insertions(+), 29 deletions(-) create mode 100644 app/models/cycle_analytics.rb diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb index ed885729fbb..9dab7bfa380 100644 --- a/app/controllers/projects/cycle_analytics_controller.rb +++ b/app/controllers/projects/cycle_analytics_controller.rb @@ -1,32 +1,5 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController def show - @metrics = { - issue: issue - } - end - - private - - def issue - query = <<-HEREDOC - WITH ordered_data AS ( - SELECT extract(milliseconds FROM (COALESCE(first_associated_with_milestone_at, first_added_to_board_at) - issues.created_at)) AS data_point, - row_number() over (order by (COALESCE(first_associated_with_milestone_at, first_added_to_board_at) - issues.created_at)) as row_id - FROM issues - INNER JOIN issue_metrics ON issue_metrics.issue_id = issues.id - WHERE COALESCE(first_associated_with_milestone_at, first_added_to_board_at) IS NOT NULL - ), - - ct AS ( - SELECT count(1) AS ct - FROM ordered_data - ) - - SELECT avg(data_point) AS median - FROM ordered_data - WHERE row_id between (select ct from ct)/2.0 and (select ct from ct)/2.0 + 1; - HEREDOC - - ActiveRecord::Base.connection.execute(query).to_a.first['median'] + @cycle_analytics = CycleAnalytics.new end end diff --git a/app/models/cycle_analytics.rb b/app/models/cycle_analytics.rb new file mode 100644 index 00000000000..d037b1fef12 --- /dev/null +++ b/app/models/cycle_analytics.rb @@ -0,0 +1,40 @@ +class CycleAnalytics + def issue + issues = Issue.includes(:metrics).where("issue_metrics.id IS NOT NULL").references(:issue_metrics).to_a + start_time_fn = -> (issue) { issue.created_at } + end_time_fn = -> (issue) { issue.metrics.first_associated_with_milestone_at.presence || issue.metrics.first_added_to_board_at.presence } + + calculate_metric(issues, start_time_fn, end_time_fn) + end + + def plan + issues = Issue.includes(:metrics).where("issue_metrics.id IS NOT NULL").references(:issue_metrics).to_a + start_time_fn = -> (issue) { issue.metrics.first_associated_with_milestone_at.presence || issue.metrics.first_added_to_board_at.presence } + end_time_fn = lambda do |issue| + merge_requests = issue.closed_by_merge_requests + merge_requests.map(&:created_at).min if merge_requests.present? + end + + calculate_metric(issues, start_time_fn, end_time_fn) + end + + private + + def calculate_metric(data, start_time_fn, end_time_fn) + times = data.map do |data_point| + start_time = start_time_fn[data_point] + end_time = end_time_fn[data_point] + + if start_time.present? && end_time.present? + end_time - start_time + end + end + + median(times.compact) + end + + def median(coll) + size = coll.length + (coll[size / 2] + coll[(size - 1) / 2]) / 2.0 + end +end diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml index e9bdd71b30a..6aad68617fe 100644 --- a/app/views/projects/cycle_analytics/show.html.haml +++ b/app/views/projects/cycle_analytics/show.html.haml @@ -1 +1,8 @@ -%pre= @metrics +%ul.list-group + %li.list-group-item + Issue: + = distance_of_time_in_words @cycle_analytics.issue + + %li.list-group-item + Plan: + = distance_of_time_in_words @cycle_analytics.plan -- GitLab