##// END OF EJS Templates
typo fix + code garden
marcink -
r2356:2da7b598 beta
parent child Browse files
Show More
@@ -1,1338 +1,1339
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) 2010-2012 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 import hashlib
30 31 from collections import defaultdict
31 32
32 33 from sqlalchemy import *
33 34 from sqlalchemy.ext.hybrid import hybrid_property
34 35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
35 36 from beaker.cache import cache_region, region_invalidate
36 37
37 38 from rhodecode.lib.vcs import get_backend
38 39 from rhodecode.lib.vcs.utils.helpers import get_scm
39 40 from rhodecode.lib.vcs.exceptions import VCSError
40 41 from rhodecode.lib.vcs.utils.lazy import LazyProperty
41 42
42 43 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
43 44 safe_unicode
44 45 from rhodecode.lib.compat import json
45 46 from rhodecode.lib.caching_query import FromCache
47 from rhodecode.model.meta import Base, Session
46 48
47 from rhodecode.model.meta import Base, Session
48 import hashlib
49 49
50 50 URL_SEP = '/'
51 51 log = logging.getLogger(__name__)
52 52
53 53 #==============================================================================
54 54 # BASE CLASSES
55 55 #==============================================================================
56 56
57 57 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
58 58
59 59
60 60 class ModelSerializer(json.JSONEncoder):
61 61 """
62 62 Simple Serializer for JSON,
63 63
64 64 usage::
65 65
66 66 to make object customized for serialization implement a __json__
67 67 method that will return a dict for serialization into json
68 68
69 69 example::
70 70
71 71 class Task(object):
72 72
73 73 def __init__(self, name, value):
74 74 self.name = name
75 75 self.value = value
76 76
77 77 def __json__(self):
78 78 return dict(name=self.name,
79 79 value=self.value)
80 80
81 81 """
82 82
83 83 def default(self, obj):
84 84
85 85 if hasattr(obj, '__json__'):
86 86 return obj.__json__()
87 87 else:
88 88 return json.JSONEncoder.default(self, obj)
89 89
90 90
91 91 class BaseModel(object):
92 92 """
93 93 Base Model for all classess
94 94 """
95 95
96 96 @classmethod
97 97 def _get_keys(cls):
98 98 """return column names for this model """
99 99 return class_mapper(cls).c.keys()
100 100
101 101 def get_dict(self):
102 102 """
103 103 return dict with keys and values corresponding
104 104 to this model data """
105 105
106 106 d = {}
107 107 for k in self._get_keys():
108 108 d[k] = getattr(self, k)
109 109
110 110 # also use __json__() if present to get additional fields
111 111 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
112 112 d[k] = val
113 113 return d
114 114
115 115 def get_appstruct(self):
116 116 """return list with keys and values tupples corresponding
117 117 to this model data """
118 118
119 119 l = []
120 120 for k in self._get_keys():
121 121 l.append((k, getattr(self, k),))
122 122 return l
123 123
124 124 def populate_obj(self, populate_dict):
125 125 """populate model with data from given populate_dict"""
126 126
127 127 for k in self._get_keys():
128 128 if k in populate_dict:
129 129 setattr(self, k, populate_dict[k])
130 130
131 131 @classmethod
132 132 def query(cls):
133 133 return Session.query(cls)
134 134
135 135 @classmethod
136 136 def get(cls, id_):
137 137 if id_:
138 138 return cls.query().get(id_)
139 139
140 140 @classmethod
141 141 def getAll(cls):
142 142 return cls.query().all()
143 143
144 144 @classmethod
145 145 def delete(cls, id_):
146 146 obj = cls.query().get(id_)
147 147 Session.delete(obj)
148 148
149 149 def __repr__(self):
150 150 if hasattr(self, '__unicode__'):
151 151 # python repr needs to return str
152 152 return safe_str(self.__unicode__())
153 153 return '<DB:%s>' % (self.__class__.__name__)
154 154
155
155 156 class RhodeCodeSetting(Base, BaseModel):
156 157 __tablename__ = 'rhodecode_settings'
157 158 __table_args__ = (
158 159 UniqueConstraint('app_settings_name'),
159 {'extend_existing': True, 'mysql_engine':'InnoDB',
160 {'extend_existing': True, 'mysql_engine': 'InnoDB',
160 161 'mysql_charset': 'utf8'}
161 162 )
162 163 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
163 164 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
164 165 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
165 166
166 167 def __init__(self, k='', v=''):
167 168 self.app_settings_name = k
168 169 self.app_settings_value = v
169 170
170 171 @validates('_app_settings_value')
171 172 def validate_settings_value(self, key, val):
172 173 assert type(val) == unicode
173 174 return val
174 175
175 176 @hybrid_property
176 177 def app_settings_value(self):
177 178 v = self._app_settings_value
178 179 if self.app_settings_name == 'ldap_active':
179 180 v = str2bool(v)
180 181 return v
181 182
182 183 @app_settings_value.setter
183 184 def app_settings_value(self, val):
184 185 """
185 186 Setter that will always make sure we use unicode in app_settings_value
186 187
187 188 :param val:
188 189 """
189 190 self._app_settings_value = safe_unicode(val)
190 191
191 192 def __unicode__(self):
192 193 return u"<%s('%s:%s')>" % (
193 194 self.__class__.__name__,
194 195 self.app_settings_name, self.app_settings_value
195 196 )
196 197
197 198 @classmethod
198 199 def get_by_name(cls, ldap_key):
199 200 return cls.query()\
200 201 .filter(cls.app_settings_name == ldap_key).scalar()
201 202
202 203 @classmethod
203 204 def get_app_settings(cls, cache=False):
204 205
205 206 ret = cls.query()
206 207
207 208 if cache:
208 209 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
209 210
210 211 if not ret:
211 212 raise Exception('Could not get application settings !')
212 213 settings = {}
213 214 for each in ret:
214 215 settings['rhodecode_' + each.app_settings_name] = \
215 216 each.app_settings_value
216 217
217 218 return settings
218 219
219 220 @classmethod
220 221 def get_ldap_settings(cls, cache=False):
221 222 ret = cls.query()\
222 223 .filter(cls.app_settings_name.startswith('ldap_')).all()
223 224 fd = {}
224 225 for row in ret:
225 fd.update({row.app_settings_name:row.app_settings_value})
226 fd.update({row.app_settings_name: row.app_settings_value})
226 227
227 228 return fd
228 229
229 230
230 231 class RhodeCodeUi(Base, BaseModel):
231 232 __tablename__ = 'rhodecode_ui'
232 233 __table_args__ = (
233 234 UniqueConstraint('ui_key'),
234 {'extend_existing': True, 'mysql_engine':'InnoDB',
235 {'extend_existing': True, 'mysql_engine': 'InnoDB',
235 236 'mysql_charset': 'utf8'}
236 237 )
237 238
238 239 HOOK_UPDATE = 'changegroup.update'
239 240 HOOK_REPO_SIZE = 'changegroup.repo_size'
240 241 HOOK_PUSH = 'pretxnchangegroup.push_logger'
241 242 HOOK_PULL = 'preoutgoing.pull_logger'
242 243
243 244 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
244 245 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
245 246 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
246 247 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
247 248 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
248 249
249 250 @classmethod
250 251 def get_by_key(cls, key):
251 252 return cls.query().filter(cls.ui_key == key)
252 253
253 254 @classmethod
254 255 def get_builtin_hooks(cls):
255 256 q = cls.query()
256 257 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
257 258 cls.HOOK_REPO_SIZE,
258 259 cls.HOOK_PUSH, cls.HOOK_PULL]))
259 260 return q.all()
260 261
261 262 @classmethod
262 263 def get_custom_hooks(cls):
263 264 q = cls.query()
264 265 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
265 266 cls.HOOK_REPO_SIZE,
266 267 cls.HOOK_PUSH, cls.HOOK_PULL]))
267 268 q = q.filter(cls.ui_section == 'hooks')
268 269 return q.all()
269 270
270 271 @classmethod
271 272 def create_or_update_hook(cls, key, val):
272 273 new_ui = cls.get_by_key(key).scalar() or cls()
273 274 new_ui.ui_section = 'hooks'
274 275 new_ui.ui_active = True
275 276 new_ui.ui_key = key
276 277 new_ui.ui_value = val
277 278
278 279 Session.add(new_ui)
279 280
280 281
281 282 class User(Base, BaseModel):
282 283 __tablename__ = 'users'
283 284 __table_args__ = (
284 285 UniqueConstraint('username'), UniqueConstraint('email'),
285 {'extend_existing': True, 'mysql_engine':'InnoDB',
286 {'extend_existing': True, 'mysql_engine': 'InnoDB',
286 287 'mysql_charset': 'utf8'}
287 288 )
288 289 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
289 290 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
290 291 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
291 292 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
292 293 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
293 294 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
294 295 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
295 296 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
296 297 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
297 298 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
298 299 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
299 300
300 301 user_log = relationship('UserLog', cascade='all')
301 302 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
302 303
303 304 repositories = relationship('Repository')
304 305 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
305 306 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
306 307 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
307 308
308 309 group_member = relationship('UsersGroupMember', cascade='all')
309 310
310 311 notifications = relationship('UserNotification', cascade='all')
311 312 # notifications assigned to this user
312 313 user_created_notifications = relationship('Notification', cascade='all')
313 314 # comments created by this user
314 315 user_comments = relationship('ChangesetComment', cascade='all')
315 316
316 317 @hybrid_property
317 318 def email(self):
318 319 return self._email
319 320
320 321 @email.setter
321 322 def email(self, val):
322 323 self._email = val.lower() if val else None
323 324
324 325 @property
325 326 def full_name(self):
326 327 return '%s %s' % (self.name, self.lastname)
327 328
328 329 @property
329 330 def full_name_or_username(self):
330 331 return ('%s %s' % (self.name, self.lastname)
331 332 if (self.name and self.lastname) else self.username)
332 333
333 334 @property
334 335 def full_contact(self):
335 336 return '%s %s <%s>' % (self.name, self.lastname, self.email)
336 337
337 338 @property
338 339 def short_contact(self):
339 340 return '%s %s' % (self.name, self.lastname)
340 341
341 342 @property
342 343 def is_admin(self):
343 344 return self.admin
344 345
345 346 def __unicode__(self):
346 347 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
347 348 self.user_id, self.username)
348 349
349 350 @classmethod
350 351 def get_by_username(cls, username, case_insensitive=False, cache=False):
351 352 if case_insensitive:
352 353 q = cls.query().filter(cls.username.ilike(username))
353 354 else:
354 355 q = cls.query().filter(cls.username == username)
355 356
356 357 if cache:
357 358 q = q.options(FromCache(
358 359 "sql_cache_short",
359 360 "get_user_%s" % _hash_key(username)
360 361 )
361 362 )
362 363 return q.scalar()
363 364
364 365 @classmethod
365 366 def get_by_api_key(cls, api_key, cache=False):
366 367 q = cls.query().filter(cls.api_key == api_key)
367 368
368 369 if cache:
369 370 q = q.options(FromCache("sql_cache_short",
370 371 "get_api_key_%s" % api_key))
371 372 return q.scalar()
372 373
373 374 @classmethod
374 375 def get_by_email(cls, email, case_insensitive=False, cache=False):
375 376 if case_insensitive:
376 377 q = cls.query().filter(cls.email.ilike(email))
377 378 else:
378 379 q = cls.query().filter(cls.email == email)
379 380
380 381 if cache:
381 382 q = q.options(FromCache("sql_cache_short",
382 383 "get_api_key_%s" % email))
383 384 return q.scalar()
384 385
385 386 def update_lastlogin(self):
386 387 """Update user lastlogin"""
387 388 self.last_login = datetime.datetime.now()
388 389 Session.add(self)
389 390 log.debug('updated user %s lastlogin' % self.username)
390 391
391 392 def __json__(self):
392 393 return dict(
393 394 user_id=self.user_id,
394 395 first_name=self.name,
395 396 last_name=self.lastname,
396 397 email=self.email,
397 398 full_name=self.full_name,
398 399 full_name_or_username=self.full_name_or_username,
399 400 short_contact=self.short_contact,
400 401 full_contact=self.full_contact
401 402 )
402 403
403 404
404 405 class UserLog(Base, BaseModel):
405 406 __tablename__ = 'user_logs'
406 407 __table_args__ = (
407 {'extend_existing': True, 'mysql_engine':'InnoDB',
408 {'extend_existing': True, 'mysql_engine': 'InnoDB',
408 409 'mysql_charset': 'utf8'},
409 410 )
410 411 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
411 412 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
412 413 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
413 414 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
414 415 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
415 416 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
416 417 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
417 418
418 419 @property
419 420 def action_as_day(self):
420 421 return datetime.date(*self.action_date.timetuple()[:3])
421 422
422 423 user = relationship('User')
423 424 repository = relationship('Repository', cascade='')
424 425
425 426
426 427 class UsersGroup(Base, BaseModel):
427 428 __tablename__ = 'users_groups'
428 429 __table_args__ = (
429 {'extend_existing': True, 'mysql_engine':'InnoDB',
430 {'extend_existing': True, 'mysql_engine': 'InnoDB',
430 431 'mysql_charset': 'utf8'},
431 432 )
432 433
433 434 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
434 435 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
435 436 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
436 437
437 438 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
438 439 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
439 440 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
440 441
441 442 def __unicode__(self):
442 443 return u'<userGroup(%s)>' % (self.users_group_name)
443 444
444 445 @classmethod
445 446 def get_by_group_name(cls, group_name, cache=False,
446 447 case_insensitive=False):
447 448 if case_insensitive:
448 449 q = cls.query().filter(cls.users_group_name.ilike(group_name))
449 450 else:
450 451 q = cls.query().filter(cls.users_group_name == group_name)
451 452 if cache:
452 453 q = q.options(FromCache(
453 454 "sql_cache_short",
454 455 "get_user_%s" % _hash_key(group_name)
455 456 )
456 457 )
457 458 return q.scalar()
458 459
459 460 @classmethod
460 461 def get(cls, users_group_id, cache=False):
461 462 users_group = cls.query()
462 463 if cache:
463 464 users_group = users_group.options(FromCache("sql_cache_short",
464 465 "get_users_group_%s" % users_group_id))
465 466 return users_group.get(users_group_id)
466 467
467 468
468 469 class UsersGroupMember(Base, BaseModel):
469 470 __tablename__ = 'users_groups_members'
470 471 __table_args__ = (
471 {'extend_existing': True, 'mysql_engine':'InnoDB',
472 {'extend_existing': True, 'mysql_engine': 'InnoDB',
472 473 'mysql_charset': 'utf8'},
473 474 )
474 475
475 476 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
476 477 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
477 478 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
478 479
479 480 user = relationship('User', lazy='joined')
480 481 users_group = relationship('UsersGroup')
481 482
482 483 def __init__(self, gr_id='', u_id=''):
483 484 self.users_group_id = gr_id
484 485 self.user_id = u_id
485 486
486 487
487 488 class Repository(Base, BaseModel):
488 489 __tablename__ = 'repositories'
489 490 __table_args__ = (
490 491 UniqueConstraint('repo_name'),
491 {'extend_existing': True, 'mysql_engine':'InnoDB',
492 {'extend_existing': True, 'mysql_engine': 'InnoDB',
492 493 'mysql_charset': 'utf8'},
493 494 )
494 495
495 496 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
496 497 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
497 498 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
498 499 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
499 500 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
500 501 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
501 502 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
502 503 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
503 504 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
504 505 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
505 506
506 507 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
507 508 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
508 509
509 510 user = relationship('User')
510 511 fork = relationship('Repository', remote_side=repo_id)
511 512 group = relationship('RepoGroup')
512 513 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
513 514 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
514 515 stats = relationship('Statistics', cascade='all', uselist=False)
515 516
516 517 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
517 518
518 519 logs = relationship('UserLog')
519 520
520 521 def __unicode__(self):
521 522 return u"<%s('%s:%s')>" % (self.__class__.__name__,self.repo_id,
522 523 self.repo_name)
523 524
524 525 @classmethod
525 526 def url_sep(cls):
526 527 return URL_SEP
527 528
528 529 @classmethod
529 530 def get_by_repo_name(cls, repo_name):
530 531 q = Session.query(cls).filter(cls.repo_name == repo_name)
531 532 q = q.options(joinedload(Repository.fork))\
532 533 .options(joinedload(Repository.user))\
533 534 .options(joinedload(Repository.group))
534 535 return q.scalar()
535 536
536 537 @classmethod
537 538 def get_repo_forks(cls, repo_id):
538 539 return cls.query().filter(Repository.fork_id == repo_id)
539 540
540 541 @classmethod
541 542 def base_path(cls):
542 543 """
543 544 Returns base path when all repos are stored
544 545
545 546 :param cls:
546 547 """
547 548 q = Session.query(RhodeCodeUi)\
548 549 .filter(RhodeCodeUi.ui_key == cls.url_sep())
549 550 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
550 551 return q.one().ui_value
551 552
552 553 @property
553 554 def just_name(self):
554 555 return self.repo_name.split(Repository.url_sep())[-1]
555 556
556 557 @property
557 558 def groups_with_parents(self):
558 559 groups = []
559 560 if self.group is None:
560 561 return groups
561 562
562 563 cur_gr = self.group
563 564 groups.insert(0, cur_gr)
564 565 while 1:
565 566 gr = getattr(cur_gr, 'parent_group', None)
566 567 cur_gr = cur_gr.parent_group
567 568 if gr is None:
568 569 break
569 570 groups.insert(0, gr)
570 571
571 572 return groups
572 573
573 574 @property
574 575 def groups_and_repo(self):
575 576 return self.groups_with_parents, self.just_name
576 577
577 578 @LazyProperty
578 579 def repo_path(self):
579 580 """
580 581 Returns base full path for that repository means where it actually
581 582 exists on a filesystem
582 583 """
583 584 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
584 585 Repository.url_sep())
585 586 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
586 587 return q.one().ui_value
587 588
588 589 @property
589 590 def repo_full_path(self):
590 591 p = [self.repo_path]
591 592 # we need to split the name by / since this is how we store the
592 593 # names in the database, but that eventually needs to be converted
593 594 # into a valid system path
594 595 p += self.repo_name.split(Repository.url_sep())
595 596 return os.path.join(*p)
596 597
597 598 def get_new_name(self, repo_name):
598 599 """
599 600 returns new full repository name based on assigned group and new new
600 601
601 602 :param group_name:
602 603 """
603 604 path_prefix = self.group.full_path_splitted if self.group else []
604 605 return Repository.url_sep().join(path_prefix + [repo_name])
605 606
606 607 @property
607 608 def _ui(self):
608 609 """
609 610 Creates an db based ui object for this repository
610 611 """
611 612 from mercurial import ui
612 613 from mercurial import config
613 614 baseui = ui.ui()
614 615
615 616 #clean the baseui object
616 617 baseui._ocfg = config.config()
617 618 baseui._ucfg = config.config()
618 619 baseui._tcfg = config.config()
619 620
620 621 ret = RhodeCodeUi.query()\
621 622 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
622 623
623 624 hg_ui = ret
624 625 for ui_ in hg_ui:
625 626 if ui_.ui_active:
626 627 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
627 628 ui_.ui_key, ui_.ui_value)
628 629 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
629 630
630 631 return baseui
631 632
632 633 @classmethod
633 634 def is_valid(cls, repo_name):
634 635 """
635 636 returns True if given repo name is a valid filesystem repository
636 637
637 638 :param cls:
638 639 :param repo_name:
639 640 """
640 641 from rhodecode.lib.utils import is_valid_repo
641 642
642 643 return is_valid_repo(repo_name, cls.base_path())
643 644
644 645 #==========================================================================
645 646 # SCM PROPERTIES
646 647 #==========================================================================
647 648
648 649 def get_changeset(self, rev=None):
649 650 return get_changeset_safe(self.scm_instance, rev)
650 651
651 652 @property
652 653 def tip(self):
653 654 return self.get_changeset('tip')
654 655
655 656 @property
656 657 def author(self):
657 658 return self.tip.author
658 659
659 660 @property
660 661 def last_change(self):
661 662 return self.scm_instance.last_change
662 663
663 664 def comments(self, revisions=None):
664 665 """
665 666 Returns comments for this repository grouped by revisions
666 667
667 668 :param revisions: filter query by revisions only
668 669 """
669 670 cmts = ChangesetComment.query()\
670 671 .filter(ChangesetComment.repo == self)
671 672 if revisions:
672 673 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
673 674 grouped = defaultdict(list)
674 675 for cmt in cmts.all():
675 676 grouped[cmt.revision].append(cmt)
676 677 return grouped
677 678
678 679 #==========================================================================
679 680 # SCM CACHE INSTANCE
680 681 #==========================================================================
681 682
682 683 @property
683 684 def invalidate(self):
684 685 return CacheInvalidation.invalidate(self.repo_name)
685 686
686 687 def set_invalidate(self):
687 688 """
688 689 set a cache for invalidation for this instance
689 690 """
690 691 CacheInvalidation.set_invalidate(self.repo_name)
691 692
692 693 @LazyProperty
693 694 def scm_instance(self):
694 695 return self.__get_instance()
695 696
696 697 def scm_instance_cached(self, cache_map=None):
697 698 @cache_region('long_term')
698 699 def _c(repo_name):
699 700 return self.__get_instance()
700 701 rn = self.repo_name
701 702 log.debug('Getting cached instance of repo')
702 703
703 704 if cache_map:
704 705 # get using prefilled cache_map
705 706 invalidate_repo = cache_map[self.repo_name]
706 707 if invalidate_repo:
707 invalidate_repo = (None if invalidate_repo.cache_active
708 invalidate_repo = (None if invalidate_repo.cache_active
708 709 else invalidate_repo)
709 710 else:
710 711 # get from invalidate
711 712 invalidate_repo = self.invalidate
712 713
713 714 if invalidate_repo is not None:
714 715 region_invalidate(_c, None, rn)
715 716 # update our cache
716 717 CacheInvalidation.set_valid(invalidate_repo.cache_key)
717 718 return _c(rn)
718 719
719 720 def __get_instance(self):
720 721 repo_full_path = self.repo_full_path
721 722 try:
722 723 alias = get_scm(repo_full_path)[0]
723 724 log.debug('Creating instance of %s repository' % alias)
724 725 backend = get_backend(alias)
725 726 except VCSError:
726 727 log.error(traceback.format_exc())
727 728 log.error('Perhaps this repository is in db and not in '
728 729 'filesystem run rescan repositories with '
729 730 '"destroy old data " option from admin panel')
730 731 return
731 732
732 733 if alias == 'hg':
733 734
734 735 repo = backend(safe_str(repo_full_path), create=False,
735 736 baseui=self._ui)
736 737 # skip hidden web repository
737 738 if repo._get_hidden():
738 739 return
739 740 else:
740 741 repo = backend(repo_full_path, create=False)
741 742
742 743 return repo
743 744
744 745
745 746 class RepoGroup(Base, BaseModel):
746 747 __tablename__ = 'groups'
747 748 __table_args__ = (
748 749 UniqueConstraint('group_name', 'group_parent_id'),
749 750 CheckConstraint('group_id != group_parent_id'),
750 {'extend_existing': True, 'mysql_engine':'InnoDB',
751 {'extend_existing': True, 'mysql_engine': 'InnoDB',
751 752 'mysql_charset': 'utf8'},
752 753 )
753 754 __mapper_args__ = {'order_by': 'group_name'}
754 755
755 756 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
756 757 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
757 758 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
758 759 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
759 760
760 761 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
761 762 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
762 763
763 764 parent_group = relationship('RepoGroup', remote_side=group_id)
764 765
765 766 def __init__(self, group_name='', parent_group=None):
766 767 self.group_name = group_name
767 768 self.parent_group = parent_group
768 769
769 770 def __unicode__(self):
770 771 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
771 772 self.group_name)
772 773
773 774 @classmethod
774 775 def groups_choices(cls):
775 776 from webhelpers.html import literal as _literal
776 777 repo_groups = [('', '')]
777 778 sep = ' &raquo; '
778 779 _name = lambda k: _literal(sep.join(k))
779 780
780 781 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
781 782 for x in cls.query().all()])
782 783
783 784 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
784 785 return repo_groups
785 786
786 787 @classmethod
787 788 def url_sep(cls):
788 789 return URL_SEP
789 790
790 791 @classmethod
791 792 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
792 793 if case_insensitive:
793 794 gr = cls.query()\
794 795 .filter(cls.group_name.ilike(group_name))
795 796 else:
796 797 gr = cls.query()\
797 798 .filter(cls.group_name == group_name)
798 799 if cache:
799 800 gr = gr.options(FromCache(
800 801 "sql_cache_short",
801 802 "get_group_%s" % _hash_key(group_name)
802 803 )
803 804 )
804 805 return gr.scalar()
805 806
806 807 @property
807 808 def parents(self):
808 809 parents_recursion_limit = 5
809 810 groups = []
810 811 if self.parent_group is None:
811 812 return groups
812 813 cur_gr = self.parent_group
813 814 groups.insert(0, cur_gr)
814 815 cnt = 0
815 816 while 1:
816 817 cnt += 1
817 818 gr = getattr(cur_gr, 'parent_group', None)
818 819 cur_gr = cur_gr.parent_group
819 820 if gr is None:
820 821 break
821 822 if cnt == parents_recursion_limit:
822 823 # this will prevent accidental infinit loops
823 824 log.error('group nested more than %s' %
824 825 parents_recursion_limit)
825 826 break
826 827
827 828 groups.insert(0, gr)
828 829 return groups
829 830
830 831 @property
831 832 def children(self):
832 833 return RepoGroup.query().filter(RepoGroup.parent_group == self)
833 834
834 835 @property
835 836 def name(self):
836 837 return self.group_name.split(RepoGroup.url_sep())[-1]
837 838
838 839 @property
839 840 def full_path(self):
840 841 return self.group_name
841 842
842 843 @property
843 844 def full_path_splitted(self):
844 845 return self.group_name.split(RepoGroup.url_sep())
845 846
846 847 @property
847 848 def repositories(self):
848 849 return Repository.query()\
849 850 .filter(Repository.group == self)\
850 851 .order_by(Repository.repo_name)
851 852
852 853 @property
853 854 def repositories_recursive_count(self):
854 855 cnt = self.repositories.count()
855 856
856 857 def children_count(group):
857 858 cnt = 0
858 859 for child in group.children:
859 860 cnt += child.repositories.count()
860 861 cnt += children_count(child)
861 862 return cnt
862 863
863 864 return cnt + children_count(self)
864 865
865 866 def get_new_name(self, group_name):
866 867 """
867 868 returns new full group name based on parent and new name
868 869
869 870 :param group_name:
870 871 """
871 872 path_prefix = (self.parent_group.full_path_splitted if
872 873 self.parent_group else [])
873 874 return RepoGroup.url_sep().join(path_prefix + [group_name])
874 875
875 876
876 877 class Permission(Base, BaseModel):
877 878 __tablename__ = 'permissions'
878 879 __table_args__ = (
879 {'extend_existing': True, 'mysql_engine':'InnoDB',
880 {'extend_existing': True, 'mysql_engine': 'InnoDB',
880 881 'mysql_charset': 'utf8'},
881 882 )
882 883 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
883 884 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
884 885 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
885 886
886 887 def __unicode__(self):
887 888 return u"<%s('%s:%s')>" % (
888 889 self.__class__.__name__, self.permission_id, self.permission_name
889 890 )
890 891
891 892 @classmethod
892 893 def get_by_key(cls, key):
893 894 return cls.query().filter(cls.permission_name == key).scalar()
894 895
895 896 @classmethod
896 897 def get_default_perms(cls, default_user_id):
897 898 q = Session.query(UserRepoToPerm, Repository, cls)\
898 899 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
899 900 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
900 901 .filter(UserRepoToPerm.user_id == default_user_id)
901 902
902 903 return q.all()
903 904
904 905 @classmethod
905 906 def get_default_group_perms(cls, default_user_id):
906 907 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
907 908 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
908 909 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
909 910 .filter(UserRepoGroupToPerm.user_id == default_user_id)
910 911
911 912 return q.all()
912 913
913 914
914 915 class UserRepoToPerm(Base, BaseModel):
915 916 __tablename__ = 'repo_to_perm'
916 917 __table_args__ = (
917 918 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
918 {'extend_existing': True, 'mysql_engine':'InnoDB',
919 {'extend_existing': True, 'mysql_engine': 'InnoDB',
919 920 'mysql_charset': 'utf8'}
920 921 )
921 922 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
922 923 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
923 924 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
924 925 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
925 926
926 927 user = relationship('User')
927 928 repository = relationship('Repository')
928 929 permission = relationship('Permission')
929 930
930 931 @classmethod
931 932 def create(cls, user, repository, permission):
932 933 n = cls()
933 934 n.user = user
934 935 n.repository = repository
935 936 n.permission = permission
936 937 Session.add(n)
937 938 return n
938 939
939 940 def __unicode__(self):
940 941 return u'<user:%s => %s >' % (self.user, self.repository)
941 942
942 943
943 944 class UserToPerm(Base, BaseModel):
944 945 __tablename__ = 'user_to_perm'
945 946 __table_args__ = (
946 947 UniqueConstraint('user_id', 'permission_id'),
947 {'extend_existing': True, 'mysql_engine':'InnoDB',
948 {'extend_existing': True, 'mysql_engine': 'InnoDB',
948 949 'mysql_charset': 'utf8'}
949 950 )
950 951 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
951 952 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
952 953 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
953 954
954 955 user = relationship('User')
955 956 permission = relationship('Permission', lazy='joined')
956 957
957 958
958 959 class UsersGroupRepoToPerm(Base, BaseModel):
959 960 __tablename__ = 'users_group_repo_to_perm'
960 961 __table_args__ = (
961 962 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
962 {'extend_existing': True, 'mysql_engine':'InnoDB',
963 {'extend_existing': True, 'mysql_engine': 'InnoDB',
963 964 'mysql_charset': 'utf8'}
964 965 )
965 966 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
966 967 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
967 968 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
968 969 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
969 970
970 971 users_group = relationship('UsersGroup')
971 972 permission = relationship('Permission')
972 973 repository = relationship('Repository')
973 974
974 975 @classmethod
975 976 def create(cls, users_group, repository, permission):
976 977 n = cls()
977 978 n.users_group = users_group
978 979 n.repository = repository
979 980 n.permission = permission
980 981 Session.add(n)
981 982 return n
982 983
983 984 def __unicode__(self):
984 985 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
985 986
986 987
987 988 class UsersGroupToPerm(Base, BaseModel):
988 989 __tablename__ = 'users_group_to_perm'
989 990 __table_args__ = (
990 991 UniqueConstraint('users_group_id', 'permission_id',),
991 {'extend_existing': True, 'mysql_engine':'InnoDB',
992 {'extend_existing': True, 'mysql_engine': 'InnoDB',
992 993 'mysql_charset': 'utf8'}
993 994 )
994 995 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
995 996 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
996 997 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
997 998
998 999 users_group = relationship('UsersGroup')
999 1000 permission = relationship('Permission')
1000 1001
1001 1002
1002 1003 class UserRepoGroupToPerm(Base, BaseModel):
1003 1004 __tablename__ = 'user_repo_group_to_perm'
1004 1005 __table_args__ = (
1005 1006 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1006 {'extend_existing': True, 'mysql_engine':'InnoDB',
1007 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1007 1008 'mysql_charset': 'utf8'}
1008 1009 )
1009 1010
1010 1011 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1011 1012 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1012 1013 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1013 1014 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1014 1015
1015 1016 user = relationship('User')
1016 1017 group = relationship('RepoGroup')
1017 1018 permission = relationship('Permission')
1018 1019
1019 1020
1020 1021 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1021 1022 __tablename__ = 'users_group_repo_group_to_perm'
1022 1023 __table_args__ = (
1023 1024 UniqueConstraint('users_group_id', 'group_id'),
1024 {'extend_existing': True, 'mysql_engine':'InnoDB',
1025 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1025 1026 'mysql_charset': 'utf8'}
1026 1027 )
1027 1028
1028 1029 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1029 1030 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1030 1031 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1031 1032 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1032 1033
1033 1034 users_group = relationship('UsersGroup')
1034 1035 permission = relationship('Permission')
1035 1036 group = relationship('RepoGroup')
1036 1037
1037 1038
1038 1039 class Statistics(Base, BaseModel):
1039 1040 __tablename__ = 'statistics'
1040 1041 __table_args__ = (
1041 1042 UniqueConstraint('repository_id'),
1042 {'extend_existing': True, 'mysql_engine':'InnoDB',
1043 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1043 1044 'mysql_charset': 'utf8'}
1044 1045 )
1045 1046 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1046 1047 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1047 1048 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1048 1049 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1049 1050 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1050 1051 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1051 1052
1052 1053 repository = relationship('Repository', single_parent=True)
1053 1054
1054 1055
1055 1056 class UserFollowing(Base, BaseModel):
1056 1057 __tablename__ = 'user_followings'
1057 1058 __table_args__ = (
1058 1059 UniqueConstraint('user_id', 'follows_repository_id'),
1059 1060 UniqueConstraint('user_id', 'follows_user_id'),
1060 {'extend_existing': True, 'mysql_engine':'InnoDB',
1061 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1061 1062 'mysql_charset': 'utf8'}
1062 1063 )
1063 1064
1064 1065 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1065 1066 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1066 1067 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1067 1068 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1068 1069 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1069 1070
1070 1071 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1071 1072
1072 1073 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1073 1074 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1074 1075
1075 1076 @classmethod
1076 1077 def get_repo_followers(cls, repo_id):
1077 1078 return cls.query().filter(cls.follows_repo_id == repo_id)
1078 1079
1079 1080
1080 1081 class CacheInvalidation(Base, BaseModel):
1081 1082 __tablename__ = 'cache_invalidation'
1082 1083 __table_args__ = (
1083 1084 UniqueConstraint('cache_key'),
1084 1085 Index('key_idx', 'cache_key'),
1085 {'extend_existing': True, 'mysql_engine':'InnoDB',
1086 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1086 1087 'mysql_charset': 'utf8'},
1087 1088 )
1088 1089 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1089 1090 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1090 1091 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1091 1092 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1092 1093
1093 1094 def __init__(self, cache_key, cache_args=''):
1094 1095 self.cache_key = cache_key
1095 1096 self.cache_args = cache_args
1096 1097 self.cache_active = False
1097 1098
1098 1099 def __unicode__(self):
1099 1100 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1100 1101 self.cache_id, self.cache_key)
1101 1102
1102 1103 @classmethod
1103 1104 def clear_cache(cls):
1104 1105 cls.query().delete()
1105 1106
1106 1107 @classmethod
1107 1108 def _get_key(cls, key):
1108 1109 """
1109 1110 Wrapper for generating a key, together with a prefix
1110 1111
1111 1112 :param key:
1112 1113 """
1113 1114 import rhodecode
1114 1115 prefix = ''
1115 1116 iid = rhodecode.CONFIG.get('instance_id')
1116 1117 if iid:
1117 1118 prefix = iid
1118 1119 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1119 1120
1120 1121 @classmethod
1121 1122 def get_by_key(cls, key):
1122 1123 return cls.query().filter(cls.cache_key == key).scalar()
1123 1124
1124 1125 @classmethod
1125 1126 def _get_or_create_key(cls, key, prefix, org_key):
1126 1127 inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar()
1127 1128 if not inv_obj:
1128 1129 try:
1129 1130 inv_obj = CacheInvalidation(key, org_key)
1130 1131 Session.add(inv_obj)
1131 #Session.commit()
1132 Session.commit()
1132 1133 except Exception:
1133 1134 log.error(traceback.format_exc())
1134 1135 Session.rollback()
1135 1136 return inv_obj
1136 1137
1137 1138 @classmethod
1138 1139 def invalidate(cls, key):
1139 1140 """
1140 1141 Returns Invalidation object if this given key should be invalidated
1141 1142 None otherwise. `cache_active = False` means that this cache
1142 1143 state is not valid and needs to be invalidated
1143 1144
1144 1145 :param key:
1145 1146 """
1146 1147
1147 1148 key, _prefix, _org_key = cls._get_key(key)
1148 1149 inv = cls._get_or_create_key(key, _prefix, _org_key)
1149 1150
1150 1151 if inv and inv.cache_active is False:
1151 1152 return inv
1152 1153
1153 1154 @classmethod
1154 1155 def set_invalidate(cls, key):
1155 1156 """
1156 1157 Mark this Cache key for invalidation
1157 1158
1158 1159 :param key:
1159 1160 """
1160 1161
1161 1162 key, _prefix, _org_key = cls._get_key(key)
1162 1163 inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all()
1163 1164 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1164 1165 _org_key))
1165 1166 try:
1166 1167 for inv_obj in inv_objs:
1167 1168 if inv_obj:
1168 1169 inv_obj.cache_active = False
1169 1170
1170 1171 Session.add(inv_obj)
1171 1172 Session.commit()
1172 1173 except Exception:
1173 1174 log.error(traceback.format_exc())
1174 1175 Session.rollback()
1175 1176
1176 1177 @classmethod
1177 1178 def set_valid(cls, key):
1178 1179 """
1179 1180 Mark this cache key as active and currently cached
1180 1181
1181 1182 :param key:
1182 1183 """
1183 1184 inv_obj = cls.get_by_key(key)
1184 1185 inv_obj.cache_active = True
1185 1186 Session.add(inv_obj)
1186 1187 Session.commit()
1187 1188
1188 1189 @classmethod
1189 1190 def get_cache_map(cls):
1190 1191
1191 1192 class cachemapdict(dict):
1192 1193
1193 1194 def __init__(self, *args, **kwargs):
1194 1195 fixkey = kwargs.get('fixkey')
1195 1196 if fixkey:
1196 1197 del kwargs['fixkey']
1197 self.fixkey = fixkey
1198 self.fixkey = fixkey
1198 1199 super(cachemapdict, self).__init__(*args, **kwargs)
1199 1200
1200 1201 def __getattr__(self, name):
1201 1202 key = name
1202 1203 if self.fixkey:
1203 1204 key, _prefix, _org_key = cls._get_key(key)
1204 1205 if key in self.__dict__:
1205 1206 return self.__dict__[key]
1206 1207 else:
1207 1208 return self[key]
1208 1209
1209 1210 def __getitem__(self, key):
1210 1211 if self.fixkey:
1211 1212 key, _prefix, _org_key = cls._get_key(key)
1212 1213 try:
1213 1214 return super(cachemapdict, self).__getitem__(key)
1214 1215 except KeyError:
1215 1216 return
1216 1217
1217 1218 cache_map = cachemapdict(fixkey=True)
1218 1219 for obj in cls.query().all():
1219 1220 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1220 1221 return cache_map
1221 1222
1222 1223
1223 1224 class ChangesetComment(Base, BaseModel):
1224 1225 __tablename__ = 'changeset_comments'
1225 1226 __table_args__ = (
1226 {'extend_existing': True, 'mysql_engine':'InnoDB',
1227 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1227 1228 'mysql_charset': 'utf8'},
1228 1229 )
1229 1230 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1230 1231 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1231 1232 revision = Column('revision', String(40), nullable=False)
1232 1233 line_no = Column('line_no', Unicode(10), nullable=True)
1233 1234 f_path = Column('f_path', Unicode(1000), nullable=True)
1234 1235 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1235 1236 text = Column('text', Unicode(25000), nullable=False)
1236 1237 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1237 1238
1238 1239 author = relationship('User', lazy='joined')
1239 1240 repo = relationship('Repository')
1240 1241
1241 1242 @classmethod
1242 1243 def get_users(cls, revision):
1243 1244 """
1244 1245 Returns user associated with this changesetComment. ie those
1245 1246 who actually commented
1246 1247
1247 1248 :param cls:
1248 1249 :param revision:
1249 1250 """
1250 1251 return Session.query(User)\
1251 1252 .filter(cls.revision == revision)\
1252 1253 .join(ChangesetComment.author).all()
1253 1254
1254 1255
1255 1256 class Notification(Base, BaseModel):
1256 1257 __tablename__ = 'notifications'
1257 1258 __table_args__ = (
1258 {'extend_existing': True, 'mysql_engine':'InnoDB',
1259 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1259 1260 'mysql_charset': 'utf8'},
1260 1261 )
1261 1262
1262 1263 TYPE_CHANGESET_COMMENT = u'cs_comment'
1263 1264 TYPE_MESSAGE = u'message'
1264 1265 TYPE_MENTION = u'mention'
1265 1266 TYPE_REGISTRATION = u'registration'
1266 1267
1267 1268 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1268 1269 subject = Column('subject', Unicode(512), nullable=True)
1269 1270 body = Column('body', Unicode(50000), nullable=True)
1270 1271 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1271 1272 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1272 1273 type_ = Column('type', Unicode(256))
1273 1274
1274 1275 created_by_user = relationship('User')
1275 1276 notifications_to_users = relationship('UserNotification', lazy='joined',
1276 1277 cascade="all, delete, delete-orphan")
1277 1278
1278 1279 @property
1279 1280 def recipients(self):
1280 1281 return [x.user for x in UserNotification.query()\
1281 1282 .filter(UserNotification.notification == self)\
1282 1283 .order_by(UserNotification.user).all()]
1283 1284
1284 1285 @classmethod
1285 1286 def create(cls, created_by, subject, body, recipients, type_=None):
1286 1287 if type_ is None:
1287 1288 type_ = Notification.TYPE_MESSAGE
1288 1289
1289 1290 notification = cls()
1290 1291 notification.created_by_user = created_by
1291 1292 notification.subject = subject
1292 1293 notification.body = body
1293 1294 notification.type_ = type_
1294 1295 notification.created_on = datetime.datetime.now()
1295 1296
1296 1297 for u in recipients:
1297 1298 assoc = UserNotification()
1298 1299 assoc.notification = notification
1299 1300 u.notifications.append(assoc)
1300 1301 Session.add(notification)
1301 1302 return notification
1302 1303
1303 1304 @property
1304 1305 def description(self):
1305 1306 from rhodecode.model.notification import NotificationModel
1306 1307 return NotificationModel().make_description(self)
1307 1308
1308 1309
1309 1310 class UserNotification(Base, BaseModel):
1310 1311 __tablename__ = 'user_to_notification'
1311 1312 __table_args__ = (
1312 1313 UniqueConstraint('user_id', 'notification_id'),
1313 {'extend_existing': True, 'mysql_engine':'InnoDB',
1314 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1314 1315 'mysql_charset': 'utf8'}
1315 1316 )
1316 1317 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1317 1318 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1318 1319 read = Column('read', Boolean, default=False)
1319 1320 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1320 1321
1321 1322 user = relationship('User', lazy="joined")
1322 1323 notification = relationship('Notification', lazy="joined",
1323 1324 order_by=lambda: Notification.created_on.desc(),)
1324 1325
1325 1326 def mark_as_read(self):
1326 1327 self.read = True
1327 1328 Session.add(self)
1328 1329
1329 1330
1330 1331 class DbMigrateVersion(Base, BaseModel):
1331 1332 __tablename__ = 'db_migrate_version'
1332 1333 __table_args__ = (
1333 {'extend_existing': True, 'mysql_engine':'InnoDB',
1334 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1334 1335 'mysql_charset': 'utf8'},
1335 1336 )
1336 1337 repository_id = Column('repository_id', String(250), primary_key=True)
1337 1338 repository_path = Column('repository_path', Text)
1338 1339 version = Column('version', Integer)
General Comments 0
You need to be logged in to leave comments. Login now