mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Added support for visualise the graph using a Pie chart in the query tool. Fixes #7487
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 28 KiB |
BIN
docs/en_US/images/query_pie_chart.png
Normal file
BIN
docs/en_US/images/query_pie_chart.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
@@ -331,8 +331,8 @@ Graph Visualiser Panel
|
|||||||
**********************
|
**********************
|
||||||
|
|
||||||
Click the Graph Visualiser button in the toolbar to generate the *Graphs* of
|
Click the Graph Visualiser button in the toolbar to generate the *Graphs* of
|
||||||
the query results. The graph visualiser currently supports only Line Charts,
|
the query results. The graph visualiser supports Line Charts, Stacked Line Charts,
|
||||||
but more charts (Bar, Stacked Bar, Pie...) will be added soon.
|
Bar Charts, Stacked Bar Charts, and Pie Charts.
|
||||||
|
|
||||||
.. image:: images/query_graph_visualiser_panel.png
|
.. image:: images/query_graph_visualiser_panel.png
|
||||||
:alt: Query tool graph visualiser panel
|
:alt: Query tool graph visualiser panel
|
||||||
@@ -427,6 +427,18 @@ clicking on the 'Generate' button.
|
|||||||
:alt: Query tool graph visualiser stacked bar chart
|
:alt: Query tool graph visualiser stacked bar chart
|
||||||
:align: center
|
: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
|
Connection Status
|
||||||
*****************
|
*****************
|
||||||
|
|
||||||
|
|||||||
@@ -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 #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
|
Housekeeping
|
||||||
************
|
************
|
||||||
|
|||||||
@@ -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}) {
|
export default function BaseChart({type='line', id, options, data, redraw=false, plugins={}, ...props}) {
|
||||||
const chartRef = React.useRef();
|
const chartRef = React.useRef();
|
||||||
const chartObj = React.useRef();
|
const chartObj = React.useRef();
|
||||||
let optionsMerged = _.merge(defaultOptions, options);
|
|
||||||
|
|
||||||
const initChart = function() {
|
const initChart = function() {
|
||||||
Chart.register(...registerables);
|
Chart.register(...registerables);
|
||||||
@@ -125,7 +113,7 @@ export default function BaseChart({type='line', id, options, data, redraw=false,
|
|||||||
type: type,
|
type: type,
|
||||||
data: data,
|
data: data,
|
||||||
plugins: [plugins],
|
plugins: [plugins],
|
||||||
options: optionsMerged,
|
options: options,
|
||||||
});
|
});
|
||||||
props.onInit && props.onInit(chartObj.current);
|
props.onInit && props.onInit(chartObj.current);
|
||||||
};
|
};
|
||||||
@@ -149,7 +137,7 @@ export default function BaseChart({type='line', id, options, data, redraw=false,
|
|||||||
...data.datasets[i],
|
...data.datasets[i],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
chartObj.current.options = optionsMerged;
|
chartObj.current.options = options;
|
||||||
chartObj.current.update(props.updateOptions || {});
|
chartObj.current.update(props.updateOptions || {});
|
||||||
props.onUpdate && props.onUpdate(chartObj.current);
|
props.onUpdate && props.onUpdate(chartObj.current);
|
||||||
}
|
}
|
||||||
@@ -179,11 +167,22 @@ BaseChart.propTypes = {
|
|||||||
plugins: PropTypes.object,
|
plugins: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const stackedOptions = {
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
stacked: true,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
stacked: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export function LineChart({stacked, options, ...props}) {
|
export function LineChart({stacked, options, ...props}) {
|
||||||
// if stacked true then merge the stacked specific options.
|
// if stacked true then merge the stacked specific options.
|
||||||
options = stacked ? _.merge(options, stackedOptions) : options;
|
let optionsMerged = _.merge(defaultOptions, options, stacked ? stackedOptions : {});
|
||||||
return (
|
return (
|
||||||
<BaseChart {...props} options={options} type='line'/>
|
<BaseChart {...props} options={optionsMerged} type='line'/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
LineChart.propTypes = {
|
LineChart.propTypes = {
|
||||||
@@ -193,9 +192,9 @@ LineChart.propTypes = {
|
|||||||
|
|
||||||
export function BarChart({stacked, options, ...props}) {
|
export function BarChart({stacked, options, ...props}) {
|
||||||
// if stacked true then merge the stacked specific options.
|
// if stacked true then merge the stacked specific options.
|
||||||
options = stacked ? _.merge(options, stackedOptions) : options;
|
let optionsMerged = _.merge(defaultOptions, options, stacked ? stackedOptions : {});
|
||||||
return (
|
return (
|
||||||
<BaseChart {...props} options={options} type='bar'/>
|
<BaseChart {...props} options={optionsMerged} type='bar'/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
BarChart.propTypes = {
|
BarChart.propTypes = {
|
||||||
@@ -203,6 +202,20 @@ BarChart.propTypes = {
|
|||||||
stacked: PropTypes.bool
|
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) {
|
export function ConvertHexToRGBA(hex, opacity) {
|
||||||
const tempHex = hex.replace('#', '');
|
const tempHex = hex.replace('#', '');
|
||||||
const r = parseInt(tempHex.substring(0, 2), 16);
|
const r = parseInt(tempHex.substring(0, 2), 16);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import ZoomOutMapIcon from '@material-ui/icons/ZoomOutMap';
|
|||||||
import SaveAltIcon from '@material-ui/icons/SaveAlt';
|
import SaveAltIcon from '@material-ui/icons/SaveAlt';
|
||||||
import { InputSelect } from '../../../../../../static/js/components/FormComponents';
|
import { InputSelect } from '../../../../../../static/js/components/FormComponents';
|
||||||
import { DefaultButton, PgButtonGroup, PgIconButton} from '../../../../../../static/js/components/Buttons';
|
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';
|
CHART_THEME_COLORS, CHART_THEME_COLORS_LENGTH, ConvertHexToRGBA} from 'sources/chartjs';
|
||||||
import { QueryToolEventsContext, QueryToolContext } from '../QueryToolComponent';
|
import { QueryToolEventsContext, QueryToolContext } from '../QueryToolComponent';
|
||||||
import { QUERY_TOOL_EVENTS, PANELS } from '../QueryToolConstants';
|
import { QUERY_TOOL_EVENTS, PANELS } from '../QueryToolConstants';
|
||||||
@@ -136,7 +136,10 @@ function GenerateGraph({graphType, graphData, ...props}) {
|
|||||||
} else if (graphType == 'B' || graphType == 'SB') {
|
} else if (graphType == 'B' || graphType == 'SB') {
|
||||||
return <BarChart options={defaultOptions} data={graphData} stacked={graphType == 'SB'}
|
return <BarChart options={defaultOptions} data={graphData} stacked={graphType == 'SB'}
|
||||||
{...props}/>;
|
{...props}/>;
|
||||||
} else {
|
} else if (graphType == 'P') {
|
||||||
|
return <PieChart data={graphData} {...props}/>;
|
||||||
|
}
|
||||||
|
else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,7 +148,47 @@ GenerateGraph.propTypes = {
|
|||||||
graphData: PropTypes.object,
|
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 getGraphDataSet(graphType, rows, columns, xaxis, yaxis, queryToolCtx) {
|
||||||
// Function is used to the find the position of the column
|
// Function is used to the find the position of the column
|
||||||
function getColumnPosition(colName) {
|
function getColumnPosition(colName) {
|
||||||
@@ -180,25 +223,14 @@ function getGraphDataSet(graphType, rows, columns, xaxis, yaxis, queryToolCtx) {
|
|||||||
}
|
}
|
||||||
styleIndex = styleIndex + 1;
|
styleIndex = styleIndex + 1;
|
||||||
|
|
||||||
if (graphType !== 'L' && graphType !== 'SL') {
|
if (graphType === 'P') {
|
||||||
return {
|
return getPieChartData(rows, colName, colPosition, queryToolCtx);
|
||||||
label: colName,
|
} else if (graphType === 'B' || graphType === 'SB') {
|
||||||
data: rows.map((r)=>r[colPosition]),
|
return getBarChartData(rows, colName, colPosition, color);
|
||||||
backgroundColor: color,
|
} else if (graphType === 'L' || graphType === 'SL') {
|
||||||
borderColor:color
|
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);
|
clearTimeout(timeoutId);
|
||||||
if(LayoutHelper.isTabVisible(queryToolCtx.docker, PANELS.GRAPH_VISUALISER)) {
|
if(LayoutHelper.isTabVisible(queryToolCtx.docker, PANELS.GRAPH_VISUALISER)) {
|
||||||
timeoutId = setTimeout(function () {
|
timeoutId = setTimeout(function () {
|
||||||
setGraphHeight(contentRef.current.offsetHeight);
|
setGraphHeight(contentRef.current.offsetHeight - 8);
|
||||||
}, 300);
|
}, 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
|
// Generate button callback
|
||||||
const onGenerate = async ()=>{
|
const onGenerate = async ()=>{
|
||||||
setLoaderText(gettext('Fetching all the records...'));
|
setLoaderText(gettext('Fetching all the records...'));
|
||||||
@@ -290,7 +329,7 @@ export function GraphVisualiser({initColumns}) {
|
|||||||
setLoaderText(gettext('Rendering data points...'));
|
setLoaderText(gettext('Rendering data points...'));
|
||||||
// Set the Graph Data
|
// Set the Graph Data
|
||||||
setGraphData(
|
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('');
|
setLoaderText('');
|
||||||
@@ -328,7 +367,7 @@ export function GraphVisualiser({initColumns}) {
|
|||||||
<Box className={classes.topContainer}>
|
<Box className={classes.topContainer}>
|
||||||
<Box className={classes.displayFlex}>
|
<Box className={classes.displayFlex}>
|
||||||
<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}
|
<InputSelect className={classes.selectCtrl} options={xAxisOptions}
|
||||||
onChange={(v)=>setXAxis(v)} value={xaxis} optionsReloadBasis={optionsReload}/>
|
onChange={(v)=>setXAxis(v)} value={xaxis} optionsReloadBasis={optionsReload}/>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -340,6 +379,7 @@ export function GraphVisualiser({initColumns}) {
|
|||||||
{label: gettext('Stacked Line Chart'), value: 'SL'},
|
{label: gettext('Stacked Line Chart'), value: 'SL'},
|
||||||
{label: gettext('Bar Chart'), value: 'B'},
|
{label: gettext('Bar Chart'), value: 'B'},
|
||||||
{label: gettext('Stacked Bar Chart'), value: 'SB'},
|
{label: gettext('Stacked Bar Chart'), value: 'SB'},
|
||||||
|
{label: gettext('Pie Chart'), value: 'P'},
|
||||||
]} onChange={(v)=>setGraphType(v)} value={graphType} />
|
]} onChange={(v)=>setGraphType(v)} value={graphType} />
|
||||||
</Box>
|
</Box>
|
||||||
<DefaultButton onClick={onGenerate} startIcon={<ShowChartRoundedIcon />}
|
<DefaultButton onClick={onGenerate} startIcon={<ShowChartRoundedIcon />}
|
||||||
@@ -348,8 +388,8 @@ export function GraphVisualiser({initColumns}) {
|
|||||||
</DefaultButton>
|
</DefaultButton>
|
||||||
</Box>
|
</Box>
|
||||||
<Box className={classes.displayFlex}>
|
<Box className={classes.displayFlex}>
|
||||||
<span className={classes.spanLabel}>{gettext('Y Axis')}</span>
|
<span className={classes.spanLabel}>{graphType != 'P' ? gettext('Y Axis') : gettext('Value')}</span>
|
||||||
<InputSelect className={classes.selectCtrl} controlProps={{'multiple': true, allowSelectAll: true}}
|
<InputSelect className={classes.selectCtrl} controlProps={{'multiple': graphType != 'P', allowSelectAll: graphType != 'P'}}
|
||||||
options={yAxisOptions} onChange={(v)=>setYAxis(v)} value={yaxis} optionsReloadBasis={optionsReload}/>
|
options={yAxisOptions} onChange={(v)=>setYAxis(v)} value={yaxis} optionsReloadBasis={optionsReload}/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
Reference in New Issue
Block a user