Provide chart functions

This commit is contained in:
Alexander Meindl 2019-10-03 17:35:12 +02:00
parent e7a1aa1eab
commit 5275a921f5
22 changed files with 203 additions and 11 deletions

View File

@ -0,0 +1,68 @@
class AdditionalsChart
include Redmine::I18n
MAX_ALLOWED_ELEMENTS = 200
class << self
def color_schema
Redmine::Plugin.installed?('redmine_reporting') ? RedmineReporting.setting(:chart_color_schema) : 'tableau.Classic20'
end
def data
raise 'overwrite it!'
end
# build return value
def build_chart_data(datasets, options = {})
cached_labels = labels
data = { datasets: datasets.to_json,
labels: cached_labels.keys,
label_ids: cached_labels.values }
required_labels = options.key?(:required_labels) ? options.delete(:required_labels) : 2
unless options.key?(:valid)
data[:valid] = cached_labels.any? &&
cached_labels.count >= required_labels &&
cached_labels.count < MAX_ALLOWED_ELEMENTS
end
data[:width] = RedmineReporting::CHART_DEFAULT_WIDTH unless options.key?(:width)
data[:height] = RedmineReporting::CHART_DEFAULT_HEIGHT unless options.key?(:height)
data[:value_link_method] = '_project_issues_path' unless options.key?(:value_link_method)
data[:color_schema] = color_schema
data.merge(options)
end
private
def build_values_without_gaps(data, gap_value = 0)
values = []
labels.each do |label, _label_id|
values << if data.key?(label)
data[label]
else
gap_value
end
end
values
end
def init_labels
@labels = {}
end
def labels
# NOTE: do not sort it, because color changes if user switch language
@labels.to_h
end
def add_label(label, id)
return if @labels.key? label
@labels[label] = id
end
end
end

View File

@ -0,0 +1,5 @@
Deface::Override.new virtual_path: 'projects/show',
name: 'view-projects-show-bottom-hook',
insert_after: 'div.splitcontentright',
original: '8cd4d1b38e8afcb4665dbfea661b7048fbd92cf7',
partial: 'hooks/view_projects_show_bottom'

View File

@ -1,8 +1,8 @@
Deface::Override.new virtual_path: 'welcome/index',
name: 'add-welcome-bottom-content',
name: 'view-welcome-index-bottom-hook',
insert_after: 'div.splitcontentright',
original: 'dd470844bcaa4d7c9dc66e70e6c0c843d42969bf',
partial: 'welcome/overview_bottom'
partial: 'hooks/view_welcome_index_bottom'
Deface::Override.new virtual_path: 'welcome/index',
name: 'add-welcome-top-content',
insert_before: 'div.splitcontentleft',

View File

@ -0,0 +1,17 @@
table.list.issue-report.table-of-values
= title_with_fontawesome(l(:label_table_of_values), 'far fa-list-alt', 'caption')
thead
tr
th = @chart[:label]
th = l(:label_total)
tbody
- @chart[:filters].each do |line|
- if line[:filter]
- options = { set_filter: 1 }.merge(line[:filter])
tr class="#{cycle('odd', 'even')}"
td.name class="#{line[:id].to_s == '0' ? 'summary' : ''}"
- if line[:filter].nil?
= line[:name]
- else
= link_to line[:name], send(@chart[:value_link_method], @project, options)
td = line[:count]

View File

@ -0,0 +1,46 @@
.additionals-chart-wrapper
.additionals-chart-left
canvas id="#{@chart[:id]}" style="width: #{@chart[:width]}px; height: #{@chart[:height]}px;"
.additionals-table-of-values
= render partial: 'additionals/chart_table_values'
.clear-both
javascript:
const pie_chart_#{{@chart[:id]}} = new Chart(document.getElementById("#{@chart[:id]}"), {
type: 'pie',
data: {
label_ids: #{raw json_escape(@chart[:label_ids])},
labels: #{raw json_escape(@chart[:labels])},
datasets: #{raw json_escape(@chart[:datasets])}
},
options: {
responsive: true,
onClick: function(c, i) {
e = i[0];
if (e !== undefined && #{{@chart[:filter_path].present? ? 1 : 0}} == 1 ) {
var activePoints = pie_chart_#{{@chart[:id]}}.getElementAtEvent(c);
var label_id = this.data.label_ids[activePoints[0]._index];
window.open("#{{@chart[:filter_path]}}" + label_id);
}
},
plugins: {
colorschemes: {
scheme: "#{@chart[:color_schema]}",
fillAlpha: 0.8,
},
datalabels: {
formatter: (value, ctx) => {
let sum = 0;
let dataArr = ctx.chart.data.datasets[0].data;
dataArr.map(data => {
sum += data;
});
let percentage = (value*100 / sum).toFixed(0)+"%";
return percentage;
},
color: '#000',
}
}
}
});

View File

@ -0,0 +1 @@
= call_hook(:view_projects_show_bottom, project: @project)

View File

@ -0,0 +1 @@
= call_hook(:view_welcome_index_bottom)

View File

@ -329,6 +329,43 @@ div.additionals-projects li.project.odd {
list-style-position: inside;
}
.additionals-chart-wrapper {
width: 99%;
}
.additionals-chart-left {
float: left;
font: normal 12px sans-serif;
height: 320px;
width: 360px;
}
/* has to be same width as additionals-table-of-values */
.additionals-chart-right {
float: left;
font: normal 12px sans-serif;
height: 320px;
width: 450px;
}
.additionals-table-of-values {
float: left;
font: normal 12px sans-serif;
width: 450px;
}
table.table-of-values {
margin-left: 20px;
}
table.table-of-values td {
padding: 3px;
}
table.table-of-values caption {
font-weight: bold;
}
.additionals-projects ul {
padding-left: 0;
}

View File

@ -170,3 +170,4 @@ de:
label_query_name_search: Nach Name suchen
label_chartjs_colorscheme_info: Farbschema Auflistung
label_chart_color_schema: Charts Farbschema
label_without_value: "Ohne %{value}"

View File

@ -170,3 +170,4 @@ en:
label_query_name_search: Search for name
label_chartjs_colorscheme_info: Color list
label_chart_color_schema: Charts color scheme
label_without_value: "No %{value}"

View File

@ -170,3 +170,4 @@ es:
label_query_name_search: Buscar por nombre
label_chartjs_colorscheme_info: Color list
label_chart_color_schema: Charts color scheme
label_without_value: "No %{value}"

View File

@ -170,3 +170,4 @@ fr:
label_query_name_search: Rechercher un nom
label_chartjs_colorscheme_info: Color list
label_chart_color_schema: Charts color scheme
label_without_value: "No %{value}"

View File

@ -170,3 +170,4 @@ it:
label_query_name_search: Ricerca per nome
label_chartjs_colorscheme_info: Color list
label_chart_color_schema: Charts color scheme
label_without_value: "No %{value}"

View File

@ -170,3 +170,4 @@ ja:
label_query_name_search: Search for name
label_chartjs_colorscheme_info: Color list
label_chart_color_schema: Charts color scheme
label_without_value: "No %{value}"

View File

@ -170,3 +170,4 @@ pl:
label_query_name_search: "Wyszukaj nazwę"
label_chartjs_colorscheme_info: Color list
label_chart_color_schema: Charts color scheme
label_without_value: "No %{value}"

View File

@ -170,3 +170,4 @@ ru:
label_query_name_search: "Поиск имени"
label_chartjs_colorscheme_info: Color list
label_chart_color_schema: Charts color scheme
label_without_value: "No %{value}"

View File

@ -170,3 +170,4 @@
label_query_name_search: Search for name
label_chartjs_colorscheme_info: Color list
label_chart_color_schema: Charts color scheme
label_without_value: "No %{value}"

View File

@ -170,3 +170,4 @@ zh:
label_query_name_search: Search for name
label_chartjs_colorscheme_info: Color list
label_chart_color_schema: Charts color scheme
label_without_value: "No %{value}"

View File

@ -212,6 +212,17 @@ module Additionals
classes.join(' ')
end
def options_with_custom_fields(type, format, current, options = {})
klass = Object.const_get("#{type}CustomField")
fields = []
fields << ["- #{l(:label_disabled)} -", 0] if options[:include_disabled]
klass.sorted.each do |field|
fields << [field.name, field.id] if Array(format).include?(field.field_format)
end
options_for_select(fields, current)
end
private
def additionals_already_loaded(scope, js_name)

View File

@ -17,6 +17,7 @@ module Additionals
render_on(:view_projects_show_right, partial: 'project_overview')
render_on(:view_projects_show_sidebar_bottom, partial: 'additionals/global_sidebar')
render_on(:view_welcome_index_right, partial: 'overview_right')
render_on(:view_welcome_index_bottom, partial: 'additionals_index_bottom')
render_on(:view_my_account_preferences, partial: 'users/autowatch_involved_issue')
render_on(:view_users_form_preferences, partial: 'users/autowatch_involved_issue')
render_on(:view_users_show_contextual, partial: 'users/additionals_contextual')

View File

@ -2,17 +2,12 @@ module Additionals
module Patches
module FormattingHelperPatch
def self.included(base)
base.send(:include, InstanceMethods)
base.class_eval do
alias_method :heads_for_wiki_formatter_without_additionals, :heads_for_wiki_formatter
alias_method :heads_for_wiki_formatter, :heads_for_wiki_formatter_with_additionals
end
base.send(:prepend, InstancOverwriteMethods)
end
module InstanceMethods
def heads_for_wiki_formatter_with_additionals
heads_for_wiki_formatter_without_additionals
module InstancOverwriteMethods
def heads_for_wiki_formatter
super
return if @additionals_macro_list
@additionals_macro_list = AdditionalsMacro.all(filtered: Additionals.setting(:hidden_macros_in_toolbar).to_a,