Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Persistent Dashboard refresh settings. #814

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions caravel/assets/javascripts/dashboard/Dashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ function dashboardContainer(dashboardData) {
filters: {},
init() {
this.initDashboardView();
this.firstLoad = true;
px.initFavStars();
const sliceObjects = [];
const dash = this;
Expand All @@ -64,7 +63,7 @@ function dashboardContainer(dashboardData) {
this.slices = sliceObjects;
this.refreshTimer = null;
this.loadPreSelectFilters();
this.startPeriodicRender(0);
this.startPeriodicRender();
this.bindResizeToWindowResize();
},
loadPreSelectFilters() {
Expand Down Expand Up @@ -131,14 +130,12 @@ function dashboardContainer(dashboardData) {
const maxRandomDelay = Math.min(interval * 0.2, 5000);
const refreshAll = function () {
dash.slices.forEach(function (slice) {
const force = !dash.firstLoad;
setTimeout(function () {
slice.render(force);
},
// Randomize to prevent all widgets refreshing at the same time
maxRandomDelay * Math.random());
});
dash.firstLoad = false;
};

const fetchAndRender = function () {
Expand All @@ -149,7 +146,9 @@ function dashboardContainer(dashboardData) {
}, interval);
}
};
fetchAndRender();
if (interval > 0){
fetchAndRender();
}
},
refreshExcept(sliceId) {
const immune = this.metadata.filter_immune_slices || [];
Expand Down Expand Up @@ -244,6 +243,8 @@ function dashboardContainer(dashboardData) {
positions,
css: this.editor.getValue(),
expanded_slices: expandedSlices,
autorefresh_seconds: dashboard.autorefresh_seconds,
autorefresh_from_cache: dashboard.autorefresh_from_cache
};
$.ajax({
type: 'POST',
Expand Down Expand Up @@ -323,9 +324,10 @@ function dashboardContainer(dashboardData) {
dashboard.readFilters(),
});
});
$('#refresh_dash_interval').on('change', function () {
const interval = $(this).find('option:selected').val() * 1000;
dashboard.startPeriodicRender(interval);
$("#refresh_dash_apply").click(function () {
dashboard.autorefresh_seconds = $("#refresh_dash_interval").val();
dashboard.autorefresh_from_cache = $("#refresh_dach_from_cache").is(':checked');
dashboard.startPeriodicRender();
});
$('#refresh_dash').click(() => {
dashboard.slices.forEach((slice) => {
Expand Down
59 changes: 54 additions & 5 deletions caravel/migrations/versions/27ae655e4247_make_creator_owners.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,67 @@
down_revision = 'd8bc074f7aad'

from alembic import op
from caravel import db, models
from caravel import db

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy import (
Column, Integer, ForeignKey, Table)

Base = declarative_base()

slice_user = Table(
'slice_user', Base.metadata,
Column('id', Integer, primary_key=True),
Column('user_id', Integer, ForeignKey('ab_user.id')),
Column('slice_id', Integer, ForeignKey('slices.id'))
)

dashboard_user = Table(
'dashboard_user', Base.metadata,
Column('id', Integer, primary_key=True),
Column('user_id', Integer, ForeignKey('ab_user.id')),
Column('dashboard_id', Integer, ForeignKey('dashboards.id'))
)


class User(Base):

"""Declarative class to do query in upgrade"""

__tablename__ = 'ab_user'
id = Column(Integer, primary_key=True)


class Slice(Base):

"""Declarative class to do query in upgrade"""

__tablename__ = 'slices'
id = Column(Integer, primary_key=True)
owners = relationship("User", secondary=slice_user)
created_by_fk = Column(Integer)


class Dashboard(Base):

"""Declarative class to do query in upgrade"""

__tablename__ = 'dashboards'
id = Column(Integer, primary_key=True)
owners = relationship("User", secondary=dashboard_user)
created_by_fk = Column(Integer)


def upgrade():
bind = op.get_bind()
session = db.Session(bind=bind)

objects = session.query(models.Slice).all()
objects += session.query(models.Dashboard).all()
objects = session.query(Slice).all()
objects += session.query(Dashboard).all()
for obj in objects:
if obj.created_by and obj.created_by not in obj.owners:
obj.owners.append(obj.created_by)
if obj.created_by_fk and obj.created_by_fk not in obj.owners:
obj.owners.append(obj.created_by_fk)
session.commit()
session.close()

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""Adding Columns for Dashboard Refresh Properties

Revision ID: 79aef3baedae
Revises: f162a1dea4c4
Create Date: 2016-07-02 14:51:00.106192

"""

# revision identifiers, used by Alembic.
revision = '79aef3baedae'
down_revision = 'f162a1dea4c4'

from alembic import op
import sqlalchemy as sa


def upgrade():
try:
op.add_column('dashboards', sa.Column('autorefresh_from_cache', sa.Boolean(), nullable=False, server_default='True'))
except:
# To pick up databases (like some MySQL variants) without a true Boolean value
op.add_column('dashboards', sa.Column('autorefresh_from_cache', sa.Boolean(), nullable=False, server_default='1'))

op.add_column('dashboards', sa.Column('autorefresh_seconds', sa.Integer(), nullable=False, server_default='0'))


def downgrade():
op.drop_column('dashboards', 'autorefresh_seconds')
op.drop_column('dashboards', 'autorefresh_from_cache')
6 changes: 6 additions & 0 deletions caravel/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,9 @@ class Dashboard(Model, AuditMixinNullable):
slices = relationship(
'Slice', secondary=dashboard_slices, backref='dashboards')
owners = relationship("User", secondary=dashboard_user)
# A zero for autorefresh seconds implies no autorefresh
autorefresh_seconds = Column(Integer, default=0, nullable=False)
autorefresh_from_cache = Column(Boolean, default=True, nullable=False)

def __repr__(self):
return self.dashboard_title
Expand All @@ -315,13 +318,16 @@ def dashboard_link(self):

@property
def json_data(self):
"""Returns the configuration data for the dashboard as json"""
d = {
'id': self.id,
'metadata': self.metadata_dejson,
'dashboard_title': self.dashboard_title,
'slug': self.slug,
'slices': [slc.data for slc in self.slices],
'position_json': json.loads(self.position_json) if self.position_json else [],
'autorefresh_seconds': self.autorefresh_seconds,
'autorefresh_from_cache': self.autorefresh_from_cache,
}
return json.dumps(d)

Expand Down
27 changes: 14 additions & 13 deletions caravel/templates/caravel/dashboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,22 @@ <h6><strong>Styling applies to this dashboard only</strong></h6>
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="myModalLabel">Refresh Interval</h4>
<h6><strong>Choose how frequent should the dashboard refresh</strong></h6>
<h4 class="modal-title" id="myModalLabel">Autorefresh Interval and Settings</h4>
<h6><strong>Choose if and how frequently the dashboard should refresh</strong></h6>
</div>
<div class="modal-body">
<select id="refresh_dash_interval" class="select2" style="margin-bottom: 5px;">
<option value="0">Don't refresh</option>
<option value="10">10 seconds</option>
<option value="30">30 seconds</option>
<option value="60">1 minute</option>
<option value="300">5 minutes</option>
</select><br>
<label for="refresh_dash_interval">Dashboard Refresh Interval</label>
<input id="refresh_dash_interval" value="{{ dashboard.autorefresh_seconds }}" />
<p class="help-block">Number of seconds between refreshes. Set to 0 for no auto-refresh</p>
<br>
<label for="refresh_dash_from_cache">Refresh from Cache</label>
<input type="checkbox" id="refresh_dash_force" {{ "checked" if dashboard.autorefresh_from_cache }}/>
<p class="help-block">If unchecked the dashboard will force fresh values from the database. While checked the dashboard will allow values from the cache resulting in better performance for multiple users.</p>
<br>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">
Close
<button type="button" class="btn btn-default" data-dismiss="modal" id="refresh_dash_apply">
Apply and Close
</button>
</div>
</div>
Expand All @@ -84,7 +85,7 @@ <h2>
<i class="fa fa-plus" data-toggle="tooltip" title="Add a new slice to the dashboard"></i>
</button>
<button type="button" id="refresh_dash_periodic" class="btn btn-default" data-toggle="modal" data-target="#refresh_modal">
<i class="fa fa-clock-o" data-toggle="tooltip" title="Decide how frequent should the dashboard refresh"></i>
<i class="fa fa-clock-o" data-toggle="tooltip" title="Decide if and how frequently should the dashboard refresh"></i>
</button>
<button type="button" id="filters" class="btn btn-default" data-toggle="tooltip" title="View the list of active filters">
<i class="fa fa-filter"></i>
Expand All @@ -95,7 +96,7 @@ <h2>
<a id="editdash" class="btn btn-default {{ "disabled disabledButton" if not dash_edit_perm }} " href="/dashboardmodelview/edit/{{ dashboard.id }}" title="Edit this dashboard's property" data-toggle="tooltip" >
<i class="fa fa-edit"></i>
</a>
<button type="button" id="savedash" class="btn btn-default {{ "disabled disabledButton" if not dash_save_perm }}" data-toggle="tooltip" title="Save the current positioning and CSS">
<button type="button" id="savedash" class="btn btn-default {{ "disabled disabledButton" if not dash_save_perm }}" data-toggle="tooltip" title="Save the current refresh settings, positioning and CSS">
<i class="fa fa-save"></i>
</button>
</div>
Expand Down
18 changes: 16 additions & 2 deletions caravel/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -628,12 +628,22 @@ class DashboardModelView(CaravelModelView, DeleteMixin): # noqa
datamodel = SQLAInterface(models.Dashboard)
list_columns = ['dashboard_link', 'creator', 'modified']
edit_columns = [
'dashboard_title', 'slug', 'slices', 'owners', 'position_json', 'css',
'json_metadata']
'dashboard_title', 'slug', 'slices', 'owners',
'autorefresh_seconds', 'autorefresh_from_cache',
'position_json', 'css', 'json_metadata']
show_columns = edit_columns + ['table_names']
add_columns = edit_columns
base_order = ('changed_on', 'desc')
description_columns = {
'autorefresh_seconds': _(
"The number of seconds between automatic refreshes "
"of the dashboard. The default value of 0 means the "
"dashboard will not automatically refresh."),
'autorefresh_from_cache': _(
"If checked the dashboard will use cached values when "
"refreshing. To force the dashboard to always use fresh "
"values then uncheck this option. Performance with many "
"users will be lower if unchecked."),
'position_json': _(
"This json object describes the positioning of the widgets in "
"the dashboard. It is dynamically generated when adjusting "
Expand All @@ -660,6 +670,8 @@ class DashboardModelView(CaravelModelView, DeleteMixin): # noqa
'owners': _("Owners"),
'creator': _("Creator"),
'modified': _("Modified"),
'autorefresh_seconds': _("Dashboard Refresh Frequency"),
'autorefresh_from_cache': _("Refresh From Cache"),
'position_json': _("Position JSON"),
'css': _("CSS"),
'json_metadata': _("JSON Metadata"),
Expand Down Expand Up @@ -1066,6 +1078,8 @@ def save_dash(self, dashboard_id):
md['expanded_slices'] = data['expanded_slices']
dash.json_metadata = json.dumps(md, indent=4)
dash.css = data['css']
dash.autorefresh_seconds = data['autorefresh_seconds']
dash.autorefresh_from_cache = data['autorefresh_from_cache']
session.merge(dash)
session.commit()
session.close()
Expand Down
2 changes: 2 additions & 0 deletions tests/core_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@ def test_save_dash(self, username='admin'):
'css': '',
'expanded_slices': {},
'positions': positions,
'autorefresh_seconds': 60,
'autorefresh_from_cache': True,
}
url = '/caravel/save_dash/{}/'.format(dash.id)
resp = self.client.post(url, data=dict(data=json.dumps(data)))
Expand Down