mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2024-11-23 01:06:31 -06:00
Adding initial version for showing statistics for the server, and
database nodes.
This commit is contained in:
parent
d1e2b902dc
commit
63b951baee
37
web/pgadmin/misc/statistics/__init__.py
Normal file
37
web/pgadmin/misc/statistics/__init__.py
Normal file
@ -0,0 +1,37 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
"""A blueprint module providing utility functions for the application."""
|
||||
|
||||
from flask import url_for
|
||||
from pgadmin.utils import PgAdminModule
|
||||
|
||||
MODULE_NAME = 'statistics'
|
||||
|
||||
|
||||
class StatisticsModule(PgAdminModule):
|
||||
"""
|
||||
StatisticsModule
|
||||
|
||||
This module will render the statistics of the browser nodes on selection
|
||||
when statistics panel is active.
|
||||
"""
|
||||
|
||||
def get_own_javascripts(self):
|
||||
return [{
|
||||
'name': 'pgadmin.browser.object_statistics',
|
||||
'path': url_for('statistics.static', filename='js/statistics'),
|
||||
'when': None
|
||||
}]
|
||||
|
||||
|
||||
# Initialise the module
|
||||
blueprint = StatisticsModule(
|
||||
MODULE_NAME, __name__, url_prefix='/misc/statistics'
|
||||
)
|
301
web/pgadmin/misc/statistics/static/js/statistics.js
Normal file
301
web/pgadmin/misc/statistics/static/js/statistics.js
Normal file
@ -0,0 +1,301 @@
|
||||
define(
|
||||
['underscore', 'jquery', 'pgadmin.browser', 'backgrid', 'wcdocker', 'pgadmin.backgrid'],
|
||||
function(_, $, pgBrowser, Backgrid) {
|
||||
|
||||
if (pgBrowser.NodeStatistics)
|
||||
return pgBrowser.NodeStatistics;
|
||||
|
||||
pgBrowser.NodeStatistics = pgBrowser.NodeStatistics || {};
|
||||
|
||||
if (pgBrowser.NodeStatistics.initialized) {
|
||||
return pgBrowser.NodeStatistics;
|
||||
}
|
||||
|
||||
var PGBooleanCell = Backgrid.Extension.SwitchCell.extend({
|
||||
defaults: _.extend({}, Backgrid.Extension.SwitchCell.prototype.defaults)
|
||||
}),
|
||||
typeCellMapper = {
|
||||
// boolean
|
||||
16: PGBooleanCell,
|
||||
// int8
|
||||
20: Backgrid.IntegerCell,
|
||||
// int2
|
||||
21: Backgrid.IntegerCell,
|
||||
// int4
|
||||
23: Backgrid.IntegerCell,
|
||||
// float4
|
||||
700: Backgrid.NumberCell,
|
||||
// float8
|
||||
701: Backgrid.NumberCell,
|
||||
// numeric
|
||||
1700: Backgrid.NumberCell,
|
||||
// abstime
|
||||
702: Backgrid.DatetimeCell,
|
||||
// reltime
|
||||
703: Backgrid.DatetimeCell,
|
||||
// date
|
||||
1082: Backgrid.DatetimeCell.extend({
|
||||
includeDate: true, includeTime: false, includeMilli: false
|
||||
}),
|
||||
// time
|
||||
1083: Backgrid.DatetimeCell.extend({
|
||||
includeDate: false, includeTime: true, includeMilli: true
|
||||
}),
|
||||
// timestamp
|
||||
1114: Backgrid.DatetimeCell.extend({
|
||||
includeDate: true, includeTime: true, includeMilli: true
|
||||
}),
|
||||
// timestamptz
|
||||
1184: 'string'/* Backgrid.DatetimeCell.extend({
|
||||
includeDate: true, includeTime: true, includeMilli: true
|
||||
}) */,
|
||||
1266: 'string'/* Backgrid.DatetimeCell.extend({
|
||||
includeDate: false, includeTime: true, includeMilli: true
|
||||
}) */
|
||||
},
|
||||
GRID_CLASSES = "backgrid presentation table backgrid-striped table-bordered table-hover",
|
||||
wcDocker = window.wcDocker;
|
||||
|
||||
_.extend(
|
||||
PGBooleanCell.prototype.defaults.options, {
|
||||
onText: pgBrowser.messages.TRUE,
|
||||
offText: pgBrowser.messages.FALSE,
|
||||
onColor: 'success',
|
||||
offColor: 'primary',
|
||||
size: 'mini'
|
||||
}
|
||||
);
|
||||
|
||||
_.extend(pgBrowser.NodeStatistics, {
|
||||
init: function() {
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
this.initialized = true;
|
||||
_.bindAll(
|
||||
this,
|
||||
'showStatistics', 'panelVisibilityChanged',
|
||||
'__createMultiLineStatistics', '__createSingleLineStatistics');
|
||||
|
||||
_.extend(
|
||||
this, {
|
||||
initialized: true,
|
||||
collection: new (Backbone.Collection)(null),
|
||||
statistic_columns: [{
|
||||
editable: false,
|
||||
name: 'statistics',
|
||||
label: pgBrowser.messages.STATISTICS_LABEL,
|
||||
cell: 'string',
|
||||
headerCell: Backgrid.Extension.CustomHeaderCell,
|
||||
cellHeaderClasses: 'width_percent_25'
|
||||
},{
|
||||
editable: false,
|
||||
name: 'value',
|
||||
label: pgBrowser.messages.STATISTICS_VALUE_LABEL,
|
||||
cell: 'string'
|
||||
}],
|
||||
panel: pgBrowser.docker.findPanels('statistics'),
|
||||
columns: null,
|
||||
grid: null
|
||||
});
|
||||
|
||||
var self = this;
|
||||
|
||||
// We will listen to the visibility change of the statistics panel
|
||||
pgBrowser.Events.on(
|
||||
'pgadmin-browser:panel-statistics:' +
|
||||
wcDocker.EVENT.VISIBILITY_CHANGED,
|
||||
this.panelVisibilityChanged);
|
||||
|
||||
// Hmm.. Did we find the statistics panel, and is it visible (openned)?
|
||||
// If that is the case - we need to listen the browser tree selection
|
||||
// events.
|
||||
if (this.panel.length == 0) {
|
||||
pgBrowser.Events.on(
|
||||
'pgadmin-browser:panel-statistics:' + wcDocker.EVENT.INIT,
|
||||
function() {
|
||||
self.panel = pgBrowser.docker.findPanels('statistics');
|
||||
if (self.panel[0].isVisible() ||
|
||||
self.panel.length != 1) {
|
||||
pgBrowser.Events.on(
|
||||
'pgadmin-browser:tree:selected', this.showStatistics
|
||||
);
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
} else {
|
||||
if (self.panel[0].isVisible() ||
|
||||
self.panel.length != 1) {
|
||||
pgBrowser.Events.on(
|
||||
'pgadmin-browser:tree:selected', this.showStatistics
|
||||
);
|
||||
}
|
||||
}
|
||||
if (self.panel.length > 0 && self.panel[0].isVisible()) {
|
||||
pgBrowser.Events.on(
|
||||
'pgadmin-browser:tree:selected', this.showStatistics
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
// Fetch the actual data and update the collection
|
||||
__updateCollection: function(url, node) {
|
||||
var $container = this.panel[0].layout().scene().find('.pg-panel-content'),
|
||||
$msgContainer = $container.find('.pg-panel-statistics-message'),
|
||||
$gridContainer = $container.find('.pg-panel-statistics-container'),
|
||||
collection = this.collection,
|
||||
panel = this.panel,
|
||||
self = this,
|
||||
msg = '';
|
||||
|
||||
if (node) {
|
||||
msg = pgBrowser.messages.NODE_HAS_NO_STATISTICS;
|
||||
/* We fetch the statistics only for those node who set the parameter
|
||||
* showStatistics function.
|
||||
*/
|
||||
if (node.hasStatistics) {
|
||||
/* Set the message because ajax request may take time to
|
||||
* fetch the information from the server.
|
||||
*/
|
||||
msg = '';
|
||||
$msgContainer.text(msg);
|
||||
|
||||
// Set the url, fetch the data and update the collection
|
||||
$.ajax({
|
||||
url: url,
|
||||
type:'GET',
|
||||
success: function(res) {
|
||||
if (res.data) {
|
||||
var data = res.data;
|
||||
if (node.hasCollectiveStatistics || data['rows'].length > 1) {
|
||||
self.__createMultiLineStatistics.call(self, data);
|
||||
} else {
|
||||
self.__createSingleLineStatistics.call(self, data);
|
||||
}
|
||||
|
||||
if (self.grid) {
|
||||
delete self.grid;
|
||||
self.grid = null;
|
||||
}
|
||||
|
||||
self.grid = new Backgrid.Grid({
|
||||
columns: self.columns,
|
||||
collection: self.collection,
|
||||
className: GRID_CLASSES
|
||||
});
|
||||
self.grid.render();
|
||||
$gridContainer.empty();
|
||||
$gridContainer.append(self.grid.$el);
|
||||
|
||||
if (!$msgContainer.hasClass('hidden')) {
|
||||
$msgContainer.addClass('hidden')
|
||||
}
|
||||
$gridContainer.removeClass('hidden');
|
||||
|
||||
} else if (res.info) {
|
||||
if (!$gridContainer.hasClass('hidden')) {
|
||||
$gridContainer.addClass('hidden')
|
||||
}
|
||||
$msgContainer.text(res.info);
|
||||
$msgContainer.removeClass('hidden');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
// TODO:: Report this error.
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (msg != '') {
|
||||
// Hide the grid container and show the default message container
|
||||
if (!$gridContainer.hasClass('hidden'))
|
||||
$gridContainer.addClass('hidden');
|
||||
$msgContainer.removeClass('hidden');
|
||||
|
||||
$msgContainer.text(msg);
|
||||
}
|
||||
},
|
||||
|
||||
showStatistics: function(item, data, node) {
|
||||
var self = this;
|
||||
/**
|
||||
* We can't start fetching the statistics immediately, it is possible -
|
||||
* the user is just using keyboards to select the node, and just
|
||||
* traversing through.
|
||||
*
|
||||
* We will wait for some time before fetching the statistics for the
|
||||
* selected node.
|
||||
**/
|
||||
if (self.timeout) {
|
||||
clearTimeout(self.timeout);
|
||||
}
|
||||
self.timeout = setTimeout(
|
||||
function() {
|
||||
self.__updateCollection.call(
|
||||
self, node.generate_url(item, 'stats', data, true), node
|
||||
);
|
||||
}, 400);
|
||||
},
|
||||
|
||||
__createMultiLineStatistics: function(data) {
|
||||
var rows = data['rows'],
|
||||
columns = data['columns'];
|
||||
|
||||
this.columns = [];
|
||||
for (var idx in columns) {
|
||||
var c = columns[idx];
|
||||
this.columns.push({
|
||||
editable: false,
|
||||
name: c['name'],
|
||||
cell: typeCellMapper[c['type_code']] || 'string'
|
||||
});
|
||||
}
|
||||
|
||||
this.collection.reset(rows);
|
||||
},
|
||||
|
||||
__createSingleLineStatistics: function(data) {
|
||||
var row = data['rows'][0],
|
||||
columns = data['columns']
|
||||
res = [];
|
||||
|
||||
this.columns = this.statistic_columns;
|
||||
for (var idx in columns) {
|
||||
name = (columns[idx])['name'];
|
||||
res.push({
|
||||
'statistics': name,
|
||||
'value': row[name]
|
||||
});
|
||||
}
|
||||
|
||||
this.collection.reset(res);
|
||||
},
|
||||
|
||||
panelVisibilityChanged: function(panel) {
|
||||
if (panel.isVisible()) {
|
||||
var t = pgBrowser.tree,
|
||||
i = t.selected(),
|
||||
d = i && t.itemData(i),
|
||||
n = i && d && pgBrowser.Nodes[d._type];
|
||||
|
||||
pgBrowser.NodeStatistics.showStatistics.apply(
|
||||
pgBrowser.NodeStatistics, [i, d, n]
|
||||
);
|
||||
|
||||
// We will start listening the tree selection event.
|
||||
pgBrowser.Events.on(
|
||||
'pgadmin-browser:tree:selected',
|
||||
pgBrowser.NodeStatistics.showStatistics
|
||||
);
|
||||
} else {
|
||||
// We don't need to listen the tree item selection event.
|
||||
pgBrowser.Events.off(
|
||||
'pgadmin-browser:tree:selected',
|
||||
pgBrowser.NodeStatistics.showStatistics
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return pgBrowser.NodeStatistics;
|
||||
});
|
@ -483,7 +483,15 @@ fieldset[disabled] .form-control {
|
||||
width:25px;
|
||||
}
|
||||
|
||||
.backgrid td.renderable:not(.editable):not(.delete-cell) {
|
||||
.backgrid.presentation {
|
||||
background-color: #EEEEEE;
|
||||
}
|
||||
|
||||
.backgrid.presentation td.renderable {
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.backgrid:not(.presentation) td.renderable:not(.editable):not(.delete-cell) {
|
||||
background-color: #F1F1F1;
|
||||
}
|
||||
|
||||
|
@ -2880,4 +2880,4 @@ var Grid = Backgrid.Grid = Backbone.View.extend({
|
||||
|
||||
});
|
||||
return Backgrid;
|
||||
}));
|
||||
}));
|
||||
|
Loading…
Reference in New Issue
Block a user