Adding initial version for showing statistics for the server, and

database nodes.
This commit is contained in:
Ashesh Vashi 2016-05-16 01:57:59 +05:30
parent d1e2b902dc
commit 63b951baee
4 changed files with 348 additions and 2 deletions

View 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'
)

View 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;
});

View File

@ -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;
}

View File

@ -2880,4 +2880,4 @@ var Grid = Backgrid.Grid = Backbone.View.extend({
});
return Backgrid;
}));
}));