##// END OF EJS Templates
feature: Go To switcher now searches commit hashes as well
dan -
r62:81398162 default
parent child Browse files
Show More
@@ -125,8 +125,8 b' def make_map(config):'
125 125
126 126 # MAIN PAGE
127 127 rmap.connect('home', '/', controller='home', action='index')
128 rmap.connect('repo_switcher_data', '/_repos_and_groups', controller='home',
129 action='repo_switcher_data')
128 rmap.connect('goto_switcher_data', '/_goto_data', controller='home',
129 action='goto_switcher_data')
130 130 rmap.connect('repo_list_data', '/_repos', controller='home',
131 131 action='repo_list_data')
132 132
@@ -24,16 +24,17 b' Home controller for RhodeCode Enterprise'
24 24
25 25 import logging
26 26 import time
27
27 import re
28 28
29 from pylons import tmpl_context as c, request
29 from pylons import tmpl_context as c, request, url, config
30 30 from pylons.i18n.translation import _
31 31 from sqlalchemy.sql import func
32 32
33 33 from rhodecode.lib.auth import (
34 LoginRequired, HasPermissionAllDecorator,
34 LoginRequired, HasPermissionAllDecorator, AuthUser,
35 35 HasRepoGroupPermissionAnyDecorator, XHRRequired)
36 36 from rhodecode.lib.base import BaseController, render
37 from rhodecode.lib.index import searcher_from_config
37 38 from rhodecode.lib.ext_json import json
38 39 from rhodecode.lib.utils import jsonify
39 40 from rhodecode.lib.utils2 import safe_unicode
@@ -134,7 +135,8 b' class HomeController(BaseController):'
134 135 'id': obj['name'],
135 136 'text': obj['name'],
136 137 'type': 'repo',
137 'obj': obj['dbrepo']
138 'obj': obj['dbrepo'],
139 'url': url('summary_home', repo_name=obj['name'])
138 140 }
139 141 for obj in repo_iter]
140 142
@@ -156,16 +158,45 b' class HomeController(BaseController):'
156 158 'id': obj.group_name,
157 159 'text': obj.group_name,
158 160 'type': 'group',
159 'obj': {}
161 'obj': {},
162 'url': url('repo_group_home', group_name=obj.group_name)
160 163 }
161 164 for obj in repo_groups_iter]
162 165
166 def _get_hash_commit_list(self, hash_starts_with=None, limit=20):
167 if not hash_starts_with or len(hash_starts_with) < 3:
168 return []
169
170 commit_hashes = re.compile('([0-9a-f]{2,40})').findall(hash_starts_with)
171
172 if len(commit_hashes) != 1:
173 return []
174
175 commit_hash_prefix = commit_hashes[0]
176
177 auth_user = AuthUser(
178 user_id=c.rhodecode_user.user_id, ip_addr=self.ip_addr)
179 searcher = searcher_from_config(config)
180 result = searcher.search(
181 'commit_id:%s*' % commit_hash_prefix, 'commit', auth_user)
182
183 return [
184 {
185 'id': entry['commit_id'],
186 'text': entry['commit_id'],
187 'type': 'commit',
188 'obj': {'repo': entry['repository']},
189 'url': url('changeset_home',
190 repo_name=entry['repository'], revision=entry['commit_id'])
191 }
192 for entry in result['results']]
193
163 194 @LoginRequired()
164 195 @XHRRequired()
165 196 @jsonify
166 def repo_switcher_data(self):
197 def goto_switcher_data(self):
167 198 query = request.GET.get('query')
168 log.debug('generating switcher repo/groups list, query %s', query)
199 log.debug('generating goto switcher list, query %s', query)
169 200
170 201 res = []
171 202 repo_groups = self._get_repo_group_list(query)
@@ -182,6 +213,19 b' class HomeController(BaseController):'
182 213 'children': repos
183 214 })
184 215
216 commits = self._get_hash_commit_list(query)
217 if commits:
218 unique_repos = {}
219 for commit in commits:
220 unique_repos.setdefault(commit['obj']['repo'], []
221 ).append(commit)
222
223 for repo in unique_repos:
224 res.append({
225 'text': _('Commits in %(repo)s') % {'repo': repo},
226 'children': unique_repos[repo]
227 })
228
185 229 data = {
186 230 'more': False,
187 231 'results': res
@@ -203,6 +247,7 b' class HomeController(BaseController):'
203 247 'text': _('Repositories'),
204 248 'children': repos
205 249 })
250
206 251 data = {
207 252 'more': False,
208 253 'results': res
@@ -42,7 +42,6 b' class BaseSearch(object):'
42 42 def search(self, query, document_type, search_user, repo_name=None):
43 43 raise Exception('NotImplemented')
44 44
45
46 45 def searcher_from_config(config, prefix='search.'):
47 46 _config = {}
48 47 for key in config.keys():
@@ -25,6 +25,7 b' Index schema for RhodeCode'
25 25 from __future__ import absolute_import
26 26 import logging
27 27 import os
28 import re
28 29
29 30 from pylons.i18n.translation import _
30 31
@@ -59,6 +60,7 b' FRAGMENTER = ContextFragmenter(200)'
59 60 log = logging.getLogger(__name__)
60 61
61 62
63
62 64 class Search(BaseSearch):
63 65
64 66 name = 'whoosh'
@@ -90,8 +92,19 b' class Search(BaseSearch):'
90 92 if self.searcher:
91 93 self.searcher.close()
92 94
95 def _extend_query(self, query):
96 hashes = re.compile('([0-9a-f]{5,40})').findall(query)
97 if hashes:
98 hashes_or_query = ' OR '.join('commit_id:%s*' % h for h in hashes)
99 query = u'(%s) OR %s' % (query, hashes_or_query)
100 return query
101
93 102 def search(self, query, document_type, search_user, repo_name=None,
94 103 requested_page=1, page_limit=10):
104
105 original_query = query
106 query = self._extend_query(query)
107
95 108 log.debug(u'QUERY: %s on %s', query, document_type)
96 109 result = {
97 110 'results': [],
@@ -455,6 +455,9 b''
455 455 tmpl += '<i class="icon-unlock-alt"></i> ';
456 456 }
457 457 }
458 if(obj_dict && state.type == 'commit') {
459 tmpl += '<i class="icon-tag"></i>';
460 }
458 461 if(obj_dict && state.type == 'group'){
459 462 tmpl += '<i class="icon-folder-close"></i> ';
460 463 }
@@ -496,7 +499,7 b''
496 499 query.callback({results: cachedData.results});
497 500 } else {
498 501 $.ajax({
499 url: "${h.url('repo_switcher_data')}",
502 url: "${h.url('goto_switcher_data')}",
500 503 data: {'query': query.term},
501 504 dataType: 'json',
502 505 type: 'GET',
@@ -514,7 +517,7 b''
514 517
515 518 $("#repo_switcher").on('select2-selecting', function(e){
516 519 e.preventDefault();
517 window.location = pyroutes.url('summary_home', {'repo_name': e.val});
520 window.location = e.choice.url;
518 521 });
519 522
520 523 ## Global mouse bindings ##
@@ -181,19 +181,25 b' class TestUserAutocompleteData(TestContr'
181 181 def assert_and_get_content(result):
182 182 repos = []
183 183 groups = []
184 commits = []
184 185 for data in result:
185 186 for data_item in data['children']:
186 187 assert data_item['id']
187 188 assert data_item['text']
189 assert data_item['url']
188 190 if data_item['type'] == 'repo':
189 191 repos.append(data_item)
190 else:
192 elif data_item['type'] == 'group':
191 193 groups.append(data_item)
194 elif data_item['type'] == 'commit':
195 commits.append(data_item)
196 else:
197 raise Exception('invalid type %s' % data_item['type'])
192 198
193 return repos, groups
199 return repos, groups, commits
194 200
195 201
196 class TestRepoSwitcherData(TestController):
202 class TestGotoSwitcherData(TestController):
197 203 required_repos_with_groups = [
198 204 'abc',
199 205 'abc-fork',
@@ -253,39 +259,41 b' class TestRepoSwitcherData(TestControlle'
253 259 self.log_user()
254 260
255 261 response = self.app.get(
256 url(controller='home', action='repo_switcher_data'),
262 url(controller='home', action='goto_switcher_data'),
257 263 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
258 264 result = json.loads(response.body)['results']
259 265
260 repos, groups = assert_and_get_content(result)
266 repos, groups, commits = assert_and_get_content(result)
261 267
262 268 assert len(repos) == len(Repository.get_all())
263 269 assert len(groups) == len(RepoGroup.get_all())
270 assert len(commits) == 0
264 271
265 272 def test_returns_list_of_repos_and_groups_filtered(self):
266 273 self.log_user()
267 274
268 275 response = self.app.get(
269 url(controller='home', action='repo_switcher_data'),
276 url(controller='home', action='goto_switcher_data'),
270 277 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
271 278 params={'query': 'abc'}, status=200)
272 279 result = json.loads(response.body)['results']
273 280
274 repos, groups = assert_and_get_content(result)
281 repos, groups, commits = assert_and_get_content(result)
275 282
276 283 assert len(repos) == 13
277 284 assert len(groups) == 5
285 assert len(commits) == 0
278 286
279 287 def test_returns_list_of_properly_sorted_and_filtered(self):
280 288 self.log_user()
281 289
282 290 response = self.app.get(
283 url(controller='home', action='repo_switcher_data'),
291 url(controller='home', action='goto_switcher_data'),
284 292 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
285 293 params={'query': 'abc'}, status=200)
286 294 result = json.loads(response.body)['results']
287 295
288 repos, groups = assert_and_get_content(result)
296 repos, groups, commits = assert_and_get_content(result)
289 297
290 298 test_repos = [x['text'] for x in repos[:4]]
291 299 assert ['abc', 'abcd', 'a/abc', 'abcde'] == test_repos
@@ -300,54 +308,58 b' class TestRepoListData(TestController):'
300 308 self.log_user()
301 309
302 310 response = self.app.get(
303 url(controller='home', action='repo_switcher_data'),
311 url(controller='home', action='repo_list_data'),
304 312 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
305 313 result = json.loads(response.body)['results']
306 314
307 repos, groups = assert_and_get_content(result)
315 repos, groups, commits = assert_and_get_content(result)
308 316
309 317 assert len(repos) == len(Repository.get_all())
310 318 assert len(groups) == 0
319 assert len(commits) == 0
311 320
312 321 def test_returns_list_of_repos_and_groups_filtered(self):
313 322 self.log_user()
314 323
315 324 response = self.app.get(
316 url(controller='home', action='repo_switcher_data'),
325 url(controller='home', action='repo_list_data'),
317 326 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
318 327 params={'query': 'vcs_test_git'}, status=200)
319 328 result = json.loads(response.body)['results']
320 329
321 repos, groups = assert_and_get_content(result)
330 repos, groups, commits = assert_and_get_content(result)
322 331
323 332 assert len(repos) == len(Repository.query().filter(
324 333 Repository.repo_name.ilike('%vcs_test_git%')).all())
325 334 assert len(groups) == 0
335 assert len(commits) == 0
326 336
327 337 def test_returns_list_of_repos_and_groups_filtered_with_type(self):
328 338 self.log_user()
329 339
330 340 response = self.app.get(
331 url(controller='home', action='repo_switcher_data'),
341 url(controller='home', action='repo_list_data'),
332 342 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
333 343 params={'query': 'vcs_test_git', 'repo_type': 'git'}, status=200)
334 344 result = json.loads(response.body)['results']
335 345
336 repos, groups = assert_and_get_content(result)
346 repos, groups, commits = assert_and_get_content(result)
337 347
338 348 assert len(repos) == len(Repository.query().filter(
339 349 Repository.repo_name.ilike('%vcs_test_git%')).all())
340 350 assert len(groups) == 0
351 assert len(commits) == 0
341 352
342 353 def test_returns_list_of_repos_non_ascii_query(self):
343 354 self.log_user()
344 355 response = self.app.get(
345 url(controller='home', action='repo_switcher_data'),
356 url(controller='home', action='repo_list_data'),
346 357 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
347 358 params={'query': 'ć_vcs_test_ą', 'repo_type': 'git'}, status=200)
348 359 result = json.loads(response.body)['results']
349 360
350 repos, groups = assert_and_get_content(result)
361 repos, groups, commits = assert_and_get_content(result)
351 362
352 363 assert len(repos) == 0
353 364 assert len(groups) == 0
365 assert len(commits) == 0
@@ -129,6 +129,10 b' class TestSearchController(TestControlle'
129 129 ('author:marcin@python-blog.com '
130 130 'commit_id:b986218ba1c9b0d6a259fac9b050b1724ed8e545', 1, [
131 131 ('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]),
132 ('b986218ba1c9b0d6a259fac9b050b1724ed8e545', 1, [
133 ('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]),
134 ('b986218b', 1, [
135 ('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]),
132 136 ])
133 137 def test_search_commit_messages(
134 138 self, query, expected_hits, expected_commits, enabled_backends):
General Comments 0
You need to be logged in to leave comments. Login now