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