##// END OF EJS Templates
goto-switcher: optimized performance and query capabilities....
marcink -
r2038:2bdf9d4d default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,305 +1,321 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import re
21 import re
22 import logging
22 import logging
23
23
24 from pyramid.view import view_config
24 from pyramid.view import view_config
25
25
26 from rhodecode.apps._base import BaseAppView
26 from rhodecode.apps._base import BaseAppView
27 from rhodecode.lib import helpers as h
27 from rhodecode.lib import helpers as h
28 from rhodecode.lib.auth import LoginRequired, NotAnonymous, \
28 from rhodecode.lib.auth import LoginRequired, NotAnonymous, \
29 HasRepoGroupPermissionAnyDecorator
29 HasRepoGroupPermissionAnyDecorator
30 from rhodecode.lib.index import searcher_from_config
30 from rhodecode.lib.index import searcher_from_config
31 from rhodecode.lib.utils2 import safe_unicode, str2bool
31 from rhodecode.lib.utils2 import safe_unicode, str2bool
32 from rhodecode.lib.ext_json import json
32 from rhodecode.lib.ext_json import json
33 from rhodecode.model.db import func, Repository, RepoGroup
33 from rhodecode.model.db import (
34 func, or_, in_filter_generator, Repository, RepoGroup)
34 from rhodecode.model.repo import RepoModel
35 from rhodecode.model.repo import RepoModel
35 from rhodecode.model.repo_group import RepoGroupModel
36 from rhodecode.model.repo_group import RepoGroupModel
36 from rhodecode.model.scm import ScmModel, RepoGroupList, RepoList
37 from rhodecode.model.scm import RepoGroupList, RepoList
37 from rhodecode.model.user import UserModel
38 from rhodecode.model.user import UserModel
38 from rhodecode.model.user_group import UserGroupModel
39 from rhodecode.model.user_group import UserGroupModel
39
40
40 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
41
42
42
43
43 class HomeView(BaseAppView):
44 class HomeView(BaseAppView):
44
45
45 def load_default_context(self):
46 def load_default_context(self):
46 c = self._get_local_tmpl_context()
47 c = self._get_local_tmpl_context()
47 c.user = c.auth_user.get_instance()
48 c.user = c.auth_user.get_instance()
48 self._register_global_c(c)
49 self._register_global_c(c)
49 return c
50 return c
50
51
51 @LoginRequired()
52 @LoginRequired()
52 @view_config(
53 @view_config(
53 route_name='user_autocomplete_data', request_method='GET',
54 route_name='user_autocomplete_data', request_method='GET',
54 renderer='json_ext', xhr=True)
55 renderer='json_ext', xhr=True)
55 def user_autocomplete_data(self):
56 def user_autocomplete_data(self):
56 query = self.request.GET.get('query')
57 query = self.request.GET.get('query')
57 active = str2bool(self.request.GET.get('active') or True)
58 active = str2bool(self.request.GET.get('active') or True)
58 include_groups = str2bool(self.request.GET.get('user_groups'))
59 include_groups = str2bool(self.request.GET.get('user_groups'))
59 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
60 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
60 skip_default_user = str2bool(self.request.GET.get('skip_default_user'))
61 skip_default_user = str2bool(self.request.GET.get('skip_default_user'))
61
62
62 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
63 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
63 query, active, include_groups)
64 query, active, include_groups)
64
65
65 _users = UserModel().get_users(
66 _users = UserModel().get_users(
66 name_contains=query, only_active=active)
67 name_contains=query, only_active=active)
67
68
68 def maybe_skip_default_user(usr):
69 def maybe_skip_default_user(usr):
69 if skip_default_user and usr['username'] == UserModel.cls.DEFAULT_USER:
70 if skip_default_user and usr['username'] == UserModel.cls.DEFAULT_USER:
70 return False
71 return False
71 return True
72 return True
72 _users = filter(maybe_skip_default_user, _users)
73 _users = filter(maybe_skip_default_user, _users)
73
74
74 if include_groups:
75 if include_groups:
75 # extend with user groups
76 # extend with user groups
76 _user_groups = UserGroupModel().get_user_groups(
77 _user_groups = UserGroupModel().get_user_groups(
77 name_contains=query, only_active=active,
78 name_contains=query, only_active=active,
78 expand_groups=expand_groups)
79 expand_groups=expand_groups)
79 _users = _users + _user_groups
80 _users = _users + _user_groups
80
81
81 return {'suggestions': _users}
82 return {'suggestions': _users}
82
83
83 @LoginRequired()
84 @LoginRequired()
84 @NotAnonymous()
85 @NotAnonymous()
85 @view_config(
86 @view_config(
86 route_name='user_group_autocomplete_data', request_method='GET',
87 route_name='user_group_autocomplete_data', request_method='GET',
87 renderer='json_ext', xhr=True)
88 renderer='json_ext', xhr=True)
88 def user_group_autocomplete_data(self):
89 def user_group_autocomplete_data(self):
89 query = self.request.GET.get('query')
90 query = self.request.GET.get('query')
90 active = str2bool(self.request.GET.get('active') or True)
91 active = str2bool(self.request.GET.get('active') or True)
91 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
92 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
92
93
93 log.debug('generating user group list, query:%s, active:%s',
94 log.debug('generating user group list, query:%s, active:%s',
94 query, active)
95 query, active)
95
96
96 _user_groups = UserGroupModel().get_user_groups(
97 _user_groups = UserGroupModel().get_user_groups(
97 name_contains=query, only_active=active,
98 name_contains=query, only_active=active,
98 expand_groups=expand_groups)
99 expand_groups=expand_groups)
99 _user_groups = _user_groups
100 _user_groups = _user_groups
100
101
101 return {'suggestions': _user_groups}
102 return {'suggestions': _user_groups}
102
103
103 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
104 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
105 allowed_ids = self._rhodecode_user.repo_acl_ids(
106 ['repository.read', 'repository.write', 'repository.admin'],
107 cache=False, name_filter=name_contains)
108
104 query = Repository.query()\
109 query = Repository.query()\
105 .order_by(func.length(Repository.repo_name))\
110 .order_by(func.length(Repository.repo_name))\
106 .order_by(Repository.repo_name)
111 .order_by(Repository.repo_name)\
112 .filter(or_(
113 # generate multiple IN to fix limitation problems
114 *in_filter_generator(Repository.repo_id, allowed_ids)
115 ))
107
116
108 if repo_type:
117 if repo_type:
109 query = query.filter(Repository.repo_type == repo_type)
118 query = query.filter(Repository.repo_type == repo_type)
110
119
111 if name_contains:
120 if name_contains:
112 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
121 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
113 query = query.filter(
122 query = query.filter(
114 Repository.repo_name.ilike(ilike_expression))
123 Repository.repo_name.ilike(ilike_expression))
115 query = query.limit(limit)
124 query = query.limit(limit)
116
125
117 all_repos = query.all()
126 acl_repo_iter = query
118 # permission checks are inside this function
127
119 repo_iter = ScmModel().get_repos(all_repos)
120 return [
128 return [
121 {
129 {
122 'id': obj['name'],
130 'id': obj.repo_name,
123 'text': obj['name'],
131 'text': obj.repo_name,
124 'type': 'repo',
132 'type': 'repo',
125 'obj': obj['dbrepo'],
133 'obj': {'repo_type': obj.repo_type, 'private': obj.private},
126 'url': h.route_path('repo_summary', repo_name=obj['name'])
134 'url': h.route_path('repo_summary', repo_name=obj.repo_name)
127 }
135 }
128 for obj in repo_iter]
136 for obj in acl_repo_iter]
129
137
130 def _get_repo_group_list(self, name_contains=None, limit=20):
138 def _get_repo_group_list(self, name_contains=None, limit=20):
139 allowed_ids = self._rhodecode_user.repo_group_acl_ids(
140 ['group.read', 'group.write', 'group.admin'],
141 cache=False, name_filter=name_contains)
142
131 query = RepoGroup.query()\
143 query = RepoGroup.query()\
132 .order_by(func.length(RepoGroup.group_name))\
144 .order_by(func.length(RepoGroup.group_name))\
133 .order_by(RepoGroup.group_name)
145 .order_by(RepoGroup.group_name) \
146 .filter(or_(
147 # generate multiple IN to fix limitation problems
148 *in_filter_generator(RepoGroup.group_id, allowed_ids)
149 ))
134
150
135 if name_contains:
151 if name_contains:
136 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
152 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
137 query = query.filter(
153 query = query.filter(
138 RepoGroup.group_name.ilike(ilike_expression))
154 RepoGroup.group_name.ilike(ilike_expression))
139 query = query.limit(limit)
155 query = query.limit(limit)
140
156
141 all_groups = query.all()
157 acl_repo_iter = query
142 repo_groups_iter = ScmModel().get_repo_groups(all_groups)
158
143 return [
159 return [
144 {
160 {
145 'id': obj.group_name,
161 'id': obj.group_name,
146 'text': obj.group_name,
162 'text': obj.group_name,
147 'type': 'group',
163 'type': 'group',
148 'obj': {},
164 'obj': {},
149 'url': h.route_path(
165 'url': h.route_path(
150 'repo_group_home', repo_group_name=obj.group_name)
166 'repo_group_home', repo_group_name=obj.group_name)
151 }
167 }
152 for obj in repo_groups_iter]
168 for obj in acl_repo_iter]
153
169
154 def _get_hash_commit_list(self, auth_user, query=None):
170 def _get_hash_commit_list(self, auth_user, query=None):
155 if not query or len(query) < 3:
171 if not query or len(query) < 3:
156 return []
172 return []
157
173
158 commit_hashes = re.compile('(?:commit:)([0-9a-f]{2,40})').findall(query)
174 commit_hashes = re.compile('(?:commit:)([0-9a-f]{2,40})').findall(query)
159
175
160 if len(commit_hashes) != 1:
176 if len(commit_hashes) != 1:
161 return []
177 return []
162
178
163 commit_hash_prefix = commit_hashes[0]
179 commit_hash_prefix = commit_hashes[0]
164
180
165 searcher = searcher_from_config(self.request.registry.settings)
181 searcher = searcher_from_config(self.request.registry.settings)
166 result = searcher.search(
182 result = searcher.search(
167 'commit_id:%s*' % commit_hash_prefix, 'commit', auth_user,
183 'commit_id:%s*' % commit_hash_prefix, 'commit', auth_user,
168 raise_on_exc=False)
184 raise_on_exc=False)
169
185
170 return [
186 return [
171 {
187 {
172 'id': entry['commit_id'],
188 'id': entry['commit_id'],
173 'text': entry['commit_id'],
189 'text': entry['commit_id'],
174 'type': 'commit',
190 'type': 'commit',
175 'obj': {'repo': entry['repository']},
191 'obj': {'repo': entry['repository']},
176 'url': h.route_path(
192 'url': h.route_path(
177 'repo_commit',
193 'repo_commit',
178 repo_name=entry['repository'], commit_id=entry['commit_id'])
194 repo_name=entry['repository'], commit_id=entry['commit_id'])
179 }
195 }
180 for entry in result['results']]
196 for entry in result['results']]
181
197
182 @LoginRequired()
198 @LoginRequired()
183 @view_config(
199 @view_config(
184 route_name='repo_list_data', request_method='GET',
200 route_name='repo_list_data', request_method='GET',
185 renderer='json_ext', xhr=True)
201 renderer='json_ext', xhr=True)
186 def repo_list_data(self):
202 def repo_list_data(self):
187 _ = self.request.translate
203 _ = self.request.translate
188
204
189 query = self.request.GET.get('query')
205 query = self.request.GET.get('query')
190 repo_type = self.request.GET.get('repo_type')
206 repo_type = self.request.GET.get('repo_type')
191 log.debug('generating repo list, query:%s, repo_type:%s',
207 log.debug('generating repo list, query:%s, repo_type:%s',
192 query, repo_type)
208 query, repo_type)
193
209
194 res = []
210 res = []
195 repos = self._get_repo_list(query, repo_type=repo_type)
211 repos = self._get_repo_list(query, repo_type=repo_type)
196 if repos:
212 if repos:
197 res.append({
213 res.append({
198 'text': _('Repositories'),
214 'text': _('Repositories'),
199 'children': repos
215 'children': repos
200 })
216 })
201
217
202 data = {
218 data = {
203 'more': False,
219 'more': False,
204 'results': res
220 'results': res
205 }
221 }
206 return data
222 return data
207
223
208 @LoginRequired()
224 @LoginRequired()
209 @view_config(
225 @view_config(
210 route_name='goto_switcher_data', request_method='GET',
226 route_name='goto_switcher_data', request_method='GET',
211 renderer='json_ext', xhr=True)
227 renderer='json_ext', xhr=True)
212 def goto_switcher_data(self):
228 def goto_switcher_data(self):
213 c = self.load_default_context()
229 c = self.load_default_context()
214
230
215 _ = self.request.translate
231 _ = self.request.translate
216
232
217 query = self.request.GET.get('query')
233 query = self.request.GET.get('query')
218 log.debug('generating goto switcher list, query %s', query)
234 log.debug('generating goto switcher list, query %s', query)
219
235
220 res = []
236 res = []
221 repo_groups = self._get_repo_group_list(query)
237 repo_groups = self._get_repo_group_list(query)
222 if repo_groups:
238 if repo_groups:
223 res.append({
239 res.append({
224 'text': _('Groups'),
240 'text': _('Groups'),
225 'children': repo_groups
241 'children': repo_groups
226 })
242 })
227
243
228 repos = self._get_repo_list(query)
244 repos = self._get_repo_list(query)
229 if repos:
245 if repos:
230 res.append({
246 res.append({
231 'text': _('Repositories'),
247 'text': _('Repositories'),
232 'children': repos
248 'children': repos
233 })
249 })
234
250
235 commits = self._get_hash_commit_list(c.auth_user, query)
251 commits = self._get_hash_commit_list(c.auth_user, query)
236 if commits:
252 if commits:
237 unique_repos = {}
253 unique_repos = {}
238 for commit in commits:
254 for commit in commits:
239 unique_repos.setdefault(commit['obj']['repo'], []
255 unique_repos.setdefault(commit['obj']['repo'], []
240 ).append(commit)
256 ).append(commit)
241
257
242 for repo in unique_repos:
258 for repo in unique_repos:
243 res.append({
259 res.append({
244 'text': _('Commits in %(repo)s') % {'repo': repo},
260 'text': _('Commits in %(repo)s') % {'repo': repo},
245 'children': unique_repos[repo]
261 'children': unique_repos[repo]
246 })
262 })
247
263
248 data = {
264 data = {
249 'more': False,
265 'more': False,
250 'results': res
266 'results': res
251 }
267 }
252 return data
268 return data
253
269
254 def _get_groups_and_repos(self, repo_group_id=None):
270 def _get_groups_and_repos(self, repo_group_id=None):
255 # repo groups groups
271 # repo groups groups
256 repo_group_list = RepoGroup.get_all_repo_groups(group_id=repo_group_id)
272 repo_group_list = RepoGroup.get_all_repo_groups(group_id=repo_group_id)
257 _perms = ['group.read', 'group.write', 'group.admin']
273 _perms = ['group.read', 'group.write', 'group.admin']
258 repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms)
274 repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms)
259 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
275 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
260 repo_group_list=repo_group_list_acl, admin=False)
276 repo_group_list=repo_group_list_acl, admin=False)
261
277
262 # repositories
278 # repositories
263 repo_list = Repository.get_all_repos(group_id=repo_group_id)
279 repo_list = Repository.get_all_repos(group_id=repo_group_id)
264 _perms = ['repository.read', 'repository.write', 'repository.admin']
280 _perms = ['repository.read', 'repository.write', 'repository.admin']
265 repo_list_acl = RepoList(repo_list, perm_set=_perms)
281 repo_list_acl = RepoList(repo_list, perm_set=_perms)
266 repo_data = RepoModel().get_repos_as_dict(
282 repo_data = RepoModel().get_repos_as_dict(
267 repo_list=repo_list_acl, admin=False)
283 repo_list=repo_list_acl, admin=False)
268
284
269 return repo_data, repo_group_data
285 return repo_data, repo_group_data
270
286
271 @LoginRequired()
287 @LoginRequired()
272 @view_config(
288 @view_config(
273 route_name='home', request_method='GET',
289 route_name='home', request_method='GET',
274 renderer='rhodecode:templates/index.mako')
290 renderer='rhodecode:templates/index.mako')
275 def main_page(self):
291 def main_page(self):
276 c = self.load_default_context()
292 c = self.load_default_context()
277 c.repo_group = None
293 c.repo_group = None
278
294
279 repo_data, repo_group_data = self._get_groups_and_repos()
295 repo_data, repo_group_data = self._get_groups_and_repos()
280 # json used to render the grids
296 # json used to render the grids
281 c.repos_data = json.dumps(repo_data)
297 c.repos_data = json.dumps(repo_data)
282 c.repo_groups_data = json.dumps(repo_group_data)
298 c.repo_groups_data = json.dumps(repo_group_data)
283
299
284 return self._get_template_context(c)
300 return self._get_template_context(c)
285
301
286 @LoginRequired()
302 @LoginRequired()
287 @HasRepoGroupPermissionAnyDecorator(
303 @HasRepoGroupPermissionAnyDecorator(
288 'group.read', 'group.write', 'group.admin')
304 'group.read', 'group.write', 'group.admin')
289 @view_config(
305 @view_config(
290 route_name='repo_group_home', request_method='GET',
306 route_name='repo_group_home', request_method='GET',
291 renderer='rhodecode:templates/index_repo_group.mako')
307 renderer='rhodecode:templates/index_repo_group.mako')
292 @view_config(
308 @view_config(
293 route_name='repo_group_home_slash', request_method='GET',
309 route_name='repo_group_home_slash', request_method='GET',
294 renderer='rhodecode:templates/index_repo_group.mako')
310 renderer='rhodecode:templates/index_repo_group.mako')
295 def repo_group_main_page(self):
311 def repo_group_main_page(self):
296 c = self.load_default_context()
312 c = self.load_default_context()
297 c.repo_group = self.request.db_repo_group
313 c.repo_group = self.request.db_repo_group
298 repo_data, repo_group_data = self._get_groups_and_repos(
314 repo_data, repo_group_data = self._get_groups_and_repos(
299 c.repo_group.group_id)
315 c.repo_group.group_id)
300
316
301 # json used to render the grids
317 # json used to render the grids
302 c.repos_data = json.dumps(repo_data)
318 c.repos_data = json.dumps(repo_data)
303 c.repo_groups_data = json.dumps(repo_group_data)
319 c.repo_groups_data = json.dumps(repo_group_data)
304
320
305 return self._get_template_context(c)
321 return self._get_template_context(c)
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now