Skip to content

Commit

Permalink
Merge pull request #192 from EverythingMe/feature_markdown_widget
Browse files Browse the repository at this point in the history
Feature: text box widget that supports markdown
  • Loading branch information
arikfr committed Apr 29, 2014
2 parents 7d0324b + ef366df commit f23b434
Show file tree
Hide file tree
Showing 11 changed files with 213 additions and 116 deletions.
13 changes: 13 additions & 0 deletions migrations/add_text_to_widgets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from playhouse.migrate import Migrator
from redash import db
from redash import models


if __name__ == '__main__':
db.connect_db()
migrator = Migrator(db.database)
with db.database.transaction():
migrator.add_column(models.Widget, models.Widget.text, 'text')
migrator.set_nullable(models.Widget, models.Widget.visualization, True)

db.close_db(None)
4 changes: 2 additions & 2 deletions rd_ui/app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,11 @@
<script src="/bower_components/mousetrap/plugins/global-bind/mousetrap-global-bind.js"></script>
<script src="/bower_components/select2/select2.js"></script>
<script src="/bower_components/angular-ui-select2/src/select2.js"></script>

<script src="/bower_components/underscore.string/lib/underscore.string.js"></script>
<script src="/bower_components/marked/lib/marked.js"></script>
<script src="/scripts/ng_highchart.js"></script>
<script src="/scripts/ng_smart_table.js"></script>
<script src="/scripts/ui-bootstrap-tpls-0.5.0.min.js"></script>

<!-- endbuild -->

<!-- build:js({.tmp,app}) /scripts/scripts.js -->
Expand Down
41 changes: 23 additions & 18 deletions rd_ui/app/scripts/controllers/dashboard.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
(function() {
var DashboardCtrl = function($scope, Events, $routeParams, $http, $timeout, Dashboard) {
var DashboardCtrl = function($scope, Events, Widget, $routeParams, $http, $timeout, Dashboard) {
Events.record(currentUser, "view", "dashboard", dashboard.id);

$scope.refreshEnabled = false;
$scope.refreshRate = 60;
$scope.dashboard = Dashboard.get({
slug: $routeParams.dashboardSlug
}, function(dashboard) {
$scope.dashboard = Dashboard.get({ slug: $routeParams.dashboardSlug }, function (dashboard) {
$scope.$parent.pageTitle = dashboard.name;
$scope.dashboard.widgets = _.map($scope.dashboard.widgets, function (row) {
return _.map(row, function (widget) {
return new Widget(widget);
});
});
});

var autoRefresh = function() {
Expand Down Expand Up @@ -51,39 +54,41 @@
};
};

var WidgetCtrl = function($scope, Events, $http, $location, Query) {
var WidgetCtrl = function($scope, Events, Query) {
$scope.deleteWidget = function() {
if (!confirm('Are you sure you want to remove "' + $scope.widget.visualization.name + '" from the dashboard?')) {
if (!confirm('Are you sure you want to remove "' + $scope.widget.getName() + '" from the dashboard?')) {
return;
}

Events.record(currentUser, "delete", "widget", $scope.widget.id);

$http.delete('/api/widgets/' + $scope.widget.id).success(function() {
$scope.widget.$delete(function() {
$scope.dashboard.widgets = _.map($scope.dashboard.widgets, function(row) {
return _.filter(row, function(widget) {
return widget.id != $scope.widget.id;
return widget.id != undefined;
})
});
});
};

// TODO: fire event for query view for each query
Events.record(currentUser, "view", "widget", $scope.widget.id);
Events.record(currentUser, "view", "query", $scope.widget.visualization.query.id);
Events.record(currentUser, "view", "visualization", $scope.widget.visualization.id);

$scope.query = new Query($scope.widget.visualization.query);
$scope.queryResult = $scope.query.getQueryResult();
if ($scope.widget.visualization) {
Events.record(currentUser, "view", "query", $scope.widget.visualization.query.id);
Events.record(currentUser, "view", "visualization", $scope.widget.visualization.id);

$scope.updateTime = (new Date($scope.queryResult.getUpdatedAt())).toISOString();
$scope.nextUpdateTime = moment(new Date(($scope.query.updated_at + $scope.query.ttl + $scope.query.runtime + 300) * 1000)).fromNow();
$scope.query = new Query($scope.widget.visualization.query);
$scope.queryResult = $scope.query.getQueryResult();
$scope.nextUpdateTime = moment(new Date(($scope.query.updated_at + $scope.query.ttl + $scope.query.runtime + 300) * 1000)).fromNow();

$scope.updateTime = '';
$scope.type = 'visualization';
} else {
$scope.type = 'textbox';
}
};

angular.module('redash.controllers')
.controller('DashboardCtrl', ['$scope', 'Events', '$routeParams', '$http', '$timeout', 'Dashboard', DashboardCtrl])
.controller('WidgetCtrl', ['$scope', 'Events', '$http', '$location', 'Query', WidgetCtrl])
.controller('DashboardCtrl', ['$scope', 'Events', 'Widget', '$routeParams', '$http', '$timeout', 'Dashboard', DashboardCtrl])
.controller('WidgetCtrl', ['$scope', 'Events', 'Query', WidgetCtrl])

})();
36 changes: 25 additions & 11 deletions rd_ui/app/scripts/directives/dashboard_directives.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
row: rowIndex + 1,
ySize: 1,
xSize: widget.width,
name: widget.visualization.query.name
name: widget.getName()//visualization.query.name
});
});
});
Expand Down Expand Up @@ -125,25 +125,37 @@
value: 2
}];

$scope.type = 'visualization';

$scope.isVisualization = function () {
return $scope.type == 'visualization';
};

$scope.isTextBox = function () {
return $scope.type == 'textbox';
};

$scope.setType = function (type) {
$scope.type = type;
};

var reset = function() {
$scope.saveInProgress = false;
$scope.widgetSize = 1;
$scope.queryId = null;
$scope.selectedVis = null;
$scope.query = null;

}
$scope.text = "";
};

reset();

$scope.loadVisualizations = function() {
$scope.loadVisualizations = function () {
if (!$scope.queryId) {
return;
}

Query.get({
id: $scope.queryId
}, function(query) {
Query.get({ id: $scope.queryId }, function(query) {
if (query) {
$scope.query = query;
if (query.visualizations.length) {
Expand All @@ -157,19 +169,21 @@
$scope.saveInProgress = true;

var widget = new Widget({
'visualization_id': $scope.selectedVis.id,
'visualization_id': $scope.selectedVis && $scope.selectedVis.id,
'dashboard_id': $scope.dashboard.id,
'options': {},
'width': $scope.widgetSize
'width': $scope.widgetSize,
'text': $scope.text
});

widget.$save().then(function(response) {
// update dashboard layout
$scope.dashboard.layout = response['layout'];
var newWidget = new Widget(response['widget']);
if (response['new_row']) {
$scope.dashboard.widgets.push([response['widget']]);
$scope.dashboard.widgets.push([newWidget]);
} else {
$scope.dashboard.widgets[$scope.dashboard.widgets.length - 1].push(response['widget']);
$scope.dashboard.widgets[$scope.dashboard.widgets.length - 1].push(newWidget);
}

// close the dialog
Expand Down
114 changes: 60 additions & 54 deletions rd_ui/app/scripts/filters.js
Original file line number Diff line number Diff line change
@@ -1,61 +1,67 @@
var durationHumanize = function (duration) {
var humanized = "";
if (duration == undefined) {
humanized = "-";
} else if (duration < 60) {
humanized = Math.round(duration) + "s";
} else if (duration > 3600*24) {
var days = Math.round(parseFloat(duration) / 60.0 / 60.0 / 24.0);
humanized = days + "days";
} else if (duration >= 3600) {
var hours = Math.round(parseFloat(duration) / 60.0 / 60.0);
humanized = hours + "h";
} else {
var minutes = Math.round(parseFloat(duration) / 60.0);
humanized = minutes + "m";
}
return humanized;
var humanized = "";
if (duration == undefined) {
humanized = "-";
} else if (duration < 60) {
humanized = Math.round(duration) + "s";
} else if (duration > 3600 * 24) {
var days = Math.round(parseFloat(duration) / 60.0 / 60.0 / 24.0);
humanized = days + "days";
} else if (duration >= 3600) {
var hours = Math.round(parseFloat(duration) / 60.0 / 60.0);
humanized = hours + "h";
} else {
var minutes = Math.round(parseFloat(duration) / 60.0);
humanized = minutes + "m";
}
return humanized;
}

angular.module('redash.filters', []).
filter('durationHumanize', function () {
return durationHumanize;
})
filter('durationHumanize', function () {
return durationHumanize;
})

.filter('refreshRateHumanize', function () {
return function (ttl) {
if (ttl==-1) {
return "Never";
} else {
return "Every " + durationHumanize(ttl);
}
}
})
.filter('refreshRateHumanize', function () {
return function (ttl) {
if (ttl == -1) {
return "Never";
} else {
return "Every " + durationHumanize(ttl);
}
}
})

.filter('toHuman', function() {
return function(text) {
return text.replace(/_/g, ' ').replace(/(?:^|\s)\S/g, function (a) {
return a.toUpperCase();
});
}
})
.filter('toHuman', function () {
return function (text) {
return text.replace(/_/g, ' ').replace(/(?:^|\s)\S/g, function (a) {
return a.toUpperCase();
});
}
})

.filter('colWidth', function () {
return function (widgetWidth) {
if (widgetWidth == 1) {
return 6;
}
return 12;
}
})

.filter('colWidth', function () {
return function (widgetWidth) {
if (widgetWidth == 1) {
return 6;
}
return 12;
}
})

.filter('capitalize', function () {
return function (text) {
if (text) {
return text[0].toUpperCase() + text.slice(1).toLowerCase();
} else {
return null;
}

}
});
.filter('capitalize', function () {
return function (text) {
if (text) {
return _.str.capitalize(text);
} else {
return null;
}

}
})

.filter('markdown', ['$sce', function($sce) {
return function(text) {
return $sce.trustAsHtml(marked(text));
}
}]);
7 changes: 7 additions & 0 deletions rd_ui/app/scripts/services/resources.js
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,13 @@
var Widget = function ($resource) {
var WidgetResource = $resource('/api/widgets/:id', {id: '@id'});

WidgetResource.prototype.getName = function () {
if (this.visualization) {
return this.visualization.query.name + ' (' + this.visualization.name + ')';
}
return _.str.truncate(this.text, 20);
};

return WidgetResource;
}

Expand Down
11 changes: 10 additions & 1 deletion rd_ui/app/views/dashboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ <h2 id="dashboard_title">
<div ng-repeat="widget in row" class="col-lg-{{widget.width | colWidth}}"
ng-controller='WidgetCtrl'>

<div class="panel panel-default">
<div class="panel panel-default" ng-if="type=='visualization'">
<div class="panel-heading">
<h3 class="panel-title">
<p>
Expand All @@ -43,7 +43,16 @@ <h3 class="panel-title">
<a class="btn btn-default btn-xs" ng-href="/queries/{{query.id}}#{{widget.visualization.id}}" ng-show="currentUser.hasPermission('view_query')"><span class="glyphicon glyphicon-link"></span></a>
<button type="button" class="btn btn-default btn-xs" ng-show="dashboard.canEdit()" ng-click="deleteWidget()" title="Remove Widget"><span class="glyphicon glyphicon-trash"></span></button>
</span>
</div>
</div>

<div class="panel panel-default" ng-if="type=='textbox'" ng-mouseenter="showControls = true" ng-mouseleave="showControls = false">
<div class="panel-body">
<p ng-bind-html="widget.text | markdown"></p>

<span class="pull-right" ng-show="showControls">
<button type="button" class="btn btn-default btn-xs" ng-show="dashboard.canEdit()" ng-click="deleteWidget()" title="Remove Widget"><span class="glyphicon glyphicon-trash"></span></button>
</span>
</div>
</div>
</div>
Expand Down
Loading

0 comments on commit f23b434

Please sign in to comment.