##// END OF EJS Templates
fixed some issues with cache invalidation, and simplified invalidation codes
marcink -
r1428:e5467730 beta
parent child Browse files
Show More
@@ -1,800 +1,799 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.db
4 4 ~~~~~~~~~~~~~~~~~~
5 5
6 6 Database Models for RhodeCode
7 7
8 8 :created_on: Apr 08, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 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 modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
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, see <http://www.gnu.org/licenses/>.
25 25
26 26 import os
27 27 import logging
28 28 import datetime
29 29 import traceback
30 30 from datetime import date
31 31
32 32 from sqlalchemy import *
33 33 from sqlalchemy.exc import DatabaseError
34 34 from sqlalchemy.orm import relationship, backref, joinedload, class_mapper
35 35 from sqlalchemy.orm.interfaces import MapperExtension
36 36
37 37 from beaker.cache import cache_region, region_invalidate
38 38
39 39 from vcs import get_backend
40 40 from vcs.utils.helpers import get_scm
41 41 from vcs.exceptions import RepositoryError, VCSError
42 42 from vcs.utils.lazy import LazyProperty
43 43 from vcs.nodes import FileNode
44 44
45 45 from rhodecode.lib import str2bool, json, safe_str
46 46 from rhodecode.model.meta import Base, Session
47 47 from rhodecode.model.caching_query import FromCache
48 48
49 49 log = logging.getLogger(__name__)
50 50
51 51 #==============================================================================
52 52 # BASE CLASSES
53 53 #==============================================================================
54 54
55 55 class ModelSerializer(json.JSONEncoder):
56 56 """
57 57 Simple Serializer for JSON,
58 58
59 59 usage::
60 60
61 61 to make object customized for serialization implement a __json__
62 62 method that will return a dict for serialization into json
63 63
64 64 example::
65 65
66 66 class Task(object):
67 67
68 68 def __init__(self, name, value):
69 69 self.name = name
70 70 self.value = value
71 71
72 72 def __json__(self):
73 73 return dict(name=self.name,
74 74 value=self.value)
75 75
76 76 """
77 77
78 78 def default(self, obj):
79 79
80 80 if hasattr(obj, '__json__'):
81 81 return obj.__json__()
82 82 else:
83 83 return json.JSONEncoder.default(self, obj)
84 84
85 85 class BaseModel(object):
86 86 """Base Model for all classess
87 87
88 88 """
89 89
90 90 @classmethod
91 91 def _get_keys(cls):
92 92 """return column names for this model """
93 93 return class_mapper(cls).c.keys()
94 94
95 95 def get_dict(self):
96 96 """return dict with keys and values corresponding
97 97 to this model data """
98 98
99 99 d = {}
100 100 for k in self._get_keys():
101 101 d[k] = getattr(self, k)
102 102 return d
103 103
104 104 def get_appstruct(self):
105 105 """return list with keys and values tupples corresponding
106 106 to this model data """
107 107
108 108 l = []
109 109 for k in self._get_keys():
110 110 l.append((k, getattr(self, k),))
111 111 return l
112 112
113 113 def populate_obj(self, populate_dict):
114 114 """populate model with data from given populate_dict"""
115 115
116 116 for k in self._get_keys():
117 117 if k in populate_dict:
118 118 setattr(self, k, populate_dict[k])
119 119
120 120 @classmethod
121 121 def query(cls):
122 122 return Session.query(cls)
123 123
124 124 @classmethod
125 125 def get(cls, id_):
126 126 return Session.query(cls).get(id_)
127 127
128 128
129 129 class RhodeCodeSettings(Base, BaseModel):
130 130 __tablename__ = 'rhodecode_settings'
131 131 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
132 132 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
133 133 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
134 134 app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
135 135
136 136 def __init__(self, k='', v=''):
137 137 self.app_settings_name = k
138 138 self.app_settings_value = v
139 139
140 140 def __repr__(self):
141 141 return "<%s('%s:%s')>" % (self.__class__.__name__,
142 142 self.app_settings_name, self.app_settings_value)
143 143
144 144
145 145 @classmethod
146 146 def get_by_name(cls, ldap_key):
147 147 return Session.query(cls)\
148 148 .filter(cls.app_settings_name == ldap_key).scalar()
149 149
150 150 @classmethod
151 151 def get_app_settings(cls, cache=False):
152 152
153 153 ret = Session.query(cls)
154 154
155 155 if cache:
156 156 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
157 157
158 158 if not ret:
159 159 raise Exception('Could not get application settings !')
160 160 settings = {}
161 161 for each in ret:
162 162 settings['rhodecode_' + each.app_settings_name] = \
163 163 each.app_settings_value
164 164
165 165 return settings
166 166
167 167 @classmethod
168 168 def get_ldap_settings(cls, cache=False):
169 169 ret = Session.query(cls)\
170 170 .filter(cls.app_settings_name.startswith('ldap_'))\
171 171 .all()
172 172 fd = {}
173 173 for row in ret:
174 174 fd.update({row.app_settings_name:row.app_settings_value})
175 175
176 176 fd.update({'ldap_active':str2bool(fd.get('ldap_active'))})
177 177
178 178 return fd
179 179
180 180
181 181 class RhodeCodeUi(Base, BaseModel):
182 182 __tablename__ = 'rhodecode_ui'
183 183 __table_args__ = {'extend_existing':True}
184 184 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
185 185 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
186 186 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
187 187 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
188 188 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
189 189
190 190
191 191 @classmethod
192 192 def get_by_key(cls, key):
193 193 return Session.query(cls).filter(cls.ui_key == key)
194 194
195 195
196 196 class User(Base, BaseModel):
197 197 __tablename__ = 'users'
198 198 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
199 199 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
200 200 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
201 201 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
202 202 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
203 203 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
204 204 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
205 205 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
206 206 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
207 207 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
208 208 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
209 209 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
210 210
211 211 user_log = relationship('UserLog', cascade='all')
212 212 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
213 213
214 214 repositories = relationship('Repository')
215 215 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
216 216 repo_to_perm = relationship('RepoToPerm', primaryjoin='RepoToPerm.user_id==User.user_id', cascade='all')
217 217
218 218 group_member = relationship('UsersGroupMember', cascade='all')
219 219
220 220 @property
221 221 def full_contact(self):
222 222 return '%s %s <%s>' % (self.name, self.lastname, self.email)
223 223
224 224 @property
225 225 def short_contact(self):
226 226 return '%s %s' % (self.name, self.lastname)
227 227
228 228 @property
229 229 def is_admin(self):
230 230 return self.admin
231 231
232 232 def __repr__(self):
233 233 try:
234 234 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
235 235 self.user_id, self.username)
236 236 except:
237 237 return self.__class__.__name__
238 238
239 239 @classmethod
240 240 def by_username(cls, username, case_insensitive=False):
241 241 if case_insensitive:
242 242 return Session.query(cls).filter(cls.username.like(username)).one()
243 243 else:
244 244 return Session.query(cls).filter(cls.username == username).one()
245 245
246 246 @classmethod
247 247 def get_by_api_key(cls, api_key):
248 248 return Session.query(cls).filter(cls.api_key == api_key).one()
249 249
250 250
251 251 def update_lastlogin(self):
252 252 """Update user lastlogin"""
253 253
254 254 self.last_login = datetime.datetime.now()
255 255 Session.add(self)
256 256 Session.commit()
257 257 log.debug('updated user %s lastlogin', self.username)
258 258
259 259
260 260 class UserLog(Base, BaseModel):
261 261 __tablename__ = 'user_logs'
262 262 __table_args__ = {'extend_existing':True}
263 263 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
264 264 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
265 265 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
266 266 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
267 267 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 268 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 269 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
270 270
271 271 @property
272 272 def action_as_day(self):
273 273 return date(*self.action_date.timetuple()[:3])
274 274
275 275 user = relationship('User')
276 276 repository = relationship('Repository')
277 277
278 278
279 279 class UsersGroup(Base, BaseModel):
280 280 __tablename__ = 'users_groups'
281 281 __table_args__ = {'extend_existing':True}
282 282
283 283 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
284 284 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
285 285 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
286 286
287 287 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
288 288
289 289
290 290 @classmethod
291 291 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
292 292 if case_insensitive:
293 293 gr = Session.query(cls)\
294 294 .filter(cls.users_group_name.ilike(group_name))
295 295 else:
296 296 gr = Session.query(UsersGroup)\
297 297 .filter(UsersGroup.users_group_name == group_name)
298 298 if cache:
299 299 gr = gr.options(FromCache("sql_cache_short",
300 300 "get_user_%s" % group_name))
301 301 return gr.scalar()
302 302
303 303 class UsersGroupMember(Base, BaseModel):
304 304 __tablename__ = 'users_groups_members'
305 305 __table_args__ = {'extend_existing':True}
306 306
307 307 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
308 308 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
309 309 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
310 310
311 311 user = relationship('User', lazy='joined')
312 312 users_group = relationship('UsersGroup')
313 313
314 314 def __init__(self, gr_id='', u_id=''):
315 315 self.users_group_id = gr_id
316 316 self.user_id = u_id
317 317
318 318 class Repository(Base, BaseModel):
319 319 __tablename__ = 'repositories'
320 320 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
321 321
322 322 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
323 323 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
324 324 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
325 325 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
326 326 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
327 327 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
328 328 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
329 329 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
330 330 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 331 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
332 332
333 333 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
334 334 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
335 335
336 336
337 337 user = relationship('User')
338 338 fork = relationship('Repository', remote_side=repo_id)
339 339 group = relationship('Group')
340 340 repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id')
341 341 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
342 342 stats = relationship('Statistics', cascade='all', uselist=False)
343 343
344 344 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
345 345
346 346 logs = relationship('UserLog', cascade='all')
347 347
348 348 def __repr__(self):
349 349 return "<%s('%s:%s')>" % (self.__class__.__name__,
350 350 self.repo_id, self.repo_name)
351 351
352 352 @classmethod
353 353 def by_repo_name(cls, repo_name):
354 354 q = Session.query(cls).filter(cls.repo_name == repo_name)
355 355
356 356 q = q.options(joinedload(Repository.fork))\
357 357 .options(joinedload(Repository.user))\
358 358 .options(joinedload(Repository.group))\
359 359
360 360 return q.one()
361 361
362 362 @classmethod
363 363 def get_repo_forks(cls, repo_id):
364 364 return Session.query(cls).filter(Repository.fork_id == repo_id)
365 365
366 366 @property
367 367 def just_name(self):
368 368 return self.repo_name.split(os.sep)[-1]
369 369
370 370 @property
371 371 def groups_with_parents(self):
372 372 groups = []
373 373 if self.group is None:
374 374 return groups
375 375
376 376 cur_gr = self.group
377 377 groups.insert(0, cur_gr)
378 378 while 1:
379 379 gr = getattr(cur_gr, 'parent_group', None)
380 380 cur_gr = cur_gr.parent_group
381 381 if gr is None:
382 382 break
383 383 groups.insert(0, gr)
384 384
385 385 return groups
386 386
387 387 @property
388 388 def groups_and_repo(self):
389 389 return self.groups_with_parents, self.just_name
390 390
391 391 @LazyProperty
392 392 def repo_path(self):
393 393 """
394 394 Returns base full path for that repository means where it actually
395 395 exists on a filesystem
396 396 """
397 397 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
398 398 q.options(FromCache("sql_cache_short", "repository_repo_path"))
399 399 return q.one().ui_value
400 400
401 401 @property
402 402 def repo_full_path(self):
403 403 p = [self.repo_path]
404 404 # we need to split the name by / since this is how we store the
405 405 # names in the database, but that eventually needs to be converted
406 406 # into a valid system path
407 407 p += self.repo_name.split('/')
408 408 return os.path.join(*p)
409 409
410 410 @property
411 411 def _ui(self):
412 412 """
413 413 Creates an db based ui object for this repository
414 414 """
415 415 from mercurial import ui
416 416 from mercurial import config
417 417 baseui = ui.ui()
418 418
419 419 #clean the baseui object
420 420 baseui._ocfg = config.config()
421 421 baseui._ucfg = config.config()
422 422 baseui._tcfg = config.config()
423 423
424 424
425 425 ret = Session.query(RhodeCodeUi)\
426 426 .options(FromCache("sql_cache_short",
427 427 "repository_repo_ui")).all()
428 428
429 429 hg_ui = ret
430 430 for ui_ in hg_ui:
431 431 if ui_.ui_active:
432 432 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
433 433 ui_.ui_key, ui_.ui_value)
434 434 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
435 435
436 436 return baseui
437 437
438 438 #==========================================================================
439 439 # SCM CACHE INSTANCE
440 440 #==========================================================================
441 441
442 442 @property
443 443 def invalidate(self):
444 444 """
445 445 Returns Invalidation object if this repo should be invalidated
446 446 None otherwise. `cache_active = False` means that this cache
447 447 state is not valid and needs to be invalidated
448 448 """
449 449 return Session.query(CacheInvalidation)\
450 450 .filter(CacheInvalidation.cache_key == self.repo_name)\
451 451 .filter(CacheInvalidation.cache_active == False)\
452 452 .scalar()
453 453
454 @property
455 454 def set_invalidate(self):
456 455 """
457 456 set a cache for invalidation for this instance
458 457 """
459 458 inv = Session.query(CacheInvalidation)\
460 459 .filter(CacheInvalidation.cache_key == self.repo_name)\
461 460 .scalar()
462 461
463 462 if inv is None:
464 463 inv = CacheInvalidation(self.repo_name)
465 464 inv.cache_active = True
466 465 Session.add(inv)
467 466 Session.commit()
468 467
469 468 @property
470 469 def scm_instance(self):
471 470 return self.__get_instance()
472 471
473 472 @property
474 473 def scm_instance_cached(self):
475 474 @cache_region('long_term')
476 475 def _c(repo_name):
477 476 return self.__get_instance()
478 477
478 # TODO: remove this trick when beaker 1.6 is released
479 # and have fixed this issue with not supporting unicode keys
480 rn = safe_str(self.repo_name)
481
479 482 inv = self.invalidate
480 483 if inv is not None:
481 region_invalidate(_c, None, self.repo_name)
482 #update our cache
484 region_invalidate(_c, None, rn)
485 # update our cache
483 486 inv.cache_active = True
484 487 Session.add(inv)
485 488 Session.commit()
486 489
487 # TODO: remove this trick when beaker 1.6 is released
488 # and have fixed this issue
489 rn = safe_str(self.repo_name)
490
491 490 return _c(rn)
492 491
493 492 def __get_instance(self):
494 493
495 494 repo_full_path = self.repo_full_path
496 495
497 496 try:
498 497 alias = get_scm(repo_full_path)[0]
499 498 log.debug('Creating instance of %s repository', alias)
500 499 backend = get_backend(alias)
501 500 except VCSError:
502 501 log.error(traceback.format_exc())
503 502 log.error('Perhaps this repository is in db and not in '
504 503 'filesystem run rescan repositories with '
505 504 '"destroy old data " option from admin panel')
506 505 return
507 506
508 507 if alias == 'hg':
509 508
510 509 repo = backend(safe_str(repo_full_path), create=False,
511 510 baseui=self._ui)
512 511 #skip hidden web repository
513 512 if repo._get_hidden():
514 513 return
515 514 else:
516 515 repo = backend(repo_full_path, create=False)
517 516
518 517 return repo
519 518
520 519
521 520 class Group(Base, BaseModel):
522 521 __tablename__ = 'groups'
523 522 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
524 523 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
525 524 __mapper_args__ = {'order_by':'group_name'}
526 525
527 526 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
528 527 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
529 528 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
530 529 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
531 530
532 531 parent_group = relationship('Group', remote_side=group_id)
533 532
534 533
535 534 def __init__(self, group_name='', parent_group=None):
536 535 self.group_name = group_name
537 536 self.parent_group = parent_group
538 537
539 538 def __repr__(self):
540 539 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
541 540 self.group_name)
542 541
543 542 @classmethod
544 543 def url_sep(cls):
545 544 return '/'
546 545
547 546 @property
548 547 def parents(self):
549 548 parents_recursion_limit = 5
550 549 groups = []
551 550 if self.parent_group is None:
552 551 return groups
553 552 cur_gr = self.parent_group
554 553 groups.insert(0, cur_gr)
555 554 cnt = 0
556 555 while 1:
557 556 cnt += 1
558 557 gr = getattr(cur_gr, 'parent_group', None)
559 558 cur_gr = cur_gr.parent_group
560 559 if gr is None:
561 560 break
562 561 if cnt == parents_recursion_limit:
563 562 # this will prevent accidental infinit loops
564 563 log.error('group nested more than %s' %
565 564 parents_recursion_limit)
566 565 break
567 566
568 567 groups.insert(0, gr)
569 568 return groups
570 569
571 570 @property
572 571 def children(self):
573 572 return Session.query(Group).filter(Group.parent_group == self)
574 573
575 574 @property
576 575 def full_path(self):
577 576 return Group.url_sep().join([g.group_name for g in self.parents] +
578 577 [self.group_name])
579 578
580 579 @property
581 580 def repositories(self):
582 581 return Session.query(Repository).filter(Repository.group == self)
583 582
584 583 @property
585 584 def repositories_recursive_count(self):
586 585 cnt = self.repositories.count()
587 586
588 587 def children_count(group):
589 588 cnt = 0
590 589 for child in group.children:
591 590 cnt += child.repositories.count()
592 591 cnt += children_count(child)
593 592 return cnt
594 593
595 594 return cnt + children_count(self)
596 595
597 596 class Permission(Base, BaseModel):
598 597 __tablename__ = 'permissions'
599 598 __table_args__ = {'extend_existing':True}
600 599 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
601 600 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
602 601 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
603 602
604 603 def __repr__(self):
605 604 return "<%s('%s:%s')>" % (self.__class__.__name__,
606 605 self.permission_id, self.permission_name)
607 606
608 607 @classmethod
609 608 def get_by_key(cls, key):
610 609 return Session.query(cls).filter(cls.permission_name == key).scalar()
611 610
612 611 class RepoToPerm(Base, BaseModel):
613 612 __tablename__ = 'repo_to_perm'
614 613 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
615 614 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
616 615 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
617 616 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
618 617 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
619 618
620 619 user = relationship('User')
621 620 permission = relationship('Permission')
622 621 repository = relationship('Repository')
623 622
624 623 class UserToPerm(Base, BaseModel):
625 624 __tablename__ = 'user_to_perm'
626 625 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
627 626 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
628 627 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
629 628 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
630 629
631 630 user = relationship('User')
632 631 permission = relationship('Permission')
633 632
634 633 @classmethod
635 634 def has_perm(cls, user_id, perm):
636 635 if not isinstance(perm, Permission):
637 636 raise Exception('perm needs to be an instance of Permission class')
638 637
639 638 return Session.query(cls).filter(cls.user_id == user_id)\
640 639 .filter(cls.permission == perm).scalar() is not None
641 640
642 641 @classmethod
643 642 def grant_perm(cls, user_id, perm):
644 643 if not isinstance(perm, Permission):
645 644 raise Exception('perm needs to be an instance of Permission class')
646 645
647 646 new = cls()
648 647 new.user_id = user_id
649 648 new.permission = perm
650 649 try:
651 650 Session.add(new)
652 651 Session.commit()
653 652 except:
654 653 Session.rollback()
655 654
656 655
657 656 @classmethod
658 657 def revoke_perm(cls, user_id, perm):
659 658 if not isinstance(perm, Permission):
660 659 raise Exception('perm needs to be an instance of Permission class')
661 660
662 661 try:
663 662 Session.query(cls).filter(cls.user_id == user_id)\
664 663 .filter(cls.permission == perm).delete()
665 664 Session.commit()
666 665 except:
667 666 Session.rollback()
668 667
669 668 class UsersGroupRepoToPerm(Base, BaseModel):
670 669 __tablename__ = 'users_group_repo_to_perm'
671 670 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
672 671 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
673 672 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
674 673 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
675 674 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
676 675
677 676 users_group = relationship('UsersGroup')
678 677 permission = relationship('Permission')
679 678 repository = relationship('Repository')
680 679
681 680
682 681 class UsersGroupToPerm(Base, BaseModel):
683 682 __tablename__ = 'users_group_to_perm'
684 683 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
685 684 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
686 685 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
687 686
688 687 users_group = relationship('UsersGroup')
689 688 permission = relationship('Permission')
690 689
691 690
692 691 @classmethod
693 692 def has_perm(cls, users_group_id, perm):
694 693 if not isinstance(perm, Permission):
695 694 raise Exception('perm needs to be an instance of Permission class')
696 695
697 696 return Session.query(cls).filter(cls.users_group_id ==
698 697 users_group_id)\
699 698 .filter(cls.permission == perm)\
700 699 .scalar() is not None
701 700
702 701 @classmethod
703 702 def grant_perm(cls, users_group_id, perm):
704 703 if not isinstance(perm, Permission):
705 704 raise Exception('perm needs to be an instance of Permission class')
706 705
707 706 new = cls()
708 707 new.users_group_id = users_group_id
709 708 new.permission = perm
710 709 try:
711 710 Session.add(new)
712 711 Session.commit()
713 712 except:
714 713 Session.rollback()
715 714
716 715
717 716 @classmethod
718 717 def revoke_perm(cls, users_group_id, perm):
719 718 if not isinstance(perm, Permission):
720 719 raise Exception('perm needs to be an instance of Permission class')
721 720
722 721 try:
723 722 Session.query(cls).filter(cls.users_group_id == users_group_id)\
724 723 .filter(cls.permission == perm).delete()
725 724 Session.commit()
726 725 except:
727 726 Session.rollback()
728 727
729 728
730 729 class GroupToPerm(Base, BaseModel):
731 730 __tablename__ = 'group_to_perm'
732 731 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
733 732
734 733 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
735 734 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
736 735 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
737 736 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
738 737
739 738 user = relationship('User')
740 739 permission = relationship('Permission')
741 740 group = relationship('Group')
742 741
743 742 class Statistics(Base, BaseModel):
744 743 __tablename__ = 'statistics'
745 744 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
746 745 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
747 746 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
748 747 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
749 748 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
750 749 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
751 750 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
752 751
753 752 repository = relationship('Repository', single_parent=True)
754 753
755 754 class UserFollowing(Base, BaseModel):
756 755 __tablename__ = 'user_followings'
757 756 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
758 757 UniqueConstraint('user_id', 'follows_user_id')
759 758 , {'extend_existing':True})
760 759
761 760 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
762 761 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
763 762 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
764 763 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
765 764 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
766 765
767 766 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
768 767
769 768 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
770 769 follows_repository = relationship('Repository', order_by='Repository.repo_name')
771 770
772 771
773 772 @classmethod
774 773 def get_repo_followers(cls, repo_id):
775 774 return Session.query(cls).filter(cls.follows_repo_id == repo_id)
776 775
777 776 class CacheInvalidation(Base, BaseModel):
778 777 __tablename__ = 'cache_invalidation'
779 778 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
780 779 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
781 780 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
782 781 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
783 782 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
784 783
785 784
786 785 def __init__(self, cache_key, cache_args=''):
787 786 self.cache_key = cache_key
788 787 self.cache_args = cache_args
789 788 self.cache_active = False
790 789
791 790 def __repr__(self):
792 791 return "<%s('%s:%s')>" % (self.__class__.__name__,
793 792 self.cache_id, self.cache_key)
794 793
795 794 class DbMigrateVersion(Base, BaseModel):
796 795 __tablename__ = 'db_migrate_version'
797 796 __table_args__ = {'extend_existing':True}
798 797 repository_id = Column('repository_id', String(250), primary_key=True)
799 798 repository_path = Column('repository_path', Text)
800 799 version = Column('version', Integer)
@@ -1,413 +1,374 b''
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-2011 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 modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
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, see <http://www.gnu.org/licenses/>.
25 25 import os
26 26 import time
27 27 import traceback
28 28 import logging
29 29
30 30 from sqlalchemy.exc import DatabaseError
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 from vcs.nodes import FileNode
37 37
38 38 from rhodecode import BACKENDS
39 39 from rhodecode.lib import helpers as h
40 40 from rhodecode.lib import safe_str
41 41 from rhodecode.lib.auth import HasRepoPermissionAny
42 42 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
43 43 action_logger
44 44 from rhodecode.model import BaseModel
45 45 from rhodecode.model.user import UserModel
46 46 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
47 47 UserFollowing, UserLog
48 48
49 49 log = logging.getLogger(__name__)
50 50
51 51
52 52 class UserTemp(object):
53 53 def __init__(self, user_id):
54 54 self.user_id = user_id
55 55
56 56 def __repr__(self):
57 57 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
58 58
59 59
60 60 class RepoTemp(object):
61 61 def __init__(self, repo_id):
62 62 self.repo_id = repo_id
63 63
64 64 def __repr__(self):
65 65 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
66 66
67 67 class CachedRepoList(object):
68 68
69 def __init__(self, db_repo_list, invalidation_list, repos_path,
70 order_by=None):
69 def __init__(self, db_repo_list, repos_path, order_by=None):
71 70 self.db_repo_list = db_repo_list
72 self.invalidation_list = invalidation_list
73 71 self.repos_path = repos_path
74 72 self.order_by = order_by
75 73 self.reversed = (order_by or '').startswith('-')
76 74
77 75 def __len__(self):
78 76 return len(self.db_repo_list)
79 77
80 78 def __repr__(self):
81 79 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
82 80
83 81 def __iter__(self):
84 for db_repo in self.db_repo_list:
85 dbr = db_repo
86
87 # invalidate the repo cache if needed before getting the
88 # scm instance
82 for dbr in self.db_repo_list:
89 83
90 scm_invalidate = False
91 if self.invalidation_list is not None:
92 scm_invalidate = dbr.repo_name in self.invalidation_list
93
94 if scm_invalidate:
95 log.info('invalidating cache for repository %s',
96 dbr.repo_name)
97 db_repo.set_invalidate
98
99 scmr = db_repo.scm_instance_cached
84 scmr = dbr.scm_instance_cached
100 85
101 86 #check permission at this level
102 87 if not HasRepoPermissionAny('repository.read',
103 88 'repository.write',
104 89 'repository.admin')(dbr.repo_name,
105 90 'get repo check'):
106 91 continue
107 92
108 93
109 94 if scmr is None:
110 95 log.error('%s this repository is present in database but it '
111 96 'cannot be created as an scm instance',
112 97 dbr.repo_name)
113 98 continue
114 99
115 100
116 101 last_change = scmr.last_change
117 102 tip = h.get_changeset_safe(scmr, 'tip')
118 103
119 104 tmp_d = {}
120 105 tmp_d['name'] = dbr.repo_name
121 106 tmp_d['name_sort'] = tmp_d['name'].lower()
122 107 tmp_d['description'] = dbr.description
123 108 tmp_d['description_sort'] = tmp_d['description']
124 109 tmp_d['last_change'] = last_change
125 110 tmp_d['last_change_sort'] = time.mktime(last_change \
126 111 .timetuple())
127 112 tmp_d['tip'] = tip.raw_id
128 113 tmp_d['tip_sort'] = tip.revision
129 114 tmp_d['rev'] = tip.revision
130 115 tmp_d['contact'] = dbr.user.full_contact
131 116 tmp_d['contact_sort'] = tmp_d['contact']
132 117 tmp_d['owner_sort'] = tmp_d['contact']
133 118 tmp_d['repo_archives'] = list(scmr._get_archives())
134 119 tmp_d['last_msg'] = tip.message
135 120 tmp_d['repo'] = scmr
136 121 tmp_d['dbrepo'] = dbr.get_dict()
137 122 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork \
138 123 else {}
139 124 yield tmp_d
140 125
141 126 class ScmModel(BaseModel):
142 127 """Generic Scm Model
143 128 """
144 129
145 130 @LazyProperty
146 131 def repos_path(self):
147 132 """Get's the repositories root path from database
148 133 """
149 134
150 135 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
151 136
152 137 return q.ui_value
153 138
154 139 def repo_scan(self, repos_path=None):
155 140 """Listing of repositories in given path. This path should not be a
156 141 repository itself. Return a dictionary of repository objects
157 142
158 143 :param repos_path: path to directory containing repositories
159 144 """
160 145
161 146 log.info('scanning for repositories in %s', repos_path)
162 147
163 148 if repos_path is None:
164 149 repos_path = self.repos_path
165 150
166 151 baseui = make_ui('db')
167 152 repos_list = {}
168 153
169 154 for name, path in get_filesystem_repos(repos_path, recursive=True):
170 155 try:
171 156 if name in repos_list:
172 157 raise RepositoryError('Duplicate repository name %s '
173 158 'found in %s' % (name, path))
174 159 else:
175 160
176 161 klass = get_backend(path[0])
177 162
178 163 if path[0] == 'hg' and path[0] in BACKENDS.keys():
179 164
180 165 # for mercurial we need to have an str path
181 166 repos_list[name] = klass(safe_str(path[1]),
182 167 baseui=baseui)
183 168
184 169 if path[0] == 'git' and path[0] in BACKENDS.keys():
185 170 repos_list[name] = klass(path[1])
186 171 except OSError:
187 172 continue
188 173
189 174 return repos_list
190 175
191 176 def get_repos(self, all_repos=None, sort_key=None):
192 177 """
193 178 Get all repos from db and for each repo create it's
194 179 backend instance and fill that backed with information from database
195 180
196 181 :param all_repos: list of repository names as strings
197 182 give specific repositories list, good for filtering
198 183 """
199 184 if all_repos is None:
200 185 all_repos = self.sa.query(Repository)\
201 186 .filter(Repository.group_id == None)\
202 187 .order_by(Repository.repo_name).all()
203 188
204 #get the repositories that should be invalidated
205 invalidation_list = [str(x.cache_key) for x in \
206 self.sa.query(CacheInvalidation.cache_key)\
207 .filter(CacheInvalidation.cache_active == False)\
208 .all()]
209
210 repo_iter = CachedRepoList(all_repos, invalidation_list,
211 repos_path=self.repos_path,
189 repo_iter = CachedRepoList(all_repos, repos_path=self.repos_path,
212 190 order_by=sort_key)
213 191
214 192 return repo_iter
215 193
216 194 def mark_for_invalidation(self, repo_name):
217 195 """Puts cache invalidation task into db for
218 196 further global cache invalidation
219 197
220 198 :param repo_name: this repo that should invalidation take place
221 199 """
222 200
223 201 log.debug('marking %s for invalidation', repo_name)
224 202 cache = self.sa.query(CacheInvalidation)\
225 203 .filter(CacheInvalidation.cache_key == repo_name).scalar()
226 204
227 205 if cache:
228 #mark this cache as inactive
206 # mark this cache as inactive
229 207 cache.cache_active = False
230 208 else:
231 209 log.debug('cache key not found in invalidation db -> creating one')
232 210 cache = CacheInvalidation(repo_name)
233 211
234 212 try:
235 213 self.sa.add(cache)
236 214 self.sa.commit()
237 215 except (DatabaseError,):
238 216 log.error(traceback.format_exc())
239 217 self.sa.rollback()
240 218
241 219 def toggle_following_repo(self, follow_repo_id, user_id):
242 220
243 221 f = self.sa.query(UserFollowing)\
244 222 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
245 223 .filter(UserFollowing.user_id == user_id).scalar()
246 224
247 225 if f is not None:
248 226
249 227 try:
250 228 self.sa.delete(f)
251 229 self.sa.commit()
252 230 action_logger(UserTemp(user_id),
253 231 'stopped_following_repo',
254 232 RepoTemp(follow_repo_id))
255 233 return
256 234 except:
257 235 log.error(traceback.format_exc())
258 236 self.sa.rollback()
259 237 raise
260 238
261 239 try:
262 240 f = UserFollowing()
263 241 f.user_id = user_id
264 242 f.follows_repo_id = follow_repo_id
265 243 self.sa.add(f)
266 244 self.sa.commit()
267 245 action_logger(UserTemp(user_id),
268 246 'started_following_repo',
269 247 RepoTemp(follow_repo_id))
270 248 except:
271 249 log.error(traceback.format_exc())
272 250 self.sa.rollback()
273 251 raise
274 252
275 253 def toggle_following_user(self, follow_user_id, user_id):
276 254 f = self.sa.query(UserFollowing)\
277 255 .filter(UserFollowing.follows_user_id == follow_user_id)\
278 256 .filter(UserFollowing.user_id == user_id).scalar()
279 257
280 258 if f is not None:
281 259 try:
282 260 self.sa.delete(f)
283 261 self.sa.commit()
284 262 return
285 263 except:
286 264 log.error(traceback.format_exc())
287 265 self.sa.rollback()
288 266 raise
289 267
290 268 try:
291 269 f = UserFollowing()
292 270 f.user_id = user_id
293 271 f.follows_user_id = follow_user_id
294 272 self.sa.add(f)
295 273 self.sa.commit()
296 274 except:
297 275 log.error(traceback.format_exc())
298 276 self.sa.rollback()
299 277 raise
300 278
301 279 def is_following_repo(self, repo_name, user_id, cache=False):
302 280 r = self.sa.query(Repository)\
303 281 .filter(Repository.repo_name == repo_name).scalar()
304 282
305 283 f = self.sa.query(UserFollowing)\
306 284 .filter(UserFollowing.follows_repository == r)\
307 285 .filter(UserFollowing.user_id == user_id).scalar()
308 286
309 287 return f is not None
310 288
311 289 def is_following_user(self, username, user_id, cache=False):
312 290 u = UserModel(self.sa).get_by_username(username)
313 291
314 292 f = self.sa.query(UserFollowing)\
315 293 .filter(UserFollowing.follows_user == u)\
316 294 .filter(UserFollowing.user_id == user_id).scalar()
317 295
318 296 return f is not None
319 297
320 298 def get_followers(self, repo_id):
321 299 if not isinstance(repo_id, int):
322 300 repo_id = getattr(Repository.by_repo_name(repo_id), 'repo_id')
323 301
324 302 return self.sa.query(UserFollowing)\
325 303 .filter(UserFollowing.follows_repo_id == repo_id).count()
326 304
327 305 def get_forks(self, repo_id):
328 306 if not isinstance(repo_id, int):
329 307 repo_id = getattr(Repository.by_repo_name(repo_id), 'repo_id')
330 308
331 309 return self.sa.query(Repository)\
332 310 .filter(Repository.fork_id == repo_id).count()
333 311
334 312 def pull_changes(self, repo_name, username):
335 313 dbrepo = Repository.by_repo_name(repo_name)
336 314 repo = dbrepo.scm_instance
337 315 try:
338 316 extras = {'ip': '',
339 317 'username': username,
340 318 'action': 'push_remote',
341 319 'repository': repo_name}
342 320
343 321 #inject ui extra param to log this action via push logger
344 322 for k, v in extras.items():
345 323 repo._repo.ui.setconfig('rhodecode_extras', k, v)
346 324
347 325 repo.pull(dbrepo.clone_uri)
348 326 self.mark_for_invalidation(repo_name)
349 327 except:
350 328 log.error(traceback.format_exc())
351 329 raise
352 330
353 331
354 332 def commit_change(self, repo, repo_name, cs, user, author, message, content,
355 333 f_path):
356 334
357 335 if repo.alias == 'hg':
358 336 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
359 337 elif repo.alias == 'git':
360 338 from vcs.backends.git import GitInMemoryChangeset as IMC
361 339
362 340 # decoding here will force that we have proper encoded values
363 341 # in any other case this will throw exceptions and deny commit
364 342 content = safe_str(content)
365 343 message = safe_str(message)
366 344 path = safe_str(f_path)
367 345 author = safe_str(author)
368 346 m = IMC(repo)
369 347 m.change(FileNode(path, content))
370 348 tip = m.commit(message=message,
371 349 author=author,
372 350 parents=[cs], branch=cs.branch)
373 351
374 352 new_cs = tip.short_id
375 353 action = 'push_local:%s' % new_cs
376 354
377 355 action_logger(user, action, repo_name)
378 356
379 357 self.mark_for_invalidation(repo_name)
380 358
381 359
382 360 def get_unread_journal(self):
383 361 return self.sa.query(UserLog).count()
384 362
385 363 def _should_invalidate(self, repo_name):
386 364 """Looks up database for invalidation signals for this repo_name
387 365
388 366 :param repo_name:
389 367 """
390 368
391 369 ret = self.sa.query(CacheInvalidation)\
392 370 .filter(CacheInvalidation.cache_key == repo_name)\
393 371 .filter(CacheInvalidation.cache_active == False)\
394 372 .scalar()
395 373
396 374 return ret
397
398 def _mark_invalidated(self, cache_key):
399 """ Marks all occurrences of cache to invalidation as already
400 invalidated
401
402 :param cache_key:
403 """
404
405 if cache_key:
406 log.debug('marking %s as already invalidated', cache_key)
407 try:
408 cache_key.cache_active = True
409 self.sa.add(cache_key)
410 self.sa.commit()
411 except (DatabaseError,):
412 log.error(traceback.format_exc())
413 self.sa.rollback()
General Comments 0
You need to be logged in to leave comments. Login now