Feature: personal home with recent queries & dashboards

This commit is contained in:
Arik Fraimovich 2015-01-19 12:09:06 +02:00
parent 758e27ce91
commit f51c2328c9
7 changed files with 107 additions and 4 deletions

View File

@ -90,6 +90,10 @@ angular.module('redash', [
templateUrl: '/views/index.html',
controller: 'IndexCtrl'
});
$routeProvider.when('/personal', {
templateUrl: '/views/personal.html',
controller: 'PersonalIndexCtrl'
});
$routeProvider.otherwise({
redirectTo: '/'
});

View File

@ -192,7 +192,7 @@
$(window).click(function () {
notifications.getPermissions();
});
}
};
var IndexCtrl = function ($scope, Events, Dashboard) {
Events.record(currentUser, "view", "page", "homepage");
@ -206,11 +206,29 @@
});
}
}
};
var PersonalIndexCtrl = function ($scope, Events, Dashboard, Query) {
Events.record(currentUser, "view", "page", "homepage");
$scope.$parent.pageTitle = "Home";
$scope.recentQueries = Query.recent();
$scope.recentDashboards = Dashboard.recent();
$scope.archiveDashboard = function (dashboard) {
if (confirm('Are you sure you want to delete "' + dashboard.name + '" dashboard?')) {
Events.record(currentUser, "archive", "dashboard", dashboard.id);
dashboard.$delete(function () {
$scope.$parent.reloadDashboards();
});
}
}
};
angular.module('redash.controllers', [])
.controller('QueriesCtrl', ['$scope', '$http', '$location', '$filter', 'Query', QueriesCtrl])
.controller('IndexCtrl', ['$scope', 'Events', 'Dashboard', IndexCtrl])
.controller('PersonalIndexCtrl', ['$scope', 'Events', 'Dashboard', 'Query', PersonalIndexCtrl])
.controller('MainCtrl', ['$scope', '$location', 'Dashboard', 'notifications', MainCtrl])
.controller('QuerySearchCtrl', ['$scope', '$location', '$filter', 'Events', 'Query', QuerySearchCtrl]);
})();

View File

@ -1,6 +1,12 @@
(function () {
var Dashboard = function($resource) {
var resource = $resource('/api/dashboards/:slug', {slug: '@slug'});
var resource = $resource('/api/dashboards/:slug', {slug: '@slug'}, {
recent: {
method: 'get',
isArray: true,
url: "/api/dashboards/recent"
}});
resource.prototype.canEdit = function() {
return currentUser.hasPermission('admin') || currentUser.canEdit(this);
}

View File

@ -377,7 +377,18 @@
};
var Query = function ($resource, QueryResult, DataSource) {
var Query = $resource('/api/queries/:id', {id: '@id'}, {search: {method: 'get', isArray: true, url: "/api/queries/search"}});
var Query = $resource('/api/queries/:id', {id: '@id'},
{
search: {
method: 'get',
isArray: true,
url: "/api/queries/search"
},
recent: {
method: 'get',
isArray: true,
url: "/api/queries/recent"
}});
Query.newQuery = function () {
return new Query({

View File

@ -0,0 +1,28 @@
<div class="container">
<div class="row">
<div class="list-group col-md-6">
<div class="list-group-item active">
Recent Dashboards
<button ng-show="currentUser.hasPermission('create_dashboard')" type="button" class="btn btn-sm btn-link" data-toggle="modal" href="#new_dashboard_dialog" tooltip="New Dashboard"><span class="glyphicon glyphicon-plus-sign"></span></button>
</div>
<div class="list-group-item" ng-repeat="dashboard in recentDashboards" >
<button type="button" class="close delete-button" aria-hidden="true" ng-show="dashboard.canEdit()" ng-click="archiveDashboard(dashboard)" tooltip="Delete Dashboard">&times;</button>
<a ng-href="/dashboard/{{dashboard.slug}}">{{dashboard.name}}</a>
</div>
</div>
<div class="list-group col-md-6">
<div class="list-group-item active">
Recent Queries
</div>
<a ng-href="/queries/{{query.id}}" class="list-group-item" ng-repeat="query in recentQueries">{{query.name}}</a>
</div>
</div>
<div ng-show="currentUser.hasPermission('admin')" class="row">
<div class="list-group">
<div class="list-group-item active">Admin</div>
<a href="/admin/status" class="list-group-item">Status</a>
</div>
</div>
</div>

View File

@ -35,6 +35,7 @@ def ping():
@app.route('/queries')
@app.route('/queries/<query_id>')
@app.route('/queries/<query_id>/<anything>')
@app.route('/personal')
@app.route('/')
@auth.required
def index(**kwargs):
@ -181,6 +182,11 @@ class DataSourceListAPI(BaseResource):
api.add_resource(DataSourceListAPI, '/api/data_sources', endpoint='data_sources')
class DashboardRecentAPI(BaseResource):
def get(self):
return [d.to_dict() for d in models.Dashboard.recent(current_user.id).limit(20)]
class DashboardListAPI(BaseResource):
def get(self):
dashboards = [d.to_dict() for d in
@ -225,6 +231,7 @@ class DashboardAPI(BaseResource):
dashboard.save()
api.add_resource(DashboardListAPI, '/api/dashboards', endpoint='dashboards')
api.add_resource(DashboardRecentAPI, '/api/dashboards/recent', endpoint='recent_dashboards')
api.add_resource(DashboardAPI, '/api/dashboards/<dashboard_slug>', endpoint='dashboard')
@ -285,6 +292,12 @@ class QuerySearchAPI(BaseResource):
return [q.to_dict() for q in models.Query.search(term)]
class QueryRecentAPI(BaseResource):
@require_permission('view_query')
def get(self):
return [q.to_dict() for q in models.Query.recent(current_user.id).limit(20)]
class QueryListAPI(BaseResource):
@require_permission('create_query')
def post(self):
@ -334,6 +347,7 @@ class QueryAPI(BaseResource):
abort(404, message="Query not found.")
api.add_resource(QuerySearchAPI, '/api/queries/search', endpoint='queries_search')
api.add_resource(QueryRecentAPI, '/api/queries/recent', endpoint='recent_queries')
api.add_resource(QueryListAPI, '/api/queries', endpoint='queries')
api.add_resource(QueryAPI, '/api/queries/<query_id>', endpoint='query')

View File

@ -360,6 +360,17 @@ class Query(BaseModel):
return cls.select().where(where)
@classmethod
def recent(cls, user_id):
return cls.select().where(Event.created_at > peewee.SQL("current_date - 7")).\
join(Event, on=(Query.id == peewee.SQL("t2.object_id::integer"))).\
where(Event.action << ('edit', 'execute', 'edit_name', 'edit_description', 'view_source')).\
where(Event.user == user_id).\
where(~(Event.object_id >> None)).\
where(Event.object_type == 'query').\
group_by(Event.object_id, Query.id).\
order_by(peewee.SQL("count(0) desc"))
@classmethod
def update_instance(cls, query_id, **kwargs):
if 'query' in kwargs:
@ -448,6 +459,17 @@ class Dashboard(BaseModel):
def get_by_slug(cls, slug):
return cls.get(cls.slug == slug)
@classmethod
def recent(cls, user_id):
return cls.select().where(Event.created_at > peewee.SQL("current_date - 7")). \
join(Event, on=(Dashboard.id == peewee.SQL("t2.object_id::integer"))). \
where(Event.action << ('edit', 'view')).\
where(Event.user == user_id). \
where(~(Event.object_id >> None)). \
where(Event.object_type == 'dashboard'). \
group_by(Event.object_id, Dashboard.id). \
order_by(peewee.SQL("count(0) desc"))
def save(self, *args, **kwargs):
if not self.slug:
self.slug = utils.slugify(self.name)