comparison rhodecode/model/hg.py @ 665:070f32743632 beta

Moved out reposcan into hg Model. Rewrote repo scann and caching of repositories, all is in hgModel. Changed invalidate cache calls. mergeds main repo list and repo switcher list into one new based on hgModel.
author Marcin Kuzminski <marcin@python-works.com>
date Sun, 07 Nov 2010 15:02:56 +0100
parents ffd07396d315
children 4685f3eafd35
comparison
equal deleted inserted replaced
664:50d9b1afaca7 665:070f32743632
20 """ 20 """
21 Created on April 9, 2010 21 Created on April 9, 2010
22 Model for RhodeCode 22 Model for RhodeCode
23 @author: marcink 23 @author: marcink
24 """ 24 """
25 from beaker.cache import cache_region 25 from beaker.cache import cache_region, region_invalidate
26 from mercurial import ui 26 from mercurial import ui
27 from rhodecode.lib import helpers as h 27 from rhodecode.lib import helpers as h
28 from rhodecode.lib.utils import invalidate_cache
29 from rhodecode.lib.auth import HasRepoPermissionAny 28 from rhodecode.lib.auth import HasRepoPermissionAny
29 from rhodecode.lib.utils import get_repos
30 from rhodecode.model import meta 30 from rhodecode.model import meta
31 from rhodecode.model.db import Repository, User 31 from rhodecode.model.caching_query import FromCache
32 from rhodecode.model.db import Repository, User, RhodeCodeUi
32 from sqlalchemy.orm import joinedload 33 from sqlalchemy.orm import joinedload
34 from vcs import get_repo as vcs_get_repo, get_backend
35 from vcs.backends.hg import MercurialRepository
33 from vcs.exceptions import RepositoryError, VCSError 36 from vcs.exceptions import RepositoryError, VCSError
37 from vcs.utils.lazy import LazyProperty
34 import logging 38 import logging
35 import sys 39 import os
36 import time 40 import time
37 41
38 log = logging.getLogger(__name__) 42 log = logging.getLogger(__name__)
39
40 try:
41 from vcs.backends.hg import MercurialRepository
42 from vcs.backends.git import GitRepository
43 except ImportError:
44 sys.stderr.write('You have to import vcs module')
45 raise Exception('Unable to import vcs')
46
47 def _get_repos_cached_initial(app_globals, initial):
48 """return cached dict with repos
49 """
50 g = app_globals
51 return HgModel().repo_scan(g.paths[0][1], g.baseui, initial)
52
53 @cache_region('long_term', 'cached_repo_list')
54 def _get_repos_cached():
55 """return cached dict with repos
56 """
57 log.info('getting all repositories list')
58 from pylons import app_globals as g
59 return HgModel().repo_scan(g.paths[0][1], g.baseui)
60
61 @cache_region('super_short_term', 'cached_repos_switcher_list')
62 def _get_repos_switcher_cached(cached_repo_list):
63 repos_lst = []
64 for repo in [x for x in cached_repo_list.values()]:
65 if HasRepoPermissionAny('repository.write', 'repository.read',
66 'repository.admin')(repo.name, 'main page check'):
67 repos_lst.append((repo.name, repo.dbrepo.private,))
68
69 return sorted(repos_lst, key=lambda k:k[0].lower())
70
71 @cache_region('long_term', 'full_changelog')
72 def _full_changelog_cached(repo_name):
73 log.info('getting full changelog for %s', repo_name)
74 return list(reversed(list(HgModel().get_repo(repo_name))))
75 43
76 class HgModel(object): 44 class HgModel(object):
77 """ 45 """
78 Mercurial Model 46 Mercurial Model
79 """ 47 """
82 if not sa: 50 if not sa:
83 self.sa = meta.Session() 51 self.sa = meta.Session()
84 else: 52 else:
85 self.sa = sa 53 self.sa = sa
86 54
55
56 @LazyProperty
57 def repos_path(self):
58 """
59 Get's the repositories root path from database
60 """
61 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
62
63 return q.ui_value
64
87 def repo_scan(self, repos_path, baseui, initial=False): 65 def repo_scan(self, repos_path, baseui, initial=False):
88 """ 66 """
89 Listing of repositories in given path. This path should not be a 67 Listing of repositories in given path. This path should not be a
90 repository itself. Return a dictionary of repository objects 68 repository itself. Return a dictionary of repository objects
91 69
92 :param repos_path: path to directory containing repositories 70 :param repos_path: path to directory containing repositories
93 :param baseui 71 :param baseui
94 :param initial: initial scann 72 :param initial: initial scan
95 """ 73 """
96 log.info('scanning for repositories in %s', repos_path) 74 log.info('scanning for repositories in %s', repos_path)
97 75
98 if not isinstance(baseui, ui.ui): 76 if not isinstance(baseui, ui.ui):
99 baseui = ui.ui() 77 baseui = ui.ui()
78 repos_list = {}
79 for name, path in get_repos(repos_path):
80 try:
81 if repos_list.has_key(name):
82 raise RepositoryError('Duplicate repository name %s '
83 'found in %s' % (name, path))
84 else:
100 85
101 from rhodecode.lib.utils import get_repos 86 klass = get_backend(path[0])
102 repos = get_repos(repos_path)
103 87
104
105 repos_list = {}
106 for name, path in repos:
107 try:
108 #name = name.split('/')[-1]
109 if repos_list.has_key(name):
110 raise RepositoryError('Duplicate repository name %s found in'
111 ' %s' % (name, path))
112 else:
113 if path[0] == 'hg': 88 if path[0] == 'hg':
114 repos_list[name] = MercurialRepository(path[1], baseui=baseui) 89 repos_list[name] = klass(path[1], baseui=baseui)
115 repos_list[name].name = name
116 90
117 if path[0] == 'git': 91 if path[0] == 'git':
118 repos_list[name] = GitRepository(path[1]) 92 repos_list[name] = klass(path[1])
119 repos_list[name].name = name
120
121 dbrepo = None
122 if not initial:
123 #for initial scann on application first run we don't
124 #have db repos yet.
125 dbrepo = self.sa.query(Repository)\
126 .options(joinedload(Repository.fork))\
127 .filter(Repository.repo_name == name)\
128 .scalar()
129
130 if dbrepo:
131 log.info('Adding db instance to cached list')
132 repos_list[name].dbrepo = dbrepo
133 repos_list[name].description = dbrepo.description
134 if dbrepo.user:
135 repos_list[name].contact = dbrepo.user.full_contact
136 else:
137 repos_list[name].contact = self.sa.query(User)\
138 .filter(User.admin == True).first().full_contact
139 except OSError: 93 except OSError:
140 continue 94 continue
141 95
142 return repos_list 96 return repos_list
143 97
144 def get_repos(self): 98 def get_repos(self, all_repos=None):
145 for name, repo in _get_repos_cached().items(): 99 """
100 Get all repos from db and for each such repo make backend and
101 fetch dependent data from db
102 """
103 if not all_repos:
104 all_repos = self.sa.query(Repository).all()
146 105
147 if isinstance(repo, MercurialRepository) and repo._get_hidden(): 106 for r in all_repos:
148 #skip hidden web repository
149 continue
150 107
151 last_change = repo.last_change 108 repo = self.get(r.repo_name)
152 tip = h.get_changeset_safe(repo, 'tip')
153 109
154 tmp_d = {} 110 if repo is not None:
155 tmp_d['name'] = repo.name 111 last_change = repo.last_change
156 tmp_d['name_sort'] = tmp_d['name'].lower() 112 tip = h.get_changeset_safe(repo, 'tip')
157 tmp_d['description'] = repo.description 113
158 tmp_d['description_sort'] = tmp_d['description'] 114 tmp_d = {}
159 tmp_d['last_change'] = last_change 115 tmp_d['name'] = repo.name
160 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple()) 116 tmp_d['name_sort'] = tmp_d['name'].lower()
161 tmp_d['tip'] = tip.raw_id 117 tmp_d['description'] = repo.dbrepo.description
162 tmp_d['tip_sort'] = tip.revision 118 tmp_d['description_sort'] = tmp_d['description']
163 tmp_d['rev'] = tip.revision 119 tmp_d['last_change'] = last_change
164 tmp_d['contact'] = repo.contact 120 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
165 tmp_d['contact_sort'] = tmp_d['contact'] 121 tmp_d['tip'] = tip.raw_id
166 tmp_d['repo_archives'] = list(repo._get_archives()) 122 tmp_d['tip_sort'] = tip.revision
167 tmp_d['last_msg'] = tip.message 123 tmp_d['rev'] = tip.revision
168 tmp_d['repo'] = repo 124 tmp_d['contact'] = repo.dbrepo.user.full_contact
169 yield tmp_d 125 tmp_d['contact_sort'] = tmp_d['contact']
126 tmp_d['repo_archives'] = list(repo._get_archives())
127 tmp_d['last_msg'] = tip.message
128 tmp_d['repo'] = repo
129 yield tmp_d
170 130
171 def get_repo(self, repo_name): 131 def get_repo(self, repo_name):
172 try: 132 return self.get(repo_name)
173 repo = _get_repos_cached()[repo_name] 133
174 return repo 134 def get(self, repo_name):
175 except KeyError: 135 """
176 #i we're here and we got key errors let's try to invalidate the 136 Get's repository from given name, creates BackendInstance and
177 #cahce and try again 137 propagates it's data from database with all additional information
178 invalidate_cache('cached_repo_list') 138 :param repo_name:
179 repo = _get_repos_cached()[repo_name] 139 """
140 if not HasRepoPermissionAny('repository.read', 'repository.write',
141 'repository.admin')(repo_name, 'get repo check'):
142 return
143
144 @cache_region('long_term', 'get_repo_cached_%s' % repo_name)
145 def _get_repo(repo_name):
146
147 repo = vcs_get_repo(os.path.join(self.repos_path, repo_name),
148 alias=None, create=False)
149
150 #skip hidden web repository
151 if isinstance(repo, MercurialRepository) and repo._get_hidden():
152 return
153
154 dbrepo = self.sa.query(Repository)\
155 .options(joinedload(Repository.fork))\
156 .options(joinedload(Repository.user))\
157 .filter(Repository.repo_name == repo_name)\
158 .scalar()
159 repo.dbrepo = dbrepo
180 return repo 160 return repo
181 161
162 invalidate = False
163 if invalidate:
164 log.info('INVALIDATING CACHE FOR %s', repo_name)
165 region_invalidate(_get_repo, None, repo_name)
182 166
167 return _get_repo(repo_name)
183 168