# HG changeset patch # User Daniel Dourvaris # Date 2016-05-30 13:05:27 # Node ID 81398162ff45196a6ee4792b3799efa2fa5955ae # Parent 01e2523cc84036014eba1fa267c29aa491b565f5 feature: Go To switcher now searches commit hashes as well diff --git a/rhodecode/config/routing.py b/rhodecode/config/routing.py --- a/rhodecode/config/routing.py +++ b/rhodecode/config/routing.py @@ -125,8 +125,8 @@ def make_map(config): # MAIN PAGE rmap.connect('home', '/', controller='home', action='index') - rmap.connect('repo_switcher_data', '/_repos_and_groups', controller='home', - action='repo_switcher_data') + rmap.connect('goto_switcher_data', '/_goto_data', controller='home', + action='goto_switcher_data') rmap.connect('repo_list_data', '/_repos', controller='home', action='repo_list_data') diff --git a/rhodecode/controllers/home.py b/rhodecode/controllers/home.py --- a/rhodecode/controllers/home.py +++ b/rhodecode/controllers/home.py @@ -24,16 +24,17 @@ Home controller for RhodeCode Enterprise import logging import time - +import re -from pylons import tmpl_context as c, request +from pylons import tmpl_context as c, request, url, config from pylons.i18n.translation import _ from sqlalchemy.sql import func from rhodecode.lib.auth import ( - LoginRequired, HasPermissionAllDecorator, + LoginRequired, HasPermissionAllDecorator, AuthUser, HasRepoGroupPermissionAnyDecorator, XHRRequired) from rhodecode.lib.base import BaseController, render +from rhodecode.lib.index import searcher_from_config from rhodecode.lib.ext_json import json from rhodecode.lib.utils import jsonify from rhodecode.lib.utils2 import safe_unicode @@ -134,7 +135,8 @@ class HomeController(BaseController): 'id': obj['name'], 'text': obj['name'], 'type': 'repo', - 'obj': obj['dbrepo'] + 'obj': obj['dbrepo'], + 'url': url('summary_home', repo_name=obj['name']) } for obj in repo_iter] @@ -156,16 +158,45 @@ class HomeController(BaseController): 'id': obj.group_name, 'text': obj.group_name, 'type': 'group', - 'obj': {} + 'obj': {}, + 'url': url('repo_group_home', group_name=obj.group_name) } for obj in repo_groups_iter] + def _get_hash_commit_list(self, hash_starts_with=None, limit=20): + if not hash_starts_with or len(hash_starts_with) < 3: + return [] + + commit_hashes = re.compile('([0-9a-f]{2,40})').findall(hash_starts_with) + + if len(commit_hashes) != 1: + return [] + + commit_hash_prefix = commit_hashes[0] + + auth_user = AuthUser( + user_id=c.rhodecode_user.user_id, ip_addr=self.ip_addr) + searcher = searcher_from_config(config) + result = searcher.search( + 'commit_id:%s*' % commit_hash_prefix, 'commit', auth_user) + + return [ + { + 'id': entry['commit_id'], + 'text': entry['commit_id'], + 'type': 'commit', + 'obj': {'repo': entry['repository']}, + 'url': url('changeset_home', + repo_name=entry['repository'], revision=entry['commit_id']) + } + for entry in result['results']] + @LoginRequired() @XHRRequired() @jsonify - def repo_switcher_data(self): + def goto_switcher_data(self): query = request.GET.get('query') - log.debug('generating switcher repo/groups list, query %s', query) + log.debug('generating goto switcher list, query %s', query) res = [] repo_groups = self._get_repo_group_list(query) @@ -182,6 +213,19 @@ class HomeController(BaseController): 'children': repos }) + commits = self._get_hash_commit_list(query) + if commits: + unique_repos = {} + for commit in commits: + unique_repos.setdefault(commit['obj']['repo'], [] + ).append(commit) + + for repo in unique_repos: + res.append({ + 'text': _('Commits in %(repo)s') % {'repo': repo}, + 'children': unique_repos[repo] + }) + data = { 'more': False, 'results': res @@ -203,6 +247,7 @@ class HomeController(BaseController): 'text': _('Repositories'), 'children': repos }) + data = { 'more': False, 'results': res diff --git a/rhodecode/lib/index/__init__.py b/rhodecode/lib/index/__init__.py --- a/rhodecode/lib/index/__init__.py +++ b/rhodecode/lib/index/__init__.py @@ -42,7 +42,6 @@ class BaseSearch(object): def search(self, query, document_type, search_user, repo_name=None): raise Exception('NotImplemented') - def searcher_from_config(config, prefix='search.'): _config = {} for key in config.keys(): diff --git a/rhodecode/lib/index/whoosh.py b/rhodecode/lib/index/whoosh.py --- a/rhodecode/lib/index/whoosh.py +++ b/rhodecode/lib/index/whoosh.py @@ -25,6 +25,7 @@ Index schema for RhodeCode from __future__ import absolute_import import logging import os +import re from pylons.i18n.translation import _ @@ -59,6 +60,7 @@ FRAGMENTER = ContextFragmenter(200) log = logging.getLogger(__name__) + class Search(BaseSearch): name = 'whoosh' @@ -90,8 +92,19 @@ class Search(BaseSearch): if self.searcher: self.searcher.close() + def _extend_query(self, query): + hashes = re.compile('([0-9a-f]{5,40})').findall(query) + if hashes: + hashes_or_query = ' OR '.join('commit_id:%s*' % h for h in hashes) + query = u'(%s) OR %s' % (query, hashes_or_query) + return query + def search(self, query, document_type, search_user, repo_name=None, requested_page=1, page_limit=10): + + original_query = query + query = self._extend_query(query) + log.debug(u'QUERY: %s on %s', query, document_type) result = { 'results': [], diff --git a/rhodecode/templates/base/base.html b/rhodecode/templates/base/base.html --- a/rhodecode/templates/base/base.html +++ b/rhodecode/templates/base/base.html @@ -455,8 +455,11 @@ tmpl += ' '; } } + if(obj_dict && state.type == 'commit') { + tmpl += ''; + } if(obj_dict && state.type == 'group'){ - tmpl += ' '; + tmpl += ' '; } tmpl += escapeMarkup(state.text); return tmpl; @@ -496,7 +499,7 @@ query.callback({results: cachedData.results}); } else { $.ajax({ - url: "${h.url('repo_switcher_data')}", + url: "${h.url('goto_switcher_data')}", data: {'query': query.term}, dataType: 'json', type: 'GET', @@ -514,7 +517,7 @@ $("#repo_switcher").on('select2-selecting', function(e){ e.preventDefault(); - window.location = pyroutes.url('summary_home', {'repo_name': e.val}); + window.location = e.choice.url; }); ## Global mouse bindings ## diff --git a/rhodecode/tests/functional/test_home.py b/rhodecode/tests/functional/test_home.py --- a/rhodecode/tests/functional/test_home.py +++ b/rhodecode/tests/functional/test_home.py @@ -181,19 +181,25 @@ class TestUserAutocompleteData(TestContr def assert_and_get_content(result): repos = [] groups = [] + commits = [] for data in result: for data_item in data['children']: assert data_item['id'] assert data_item['text'] + assert data_item['url'] if data_item['type'] == 'repo': repos.append(data_item) - else: + elif data_item['type'] == 'group': groups.append(data_item) + elif data_item['type'] == 'commit': + commits.append(data_item) + else: + raise Exception('invalid type %s' % data_item['type']) - return repos, groups + return repos, groups, commits -class TestRepoSwitcherData(TestController): +class TestGotoSwitcherData(TestController): required_repos_with_groups = [ 'abc', 'abc-fork', @@ -253,39 +259,41 @@ class TestRepoSwitcherData(TestControlle self.log_user() response = self.app.get( - url(controller='home', action='repo_switcher_data'), + url(controller='home', action='goto_switcher_data'), headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200) result = json.loads(response.body)['results'] - repos, groups = assert_and_get_content(result) + repos, groups, commits = assert_and_get_content(result) assert len(repos) == len(Repository.get_all()) assert len(groups) == len(RepoGroup.get_all()) + assert len(commits) == 0 def test_returns_list_of_repos_and_groups_filtered(self): self.log_user() response = self.app.get( - url(controller='home', action='repo_switcher_data'), + url(controller='home', action='goto_switcher_data'), headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, params={'query': 'abc'}, status=200) result = json.loads(response.body)['results'] - repos, groups = assert_and_get_content(result) + repos, groups, commits = assert_and_get_content(result) assert len(repos) == 13 assert len(groups) == 5 + assert len(commits) == 0 def test_returns_list_of_properly_sorted_and_filtered(self): self.log_user() response = self.app.get( - url(controller='home', action='repo_switcher_data'), + url(controller='home', action='goto_switcher_data'), headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, params={'query': 'abc'}, status=200) result = json.loads(response.body)['results'] - repos, groups = assert_and_get_content(result) + repos, groups, commits = assert_and_get_content(result) test_repos = [x['text'] for x in repos[:4]] assert ['abc', 'abcd', 'a/abc', 'abcde'] == test_repos @@ -300,54 +308,58 @@ class TestRepoListData(TestController): self.log_user() response = self.app.get( - url(controller='home', action='repo_switcher_data'), + url(controller='home', action='repo_list_data'), headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200) result = json.loads(response.body)['results'] - repos, groups = assert_and_get_content(result) + repos, groups, commits = assert_and_get_content(result) assert len(repos) == len(Repository.get_all()) assert len(groups) == 0 + assert len(commits) == 0 def test_returns_list_of_repos_and_groups_filtered(self): self.log_user() response = self.app.get( - url(controller='home', action='repo_switcher_data'), + url(controller='home', action='repo_list_data'), headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, params={'query': 'vcs_test_git'}, status=200) result = json.loads(response.body)['results'] - repos, groups = assert_and_get_content(result) + repos, groups, commits = assert_and_get_content(result) assert len(repos) == len(Repository.query().filter( Repository.repo_name.ilike('%vcs_test_git%')).all()) assert len(groups) == 0 + assert len(commits) == 0 def test_returns_list_of_repos_and_groups_filtered_with_type(self): self.log_user() response = self.app.get( - url(controller='home', action='repo_switcher_data'), + url(controller='home', action='repo_list_data'), headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, params={'query': 'vcs_test_git', 'repo_type': 'git'}, status=200) result = json.loads(response.body)['results'] - repos, groups = assert_and_get_content(result) + repos, groups, commits = assert_and_get_content(result) assert len(repos) == len(Repository.query().filter( Repository.repo_name.ilike('%vcs_test_git%')).all()) assert len(groups) == 0 + assert len(commits) == 0 def test_returns_list_of_repos_non_ascii_query(self): self.log_user() response = self.app.get( - url(controller='home', action='repo_switcher_data'), + url(controller='home', action='repo_list_data'), headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, params={'query': 'ć_vcs_test_ą', 'repo_type': 'git'}, status=200) result = json.loads(response.body)['results'] - repos, groups = assert_and_get_content(result) + repos, groups, commits = assert_and_get_content(result) assert len(repos) == 0 assert len(groups) == 0 + assert len(commits) == 0 diff --git a/rhodecode/tests/functional/test_search.py b/rhodecode/tests/functional/test_search.py --- a/rhodecode/tests/functional/test_search.py +++ b/rhodecode/tests/functional/test_search.py @@ -129,6 +129,10 @@ class TestSearchController(TestControlle ('author:marcin@python-blog.com ' 'commit_id:b986218ba1c9b0d6a259fac9b050b1724ed8e545', 1, [ ('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]), + ('b986218ba1c9b0d6a259fac9b050b1724ed8e545', 1, [ + ('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]), + ('b986218b', 1, [ + ('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]), ]) def test_search_commit_messages( self, query, expected_hits, expected_commits, enabled_backends):