##// END OF EJS Templates
bugfix, when user had no repos he would see all repos in my account, (correct commit)
marcink -
r767:632b0761 beta
parent child Browse files
Show More
@@ -1,365 +1,365 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 package.rhodecode.model.scm
3 package.rhodecode.model.scm
4 ~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~
5
5
6 scm model for RhodeCode
6 scm model for RhodeCode
7 :created_on: Apr 9, 2010
7 :created_on: Apr 9, 2010
8 :author: marcink
8 :author: marcink
9 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
9 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :license: GPLv3, see COPYING for more details.
10 :license: GPLv3, see COPYING for more details.
11 """
11 """
12 # This program is free software; you can redistribute it and/or
12 # This program is free software; you can redistribute it and/or
13 # modify it under the terms of the GNU General Public License
13 # modify it under the terms of the GNU General Public License
14 # as published by the Free Software Foundation; version 2
14 # as published by the Free Software Foundation; version 2
15 # of the License or (at your opinion) any later version of the license.
15 # of the License or (at your opinion) any later version of the license.
16 #
16 #
17 # This program is distributed in the hope that it will be useful,
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
20 # GNU General Public License for more details.
21 #
21 #
22 # You should have received a copy of the GNU General Public License
22 # You should have received a copy of the GNU General Public License
23 # along with this program; if not, write to the Free Software
23 # along with this program; if not, write to the Free Software
24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # MA 02110-1301, USA.
25 # MA 02110-1301, USA.
26
26
27 import os
27 import os
28 import time
28 import time
29 import traceback
29 import traceback
30 import logging
30 import logging
31
31
32 from vcs import get_backend
32 from vcs import get_backend
33 from vcs.utils.helpers import get_scm
33 from vcs.utils.helpers import get_scm
34 from vcs.exceptions import RepositoryError, VCSError
34 from vcs.exceptions import RepositoryError, VCSError
35 from vcs.utils.lazy import LazyProperty
35 from vcs.utils.lazy import LazyProperty
36
36
37 from mercurial import ui
37 from mercurial import ui
38
38
39 from beaker.cache import cache_region, region_invalidate
39 from beaker.cache import cache_region, region_invalidate
40
40
41 from rhodecode import BACKENDS
41 from rhodecode import BACKENDS
42 from rhodecode.lib import helpers as h
42 from rhodecode.lib import helpers as h
43 from rhodecode.lib.auth import HasRepoPermissionAny
43 from rhodecode.lib.auth import HasRepoPermissionAny
44 from rhodecode.lib.utils import get_repos, make_ui, action_logger
44 from rhodecode.lib.utils import get_repos, make_ui, action_logger
45 from rhodecode.model import BaseModel
45 from rhodecode.model import BaseModel
46 from rhodecode.model.user import UserModel
46 from rhodecode.model.user import UserModel
47
47
48 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
48 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
49 UserFollowing
49 UserFollowing
50 from rhodecode.model.caching_query import FromCache
50 from rhodecode.model.caching_query import FromCache
51
51
52 from sqlalchemy.orm import joinedload
52 from sqlalchemy.orm import joinedload
53 from sqlalchemy.orm.session import make_transient
53 from sqlalchemy.orm.session import make_transient
54 from sqlalchemy.exc import DatabaseError
54 from sqlalchemy.exc import DatabaseError
55
55
56 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
57
57
58
58
59 class UserTemp(object):
59 class UserTemp(object):
60 def __init__(self, user_id):
60 def __init__(self, user_id):
61 self.user_id = user_id
61 self.user_id = user_id
62 class RepoTemp(object):
62 class RepoTemp(object):
63 def __init__(self, repo_id):
63 def __init__(self, repo_id):
64 self.repo_id = repo_id
64 self.repo_id = repo_id
65
65
66 class ScmModel(BaseModel):
66 class ScmModel(BaseModel):
67 """
67 """
68 Mercurial Model
68 Mercurial Model
69 """
69 """
70
70
71 @LazyProperty
71 @LazyProperty
72 def repos_path(self):
72 def repos_path(self):
73 """
73 """
74 Get's the repositories root path from database
74 Get's the repositories root path from database
75 """
75 """
76 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
76 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
77
77
78 return q.ui_value
78 return q.ui_value
79
79
80 def repo_scan(self, repos_path, baseui):
80 def repo_scan(self, repos_path, baseui):
81 """
81 """
82 Listing of repositories in given path. This path should not be a
82 Listing of repositories in given path. This path should not be a
83 repository itself. Return a dictionary of repository objects
83 repository itself. Return a dictionary of repository objects
84
84
85 :param repos_path: path to directory containing repositories
85 :param repos_path: path to directory containing repositories
86 :param baseui
86 :param baseui
87 """
87 """
88 log.info('scanning for repositories in %s', repos_path)
88 log.info('scanning for repositories in %s', repos_path)
89
89
90 if not isinstance(baseui, ui.ui):
90 if not isinstance(baseui, ui.ui):
91 baseui = make_ui('db')
91 baseui = make_ui('db')
92 repos_list = {}
92 repos_list = {}
93
93
94 for name, path in get_repos(repos_path):
94 for name, path in get_repos(repos_path):
95 try:
95 try:
96 if repos_list.has_key(name):
96 if repos_list.has_key(name):
97 raise RepositoryError('Duplicate repository name %s '
97 raise RepositoryError('Duplicate repository name %s '
98 'found in %s' % (name, path))
98 'found in %s' % (name, path))
99 else:
99 else:
100
100
101 klass = get_backend(path[0])
101 klass = get_backend(path[0])
102
102
103 if path[0] == 'hg' and path[0] in BACKENDS.keys():
103 if path[0] == 'hg' and path[0] in BACKENDS.keys():
104 repos_list[name] = klass(path[1], baseui=baseui)
104 repos_list[name] = klass(path[1], baseui=baseui)
105
105
106 if path[0] == 'git' and path[0] in BACKENDS.keys():
106 if path[0] == 'git' and path[0] in BACKENDS.keys():
107 repos_list[name] = klass(path[1])
107 repos_list[name] = klass(path[1])
108 except OSError:
108 except OSError:
109 continue
109 continue
110
110
111 return repos_list
111 return repos_list
112
112
113 def get_repos(self, all_repos=None):
113 def get_repos(self, all_repos=None):
114 """
114 """
115 Get all repos from db and for each repo create it's backend instance.
115 Get all repos from db and for each repo create it's backend instance.
116 and fill that backed with information from database
116 and fill that backed with information from database
117
117
118 :param all_repos: give specific repositories list, good for filtering
118 :param all_repos: give specific repositories list, good for filtering
119 """
119 """
120 if not all_repos:
120 if all_repos is None:
121 all_repos = self.sa.query(Repository)\
121 all_repos = self.sa.query(Repository)\
122 .order_by(Repository.repo_name).all()
122 .order_by(Repository.repo_name).all()
123
123
124 invalidation_list = [str(x.cache_key) for x in \
124 invalidation_list = [str(x.cache_key) for x in \
125 self.sa.query(CacheInvalidation.cache_key)\
125 self.sa.query(CacheInvalidation.cache_key)\
126 .filter(CacheInvalidation.cache_active == False)\
126 .filter(CacheInvalidation.cache_active == False)\
127 .all()]
127 .all()]
128
128
129 for r in all_repos:
129 for r in all_repos:
130
130
131 repo = self.get(r.repo_name, invalidation_list)
131 repo = self.get(r.repo_name, invalidation_list)
132
132
133 if repo is not None:
133 if repo is not None:
134 last_change = repo.last_change
134 last_change = repo.last_change
135 tip = h.get_changeset_safe(repo, 'tip')
135 tip = h.get_changeset_safe(repo, 'tip')
136
136
137 tmp_d = {}
137 tmp_d = {}
138 tmp_d['name'] = repo.name
138 tmp_d['name'] = repo.name
139 tmp_d['name_sort'] = tmp_d['name'].lower()
139 tmp_d['name_sort'] = tmp_d['name'].lower()
140 tmp_d['description'] = repo.dbrepo.description
140 tmp_d['description'] = repo.dbrepo.description
141 tmp_d['description_sort'] = tmp_d['description']
141 tmp_d['description_sort'] = tmp_d['description']
142 tmp_d['last_change'] = last_change
142 tmp_d['last_change'] = last_change
143 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
143 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
144 tmp_d['tip'] = tip.raw_id
144 tmp_d['tip'] = tip.raw_id
145 tmp_d['tip_sort'] = tip.revision
145 tmp_d['tip_sort'] = tip.revision
146 tmp_d['rev'] = tip.revision
146 tmp_d['rev'] = tip.revision
147 tmp_d['contact'] = repo.dbrepo.user.full_contact
147 tmp_d['contact'] = repo.dbrepo.user.full_contact
148 tmp_d['contact_sort'] = tmp_d['contact']
148 tmp_d['contact_sort'] = tmp_d['contact']
149 tmp_d['repo_archives'] = list(repo._get_archives())
149 tmp_d['repo_archives'] = list(repo._get_archives())
150 tmp_d['last_msg'] = tip.message
150 tmp_d['last_msg'] = tip.message
151 tmp_d['repo'] = repo
151 tmp_d['repo'] = repo
152 yield tmp_d
152 yield tmp_d
153
153
154 def get_repo(self, repo_name):
154 def get_repo(self, repo_name):
155 return self.get(repo_name)
155 return self.get(repo_name)
156
156
157 def get(self, repo_name, invalidation_list=None):
157 def get(self, repo_name, invalidation_list=None):
158 """
158 """
159 Get's repository from given name, creates BackendInstance and
159 Get's repository from given name, creates BackendInstance and
160 propagates it's data from database with all additional information
160 propagates it's data from database with all additional information
161 :param repo_name:
161 :param repo_name:
162 """
162 """
163 if not HasRepoPermissionAny('repository.read', 'repository.write',
163 if not HasRepoPermissionAny('repository.read', 'repository.write',
164 'repository.admin')(repo_name, 'get repo check'):
164 'repository.admin')(repo_name, 'get repo check'):
165 return
165 return
166
166
167
167
168 @cache_region('long_term')
168 @cache_region('long_term')
169 def _get_repo(repo_name):
169 def _get_repo(repo_name):
170
170
171 repo_path = os.path.join(self.repos_path, repo_name)
171 repo_path = os.path.join(self.repos_path, repo_name)
172
172
173 try:
173 try:
174 alias = get_scm(repo_path)[0]
174 alias = get_scm(repo_path)[0]
175
175
176 log.debug('Creating instance of %s repository', alias)
176 log.debug('Creating instance of %s repository', alias)
177 backend = get_backend(alias)
177 backend = get_backend(alias)
178 except VCSError:
178 except VCSError:
179 log.error(traceback.format_exc())
179 log.error(traceback.format_exc())
180 return
180 return
181
181
182 if alias == 'hg':
182 if alias == 'hg':
183 from pylons import app_globals as g
183 from pylons import app_globals as g
184 repo = backend(repo_path, create=False, baseui=g.baseui)
184 repo = backend(repo_path, create=False, baseui=g.baseui)
185 #skip hidden web repository
185 #skip hidden web repository
186 if repo._get_hidden():
186 if repo._get_hidden():
187 return
187 return
188 else:
188 else:
189 repo = backend(repo_path, create=False)
189 repo = backend(repo_path, create=False)
190
190
191 dbrepo = self.sa.query(Repository)\
191 dbrepo = self.sa.query(Repository)\
192 .options(joinedload(Repository.fork))\
192 .options(joinedload(Repository.fork))\
193 .options(joinedload(Repository.user))\
193 .options(joinedload(Repository.user))\
194 .filter(Repository.repo_name == repo_name)\
194 .filter(Repository.repo_name == repo_name)\
195 .scalar()
195 .scalar()
196
196
197 make_transient(dbrepo)
197 make_transient(dbrepo)
198 if dbrepo.user:
198 if dbrepo.user:
199 make_transient(dbrepo.user)
199 make_transient(dbrepo.user)
200 if dbrepo.fork:
200 if dbrepo.fork:
201 make_transient(dbrepo.fork)
201 make_transient(dbrepo.fork)
202
202
203 repo.dbrepo = dbrepo
203 repo.dbrepo = dbrepo
204 return repo
204 return repo
205
205
206 pre_invalidate = True
206 pre_invalidate = True
207 if invalidation_list:
207 if invalidation_list:
208 pre_invalidate = repo_name in invalidation_list
208 pre_invalidate = repo_name in invalidation_list
209
209
210 if pre_invalidate:
210 if pre_invalidate:
211 invalidate = self._should_invalidate(repo_name)
211 invalidate = self._should_invalidate(repo_name)
212
212
213 if invalidate:
213 if invalidate:
214 log.info('invalidating cache for repository %s', repo_name)
214 log.info('invalidating cache for repository %s', repo_name)
215 region_invalidate(_get_repo, None, repo_name)
215 region_invalidate(_get_repo, None, repo_name)
216 self._mark_invalidated(invalidate)
216 self._mark_invalidated(invalidate)
217
217
218 return _get_repo(repo_name)
218 return _get_repo(repo_name)
219
219
220
220
221
221
222 def mark_for_invalidation(self, repo_name):
222 def mark_for_invalidation(self, repo_name):
223 """
223 """
224 Puts cache invalidation task into db for
224 Puts cache invalidation task into db for
225 further global cache invalidation
225 further global cache invalidation
226
226
227 :param repo_name: this repo that should invalidation take place
227 :param repo_name: this repo that should invalidation take place
228 """
228 """
229 log.debug('marking %s for invalidation', repo_name)
229 log.debug('marking %s for invalidation', repo_name)
230 cache = self.sa.query(CacheInvalidation)\
230 cache = self.sa.query(CacheInvalidation)\
231 .filter(CacheInvalidation.cache_key == repo_name).scalar()
231 .filter(CacheInvalidation.cache_key == repo_name).scalar()
232
232
233 if cache:
233 if cache:
234 #mark this cache as inactive
234 #mark this cache as inactive
235 cache.cache_active = False
235 cache.cache_active = False
236 else:
236 else:
237 log.debug('cache key not found in invalidation db -> creating one')
237 log.debug('cache key not found in invalidation db -> creating one')
238 cache = CacheInvalidation(repo_name)
238 cache = CacheInvalidation(repo_name)
239
239
240 try:
240 try:
241 self.sa.add(cache)
241 self.sa.add(cache)
242 self.sa.commit()
242 self.sa.commit()
243 except (DatabaseError,):
243 except (DatabaseError,):
244 log.error(traceback.format_exc())
244 log.error(traceback.format_exc())
245 self.sa.rollback()
245 self.sa.rollback()
246
246
247
247
248 def toggle_following_repo(self, follow_repo_id, user_id):
248 def toggle_following_repo(self, follow_repo_id, user_id):
249
249
250 f = self.sa.query(UserFollowing)\
250 f = self.sa.query(UserFollowing)\
251 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
251 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
252 .filter(UserFollowing.user_id == user_id).scalar()
252 .filter(UserFollowing.user_id == user_id).scalar()
253
253
254 if f is not None:
254 if f is not None:
255
255
256 try:
256 try:
257 self.sa.delete(f)
257 self.sa.delete(f)
258 self.sa.commit()
258 self.sa.commit()
259 action_logger(UserTemp(user_id),
259 action_logger(UserTemp(user_id),
260 'stopped_following_repo',
260 'stopped_following_repo',
261 RepoTemp(follow_repo_id))
261 RepoTemp(follow_repo_id))
262 return
262 return
263 except:
263 except:
264 log.error(traceback.format_exc())
264 log.error(traceback.format_exc())
265 self.sa.rollback()
265 self.sa.rollback()
266 raise
266 raise
267
267
268
268
269 try:
269 try:
270 f = UserFollowing()
270 f = UserFollowing()
271 f.user_id = user_id
271 f.user_id = user_id
272 f.follows_repo_id = follow_repo_id
272 f.follows_repo_id = follow_repo_id
273 self.sa.add(f)
273 self.sa.add(f)
274 self.sa.commit()
274 self.sa.commit()
275 action_logger(UserTemp(user_id),
275 action_logger(UserTemp(user_id),
276 'started_following_repo',
276 'started_following_repo',
277 RepoTemp(follow_repo_id))
277 RepoTemp(follow_repo_id))
278 except:
278 except:
279 log.error(traceback.format_exc())
279 log.error(traceback.format_exc())
280 self.sa.rollback()
280 self.sa.rollback()
281 raise
281 raise
282
282
283 def toggle_following_user(self, follow_user_id , user_id):
283 def toggle_following_user(self, follow_user_id , user_id):
284 f = self.sa.query(UserFollowing)\
284 f = self.sa.query(UserFollowing)\
285 .filter(UserFollowing.follows_user_id == follow_user_id)\
285 .filter(UserFollowing.follows_user_id == follow_user_id)\
286 .filter(UserFollowing.user_id == user_id).scalar()
286 .filter(UserFollowing.user_id == user_id).scalar()
287
287
288 if f is not None:
288 if f is not None:
289 try:
289 try:
290 self.sa.delete(f)
290 self.sa.delete(f)
291 self.sa.commit()
291 self.sa.commit()
292 return
292 return
293 except:
293 except:
294 log.error(traceback.format_exc())
294 log.error(traceback.format_exc())
295 self.sa.rollback()
295 self.sa.rollback()
296 raise
296 raise
297
297
298 try:
298 try:
299 f = UserFollowing()
299 f = UserFollowing()
300 f.user_id = user_id
300 f.user_id = user_id
301 f.follows_user_id = follow_user_id
301 f.follows_user_id = follow_user_id
302 self.sa.add(f)
302 self.sa.add(f)
303 self.sa.commit()
303 self.sa.commit()
304 except:
304 except:
305 log.error(traceback.format_exc())
305 log.error(traceback.format_exc())
306 self.sa.rollback()
306 self.sa.rollback()
307 raise
307 raise
308
308
309 def is_following_repo(self, repo_name, user_id):
309 def is_following_repo(self, repo_name, user_id):
310 r = self.sa.query(Repository)\
310 r = self.sa.query(Repository)\
311 .filter(Repository.repo_name == repo_name).scalar()
311 .filter(Repository.repo_name == repo_name).scalar()
312
312
313 f = self.sa.query(UserFollowing)\
313 f = self.sa.query(UserFollowing)\
314 .filter(UserFollowing.follows_repository == r)\
314 .filter(UserFollowing.follows_repository == r)\
315 .filter(UserFollowing.user_id == user_id).scalar()
315 .filter(UserFollowing.user_id == user_id).scalar()
316
316
317 return f is not None
317 return f is not None
318
318
319 def is_following_user(self, username, user_id):
319 def is_following_user(self, username, user_id):
320 u = UserModel(self.sa).get_by_username(username)
320 u = UserModel(self.sa).get_by_username(username)
321
321
322 f = self.sa.query(UserFollowing)\
322 f = self.sa.query(UserFollowing)\
323 .filter(UserFollowing.follows_user == u)\
323 .filter(UserFollowing.follows_user == u)\
324 .filter(UserFollowing.user_id == user_id).scalar()
324 .filter(UserFollowing.user_id == user_id).scalar()
325
325
326 return f is not None
326 return f is not None
327
327
328 def get_followers(self, repo_id):
328 def get_followers(self, repo_id):
329 return self.sa.query(UserFollowing)\
329 return self.sa.query(UserFollowing)\
330 .filter(UserFollowing.follows_repo_id == repo_id).count()
330 .filter(UserFollowing.follows_repo_id == repo_id).count()
331
331
332 def get_forks(self, repo_id):
332 def get_forks(self, repo_id):
333 return self.sa.query(Repository)\
333 return self.sa.query(Repository)\
334 .filter(Repository.fork_id == repo_id).count()
334 .filter(Repository.fork_id == repo_id).count()
335
335
336 def _should_invalidate(self, repo_name):
336 def _should_invalidate(self, repo_name):
337 """
337 """
338 Looks up database for invalidation signals for this repo_name
338 Looks up database for invalidation signals for this repo_name
339 :param repo_name:
339 :param repo_name:
340 """
340 """
341
341
342 ret = self.sa.query(CacheInvalidation)\
342 ret = self.sa.query(CacheInvalidation)\
343 .options(FromCache('sql_cache_short',
343 .options(FromCache('sql_cache_short',
344 'get_invalidation_%s' % repo_name))\
344 'get_invalidation_%s' % repo_name))\
345 .filter(CacheInvalidation.cache_key == repo_name)\
345 .filter(CacheInvalidation.cache_key == repo_name)\
346 .filter(CacheInvalidation.cache_active == False)\
346 .filter(CacheInvalidation.cache_active == False)\
347 .scalar()
347 .scalar()
348
348
349 return ret
349 return ret
350
350
351 def _mark_invalidated(self, cache_key):
351 def _mark_invalidated(self, cache_key):
352 """
352 """
353 Marks all occurences of cache to invaldation as already invalidated
353 Marks all occurences of cache to invaldation as already invalidated
354 :param repo_name:
354 :param repo_name:
355 """
355 """
356 if cache_key:
356 if cache_key:
357 log.debug('marking %s as already invalidated', cache_key)
357 log.debug('marking %s as already invalidated', cache_key)
358 try:
358 try:
359 cache_key.cache_active = True
359 cache_key.cache_active = True
360 self.sa.add(cache_key)
360 self.sa.add(cache_key)
361 self.sa.commit()
361 self.sa.commit()
362 except (DatabaseError,):
362 except (DatabaseError,):
363 log.error(traceback.format_exc())
363 log.error(traceback.format_exc())
364 self.sa.rollback()
364 self.sa.rollback()
365
365
General Comments 0
You need to be logged in to leave comments. Login now