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