Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] numeric report charts #3528

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
3e3acdf
Reporting charts: allow creating concrete number charts.
martinpovolny Apr 28, 2015
f414e8c
Report charts: implement charts with concrete numeric values.
martinpovolny Jul 16, 2015
42584d5
Report charts: implement sum of "other" values for 2dim case.
martinpovolny Jul 21, 2015
6180f83
Report charts: cleanups
martinpovolny Jul 21, 2015
a17100d
jqplot charting: fix a regression in pie sample chart.
martinpovolny Jul 22, 2015
388127a
Charting: handle empty values better.
martinpovolny Jul 22, 2015
84db438
Report charts: cleanups
martinpovolny Jul 22, 2015
3455499
Report charts: style cleanups.
martinpovolny Jul 23, 2015
6780a73
Report charts: fixes after rebase.
martinpovolny Jul 24, 2015
9e48c4f
Report charts: handle situation with other=>true...
martinpovolny Aug 3, 2015
e5a63c0
Reporting charts: allow creating concrete number charts.
martinpovolny Apr 28, 2015
dd5796a
Report charts: implement charts with concrete numeric values.
martinpovolny Jul 16, 2015
0e7f9d9
Report charts: implement sum of "other" values for 2dim case.
martinpovolny Jul 21, 2015
0853454
Report charts: cleanups
martinpovolny Jul 21, 2015
a3e3cf5
jqplot charting: fix a regression in pie sample chart.
martinpovolny Jul 22, 2015
6c22479
Charting: handle empty values better.
martinpovolny Jul 22, 2015
6e5a0d9
Report charts: style cleanups.
martinpovolny Jul 23, 2015
3d38dbe
Report charts: fixes after rebase.
martinpovolny Jul 24, 2015
25e9b29
Report charts: add js options to eval.
martinpovolny Aug 4, 2015
fd2d4ee
WIP
martinpovolny Aug 4, 2015
d211fa3
report editor fix
martinpovolny Aug 7, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/assets/javascripts/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
//= require jqplot-plugins/jqplot.highlighter
//= require jqplot-plugins/jqplot.cursor
//= require jqplot-plugins/jqplot.enhancedLegendRenderer
//= require jqplot-plugins/jqplot.canvasAxisTickRenderer
//= require jqplot-plugins/jqplot.canvasTextRenderer
//= require miq_jqplot
//= require jquery/jquery-ui-1.9.2.custom.min
//= require bootstrap
Expand Down
3 changes: 3 additions & 0 deletions app/assets/javascripts/miq_jqplot.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ function _jqplot_eval_option(data, option) {
function jqplot_process_options(data) {
$.each([ 'seriesDefaults.renderer',
'axes.xaxis.renderer',
'axes.yaxis.tickRenderer',
'axes.yaxis.renderer',
'axes.xaxis.tickRenderer',
'legend.renderer',
'highlighter.tooltipContentEditor' ], function (index, key) {
_jqplot_eval_option(data, key);
Expand Down
57 changes: 42 additions & 15 deletions app/controllers/report_controller/reports/editor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,8 @@ def reset_report_col_fields
@edit[:new][:filter_string] = nil
@edit[:new][:categories] = []
@edit[:new][:graph_type] = nil # Clear graph field
@edit[:new][:chart_mode] = nil
@edit[:new][:chart_column] = nil
@edit[:new][:perf_trend_col] = nil
@edit[:new][:perf_trend_db] = nil
@edit[:new][:perf_trend_pct1] = nil
Expand Down Expand Up @@ -661,21 +663,39 @@ def gfv_charts
if params[:chosen_graph] == "<No chart>"
@edit[:new][:graph_type] = nil
# Reset other setting to initial settings if choosing <No chart>
@edit[:new][:graph_count] = @edit[:current][:graph_count]
@edit[:new][:graph_other] = @edit[:current][:graph_other]
@edit[:new][:graph_count] = @edit[:current][:graph_count]
@edit[:new][:graph_other] = @edit[:current][:graph_other]
@edit[:new][:chart_mode] = @edit[:current][:chart_mode]
@edit[:new][:chart_column] = @edit[:current][:chart_column]
else
@edit[:new][:graph_other] = true if @edit[:new][:graph_type].nil? # Reset other setting if choosing first chart
@edit[:new][:graph_type] = params[:chosen_graph] # Save graph type
@edit[:new][:graph_count] ||= GRAPH_MAX_COUNT # Reset graph count, if not set
@edit[:new][:graph_other] = true if @edit[:new][:graph_type].nil? # Reset other setting if choosing first chart
@edit[:new][:graph_type] = params[:chosen_graph] # Save graph type
@edit[:new][:graph_count] ||= GRAPH_MAX_COUNT # Reset graph count, if not set
@edit[:new][:chart_mode] ||= 'counts'
@edit[:new][:chart_column] ||= ''
end
@refresh_div = "chart_div"
@refresh_partial = "form_chart"
end

if params[:chart_mode] && params[:chart_mode] != @edit[:new][:chart_mode]
@edit[:new][:chart_mode] = params[:chart_mode]
@refresh_div = "chart_div"
@refresh_partial = "form_chart"
end

if params[:chart_column] && params[:chart_column] != @edit[:new][:chart_column]
@edit[:new][:chart_column] = params[:chart_column]
@refresh_div = "chart_sample_div"
@refresh_partial = "form_chart_sample"
end

if params[:chosen_count] && params[:chosen_count] != @edit[:new][:graph_count]
@edit[:new][:graph_count] = params[:chosen_count]
@refresh_div = "chart_sample_div"
@refresh_partial = "form_chart_sample"
end

if params[:chosen_other] # If a chart is showing, set the other setting based on check box present
chosen = (params[:chosen_other].to_s == "1")
if @edit[:new][:graph_other] != chosen
Expand Down Expand Up @@ -1108,10 +1128,13 @@ def set_record_vars(rpt)
else
rpt.dims = @edit[:new][:sortby2] == NOTHING_STRING ? 1 : 2 # Set dims to 1 or 2 based on presence of sortby2
end
rpt.graph = Hash.new
rpt.graph[:type] = @edit[:new][:graph_type]
rpt.graph[:count] = @edit[:new][:graph_count]
rpt.graph[:other] = @edit[:new][:graph_other]
rpt.graph = {
:type => @edit[:new][:graph_type],
:mode => @edit[:new][:chart_mode],
:column => @edit[:new][:chart_column],
:count => @edit[:new][:graph_count],
:other => @edit[:new][:graph_other],
}
end

# Set the conditions field (expression)
Expand Down Expand Up @@ -1378,13 +1401,17 @@ def set_form_vars
# @edit[:new][:graph] = @rpt.graph
# Replaced above line to handle new graph settings Hash
if @rpt.graph.is_a?(Hash)
@edit[:new][:graph_type] = @rpt.graph[:type]
@edit[:new][:graph_count] = @rpt.graph[:count]
@edit[:new][:graph_other] = @rpt.graph[:other] ? @rpt.graph[:other] : false
@edit[:new][:graph_type] = @rpt.graph[:type]
@edit[:new][:graph_count] = @rpt.graph[:count]
@edit[:new][:chart_mode] = @rpt.graph[:mode]
@edit[:new][:chart_column] = @rpt.graph[:column]
@edit[:new][:graph_other] = @rpt.graph[:other] ? @rpt.graph[:other] : false
else
@edit[:new][:graph_type] = @rpt.graph
@edit[:new][:graph_count] = GRAPH_MAX_COUNT
@edit[:new][:graph_other] = true
@edit[:new][:graph_type] = @rpt.graph
@edit[:new][:graph_count] = GRAPH_MAX_COUNT
@edit[:new][:chart_mode] = 'counts'
@edit[:new][:chart_column] = ''
@edit[:new][:graph_other] = true
end

@edit[:new][:dims] = @rpt.dims
Expand Down
12 changes: 12 additions & 0 deletions app/helpers/report_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,16 @@ def visibility_options(widget)
_("By %{typ}: %{values}") % {:typ => typ.to_s.titleize, :values => values.join(',')}
end
end

def chart_fields_options
if @edit[:pivot_cols].empty?
@edit[:new][:fields]
else
@edit[:pivot_cols].each_with_object([]) do |(field, agreg), options|
agreg.each do |fun|
options << ["#{field} (#{fun.to_s.titleize})", "#{field}:#{fun}"]
end
end
end
end
end
24 changes: 23 additions & 1 deletion app/views/report/_form_chart.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,35 @@
= _('Choose a chart type')
%td
= select_tag('chosen_graph',
options_for_select(["<No chart>"] + Charting.chart_names_for_select, @edit[:new][:graph_type]),
options_for_select([_("<No chart>")] + Charting.chart_names_for_select, @edit[:new][:graph_type]),
:multiple => false,
:class => "widthed",
"data-miq_sparkle_on" => true,
"data-miq_sparkle_off" => true,
"data-miq_observe" => {:url => url}.to_json)
- unless @edit[:new][:graph_type].blank?
%tr
%td.key
= _('Chart mode')
%td
= select_tag('chart_mode',
options_for_select([[_('Counts'), 'counts'], [_('Values'), 'values']], @edit[:new][:chart_mode]),
:multiple => false,
:class => "widthed",
"data-miq_sparkle_on" => true,
"data-miq_sparkle_off" => true,
"data-miq_observe" => {:url => url}.to_json)
%tr
%td.key
= _('Data column')
%td
= select_tag('chart_column',
options_for_select(chart_fields_options, @edit[:new][:chart_column]),
:multiple => false,
:class => "widthed",
"data-miq_sparkle_on" => true,
"data-miq_sparkle_off" => true,
"data-miq_observe" => {:url => url}.to_json)
%tr
%td.key
= _('Top values to show')
Expand Down
8 changes: 4 additions & 4 deletions lib/charting/jqplot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def basic_chart(chart_type)
:options => {
:seriesDefaults => {
:renderer => 'jQuery.jqplot.BarRenderer',
:rendererOptions => {:barDirection => 'horizontal'},
:rendererOptions => {:barDirection => 'horizontal', :barWidth => 5},
},
:series => []
},
Expand All @@ -52,7 +52,7 @@ def basic_chart(chart_type)
:stackSeries => true,
:seriesDefaults => {
:renderer => 'jQuery.jqplot.BarRenderer',
:rendererOptions => {:barDirection => 'horizontal'},
:rendererOptions => {:barDirection => 'horizontal', :barWidth => 5},
},
:series => []
},
Expand All @@ -63,7 +63,7 @@ def basic_chart(chart_type)
:options => {
:seriesDefaults => {
:renderer => 'jQuery.jqplot.BarRenderer',
:rendererOptions => {:barDirection => 'vertical'},
:rendererOptions => {:barDirection => 'vertical', :barWidth => 5},
},
:series => []
},
Expand All @@ -75,7 +75,7 @@ def basic_chart(chart_type)
:stackSeries => true,
:seriesDefaults => {
:renderer => 'jQuery.jqplot.BarRenderer',
:rendererOptions => {:barDirection => 'vertical'},
:rendererOptions => {:barDirection => 'vertical', :barWidth => 5},
},
:series => []
},
Expand Down
114 changes: 107 additions & 7 deletions lib/report_formatter/chart_common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module ReportFormatter
module ChartCommon
def slice_legend(string, limit = LEGEND_LENGTH)
string = string.to_s
string.length > limit ? string.slice(0, limit) + "..." : string
string = string.length > limit ? string.slice(0, limit) + "..." : string
string.gsub(/\n/, ' ')
end

Expand Down Expand Up @@ -58,7 +58,8 @@ def build_document_body
when :performance then :build_performance_chart # performance chart (time based)
when :util_ts then :build_util_ts_chart # utilization timestamp chart (grouped columns)
when :planning then :build_planning_chart # trend based planning chart
else :build_reporting_chart # standard reporting chart
else # reporting charts
mri.graph[:mode] == 'values' ? :build_reporting_chart_numeric : :build_reporting_chart
end
method(fun).call(maxcols, divider)
end
Expand Down Expand Up @@ -347,13 +348,108 @@ def build_reporting_chart_dim2
series.push(:value => ocount,
:tooltip => "#{key1} / Other: #{ocount}")
end
add_series("Other", series)
add_series(_("Other"), series)
end
counts # FIXME
end

def build_reporting_chart_dim2_numeric
(sort1, sort2) = mri.sortby
(keep, show_other) = keep_and_show_other

# Group values by sort1
# 3rd dimension in the chart is defined by sort2
groups = mri.table.data.group_by { |row| row[sort1] }

group_sums = groups.each_with_object({}) do |(key, rows), h|
h[key] = rows.inject(0) { |sum, row| sum + row[data_column_name] }
end
sorted_sums = group_sums.sort_by { |_key, sum| sum }

selected_groups = sorted_sums.reverse.take(keep)

cathegory_texts = selected_groups.collect { |key, _| slice_legend(key, LABEL_LENGTH) }
cathegory_texts << _('Other') if show_other

add_axis_category_text(cathegory_texts)

groups_hash = selected_groups.each_with_object(Hash.new { |h, k| h[k] = {} }) do |(key, _), h|
groups[key].each { |row| h[key][row[sort2]] = row }
end

if show_other
other_groups = Array(sorted_sums[0, sorted_sums.length - keep])
other = other_groups.each_with_object(Hash.new(0)) do |(key, _), o|
groups[key].each do |row|
o[row[sort2]] += row[data_column_name]
end
end
end

# For each value in sort2 column we create a series.
sort2_values = mri.table.data.each_with_object({}) { |row, h| h[row[sort2]] = true }
sort2_values.each_key do |val2|
series = selected_groups.each_with_object(series_class.new) do |(key1, _), a|
row = groups_hash.fetch_path(key1, val2)
value = row ? row[data_column_name] : 0
a.push(:value => value,
:tooltip => "#{key1} / #{val2}: #{value}")
end

series.push(:value => other[val2],
:tooltip => "Other / #{val2}: #{other[val2]}") if show_other

label = slice_legend(val2) if val2.kind_of?(String)
label = label.to_s.gsub(/\\/, ' \ ')
label = _('no value') if label.blank?
add_series(label, series)
end
groups
end

def data_column_name
@data_column_name ||= (
_model, col = mri.graph[:column].split('-', 2)
col, aggreg = col.split(':', 2)
aggreg.blank? ? col : "#{col}__#{aggreg}"
)
end

def build_reporting_chart_other_numeric
categories = []
(sort1,) = mri.sortby
(keep, show_other) = keep_and_show_other
sorted_data = mri.table.data.sort_by { |row| row[data_column_name] || 0 }

series = sorted_data.reverse.take(keep)
.each_with_object(series_class.new(pie_type? ? :pie : :flat)) do |row, a|
tooltip = row[sort1]
tooltip = _('no value') if tooltip.blank?
a.push(:value => row[data_column_name],
:tooltip => tooltip)
categories.push([tooltip, row[data_column_name]])
end

if show_other
other_sum = Array(sorted_data[0, sorted_data.length - keep])
.inject(0) { |sum, row| sum + row[data_column_name] }
series.push(:value => other_sum, :tooltip => _('Other'))
categories.push([_('Other'), other_sum])
end

# Pie charts put categories in legend, else in axis labels
limit = pie_type? ? LEGEND_LENGTH : LABEL_LENGTH
categories.collect! { |c| slice_legend(c[0], limit) }
add_axis_category_text(categories)

add_series(mri.headers[0], series)
end

def pie_type?
@pie_type ||= mri.graph[:type] =~ /^(Pie|Donut)/
end

def build_reporting_chart_other
@is_pie_type = mri.graph[:type] =~ /^(Pie|Donut)/
save_key = nil
counter = 0
categories = [] # Store categories and series counts in an array of arrays
Expand All @@ -379,12 +475,12 @@ def build_reporting_chart_other
end

series = categories.each_with_object(
series_class.new(@is_pie_type ? :pie : :flat)) do |cat, a|
series_class.new(pie_type? ? :pie : :flat)) do |cat, a|
a.push(:value => cat.last, :tooltip => "#{cat.first}: #{cat.last}")
end

# Pie charts put categories in legend, else in axis labels
limit = @is_pie_type ? LEGEND_LENGTH : LABEL_LENGTH
limit = pie_type? ? LEGEND_LENGTH : LABEL_LENGTH
categories.collect! { |c| slice_legend(c[0], limit) }
add_axis_category_text(categories)
add_series(mri.headers[0], series)
Expand All @@ -406,7 +502,11 @@ def build_util_ts_chart(maxcols, divider)
build_util_ts_chart_column if %w(Column ColumnThreed).index(mri.graph[:type])
end

def build_reporting_chart(maxcols, divider)
def build_reporting_chart_numeric(_maxcols, _divider)
mri.dims == 2 ? build_reporting_chart_dim2_numeric : build_reporting_chart_other_numeric
end

def build_reporting_chart(_maxcols, _divider)
mri.dims == 2 ? build_reporting_chart_dim2 : build_reporting_chart_other
end
end
Expand Down
Loading