Added support for visualise the graph using a Pie chart in the query tool. Fixes #7487

This commit is contained in:
Akshay Joshi 2022-07-05 10:47:17 +05:30
parent 239e3bb6f1
commit 8b62cd1f04
6 changed files with 112 additions and 46 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

View File

@ -331,8 +331,8 @@ Graph Visualiser Panel
**********************
Click the Graph Visualiser button in the toolbar to generate the *Graphs* of
the query results. The graph visualiser currently supports only Line Charts,
but more charts (Bar, Stacked Bar, Pie...) will be added soon.
the query results. The graph visualiser supports Line Charts, Stacked Line Charts,
Bar Charts, Stacked Bar Charts, and Pie Charts.
.. image:: images/query_graph_visualiser_panel.png
:alt: Query tool graph visualiser panel
@ -427,6 +427,18 @@ clicking on the 'Generate' button.
:alt: Query tool graph visualiser stacked bar chart
:align: center
Pie Chart
=========
The *Pie Chart* can be generated by selecting the 'Pie Chart'
from the Graph Type drop-down, selecting the Label and Value, and
clicking on the 'Generate' button.
.. image:: images/query_pie_chart.png
:alt: Query tool graph visualiser pie chart
:align: center
Connection Status
*****************

View File

@ -10,6 +10,7 @@ New features
************
| `Issue #7486 <https://redmine.postgresql.org/issues/7486>`_ - Added support for visualizing the graphs using Stacked Line, Bar, and Stacked Bar charts in the query tool.
| `Issue #7487 <https://redmine.postgresql.org/issues/7487>`_ - Added support for visualise the graph using a Pie chart in the query tool.
Housekeeping
************

View File

@ -100,21 +100,9 @@ const defaultOptions = {
}
};
const stackedOptions = {
scales: {
x: {
stacked: true,
},
y: {
stacked: true,
},
}
};
export default function BaseChart({type='line', id, options, data, redraw=false, plugins={}, ...props}) {
const chartRef = React.useRef();
const chartObj = React.useRef();
let optionsMerged = _.merge(defaultOptions, options);
const initChart = function() {
Chart.register(...registerables);
@ -125,7 +113,7 @@ export default function BaseChart({type='line', id, options, data, redraw=false,
type: type,
data: data,
plugins: [plugins],
options: optionsMerged,
options: options,
});
props.onInit && props.onInit(chartObj.current);
};
@ -149,7 +137,7 @@ export default function BaseChart({type='line', id, options, data, redraw=false,
...data.datasets[i],
};
}
chartObj.current.options = optionsMerged;
chartObj.current.options = options;
chartObj.current.update(props.updateOptions || {});
props.onUpdate && props.onUpdate(chartObj.current);
}
@ -179,11 +167,22 @@ BaseChart.propTypes = {
plugins: PropTypes.object,
};
const stackedOptions = {
scales: {
x: {
stacked: true,
},
y: {
stacked: true,
},
}
};
export function LineChart({stacked, options, ...props}) {
// if stacked true then merge the stacked specific options.
options = stacked ? _.merge(options, stackedOptions) : options;
let optionsMerged = _.merge(defaultOptions, options, stacked ? stackedOptions : {});
return (
<BaseChart {...props} options={options} type='line'/>
<BaseChart {...props} options={optionsMerged} type='line'/>
);
}
LineChart.propTypes = {
@ -193,9 +192,9 @@ LineChart.propTypes = {
export function BarChart({stacked, options, ...props}) {
// if stacked true then merge the stacked specific options.
options = stacked ? _.merge(options, stackedOptions) : options;
let optionsMerged = _.merge(defaultOptions, options, stacked ? stackedOptions : {});
return (
<BaseChart {...props} options={options} type='bar'/>
<BaseChart {...props} options={optionsMerged} type='bar'/>
);
}
BarChart.propTypes = {
@ -203,6 +202,20 @@ BarChart.propTypes = {
stacked: PropTypes.bool
};
export function PieChart({options, ...props}) {
let optionsMerged = _.merge({
responsive: true,
maintainAspectRatio: false,
normalized: true
}, options);
return (
<BaseChart {...props} options={optionsMerged} type='pie'/>
);
}
PieChart.propTypes = {
options: PropTypes.object
};
export function ConvertHexToRGBA(hex, opacity) {
const tempHex = hex.replace('#', '');
const r = parseInt(tempHex.substring(0, 2), 16);

View File

@ -20,7 +20,7 @@ import ZoomOutMapIcon from '@material-ui/icons/ZoomOutMap';
import SaveAltIcon from '@material-ui/icons/SaveAlt';
import { InputSelect } from '../../../../../../static/js/components/FormComponents';
import { DefaultButton, PgButtonGroup, PgIconButton} from '../../../../../../static/js/components/Buttons';
import { LineChart, BarChart, DATA_POINT_STYLE, DATA_POINT_SIZE,
import { LineChart, BarChart, PieChart, DATA_POINT_STYLE, DATA_POINT_SIZE,
CHART_THEME_COLORS, CHART_THEME_COLORS_LENGTH, ConvertHexToRGBA} from 'sources/chartjs';
import { QueryToolEventsContext, QueryToolContext } from '../QueryToolComponent';
import { QUERY_TOOL_EVENTS, PANELS } from '../QueryToolConstants';
@ -136,7 +136,10 @@ function GenerateGraph({graphType, graphData, ...props}) {
} else if (graphType == 'B' || graphType == 'SB') {
return <BarChart options={defaultOptions} data={graphData} stacked={graphType == 'SB'}
{...props}/>;
} else {
} else if (graphType == 'P') {
return <PieChart data={graphData} {...props}/>;
}
else {
return null;
}
}
@ -145,7 +148,47 @@ GenerateGraph.propTypes = {
graphData: PropTypes.object,
};
// This function is used to get the data set for the X axis and Y axis
// This function is used to get the dataset for Line Chart and Stacked Line Chart.
function getLineChartData(graphType, rows, colName, colPosition, color, colorIndex, styleIndex, queryToolCtx) {
return {
label: colName,
data: rows.map((r)=>r[colPosition]),
backgroundColor: graphType == 'SL' ? ConvertHexToRGBA(color, 30) : color,
borderColor:color,
pointHitRadius: DATA_POINT_SIZE,
pointHoverRadius: 5,
pointStyle: queryToolCtx.preferences.graphs['use_diff_point_style'] ? DATA_POINT_STYLE[styleIndex] : 'circle',
fill: graphType == 'L' ? false : 'origin',
};
}
// This function is used to get the dataset for Bar Chart and Stacked Bar Chart.
function getBarChartData(rows, colName, colPosition, color) {
return {
label: colName,
data: rows.map((r)=>r[colPosition]),
backgroundColor: color,
borderColor:color
};
}
// This function is used to get the dataset for Pie Chart.
function getPieChartData(rows, colName, colPosition, queryToolCtx) {
let rowCount = -1;
return {
label: colName,
data: rows.map((r)=>r[colPosition]),
backgroundColor: rows.map(()=> {
if (rowCount >= (CHART_THEME_COLORS_LENGTH - 1)) {
rowCount = -1;
}
rowCount = rowCount + 1;
return CHART_THEME_COLORS[queryToolCtx.preferences.misc.theme][rowCount];
}),
};
}
// This function is used to get the graph data set for the X axis and Y axis
function getGraphDataSet(graphType, rows, columns, xaxis, yaxis, queryToolCtx) {
// Function is used to the find the position of the column
function getColumnPosition(colName) {
@ -180,25 +223,14 @@ function getGraphDataSet(graphType, rows, columns, xaxis, yaxis, queryToolCtx) {
}
styleIndex = styleIndex + 1;
if (graphType !== 'L' && graphType !== 'SL') {
return {
label: colName,
data: rows.map((r)=>r[colPosition]),
backgroundColor: color,
borderColor:color
};
if (graphType === 'P') {
return getPieChartData(rows, colName, colPosition, queryToolCtx);
} else if (graphType === 'B' || graphType === 'SB') {
return getBarChartData(rows, colName, colPosition, color);
} else if (graphType === 'L' || graphType === 'SL') {
return getLineChartData(graphType, rows, colName, colPosition, color,
colorIndex, styleIndex, queryToolCtx);
}
return {
label: colName,
data: rows.map((r)=>r[colPosition]),
backgroundColor: graphType == 'SL' ? ConvertHexToRGBA(color, 30) : color,
borderColor:color,
pointHitRadius: DATA_POINT_SIZE,
pointHoverRadius: 5,
pointStyle: queryToolCtx.preferences.graphs['use_diff_point_style'] ? DATA_POINT_STYLE[styleIndex] : 'circle',
fill: graphType == 'L' ? false : 'origin',
};
}),
};
}
@ -253,7 +285,7 @@ export function GraphVisualiser({initColumns}) {
clearTimeout(timeoutId);
if(LayoutHelper.isTabVisible(queryToolCtx.docker, PANELS.GRAPH_VISUALISER)) {
timeoutId = setTimeout(function () {
setGraphHeight(contentRef.current.offsetHeight);
setGraphHeight(contentRef.current.offsetHeight - 8);
}, 300);
}
});
@ -272,6 +304,13 @@ export function GraphVisualiser({initColumns}) {
};
}, []);
// Reset the Y axis if graph type is Pie Chart.
useEffect(()=>{
if (graphType === 'P') {
setYAxis('');
}
}, [graphType]);
// Generate button callback
const onGenerate = async ()=>{
setLoaderText(gettext('Fetching all the records...'));
@ -290,7 +329,7 @@ export function GraphVisualiser({initColumns}) {
setLoaderText(gettext('Rendering data points...'));
// Set the Graph Data
setGraphData(
(prev)=> [getGraphDataSet(graphType, res.data.data.result, columns, xaxis, yaxis, queryToolCtx), prev[1] + 1]
(prev)=> [getGraphDataSet(graphType, res.data.data.result, columns, xaxis, _.isArray(yaxis) ? yaxis : [yaxis] , queryToolCtx), prev[1] + 1]
);
setLoaderText('');
@ -328,7 +367,7 @@ export function GraphVisualiser({initColumns}) {
<Box className={classes.topContainer}>
<Box className={classes.displayFlex}>
<Box className={classes.displayFlex}>
<span className={classes.spanLabel}>{gettext('X Axis')}</span>
<span className={classes.spanLabel}>{graphType != 'P' ? gettext('X Axis') : gettext('Label')}</span>
<InputSelect className={classes.selectCtrl} options={xAxisOptions}
onChange={(v)=>setXAxis(v)} value={xaxis} optionsReloadBasis={optionsReload}/>
</Box>
@ -340,6 +379,7 @@ export function GraphVisualiser({initColumns}) {
{label: gettext('Stacked Line Chart'), value: 'SL'},
{label: gettext('Bar Chart'), value: 'B'},
{label: gettext('Stacked Bar Chart'), value: 'SB'},
{label: gettext('Pie Chart'), value: 'P'},
]} onChange={(v)=>setGraphType(v)} value={graphType} />
</Box>
<DefaultButton onClick={onGenerate} startIcon={<ShowChartRoundedIcon />}
@ -348,8 +388,8 @@ export function GraphVisualiser({initColumns}) {
</DefaultButton>
</Box>
<Box className={classes.displayFlex}>
<span className={classes.spanLabel}>{gettext('Y Axis')}</span>
<InputSelect className={classes.selectCtrl} controlProps={{'multiple': true, allowSelectAll: true}}
<span className={classes.spanLabel}>{graphType != 'P' ? gettext('Y Axis') : gettext('Value')}</span>
<InputSelect className={classes.selectCtrl} controlProps={{'multiple': graphType != 'P', allowSelectAll: graphType != 'P'}}
options={yAxisOptions} onChange={(v)=>setYAxis(v)} value={yaxis} optionsReloadBasis={optionsReload}/>
</Box>
</Box>