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