Commit fc6f8f20 authored by James Lopez's avatar James Lopez

added new summary serializers and refactor all of the summary stuff into separate logical classes

parent dc6ea14b
...@@ -8,10 +8,6 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController ...@@ -8,10 +8,6 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController
def show def show
@cycle_analytics = ::CycleAnalytics.new(@project, current_user, from: start_date(cycle_analytics_params)) @cycle_analytics = ::CycleAnalytics.new(@project, current_user, from: start_date(cycle_analytics_params))
stats_values, cycle_analytics_json = generate_cycle_analytics_data
@cycle_analytics_no_data = stats_values.blank?
respond_to do |format| respond_to do |format|
format.html format.html
format.json { render json: cycle_analytics_json } format.json { render json: cycle_analytics_json }
...@@ -26,47 +22,11 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController ...@@ -26,47 +22,11 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController
{ start_date: params[:cycle_analytics][:start_date] } { start_date: params[:cycle_analytics][:start_date] }
end end
def generate_cycle_analytics_data def cycle_analytics_json
stats_values = [] {
summary: @cycle_analytics.summary,
cycle_analytics_view_data = [[:issue, "Issue", "Related Issues", "Time before an issue gets scheduled"], stats: nil, # TODO
[:plan, "Plan", "Related Commits", "Time before an issue starts implementation"], permissions: @cycle_analytics.permissions(user: current_user)# TODO
[:code, "Code", "Related Merge Requests", "Time spent coding"],
[:test, "Test", "Relative Builds Trigger by Commits", "The time taken to build and test the application"],
[:review, "Review", "Relative Merged Requests", "The time taken to review the code"],
[:staging, "Staging", "Relative Deployed Builds", "The time taken in staging"],
[:production, "Production", "Related Issues", "The total time taken from idea to production"]]
stats = cycle_analytics_view_data.reduce([]) do |stats, (stage_method, stage_text, stage_legend, stage_description)|
value = @cycle_analytics.send(stage_method).presence
stats_values << value.abs if value
stats << {
title: stage_text,
description: stage_description,
legend: stage_legend,
value: value && !value.zero? ? distance_of_time_in_words(value) : nil
}
stats
end
issues = @cycle_analytics.summary.new_issues
commits = @cycle_analytics.summary.commits
deploys = @cycle_analytics.summary.deploys
summary = [
{ title: "New Issue".pluralize(issues), value: issues },
{ title: "Commit".pluralize(commits), value: commits },
{ title: "Deploy".pluralize(deploys), value: deploys }
]
cycle_analytics_hash = { summary: summary,
stats: stats,
permissions: @cycle_analytics.permissions(user: current_user)
} }
[stats_values, cycle_analytics_hash]
end end
end end
...@@ -7,7 +7,7 @@ class CycleAnalytics ...@@ -7,7 +7,7 @@ class CycleAnalytics
end end
def summary def summary
@summary ||= Summary.new(@project, from: @options[:from]) @summary ||= Gitlab::CycleAnalytics::Summary.new(@project, from: @options[:from]).data
end end
def method_missing(method_sym, *arguments, &block) def method_missing(method_sym, *arguments, &block)
......
class CycleAnalytics
class Summary
def initialize(project, current_user, from:)
@project = project
@current_user = current_user
@from = from
end
def new_issues
IssuesFinder.new(@current_user, project_id: @project.id).execute.created_after(@from).count
end
def commits
ref = @project.default_branch.presence
count_commits_for(ref)
end
def deploys
@project.deployments.where("created_at > ?", @from).count
end
private
# Don't use the `Gitlab::Git::Repository#log` method, because it enforces
# a limit. Since we need a commit count, we _can't_ enforce a limit, so
# the easiest way forward is to replicate the relevant portions of the
# `log` function here.
def count_commits_for(ref)
return unless ref
repository = @project.repository.raw_repository
sha = @project.repository.commit(ref).sha
cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{repository.path} log)
cmd << '--format=%H'
cmd << "--after=#{@from.iso8601}"
cmd << sha
raw_output = IO.popen(cmd) { |io| io.read }
raw_output.lines.count
end
end
end
class AnalyticsSummaryEntity < Grape::Entity
expose :value, safe: true
expose :title do |object|
object.title.pluralize(object.value)
end
end
class AnalyticsSummarySerializer < BaseSerializer
entity AnalyticsSummaryEntity
end
module Gitlab
module CycleAnalytics
module Summary
extend self
def initialize(project, from:)
@project = project
@from = from
end
def data
[serialize(issue),
serialize(commit),
serialize(deploy)]
end
private
def serialize(summary_object)
AnalyticsSummarySerializer.new.represent(summary_object).as_json
end
def issue
Summary::Issue.new(project: @project, from: @from)
end
def deploy
Summary::Deploy.new(project: @project, from: @from)
end
def commit
Summary::Commit.new(project: @project, from: @from)
end
end
end
end
module Gitlab
module CycleAnalytics
module Summary
class Base
def initialize(project:, from:)
@project = project
@from = from
end
def title
self.name
end
def value
raise NotImplementedError.new("Expected #{self.name} to implement value")
end
end
end
end
end
module Gitlab
module CycleAnalytics
module Summary
class Commit < Base
def value
@value ||= count_commits
end
private
# Don't use the `Gitlab::Git::Repository#log` method, because it enforces
# a limit. Since we need a commit count, we _can't_ enforce a limit, so
# the easiest way forward is to replicate the relevant portions of the
# `log` function here.
def count_commits
return unless ref
repository = @project.repository.raw_repository
sha = @project.repository.commit(ref).sha
cmd = %W(git --git-dir=#{repository.path} log)
cmd << '--format=%H'
cmd << "--after=#{@from.iso8601}"
cmd << sha
raw_output = IO.popen(cmd) { |io| io.read }
raw_output.lines.count
end
def ref
@ref ||= @project.default_branch.presence
end
end
end
end
end
module Gitlab
module CycleAnalytics
module Summary
class Deploy < Base
def value
@value ||= @project.deployments.where("created_at > ?", @from).count
end
end
end
end
end
module Gitlab
module CycleAnalytics
module Summary
class Issue < Base
def title
'New Issue'
end
def value
@value ||= @project.issues.created_after(@from).count
end
end
end
end
end
require 'spec_helper'
describe AnalyticsStageSerializer do
let(:serializer) do
described_class
.new.represent(resource)
end
let(:json) { serializer.as_json }
let(:resource) { Gitlab::CycleAnalytics::CodeStage.new(project: double, options: {}, stage: :code) }
before do
allow_any_instance_of(Gitlab::CycleAnalytics::MetricsFetcher).to receive(:calculate_metric).and_return(1.12)
allow_any_instance_of(Gitlab::CycleAnalytics::BaseEvent).to receive(:event_result).and_return({})
end
it 'it generates payload for single object' do
expect(json).to be_an_instance_of Hash
end
it 'contains important elements of AnalyticsStage' do
expect(json).to include(:title, :description, :value)
end
end
require 'spec_helper'
describe AnalyticsSummarySerializer do
let(:serializer) do
described_class
.new.represent(resource)
end
let(:json) { serializer.as_json }
let(:project) { create(:empty_project) }
let(:resource) { Gitlab::CycleAnalytics::Summary::Issue.new(project: double, from: 1.day.ago) }
before do
allow_any_instance_of(Gitlab::CycleAnalytics::Summary::Issue).to receive(:value).and_return(1.12)
end
it 'it generates payload for single object' do
expect(json).to be_an_instance_of Hash
end
it 'contains important elements of AnalyticsStage' do
expect(json).to include(:title, :value)
end
end
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