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