forked from ManageIQ/manageiq
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request ManageIQ#19873 from jrafanie/move_report_formatter…
…_charting_to_core Move report formatter and charting to core
- Loading branch information
Showing
27 changed files
with
2,553 additions
and
6 deletions.
There are no files selected for viewing
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# If code uses the old constant name: | ||
# * Rails will autoload it and start here. | ||
# * We assign the old toplevel constant to the new constant. | ||
# * We can't include rails deprecate_constant globally, so we use ruby's. | ||
Charting = ManageIQ::Reporting::Charting | ||
Object.deprecate_constant :Charting |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
module ManageIQ | ||
module Reporting | ||
class Charting | ||
class << self | ||
extend Forwardable | ||
delegate [ | ||
:backend, # charting backend name; FIXME: remove this method | ||
:render_format, | ||
:format, # format for Ruport renderer | ||
:load_helpers, | ||
:data_ok?, | ||
:sample_chart, | ||
:chart_names_for_select, | ||
:chart_themes_for_select, | ||
:serialized, | ||
:deserialized, | ||
:js_load_statement # javascript statement to reload charts | ||
] => :instance | ||
end | ||
|
||
# discovery | ||
# | ||
# | ||
def self.instance | ||
@instance ||= new | ||
end | ||
|
||
def self.new | ||
self == ManageIQ::Reporting::Charting ? detect_available_plugin.new : super | ||
end | ||
|
||
def self.detect_available_plugin | ||
subclasses.select(&:available?).max_by(&:priority) | ||
end | ||
end | ||
end | ||
end | ||
|
||
# load all plugins | ||
Dir.glob(File.join(File.dirname(__FILE__), "charting/*.rb")).each { |f| require_dependency f } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
module ManageIQ | ||
module Reporting | ||
class C3Charting < ManageIQ::Reporting::Charting | ||
# for Charting.detect_available_plugin | ||
def self.available? | ||
true | ||
end | ||
|
||
# for Charting.detect_available_plugin | ||
def self.priority | ||
1000 | ||
end | ||
|
||
# backend identifier | ||
def backend | ||
:c3 | ||
end | ||
|
||
# format for rails' render | ||
def render_format | ||
:json | ||
end | ||
|
||
# formatter for Rupport::Controller#render - see lib/report_formatter/... | ||
def format | ||
:c3 | ||
end | ||
|
||
# called from each ApplicationController instance | ||
def load_helpers(klass) | ||
klass.instance_eval do | ||
helper ManageIQ::Reporting::Formatter::C3Helper | ||
end | ||
end | ||
|
||
def data_ok?(data) | ||
obj = YAML.load(data) | ||
!!obj && obj.kind_of?(Hash) && !obj[:options] | ||
rescue Psych::SyntaxError, ArgumentError | ||
false | ||
end | ||
|
||
def sample_chart(_options, _report_theme) | ||
sample = { | ||
:data => { | ||
:axis => {}, | ||
:tooltip => {}, | ||
:columns => [ | ||
['data1', 30, 200, 100, 400, 150, 250], | ||
['data2', 50, 20, 10, 40, 15, 25], | ||
['data3', 10, 25, 10, 250, 10, 30] | ||
], | ||
}, | ||
:miqChart => _options[:graph_type], | ||
:miq => { :zoomed => false } | ||
} | ||
sample[:data][:groups] = [['data1','data2', 'data3']] if _options[:graph_type].include? 'Stacked' | ||
sample | ||
end | ||
|
||
def js_load_statement(delayed = false) | ||
delayed ? 'setTimeout(function(){ load_c3_charts(); }, 100);' : 'load_c3_charts();' | ||
end | ||
|
||
# list of available chart types - in options_for_select format | ||
def chart_names_for_select | ||
CHART_NAMES | ||
end | ||
|
||
# list of themes - in options_for_select format | ||
def chart_themes_for_select | ||
[%w(Default default)] | ||
end | ||
|
||
def serialized(data) | ||
data.try(:to_yaml) | ||
end | ||
|
||
def deserialized(data) | ||
YAML.load(data) | ||
end | ||
|
||
CHART_NAMES = [ | ||
["Bars (2D)", "Bar"], | ||
["Bars, Stacked (2D)", "StackedBar"], | ||
["Columns (2D)", "Column"], | ||
["Columns, Stacked (2D)", "StackedColumn"], | ||
["Donut (2D)", "Donut"], | ||
["Pie (2D)", "Pie"], | ||
["Line (2D)", "Line"], | ||
["Area (2D)", "Area"], | ||
["Area, Stacked (2D)", "StackedArea"], | ||
] | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
include ActionView::Helpers::NumberHelper | ||
|
||
require_dependency 'manageiq/reporting/formatter/report_renderer' | ||
require_dependency 'manageiq/reporting/formatter/c3' | ||
require_dependency 'manageiq/reporting/formatter/converter' | ||
require_dependency 'manageiq/reporting/formatter/html' | ||
require_dependency 'manageiq/reporting/formatter/text' | ||
require_dependency 'manageiq/reporting/formatter/timeline' | ||
|
||
module ManageIQ | ||
module Reporting | ||
module Formatter | ||
BLANK_VALUE = "Unknown" # Chart constant for nil or blank key values | ||
CRLF = "\r\n" | ||
LEGEND_LENGTH = 11 # Top legend text limit | ||
LABEL_LENGTH = 21 # Chart label text limit | ||
end | ||
end | ||
end | ||
|
||
# Deprecate the constants within ReportFormatter with a helpful replacement. | ||
module ReportFormatter | ||
include ActiveSupport::Deprecation::DeprecatedConstantAccessor | ||
deprecate_constant 'BLANK_VALUE', 'ManageIQ::Reporting::Formatter::BLANK_VALUE' | ||
deprecate_constant 'CRLF', 'ManageIQ::Reporting::Formatter::CRLF' | ||
deprecate_constant 'LABEL_LENGTH', 'ManageIQ::Reporting::Formatter::LABEL_LENGTH' | ||
deprecate_constant 'LEGEND_LENGTH', 'ManageIQ::Reporting::Formatter::LEGEND_LENGTH' | ||
|
||
deprecate_constant 'C3Formatter', 'ManageIQ::Reporting::Formatter::C3' | ||
deprecate_constant 'C3Series', 'ManageIQ::Reporting::Formatter::C3Series' | ||
deprecate_constant 'C3Charting', 'ManageIQ::Reporting::Formatter::C3Charting' | ||
deprecate_constant 'ChartCommon', 'ManageIQ::Reporting::Formatter::ChartCommon' | ||
deprecate_constant 'Converter', 'ManageIQ::Reporting::Formatter::Converter' | ||
deprecate_constant 'ReportHTML', 'ManageIQ::Reporting::Formatter::HTML' | ||
deprecate_constant 'ReportRenderer', 'ManageIQ::Reporting::Formatter::ReportRenderer' | ||
deprecate_constant 'ReportText', 'ManageIQ::Reporting::Formatter::Text' | ||
deprecate_constant 'ReportTimeline', 'ManageIQ::Reporting::Formatter::Timeline' | ||
deprecate_constant 'TimelineMessage', 'ManageIQ::Reporting::Formatter::TimelineMessage' | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
require_dependency 'manageiq/reporting/formatter/c3_series' | ||
|
||
module ManageIQ | ||
module Reporting | ||
module Formatter | ||
class C3 < Ruport::Formatter | ||
include ActionView::Helpers::UrlHelper | ||
include ChartCommon | ||
include MiqReport::Formatting | ||
renders :c3, :for => ReportRenderer | ||
|
||
# series handling methods | ||
def series_class | ||
ManageIQ::Reporting::Formatter::C3Series | ||
end | ||
|
||
CONVERT_TYPES = { | ||
"ColumnThreed" => "Column", | ||
"ParallelThreedColumn" => "Column", | ||
"StackedThreedColumn" => "StackedColumn", | ||
"PieThreed" => "Pie", | ||
"AreaThreed" => "Area", | ||
"StackedAreaThreed" => "StackedArea" | ||
} | ||
def add_series(label, data) | ||
@counter ||= 0 | ||
@counter += 1 | ||
series_id = @counter.to_s | ||
limit = pie_type? ? LEGEND_LENGTH : LABEL_LENGTH | ||
|
||
if chart_is_2d? | ||
mri.chart[:data][:columns] << [series_id, *data.map { |a| a[:value] }] | ||
mri.chart[:data][:names][series_id] = slice_legend(_(label), limit) | ||
mri.chart[:miq][:name_table][series_id] = label | ||
else | ||
data.each_with_index do |a, index| | ||
id = index.to_s | ||
mri.chart[:data][:columns].push([id, a[:value]]) | ||
mri.chart[:data][:names][id] = slice_legend(_(a[:tooltip]), limit) | ||
mri.chart[:miq][:name_table][id] = a[:tooltip] | ||
end | ||
end | ||
|
||
if chart_is_stacked? | ||
mri.chart[:data][:groups][0] << series_id | ||
end | ||
end | ||
|
||
def add_axis_category_text(categories) | ||
if chart_is_2d? | ||
category_labels = categories.collect { |c| c.kind_of?(Array) ? c.first : c } | ||
limit = pie_type? ? LEGEND_LENGTH : LABEL_LENGTH | ||
mri.chart[:axis][:x][:categories] = category_labels.collect { |c| slice_legend(c, limit) } | ||
mri.chart[:miq][:category_table] = category_labels | ||
end | ||
end | ||
|
||
# report building methods | ||
def build_document_header | ||
super | ||
type = c3_convert_type(mri.graph[:type].to_s) | ||
mri.chart = { | ||
:miqChart => type, | ||
:data => {:columns => [], :names => {}, :empty => {:label => {:text => _('No data available.')}}}, | ||
:axis => {:x => {:tick => {}}, :y => {:tick => {}, :padding => {:bottom => 0}}}, | ||
:tooltip => {:format => {}}, | ||
:miq => {:name_table => {}, :category_table => {}}, | ||
:legend => {} | ||
} | ||
|
||
if chart_is_2d? | ||
mri.chart[:axis][:x] = { | ||
:categories => [], | ||
:tick => {} | ||
} | ||
end | ||
|
||
if chart_is_stacked? | ||
mri.chart[:data][:groups] = [[]] | ||
end | ||
|
||
# chart is numeric | ||
if mri.graph[:mode] == 'values' | ||
custom_format = Array(mri[:col_formats])[Array(mri[:col_order]).index(raw_column_name)] | ||
format, options = javascript_format(mri.graph[:column].split(/(?<!:):(?!:)/)[0], custom_format) | ||
|
||
if format | ||
axis_formatter = {:function => format, :options => options} | ||
mri.chart[:axis][:y] = {:tick => {:format => axis_formatter}} | ||
end | ||
end | ||
|
||
# C&U chart | ||
if graph_options[:chart_type] == :performance | ||
unless mri.graph[:type] == 'Donut' || mri.graph[:type] == 'Pie' | ||
mri.chart[:legend] = {:position => 'bottom'} | ||
end | ||
|
||
return if mri.graph[:columns].blank? | ||
column = grouped_by_tag_category? ? mri.graph[:columns][0].split(/_+/)[0..-2].join('_') : mri.graph[:columns][0] | ||
format, options = javascript_format(column, nil) | ||
return unless format | ||
|
||
axis_formatter = {:function => format, :options => options} | ||
mri.chart[:axis][:y][:tick] = {:format => axis_formatter} | ||
mri.chart[:miq][:format] = axis_formatter | ||
end | ||
end | ||
|
||
def c3_convert_type(type) | ||
CONVERT_TYPES[type] || type | ||
end | ||
|
||
def chart_is_2d? | ||
['Bar', 'Column', 'StackedBar', 'StackedColumn', 'Line', 'Area', 'StackedArea'].include?(c3_convert_type(mri.graph[:type])) | ||
end | ||
|
||
def chart_is_stacked? | ||
%w(StackedBar StackedColumn StackedArea).include?(mri.graph[:type]) | ||
end | ||
|
||
# change structure of chart JSON to performance chart with timeseries data | ||
def build_performance_chart_area(maxcols) | ||
super | ||
change_structure_to_timeseries | ||
end | ||
|
||
def no_records_found_chart(*) | ||
mri.chart = { | ||
:axis => {:y => {:show => false}}, | ||
:data => {:columns => [], :empty => {:label => {:text => _('No data available.')}}}, | ||
:miq => {:empty => true}, | ||
} | ||
end | ||
|
||
def finalize_document | ||
mri.chart | ||
end | ||
|
||
private | ||
|
||
# change structure of hash from standard chart to timeseries chart | ||
def change_structure_to_timeseries | ||
# add 'x' as first element and move mri.chart[:axis][:x][:categories] to mri.chart[:data][:columns] as first column | ||
x = mri.chart[:axis][:x][:categories] | ||
x.unshift('x') | ||
mri.chart[:data][:columns].unshift(x) | ||
mri.chart[:data][:x] = 'x' | ||
# set x axis type to timeseries and remove categories | ||
mri.chart[:axis][:x] = {:type => 'timeseries', :tick => {}} | ||
# set flag for performance chart | ||
mri.chart[:miq][:performance_chart] = true | ||
# this conditions are taken from build_performance_chart_area method from chart_commons.rb | ||
if mri.db.include?("Daily") || (mri.where_clause && mri.where_clause.include?("daily")) | ||
# set format for parsing | ||
mri.chart[:data][:xFormat] = '%m/%d' | ||
# set format for labels | ||
mri.chart[:axis][:x][:tick][:format] = '%m/%d' | ||
elsif mri.extras[:realtime] == true | ||
mri.chart[:data][:xFormat] = '%H:%M:%S' | ||
mri.chart[:axis][:x][:tick][:format] = '%H:%M:%S' | ||
else | ||
mri.chart[:data][:xFormat] = '%H:%M' | ||
mri.chart[:axis][:x][:tick][:format] = '%H:%M' | ||
end | ||
end | ||
|
||
def build_reporting_chart(_maxcols) | ||
mri.chart[:miq][:expand_tooltip] = true | ||
super | ||
end | ||
|
||
def build_reporting_chart_numeric(_maxcols) | ||
mri.chart[:miq][:expand_tooltip] = true | ||
super | ||
end | ||
|
||
def build_performance_chart_pie(_maxcols) | ||
mri.chart[:miq][:expand_tooltip] = true | ||
super | ||
end | ||
|
||
def grouped_by_tag_category? | ||
!!(mri.performance && mri.performance.fetch_path(:group_by_category)) | ||
end | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.