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