Commit ce77ff8a authored by Rémy Coutable's avatar Rémy Coutable

Use a single color for the Insights time series bar charts

Signed-off-by: default avatarRémy Coutable <remy@rymai.me>
parent 096cf2aa
...@@ -129,7 +129,8 @@ Supported values are: ...@@ -129,7 +129,8 @@ Supported values are:
| Name | Example | | Name | Example |
| ----- | ------- | | ----- | ------- |
| `bar` | ![Insights example stacked bar chart](img/insights_example_bar_chart.png) | | `bar` | ![Insights example bar chart](img/insights_example_bar_chart.png) |
| `bar` (time series, i.e. when `group_by` is used) | ![Insights example bar time series chart](img/insights_example_bar_time_series_chart.png) |
| `line` | ![Insights example stacked bar chart](img/insights_example_line_chart.png) | | `line` | ![Insights example stacked bar chart](img/insights_example_line_chart.png) |
| `stacked-bar` | ![Insights example stacked bar chart](img/insights_example_stacked_bar_chart.png) | | `stacked-bar` | ![Insights example stacked bar chart](img/insights_example_stacked_bar_chart.png) |
......
...@@ -8,7 +8,8 @@ module InsightsActions ...@@ -8,7 +8,8 @@ module InsightsActions
before_action :validate_params, only: [:query] before_action :validate_params, only: [:query]
rescue_from Gitlab::Insights::Validators::ParamsValidator::ParamsValidatorError, rescue_from Gitlab::Insights::Validators::ParamsValidator::ParamsValidatorError,
Gitlab::Insights::Finders::IssuableFinder::IssuableFinderError, with: :render_insights_chart_error Gitlab::Insights::Finders::IssuableFinder::IssuableFinderError,
Gitlab::Insights::Reducers::BaseReducer::BaseReducerError, with: :render_insights_chart_error
end end
def show def show
...@@ -38,38 +39,57 @@ module InsightsActions ...@@ -38,38 +39,57 @@ module InsightsActions
Gitlab::Insights::Validators::ParamsValidator.new(params).validate! Gitlab::Insights::Validators::ParamsValidator.new(params).validate!
end end
def chart_type_param
@chart_type_param ||= params[:chart_type]
end
def query_param
@query_param ||= params[:query]
end
def period_param
@period_param ||= query_param[:group_by]
end
def collection_labels_param
@collection_labels_param ||= query_param[:collection_labels]
end
def insights_json def insights_json
issuables_finder = finder(params[:query]) issuables = finder.find
issuables = issuables_finder.find insights = reduce(issuables: issuables, period_limit: finder.period_limit)
insights = reduce( serializer.present(insights)
issuables: issuables, end
chart_type: params[:chart_type],
period: params[:query][:group_by], def reduce(issuables:, period_limit: nil)
period_limit: issuables_finder.period_limit, case chart_type_param
labels: params[:query][:collection_labels])
serializer(params[:chart_type]).present(insights)
end
def reduce(issuables:, chart_type:, period:, period_limit:, labels: nil)
case chart_type
when 'stacked-bar', 'line' when 'stacked-bar', 'line'
Gitlab::Insights::Reducers::LabelCountPerPeriodReducer.reduce(issuables, period: period, period_limit: period_limit, labels: labels) Gitlab::Insights::Reducers::LabelCountPerPeriodReducer.reduce(issuables, period: period_param, period_limit: period_limit, labels: collection_labels_param)
when 'bar' when 'bar'
Gitlab::Insights::Reducers::CountPerPeriodReducer.reduce(issuables, period: period, period_limit: period_limit) if period_param
Gitlab::Insights::Reducers::CountPerPeriodReducer.reduce(issuables, period: period_param, period_limit: period_limit)
else
Gitlab::Insights::Reducers::CountPerLabelReducer.reduce(issuables, labels: collection_labels_param)
end
end end
end end
def finder(query) def finder
@finder ||=
Gitlab::Insights::Finders::IssuableFinder Gitlab::Insights::Finders::IssuableFinder
.new(insights_entity, current_user, query) .new(insights_entity, current_user, query_param)
end end
def serializer(chart_type) def serializer
case chart_type case chart_type_param
when 'stacked-bar' when 'stacked-bar'
Gitlab::Insights::Serializers::Chartjs::MultiSeriesSerializer Gitlab::Insights::Serializers::Chartjs::MultiSeriesSerializer
when 'bar' when 'bar'
if period_param
Gitlab::Insights::Serializers::Chartjs::BarTimeSeriesSerializer
else
Gitlab::Insights::Serializers::Chartjs::BarSerializer Gitlab::Insights::Serializers::Chartjs::BarSerializer
end
when 'line' when 'line'
Gitlab::Insights::Serializers::Chartjs::LineSerializer Gitlab::Insights::Serializers::Chartjs::LineSerializer
end end
......
---
title: Use a single color for the Insights time series bar charts
merge_request: 11076
author:
type: changed
...@@ -89,7 +89,7 @@ module Gitlab ...@@ -89,7 +89,7 @@ module Gitlab
def period def period
@period ||= @period ||=
begin if opts.key?(:group_by)
period = opts[:group_by].to_s.pluralize.to_sym period = opts[:group_by].to_s.pluralize.to_sym
unless PERIODS.key?(period) unless PERIODS.key?(period)
...@@ -97,6 +97,8 @@ module Gitlab ...@@ -97,6 +97,8 @@ module Gitlab
end end
period period
else
:days
end end
end end
end end
......
# frozen_string_literal: true
module Gitlab
module Insights
module Serializers
module Chartjs
class BarTimeSeriesSerializer < Chartjs::BarSerializer
private
# series_data - A hash of the form `Hash[Symbol|String, Integer]`, e.g.
# {
# "January 2019": 1,
# "February 2019": 2
# }
#
# Returns a datasets array, e.g.
# [{ label: nil, data: [1, 2], borderColor: ['#428bca', '#ffd8b1'] }]
def chart_datasets(series_data)
background_colors = Array.new(series_data.size - 1, Gitlab::Insights::DEFAULT_COLOR)
background_colors << Gitlab::Insights::COLOR_SCHEME[:apricot]
[dataset(nil, series_data.values, background_colors)]
end
end
end
end
end
end
# frozen_string_literal: true # frozen_string_literal: true
FactoryBot.define do FactoryBot.define do
factory :insights_issuables, class: Hash do factory :insights_issues_by_team, class: Hash do
initialize_with do initialize_with do
{ {
Manage: 1, Manage: 1,
...@@ -12,7 +12,17 @@ FactoryBot.define do ...@@ -12,7 +12,17 @@ FactoryBot.define do
end end
end end
factory :insights_issuables_per_month, class: Hash do factory :insights_merge_requests_per_month, class: Hash do
initialize_with do
{
'January 2019' => 1,
'February 2019' => 2,
'March 2019' => 3
}
end
end
factory :insights_issues_by_team_per_month, class: Hash do
initialize_with do initialize_with do
{ {
'January 2019' => { 'January 2019' => {
......
...@@ -20,7 +20,7 @@ RSpec.describe Gitlab::Insights::Finders::IssuableFinder do ...@@ -20,7 +20,7 @@ RSpec.describe Gitlab::Insights::Finders::IssuableFinder do
end end
it 'raises an error for an invalid :issuable_type option' do it 'raises an error for an invalid :issuable_type option' do
expect { find(nil, issuable_type: 'foo') }.to raise_error(described_class::InvalidIssuableTypeError, "Invalid `:issuable_type` option: `foo`. Allowed values are #{described_class::FINDERS.keys}!") expect { find(build(:project), issuable_type: 'foo') }.to raise_error(described_class::InvalidIssuableTypeError, "Invalid `:issuable_type` option: `foo`. Allowed values are #{described_class::FINDERS.keys}!")
end end
it 'raises an error for an invalid entity object' do it 'raises an error for an invalid entity object' do
...@@ -28,11 +28,15 @@ RSpec.describe Gitlab::Insights::Finders::IssuableFinder do ...@@ -28,11 +28,15 @@ RSpec.describe Gitlab::Insights::Finders::IssuableFinder do
end end
it 'raises an error for an invalid :group_by option' do it 'raises an error for an invalid :group_by option' do
expect { find(nil, issuable_type: 'issue', group_by: 'foo') }.to raise_error(described_class::InvalidGroupByError, "Invalid `:group_by` option: `foo`. Allowed values are #{described_class::PERIODS.keys}!") expect { find(build(:project), issuable_type: 'issue', group_by: 'foo') }.to raise_error(described_class::InvalidGroupByError, "Invalid `:group_by` option: `foo`. Allowed values are #{described_class::PERIODS.keys}!")
end
it 'defaults to the "days" period if no :group_by is given' do
expect(described_class.new(build(:project), nil, issuable_type: 'issue').__send__(:period)).to eq(:days)
end end
it 'raises an error for an invalid :period_limit option' do it 'raises an error for an invalid :period_limit option' do
expect { find(build(:user), issuable_type: 'issue', group_by: 'months', period_limit: 'many') }.to raise_error(described_class::InvalidPeriodLimitError, "Invalid `:period_limit` option: `many`. Expected an integer!") expect { find(build(:project), issuable_type: 'issue', group_by: 'months', period_limit: 'many') }.to raise_error(described_class::InvalidPeriodLimitError, "Invalid `:period_limit` option: `many`. Expected an integer!")
end end
shared_examples_for "insights issuable finder" do shared_examples_for "insights issuable finder" do
......
...@@ -4,7 +4,7 @@ require 'spec_helper' ...@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Insights::Serializers::Chartjs::BarSerializer do RSpec.describe Gitlab::Insights::Serializers::Chartjs::BarSerializer do
it 'returns the correct format' do it 'returns the correct format' do
input = build(:insights_issuables) input = build(:insights_issues_by_team)
expected = { expected = {
labels: %w[Manage Plan Create undefined], labels: %w[Manage Plan Create undefined],
datasets: [ datasets: [
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Insights::Serializers::Chartjs::BarTimeSeriesSerializer do
it 'returns the correct format' do
input = build(:insights_merge_requests_per_month)
expected = {
labels: ['January 2019', 'February 2019', 'March 2019'],
datasets: [
{
label: nil,
data: [1, 2, 3],
backgroundColor: [Gitlab::Insights::DEFAULT_COLOR, Gitlab::Insights::DEFAULT_COLOR, Gitlab::Insights::COLOR_SCHEME[:apricot]]
}
]
}.with_indifferent_access
expect(described_class.present(input)).to eq(expected)
end
end
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Insights::Serializers::Chartjs::LineSerializer do RSpec.describe Gitlab::Insights::Serializers::Chartjs::LineSerializer do
let(:input) { build(:insights_issuables_per_month) } let(:input) { build(:insights_issues_by_team_per_month) }
subject { described_class.present(input) } subject { described_class.present(input) }
......
...@@ -4,7 +4,7 @@ require 'spec_helper' ...@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Insights::Serializers::Chartjs::MultiSeriesSerializer do RSpec.describe Gitlab::Insights::Serializers::Chartjs::MultiSeriesSerializer do
it 'returns the correct format' do it 'returns the correct format' do
input = build(:insights_issuables_per_month) input = build(:insights_issues_by_team_per_month)
expected = { expected = {
labels: ['January 2019', 'February 2019', 'March 2019'], labels: ['January 2019', 'February 2019', 'March 2019'],
datasets: [ datasets: [
......
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