Skip to content

Commit

Permalink
MantisGraph plugin improvements
Browse files Browse the repository at this point in the history
Fixes the following issues

- #25522 - MantisGraph: limit number of slices in By Category pie chart
- #25523 - MantisGraph: improve handling of colors in Pie charts
- #25524 - MantisGraph: improve display of By Category Bar chart
- Code cleanup

Merge PR #1472
  • Loading branch information
dregad committed Mar 2, 2019
2 parents 0b6f18a + 114435a commit 65464fb
Show file tree
Hide file tree
Showing 9 changed files with 127 additions and 60 deletions.
18 changes: 9 additions & 9 deletions core/helper_api.php
Expand Up @@ -107,21 +107,21 @@ function helper_array_transpose( array $p_array ) {

/**
* get the color string for the given status, user and project
* @param integer $p_status Status value.
* @param integer|null $p_user User id, defaults to null (all users).
* @param integer|null $p_project Project id, defaults to null (all projects).
* @param integer $p_status Status value.
* @param integer|null $p_user User id, defaults to null (all users).
* @param integer|null $p_project Project id, defaults to null (all projects).
* @param string $p_default_color Fallback color in case status is not found (defaults to white).
* @return string
*/
function get_status_color( $p_status, $p_user = null, $p_project = null ) {
$t_status_label = MantisEnum::getLabel( config_get( 'status_enum_string', null, $p_user, $p_project ), $p_status );
function get_status_color( $p_status, $p_user = null, $p_project = null, $p_default_color = '#ffffff' ) {
$t_status_enum = config_get( 'status_enum_string', null, $p_user, $p_project );
$t_status_colors = config_get( 'status_colors', null, $p_user, $p_project );
$t_color = '#ffffff';
$t_status_label = MantisEnum::getLabel( $t_status_enum, $p_status );

if( isset( $t_status_colors[$t_status_label] ) ) {
$t_color = $t_status_colors[$t_status_label];
return $t_status_colors[$t_status_label];
}

return $t_color;
return $p_default_color;
}

/**
Expand Down
2 changes: 2 additions & 0 deletions library/README.md
Expand Up @@ -24,6 +24,7 @@ moment.js | 2.15.2 | unpatched
bootstrap-datetimepicker | 4.17.47 | unpatched
dropzone.js | 5.5.0 | unpatched
chart.js | 2.7.3 | unpatched
chartjs-plugin-colorschemes | 0.3.0 | unpatched
typeahead.js | 1.1.1 | unpatched
list.js | 1.4.1 | unpatched

Expand All @@ -46,5 +47,6 @@ moment.js | https://momentjs.com/ - https://github.com/moment/moment/
datetimepicker | https://github.com/Eonasdan/bootstrap-datetimepicker
dropzone.js | http://www.dropzonejs.com/ - https://github.com/enyo/dropzone
chart.js | http://www.chartjs.org/ - https://github.com/chartjs/Chart.js
chartjs-plugin-colorschemes | https://github.com/nagix/chartjs-plugin-colorschemes/
typeahead.js | https://github.com/twitter/typeahead.js
list.js | http://listjs.com/ - https://github.com/javve/list.js
10 changes: 10 additions & 0 deletions plugins/MantisGraph/MantisGraph.php
Expand Up @@ -23,6 +23,13 @@
*/
class MantisGraphPlugin extends MantisPlugin {

/**
* ChartJS colorschemes plugin
* @see https://nagix.github.io/chartjs-plugin-colorschemes/
*/
const CHARTJS_COLORSCHEMES_VERSION = '0.3.0';
const CHARTJS_COLORSCHEMES_HASH = 'sha256-x/VBrVFabQZyhV1G+oKCy98gkqf/pW5aVAhaBRxJ/x4=';

/**
* A method that populates the plugin information and minimum requirements.
* @return void
Expand Down Expand Up @@ -109,6 +116,7 @@ function routes( $p_event_name, $p_event_args ) {
function csp_headers() {
if( config_get_global( 'cdn_enabled' ) == ON ) {
http_csp_add( 'script-src', 'https://cdnjs.cloudflare.com' );
http_csp_add( 'script-src', 'https://cdn.jsdelivr.net' );
}
}

Expand All @@ -134,9 +142,11 @@ function resources() {
if( config_get_global( 'cdn_enabled' ) == ON ) {
html_javascript_cdn_link('https://cdnjs.cloudflare.com/ajax/libs/Chart.js/' . CHARTJS_VERSION . '/Chart.min.js', CHARTJS_HASH);
html_javascript_cdn_link('https://cdnjs.cloudflare.com/ajax/libs/Chart.js/' . CHARTJS_VERSION . '/Chart.bundle.min.js', CHARTJSBUNDLE_HASH);
html_javascript_cdn_link('https://cdn.jsdelivr.net/npm/chartjs-plugin-colorschemes@' . self::CHARTJS_COLORSCHEMES_VERSION . '/dist/chartjs-plugin-colorschemes.min.js', self::CHARTJS_COLORSCHEMES_HASH );
} else {
echo '<script type="text/javascript" src="' . plugin_file('Chart-' . CHARTJS_VERSION . '.min.js') . '"></script>';
echo '<script type="text/javascript" src="' . plugin_file('Chart.bundle-' . CHARTJS_VERSION . '.min.js') . '"></script>';
echo '<script type="text/javascript" src="' . plugin_file( 'chartjs-plugin-colorschemes.min.js' ) . '"></script>';
}
echo '<script type="text/javascript" src="' . plugin_file("MantisGraph.js") . '"></script>';
}
Expand Down
80 changes: 43 additions & 37 deletions plugins/MantisGraph/core/graph_api.php
Expand Up @@ -69,15 +69,16 @@ function graph_colors_to_rgbas( array $p_colors, $p_alpha ) {

/**
* Gets an array of html colors that corresponds to statuses.
* @param array $p_metrics Data set to return colors for (with status labels as keys)
* @return array An array similar to the status_colors config ordered by status enum codes.
*/
function graph_status_colors_to_colors() {
$t_status_enum = config_get( 'status_enum_string' );
$t_statuses = MantisEnum::getValues( $t_status_enum );
function graph_status_colors_to_colors( $p_metrics = array() ) {
$t_colors = array();

foreach( $t_statuses as $t_status ) {
$t_colors[] = get_status_color( $t_status );
# The metrics contain localized status, so we need an extra lookup
# to retrieve the id before we can get the color code
$t_status_lookup = MantisEnum::getAssocArrayIndexedByLabels( lang_get( 'status_enum_string' ) );
foreach( array_keys( $p_metrics ) as $t_label ) {
$t_colors[] = get_status_color( $t_status_lookup[$t_label] , null, null, '#e5e5e5' );
}

return $t_colors;
Expand All @@ -88,48 +89,60 @@ function graph_status_colors_to_colors() {
*
* @param array $p_metrics Graph Data.
* @param integer $p_wfactor Width factor for graph chart. Eg: 2 to make it double wide
* @param bool $p_horiz True for horizontal bars, defaults to false (vertical)
* @return void
*/
function graph_bar( array $p_metrics, $p_wfactor = 1 ) {
function graph_bar( array $p_metrics, $p_wfactor = 1, $p_horiz = false ) {
static $s_id = 0;

$s_id++;
$t_id = $p_horiz ? 'horizbarchart' : 'barchart';
$t_json_labels = json_encode( array_keys( $p_metrics ) );
$t_json_values = json_encode( array_values( $p_metrics ) );

$t_width = 500 * $p_wfactor;
$t_height = 400;

?>
<canvas id="barchart<?php echo $s_id ?>" width="<?php echo $t_width ?>" height="<?php echo $t_height ?>"
<canvas id="<?php echo $t_id, $s_id ?>"
width="<?php echo $t_width ?>" height="<?php echo $t_height ?>"
data-labels="<?php echo htmlspecialchars( $t_json_labels, ENT_QUOTES ) ?>"
data-values="<?php echo htmlspecialchars( $t_json_values, ENT_QUOTES ) ?>" />
data-values="<?php echo htmlspecialchars( $t_json_values, ENT_QUOTES ) ?>">
</canvas>
<?php
}

/**
* Function that displays pie charts
*
* @param array $p_metrics Graph Data.
* @param array $p_metrics Graph Data.
* @param bool $p_mantis_colors True to use colors defined in Mantis config
* {@see $g_status_colors}. By default use
* standard color scheme
*
* @return void
*/
function graph_pie( array $p_metrics ) {
function graph_pie( array $p_metrics, $p_mantis_colors = false ) {
static $s_id = 0;

$s_id++;

$t_json_labels = json_encode( array_keys( $p_metrics ) );
$t_json_values = json_encode( array_values( $p_metrics ) );

$t_colors = graph_status_colors_to_colors();
$t_background_colors = graph_colors_to_rgbas( $t_colors, 1.0 );
$t_border_colors = graph_colors_to_rgbas( $t_colors, 1 );
?>
<canvas id="piechart<?php echo $s_id ?>" width="500" height="400"
<canvas id="piechart<?php echo $s_id ?>"
width="500" height="400"
data-labels="<?php echo htmlspecialchars( $t_json_labels, ENT_QUOTES ) ?>"
data-values="<?php echo htmlspecialchars( $t_json_values, ENT_QUOTES ) ?>"
data-background-colors="[<?php echo htmlspecialchars( $t_background_colors, ENT_QUOTES ) ?>]"
data-border-colors="[<?php echo htmlspecialchars( $t_border_colors, ENT_QUOTES ) ?>]" />
<?php
if( $p_mantis_colors ) {
$t_colors = graph_colors_to_rgbas( graph_status_colors_to_colors( $p_metrics ), 1.0 );
?>
data-colors="[<?php echo htmlspecialchars( $t_colors, ENT_QUOTES ) ?>]"
<?php } ?>
>
</canvas>
<?php
}

Expand All @@ -149,18 +162,9 @@ function graph_cumulative_bydate( array $p_metrics, $p_wfactor = 1 ) {
$t_formatted_labels = array_map( function($label) { return date( 'Ymd', $label ); }, $t_labels );
$t_json_labels = json_encode( $t_formatted_labels );

$t_values = array_values( $p_metrics[0] );
$t_opened_values = json_encode( $t_values );

$t_values = array_values( $p_metrics[1] );
$t_resolved_values = json_encode( $t_values );

$t_values = array_values( $p_metrics[2] );
$t_still_open_values = json_encode( $t_values );

$t_colors = graph_status_colors_to_colors();
$t_background_colors = graph_colors_to_rgbas( $t_colors, 0.2 );
$t_border_colors = graph_colors_to_rgbas( $t_colors, 1 );
$t_opened_values = json_encode( array_values( $p_metrics[0] ) );
$t_resolved_values = json_encode( array_values( $p_metrics[1] ) );
$t_still_open_values = json_encode( array_values( $p_metrics[2] ) );

$t_legend_opened = plugin_lang_get( 'legend_reported' );
$t_legend_resolved = plugin_lang_get( 'legend_resolved' );
Expand All @@ -169,14 +173,16 @@ function graph_cumulative_bydate( array $p_metrics, $p_wfactor = 1 ) {
$t_width = 500 * $p_wfactor;
$t_height = 400;
?>
<canvas id="linebydate<?php echo $s_id ?>" width="<?php echo $t_width ?>" height="<?php echo $t_height ?>"
data-labels="<?php echo htmlspecialchars( $t_json_labels, ENT_QUOTES ) ?>"
data-opened-label="<?php echo $t_legend_opened ?>"
data-opened-values="<?php echo htmlspecialchars( $t_opened_values, ENT_QUOTES ) ?>"
data-resolved-label="<?php echo $t_legend_resolved ?>"
data-resolved-values="<?php echo htmlspecialchars( $t_resolved_values, ENT_QUOTES ) ?>"
data-still-open-label="<?php echo $t_legend_still_open ?>"
data-still-open-values="<?php echo htmlspecialchars( $t_still_open_values, ENT_QUOTES ) ?>" />
<canvas id="linebydate<?php echo $s_id ?>"
width="<?php echo $t_width ?>" height="<?php echo $t_height ?>"
data-labels="<?php echo htmlspecialchars( $t_json_labels, ENT_QUOTES ) ?>"
data-opened-label="<?php echo $t_legend_opened ?>"
data-opened-values="<?php echo htmlspecialchars( $t_opened_values, ENT_QUOTES ) ?>"
data-resolved-label="<?php echo $t_legend_resolved ?>"
data-resolved-values="<?php echo htmlspecialchars( $t_resolved_values, ENT_QUOTES ) ?>"
data-still-open-label="<?php echo $t_legend_still_open ?>"
data-still-open-values="<?php echo htmlspecialchars( $t_still_open_values, ENT_QUOTES ) ?>">
</canvas>
<?php

}
Expand Down
31 changes: 21 additions & 10 deletions plugins/MantisGraph/files/MantisGraph.js
@@ -1,14 +1,20 @@
/* jshint -W097, -W031 */
/* globals Chart */
"use strict";

$(document).ready( function() {
$("canvas[id^='barchart']").each( function() {
// Default color scheme
Chart.defaults.global.plugins.colorschemes.scheme = 'tableau.Classic20';

$("canvas[id*='barchart']").each( function() {
var type = this.id.substr(0,8) === 'barchart' ? 'bar' : 'horizontalBar';
new Chart( $(this), {
type: 'bar',
type: type,
data: {
labels: $(this).data('labels'),
datasets: [{
label: '# of issues',
data: $(this).data('values'),
backgroundColor: 'rgba(252, 189, 189, 0.7)',
borderColor: 'rgba(252, 189, 189, 1)',
borderWidth: 1
}]
},
Expand Down Expand Up @@ -38,8 +44,8 @@ $(document).ready( function() {
datasets: [{
label: '# of issues',
data: $(this).data('values'),
backgroundColor: $(this).data('background-colors'),
borderColor: $(this).data('border-colors'),
backgroundColor: $(this).data('colors'),
borderColor: $(this).data('colors'),
borderWidth: 1
}]
}
Expand All @@ -55,19 +61,17 @@ $(document).ready( function() {
datasets: [
{
label: $(this).data('opened-label'),
backgroundColor: 'rgba(255, 158, 158, 0.5)',
data: $(this).data('opened-values')
},
{
label: $(this).data('resolved-label'),
backgroundColor: 'rgba(49, 196, 110, 0.5)',
data: $(this).data('resolved-values')
},
{
label: $(this).data('still-open-label'),
backgroundColor: 'rgba(255, 0, 0, 1)',
data: $(this).data('still-open-values')
},]
}
]
},
options: {
scales: {
Expand All @@ -76,6 +80,13 @@ $(document).ready( function() {
beginAtZero: true
}
}]
},
plugins: {
colorschemes: {
scheme: 'brewer.Set1-3',
reverse: true,
fillAlpha: 0.15
}
}
}
});
Expand Down
11 changes: 11 additions & 0 deletions plugins/MantisGraph/files/chartjs-plugin-colorschemes.min.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions plugins/MantisGraph/lang/strings_english.txt
Expand Up @@ -88,3 +88,4 @@ $s_plugin_MantisGraph_graph_page = 'Graph Issue History';
$s_plugin_MantisGraph_graph_topdev = 'Top Developers by Fixed Issues';
$s_plugin_MantisGraph_graph_opendev = 'Developers by Open Issues';
$s_plugin_MantisGraph_graph_topreporter_fixed = 'Top Reporters by Fixed Issues';
$s_plugin_MantisGraph_other_categories = 'other categories (%d)';
32 changes: 29 additions & 3 deletions plugins/MantisGraph/pages/category_graph.php
Expand Up @@ -34,8 +34,34 @@
print_summary_submenu();

$t_metrics = create_category_summary( $t_filter );
array_multisort( $t_metrics, SORT_DESC, SORT_NUMERIC);

# Dynamically set width ratio between 1 and 0.25 based on number of categories
$t_wfactor = 1 - min( max( count( $t_metrics ), 25 ) - 25, 75 ) / 100;

# Set the maximum number of pie slices displayed and aggregate the rest into
# an "others" category if needed. The number of slices should not be higher
# than the number of available colors in the palette.
$t_num_slices = 20;
$t_pie_metrics = array_slice( $t_metrics, 0, $t_num_slices );
if( count( $t_metrics ) > $t_num_slices ) {
$t_num_slices--;

# Remove last element and replace it with "others"
array_pop( $t_pie_metrics );
$t_others = sprintf(
plugin_lang_get( 'other_categories' ),
count( $t_metrics ) - $t_num_slices
);
$t_pie_metrics[$t_others] = 0;

# Sum remaining categories into "others" slice
foreach( array_slice( $t_metrics, $t_num_slices ) as $t_value ) {
$t_pie_metrics[$t_others] += $t_value;
}
}
?>

<div class="col-md-12 col-xs-12">
<div class="space-10"></div>

Expand All @@ -48,11 +74,11 @@
</div>

<div class="col-md-6 col-xs-12">
<?php graph_bar( $t_metrics ); ?>
<?php graph_bar( $t_metrics, $t_wfactor, true ); ?>
</div>

<div class="col-md-6 col-xs-12">
<?php graph_pie( $t_metrics ); ?>
<?php graph_pie( $t_pie_metrics ); ?>
</div>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion plugins/MantisGraph/pages/status_graph.php
Expand Up @@ -50,7 +50,7 @@
</div>

<div class="col-md-6 col-xs-12">
<?php graph_pie( $t_metrics ); ?>
<?php graph_pie( $t_metrics, true ); ?>
</div>
</div>
</div>
Expand Down

0 comments on commit 65464fb

Please sign in to comment.