Allow dashboard tables and charts to be enabled/disabled. Fixes #2951

This commit is contained in:
Murtuza Zabuawala 2018-02-26 09:20:04 +00:00 committed by Dave Page
parent 54dd6aae83
commit 801a2084e9
8 changed files with 974 additions and 574 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

View File

@ -60,6 +60,14 @@ Use the fields on the *Graphs* panel to specify your display preferences for the
* Use the *Tuples out refresh rate* field to specify the number of seconds between tuples-out samples displayed in graphs.
.. image:: images/preferences_dashboard_display.png
:alt: Preferences dialog dashboard display options
* When the *Show graphs?* switch is set to *True*, graphs will be displayed on dashboards.
* When the *Show activity?* switch is set to *True*, activity tables will be displayed on dashboards.
**The Debugger Node**
Expand the *Debugger* node to specify your debugger display preferences.

View File

@ -115,6 +115,22 @@ class DashboardModule(PgAdminModule):
help_str=gettext('The number of seconds between graph samples.')
)
self.display_graphs = self.dashboard_preference.register(
'display', 'show_graphs',
gettext("Show graphs?"), 'boolean', True,
category_label=gettext('Display'),
help_str=gettext('If set to True, graphs '
'will be displayed on dashboards.')
)
self.display_server_activity = self.dashboard_preference.register(
'display', 'show_activity',
gettext("Show activity?"), 'boolean', True,
category_label=gettext('Display'),
help_str=gettext('If set to True, activity tables '
'will be displayed on dashboards.')
)
def get_exposed_url_endpoints(self):
"""
Returns:
@ -262,6 +278,7 @@ def index(sid=None, did=None):
"""
rates = {}
settings = {}
prefs = Preferences.module('dashboards')
@ -286,6 +303,11 @@ def index(sid=None, did=None):
rates['to_stats_refresh'] = to_stats_refresh_pref.get()
bio_stats_refresh_pref = prefs.preference('bio_stats_refresh')
rates['bio_stats_refresh'] = bio_stats_refresh_pref.get()
# Whether to display graphs and server activity preferences
show_graphs_pref = prefs.preference('show_graphs')
settings['show_graphs'] = show_graphs_pref.get()
show_activity_pref = prefs.preference('show_activity')
settings['show_activity'] = show_activity_pref.get()
# Show the appropriate dashboard based on the identifiers passed to us
if sid is None and did is None:
@ -295,7 +317,8 @@ def index(sid=None, did=None):
'/dashboard/server_dashboard.html',
sid=sid,
rates=rates,
version=g.version
version=g.version,
settings=settings
)
else:
return render_template(
@ -303,7 +326,8 @@ def index(sid=None, did=None):
sid=sid,
did=did,
rates=rates,
version=g.version
version=g.version,
settings=settings
)

View File

@ -609,7 +609,17 @@ define('pgadmin.dashboard', [
},
// Rock n' roll on the server dashboard
init_server_dashboard: function(sid, version, session_stats_refresh, tps_stats_refresh, ti_stats_refresh, to_stats_refresh, bio_stats_refresh) {
init_server_dashboard: function(
sid,
version,
session_stats_refresh,
tps_stats_refresh,
ti_stats_refresh,
to_stats_refresh,
bio_stats_refresh,
show_graphs,
show_server_activity
) {
var div_sessions = $('.dashboard-container').find('#graph-sessions')[0];
var div_tps = $('.dashboard-container').find('#graph-tps')[0];
var div_ti = $('.dashboard-container').find('#graph-ti')[0];
@ -649,6 +659,38 @@ define('pgadmin.dashboard', [
},
};
// Display graphs
if(show_graphs) {
// Render the graphs
pgAdmin.Dashboard.render_chart(
div_sessions, data_sessions, dataset_sessions, sid, did,
url_for('dashboard.session_stats'), options_line, false,
session_stats_refresh
);
pgAdmin.Dashboard.render_chart(
div_tps, data_tps, dataset_tps, sid, did,
url_for('dashboard.tps_stats'), options_line, true,
tps_stats_refresh
);
pgAdmin.Dashboard.render_chart(
div_ti, data_ti, dataset_ti, sid, did,
url_for('dashboard.ti_stats'), options_line, true,
ti_stats_refresh
);
pgAdmin.Dashboard.render_chart(
div_to, data_to, dataset_to, sid, did,
url_for('dashboard.to_stats'), options_line, true,
to_stats_refresh
);
pgAdmin.Dashboard.render_chart(
div_bio, data_bio, dataset_bio, sid, did,
url_for('dashboard.bio_stats'), options_line, true,
bio_stats_refresh
);
}
// Display server activity
if (show_server_activity) {
var server_activity_columns = [{
name: 'pid',
label: gettext('PID'),
@ -874,33 +916,6 @@ define('pgadmin.dashboard', [
cell: 'string',
}];
// Render the graphs
pgAdmin.Dashboard.render_chart(
div_sessions, data_sessions, dataset_sessions, sid, did,
url_for('dashboard.session_stats'), options_line, false,
session_stats_refresh
);
pgAdmin.Dashboard.render_chart(
div_tps, data_tps, dataset_tps, sid, did,
url_for('dashboard.tps_stats'), options_line, true,
tps_stats_refresh
);
pgAdmin.Dashboard.render_chart(
div_ti, data_ti, dataset_ti, sid, did,
url_for('dashboard.ti_stats'), options_line, true,
ti_stats_refresh
);
pgAdmin.Dashboard.render_chart(
div_to, data_to, dataset_to, sid, did,
url_for('dashboard.to_stats'), options_line, true,
to_stats_refresh
);
pgAdmin.Dashboard.render_chart(
div_bio, data_bio, dataset_bio, sid, did,
url_for('dashboard.bio_stats'), options_line, true,
bio_stats_refresh
);
// To align subnode controls properly
$(div_server_activity).addClass('pg-el-container');
$(div_server_activity).attr('el', 'sm');
@ -966,11 +981,22 @@ define('pgadmin.dashboard', [
break;
}
});
}
},
// Rock n' roll on the database dashboard
init_database_dashboard: function(sid, did, version, session_stats_refresh, tps_stats_refresh, ti_stats_refresh, to_stats_refresh, bio_stats_refresh) {
init_database_dashboard: function(
sid,
did,
version,
session_stats_refresh,
tps_stats_refresh,
ti_stats_refresh,
to_stats_refresh,
bio_stats_refresh,
show_graphs,
show_database_activity
) {
var div_sessions = document.getElementById('graph-sessions');
var div_tps = document.getElementById('graph-tps');
var div_ti = document.getElementById('graph-ti');
@ -1006,6 +1032,38 @@ define('pgadmin.dashboard', [
},
};
// Display graphs
if(show_graphs) {
// Render the graphs
pgAdmin.Dashboard.render_chart(
div_sessions, data_sessions, dataset_sessions, sid, did,
url_for('dashboard.session_stats'), options_line, false,
session_stats_refresh
);
pgAdmin.Dashboard.render_chart(
div_tps, data_tps, dataset_tps, sid, did,
url_for('dashboard.tps_stats'), options_line, true,
tps_stats_refresh
);
pgAdmin.Dashboard.render_chart(
div_ti, data_ti, dataset_ti, sid, did,
url_for('dashboard.ti_stats'), options_line, true,
ti_stats_refresh
);
pgAdmin.Dashboard.render_chart(
div_to, data_to, dataset_to, sid, did,
url_for('dashboard.to_stats'), options_line, true,
to_stats_refresh
);
pgAdmin.Dashboard.render_chart(
div_bio, data_bio, dataset_bio, sid, did,
url_for('dashboard.bio_stats'), options_line, true,
bio_stats_refresh
);
}
// Display server activity
if (show_database_activity) {
var database_activity_columns = [{
name: 'pid',
label: gettext('PID'),
@ -1072,7 +1130,6 @@ define('pgadmin.dashboard', [
obj['version'] = version;
});
// Add cancel active query button
database_activity_columns.unshift({
name: 'pg-backform-expand',
@ -1188,33 +1245,6 @@ define('pgadmin.dashboard', [
cell: 'string',
}];
// Render the graphs
pgAdmin.Dashboard.render_chart(
div_sessions, data_sessions, dataset_sessions, sid, did,
url_for('dashboard.session_stats'), options_line, false,
session_stats_refresh
);
pgAdmin.Dashboard.render_chart(
div_tps, data_tps, dataset_tps, sid, did,
url_for('dashboard.tps_stats'), options_line, true,
tps_stats_refresh
);
pgAdmin.Dashboard.render_chart(
div_ti, data_ti, dataset_ti, sid, did,
url_for('dashboard.ti_stats'), options_line, true,
ti_stats_refresh
);
pgAdmin.Dashboard.render_chart(
div_to, data_to, dataset_to, sid, did,
url_for('dashboard.to_stats'), options_line, true,
to_stats_refresh
);
pgAdmin.Dashboard.render_chart(
div_bio, data_bio, dataset_bio, sid, did,
url_for('dashboard.bio_stats'), options_line, true,
bio_stats_refresh
);
// To align subnode controls properly
$(div_database_activity).addClass('pg-el-container');
$(div_database_activity).attr('el', 'sm');
@ -1268,7 +1298,7 @@ define('pgadmin.dashboard', [
break;
}
});
}
},
toggleVisibility: function(flag) {
dashboardVisible = flag;

View File

@ -1,4 +1,5 @@
<div class="dashboard-container">
{% if settings.show_graphs %}
<div class="row">
<div class="col-xs-6">
<div class="obj_properties">
@ -49,8 +50,14 @@
<div id="graph-bio" class="graph-container"></div>
</div>
</div>
{% endif %}
{% if settings.show_activity %}
{# If we are displaying graphs then only we need space on top #}
{% if settings.show_graphs %}
<div class="row dashboard-header-spacer">
{% else %}
<div class="row">
{% endif %}
<div class="col-xs-12">
<div class="obj_properties">
<legend class="badge">{{ _('Database activity') }}</legend>
@ -101,9 +108,28 @@
</div>
</div>
</div>
{% endif %}
{% if not settings.show_graphs and not settings.show_activity %}
<div class="alert alert-info pg-panel-message" role="alert">
{{ _('All Dashboard elements are currently disabled.') }}
</div>
{% endif %}
</div>
<script type="text/javascript">
pgAdmin.Dashboard.init_database_dashboard({{ sid }}, {{ did }}, {{ version }}, {{ rates.session_stats_refresh }}, {{ rates.tps_stats_refresh }}, {{ rates.ti_stats_refresh }}, {{ rates.to_stats_refresh }}, {{ rates.bio_stats_refresh }} );
// Initiate database dashboard
pgAdmin.Dashboard.init_database_dashboard(
{{ sid }},
{{ did }},
{{ version }},
{{ rates.session_stats_refresh }},
{{ rates.tps_stats_refresh }},
{{ rates.ti_stats_refresh }},
{{ rates.to_stats_refresh }},
{{ rates.bio_stats_refresh }},
{{ settings.show_graphs|lower }},
{{ settings.show_activity|lower }}
);
</script>

View File

@ -1,4 +1,5 @@
<div class="dashboard-container">
{% if settings.show_graphs %}
<div class="row">
<div class="col-xs-6">
<div class="obj_properties">
@ -49,8 +50,14 @@
<div id="graph-bio" class="graph-container"></div>
</div>
</div>
{% endif %}
{% if settings.show_activity %}
{# If we are displaying graphs then only we need space on top #}
{% if settings.show_graphs %}
<div class="row dashboard-header-spacer">
{% else %}
<div class="row">
{% endif %}
<div class="col-xs-12">
<div class="obj_properties">
<legend class="badge">{{ _('Server activity') }}</legend>
@ -111,9 +118,27 @@
</div>
</div>
</div>
{% endif %}
{% if not settings.show_graphs and not settings.show_activity %}
<div class="alert alert-info pg-panel-message" role="alert">
{{ _('Dashboard has been disabled by user.') }}
</div>
{% endif %}
</div>
<script type="text/javascript">
pgAdmin.Dashboard.init_server_dashboard({{ sid }}, {{ version }}, {{ rates.session_stats_refresh }}, {{ rates.tps_stats_refresh }}, {{ rates.ti_stats_refresh }}, {{ rates.to_stats_refresh }}, {{ rates.bio_stats_refresh }} );
// Initiate server dashboard
pgAdmin.Dashboard.init_server_dashboard(
{{ sid }},
{{ version }},
{{ rates.session_stats_refresh }},
{{ rates.tps_stats_refresh }},
{{ rates.ti_stats_refresh }},
{{ rates.to_stats_refresh }},
{{ rates.bio_stats_refresh }},
{{ settings.show_graphs|lower }},
{{ settings.show_activity|lower }}
);
</script>

View File

@ -0,0 +1,8 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################

View File

@ -0,0 +1,279 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import os
import sys
from flask import Flask, render_template
from jinja2 import FileSystemLoader
from pgadmin import VersionedTemplateLoader
from pgadmin.utils.route import BaseTestGenerator
if sys.version_info < (3, 3):
from mock import MagicMock
else:
from unittest.mock import MagicMock
# Hard coded dummy input parameters for the templates
RATES = {
'session_stats_refresh': 1,
'tps_stats_refresh': 1,
'ti_stats_refresh': 1,
'to_stats_refresh': 1,
'bio_stats_refresh': 1
}
DISPLAY_DASHBOARD = {
'both': {
'show_graphs': True,
'show_activity': True
},
'only_graphs': {
'show_graphs': True,
'show_activity': False
},
'only_server_activity': {
'show_graphs': False,
'show_activity': True
},
'none': {
'show_graphs': False,
'show_activity': False
}
}
VERSION = 95000
SERVER_ID = 1
DATABASE_ID = 123
# To moke gettext function used in the template
_ = MagicMock(side_effect=lambda x: x)
class TestExplainPlanTemplates(BaseTestGenerator):
scenarios = [
# Server dashboard
(
'Dashboard, when returning the html page with graphs and '
'server activity related html elements for server dashboard',
dict(
template_path=os.path.join(
'dashboard', 'server_dashboard.html'
),
input_parameters=dict(
sid=SERVER_ID,
did=None,
rates=RATES,
version=VERSION,
settings=DISPLAY_DASHBOARD['both'],
_=_
),
expected_return_value=[
'Server sessions',
'Server activity'
],
not_expected_return_value=[]
)
),
(
'Dashboard, when returning the html page with only graphs '
'related html elements for server dashboard',
dict(
template_path=os.path.join(
'dashboard', 'server_dashboard.html'
),
input_parameters=dict(
sid=SERVER_ID,
did=None,
rates=RATES,
version=VERSION,
settings=DISPLAY_DASHBOARD['only_graphs'],
_=_
),
expected_return_value=[
'Server sessions'
],
not_expected_return_value=[
'Server activity'
]
)
),
(
'Dashboard, when returning the html page with only server '
'activity related html elements for server dashboard',
dict(
template_path=os.path.join(
'dashboard', 'server_dashboard.html'
),
input_parameters=dict(
sid=SERVER_ID,
did=None,
rates=RATES,
version=VERSION,
settings=DISPLAY_DASHBOARD['only_server_activity'],
_=_
),
expected_return_value=[
'Server activity'
],
not_expected_return_value=[
'Server sessions'
]
)
),
(
'Dashboard, when returning the html page with neither '
'graphs nor server activity related html elements for server '
'dashboard',
dict(
template_path=os.path.join(
'dashboard', 'server_dashboard.html'
),
input_parameters=dict(
sid=SERVER_ID,
did=None,
rates=RATES,
version=VERSION,
settings=DISPLAY_DASHBOARD['none'],
_=_
),
expected_return_value=[],
not_expected_return_value=[
'Server activity',
'Server sessions'
]
)
),
# Database dashboard
(
'Dashboard, when returning the html page with graphs and '
'database activity related html elements for database dashboard',
dict(
template_path=os.path.join(
'dashboard', 'database_dashboard.html'
),
input_parameters=dict(
sid=SERVER_ID,
did=DATABASE_ID,
rates=RATES,
version=VERSION,
settings=DISPLAY_DASHBOARD['both'],
_=_
),
expected_return_value=[
'Database sessions',
'Database activity'
],
not_expected_return_value=[]
)
),
(
'Dashboard, when returning the html page with only '
'graphs related html elements for database dashboard',
dict(
template_path=os.path.join(
'dashboard', 'database_dashboard.html'
),
input_parameters=dict(
sid=SERVER_ID,
did=DATABASE_ID,
rates=RATES,
version=VERSION,
settings=DISPLAY_DASHBOARD['only_graphs'],
_=_
),
expected_return_value=[
'Database sessions'
],
not_expected_return_value=[
'Database activity'
]
)
),
(
'Dashboard, when returning the html page with only '
'database activity related html elements for database dashboard',
dict(
template_path=os.path.join(
'dashboard', 'database_dashboard.html'
),
input_parameters=dict(
sid=SERVER_ID,
did=DATABASE_ID,
rates=RATES,
version=VERSION,
settings=DISPLAY_DASHBOARD['only_server_activity'],
_=_
),
expected_return_value=[
'Database activity'
],
not_expected_return_value=[
'Database sessions'
]
)
),
(
'Dashboard, when returning the html page with neither '
'graphs nor database activity related html elements for database '
'dashboard',
dict(
template_path=os.path.join(
'dashboard', 'database_dashboard.html'
),
input_parameters=dict(
sid=SERVER_ID,
did=DATABASE_ID,
rates=RATES,
version=VERSION,
settings=DISPLAY_DASHBOARD['none'],
_=_
),
expected_return_value=[],
not_expected_return_value=[
'Database sessions',
'Database activity'
]
)
),
]
def setUp(self):
self.loader = VersionedTemplateLoader(FakeApp())
def runTest(self):
with FakeApp().app_context():
result = render_template(
self.template_path, **self.input_parameters
)
# checks for expected html elements
for expected_string in self.expected_return_value:
self.assertIn(
expected_string, result
)
# checks for not expected html elements
for not_expected_string in self.not_expected_return_value:
self.assertNotIn(
not_expected_string, result
)
class FakeApp(Flask):
def __init__(self):
super(FakeApp, self).__init__("")
self.jinja_loader = FileSystemLoader(
os.path.dirname(os.path.realpath(__file__)) + "/../templates"
)