Dashboard graph optimization. Fixes #3941
parent
dae8186c2a
commit
aad7830d37
|
@ -50,5 +50,6 @@ Bug fixes
|
||||||
| `Bug #3929 <https://redmine.postgresql.org/issues/3929>`_ - Fix alignment of help messages in properties panels.
|
| `Bug #3929 <https://redmine.postgresql.org/issues/3929>`_ - Fix alignment of help messages in properties panels.
|
||||||
| `Bug #3932 <https://redmine.postgresql.org/issues/3932>`_ - Fix alignment of submenu for Internet Explorer.
|
| `Bug #3932 <https://redmine.postgresql.org/issues/3932>`_ - Fix alignment of submenu for Internet Explorer.
|
||||||
| `Bug #3935 <https://redmine.postgresql.org/issues/3935>`_ - Ensure that grant wizard should list down functions for EPAS server running with no-redwood-compat mode.
|
| `Bug #3935 <https://redmine.postgresql.org/issues/3935>`_ - Ensure that grant wizard should list down functions for EPAS server running with no-redwood-compat mode.
|
||||||
|
| `Bug #3941 <https://redmine.postgresql.org/issues/3941>`_ - Dashboard graph optimization.
|
||||||
| `Bug #3954 <https://redmine.postgresql.org/issues/3954>`_ - Remove Python 2.6 code that's now obsolete.
|
| `Bug #3954 <https://redmine.postgresql.org/issues/3954>`_ - Remove Python 2.6 code that's now obsolete.
|
||||||
| `Bug #3955 <https://redmine.postgresql.org/issues/3955>`_ - Expose the bind address in the Docker container via PGADMIN_BIND_ADDRESS.
|
| `Bug #3955 <https://redmine.postgresql.org/issues/3955>`_ - Expose the bind address in the Docker container via PGADMIN_BIND_ADDRESS.
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
"""A blueprint module implementing the dashboard frame."""
|
"""A blueprint module implementing the dashboard frame."""
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from flask import render_template, url_for, Response, g
|
from flask import render_template, url_for, Response, g, request
|
||||||
from flask_babelex import gettext
|
from flask_babelex import gettext
|
||||||
from flask_security import login_required
|
from flask_security import login_required
|
||||||
from pgadmin.utils import PgAdminModule
|
from pgadmin.utils import PgAdminModule
|
||||||
|
@ -154,21 +154,9 @@ class DashboardModule(PgAdminModule):
|
||||||
return [
|
return [
|
||||||
'dashboard.index', 'dashboard.get_by_sever_id',
|
'dashboard.index', 'dashboard.get_by_sever_id',
|
||||||
'dashboard.get_by_database_id',
|
'dashboard.get_by_database_id',
|
||||||
'dashboard.session_stats',
|
'dashboard.dashboard_stats',
|
||||||
'dashboard.get_session_stats_by_sever_id',
|
'dashboard.dashboard_stats_sid',
|
||||||
'dashboard.get_session_stats_by_database_id',
|
'dashboard.dashboard_stats_did',
|
||||||
'dashboard.tps_stats',
|
|
||||||
'dashboard.tps_stats_by_server_id',
|
|
||||||
'dashboard.tps_stats_by_database_id',
|
|
||||||
'dashboard.ti_stats',
|
|
||||||
'dashboard.ti_stats_by_server_id',
|
|
||||||
'dashboard.ti_stats_by_database_id',
|
|
||||||
'dashboard.to_stats',
|
|
||||||
'dashboard.to_stats_by_server_id',
|
|
||||||
'dashboard.to_stats_by_database_id',
|
|
||||||
'dashboard.bio_stats',
|
|
||||||
'dashboard.bio_stats_by_server_id',
|
|
||||||
'dashboard.bio_stats_by_database_id',
|
|
||||||
'dashboard.activity',
|
'dashboard.activity',
|
||||||
'dashboard.get_activity_by_server_id',
|
'dashboard.get_activity_by_server_id',
|
||||||
'dashboard.get_activity_by_database_id',
|
'dashboard.get_activity_by_database_id',
|
||||||
|
@ -356,87 +344,36 @@ def get_data(sid, did, template):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/session_stats/', endpoint='session_stats')
|
@blueprint.route('/dashboard_stats',
|
||||||
@blueprint.route(
|
endpoint='dashboard_stats')
|
||||||
'/session_stats/<int:sid>', endpoint='get_session_stats_by_sever_id'
|
@blueprint.route('/dashboard_stats/<int:sid>',
|
||||||
)
|
endpoint='dashboard_stats_sid')
|
||||||
@blueprint.route(
|
@blueprint.route('/dashboard_stats/<int:sid>/<int:did>',
|
||||||
'/session_stats/<int:sid>/<int:did>',
|
endpoint='dashboard_stats_did')
|
||||||
endpoint='get_session_stats_by_database_id'
|
|
||||||
)
|
|
||||||
@login_required
|
@login_required
|
||||||
@check_precondition
|
@check_precondition
|
||||||
def session_stats(sid=None, did=None):
|
def dashboard_stats(sid=None, did=None):
|
||||||
"""
|
resp_data = {}
|
||||||
This function returns server session statistics
|
|
||||||
:param sid: server id
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
return get_data(sid, did, 'session_stats.sql')
|
|
||||||
|
|
||||||
|
if request.args['chart_names'] != '':
|
||||||
|
chart_names = request.args['chart_names'].split(',')
|
||||||
|
|
||||||
@blueprint.route('/tps_stats/', endpoint='tps_stats')
|
if not sid:
|
||||||
@blueprint.route('/tps_stats/<int:sid>', endpoint='tps_stats_by_server_id')
|
return internal_server_error(errormsg='Server ID not specified.')
|
||||||
@blueprint.route(
|
|
||||||
'/tps_stats/<int:sid>/<int:did>', endpoint='tps_stats_by_database_id'
|
|
||||||
)
|
|
||||||
@login_required
|
|
||||||
@check_precondition
|
|
||||||
def tps_stats(sid=None, did=None):
|
|
||||||
"""
|
|
||||||
This function returns server TPS throughput
|
|
||||||
:param sid: server id
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
return get_data(sid, did, 'tps_stats.sql')
|
|
||||||
|
|
||||||
|
sql = render_template(
|
||||||
|
"/".join([g.template_path, 'dashboard_stats.sql']), did=did,
|
||||||
|
chart_names=chart_names,
|
||||||
|
)
|
||||||
|
status, res = g.conn.execute_dict(sql)
|
||||||
|
|
||||||
@blueprint.route('/ti_stats/', endpoint='ti_stats')
|
for chart_row in res['rows']:
|
||||||
@blueprint.route('/ti_stats/<int:sid>', endpoint='ti_stats_by_server_id')
|
resp_data[chart_row['chart_name']] = chart_row['chart_data']
|
||||||
@blueprint.route(
|
|
||||||
'/ti_stats/<int:sid>/<int:did>', endpoint='ti_stats_by_database_id'
|
|
||||||
)
|
|
||||||
@login_required
|
|
||||||
@check_precondition
|
|
||||||
def ti_stats(sid=None, did=None):
|
|
||||||
"""
|
|
||||||
This function returns server tuple input statistics
|
|
||||||
:param sid: server id
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
return get_data(sid, did, 'ti_stats.sql')
|
|
||||||
|
|
||||||
|
return ajax_response(
|
||||||
@blueprint.route('/to_stats/', endpoint='to_stats')
|
response=resp_data,
|
||||||
@blueprint.route('/to_stats/<int:sid>', endpoint='to_stats_by_server_id')
|
status=200
|
||||||
@blueprint.route(
|
)
|
||||||
'/to_stats/<int:sid>/<int:did>', endpoint='to_stats_by_database_id'
|
|
||||||
)
|
|
||||||
@login_required
|
|
||||||
@check_precondition
|
|
||||||
def to_stats(sid=None, did=None):
|
|
||||||
"""
|
|
||||||
This function returns server tuple output statistics
|
|
||||||
:param sid: server id
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
return get_data(sid, did, 'to_stats.sql')
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/bio_stats/', endpoint='bio_stats')
|
|
||||||
@blueprint.route('/bio_stats/<int:sid>', endpoint='bio_stats_by_server_id')
|
|
||||||
@blueprint.route(
|
|
||||||
'/bio_stats/<int:sid>/<int:did>', endpoint='bio_stats_by_database_id'
|
|
||||||
)
|
|
||||||
@login_required
|
|
||||||
@check_precondition
|
|
||||||
def bio_stats(sid=None, did=None):
|
|
||||||
"""
|
|
||||||
This function returns server block IO statistics
|
|
||||||
:param sid: server id
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
return get_data(sid, did, 'bio_stats.sql')
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/activity/', endpoint='activity')
|
@blueprint.route('/activity/', endpoint='activity')
|
||||||
|
|
|
@ -68,7 +68,9 @@ export class Chart {
|
||||||
}
|
}
|
||||||
|
|
||||||
getOtherData(key) {
|
getOtherData(key) {
|
||||||
return this._otherData[key];
|
if(this._otherData[key]) {
|
||||||
|
return this._otherData[key];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setOtherData(key, value) {
|
setOtherData(key, value) {
|
||||||
|
|
|
@ -10,11 +10,11 @@
|
||||||
define('pgadmin.dashboard', [
|
define('pgadmin.dashboard', [
|
||||||
'sources/url_for', 'sources/gettext', 'require', 'jquery', 'underscore',
|
'sources/url_for', 'sources/gettext', 'require', 'jquery', 'underscore',
|
||||||
'sources/pgadmin', 'backbone', 'backgrid', './charting',
|
'sources/pgadmin', 'backbone', 'backgrid', './charting',
|
||||||
'pgadmin.alertifyjs', 'pgadmin.backform',
|
'pgadmin.alertifyjs', 'pgadmin.backform', 'sources/nodes/dashboard',
|
||||||
'sources/nodes/dashboard', 'pgadmin.browser', 'bootstrap', 'wcdocker',
|
'sources/utils', 'pgadmin.browser', 'bootstrap', 'wcdocker',
|
||||||
], function(
|
], function(
|
||||||
url_for, gettext, r, $, _, pgAdmin, Backbone, Backgrid, charting,
|
url_for, gettext, r, $, _, pgAdmin, Backbone, Backgrid, charting,
|
||||||
Alertify, Backform, NodesDashboard
|
Alertify, Backform, NodesDashboard, commonUtils
|
||||||
) {
|
) {
|
||||||
|
|
||||||
pgAdmin.Browser = pgAdmin.Browser || {};
|
pgAdmin.Browser = pgAdmin.Browser || {};
|
||||||
|
@ -214,8 +214,9 @@ define('pgadmin.dashboard', [
|
||||||
// Load the default welcome dashboard
|
// Load the default welcome dashboard
|
||||||
var url = url_for('dashboard.index');
|
var url = url_for('dashboard.index');
|
||||||
|
|
||||||
/* Store the chart objects and there interval ids in this store */
|
/* Store the chart objects, refresh freq and next refresh time */
|
||||||
this.chartStore = {};
|
this.chart_store = {};
|
||||||
|
this.charts_poller_int_id = -1;
|
||||||
|
|
||||||
var dashboardPanel = pgBrowser.panels['dashboard'].panel;
|
var dashboardPanel = pgBrowser.panels['dashboard'].panel;
|
||||||
if (dashboardPanel) {
|
if (dashboardPanel) {
|
||||||
|
@ -373,96 +374,178 @@ define('pgadmin.dashboard', [
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
renderChartLoop: function(chartObj, sid, did, url, counter, refresh) {
|
// Render the charts
|
||||||
var data = [],
|
renderCharts: function(charts_config) {
|
||||||
dataset = [];
|
|
||||||
|
|
||||||
var theIntervalFunc = function() {
|
let self = this,
|
||||||
var path = url + sid;
|
tooltipFormatter = function(refresh, currVal) {
|
||||||
if (did != -1) {
|
return(`Seconds ago: ${parseInt(currVal.x * refresh)}</br>
|
||||||
path += '/' + did;
|
Value: ${currVal.y}`);
|
||||||
|
},
|
||||||
|
curr_epoch=commonUtils.getEpoch();
|
||||||
|
|
||||||
|
self.stopChartsPoller();
|
||||||
|
|
||||||
|
charts_config.map((chart_config) => {
|
||||||
|
if(self.chart_store[chart_config.chart_name]
|
||||||
|
&& self.old_preferences[chart_config.refresh_pref_name] !=
|
||||||
|
self.preferences[chart_config.refresh_pref_name]) {
|
||||||
|
self.clearChartFromStore(chart_config.chart_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(self.chart_store[chart_config.chart_name]) {
|
||||||
|
let chart_obj = self.chart_store[chart_config.chart_name].chart_obj;
|
||||||
|
chart_obj.setOptions(chart_config.options, false);
|
||||||
|
chart_obj.setTooltipFormatter(
|
||||||
|
tooltipFormatter.bind(null, self.preferences[chart_config.refresh_pref_name])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!self.chart_store[chart_config.chart_name]) {
|
||||||
|
let chart_obj = new charting.Chart(chart_config.container, chart_config.options);
|
||||||
|
|
||||||
|
chart_obj.setTooltipFormatter(
|
||||||
|
tooltipFormatter.bind(null, self.preferences[chart_config.refresh_pref_name])
|
||||||
|
);
|
||||||
|
|
||||||
|
chart_obj.setOtherData('counter', chart_config.counter);
|
||||||
|
|
||||||
|
self.chart_store[chart_config.chart_name] = {
|
||||||
|
'chart_obj' : chart_obj,
|
||||||
|
'refresh_on': curr_epoch,
|
||||||
|
'refresh_rate': self.preferences[chart_config.refresh_pref_name],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.startChartsPoller(self.chart_store, self.sid, self.did);
|
||||||
|
},
|
||||||
|
|
||||||
|
getStatsUrl: function(sid=-1, did=-1, chart_names=[]) {
|
||||||
|
let base_url = url_for('dashboard.dashboard_stats');
|
||||||
|
base_url += '/' + sid;
|
||||||
|
base_url += (did > 0) ? ('/' + did) : '';
|
||||||
|
base_url += '?chart_names=' + chart_names.join(',');
|
||||||
|
return base_url;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateChart: function(chart_obj, new_data){
|
||||||
|
// Dataset format:
|
||||||
|
// [
|
||||||
|
// { data: [[0, y0], [1, y1]...], label: 'Label 1', [options] },
|
||||||
|
// { data: [[0, y0], [1, y1]...], label: 'Label 2', [options] },
|
||||||
|
// { data: [[0, y0], [1, y1]...], label: 'Label 3', [options] }
|
||||||
|
// ]
|
||||||
|
let dataset = chart_obj.getOtherData('dataset') || [],
|
||||||
|
counter_prev_data = chart_obj.getOtherData('counter_prev_data') || new_data,
|
||||||
|
counter = chart_obj.getOtherData('counter') || false;
|
||||||
|
|
||||||
|
if (dataset.length == 0) {
|
||||||
|
// Create the initial data structure
|
||||||
|
for (let label in new_data) {
|
||||||
|
dataset.push({
|
||||||
|
'data': [
|
||||||
|
[0, counter ? (new_data[label] - counter_prev_data[label]) : new_data[label]],
|
||||||
|
],
|
||||||
|
'label': label,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Object.keys(new_data).map((label, label_ind) => {
|
||||||
|
// Push new values onto the existing data structure
|
||||||
|
// If this is a counter stat, we need to subtract the previous value
|
||||||
|
if (!counter) {
|
||||||
|
dataset[label_ind]['data'].unshift([0, new_data[label]]);
|
||||||
|
} else {
|
||||||
|
// Store the current value, minus the previous one we stashed.
|
||||||
|
// It's possible the tab has been reloaded, in which case out previous values are gone
|
||||||
|
if (_.isUndefined(counter_prev_data))
|
||||||
|
return;
|
||||||
|
|
||||||
|
dataset[label_ind]['data'].unshift([0, new_data[label] - counter_prev_data[label]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the time index to get a proper scrolling display
|
||||||
|
for (var time_ind = 0; time_ind < dataset[label_ind]['data'].length; time_ind++) {
|
||||||
|
dataset[label_ind]['data'][time_ind][0] = time_ind;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
counter_prev_data = new_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove old data points
|
||||||
|
for (let label_ind = 0; label_ind < dataset.length; label_ind++) {
|
||||||
|
if (dataset[label_ind]['data'].length > 101) {
|
||||||
|
dataset[label_ind]['data'].pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chart_obj.setOtherData('dataset', dataset);
|
||||||
|
chart_obj.setOtherData('counter_prev_data', counter_prev_data);
|
||||||
|
|
||||||
|
if (chart_obj.isInPage()) {
|
||||||
|
if (chart_obj.isVisible()) {
|
||||||
|
chart_obj.draw(dataset);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
stopChartsPoller: function() {
|
||||||
|
clearInterval(this.charts_poller_int_id);
|
||||||
|
},
|
||||||
|
|
||||||
|
startChartsPoller: function(chart_store, sid, did) {
|
||||||
|
let self = this;
|
||||||
|
/* polling will the greatest common divisor of the refresh rates*/
|
||||||
|
let poll_interval = commonUtils.getGCD(
|
||||||
|
Object.values(chart_store).map(item => item.refresh_rate)
|
||||||
|
);
|
||||||
|
const WAIT_COUNTER = 3;
|
||||||
|
let last_poll_wait_counter = 0;
|
||||||
|
|
||||||
|
/* Stop if running, only one poller lives */
|
||||||
|
self.stopChartsPoller();
|
||||||
|
|
||||||
|
var thePollingFunc = function() {
|
||||||
|
let curr_epoch = commonUtils.getEpoch();
|
||||||
|
let chart_names_to_get = [];
|
||||||
|
|
||||||
|
for(let chart_name in chart_store) {
|
||||||
|
/* when its time to get the data */
|
||||||
|
if(chart_store[chart_name].refresh_on <= curr_epoch) {
|
||||||
|
/* set the next trigger point */
|
||||||
|
chart_store[chart_name].refresh_on = curr_epoch + chart_store[chart_name].refresh_rate;
|
||||||
|
chart_names_to_get.push(chart_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If none of the chart wants data, don't trouble
|
||||||
|
* If response not received from prev poll, don't trouble !!
|
||||||
|
*/
|
||||||
|
if(chart_names_to_get.length == 0 || last_poll_wait_counter > 0) {
|
||||||
|
/* reduce the number of tries, request should be sent if last_poll_wait_counter
|
||||||
|
* completes WAIT_COUNTER times.*/
|
||||||
|
last_poll_wait_counter--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var path = self.getStatsUrl(sid, did, chart_names_to_get);
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: path,
|
url: path,
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
dataType: 'html',
|
|
||||||
})
|
})
|
||||||
.done(function(resp) {
|
.done(function(resp) {
|
||||||
$(chartObj.getContainer()).removeClass('graph-error');
|
last_poll_wait_counter = 0;
|
||||||
data = JSON.parse(resp);
|
for(let chart_name in resp) {
|
||||||
|
let chart_obj = chart_store[chart_name].chart_obj;
|
||||||
var y = 0,
|
$(chart_obj.getContainer()).removeClass('graph-error');
|
||||||
x;
|
self.updateChart(chart_obj, resp[chart_name]);
|
||||||
if (dataset.length == 0) {
|
|
||||||
if (counter == true) {
|
|
||||||
// Have we stashed initial values?
|
|
||||||
if (_.isUndefined(chartObj.getOtherData('counter_previous_vals'))) {
|
|
||||||
chartObj.setOtherData('counter_previous_vals', data[0]);
|
|
||||||
} else {
|
|
||||||
// Create the initial data structure
|
|
||||||
for (x in data[0]) {
|
|
||||||
dataset.push({
|
|
||||||
'data': [
|
|
||||||
[0, data[0][x] - chartObj.getOtherData('counter_previous_vals')[x]],
|
|
||||||
],
|
|
||||||
'label': x,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Create the initial data structure
|
|
||||||
for (x in data[0]) {
|
|
||||||
dataset.push({
|
|
||||||
'data': [
|
|
||||||
[0, data[0][x]],
|
|
||||||
],
|
|
||||||
'label': x,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (x in data[0]) {
|
|
||||||
// Push new values onto the existing data structure
|
|
||||||
// If this is a counter stat, we need to subtract the previous value
|
|
||||||
if (counter == false) {
|
|
||||||
dataset[y]['data'].unshift([0, data[0][x]]);
|
|
||||||
} else {
|
|
||||||
// Store the current value, minus the previous one we stashed.
|
|
||||||
// It's possible the tab has been reloaded, in which case out previous values are gone
|
|
||||||
if (_.isUndefined(chartObj.getOtherData('counter_previous_vals')))
|
|
||||||
return;
|
|
||||||
|
|
||||||
dataset[y]['data'].unshift([0, data[0][x] - chartObj.getOtherData('counter_previous_vals')[x]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset the time index to get a proper scrolling display
|
|
||||||
for (var z = 0; z < dataset[y]['data'].length; z++) {
|
|
||||||
dataset[y]['data'][z][0] = z;
|
|
||||||
}
|
|
||||||
|
|
||||||
y++;
|
|
||||||
}
|
|
||||||
chartObj.setOtherData('counter_previous_vals', data[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove uneeded elements
|
|
||||||
for (x = 0; x < dataset.length; x++) {
|
|
||||||
// Remove old data points
|
|
||||||
if (dataset[x]['data'].length > 101) {
|
|
||||||
dataset[x]['data'].pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chartObj.isInPage()) {
|
|
||||||
if (chartObj.isVisible()) {
|
|
||||||
chartObj.draw(dataset);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
})
|
||||||
.fail(function(xhr) {
|
.fail(function(xhr) {
|
||||||
|
last_poll_wait_counter = 0;
|
||||||
let err = '';
|
let err = '';
|
||||||
let msg = '';
|
let msg = '';
|
||||||
let cls = 'info';
|
let cls = 'info';
|
||||||
|
@ -484,66 +567,19 @@ define('pgadmin.dashboard', [
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$(chartObj.getContainer()).addClass('graph-error');
|
for(let chart_name in chart_store) {
|
||||||
$(chartObj.getContainer()).html(
|
let chart_obj = chart_store[chart_name].chart_obj;
|
||||||
'<div class="alert alert-' + cls + ' pg-panel-message" role="alert">' + msg + '</div>'
|
$(chart_obj.getContainer()).addClass('graph-error');
|
||||||
);
|
$(chart_obj.getContainer()).html(
|
||||||
|
'<div class="alert alert-' + cls + ' pg-panel-message" role="alert">' + msg + '</div>'
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
last_poll_wait_counter = WAIT_COUNTER;
|
||||||
};
|
};
|
||||||
/* Execute once for the first time as setInterval will not do */
|
/* Execute once for the first time as setInterval will not do */
|
||||||
theIntervalFunc();
|
thePollingFunc();
|
||||||
return setInterval(theIntervalFunc, refresh * 1000);
|
self.charts_poller_int_id = setInterval(thePollingFunc, poll_interval * 1000);
|
||||||
},
|
|
||||||
|
|
||||||
// Render a chart
|
|
||||||
render_chart: function(
|
|
||||||
container, url, options, counter, chartName, prefName
|
|
||||||
) {
|
|
||||||
|
|
||||||
// Data format:
|
|
||||||
// [
|
|
||||||
// { data: [[0, y0], [1, y1]...], label: 'Label 1', [options] },
|
|
||||||
// { data: [[0, y0], [1, y1]...], label: 'Label 2', [options] },
|
|
||||||
// { data: [[0, y0], [1, y1]...], label: 'Label 3', [options] }
|
|
||||||
// ]
|
|
||||||
|
|
||||||
let self = this,
|
|
||||||
tooltipFormatter = function(refresh, currVal) {
|
|
||||||
return(`Seconds ago: ${parseInt(currVal.x * refresh)}</br>
|
|
||||||
Value: ${currVal.y}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
if(self.chartStore[chartName]
|
|
||||||
&& self.old_preferences[prefName] != self.preferences[prefName]) {
|
|
||||||
self.clearChartFromStore(chartName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(self.chartStore[chartName]) {
|
|
||||||
let chartObj = self.chartStore[chartName].chartObj;
|
|
||||||
chartObj.setOptions(options, false);
|
|
||||||
chartObj.setTooltipFormatter(
|
|
||||||
tooltipFormatter.bind(null, self.preferences[prefName])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!self.chartStore[chartName]) {
|
|
||||||
|
|
||||||
let chartObj = new charting.Chart(container, options);
|
|
||||||
|
|
||||||
chartObj.setTooltipFormatter(
|
|
||||||
tooltipFormatter.bind(null, self.preferences[prefName])
|
|
||||||
);
|
|
||||||
|
|
||||||
self.chartStore[chartName] = {
|
|
||||||
'chartObj' : chartObj,
|
|
||||||
'intervalId' : undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.chartStore[chartName]['intervalId'] = self.renderChartLoop(
|
|
||||||
self.chartStore[chartName]['chartObj'], self.sid, self.did, url,
|
|
||||||
counter, self.preferences[prefName]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Handler function to support the "Add Server" link
|
// Handler function to support the "Add Server" link
|
||||||
|
@ -683,14 +719,13 @@ define('pgadmin.dashboard', [
|
||||||
clearChartFromStore: function(chartName) {
|
clearChartFromStore: function(chartName) {
|
||||||
var self = this;
|
var self = this;
|
||||||
if(!chartName){
|
if(!chartName){
|
||||||
_.each(self.chartStore, function(chart, key) {
|
self.stopChartsPoller();
|
||||||
clearInterval(chart.intervalId);
|
_.each(self.chart_store, function(chart, key) {
|
||||||
delete self.chartStore[key];
|
delete self.chart_store[key];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
clearInterval(self.chartStore[chartName].intervalId);
|
delete self.chart_store[chartName];
|
||||||
delete self.chartStore[chartName];
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -797,26 +832,37 @@ define('pgadmin.dashboard', [
|
||||||
|
|
||||||
if(self.preferences.show_graphs) {
|
if(self.preferences.show_graphs) {
|
||||||
// Render the graphs
|
// Render the graphs
|
||||||
pgAdmin.Dashboard.render_chart(
|
pgAdmin.Dashboard.renderCharts([{
|
||||||
div_sessions, url_for('dashboard.session_stats'), options_line, false,
|
chart_name: 'session_stats',
|
||||||
'session_stats', 'session_stats_refresh'
|
container: div_sessions,
|
||||||
);
|
options: options_line,
|
||||||
pgAdmin.Dashboard.render_chart(
|
counter: false,
|
||||||
div_tps, url_for('dashboard.tps_stats'), options_line, true,
|
refresh_pref_name: 'session_stats_refresh',
|
||||||
'tps_stats','tps_stats_refresh'
|
}, {
|
||||||
);
|
chart_name: 'tps_stats',
|
||||||
pgAdmin.Dashboard.render_chart(
|
container: div_tps,
|
||||||
div_ti, url_for('dashboard.ti_stats'), options_line, true,
|
options: options_line,
|
||||||
'ti_stats', 'ti_stats_refresh'
|
counter: true,
|
||||||
);
|
refresh_pref_name: 'tps_stats_refresh',
|
||||||
pgAdmin.Dashboard.render_chart(
|
}, {
|
||||||
div_to, url_for('dashboard.to_stats'), options_line, true,
|
chart_name: 'ti_stats',
|
||||||
'to_stats','to_stats_refresh'
|
container: div_ti,
|
||||||
);
|
options: options_line,
|
||||||
pgAdmin.Dashboard.render_chart(
|
counter: true,
|
||||||
div_bio, url_for('dashboard.bio_stats'), options_line, true,
|
refresh_pref_name: 'ti_stats_refresh',
|
||||||
'bio_stats','bio_stats_refresh'
|
}, {
|
||||||
);
|
chart_name: 'to_stats',
|
||||||
|
container: div_to,
|
||||||
|
options: options_line,
|
||||||
|
counter: true,
|
||||||
|
refresh_pref_name: 'to_stats_refresh',
|
||||||
|
}, {
|
||||||
|
chart_name: 'bio_stats',
|
||||||
|
container: div_bio,
|
||||||
|
options: options_line,
|
||||||
|
counter: true,
|
||||||
|
refresh_pref_name: 'bio_stats_refresh',
|
||||||
|
}]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!self.preferences.show_graphs && !self.preferences.show_activity) {
|
if(!self.preferences.show_graphs && !self.preferences.show_activity) {
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
/*pga4dash*/
|
|
||||||
SELECT
|
|
||||||
(SELECT sum(blks_read) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Reads') }}",
|
|
||||||
(SELECT sum(blks_hit) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Hits') }}"
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
{% set add_union = false %}
|
||||||
|
{% if 'session_stats' in chart_names %}
|
||||||
|
{% set add_union = true %}
|
||||||
|
SELECT 'session_stats' AS chart_name, row_to_json(t) AS chart_data
|
||||||
|
FROM (SELECT
|
||||||
|
(SELECT count(*) FROM pg_stat_activity{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Total') }}",
|
||||||
|
(SELECT count(*) FROM pg_stat_activity WHERE state = 'active'{% if did %} AND datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Active') }}",
|
||||||
|
(SELECT count(*) FROM pg_stat_activity WHERE state = 'idle'{% if did %} AND datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Idle') }}"
|
||||||
|
) t
|
||||||
|
{% endif %}
|
||||||
|
{% if add_union and 'tps_stats' in chart_names %}
|
||||||
|
UNION ALL
|
||||||
|
{% endif %}
|
||||||
|
{% if 'tps_stats' in chart_names %}
|
||||||
|
{% set add_union = true %}
|
||||||
|
SELECT 'tps_stats' AS chart_name, row_to_json(t) AS chart_data
|
||||||
|
FROM (SELECT
|
||||||
|
(SELECT sum(xact_commit) + sum(xact_rollback) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Transactions') }}",
|
||||||
|
(SELECT sum(xact_commit) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Commits') }}",
|
||||||
|
(SELECT sum(xact_rollback) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Rollbacks') }}"
|
||||||
|
) t
|
||||||
|
{% endif %}
|
||||||
|
{% if add_union and 'ti_stats' in chart_names %}
|
||||||
|
UNION ALL
|
||||||
|
{% endif %}
|
||||||
|
{% if 'ti_stats' in chart_names %}
|
||||||
|
{% set add_union = true %}
|
||||||
|
SELECT 'ti_stats' AS chart_name, row_to_json(t) AS chart_data
|
||||||
|
FROM (SELECT
|
||||||
|
(SELECT sum(tup_inserted) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Inserts') }}",
|
||||||
|
(SELECT sum(tup_updated) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Updates') }}",
|
||||||
|
(SELECT sum(tup_deleted) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Deletes') }}"
|
||||||
|
) t
|
||||||
|
{% endif %}
|
||||||
|
{% if add_union and 'to_stats' in chart_names %}
|
||||||
|
UNION ALL
|
||||||
|
{% endif %}
|
||||||
|
{% if 'to_stats' in chart_names %}
|
||||||
|
{% set add_union = true %}
|
||||||
|
SELECT 'to_stats' AS chart_name, row_to_json(t) AS chart_data
|
||||||
|
FROM (SELECT
|
||||||
|
(SELECT sum(tup_fetched) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Fetched') }}",
|
||||||
|
(SELECT sum(tup_returned) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Returned') }}"
|
||||||
|
) t
|
||||||
|
{% endif %}
|
||||||
|
{% if add_union and 'bio_stats' in chart_names %}
|
||||||
|
UNION ALL
|
||||||
|
{% endif %}
|
||||||
|
{% if 'bio_stats' in chart_names %}
|
||||||
|
{% set add_union = true %}
|
||||||
|
SELECT 'bio_stats' AS chart_name, row_to_json(t) AS chart_data
|
||||||
|
FROM (SELECT
|
||||||
|
(SELECT sum(blks_read) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Reads') }}",
|
||||||
|
(SELECT sum(blks_hit) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Hits') }}"
|
||||||
|
) t
|
||||||
|
{% endif %}
|
|
@ -1,5 +0,0 @@
|
||||||
/*pga4dash*/
|
|
||||||
SELECT
|
|
||||||
(SELECT count(*) FROM pg_stat_activity{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Total') }}",
|
|
||||||
(SELECT count(*) FROM pg_stat_activity WHERE state = 'active'{% if did %} AND datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Active') }}",
|
|
||||||
(SELECT count(*) FROM pg_stat_activity WHERE state = 'idle'{% if did %} AND datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Idle') }}"
|
|
|
@ -1,5 +0,0 @@
|
||||||
/*pga4dash*/
|
|
||||||
SELECT
|
|
||||||
(SELECT sum(tup_inserted) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Inserts') }}",
|
|
||||||
(SELECT sum(tup_updated) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Updates') }}",
|
|
||||||
(SELECT sum(tup_deleted) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Deletes') }}"
|
|
|
@ -1,4 +0,0 @@
|
||||||
/*pga4dash*/
|
|
||||||
SELECT
|
|
||||||
(SELECT sum(tup_fetched) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Fetched') }}",
|
|
||||||
(SELECT sum(tup_returned) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Returned') }}"
|
|
|
@ -1,5 +0,0 @@
|
||||||
/*pga4dash*/
|
|
||||||
SELECT
|
|
||||||
(SELECT sum(xact_commit) + sum(xact_rollback) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Transactions') }}",
|
|
||||||
(SELECT sum(xact_commit) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Commits') }}",
|
|
||||||
(SELECT sum(xact_rollback) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Rollbacks') }}"
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
{% set add_union = false %}
|
||||||
|
{% if 'session_stats' in chart_names %}
|
||||||
|
{% set add_union = true %}
|
||||||
|
SELECT 'session_stats' AS chart_name, row_to_json(t) AS chart_data
|
||||||
|
FROM (SELECT
|
||||||
|
(SELECT count(*) FROM pg_stat_activity{% if did %} WHERE datid = {{ did }} {% endif %}) AS "{{ _('Total') }}",
|
||||||
|
(SELECT count(*) FROM pg_stat_activity WHERE current_query NOT LIKE '<IDLE>%'{% if did %} AND datid = {{ did }} {% endif %}) AS "{{ _('Active') }}",
|
||||||
|
(SELECT count(*) FROM pg_stat_activity WHERE current_query LIKE '<IDLE>%'{% if did %} AND datid = {{ did }} {% endif %}) AS "{{ _('Idle') }}"
|
||||||
|
) t
|
||||||
|
{% endif %}
|
||||||
|
{% if add_union and 'tps_stats' in chart_names %}
|
||||||
|
UNION ALL
|
||||||
|
{% endif %}
|
||||||
|
{% if 'tps_stats' in chart_names %}
|
||||||
|
{% set add_union = true %}
|
||||||
|
SELECT 'tps_stats' AS chart_name, row_to_json(t) AS chart_data
|
||||||
|
FROM (SELECT
|
||||||
|
(SELECT sum(xact_commit) + sum(xact_rollback) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Transactions') }}",
|
||||||
|
(SELECT sum(xact_commit) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Commits') }}",
|
||||||
|
(SELECT sum(xact_rollback) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Rollbacks') }}"
|
||||||
|
) t
|
||||||
|
{% endif %}
|
||||||
|
{% if add_union and 'ti_stats' in chart_names %}
|
||||||
|
UNION ALL
|
||||||
|
{% endif %}
|
||||||
|
{% if 'ti_stats' in chart_names %}
|
||||||
|
{% set add_union = true %}
|
||||||
|
SELECT 'ti_stats' AS chart_name, row_to_json(t) AS chart_data
|
||||||
|
FROM (SELECT
|
||||||
|
(SELECT sum(tup_inserted) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Inserts') }}",
|
||||||
|
(SELECT sum(tup_updated) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Updates') }}",
|
||||||
|
(SELECT sum(tup_deleted) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Deletes') }}"
|
||||||
|
) t
|
||||||
|
{% endif %}
|
||||||
|
{% if add_union and 'to_stats' in chart_names %}
|
||||||
|
UNION ALL
|
||||||
|
{% endif %}
|
||||||
|
{% if 'to_stats' in chart_names %}
|
||||||
|
{% set add_union = true %}
|
||||||
|
SELECT 'to_stats' AS chart_name, row_to_json(t) AS chart_data
|
||||||
|
FROM (SELECT
|
||||||
|
(SELECT sum(tup_fetched) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Fetched') }}",
|
||||||
|
(SELECT sum(tup_returned) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Returned') }}"
|
||||||
|
) t
|
||||||
|
{% endif %}
|
||||||
|
{% if add_union and 'bio_stats' in chart_names %}
|
||||||
|
UNION ALL
|
||||||
|
{% endif %}
|
||||||
|
{% if 'bio_stats' in chart_names %}
|
||||||
|
{% set add_union = true %}
|
||||||
|
SELECT 'bio_stats' AS chart_name, row_to_json(t) AS chart_data
|
||||||
|
FROM (SELECT
|
||||||
|
(SELECT sum(blks_read) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Reads') }}",
|
||||||
|
(SELECT sum(blks_hit) FROM pg_stat_database{% if did %} WHERE datname = (SELECT datname FROM pg_database WHERE oid = {{ did }}){% endif %}) AS "{{ _('Hits') }}"
|
||||||
|
) t
|
||||||
|
{% endif %}
|
|
@ -1,5 +0,0 @@
|
||||||
/*pga4dash*/
|
|
||||||
SELECT
|
|
||||||
(SELECT count(*) FROM pg_stat_activity{% if did %} WHERE datid = {{ did }} {% endif %}) AS "{{ _('Total') }}",
|
|
||||||
(SELECT count(*) FROM pg_stat_activity WHERE current_query NOT LIKE '<IDLE>%'{% if did %} AND datid = {{ did }} {% endif %}) AS "{{ _('Active') }}",
|
|
||||||
(SELECT count(*) FROM pg_stat_activity WHERE current_query LIKE '<IDLE>%'{% if did %} AND datid = {{ did }} {% endif %}) AS "{{ _('Idle') }}"
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
##########################################################################
|
||||||
|
#
|
||||||
|
# pgAdmin 4 - PostgreSQL Tools
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 - 2019, The pgAdmin Development Team
|
||||||
|
# This software is released under the PostgreSQL Licence
|
||||||
|
#
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
from pgadmin.utils.route import BaseTestGenerator
|
||||||
|
from pgadmin.utils import server_utils as server_utils
|
||||||
|
import simplejson as json
|
||||||
|
|
||||||
|
|
||||||
|
class DashboardGraphsTestCase(BaseTestGenerator):
|
||||||
|
"""
|
||||||
|
This class validates the version in range functionality
|
||||||
|
by defining different version scenarios; where dict of
|
||||||
|
parameters describes the scenario appended by test name.
|
||||||
|
"""
|
||||||
|
|
||||||
|
scenarios = [(
|
||||||
|
'TestCase for session_stats graph', dict(
|
||||||
|
sid=1,
|
||||||
|
did=-1,
|
||||||
|
chart_data={
|
||||||
|
'session_stats': ['Total', 'Active', 'Idle'],
|
||||||
|
}
|
||||||
|
)), (
|
||||||
|
'TestCase for tps_stats graph', dict(
|
||||||
|
sid=1,
|
||||||
|
did=-1,
|
||||||
|
chart_data={
|
||||||
|
'tps_stats': ['Transactions', 'Commits', 'Rollbacks'],
|
||||||
|
}
|
||||||
|
)), (
|
||||||
|
'TestCase for ti_stats graph', dict(
|
||||||
|
sid=1,
|
||||||
|
did=-1,
|
||||||
|
chart_data={
|
||||||
|
'ti_stats': ['Inserts', 'Updates', 'Deletes'],
|
||||||
|
}
|
||||||
|
)), (
|
||||||
|
'TestCase for to_stats graph', dict(
|
||||||
|
sid=1,
|
||||||
|
did=-1,
|
||||||
|
chart_data={
|
||||||
|
'to_stats': ['Fetched', 'Returned'],
|
||||||
|
}
|
||||||
|
)), (
|
||||||
|
'TestCase for bio_stats graph', dict(
|
||||||
|
sid=1,
|
||||||
|
did=-1,
|
||||||
|
chart_data={
|
||||||
|
'bio_stats': ['Reads', 'Hits'],
|
||||||
|
}
|
||||||
|
)), (
|
||||||
|
'TestCase for two graphs', dict(
|
||||||
|
sid=1,
|
||||||
|
did=-1,
|
||||||
|
chart_data={
|
||||||
|
'session_stats': ['Total', 'Active', 'Idle'],
|
||||||
|
'bio_stats': ['Reads', 'Hits'],
|
||||||
|
}
|
||||||
|
)), (
|
||||||
|
'TestCase for five graphs', dict(
|
||||||
|
sid=1,
|
||||||
|
did=-1,
|
||||||
|
chart_data={
|
||||||
|
'session_stats': ['Total', 'Active', 'Idle'],
|
||||||
|
'tps_stats': ['Transactions', 'Commits', 'Rollbacks'],
|
||||||
|
'ti_stats': ['Inserts', 'Updates', 'Deletes'],
|
||||||
|
'to_stats': ['Fetched', 'Returned'],
|
||||||
|
'bio_stats': ['Reads', 'Hits'],
|
||||||
|
}
|
||||||
|
)), (
|
||||||
|
'TestCase for no graph', dict(
|
||||||
|
sid=1,
|
||||||
|
did=-1,
|
||||||
|
chart_data={},
|
||||||
|
))
|
||||||
|
]
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def getStatsUrl(self, sid=-1, did=-1, chart_names=''):
|
||||||
|
base_url = '/dashboard/dashboard_stats'
|
||||||
|
base_url = base_url + '/' + str(sid)
|
||||||
|
base_url += '/' + str(did) if did > 0 else ''
|
||||||
|
base_url += '?chart_names=' + chart_names
|
||||||
|
return base_url
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
server_response = server_utils.connect_server(self, self.sid)
|
||||||
|
if server_response["info"] == "Server connected.":
|
||||||
|
|
||||||
|
url = self.getStatsUrl(self.sid, self.did,
|
||||||
|
",".join(self.chart_data.keys()))
|
||||||
|
response = self.tester.get(url)
|
||||||
|
self.assertEquals(response.status_code, 200)
|
||||||
|
|
||||||
|
resp_data = json.loads(response.data)
|
||||||
|
|
||||||
|
# All requested charts received
|
||||||
|
self.assertEquals(len(resp_data.keys()),
|
||||||
|
len(self.chart_data.keys()))
|
||||||
|
|
||||||
|
# All requested charts data received
|
||||||
|
for chart_name, chart_vals in self.chart_data.items():
|
||||||
|
self.assertEquals(set(resp_data[chart_name].keys()),
|
||||||
|
set(chart_vals))
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise Exception("Error while connecting server to add the"
|
||||||
|
" database.")
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
pass
|
|
@ -46,3 +46,27 @@ let isString = (str) => (_.isString(str));
|
||||||
export {
|
export {
|
||||||
isValidData, isFunction, isString,
|
isValidData, isFunction, isString,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function getEpoch(inp_date) {
|
||||||
|
let date_obj = inp_date ? inp_date : new Date();
|
||||||
|
return parseInt(date_obj.getTime()/1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Eucladian GCD */
|
||||||
|
export function getGCD(inp_arr) {
|
||||||
|
let gcd_for_two = (a, b) => {
|
||||||
|
return a == 0?b:gcd_for_two(b % a, a);
|
||||||
|
};
|
||||||
|
|
||||||
|
let inp_len = inp_arr.length;
|
||||||
|
if(inp_len <= 2) {
|
||||||
|
return gcd_for_two(inp_arr[0], inp_arr[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = inp_arr[0];
|
||||||
|
for(let i=1; i<inp_len; i++) {
|
||||||
|
result = gcd_for_two(inp_arr[i], result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -73,6 +73,10 @@ describe('In charting related testcases', ()=> {
|
||||||
expect(chartObj.getOtherData('some_val')).toEqual(1);
|
expect(chartObj.getOtherData('some_val')).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Check if other data returns undefined for not set', ()=>{
|
||||||
|
expect(chartObj.getOtherData('some_val_not_set')).toBe(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
it('Check if isVisible returns correct', ()=>{
|
it('Check if isVisible returns correct', ()=>{
|
||||||
let dimSpy = spyOn(chartObj, 'getContainerDimensions');
|
let dimSpy = spyOn(chartObj, 'getContainerDimensions');
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// pgAdmin 4 - PostgreSQL Tools
|
||||||
|
//
|
||||||
|
// Copyright (C) 2013 - 2019, The pgAdmin Development Team
|
||||||
|
// This software is released under the PostgreSQL Licence
|
||||||
|
//
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
import { getEpoch, getGCD } from 'sources/utils';
|
||||||
|
|
||||||
|
describe('getEpoch', function () {
|
||||||
|
it('should return non zero', function () {
|
||||||
|
expect(getEpoch()).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return epoch for a date passed', function () {
|
||||||
|
let someDate = new Date(2019,1,1,10,20,30,40),
|
||||||
|
someDateEpoch = 1548996630;
|
||||||
|
|
||||||
|
expect(getEpoch(new Date(someDate))).toEqual(someDateEpoch);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getGCD', function () {
|
||||||
|
it('for two numbers', function () {
|
||||||
|
let nos = [5, 10];
|
||||||
|
expect(getGCD(nos)).toEqual(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('for more than two numbers', function () {
|
||||||
|
let nos = [9, 24, 33];
|
||||||
|
expect(getGCD(nos)).toEqual(3);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue