Commit f932bb8e authored by Timothy Andrew's avatar Timothy Andrew

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.
parent 8ccea81c
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
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
%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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment