Provide chart functions
This commit is contained in:
parent
e7a1aa1eab
commit
5275a921f5
68
app/models/additionals_chart.rb
Normal file
68
app/models/additionals_chart.rb
Normal 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
|
5
app/overrides/projects/show.rb
Normal file
5
app/overrides/projects/show.rb
Normal 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'
|
@ -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',
|
||||
|
17
app/views/additionals/_chart_table_values.html.slim
Normal file
17
app/views/additionals/_chart_table_values.html.slim
Normal 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]
|
46
app/views/additionals/charts/_pie_with_value_table.slim
Normal file
46
app/views/additionals/charts/_pie_with_value_table.slim
Normal 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',
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
1
app/views/hooks/_view_projects_show_bottom.html.slim
Normal file
1
app/views/hooks/_view_projects_show_bottom.html.slim
Normal file
@ -0,0 +1 @@
|
||||
= call_hook(:view_projects_show_bottom, project: @project)
|
1
app/views/hooks/_view_welcome_index_bottom.html.slim
Normal file
1
app/views/hooks/_view_welcome_index_bottom.html.slim
Normal file
@ -0,0 +1 @@
|
||||
= call_hook(:view_welcome_index_bottom)
|
@ -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;
|
||||
}
|
||||
|
@ -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}"
|
||||
|
@ -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}"
|
||||
|
@ -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}"
|
||||
|
@ -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}"
|
||||
|
@ -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}"
|
||||
|
@ -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}"
|
||||
|
@ -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}"
|
||||
|
@ -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}"
|
||||
|
@ -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}"
|
||||
|
@ -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}"
|
||||
|
@ -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)
|
||||
|
@ -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')
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user