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