Skip to content
Snippets Groups Projects
Commit fc6f8f20 authored by James Lopez's avatar James Lopez
Browse files

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

parent dc6ea14b
No related branches found
No related tags found
No related merge requests found
Showing with 182 additions and 89 deletions
Loading
Loading
@@ -8,10 +8,6 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController
def show
@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|
format.html
format.json { render json: cycle_analytics_json }
Loading
Loading
@@ -26,47 +22,11 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController
{ start_date: params[:cycle_analytics][:start_date] }
end
 
def generate_cycle_analytics_data
stats_values = []
cycle_analytics_view_data = [[:issue, "Issue", "Related Issues", "Time before an issue gets scheduled"],
[:plan, "Plan", "Related Commits", "Time before an issue starts implementation"],
[: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)
def cycle_analytics_json
{
summary: @cycle_analytics.summary,
stats: nil, # TODO
permissions: @cycle_analytics.permissions(user: current_user)# TODO
}
[stats_values, cycle_analytics_hash]
end
end
Loading
Loading
@@ -7,7 +7,7 @@ class CycleAnalytics
end
 
def summary
@summary ||= Summary.new(@project, from: @options[:from])
@summary ||= Gitlab::CycleAnalytics::Summary.new(@project, from: @options[:from]).data
end
 
def method_missing(method_sym, *arguments, &block)
Loading
Loading
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
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