From bbb0467cf29b45df6a49b8487c66a624d1b4bf65 Mon Sep 17 00:00:00 2001 From: Jannis Leidel Date: Tue, 2 Oct 2018 20:34:32 +0200 Subject: [PATCH] Add archived queries section to queries list. --- .../assets/less/redash/redash-newstyle.less | 13 ++ client/app/pages/queries-list/index.js | 16 +++ .../app/pages/queries-list/queries-list.html | 27 +++- client/app/services/query.js | 5 + redash/handlers/api.py | 5 +- redash/handlers/organization.py | 2 +- redash/handlers/queries.py | 125 +++++++++++------- redash/models.py | 36 +++-- 8 files changed, 161 insertions(+), 68 deletions(-) diff --git a/client/app/assets/less/redash/redash-newstyle.less b/client/app/assets/less/redash/redash-newstyle.less index 38a8ce650b..c686eb847c 100644 --- a/client/app/assets/less/redash/redash-newstyle.less +++ b/client/app/assets/less/redash/redash-newstyle.less @@ -201,6 +201,19 @@ body { } } +.btn-archived { + color: #d4d4d4; + transition: all .25s ease-in-out; + + &:hover, &:focus { + color: @gray-light; + } + + .fa-archive { + color: @gray-light; + } +} + .page-header--new .btn-favourite { font-size: 19px; } diff --git a/client/app/pages/queries-list/index.js b/client/app/pages/queries-list/index.js index 0116cf2721..4da93e0a88 100644 --- a/client/app/pages/queries-list/index.js +++ b/client/app/pages/queries-list/index.js @@ -32,6 +32,8 @@ class QueriesListCtrl extends ListCtrl { this.emptyType = 'favorites'; } else if (this.currentPage === 'my') { this.emptyType = 'my'; + } else if (this.currentPage === 'archived') { + this.emptyType = 'archived'; } else { this.emptyType = 'default'; } @@ -94,6 +96,20 @@ export default function init(ngModule) { }, route, ), + '/queries/archived': extend( + { + title: 'Archived Queries', + resolve: { + currentPage: () => 'archived', + resource: (Query) => { + 'ngInject'; + + return Query.archived.bind(Query); + }, + }, + }, + route, + ), // TODO: setup redirect? // '/queries/search': _.extend( }; diff --git a/client/app/pages/queries-list/queries-list.html b/client/app/pages/queries-list/queries-list.html index 1ac2dbcc44..b2c727356c 100644 --- a/client/app/pages/queries-list/queries-list.html +++ b/client/app/pages/queries-list/queries-list.html @@ -1,6 +1,10 @@
- - +
+
+
+
+
+
@@ -17,10 +21,19 @@ Favorites + + + + + + Archived + + My Queries +
@@ -51,6 +64,7 @@ query writing documentation.
+ @@ -129,10 +143,19 @@ Favorites + + + + + + Archived + + My Queries +
diff --git a/client/app/services/query.js b/client/app/services/query.js index 9f525caff5..c915bb6565 100644 --- a/client/app/services/query.js +++ b/client/app/services/query.js @@ -316,6 +316,11 @@ function QueryResource( isArray: true, url: 'api/queries/recent', }, + archived: { + method: 'get', + isArray: false, + url: 'api/queries/archived', + }, query: { isArray: false, }, diff --git a/redash/handlers/api.py b/redash/handlers/api.py index f8ef199857..ed01394f27 100644 --- a/redash/handlers/api.py +++ b/redash/handlers/api.py @@ -6,10 +6,10 @@ from redash.handlers.base import org_scoped_rule from redash.handlers.permissions import ObjectPermissionsListResource, CheckPermissionResource from redash.handlers.alerts import AlertResource, AlertListResource, AlertSubscriptionListResource, AlertSubscriptionResource -from redash.handlers.dashboards import DashboardListResource, DashboardResource, DashboardShareResource, PublicDashboardResource +from redash.handlers.dashboards import DashboardListResource, DashboardResource, DashboardShareResource, PublicDashboardResource from redash.handlers.data_sources import DataSourceTypeListResource, DataSourceListResource, DataSourceSchemaResource, DataSourceResource, DataSourcePauseResource, DataSourceTestResource from redash.handlers.events import EventsResource -from redash.handlers.queries import QueryForkResource, QueryRefreshResource, QueryListResource, QueryRecentResource, QuerySearchResource, QueryResource, MyQueriesResource +from redash.handlers.queries import QueryArchivedResource, QueryForkResource, QueryRefreshResource, QueryListResource, QueryRecentResource, QuerySearchResource, QueryResource, MyQueriesResource from redash.handlers.query_results import QueryResultListResource, QueryResultResource, JobResource from redash.handlers.users import UserResource, UserListResource, UserInviteResource, UserResetPasswordResource, UserDisableResource from redash.handlers.visualizations import VisualizationListResource @@ -79,6 +79,7 @@ def json_representation(data, code, headers=None): api.add_org_resource(QuerySearchResource, '/api/queries/search', endpoint='queries_search') api.add_org_resource(QueryRecentResource, '/api/queries/recent', endpoint='recent_queries') +api.add_org_resource(QueryArchivedResource, '/api/queries/archived', endpoint='archived_queries') api.add_org_resource(QueryListResource, '/api/queries', endpoint='queries') api.add_org_resource(MyQueriesResource, '/api/queries/my', endpoint='my_queries') api.add_org_resource(QueryRefreshResource, '/api/queries//refresh', endpoint='query_refresh') diff --git a/redash/handlers/organization.py b/redash/handlers/organization.py index eb81c773c6..32b73085be 100644 --- a/redash/handlers/organization.py +++ b/redash/handlers/organization.py @@ -13,7 +13,7 @@ def organization_status(org_slug=None): 'users': models.User.all_not_disabled(current_org).count(), 'alerts': models.Alert.all(group_ids=current_user.group_ids).count(), 'data_sources': models.DataSource.all(current_org, group_ids=current_user.group_ids).count(), - 'queries': models.Query.all_queries(current_user.group_ids, current_user.id, drafts=True).count(), + 'queries': models.Query.all_queries(current_user.group_ids, current_user.id, include_drafts=True).count(), 'dashboards': models.Dashboard.query.filter(models.Dashboard.org==current_org, models.Dashboard.is_archived==False).count(), } diff --git a/redash/handlers/queries.py b/redash/handlers/queries.py index 7fd5ed38ce..80a3fa1aca 100644 --- a/redash/handlers/queries.py +++ b/redash/handlers/queries.py @@ -98,7 +98,74 @@ def get(self): return QuerySerializer(results, with_last_modified_by=False, with_user=False).serialize() -class QueryListResource(BaseResource): +class BaseQueryListResource(BaseResource): + + def get_queries(self, search_term): + if search_term: + results = models.Query.search( + search_term, + self.current_user.group_ids, + self.current_user.id, + include_drafts=True, + ) + else: + results = models.Query.all_queries( + self.current_user.group_ids, + self.current_user.id, + include_drafts=True, + ) + return filter_by_tags(results, models.Query.tags) + + @require_permission('view_query') + def get(self): + """ + Retrieve a list of queries. + + :qparam number page_size: Number of queries to return per page + :qparam number page: Page number to retrieve + :qparam number order: Name of column to order by + :qparam number q: Full text search term + + Responds with an array of :ref:`query ` objects. + """ + # See if we want to do full-text search or just regular queries + search_term = request.args.get('q', '') + + queries = self.get_queries(search_term) + + results = filter_by_tags(queries, models.Query.tags) + + # order results according to passed order parameter + ordered_results = order_results(results) + + page = request.args.get('page', 1, type=int) + page_size = request.args.get('page_size', 25, type=int) + + response = paginate( + ordered_results, + page=page, + page_size=page_size, + serializer=QuerySerializer, + with_stats=True, + with_last_modified_by=False + ) + + if search_term: + self.record_event({ + 'action': 'search', + 'object_type': 'query', + 'term': search_term, + }) + else: + self.record_event({ + 'action': 'list', + 'object_type': 'query', + }) + + return response + + +class QueryListResource(BaseQueryListResource): @require_permission('create_query') def post(self): """ @@ -157,66 +224,26 @@ def post(self): return QuerySerializer(query).serialize() - @require_permission('view_query') - def get(self): - """ - Retrieve a list of queries. - :qparam number page_size: Number of queries to return per page - :qparam number page: Page number to retrieve - :qparam number order: Name of column to order by - :qparam number q: Full text search term - - Responds with an array of :ref:`query ` objects. - """ - # See if we want to do full-text search or just regular queries - search_term = request.args.get('q', '') +class QueryArchivedResource(BaseQueryListResource): + def get_queries(self, search_term): if search_term: - results = models.Query.search( + return models.Query.search( search_term, self.current_user.group_ids, self.current_user.id, - include_drafts=True, + include_drafts=False, + include_archived=True, ) else: - results = models.Query.all_queries( + return models.Query.all_queries( self.current_user.group_ids, self.current_user.id, - drafts=True, + include_drafts=False, + include_archived=True, ) - results = filter_by_tags(results, models.Query.tags) - - # order results according to passed order parameter - ordered_results = order_results(results) - - page = request.args.get('page', 1, type=int) - page_size = request.args.get('page_size', 25, type=int) - - response = paginate( - ordered_results, - page=page, - page_size=page_size, - serializer=QuerySerializer, - with_stats=True, - with_last_modified_by=False - ) - - if search_term: - self.record_event({ - 'action': 'search', - 'object_type': 'query', - 'term': search_term, - }) - else: - self.record_event({ - 'action': 'list', - 'object_type': 'query', - }) - - return response - class MyQueriesResource(BaseResource): @require_permission('view_query') diff --git a/redash/models.py b/redash/models.py index e0458816c7..04e21612e3 100644 --- a/redash/models.py +++ b/redash/models.py @@ -923,7 +923,7 @@ def create(cls, **kwargs): return query @classmethod - def all_queries(cls, group_ids, user_id=None, drafts=False): + def all_queries(cls, group_ids, user_id=None, include_drafts=False, include_archived=False): query_ids = ( db.session .query(distinct(cls.id)) @@ -931,10 +931,10 @@ def all_queries(cls, group_ids, user_id=None, drafts=False): DataSourceGroup, Query.data_source_id == DataSourceGroup.data_source_id ) - .filter(Query.is_archived == False) + .filter(Query.is_archived.is_(include_archived)) .filter(DataSourceGroup.group_id.in_(group_ids)) ) - q = ( + queries = ( cls .query .options( @@ -960,19 +960,19 @@ def all_queries(cls, group_ids, user_id=None, drafts=False): .order_by(Query.created_at.desc()) ) - if not drafts: - q = q.filter( + if not include_drafts: + queries = queries.filter( or_( - Query.is_draft == False, + Query.is_draft.is_(False), Query.user_id == user_id ) ) - return q + return queries @classmethod def favorites(cls, user, base_query=None): if base_query == None: - base_query = cls.all_queries(user.group_ids, user.id, drafts=True) + base_query = cls.all_queries(user.group_ids, user.id, include_drafts=True) return base_query.join((Favorite, and_(Favorite.object_type==u'Query', Favorite.object_id==Query.id))).filter(Favorite.user_id==user.id) @classmethod @@ -998,10 +998,12 @@ def by_user(cls, user): @classmethod def outdated_queries(cls): - queries = (db.session.query(Query) - .options(joinedload(Query.latest_query_data).load_only('retrieved_at')) - .filter(Query.schedule != None) - .order_by(Query.id)) + queries = ( + db.session.query(Query) + .options(joinedload(Query.latest_query_data).load_only('retrieved_at')) + .filter(Query.schedule != None) + .order_by(Query.id) + ) now = utils.utcnow() outdated_queries = {} @@ -1022,8 +1024,14 @@ def outdated_queries(cls): return outdated_queries.values() @classmethod - def search(cls, term, group_ids, user_id=None, include_drafts=False, limit=None): - all_queries = cls.all_queries(group_ids, user_id=user_id, drafts=include_drafts) + def search(cls, term, group_ids, user_id=None, include_drafts=False, + limit=None, include_archived=False): + all_queries = cls.all_queries( + group_ids, + user_id=user_id, + include_drafts=include_drafts, + include_archived=include_archived, + ) # sort the result using the weight as defined in the search vector column return all_queries.search(term, sort=True).limit(limit)