##// END OF EJS Templates
That should be here !
marcink -
r2766:66493675 beta
parent child Browse files
Show More
@@ -1,1772 +1,1770 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) 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 30 import hashlib
31 31 import time
32 32 from collections import defaultdict
33 33
34 34 from sqlalchemy import *
35 35 from sqlalchemy.ext.hybrid import hybrid_property
36 36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
37 37 from sqlalchemy.exc import DatabaseError
38 38 from beaker.cache import cache_region, region_invalidate
39 39 from webob.exc import HTTPNotFound
40 40
41 41 from pylons.i18n.translation import lazy_ugettext as _
42 42
43 43 from rhodecode.lib.vcs import get_backend
44 44 from rhodecode.lib.vcs.utils.helpers import get_scm
45 45 from rhodecode.lib.vcs.exceptions import VCSError
46 46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
47 47
48 48 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
49 49 safe_unicode
50 50 from rhodecode.lib.compat import json
51 51 from rhodecode.lib.caching_query import FromCache
52 52
53 53 from rhodecode.model.meta import Base, Session
54 54
55 55 URL_SEP = '/'
56 56 log = logging.getLogger(__name__)
57 57
58 58 #==============================================================================
59 59 # BASE CLASSES
60 60 #==============================================================================
61 61
62 62 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
63 63
64 64
65 65 class BaseModel(object):
66 66 """
67 67 Base Model for all classess
68 68 """
69 69
70 70 @classmethod
71 71 def _get_keys(cls):
72 72 """return column names for this model """
73 73 return class_mapper(cls).c.keys()
74 74
75 75 def get_dict(self):
76 76 """
77 77 return dict with keys and values corresponding
78 78 to this model data """
79 79
80 80 d = {}
81 81 for k in self._get_keys():
82 82 d[k] = getattr(self, k)
83 83
84 84 # also use __json__() if present to get additional fields
85 85 _json_attr = getattr(self, '__json__', None)
86 86 if _json_attr:
87 87 # update with attributes from __json__
88 88 if callable(_json_attr):
89 89 _json_attr = _json_attr()
90 90 for k, val in _json_attr.iteritems():
91 91 d[k] = val
92 92 return d
93 93
94 94 def get_appstruct(self):
95 95 """return list with keys and values tupples corresponding
96 96 to this model data """
97 97
98 98 l = []
99 99 for k in self._get_keys():
100 100 l.append((k, getattr(self, k),))
101 101 return l
102 102
103 103 def populate_obj(self, populate_dict):
104 104 """populate model with data from given populate_dict"""
105 105
106 106 for k in self._get_keys():
107 107 if k in populate_dict:
108 108 setattr(self, k, populate_dict[k])
109 109
110 110 @classmethod
111 111 def query(cls):
112 112 return Session().query(cls)
113 113
114 114 @classmethod
115 115 def get(cls, id_):
116 116 if id_:
117 117 return cls.query().get(id_)
118 118
119 119 @classmethod
120 120 def get_or_404(cls, id_):
121 121 if id_:
122 122 res = cls.query().get(id_)
123 123 if not res:
124 124 raise HTTPNotFound
125 125 return res
126 126
127 127 @classmethod
128 128 def getAll(cls):
129 129 return cls.query().all()
130 130
131 131 @classmethod
132 132 def delete(cls, id_):
133 133 obj = cls.query().get(id_)
134 134 Session().delete(obj)
135 135
136 136 def __repr__(self):
137 137 if hasattr(self, '__unicode__'):
138 138 # python repr needs to return str
139 139 return safe_str(self.__unicode__())
140 140 return '<DB:%s>' % (self.__class__.__name__)
141 141
142 142
143 143 class RhodeCodeSetting(Base, BaseModel):
144 144 __tablename__ = 'rhodecode_settings'
145 145 __table_args__ = (
146 146 UniqueConstraint('app_settings_name'),
147 147 {'extend_existing': True, 'mysql_engine': 'InnoDB',
148 148 'mysql_charset': 'utf8'}
149 149 )
150 150 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
151 151 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
152 152 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
153 153
154 154 def __init__(self, k='', v=''):
155 155 self.app_settings_name = k
156 156 self.app_settings_value = v
157 157
158 158 @validates('_app_settings_value')
159 159 def validate_settings_value(self, key, val):
160 160 assert type(val) == unicode
161 161 return val
162 162
163 163 @hybrid_property
164 164 def app_settings_value(self):
165 165 v = self._app_settings_value
166 166 if self.app_settings_name == 'ldap_active':
167 167 v = str2bool(v)
168 168 return v
169 169
170 170 @app_settings_value.setter
171 171 def app_settings_value(self, val):
172 172 """
173 173 Setter that will always make sure we use unicode in app_settings_value
174 174
175 175 :param val:
176 176 """
177 177 self._app_settings_value = safe_unicode(val)
178 178
179 179 def __unicode__(self):
180 180 return u"<%s('%s:%s')>" % (
181 181 self.__class__.__name__,
182 182 self.app_settings_name, self.app_settings_value
183 183 )
184 184
185 185 @classmethod
186 186 def get_by_name(cls, key):
187 187 return cls.query()\
188 188 .filter(cls.app_settings_name == key).scalar()
189 189
190 190 @classmethod
191 191 def get_by_name_or_create(cls, key):
192 192 res = cls.get_by_name(key)
193 193 if not res:
194 194 res = cls(key)
195 195 return res
196 196
197 197 @classmethod
198 198 def get_app_settings(cls, cache=False):
199 199
200 200 ret = cls.query()
201 201
202 202 if cache:
203 203 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
204 204
205 205 if not ret:
206 206 raise Exception('Could not get application settings !')
207 207 settings = {}
208 208 for each in ret:
209 209 settings['rhodecode_' + each.app_settings_name] = \
210 210 each.app_settings_value
211 211
212 212 return settings
213 213
214 214 @classmethod
215 215 def get_ldap_settings(cls, cache=False):
216 216 ret = cls.query()\
217 217 .filter(cls.app_settings_name.startswith('ldap_')).all()
218 218 fd = {}
219 219 for row in ret:
220 220 fd.update({row.app_settings_name: row.app_settings_value})
221 221
222 222 return fd
223 223
224 224
225 225 class RhodeCodeUi(Base, BaseModel):
226 226 __tablename__ = 'rhodecode_ui'
227 227 __table_args__ = (
228 228 UniqueConstraint('ui_key'),
229 229 {'extend_existing': True, 'mysql_engine': 'InnoDB',
230 230 'mysql_charset': 'utf8'}
231 231 )
232 232
233 233 HOOK_UPDATE = 'changegroup.update'
234 234 HOOK_REPO_SIZE = 'changegroup.repo_size'
235 235 HOOK_PUSH = 'changegroup.push_logger'
236 236 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
237 237 HOOK_PULL = 'outgoing.pull_logger'
238 238 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
239 239
240 240 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
241 241 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
242 242 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
243 243 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
244 244 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
245 245
246 246 @classmethod
247 247 def get_by_key(cls, key):
248 248 return cls.query().filter(cls.ui_key == key).scalar()
249 249
250 250 @classmethod
251 251 def get_builtin_hooks(cls):
252 252 q = cls.query()
253 253 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
254 254 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
255 255 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
256 256 return q.all()
257 257
258 258 @classmethod
259 259 def get_custom_hooks(cls):
260 260 q = cls.query()
261 261 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
262 262 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
263 263 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
264 264 q = q.filter(cls.ui_section == 'hooks')
265 265 return q.all()
266 266
267 267 @classmethod
268 268 def get_repos_location(cls):
269 269 return cls.get_by_key('/').ui_value
270 270
271 271 @classmethod
272 272 def create_or_update_hook(cls, key, val):
273 273 new_ui = cls.get_by_key(key) or cls()
274 274 new_ui.ui_section = 'hooks'
275 275 new_ui.ui_active = True
276 276 new_ui.ui_key = key
277 277 new_ui.ui_value = val
278 278
279 279 Session().add(new_ui)
280 280
281 281
282 282 class User(Base, BaseModel):
283 283 __tablename__ = 'users'
284 284 __table_args__ = (
285 285 UniqueConstraint('username'), UniqueConstraint('email'),
286 286 Index('u_username_idx', 'username'),
287 287 Index('u_email_idx', 'email'),
288 288 {'extend_existing': True, 'mysql_engine': 'InnoDB',
289 289 'mysql_charset': 'utf8'}
290 290 )
291 291 DEFAULT_USER = 'default'
292 292
293 293 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
294 294 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
295 295 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
296 296 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
297 297 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
298 298 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
299 299 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
300 300 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
301 301 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
302 302 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
303 303 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
304 304 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
305 305
306 306 user_log = relationship('UserLog', cascade='all')
307 307 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
308 308
309 309 repositories = relationship('Repository')
310 310 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
311 311 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
312 312 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
313 313
314 314 group_member = relationship('UsersGroupMember', cascade='all')
315 315
316 316 notifications = relationship('UserNotification', cascade='all')
317 317 # notifications assigned to this user
318 318 user_created_notifications = relationship('Notification', cascade='all')
319 319 # comments created by this user
320 320 user_comments = relationship('ChangesetComment', cascade='all')
321 321 #extra emails for this user
322 322 user_emails = relationship('UserEmailMap', cascade='all')
323 323
324 324 @hybrid_property
325 325 def email(self):
326 326 return self._email
327 327
328 328 @email.setter
329 329 def email(self, val):
330 330 self._email = val.lower() if val else None
331 331
332 332 @property
333 333 def firstname(self):
334 334 # alias for future
335 335 return self.name
336 336
337 337 @property
338 338 def emails(self):
339 339 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
340 340 return [self.email] + [x.email for x in other]
341 341
342 342 @property
343 343 def username_and_name(self):
344 344 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
345 345
346 346 @property
347 347 def full_name(self):
348 348 return '%s %s' % (self.firstname, self.lastname)
349 349
350 350 @property
351 351 def full_name_or_username(self):
352 352 return ('%s %s' % (self.firstname, self.lastname)
353 353 if (self.firstname and self.lastname) else self.username)
354 354
355 355 @property
356 356 def full_contact(self):
357 357 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
358 358
359 359 @property
360 360 def short_contact(self):
361 361 return '%s %s' % (self.firstname, self.lastname)
362 362
363 363 @property
364 364 def is_admin(self):
365 365 return self.admin
366 366
367 367 def __unicode__(self):
368 368 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
369 369 self.user_id, self.username)
370 370
371 371 @classmethod
372 372 def get_by_username(cls, username, case_insensitive=False, cache=False):
373 373 if case_insensitive:
374 374 q = cls.query().filter(cls.username.ilike(username))
375 375 else:
376 376 q = cls.query().filter(cls.username == username)
377 377
378 378 if cache:
379 379 q = q.options(FromCache(
380 380 "sql_cache_short",
381 381 "get_user_%s" % _hash_key(username)
382 382 )
383 383 )
384 384 return q.scalar()
385 385
386 386 @classmethod
387 387 def get_by_api_key(cls, api_key, cache=False):
388 388 q = cls.query().filter(cls.api_key == api_key)
389 389
390 390 if cache:
391 391 q = q.options(FromCache("sql_cache_short",
392 392 "get_api_key_%s" % api_key))
393 393 return q.scalar()
394 394
395 395 @classmethod
396 396 def get_by_email(cls, email, case_insensitive=False, cache=False):
397 397 if case_insensitive:
398 398 q = cls.query().filter(cls.email.ilike(email))
399 399 else:
400 400 q = cls.query().filter(cls.email == email)
401 401
402 402 if cache:
403 403 q = q.options(FromCache("sql_cache_short",
404 404 "get_email_key_%s" % email))
405 405
406 406 ret = q.scalar()
407 407 if ret is None:
408 408 q = UserEmailMap.query()
409 409 # try fetching in alternate email map
410 410 if case_insensitive:
411 411 q = q.filter(UserEmailMap.email.ilike(email))
412 412 else:
413 413 q = q.filter(UserEmailMap.email == email)
414 414 q = q.options(joinedload(UserEmailMap.user))
415 415 if cache:
416 416 q = q.options(FromCache("sql_cache_short",
417 417 "get_email_map_key_%s" % email))
418 418 ret = getattr(q.scalar(), 'user', None)
419 419
420 420 return ret
421 421
422 422 def update_lastlogin(self):
423 423 """Update user lastlogin"""
424 424 self.last_login = datetime.datetime.now()
425 425 Session().add(self)
426 426 log.debug('updated user %s lastlogin' % self.username)
427 427
428 428 def get_api_data(self):
429 429 """
430 430 Common function for generating user related data for API
431 431 """
432 432 user = self
433 433 data = dict(
434 434 user_id=user.user_id,
435 435 username=user.username,
436 436 firstname=user.name,
437 437 lastname=user.lastname,
438 438 email=user.email,
439 439 emails=user.emails,
440 440 api_key=user.api_key,
441 441 active=user.active,
442 442 admin=user.admin,
443 443 ldap_dn=user.ldap_dn,
444 444 last_login=user.last_login,
445 445 )
446 446 return data
447 447
448 448 def __json__(self):
449 449 data = dict(
450 450 full_name=self.full_name,
451 451 full_name_or_username=self.full_name_or_username,
452 452 short_contact=self.short_contact,
453 453 full_contact=self.full_contact
454 454 )
455 455 data.update(self.get_api_data())
456 456 return data
457 457
458 458
459 459 class UserEmailMap(Base, BaseModel):
460 460 __tablename__ = 'user_email_map'
461 461 __table_args__ = (
462 462 Index('uem_email_idx', 'email'),
463 463 UniqueConstraint('email'),
464 464 {'extend_existing': True, 'mysql_engine': 'InnoDB',
465 465 'mysql_charset': 'utf8'}
466 466 )
467 467 __mapper_args__ = {}
468 468
469 469 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
470 470 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
471 471 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
472
473 test = Column("test", String(255),nullable=False,default=True)
474 472 user = relationship('User', lazy='joined')
475 473
476 474 @validates('_email')
477 475 def validate_email(self, key, email):
478 476 # check if this email is not main one
479 477 main_email = Session().query(User).filter(User.email == email).scalar()
480 478 if main_email is not None:
481 479 raise AttributeError('email %s is present is user table' % email)
482 480 return email
483 481
484 482 @hybrid_property
485 483 def email(self):
486 484 return self._email
487 485
488 486 @email.setter
489 487 def email(self, val):
490 488 self._email = val.lower() if val else None
491 489
492 490
493 491 class UserLog(Base, BaseModel):
494 492 __tablename__ = 'user_logs'
495 493 __table_args__ = (
496 494 {'extend_existing': True, 'mysql_engine': 'InnoDB',
497 495 'mysql_charset': 'utf8'},
498 496 )
499 497 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
500 498 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
501 499 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
502 500 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
503 501 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
504 502 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
505 503 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
506 504
507 505 @property
508 506 def action_as_day(self):
509 507 return datetime.date(*self.action_date.timetuple()[:3])
510 508
511 509 user = relationship('User')
512 510 repository = relationship('Repository', cascade='')
513 511
514 512
515 513 class UsersGroup(Base, BaseModel):
516 514 __tablename__ = 'users_groups'
517 515 __table_args__ = (
518 516 {'extend_existing': True, 'mysql_engine': 'InnoDB',
519 517 'mysql_charset': 'utf8'},
520 518 )
521 519
522 520 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
523 521 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
524 522 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
525 523 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
526 524
527 525 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
528 526 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
529 527 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
530 528
531 529 def __unicode__(self):
532 530 return u'<userGroup(%s)>' % (self.users_group_name)
533 531
534 532 @classmethod
535 533 def get_by_group_name(cls, group_name, cache=False,
536 534 case_insensitive=False):
537 535 if case_insensitive:
538 536 q = cls.query().filter(cls.users_group_name.ilike(group_name))
539 537 else:
540 538 q = cls.query().filter(cls.users_group_name == group_name)
541 539 if cache:
542 540 q = q.options(FromCache(
543 541 "sql_cache_short",
544 542 "get_user_%s" % _hash_key(group_name)
545 543 )
546 544 )
547 545 return q.scalar()
548 546
549 547 @classmethod
550 548 def get(cls, users_group_id, cache=False):
551 549 users_group = cls.query()
552 550 if cache:
553 551 users_group = users_group.options(FromCache("sql_cache_short",
554 552 "get_users_group_%s" % users_group_id))
555 553 return users_group.get(users_group_id)
556 554
557 555 def get_api_data(self):
558 556 users_group = self
559 557
560 558 data = dict(
561 559 users_group_id=users_group.users_group_id,
562 560 group_name=users_group.users_group_name,
563 561 active=users_group.users_group_active,
564 562 )
565 563
566 564 return data
567 565
568 566
569 567 class UsersGroupMember(Base, BaseModel):
570 568 __tablename__ = 'users_groups_members'
571 569 __table_args__ = (
572 570 {'extend_existing': True, 'mysql_engine': 'InnoDB',
573 571 'mysql_charset': 'utf8'},
574 572 )
575 573
576 574 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
577 575 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
578 576 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
579 577
580 578 user = relationship('User', lazy='joined')
581 579 users_group = relationship('UsersGroup')
582 580
583 581 def __init__(self, gr_id='', u_id=''):
584 582 self.users_group_id = gr_id
585 583 self.user_id = u_id
586 584
587 585
588 586 class Repository(Base, BaseModel):
589 587 __tablename__ = 'repositories'
590 588 __table_args__ = (
591 589 UniqueConstraint('repo_name'),
592 590 Index('r_repo_name_idx', 'repo_name'),
593 591 {'extend_existing': True, 'mysql_engine': 'InnoDB',
594 592 'mysql_charset': 'utf8'},
595 593 )
596 594
597 595 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
598 596 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
599 597 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
600 598 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
601 599 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
602 600 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
603 601 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
604 602 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
605 603 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
606 604 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
607 605 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
608 606 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
609 607 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
610 608
611 609 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
612 610 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
613 611
614 612 user = relationship('User')
615 613 fork = relationship('Repository', remote_side=repo_id)
616 614 group = relationship('RepoGroup')
617 615 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
618 616 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
619 617 stats = relationship('Statistics', cascade='all', uselist=False)
620 618
621 619 followers = relationship('UserFollowing',
622 620 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
623 621 cascade='all')
624 622
625 623 logs = relationship('UserLog')
626 624 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
627 625
628 626 pull_requests_org = relationship('PullRequest',
629 627 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
630 628 cascade="all, delete, delete-orphan")
631 629
632 630 pull_requests_other = relationship('PullRequest',
633 631 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
634 632 cascade="all, delete, delete-orphan")
635 633
636 634 def __unicode__(self):
637 635 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
638 636 self.repo_name)
639 637
640 638 @hybrid_property
641 639 def locked(self):
642 640 # always should return [user_id, timelocked]
643 641 if self._locked:
644 642 _lock_info = self._locked.split(':')
645 643 return int(_lock_info[0]), _lock_info[1]
646 644 return [None, None]
647 645
648 646 @locked.setter
649 647 def locked(self, val):
650 648 if val and isinstance(val, (list, tuple)):
651 649 self._locked = ':'.join(map(str, val))
652 650 else:
653 651 self._locked = None
654 652
655 653 @classmethod
656 654 def url_sep(cls):
657 655 return URL_SEP
658 656
659 657 @classmethod
660 658 def get_by_repo_name(cls, repo_name):
661 659 q = Session().query(cls).filter(cls.repo_name == repo_name)
662 660 q = q.options(joinedload(Repository.fork))\
663 661 .options(joinedload(Repository.user))\
664 662 .options(joinedload(Repository.group))
665 663 return q.scalar()
666 664
667 665 @classmethod
668 666 def get_by_full_path(cls, repo_full_path):
669 667 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
670 668 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
671 669
672 670 @classmethod
673 671 def get_repo_forks(cls, repo_id):
674 672 return cls.query().filter(Repository.fork_id == repo_id)
675 673
676 674 @classmethod
677 675 def base_path(cls):
678 676 """
679 677 Returns base path when all repos are stored
680 678
681 679 :param cls:
682 680 """
683 681 q = Session().query(RhodeCodeUi)\
684 682 .filter(RhodeCodeUi.ui_key == cls.url_sep())
685 683 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
686 684 return q.one().ui_value
687 685
688 686 @property
689 687 def forks(self):
690 688 """
691 689 Return forks of this repo
692 690 """
693 691 return Repository.get_repo_forks(self.repo_id)
694 692
695 693 @property
696 694 def parent(self):
697 695 """
698 696 Returns fork parent
699 697 """
700 698 return self.fork
701 699
702 700 @property
703 701 def just_name(self):
704 702 return self.repo_name.split(Repository.url_sep())[-1]
705 703
706 704 @property
707 705 def groups_with_parents(self):
708 706 groups = []
709 707 if self.group is None:
710 708 return groups
711 709
712 710 cur_gr = self.group
713 711 groups.insert(0, cur_gr)
714 712 while 1:
715 713 gr = getattr(cur_gr, 'parent_group', None)
716 714 cur_gr = cur_gr.parent_group
717 715 if gr is None:
718 716 break
719 717 groups.insert(0, gr)
720 718
721 719 return groups
722 720
723 721 @property
724 722 def groups_and_repo(self):
725 723 return self.groups_with_parents, self.just_name
726 724
727 725 @LazyProperty
728 726 def repo_path(self):
729 727 """
730 728 Returns base full path for that repository means where it actually
731 729 exists on a filesystem
732 730 """
733 731 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
734 732 Repository.url_sep())
735 733 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
736 734 return q.one().ui_value
737 735
738 736 @property
739 737 def repo_full_path(self):
740 738 p = [self.repo_path]
741 739 # we need to split the name by / since this is how we store the
742 740 # names in the database, but that eventually needs to be converted
743 741 # into a valid system path
744 742 p += self.repo_name.split(Repository.url_sep())
745 743 return os.path.join(*p)
746 744
747 745 def get_new_name(self, repo_name):
748 746 """
749 747 returns new full repository name based on assigned group and new new
750 748
751 749 :param group_name:
752 750 """
753 751 path_prefix = self.group.full_path_splitted if self.group else []
754 752 return Repository.url_sep().join(path_prefix + [repo_name])
755 753
756 754 @property
757 755 def _ui(self):
758 756 """
759 757 Creates an db based ui object for this repository
760 758 """
761 759 from mercurial import ui
762 760 from mercurial import config
763 761 baseui = ui.ui()
764 762
765 763 #clean the baseui object
766 764 baseui._ocfg = config.config()
767 765 baseui._ucfg = config.config()
768 766 baseui._tcfg = config.config()
769 767
770 768 ret = RhodeCodeUi.query()\
771 769 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
772 770
773 771 hg_ui = ret
774 772 for ui_ in hg_ui:
775 773 if ui_.ui_active:
776 774 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
777 775 ui_.ui_key, ui_.ui_value)
778 776 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
779 777 if ui_.ui_key == 'push_ssl':
780 778 # force set push_ssl requirement to False, rhodecode
781 779 # handles that
782 780 baseui.setconfig(ui_.ui_section, ui_.ui_key, False)
783 781
784 782 return baseui
785 783
786 784 @classmethod
787 785 def inject_ui(cls, repo, extras={}):
788 786 from rhodecode.lib.vcs.backends.hg import MercurialRepository
789 787 from rhodecode.lib.vcs.backends.git import GitRepository
790 788 required = (MercurialRepository, GitRepository)
791 789 if not isinstance(repo, required):
792 790 raise Exception('repo must be instance of %s' % required)
793 791
794 792 # inject ui extra param to log this action via push logger
795 793 for k, v in extras.items():
796 794 repo._repo.ui.setconfig('rhodecode_extras', k, v)
797 795
798 796 @classmethod
799 797 def is_valid(cls, repo_name):
800 798 """
801 799 returns True if given repo name is a valid filesystem repository
802 800
803 801 :param cls:
804 802 :param repo_name:
805 803 """
806 804 from rhodecode.lib.utils import is_valid_repo
807 805
808 806 return is_valid_repo(repo_name, cls.base_path())
809 807
810 808 def get_api_data(self):
811 809 """
812 810 Common function for generating repo api data
813 811
814 812 """
815 813 repo = self
816 814 data = dict(
817 815 repo_id=repo.repo_id,
818 816 repo_name=repo.repo_name,
819 817 repo_type=repo.repo_type,
820 818 clone_uri=repo.clone_uri,
821 819 private=repo.private,
822 820 created_on=repo.created_on,
823 821 description=repo.description,
824 822 landing_rev=repo.landing_rev,
825 823 owner=repo.user.username,
826 824 fork_of=repo.fork.repo_name if repo.fork else None
827 825 )
828 826
829 827 return data
830 828
831 829 @classmethod
832 830 def lock(cls, repo, user_id):
833 831 repo.locked = [user_id, time.time()]
834 832 Session().add(repo)
835 833 Session().commit()
836 834
837 835 @classmethod
838 836 def unlock(cls, repo):
839 837 repo.locked = None
840 838 Session().add(repo)
841 839 Session().commit()
842 840
843 841 #==========================================================================
844 842 # SCM PROPERTIES
845 843 #==========================================================================
846 844
847 845 def get_changeset(self, rev=None):
848 846 return get_changeset_safe(self.scm_instance, rev)
849 847
850 848 def get_landing_changeset(self):
851 849 """
852 850 Returns landing changeset, or if that doesn't exist returns the tip
853 851 """
854 852 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
855 853 return cs
856 854
857 855 @property
858 856 def tip(self):
859 857 return self.get_changeset('tip')
860 858
861 859 @property
862 860 def author(self):
863 861 return self.tip.author
864 862
865 863 @property
866 864 def last_change(self):
867 865 return self.scm_instance.last_change
868 866
869 867 def get_comments(self, revisions=None):
870 868 """
871 869 Returns comments for this repository grouped by revisions
872 870
873 871 :param revisions: filter query by revisions only
874 872 """
875 873 cmts = ChangesetComment.query()\
876 874 .filter(ChangesetComment.repo == self)
877 875 if revisions:
878 876 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
879 877 grouped = defaultdict(list)
880 878 for cmt in cmts.all():
881 879 grouped[cmt.revision].append(cmt)
882 880 return grouped
883 881
884 882 def statuses(self, revisions=None):
885 883 """
886 884 Returns statuses for this repository
887 885
888 886 :param revisions: list of revisions to get statuses for
889 887 :type revisions: list
890 888 """
891 889
892 890 statuses = ChangesetStatus.query()\
893 891 .filter(ChangesetStatus.repo == self)\
894 892 .filter(ChangesetStatus.version == 0)
895 893 if revisions:
896 894 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
897 895 grouped = {}
898 896
899 897 #maybe we have open new pullrequest without a status ?
900 898 stat = ChangesetStatus.STATUS_UNDER_REVIEW
901 899 status_lbl = ChangesetStatus.get_status_lbl(stat)
902 900 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
903 901 for rev in pr.revisions:
904 902 pr_id = pr.pull_request_id
905 903 pr_repo = pr.other_repo.repo_name
906 904 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
907 905
908 906 for stat in statuses.all():
909 907 pr_id = pr_repo = None
910 908 if stat.pull_request:
911 909 pr_id = stat.pull_request.pull_request_id
912 910 pr_repo = stat.pull_request.other_repo.repo_name
913 911 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
914 912 pr_id, pr_repo]
915 913 return grouped
916 914
917 915 #==========================================================================
918 916 # SCM CACHE INSTANCE
919 917 #==========================================================================
920 918
921 919 @property
922 920 def invalidate(self):
923 921 return CacheInvalidation.invalidate(self.repo_name)
924 922
925 923 def set_invalidate(self):
926 924 """
927 925 set a cache for invalidation for this instance
928 926 """
929 927 CacheInvalidation.set_invalidate(self.repo_name)
930 928
931 929 @LazyProperty
932 930 def scm_instance(self):
933 931 return self.__get_instance()
934 932
935 933 def scm_instance_cached(self, cache_map=None):
936 934 @cache_region('long_term')
937 935 def _c(repo_name):
938 936 return self.__get_instance()
939 937 rn = self.repo_name
940 938 log.debug('Getting cached instance of repo')
941 939
942 940 if cache_map:
943 941 # get using prefilled cache_map
944 942 invalidate_repo = cache_map[self.repo_name]
945 943 if invalidate_repo:
946 944 invalidate_repo = (None if invalidate_repo.cache_active
947 945 else invalidate_repo)
948 946 else:
949 947 # get from invalidate
950 948 invalidate_repo = self.invalidate
951 949
952 950 if invalidate_repo is not None:
953 951 region_invalidate(_c, None, rn)
954 952 # update our cache
955 953 CacheInvalidation.set_valid(invalidate_repo.cache_key)
956 954 return _c(rn)
957 955
958 956 def __get_instance(self):
959 957 repo_full_path = self.repo_full_path
960 958 try:
961 959 alias = get_scm(repo_full_path)[0]
962 960 log.debug('Creating instance of %s repository' % alias)
963 961 backend = get_backend(alias)
964 962 except VCSError:
965 963 log.error(traceback.format_exc())
966 964 log.error('Perhaps this repository is in db and not in '
967 965 'filesystem run rescan repositories with '
968 966 '"destroy old data " option from admin panel')
969 967 return
970 968
971 969 if alias == 'hg':
972 970
973 971 repo = backend(safe_str(repo_full_path), create=False,
974 972 baseui=self._ui)
975 973 # skip hidden web repository
976 974 if repo._get_hidden():
977 975 return
978 976 else:
979 977 repo = backend(repo_full_path, create=False)
980 978
981 979 return repo
982 980
983 981
984 982 class RepoGroup(Base, BaseModel):
985 983 __tablename__ = 'groups'
986 984 __table_args__ = (
987 985 UniqueConstraint('group_name', 'group_parent_id'),
988 986 CheckConstraint('group_id != group_parent_id'),
989 987 {'extend_existing': True, 'mysql_engine': 'InnoDB',
990 988 'mysql_charset': 'utf8'},
991 989 )
992 990 __mapper_args__ = {'order_by': 'group_name'}
993 991
994 992 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
995 993 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
996 994 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
997 995 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
998 996 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
999 997
1000 998 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1001 999 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
1002 1000
1003 1001 parent_group = relationship('RepoGroup', remote_side=group_id)
1004 1002
1005 1003 def __init__(self, group_name='', parent_group=None):
1006 1004 self.group_name = group_name
1007 1005 self.parent_group = parent_group
1008 1006
1009 1007 def __unicode__(self):
1010 1008 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
1011 1009 self.group_name)
1012 1010
1013 1011 @classmethod
1014 1012 def groups_choices(cls):
1015 1013 from webhelpers.html import literal as _literal
1016 1014 repo_groups = [('', '')]
1017 1015 sep = ' &raquo; '
1018 1016 _name = lambda k: _literal(sep.join(k))
1019 1017
1020 1018 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1021 1019 for x in cls.query().all()])
1022 1020
1023 1021 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1024 1022 return repo_groups
1025 1023
1026 1024 @classmethod
1027 1025 def url_sep(cls):
1028 1026 return URL_SEP
1029 1027
1030 1028 @classmethod
1031 1029 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1032 1030 if case_insensitive:
1033 1031 gr = cls.query()\
1034 1032 .filter(cls.group_name.ilike(group_name))
1035 1033 else:
1036 1034 gr = cls.query()\
1037 1035 .filter(cls.group_name == group_name)
1038 1036 if cache:
1039 1037 gr = gr.options(FromCache(
1040 1038 "sql_cache_short",
1041 1039 "get_group_%s" % _hash_key(group_name)
1042 1040 )
1043 1041 )
1044 1042 return gr.scalar()
1045 1043
1046 1044 @property
1047 1045 def parents(self):
1048 1046 parents_recursion_limit = 5
1049 1047 groups = []
1050 1048 if self.parent_group is None:
1051 1049 return groups
1052 1050 cur_gr = self.parent_group
1053 1051 groups.insert(0, cur_gr)
1054 1052 cnt = 0
1055 1053 while 1:
1056 1054 cnt += 1
1057 1055 gr = getattr(cur_gr, 'parent_group', None)
1058 1056 cur_gr = cur_gr.parent_group
1059 1057 if gr is None:
1060 1058 break
1061 1059 if cnt == parents_recursion_limit:
1062 1060 # this will prevent accidental infinit loops
1063 1061 log.error('group nested more than %s' %
1064 1062 parents_recursion_limit)
1065 1063 break
1066 1064
1067 1065 groups.insert(0, gr)
1068 1066 return groups
1069 1067
1070 1068 @property
1071 1069 def children(self):
1072 1070 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1073 1071
1074 1072 @property
1075 1073 def name(self):
1076 1074 return self.group_name.split(RepoGroup.url_sep())[-1]
1077 1075
1078 1076 @property
1079 1077 def full_path(self):
1080 1078 return self.group_name
1081 1079
1082 1080 @property
1083 1081 def full_path_splitted(self):
1084 1082 return self.group_name.split(RepoGroup.url_sep())
1085 1083
1086 1084 @property
1087 1085 def repositories(self):
1088 1086 return Repository.query()\
1089 1087 .filter(Repository.group == self)\
1090 1088 .order_by(Repository.repo_name)
1091 1089
1092 1090 @property
1093 1091 def repositories_recursive_count(self):
1094 1092 cnt = self.repositories.count()
1095 1093
1096 1094 def children_count(group):
1097 1095 cnt = 0
1098 1096 for child in group.children:
1099 1097 cnt += child.repositories.count()
1100 1098 cnt += children_count(child)
1101 1099 return cnt
1102 1100
1103 1101 return cnt + children_count(self)
1104 1102
1105 1103 def recursive_groups_and_repos(self):
1106 1104 """
1107 1105 Recursive return all groups, with repositories in those groups
1108 1106 """
1109 1107 all_ = []
1110 1108
1111 1109 def _get_members(root_gr):
1112 1110 for r in root_gr.repositories:
1113 1111 all_.append(r)
1114 1112 childs = root_gr.children.all()
1115 1113 if childs:
1116 1114 for gr in childs:
1117 1115 all_.append(gr)
1118 1116 _get_members(gr)
1119 1117
1120 1118 _get_members(self)
1121 1119 return [self] + all_
1122 1120
1123 1121 def get_new_name(self, group_name):
1124 1122 """
1125 1123 returns new full group name based on parent and new name
1126 1124
1127 1125 :param group_name:
1128 1126 """
1129 1127 path_prefix = (self.parent_group.full_path_splitted if
1130 1128 self.parent_group else [])
1131 1129 return RepoGroup.url_sep().join(path_prefix + [group_name])
1132 1130
1133 1131
1134 1132 class Permission(Base, BaseModel):
1135 1133 __tablename__ = 'permissions'
1136 1134 __table_args__ = (
1137 1135 Index('p_perm_name_idx', 'permission_name'),
1138 1136 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1139 1137 'mysql_charset': 'utf8'},
1140 1138 )
1141 1139 PERMS = [
1142 1140 ('repository.none', _('Repository no access')),
1143 1141 ('repository.read', _('Repository read access')),
1144 1142 ('repository.write', _('Repository write access')),
1145 1143 ('repository.admin', _('Repository admin access')),
1146 1144
1147 1145 ('group.none', _('Repositories Group no access')),
1148 1146 ('group.read', _('Repositories Group read access')),
1149 1147 ('group.write', _('Repositories Group write access')),
1150 1148 ('group.admin', _('Repositories Group admin access')),
1151 1149
1152 1150 ('hg.admin', _('RhodeCode Administrator')),
1153 1151 ('hg.create.none', _('Repository creation disabled')),
1154 1152 ('hg.create.repository', _('Repository creation enabled')),
1155 1153 ('hg.fork.none', _('Repository forking disabled')),
1156 1154 ('hg.fork.repository', _('Repository forking enabled')),
1157 1155 ('hg.register.none', _('Register disabled')),
1158 1156 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1159 1157 'with manual activation')),
1160 1158
1161 1159 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1162 1160 'with auto activation')),
1163 1161 ]
1164 1162
1165 1163 # defines which permissions are more important higher the more important
1166 1164 PERM_WEIGHTS = {
1167 1165 'repository.none': 0,
1168 1166 'repository.read': 1,
1169 1167 'repository.write': 3,
1170 1168 'repository.admin': 4,
1171 1169
1172 1170 'group.none': 0,
1173 1171 'group.read': 1,
1174 1172 'group.write': 3,
1175 1173 'group.admin': 4,
1176 1174
1177 1175 'hg.fork.none': 0,
1178 1176 'hg.fork.repository': 1,
1179 1177 'hg.create.none': 0,
1180 1178 'hg.create.repository':1
1181 1179 }
1182 1180
1183 1181 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1184 1182 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1185 1183 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1186 1184
1187 1185 def __unicode__(self):
1188 1186 return u"<%s('%s:%s')>" % (
1189 1187 self.__class__.__name__, self.permission_id, self.permission_name
1190 1188 )
1191 1189
1192 1190 @classmethod
1193 1191 def get_by_key(cls, key):
1194 1192 return cls.query().filter(cls.permission_name == key).scalar()
1195 1193
1196 1194 @classmethod
1197 1195 def get_default_perms(cls, default_user_id):
1198 1196 q = Session().query(UserRepoToPerm, Repository, cls)\
1199 1197 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1200 1198 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1201 1199 .filter(UserRepoToPerm.user_id == default_user_id)
1202 1200
1203 1201 return q.all()
1204 1202
1205 1203 @classmethod
1206 1204 def get_default_group_perms(cls, default_user_id):
1207 1205 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1208 1206 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1209 1207 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1210 1208 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1211 1209
1212 1210 return q.all()
1213 1211
1214 1212
1215 1213 class UserRepoToPerm(Base, BaseModel):
1216 1214 __tablename__ = 'repo_to_perm'
1217 1215 __table_args__ = (
1218 1216 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1219 1217 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1220 1218 'mysql_charset': 'utf8'}
1221 1219 )
1222 1220 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1223 1221 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1224 1222 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1225 1223 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1226 1224
1227 1225 user = relationship('User')
1228 1226 repository = relationship('Repository')
1229 1227 permission = relationship('Permission')
1230 1228
1231 1229 @classmethod
1232 1230 def create(cls, user, repository, permission):
1233 1231 n = cls()
1234 1232 n.user = user
1235 1233 n.repository = repository
1236 1234 n.permission = permission
1237 1235 Session().add(n)
1238 1236 return n
1239 1237
1240 1238 def __unicode__(self):
1241 1239 return u'<user:%s => %s >' % (self.user, self.repository)
1242 1240
1243 1241
1244 1242 class UserToPerm(Base, BaseModel):
1245 1243 __tablename__ = 'user_to_perm'
1246 1244 __table_args__ = (
1247 1245 UniqueConstraint('user_id', 'permission_id'),
1248 1246 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1249 1247 'mysql_charset': 'utf8'}
1250 1248 )
1251 1249 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1252 1250 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1253 1251 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1254 1252
1255 1253 user = relationship('User')
1256 1254 permission = relationship('Permission', lazy='joined')
1257 1255
1258 1256
1259 1257 class UsersGroupRepoToPerm(Base, BaseModel):
1260 1258 __tablename__ = 'users_group_repo_to_perm'
1261 1259 __table_args__ = (
1262 1260 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1263 1261 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1264 1262 'mysql_charset': 'utf8'}
1265 1263 )
1266 1264 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1267 1265 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1268 1266 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1269 1267 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1270 1268
1271 1269 users_group = relationship('UsersGroup')
1272 1270 permission = relationship('Permission')
1273 1271 repository = relationship('Repository')
1274 1272
1275 1273 @classmethod
1276 1274 def create(cls, users_group, repository, permission):
1277 1275 n = cls()
1278 1276 n.users_group = users_group
1279 1277 n.repository = repository
1280 1278 n.permission = permission
1281 1279 Session().add(n)
1282 1280 return n
1283 1281
1284 1282 def __unicode__(self):
1285 1283 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1286 1284
1287 1285
1288 1286 class UsersGroupToPerm(Base, BaseModel):
1289 1287 __tablename__ = 'users_group_to_perm'
1290 1288 __table_args__ = (
1291 1289 UniqueConstraint('users_group_id', 'permission_id',),
1292 1290 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1293 1291 'mysql_charset': 'utf8'}
1294 1292 )
1295 1293 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1296 1294 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1297 1295 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1298 1296
1299 1297 users_group = relationship('UsersGroup')
1300 1298 permission = relationship('Permission')
1301 1299
1302 1300
1303 1301 class UserRepoGroupToPerm(Base, BaseModel):
1304 1302 __tablename__ = 'user_repo_group_to_perm'
1305 1303 __table_args__ = (
1306 1304 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1307 1305 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1308 1306 'mysql_charset': 'utf8'}
1309 1307 )
1310 1308
1311 1309 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1312 1310 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1313 1311 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1314 1312 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1315 1313
1316 1314 user = relationship('User')
1317 1315 group = relationship('RepoGroup')
1318 1316 permission = relationship('Permission')
1319 1317
1320 1318
1321 1319 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1322 1320 __tablename__ = 'users_group_repo_group_to_perm'
1323 1321 __table_args__ = (
1324 1322 UniqueConstraint('users_group_id', 'group_id'),
1325 1323 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1326 1324 'mysql_charset': 'utf8'}
1327 1325 )
1328 1326
1329 1327 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)
1330 1328 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1331 1329 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1332 1330 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1333 1331
1334 1332 users_group = relationship('UsersGroup')
1335 1333 permission = relationship('Permission')
1336 1334 group = relationship('RepoGroup')
1337 1335
1338 1336
1339 1337 class Statistics(Base, BaseModel):
1340 1338 __tablename__ = 'statistics'
1341 1339 __table_args__ = (
1342 1340 UniqueConstraint('repository_id'),
1343 1341 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1344 1342 'mysql_charset': 'utf8'}
1345 1343 )
1346 1344 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1347 1345 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1348 1346 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1349 1347 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1350 1348 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1351 1349 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1352 1350
1353 1351 repository = relationship('Repository', single_parent=True)
1354 1352
1355 1353
1356 1354 class UserFollowing(Base, BaseModel):
1357 1355 __tablename__ = 'user_followings'
1358 1356 __table_args__ = (
1359 1357 UniqueConstraint('user_id', 'follows_repository_id'),
1360 1358 UniqueConstraint('user_id', 'follows_user_id'),
1361 1359 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1362 1360 'mysql_charset': 'utf8'}
1363 1361 )
1364 1362
1365 1363 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1366 1364 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1367 1365 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1368 1366 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1369 1367 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1370 1368
1371 1369 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1372 1370
1373 1371 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1374 1372 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1375 1373
1376 1374 @classmethod
1377 1375 def get_repo_followers(cls, repo_id):
1378 1376 return cls.query().filter(cls.follows_repo_id == repo_id)
1379 1377
1380 1378
1381 1379 class CacheInvalidation(Base, BaseModel):
1382 1380 __tablename__ = 'cache_invalidation'
1383 1381 __table_args__ = (
1384 1382 UniqueConstraint('cache_key'),
1385 1383 Index('key_idx', 'cache_key'),
1386 1384 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1387 1385 'mysql_charset': 'utf8'},
1388 1386 )
1389 1387 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1390 1388 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1391 1389 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1392 1390 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1393 1391
1394 1392 def __init__(self, cache_key, cache_args=''):
1395 1393 self.cache_key = cache_key
1396 1394 self.cache_args = cache_args
1397 1395 self.cache_active = False
1398 1396
1399 1397 def __unicode__(self):
1400 1398 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1401 1399 self.cache_id, self.cache_key)
1402 1400
1403 1401 @classmethod
1404 1402 def clear_cache(cls):
1405 1403 cls.query().delete()
1406 1404
1407 1405 @classmethod
1408 1406 def _get_key(cls, key):
1409 1407 """
1410 1408 Wrapper for generating a key, together with a prefix
1411 1409
1412 1410 :param key:
1413 1411 """
1414 1412 import rhodecode
1415 1413 prefix = ''
1416 1414 iid = rhodecode.CONFIG.get('instance_id')
1417 1415 if iid:
1418 1416 prefix = iid
1419 1417 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1420 1418
1421 1419 @classmethod
1422 1420 def get_by_key(cls, key):
1423 1421 return cls.query().filter(cls.cache_key == key).scalar()
1424 1422
1425 1423 @classmethod
1426 1424 def _get_or_create_key(cls, key, prefix, org_key):
1427 1425 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1428 1426 if not inv_obj:
1429 1427 try:
1430 1428 inv_obj = CacheInvalidation(key, org_key)
1431 1429 Session().add(inv_obj)
1432 1430 Session().commit()
1433 1431 except Exception:
1434 1432 log.error(traceback.format_exc())
1435 1433 Session().rollback()
1436 1434 return inv_obj
1437 1435
1438 1436 @classmethod
1439 1437 def invalidate(cls, key):
1440 1438 """
1441 1439 Returns Invalidation object if this given key should be invalidated
1442 1440 None otherwise. `cache_active = False` means that this cache
1443 1441 state is not valid and needs to be invalidated
1444 1442
1445 1443 :param key:
1446 1444 """
1447 1445
1448 1446 key, _prefix, _org_key = cls._get_key(key)
1449 1447 inv = cls._get_or_create_key(key, _prefix, _org_key)
1450 1448
1451 1449 if inv and inv.cache_active is False:
1452 1450 return inv
1453 1451
1454 1452 @classmethod
1455 1453 def set_invalidate(cls, key):
1456 1454 """
1457 1455 Mark this Cache key for invalidation
1458 1456
1459 1457 :param key:
1460 1458 """
1461 1459
1462 1460 key, _prefix, _org_key = cls._get_key(key)
1463 1461 inv_objs = Session().query(cls).filter(cls.cache_args == _org_key).all()
1464 1462 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1465 1463 _org_key))
1466 1464 try:
1467 1465 for inv_obj in inv_objs:
1468 1466 if inv_obj:
1469 1467 inv_obj.cache_active = False
1470 1468
1471 1469 Session().add(inv_obj)
1472 1470 Session().commit()
1473 1471 except Exception:
1474 1472 log.error(traceback.format_exc())
1475 1473 Session().rollback()
1476 1474
1477 1475 @classmethod
1478 1476 def set_valid(cls, key):
1479 1477 """
1480 1478 Mark this cache key as active and currently cached
1481 1479
1482 1480 :param key:
1483 1481 """
1484 1482 inv_obj = cls.get_by_key(key)
1485 1483 inv_obj.cache_active = True
1486 1484 Session().add(inv_obj)
1487 1485 Session().commit()
1488 1486
1489 1487 @classmethod
1490 1488 def get_cache_map(cls):
1491 1489
1492 1490 class cachemapdict(dict):
1493 1491
1494 1492 def __init__(self, *args, **kwargs):
1495 1493 fixkey = kwargs.get('fixkey')
1496 1494 if fixkey:
1497 1495 del kwargs['fixkey']
1498 1496 self.fixkey = fixkey
1499 1497 super(cachemapdict, self).__init__(*args, **kwargs)
1500 1498
1501 1499 def __getattr__(self, name):
1502 1500 key = name
1503 1501 if self.fixkey:
1504 1502 key, _prefix, _org_key = cls._get_key(key)
1505 1503 if key in self.__dict__:
1506 1504 return self.__dict__[key]
1507 1505 else:
1508 1506 return self[key]
1509 1507
1510 1508 def __getitem__(self, key):
1511 1509 if self.fixkey:
1512 1510 key, _prefix, _org_key = cls._get_key(key)
1513 1511 try:
1514 1512 return super(cachemapdict, self).__getitem__(key)
1515 1513 except KeyError:
1516 1514 return
1517 1515
1518 1516 cache_map = cachemapdict(fixkey=True)
1519 1517 for obj in cls.query().all():
1520 1518 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1521 1519 return cache_map
1522 1520
1523 1521
1524 1522 class ChangesetComment(Base, BaseModel):
1525 1523 __tablename__ = 'changeset_comments'
1526 1524 __table_args__ = (
1527 1525 Index('cc_revision_idx', 'revision'),
1528 1526 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1529 1527 'mysql_charset': 'utf8'},
1530 1528 )
1531 1529 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1532 1530 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1533 1531 revision = Column('revision', String(40), nullable=True)
1534 1532 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1535 1533 line_no = Column('line_no', Unicode(10), nullable=True)
1536 1534 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1537 1535 f_path = Column('f_path', Unicode(1000), nullable=True)
1538 1536 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1539 1537 text = Column('text', Unicode(25000), nullable=False)
1540 1538 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1541 1539 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1542 1540
1543 1541 author = relationship('User', lazy='joined')
1544 1542 repo = relationship('Repository')
1545 1543 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1546 1544 pull_request = relationship('PullRequest', lazy='joined')
1547 1545
1548 1546 @classmethod
1549 1547 def get_users(cls, revision=None, pull_request_id=None):
1550 1548 """
1551 1549 Returns user associated with this ChangesetComment. ie those
1552 1550 who actually commented
1553 1551
1554 1552 :param cls:
1555 1553 :param revision:
1556 1554 """
1557 1555 q = Session().query(User)\
1558 1556 .join(ChangesetComment.author)
1559 1557 if revision:
1560 1558 q = q.filter(cls.revision == revision)
1561 1559 elif pull_request_id:
1562 1560 q = q.filter(cls.pull_request_id == pull_request_id)
1563 1561 return q.all()
1564 1562
1565 1563
1566 1564 class ChangesetStatus(Base, BaseModel):
1567 1565 __tablename__ = 'changeset_statuses'
1568 1566 __table_args__ = (
1569 1567 Index('cs_revision_idx', 'revision'),
1570 1568 Index('cs_version_idx', 'version'),
1571 1569 UniqueConstraint('repo_id', 'revision', 'version'),
1572 1570 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1573 1571 'mysql_charset': 'utf8'}
1574 1572 )
1575 1573 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1576 1574 STATUS_APPROVED = 'approved'
1577 1575 STATUS_REJECTED = 'rejected'
1578 1576 STATUS_UNDER_REVIEW = 'under_review'
1579 1577
1580 1578 STATUSES = [
1581 1579 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1582 1580 (STATUS_APPROVED, _("Approved")),
1583 1581 (STATUS_REJECTED, _("Rejected")),
1584 1582 (STATUS_UNDER_REVIEW, _("Under Review")),
1585 1583 ]
1586 1584
1587 1585 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1588 1586 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1589 1587 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1590 1588 revision = Column('revision', String(40), nullable=False)
1591 1589 status = Column('status', String(128), nullable=False, default=DEFAULT)
1592 1590 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1593 1591 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1594 1592 version = Column('version', Integer(), nullable=False, default=0)
1595 1593 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1596 1594
1597 1595 author = relationship('User', lazy='joined')
1598 1596 repo = relationship('Repository')
1599 1597 comment = relationship('ChangesetComment', lazy='joined')
1600 1598 pull_request = relationship('PullRequest', lazy='joined')
1601 1599
1602 1600 def __unicode__(self):
1603 1601 return u"<%s('%s:%s')>" % (
1604 1602 self.__class__.__name__,
1605 1603 self.status, self.author
1606 1604 )
1607 1605
1608 1606 @classmethod
1609 1607 def get_status_lbl(cls, value):
1610 1608 return dict(cls.STATUSES).get(value)
1611 1609
1612 1610 @property
1613 1611 def status_lbl(self):
1614 1612 return ChangesetStatus.get_status_lbl(self.status)
1615 1613
1616 1614
1617 1615 class PullRequest(Base, BaseModel):
1618 1616 __tablename__ = 'pull_requests'
1619 1617 __table_args__ = (
1620 1618 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1621 1619 'mysql_charset': 'utf8'},
1622 1620 )
1623 1621
1624 1622 STATUS_NEW = u'new'
1625 1623 STATUS_OPEN = u'open'
1626 1624 STATUS_CLOSED = u'closed'
1627 1625
1628 1626 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1629 1627 title = Column('title', Unicode(256), nullable=True)
1630 1628 description = Column('description', UnicodeText(10240), nullable=True)
1631 1629 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1632 1630 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1633 1631 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1634 1632 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1635 1633 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1636 1634 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1637 1635 org_ref = Column('org_ref', Unicode(256), nullable=False)
1638 1636 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1639 1637 other_ref = Column('other_ref', Unicode(256), nullable=False)
1640 1638
1641 1639 @hybrid_property
1642 1640 def revisions(self):
1643 1641 return self._revisions.split(':')
1644 1642
1645 1643 @revisions.setter
1646 1644 def revisions(self, val):
1647 1645 self._revisions = ':'.join(val)
1648 1646
1649 1647 author = relationship('User', lazy='joined')
1650 1648 reviewers = relationship('PullRequestReviewers',
1651 1649 cascade="all, delete, delete-orphan")
1652 1650 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1653 1651 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1654 1652 statuses = relationship('ChangesetStatus')
1655 1653 comments = relationship('ChangesetComment',
1656 1654 cascade="all, delete, delete-orphan")
1657 1655
1658 1656 def is_closed(self):
1659 1657 return self.status == self.STATUS_CLOSED
1660 1658
1661 1659 def __json__(self):
1662 1660 return dict(
1663 1661 revisions=self.revisions
1664 1662 )
1665 1663
1666 1664
1667 1665 class PullRequestReviewers(Base, BaseModel):
1668 1666 __tablename__ = 'pull_request_reviewers'
1669 1667 __table_args__ = (
1670 1668 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1671 1669 'mysql_charset': 'utf8'},
1672 1670 )
1673 1671
1674 1672 def __init__(self, user=None, pull_request=None):
1675 1673 self.user = user
1676 1674 self.pull_request = pull_request
1677 1675
1678 1676 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1679 1677 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1680 1678 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1681 1679
1682 1680 user = relationship('User')
1683 1681 pull_request = relationship('PullRequest')
1684 1682
1685 1683
1686 1684 class Notification(Base, BaseModel):
1687 1685 __tablename__ = 'notifications'
1688 1686 __table_args__ = (
1689 1687 Index('notification_type_idx', 'type'),
1690 1688 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1691 1689 'mysql_charset': 'utf8'},
1692 1690 )
1693 1691
1694 1692 TYPE_CHANGESET_COMMENT = u'cs_comment'
1695 1693 TYPE_MESSAGE = u'message'
1696 1694 TYPE_MENTION = u'mention'
1697 1695 TYPE_REGISTRATION = u'registration'
1698 1696 TYPE_PULL_REQUEST = u'pull_request'
1699 1697 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1700 1698
1701 1699 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1702 1700 subject = Column('subject', Unicode(512), nullable=True)
1703 1701 body = Column('body', UnicodeText(50000), nullable=True)
1704 1702 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1705 1703 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1706 1704 type_ = Column('type', Unicode(256))
1707 1705
1708 1706 created_by_user = relationship('User')
1709 1707 notifications_to_users = relationship('UserNotification', lazy='joined',
1710 1708 cascade="all, delete, delete-orphan")
1711 1709
1712 1710 @property
1713 1711 def recipients(self):
1714 1712 return [x.user for x in UserNotification.query()\
1715 1713 .filter(UserNotification.notification == self)\
1716 1714 .order_by(UserNotification.user_id.asc()).all()]
1717 1715
1718 1716 @classmethod
1719 1717 def create(cls, created_by, subject, body, recipients, type_=None):
1720 1718 if type_ is None:
1721 1719 type_ = Notification.TYPE_MESSAGE
1722 1720
1723 1721 notification = cls()
1724 1722 notification.created_by_user = created_by
1725 1723 notification.subject = subject
1726 1724 notification.body = body
1727 1725 notification.type_ = type_
1728 1726 notification.created_on = datetime.datetime.now()
1729 1727
1730 1728 for u in recipients:
1731 1729 assoc = UserNotification()
1732 1730 assoc.notification = notification
1733 1731 u.notifications.append(assoc)
1734 1732 Session().add(notification)
1735 1733 return notification
1736 1734
1737 1735 @property
1738 1736 def description(self):
1739 1737 from rhodecode.model.notification import NotificationModel
1740 1738 return NotificationModel().make_description(self)
1741 1739
1742 1740
1743 1741 class UserNotification(Base, BaseModel):
1744 1742 __tablename__ = 'user_to_notification'
1745 1743 __table_args__ = (
1746 1744 UniqueConstraint('user_id', 'notification_id'),
1747 1745 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1748 1746 'mysql_charset': 'utf8'}
1749 1747 )
1750 1748 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1751 1749 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1752 1750 read = Column('read', Boolean, default=False)
1753 1751 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1754 1752
1755 1753 user = relationship('User', lazy="joined")
1756 1754 notification = relationship('Notification', lazy="joined",
1757 1755 order_by=lambda: Notification.created_on.desc(),)
1758 1756
1759 1757 def mark_as_read(self):
1760 1758 self.read = True
1761 1759 Session().add(self)
1762 1760
1763 1761
1764 1762 class DbMigrateVersion(Base, BaseModel):
1765 1763 __tablename__ = 'db_migrate_version'
1766 1764 __table_args__ = (
1767 1765 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1768 1766 'mysql_charset': 'utf8'},
1769 1767 )
1770 1768 repository_id = Column('repository_id', String(250), primary_key=True)
1771 1769 repository_path = Column('repository_path', Text)
1772 1770 version = Column('version', Integer)
General Comments 0
You need to be logged in to leave comments. Login now