##// END OF EJS Templates
Extended repo2db mapper with group creation via directory structures...
marcink -
r878:859bc9e7 beta
parent child Browse files
Show More
@@ -1,621 +1,650 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.utils
3 rhodecode.lib.utils
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Utilities library for RhodeCode
6 Utilities library for RhodeCode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 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
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
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, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import os
28 import os
29 import logging
29 import logging
30 import datetime
30 import datetime
31 import traceback
31 import traceback
32
32
33 from UserDict import DictMixin
33 from UserDict import DictMixin
34
34
35 from mercurial import ui, config, hg
35 from mercurial import ui, config, hg
36 from mercurial.error import RepoError
36 from mercurial.error import RepoError
37
37
38 import paste
38 import paste
39 import beaker
39 import beaker
40 from paste.script.command import Command, BadCommand
40 from paste.script.command import Command, BadCommand
41
41
42 from vcs.backends.base import BaseChangeset
42 from vcs.backends.base import BaseChangeset
43 from vcs.utils.lazy import LazyProperty
43 from vcs.utils.lazy import LazyProperty
44
44
45 from rhodecode.model import meta
45 from rhodecode.model import meta
46 from rhodecode.model.caching_query import FromCache
46 from rhodecode.model.caching_query import FromCache
47 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog
47 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group
48 from rhodecode.model.repo import RepoModel
48 from rhodecode.model.repo import RepoModel
49 from rhodecode.model.user import UserModel
49 from rhodecode.model.user import UserModel
50
50
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53
53
54 def get_repo_slug(request):
54 def get_repo_slug(request):
55 return request.environ['pylons.routes_dict'].get('repo_name')
55 return request.environ['pylons.routes_dict'].get('repo_name')
56
56
57 def action_logger(user, action, repo, ipaddr='', sa=None):
57 def action_logger(user, action, repo, ipaddr='', sa=None):
58 """
58 """
59 Action logger for various actions made by users
59 Action logger for various actions made by users
60
60
61 :param user: user that made this action, can be a unique username string or
61 :param user: user that made this action, can be a unique username string or
62 object containing user_id attribute
62 object containing user_id attribute
63 :param action: action to log, should be on of predefined unique actions for
63 :param action: action to log, should be on of predefined unique actions for
64 easy translations
64 easy translations
65 :param repo: string name of repository or object containing repo_id,
65 :param repo: string name of repository or object containing repo_id,
66 that action was made on
66 that action was made on
67 :param ipaddr: optional ip address from what the action was made
67 :param ipaddr: optional ip address from what the action was made
68 :param sa: optional sqlalchemy session
68 :param sa: optional sqlalchemy session
69
69
70 """
70 """
71
71
72 if not sa:
72 if not sa:
73 sa = meta.Session()
73 sa = meta.Session()
74
74
75 try:
75 try:
76 um = UserModel()
76 um = UserModel()
77 if hasattr(user, 'user_id'):
77 if hasattr(user, 'user_id'):
78 user_obj = user
78 user_obj = user
79 elif isinstance(user, basestring):
79 elif isinstance(user, basestring):
80 user_obj = um.get_by_username(user, cache=False)
80 user_obj = um.get_by_username(user, cache=False)
81 else:
81 else:
82 raise Exception('You have to provide user object or username')
82 raise Exception('You have to provide user object or username')
83
83
84
84
85 rm = RepoModel()
85 rm = RepoModel()
86 if hasattr(repo, 'repo_id'):
86 if hasattr(repo, 'repo_id'):
87 repo_obj = rm.get(repo.repo_id, cache=False)
87 repo_obj = rm.get(repo.repo_id, cache=False)
88 repo_name = repo_obj.repo_name
88 repo_name = repo_obj.repo_name
89 elif isinstance(repo, basestring):
89 elif isinstance(repo, basestring):
90 repo_name = repo.lstrip('/')
90 repo_name = repo.lstrip('/')
91 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
91 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
92 else:
92 else:
93 raise Exception('You have to provide repository to action logger')
93 raise Exception('You have to provide repository to action logger')
94
94
95
95
96 user_log = UserLog()
96 user_log = UserLog()
97 user_log.user_id = user_obj.user_id
97 user_log.user_id = user_obj.user_id
98 user_log.action = action
98 user_log.action = action
99
99
100 user_log.repository_id = repo_obj.repo_id
100 user_log.repository_id = repo_obj.repo_id
101 user_log.repository_name = repo_name
101 user_log.repository_name = repo_name
102
102
103 user_log.action_date = datetime.datetime.now()
103 user_log.action_date = datetime.datetime.now()
104 user_log.user_ip = ipaddr
104 user_log.user_ip = ipaddr
105 sa.add(user_log)
105 sa.add(user_log)
106 sa.commit()
106 sa.commit()
107
107
108 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
108 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
109 except:
109 except:
110 log.error(traceback.format_exc())
110 log.error(traceback.format_exc())
111 sa.rollback()
111 sa.rollback()
112
112
113 def get_repos(path, recursive=False):
113 def get_repos(path, recursive=False):
114 """
114 """
115 Scans given path for repos and return (name,(type,path)) tuple
115 Scans given path for repos and return (name,(type,path)) tuple
116
116
117 :param path: path to scann for repositories
117 :param path: path to scann for repositories
118 :param recursive: recursive search and return names with subdirs in front
118 :param recursive: recursive search and return names with subdirs in front
119 """
119 """
120 from vcs.utils.helpers import get_scm
120 from vcs.utils.helpers import get_scm
121 from vcs.exceptions import VCSError
121 from vcs.exceptions import VCSError
122
122
123 if path.endswith('/'):
123 if path.endswith('/'):
124 #add ending slash for better results
124 #add ending slash for better results
125 path = path[:-1]
125 path = path[:-1]
126
126
127 def _get_repos(p):
127 def _get_repos(p):
128 for dirpath in os.listdir(p):
128 for dirpath in os.listdir(p):
129 if os.path.isfile(os.path.join(p, dirpath)):
129 if os.path.isfile(os.path.join(p, dirpath)):
130 continue
130 continue
131 cur_path = os.path.join(p, dirpath)
131 cur_path = os.path.join(p, dirpath)
132 try:
132 try:
133 scm_info = get_scm(cur_path)
133 scm_info = get_scm(cur_path)
134 yield scm_info[1].split(path)[-1].lstrip('/'), scm_info
134 yield scm_info[1].split(path)[-1].lstrip('/'), scm_info
135 except VCSError:
135 except VCSError:
136 if not recursive:
136 if not recursive:
137 continue
137 continue
138 #check if this dir containts other repos for recursive scan
138 #check if this dir containts other repos for recursive scan
139 rec_path = os.path.join(p, dirpath)
139 rec_path = os.path.join(p, dirpath)
140 if os.path.isdir(rec_path):
140 if os.path.isdir(rec_path):
141 for inner_scm in _get_repos(rec_path):
141 for inner_scm in _get_repos(rec_path):
142 yield inner_scm
142 yield inner_scm
143
143
144 return _get_repos(path)
144 return _get_repos(path)
145
145
146 def check_repo_fast(repo_name, base_path):
146 def check_repo_fast(repo_name, base_path):
147 """
147 """
148 Check given path for existence of directory
148 Check given path for existence of directory
149 :param repo_name:
149 :param repo_name:
150 :param base_path:
150 :param base_path:
151
151
152 :return False: if this directory is present
152 :return False: if this directory is present
153 """
153 """
154 if os.path.isdir(os.path.join(base_path, repo_name)):return False
154 if os.path.isdir(os.path.join(base_path, repo_name)):return False
155 return True
155 return True
156
156
157 def check_repo(repo_name, base_path, verify=True):
157 def check_repo(repo_name, base_path, verify=True):
158
158
159 repo_path = os.path.join(base_path, repo_name)
159 repo_path = os.path.join(base_path, repo_name)
160
160
161 try:
161 try:
162 if not check_repo_fast(repo_name, base_path):
162 if not check_repo_fast(repo_name, base_path):
163 return False
163 return False
164 r = hg.repository(ui.ui(), repo_path)
164 r = hg.repository(ui.ui(), repo_path)
165 if verify:
165 if verify:
166 hg.verify(r)
166 hg.verify(r)
167 #here we hnow that repo exists it was verified
167 #here we hnow that repo exists it was verified
168 log.info('%s repo is already created', repo_name)
168 log.info('%s repo is already created', repo_name)
169 return False
169 return False
170 except RepoError:
170 except RepoError:
171 #it means that there is no valid repo there...
171 #it means that there is no valid repo there...
172 log.info('%s repo is free for creation', repo_name)
172 log.info('%s repo is free for creation', repo_name)
173 return True
173 return True
174
174
175 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
175 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
176 while True:
176 while True:
177 ok = raw_input(prompt)
177 ok = raw_input(prompt)
178 if ok in ('y', 'ye', 'yes'): return True
178 if ok in ('y', 'ye', 'yes'): return True
179 if ok in ('n', 'no', 'nop', 'nope'): return False
179 if ok in ('n', 'no', 'nop', 'nope'): return False
180 retries = retries - 1
180 retries = retries - 1
181 if retries < 0: raise IOError
181 if retries < 0: raise IOError
182 print complaint
182 print complaint
183
183
184 #propagated from mercurial documentation
184 #propagated from mercurial documentation
185 ui_sections = ['alias', 'auth',
185 ui_sections = ['alias', 'auth',
186 'decode/encode', 'defaults',
186 'decode/encode', 'defaults',
187 'diff', 'email',
187 'diff', 'email',
188 'extensions', 'format',
188 'extensions', 'format',
189 'merge-patterns', 'merge-tools',
189 'merge-patterns', 'merge-tools',
190 'hooks', 'http_proxy',
190 'hooks', 'http_proxy',
191 'smtp', 'patch',
191 'smtp', 'patch',
192 'paths', 'profiling',
192 'paths', 'profiling',
193 'server', 'trusted',
193 'server', 'trusted',
194 'ui', 'web', ]
194 'ui', 'web', ]
195
195
196 def make_ui(read_from='file', path=None, checkpaths=True):
196 def make_ui(read_from='file', path=None, checkpaths=True):
197 """
197 """
198 A function that will read python rc files or database
198 A function that will read python rc files or database
199 and make an mercurial ui object from read options
199 and make an mercurial ui object from read options
200
200
201 :param path: path to mercurial config file
201 :param path: path to mercurial config file
202 :param checkpaths: check the path
202 :param checkpaths: check the path
203 :param read_from: read from 'file' or 'db'
203 :param read_from: read from 'file' or 'db'
204 """
204 """
205
205
206 baseui = ui.ui()
206 baseui = ui.ui()
207
207
208 #clean the baseui object
208 #clean the baseui object
209 baseui._ocfg = config.config()
209 baseui._ocfg = config.config()
210 baseui._ucfg = config.config()
210 baseui._ucfg = config.config()
211 baseui._tcfg = config.config()
211 baseui._tcfg = config.config()
212
212
213 if read_from == 'file':
213 if read_from == 'file':
214 if not os.path.isfile(path):
214 if not os.path.isfile(path):
215 log.warning('Unable to read config file %s' % path)
215 log.warning('Unable to read config file %s' % path)
216 return False
216 return False
217 log.debug('reading hgrc from %s', path)
217 log.debug('reading hgrc from %s', path)
218 cfg = config.config()
218 cfg = config.config()
219 cfg.read(path)
219 cfg.read(path)
220 for section in ui_sections:
220 for section in ui_sections:
221 for k, v in cfg.items(section):
221 for k, v in cfg.items(section):
222 log.debug('settings ui from file[%s]%s:%s', section, k, v)
222 log.debug('settings ui from file[%s]%s:%s', section, k, v)
223 baseui.setconfig(section, k, v)
223 baseui.setconfig(section, k, v)
224
224
225
225
226 elif read_from == 'db':
226 elif read_from == 'db':
227 sa = meta.Session()
227 sa = meta.Session()
228 ret = sa.query(RhodeCodeUi)\
228 ret = sa.query(RhodeCodeUi)\
229 .options(FromCache("sql_cache_short",
229 .options(FromCache("sql_cache_short",
230 "get_hg_ui_settings")).all()
230 "get_hg_ui_settings")).all()
231
231
232 hg_ui = ret
232 hg_ui = ret
233 for ui_ in hg_ui:
233 for ui_ in hg_ui:
234 if ui_.ui_active:
234 if ui_.ui_active:
235 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
235 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
236 ui_.ui_key, ui_.ui_value)
236 ui_.ui_key, ui_.ui_value)
237 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
237 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
238
238
239 meta.Session.remove()
239 meta.Session.remove()
240 return baseui
240 return baseui
241
241
242
242
243 def set_rhodecode_config(config):
243 def set_rhodecode_config(config):
244 """Updates pylons config with new settings from database
244 """Updates pylons config with new settings from database
245
245
246 :param config:
246 :param config:
247 """
247 """
248 from rhodecode.model.settings import SettingsModel
248 from rhodecode.model.settings import SettingsModel
249 hgsettings = SettingsModel().get_app_settings()
249 hgsettings = SettingsModel().get_app_settings()
250
250
251 for k, v in hgsettings.items():
251 for k, v in hgsettings.items():
252 config[k] = v
252 config[k] = v
253
253
254 def invalidate_cache(cache_key, *args):
254 def invalidate_cache(cache_key, *args):
255 """Puts cache invalidation task into db for
255 """Puts cache invalidation task into db for
256 further global cache invalidation
256 further global cache invalidation
257 """
257 """
258
258
259 from rhodecode.model.scm import ScmModel
259 from rhodecode.model.scm import ScmModel
260
260
261 if cache_key.startswith('get_repo_cached_'):
261 if cache_key.startswith('get_repo_cached_'):
262 name = cache_key.split('get_repo_cached_')[-1]
262 name = cache_key.split('get_repo_cached_')[-1]
263 ScmModel().mark_for_invalidation(name)
263 ScmModel().mark_for_invalidation(name)
264
264
265 class EmptyChangeset(BaseChangeset):
265 class EmptyChangeset(BaseChangeset):
266 """
266 """
267 An dummy empty changeset. It's possible to pass hash when creating
267 An dummy empty changeset. It's possible to pass hash when creating
268 an EmptyChangeset
268 an EmptyChangeset
269 """
269 """
270
270
271 def __init__(self, cs='0' * 40):
271 def __init__(self, cs='0' * 40):
272 self._empty_cs = cs
272 self._empty_cs = cs
273 self.revision = -1
273 self.revision = -1
274 self.message = ''
274 self.message = ''
275 self.author = ''
275 self.author = ''
276 self.date = ''
276 self.date = ''
277
277
278 @LazyProperty
278 @LazyProperty
279 def raw_id(self):
279 def raw_id(self):
280 """Returns raw string identifying this changeset, useful for web
280 """Returns raw string identifying this changeset, useful for web
281 representation.
281 representation.
282 """
282 """
283
283
284 return self._empty_cs
284 return self._empty_cs
285
285
286 @LazyProperty
286 @LazyProperty
287 def short_id(self):
287 def short_id(self):
288 return self.raw_id[:12]
288 return self.raw_id[:12]
289
289
290 def get_file_changeset(self, path):
290 def get_file_changeset(self, path):
291 return self
291 return self
292
292
293 def get_file_content(self, path):
293 def get_file_content(self, path):
294 return u''
294 return u''
295
295
296 def get_file_size(self, path):
296 def get_file_size(self, path):
297 return 0
297 return 0
298
298
299 def map_groups(groups):
300 """Checks for groups existence, and creates groups structures.
301 It returns last group in structure
302
303 :param groups: list of groups structure
304 """
305 sa = meta.Session()
306
307 parent = None
308 group = None
309 for lvl, group_name in enumerate(groups[:-1]):
310 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
311
312 if group is None:
313 group = Group(group_name, parent)
314 sa.add(group)
315 sa.commit()
316
317 parent = group
318
319 return group
320
299 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
321 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
300 """maps all found repositories into db
322 """maps all repos given in initial_repo_list, non existing repositories
323 are created, if remove_obsolete is True it also check for db entries
324 that are not in initial_repo_list and removes them.
325
326 :param initial_repo_list: list of repositories found by scanning methods
327 :param remove_obsolete: check for obsolete entries in database
301 """
328 """
302
329
303 sa = meta.Session()
330 sa = meta.Session()
304 rm = RepoModel()
331 rm = RepoModel()
305 user = sa.query(User).filter(User.admin == True).first()
332 user = sa.query(User).filter(User.admin == True).first()
306
333
307 for name, repo in initial_repo_list.items():
334 for name, repo in initial_repo_list.items():
335 group = map_groups(name.split('/'))
308 if not rm.get_by_repo_name(name, cache=False):
336 if not rm.get_by_repo_name(name, cache=False):
309 log.info('repository %s not found creating default', name)
337 log.info('repository %s not found creating default', name)
310
338
311 form_data = {
339 form_data = {
312 'repo_name':name,
340 'repo_name':name,
313 'repo_type':repo.alias,
341 'repo_type':repo.alias,
314 'description':repo.description \
342 'description':repo.description \
315 if repo.description != 'unknown' else \
343 if repo.description != 'unknown' else \
316 '%s repository' % name,
344 '%s repository' % name,
317 'private':False
345 'private':False,
346 'group_id':getattr(group, 'group_id', None)
318 }
347 }
319 rm.create(form_data, user, just_db=True)
348 rm.create(form_data, user, just_db=True)
320
349
321 if remove_obsolete:
350 if remove_obsolete:
322 #remove from database those repositories that are not in the filesystem
351 #remove from database those repositories that are not in the filesystem
323 for repo in sa.query(Repository).all():
352 for repo in sa.query(Repository).all():
324 if repo.repo_name not in initial_repo_list.keys():
353 if repo.repo_name not in initial_repo_list.keys():
325 sa.delete(repo)
354 sa.delete(repo)
326 sa.commit()
355 sa.commit()
327
356
328 class OrderedDict(dict, DictMixin):
357 class OrderedDict(dict, DictMixin):
329
358
330 def __init__(self, *args, **kwds):
359 def __init__(self, *args, **kwds):
331 if len(args) > 1:
360 if len(args) > 1:
332 raise TypeError('expected at most 1 arguments, got %d' % len(args))
361 raise TypeError('expected at most 1 arguments, got %d' % len(args))
333 try:
362 try:
334 self.__end
363 self.__end
335 except AttributeError:
364 except AttributeError:
336 self.clear()
365 self.clear()
337 self.update(*args, **kwds)
366 self.update(*args, **kwds)
338
367
339 def clear(self):
368 def clear(self):
340 self.__end = end = []
369 self.__end = end = []
341 end += [None, end, end] # sentinel node for doubly linked list
370 end += [None, end, end] # sentinel node for doubly linked list
342 self.__map = {} # key --> [key, prev, next]
371 self.__map = {} # key --> [key, prev, next]
343 dict.clear(self)
372 dict.clear(self)
344
373
345 def __setitem__(self, key, value):
374 def __setitem__(self, key, value):
346 if key not in self:
375 if key not in self:
347 end = self.__end
376 end = self.__end
348 curr = end[1]
377 curr = end[1]
349 curr[2] = end[1] = self.__map[key] = [key, curr, end]
378 curr[2] = end[1] = self.__map[key] = [key, curr, end]
350 dict.__setitem__(self, key, value)
379 dict.__setitem__(self, key, value)
351
380
352 def __delitem__(self, key):
381 def __delitem__(self, key):
353 dict.__delitem__(self, key)
382 dict.__delitem__(self, key)
354 key, prev, next = self.__map.pop(key)
383 key, prev, next = self.__map.pop(key)
355 prev[2] = next
384 prev[2] = next
356 next[1] = prev
385 next[1] = prev
357
386
358 def __iter__(self):
387 def __iter__(self):
359 end = self.__end
388 end = self.__end
360 curr = end[2]
389 curr = end[2]
361 while curr is not end:
390 while curr is not end:
362 yield curr[0]
391 yield curr[0]
363 curr = curr[2]
392 curr = curr[2]
364
393
365 def __reversed__(self):
394 def __reversed__(self):
366 end = self.__end
395 end = self.__end
367 curr = end[1]
396 curr = end[1]
368 while curr is not end:
397 while curr is not end:
369 yield curr[0]
398 yield curr[0]
370 curr = curr[1]
399 curr = curr[1]
371
400
372 def popitem(self, last=True):
401 def popitem(self, last=True):
373 if not self:
402 if not self:
374 raise KeyError('dictionary is empty')
403 raise KeyError('dictionary is empty')
375 if last:
404 if last:
376 key = reversed(self).next()
405 key = reversed(self).next()
377 else:
406 else:
378 key = iter(self).next()
407 key = iter(self).next()
379 value = self.pop(key)
408 value = self.pop(key)
380 return key, value
409 return key, value
381
410
382 def __reduce__(self):
411 def __reduce__(self):
383 items = [[k, self[k]] for k in self]
412 items = [[k, self[k]] for k in self]
384 tmp = self.__map, self.__end
413 tmp = self.__map, self.__end
385 del self.__map, self.__end
414 del self.__map, self.__end
386 inst_dict = vars(self).copy()
415 inst_dict = vars(self).copy()
387 self.__map, self.__end = tmp
416 self.__map, self.__end = tmp
388 if inst_dict:
417 if inst_dict:
389 return (self.__class__, (items,), inst_dict)
418 return (self.__class__, (items,), inst_dict)
390 return self.__class__, (items,)
419 return self.__class__, (items,)
391
420
392 def keys(self):
421 def keys(self):
393 return list(self)
422 return list(self)
394
423
395 setdefault = DictMixin.setdefault
424 setdefault = DictMixin.setdefault
396 update = DictMixin.update
425 update = DictMixin.update
397 pop = DictMixin.pop
426 pop = DictMixin.pop
398 values = DictMixin.values
427 values = DictMixin.values
399 items = DictMixin.items
428 items = DictMixin.items
400 iterkeys = DictMixin.iterkeys
429 iterkeys = DictMixin.iterkeys
401 itervalues = DictMixin.itervalues
430 itervalues = DictMixin.itervalues
402 iteritems = DictMixin.iteritems
431 iteritems = DictMixin.iteritems
403
432
404 def __repr__(self):
433 def __repr__(self):
405 if not self:
434 if not self:
406 return '%s()' % (self.__class__.__name__,)
435 return '%s()' % (self.__class__.__name__,)
407 return '%s(%r)' % (self.__class__.__name__, self.items())
436 return '%s(%r)' % (self.__class__.__name__, self.items())
408
437
409 def copy(self):
438 def copy(self):
410 return self.__class__(self)
439 return self.__class__(self)
411
440
412 @classmethod
441 @classmethod
413 def fromkeys(cls, iterable, value=None):
442 def fromkeys(cls, iterable, value=None):
414 d = cls()
443 d = cls()
415 for key in iterable:
444 for key in iterable:
416 d[key] = value
445 d[key] = value
417 return d
446 return d
418
447
419 def __eq__(self, other):
448 def __eq__(self, other):
420 if isinstance(other, OrderedDict):
449 if isinstance(other, OrderedDict):
421 return len(self) == len(other) and self.items() == other.items()
450 return len(self) == len(other) and self.items() == other.items()
422 return dict.__eq__(self, other)
451 return dict.__eq__(self, other)
423
452
424 def __ne__(self, other):
453 def __ne__(self, other):
425 return not self == other
454 return not self == other
426
455
427
456
428 #set cache regions for beaker so celery can utilise it
457 #set cache regions for beaker so celery can utilise it
429 def add_cache(settings):
458 def add_cache(settings):
430 cache_settings = {'regions':None}
459 cache_settings = {'regions':None}
431 for key in settings.keys():
460 for key in settings.keys():
432 for prefix in ['beaker.cache.', 'cache.']:
461 for prefix in ['beaker.cache.', 'cache.']:
433 if key.startswith(prefix):
462 if key.startswith(prefix):
434 name = key.split(prefix)[1].strip()
463 name = key.split(prefix)[1].strip()
435 cache_settings[name] = settings[key].strip()
464 cache_settings[name] = settings[key].strip()
436 if cache_settings['regions']:
465 if cache_settings['regions']:
437 for region in cache_settings['regions'].split(','):
466 for region in cache_settings['regions'].split(','):
438 region = region.strip()
467 region = region.strip()
439 region_settings = {}
468 region_settings = {}
440 for key, value in cache_settings.items():
469 for key, value in cache_settings.items():
441 if key.startswith(region):
470 if key.startswith(region):
442 region_settings[key.split('.')[1]] = value
471 region_settings[key.split('.')[1]] = value
443 region_settings['expire'] = int(region_settings.get('expire',
472 region_settings['expire'] = int(region_settings.get('expire',
444 60))
473 60))
445 region_settings.setdefault('lock_dir',
474 region_settings.setdefault('lock_dir',
446 cache_settings.get('lock_dir'))
475 cache_settings.get('lock_dir'))
447 if 'type' not in region_settings:
476 if 'type' not in region_settings:
448 region_settings['type'] = cache_settings.get('type',
477 region_settings['type'] = cache_settings.get('type',
449 'memory')
478 'memory')
450 beaker.cache.cache_regions[region] = region_settings
479 beaker.cache.cache_regions[region] = region_settings
451
480
452 def get_current_revision():
481 def get_current_revision():
453 """Returns tuple of (number, id) from repository containing this package
482 """Returns tuple of (number, id) from repository containing this package
454 or None if repository could not be found.
483 or None if repository could not be found.
455 """
484 """
456
485
457 try:
486 try:
458 from vcs import get_repo
487 from vcs import get_repo
459 from vcs.utils.helpers import get_scm
488 from vcs.utils.helpers import get_scm
460 from vcs.exceptions import RepositoryError, VCSError
489 from vcs.exceptions import RepositoryError, VCSError
461 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
490 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
462 scm = get_scm(repopath)[0]
491 scm = get_scm(repopath)[0]
463 repo = get_repo(path=repopath, alias=scm)
492 repo = get_repo(path=repopath, alias=scm)
464 tip = repo.get_changeset()
493 tip = repo.get_changeset()
465 return (tip.revision, tip.short_id)
494 return (tip.revision, tip.short_id)
466 except (ImportError, RepositoryError, VCSError), err:
495 except (ImportError, RepositoryError, VCSError), err:
467 logging.debug("Cannot retrieve rhodecode's revision. Original error "
496 logging.debug("Cannot retrieve rhodecode's revision. Original error "
468 "was: %s" % err)
497 "was: %s" % err)
469 return None
498 return None
470
499
471 #===============================================================================
500 #===============================================================================
472 # TEST FUNCTIONS AND CREATORS
501 # TEST FUNCTIONS AND CREATORS
473 #===============================================================================
502 #===============================================================================
474 def create_test_index(repo_location, full_index):
503 def create_test_index(repo_location, full_index):
475 """Makes default test index
504 """Makes default test index
476 :param repo_location:
505 :param repo_location:
477 :param full_index:
506 :param full_index:
478 """
507 """
479 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
508 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
480 from rhodecode.lib.pidlock import DaemonLock, LockHeld
509 from rhodecode.lib.pidlock import DaemonLock, LockHeld
481 import shutil
510 import shutil
482
511
483 index_location = os.path.join(repo_location, 'index')
512 index_location = os.path.join(repo_location, 'index')
484 if os.path.exists(index_location):
513 if os.path.exists(index_location):
485 shutil.rmtree(index_location)
514 shutil.rmtree(index_location)
486
515
487 try:
516 try:
488 l = DaemonLock()
517 l = DaemonLock()
489 WhooshIndexingDaemon(index_location=index_location,
518 WhooshIndexingDaemon(index_location=index_location,
490 repo_location=repo_location)\
519 repo_location=repo_location)\
491 .run(full_index=full_index)
520 .run(full_index=full_index)
492 l.release()
521 l.release()
493 except LockHeld:
522 except LockHeld:
494 pass
523 pass
495
524
496 def create_test_env(repos_test_path, config):
525 def create_test_env(repos_test_path, config):
497 """Makes a fresh database and
526 """Makes a fresh database and
498 install test repository into tmp dir
527 install test repository into tmp dir
499 """
528 """
500 from rhodecode.lib.db_manage import DbManage
529 from rhodecode.lib.db_manage import DbManage
501 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
530 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
502 HG_FORK, GIT_FORK, TESTS_TMP_PATH
531 HG_FORK, GIT_FORK, TESTS_TMP_PATH
503 import tarfile
532 import tarfile
504 import shutil
533 import shutil
505 from os.path import dirname as dn, join as jn, abspath
534 from os.path import dirname as dn, join as jn, abspath
506
535
507 log = logging.getLogger('TestEnvCreator')
536 log = logging.getLogger('TestEnvCreator')
508 # create logger
537 # create logger
509 log.setLevel(logging.DEBUG)
538 log.setLevel(logging.DEBUG)
510 log.propagate = True
539 log.propagate = True
511 # create console handler and set level to debug
540 # create console handler and set level to debug
512 ch = logging.StreamHandler()
541 ch = logging.StreamHandler()
513 ch.setLevel(logging.DEBUG)
542 ch.setLevel(logging.DEBUG)
514
543
515 # create formatter
544 # create formatter
516 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
545 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
517
546
518 # add formatter to ch
547 # add formatter to ch
519 ch.setFormatter(formatter)
548 ch.setFormatter(formatter)
520
549
521 # add ch to logger
550 # add ch to logger
522 log.addHandler(ch)
551 log.addHandler(ch)
523
552
524 #PART ONE create db
553 #PART ONE create db
525 dbconf = config['sqlalchemy.db1.url']
554 dbconf = config['sqlalchemy.db1.url']
526 log.debug('making test db %s', dbconf)
555 log.debug('making test db %s', dbconf)
527
556
528 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
557 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
529 tests=True)
558 tests=True)
530 dbmanage.create_tables(override=True)
559 dbmanage.create_tables(override=True)
531 dbmanage.config_prompt(repos_test_path)
560 dbmanage.config_prompt(repos_test_path)
532 dbmanage.create_default_user()
561 dbmanage.create_default_user()
533 dbmanage.admin_prompt()
562 dbmanage.admin_prompt()
534 dbmanage.create_permissions()
563 dbmanage.create_permissions()
535 dbmanage.populate_default_permissions()
564 dbmanage.populate_default_permissions()
536
565
537 #PART TWO make test repo
566 #PART TWO make test repo
538 log.debug('making test vcs repositories')
567 log.debug('making test vcs repositories')
539
568
540 #remove old one from previos tests
569 #remove old one from previos tests
541 for r in [HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, HG_FORK, GIT_FORK]:
570 for r in [HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, HG_FORK, GIT_FORK]:
542
571
543 if os.path.isdir(jn(TESTS_TMP_PATH, r)):
572 if os.path.isdir(jn(TESTS_TMP_PATH, r)):
544 log.debug('removing %s', r)
573 log.debug('removing %s', r)
545 shutil.rmtree(jn(TESTS_TMP_PATH, r))
574 shutil.rmtree(jn(TESTS_TMP_PATH, r))
546
575
547 #CREATE DEFAULT HG REPOSITORY
576 #CREATE DEFAULT HG REPOSITORY
548 cur_dir = dn(dn(abspath(__file__)))
577 cur_dir = dn(dn(abspath(__file__)))
549 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
578 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
550 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
579 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
551 tar.close()
580 tar.close()
552
581
553
582
554 #==============================================================================
583 #==============================================================================
555 # PASTER COMMANDS
584 # PASTER COMMANDS
556 #==============================================================================
585 #==============================================================================
557
586
558 class BasePasterCommand(Command):
587 class BasePasterCommand(Command):
559 """
588 """
560 Abstract Base Class for paster commands.
589 Abstract Base Class for paster commands.
561
590
562 The celery commands are somewhat aggressive about loading
591 The celery commands are somewhat aggressive about loading
563 celery.conf, and since our module sets the `CELERY_LOADER`
592 celery.conf, and since our module sets the `CELERY_LOADER`
564 environment variable to our loader, we have to bootstrap a bit and
593 environment variable to our loader, we have to bootstrap a bit and
565 make sure we've had a chance to load the pylons config off of the
594 make sure we've had a chance to load the pylons config off of the
566 command line, otherwise everything fails.
595 command line, otherwise everything fails.
567 """
596 """
568 min_args = 1
597 min_args = 1
569 min_args_error = "Please provide a paster config file as an argument."
598 min_args_error = "Please provide a paster config file as an argument."
570 takes_config_file = 1
599 takes_config_file = 1
571 requires_config_file = True
600 requires_config_file = True
572
601
573 def notify_msg(self, msg, log=False):
602 def notify_msg(self, msg, log=False):
574 """Make a notification to user, additionally if logger is passed
603 """Make a notification to user, additionally if logger is passed
575 it logs this action using given logger
604 it logs this action using given logger
576
605
577 :param msg: message that will be printed to user
606 :param msg: message that will be printed to user
578 :param log: logging instance, to use to additionally log this message
607 :param log: logging instance, to use to additionally log this message
579
608
580 """
609 """
581 print msg
610 print msg
582 if log and isinstance(log, logging):
611 if log and isinstance(log, logging):
583 log(msg)
612 log(msg)
584
613
585
614
586 def run(self, args):
615 def run(self, args):
587 """
616 """
588 Overrides Command.run
617 Overrides Command.run
589
618
590 Checks for a config file argument and loads it.
619 Checks for a config file argument and loads it.
591 """
620 """
592 if len(args) < self.min_args:
621 if len(args) < self.min_args:
593 raise BadCommand(
622 raise BadCommand(
594 self.min_args_error % {'min_args': self.min_args,
623 self.min_args_error % {'min_args': self.min_args,
595 'actual_args': len(args)})
624 'actual_args': len(args)})
596
625
597 # Decrement because we're going to lob off the first argument.
626 # Decrement because we're going to lob off the first argument.
598 # @@ This is hacky
627 # @@ This is hacky
599 self.min_args -= 1
628 self.min_args -= 1
600 self.bootstrap_config(args[0])
629 self.bootstrap_config(args[0])
601 self.update_parser()
630 self.update_parser()
602 return super(BasePasterCommand, self).run(args[1:])
631 return super(BasePasterCommand, self).run(args[1:])
603
632
604 def update_parser(self):
633 def update_parser(self):
605 """
634 """
606 Abstract method. Allows for the class's parser to be updated
635 Abstract method. Allows for the class's parser to be updated
607 before the superclass's `run` method is called. Necessary to
636 before the superclass's `run` method is called. Necessary to
608 allow options/arguments to be passed through to the underlying
637 allow options/arguments to be passed through to the underlying
609 celery command.
638 celery command.
610 """
639 """
611 raise NotImplementedError("Abstract Method.")
640 raise NotImplementedError("Abstract Method.")
612
641
613 def bootstrap_config(self, conf):
642 def bootstrap_config(self, conf):
614 """
643 """
615 Loads the pylons configuration.
644 Loads the pylons configuration.
616 """
645 """
617 from pylons import config as pylonsconfig
646 from pylons import config as pylonsconfig
618
647
619 path_to_ini_file = os.path.realpath(conf)
648 path_to_ini_file = os.path.realpath(conf)
620 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
649 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
621 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
650 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
@@ -1,257 +1,282 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) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 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
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
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, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27 import logging
27 import logging
28 import datetime
28 import datetime
29
29
30 from sqlalchemy import *
30 from sqlalchemy import *
31 from sqlalchemy.exc import DatabaseError
31 from sqlalchemy.exc import DatabaseError
32 from sqlalchemy.orm import relation, backref, class_mapper
32 from sqlalchemy.orm import relation, backref, class_mapper
33 from sqlalchemy.orm.session import Session
33 from sqlalchemy.orm.session import Session
34
34
35 from rhodecode.model.meta import Base
35 from rhodecode.model.meta import Base
36
36
37 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
38
38
39 class BaseModel(object):
39 class BaseModel(object):
40
40
41 @classmethod
41 @classmethod
42 def _get_keys(cls):
42 def _get_keys(cls):
43 """return column names for this model """
43 """return column names for this model """
44 return class_mapper(cls).c.keys()
44 return class_mapper(cls).c.keys()
45
45
46 def get_dict(self):
46 def get_dict(self):
47 """return dict with keys and values corresponding
47 """return dict with keys and values corresponding
48 to this model data """
48 to this model data """
49
49
50 d = {}
50 d = {}
51 for k in self._get_keys():
51 for k in self._get_keys():
52 d[k] = getattr(self, k)
52 d[k] = getattr(self, k)
53 return d
53 return d
54
54
55 def get_appstruct(self):
55 def get_appstruct(self):
56 """return list with keys and values tupples corresponding
56 """return list with keys and values tupples corresponding
57 to this model data """
57 to this model data """
58
58
59 l = []
59 l = []
60 for k in self._get_keys():
60 for k in self._get_keys():
61 l.append((k, getattr(self, k),))
61 l.append((k, getattr(self, k),))
62 return l
62 return l
63
63
64 def populate_obj(self, populate_dict):
64 def populate_obj(self, populate_dict):
65 """populate model with data from given populate_dict"""
65 """populate model with data from given populate_dict"""
66
66
67 for k in self._get_keys():
67 for k in self._get_keys():
68 if k in populate_dict:
68 if k in populate_dict:
69 setattr(self, k, populate_dict[k])
69 setattr(self, k, populate_dict[k])
70
70
71 class RhodeCodeSettings(Base, BaseModel):
71 class RhodeCodeSettings(Base, BaseModel):
72 __tablename__ = 'rhodecode_settings'
72 __tablename__ = 'rhodecode_settings'
73 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
73 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
74 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
74 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
75 app_settings_name = Column("app_settings_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
75 app_settings_name = Column("app_settings_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
76 app_settings_value = Column("app_settings_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
76 app_settings_value = Column("app_settings_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
77
77
78 def __init__(self, k, v):
78 def __init__(self, k, v):
79 self.app_settings_name = k
79 self.app_settings_name = k
80 self.app_settings_value = v
80 self.app_settings_value = v
81
81
82 def __repr__(self):
82 def __repr__(self):
83 return "<RhodeCodeSetting('%s:%s')>" % (self.app_settings_name,
83 return "<%s('%s:%s')>" % (self.__class__.__name__,
84 self.app_settings_value)
84 self.app_settings_name, self.app_settings_value)
85
85
86 class RhodeCodeUi(Base, BaseModel):
86 class RhodeCodeUi(Base, BaseModel):
87 __tablename__ = 'rhodecode_ui'
87 __tablename__ = 'rhodecode_ui'
88 __table_args__ = {'useexisting':True}
88 __table_args__ = {'useexisting':True}
89 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
89 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
90 ui_section = Column("ui_section", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
90 ui_section = Column("ui_section", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
91 ui_key = Column("ui_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
91 ui_key = Column("ui_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
92 ui_value = Column("ui_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
92 ui_value = Column("ui_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
93 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
93 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
94
94
95
95
96 class User(Base, BaseModel):
96 class User(Base, BaseModel):
97 __tablename__ = 'users'
97 __tablename__ = 'users'
98 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
98 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
99 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
99 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
100 username = Column("username", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
100 username = Column("username", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
101 password = Column("password", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
101 password = Column("password", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
102 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
102 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
103 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
103 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
104 name = Column("name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
104 name = Column("name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
105 lastname = Column("lastname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
105 lastname = Column("lastname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
106 email = Column("email", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
106 email = Column("email", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
107 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
107 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
108 is_ldap = Column("is_ldap", Boolean(), nullable=False, unique=None, default=False)
108 is_ldap = Column("is_ldap", Boolean(), nullable=False, unique=None, default=False)
109
109
110 user_log = relation('UserLog', cascade='all')
110 user_log = relation('UserLog', cascade='all')
111 user_perms = relation('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
111 user_perms = relation('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
112
112
113 repositories = relation('Repository')
113 repositories = relation('Repository')
114 user_followers = relation('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
114 user_followers = relation('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
115
115
116 @property
116 @property
117 def full_contact(self):
117 def full_contact(self):
118 return '%s %s <%s>' % (self.name, self.lastname, self.email)
118 return '%s %s <%s>' % (self.name, self.lastname, self.email)
119
119
120 def __repr__(self):
120 def __repr__(self):
121 return "<User('id:%s:%s')>" % (self.user_id, self.username)
121 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
122 self.user_id, self.username)
122
123
123 def update_lastlogin(self):
124 def update_lastlogin(self):
124 """Update user lastlogin"""
125 """Update user lastlogin"""
125
126
126 try:
127 try:
127 session = Session.object_session(self)
128 session = Session.object_session(self)
128 self.last_login = datetime.datetime.now()
129 self.last_login = datetime.datetime.now()
129 session.add(self)
130 session.add(self)
130 session.commit()
131 session.commit()
131 log.debug('updated user %s lastlogin', self.username)
132 log.debug('updated user %s lastlogin', self.username)
132 except (DatabaseError,):
133 except (DatabaseError,):
133 session.rollback()
134 session.rollback()
134
135
135
136
136 class UserLog(Base, BaseModel):
137 class UserLog(Base, BaseModel):
137 __tablename__ = 'user_logs'
138 __tablename__ = 'user_logs'
138 __table_args__ = {'useexisting':True}
139 __table_args__ = {'useexisting':True}
139 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
140 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
140 user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
141 user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
141 repository_id = Column("repository_id", Integer(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
142 repository_id = Column("repository_id", Integer(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
142 repository_name = Column("repository_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
143 repository_name = Column("repository_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
143 user_ip = Column("user_ip", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
144 user_ip = Column("user_ip", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
144 action = Column("action", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
145 action = Column("action", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
145 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
146 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
146
147
147 user = relation('User')
148 user = relation('User')
148 repository = relation('Repository')
149 repository = relation('Repository')
149
150
150 class Repository(Base, BaseModel):
151 class Repository(Base, BaseModel):
151 __tablename__ = 'repositories'
152 __tablename__ = 'repositories'
152 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
153 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
153 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
154 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
154 repo_name = Column("repo_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
155 repo_name = Column("repo_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
155 repo_type = Column("repo_type", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
156 repo_type = Column("repo_type", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
156 user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=False, default=None)
157 user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=False, default=None)
157 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
158 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
158 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
159 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
159 description = Column("description", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
160 description = Column("description", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
160 fork_id = Column("fork_id", Integer(), ForeignKey(u'repositories.repo_id'), nullable=True, unique=False, default=None)
161 fork_id = Column("fork_id", Integer(), ForeignKey(u'repositories.repo_id'), nullable=True, unique=False, default=None)
162 group_id = Column("group_id", Integer(), ForeignKey(u'groups.group_id'), nullable=True, unique=False, default=None)
161
163
162 user = relation('User')
164 user = relation('User')
163 fork = relation('Repository', remote_side=repo_id)
165 fork = relation('Repository', remote_side=repo_id)
166 group = relation('Group')
164 repo_to_perm = relation('RepoToPerm', cascade='all')
167 repo_to_perm = relation('RepoToPerm', cascade='all')
165 stats = relation('Statistics', cascade='all', uselist=False)
168 stats = relation('Statistics', cascade='all', uselist=False)
166
169
167 repo_followers = relation('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
170 repo_followers = relation('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
168
171
169
172
170 def __repr__(self):
173 def __repr__(self):
171 return "<Repository('%s:%s')>" % (self.repo_id, self.repo_name)
174 return "<%s('%s:%s')>" % (self.__class__.__name__,
175 self.repo_id, self.repo_name)
176
177 class Group(Base, BaseModel):
178 __tablename__ = 'groups'
179 __table_args__ = (UniqueConstraint('group_name'), {'useexisting':True},)
180
181 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
182 group_name = Column("group_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
183 group_parent_id = Column("group_parent_id", Integer(), ForeignKey(u'groups.group_id'), nullable=True, unique=None, default=None)
184
185 parent_group = relation('Group', remote_side=group_id)
186
187
188 def __init__(self, group_name='', parent_group=None):
189 self.group_name = group_name
190 self.parent_group = parent_group
191
192 def __repr__(self):
193 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
194 self.group_name)
172
195
173 class Permission(Base, BaseModel):
196 class Permission(Base, BaseModel):
174 __tablename__ = 'permissions'
197 __tablename__ = 'permissions'
175 __table_args__ = {'useexisting':True}
198 __table_args__ = {'useexisting':True}
176 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
199 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
177 permission_name = Column("permission_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
200 permission_name = Column("permission_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
178 permission_longname = Column("permission_longname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
201 permission_longname = Column("permission_longname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
179
202
180 def __repr__(self):
203 def __repr__(self):
181 return "<Permission('%s:%s')>" % (self.permission_id, self.permission_name)
204 return "<%s('%s:%s')>" % (self.__class__.__name__,
205 self.permission_id, self.permission_name)
182
206
183 class RepoToPerm(Base, BaseModel):
207 class RepoToPerm(Base, BaseModel):
184 __tablename__ = 'repo_to_perm'
208 __tablename__ = 'repo_to_perm'
185 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
209 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
186 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
210 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
187 user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
211 user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
188 permission_id = Column("permission_id", Integer(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
212 permission_id = Column("permission_id", Integer(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
189 repository_id = Column("repository_id", Integer(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
213 repository_id = Column("repository_id", Integer(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
190
214
191 user = relation('User')
215 user = relation('User')
192 permission = relation('Permission')
216 permission = relation('Permission')
193 repository = relation('Repository')
217 repository = relation('Repository')
194
218
195 class UserToPerm(Base, BaseModel):
219 class UserToPerm(Base, BaseModel):
196 __tablename__ = 'user_to_perm'
220 __tablename__ = 'user_to_perm'
197 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
221 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
198 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
222 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
199 user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
223 user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
200 permission_id = Column("permission_id", Integer(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
224 permission_id = Column("permission_id", Integer(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
201
225
202 user = relation('User')
226 user = relation('User')
203 permission = relation('Permission')
227 permission = relation('Permission')
204
228
205 class Statistics(Base, BaseModel):
229 class Statistics(Base, BaseModel):
206 __tablename__ = 'statistics'
230 __tablename__ = 'statistics'
207 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
231 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
208 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
232 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
209 repository_id = Column("repository_id", Integer(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=True, default=None)
233 repository_id = Column("repository_id", Integer(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=True, default=None)
210 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
234 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
211 commit_activity = Column("commit_activity", LargeBinary(), nullable=False)#JSON data
235 commit_activity = Column("commit_activity", LargeBinary(), nullable=False)#JSON data
212 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
236 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
213 languages = Column("languages", LargeBinary(), nullable=False)#JSON data
237 languages = Column("languages", LargeBinary(), nullable=False)#JSON data
214
238
215 repository = relation('Repository', single_parent=True)
239 repository = relation('Repository', single_parent=True)
216
240
217 class UserFollowing(Base, BaseModel):
241 class UserFollowing(Base, BaseModel):
218 __tablename__ = 'user_followings'
242 __tablename__ = 'user_followings'
219 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
243 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
220 UniqueConstraint('user_id', 'follows_user_id')
244 UniqueConstraint('user_id', 'follows_user_id')
221 , {'useexisting':True})
245 , {'useexisting':True})
222
246
223 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
247 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
224 user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
248 user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
225 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey(u'repositories.repo_id'), nullable=True, unique=None, default=None)
249 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey(u'repositories.repo_id'), nullable=True, unique=None, default=None)
226 follows_user_id = Column("follows_user_id", Integer(), ForeignKey(u'users.user_id'), nullable=True, unique=None, default=None)
250 follows_user_id = Column("follows_user_id", Integer(), ForeignKey(u'users.user_id'), nullable=True, unique=None, default=None)
227
251
228 user = relation('User', primaryjoin='User.user_id==UserFollowing.user_id')
252 user = relation('User', primaryjoin='User.user_id==UserFollowing.user_id')
229
253
230 follows_user = relation('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
254 follows_user = relation('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
231 follows_repository = relation('Repository')
255 follows_repository = relation('Repository')
232
256
233
257
234 class CacheInvalidation(Base, BaseModel):
258 class CacheInvalidation(Base, BaseModel):
235 __tablename__ = 'cache_invalidation'
259 __tablename__ = 'cache_invalidation'
236 __table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
260 __table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
237 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
261 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
238 cache_key = Column("cache_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
262 cache_key = Column("cache_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
239 cache_args = Column("cache_args", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
263 cache_args = Column("cache_args", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
240 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
264 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
241
265
242
266
243 def __init__(self, cache_key, cache_args=''):
267 def __init__(self, cache_key, cache_args=''):
244 self.cache_key = cache_key
268 self.cache_key = cache_key
245 self.cache_args = cache_args
269 self.cache_args = cache_args
246 self.cache_active = False
270 self.cache_active = False
247
271
248 def __repr__(self):
272 def __repr__(self):
249 return "<CacheInvalidation('%s:%s')>" % (self.cache_id, self.cache_key)
273 return "<%s('%s:%s')>" % (self.__class__.__name__,
274 self.cache_id, self.cache_key)
250
275
251 class DbMigrateVersion(Base, BaseModel):
276 class DbMigrateVersion(Base, BaseModel):
252 __tablename__ = 'db_migrate_version'
277 __tablename__ = 'db_migrate_version'
253 __table_args__ = {'useexisting':True}
278 __table_args__ = {'useexisting':True}
254 repository_id = Column('repository_id', String(250), primary_key=True)
279 repository_id = Column('repository_id', String(250), primary_key=True)
255 repository_path = Column('repository_path', Text)
280 repository_path = Column('repository_path', Text)
256 version = Column('version', Integer)
281 version = Column('version', Integer)
257
282
@@ -1,377 +1,377 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.scm
3 rhodecode.model.scm
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Scm model for RhodeCode
6 Scm model for RhodeCode
7
7
8 :created_on: Apr 9, 2010
8 :created_on: Apr 9, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 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
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
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, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27 import os
27 import os
28 import time
28 import time
29 import traceback
29 import traceback
30 import logging
30 import logging
31
31
32 from vcs import get_backend
32 from vcs import get_backend
33 from vcs.utils.helpers import get_scm
33 from vcs.utils.helpers import get_scm
34 from vcs.exceptions import RepositoryError, VCSError
34 from vcs.exceptions import RepositoryError, VCSError
35 from vcs.utils.lazy import LazyProperty
35 from vcs.utils.lazy import LazyProperty
36
36
37 from mercurial import ui
37 from mercurial import ui
38
38
39 from beaker.cache import cache_region, region_invalidate
39 from beaker.cache import cache_region, region_invalidate
40
40
41 from rhodecode import BACKENDS
41 from rhodecode import BACKENDS
42 from rhodecode.lib import helpers as h
42 from rhodecode.lib import helpers as h
43 from rhodecode.lib.auth import HasRepoPermissionAny
43 from rhodecode.lib.auth import HasRepoPermissionAny
44 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, action_logger
44 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, action_logger
45 from rhodecode.model import BaseModel
45 from rhodecode.model import BaseModel
46 from rhodecode.model.user import UserModel
46 from rhodecode.model.user import UserModel
47
47
48 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
48 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
49 UserFollowing, UserLog
49 UserFollowing, UserLog
50 from rhodecode.model.caching_query import FromCache
50 from rhodecode.model.caching_query import FromCache
51
51
52 from sqlalchemy.orm import joinedload
52 from sqlalchemy.orm import joinedload
53 from sqlalchemy.orm.session import make_transient
53 from sqlalchemy.orm.session import make_transient
54 from sqlalchemy.exc import DatabaseError
54 from sqlalchemy.exc import DatabaseError
55
55
56 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
57
57
58
58
59 class UserTemp(object):
59 class UserTemp(object):
60 def __init__(self, user_id):
60 def __init__(self, user_id):
61 self.user_id = user_id
61 self.user_id = user_id
62 class RepoTemp(object):
62 class RepoTemp(object):
63 def __init__(self, repo_id):
63 def __init__(self, repo_id):
64 self.repo_id = repo_id
64 self.repo_id = repo_id
65
65
66 class ScmModel(BaseModel):
66 class ScmModel(BaseModel):
67 """Generic Scm Model
67 """Generic Scm Model
68 """
68 """
69
69
70 @LazyProperty
70 @LazyProperty
71 def repos_path(self):
71 def repos_path(self):
72 """Get's the repositories root path from database
72 """Get's the repositories root path from database
73 """
73 """
74
74
75 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
75 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
76
76
77 return q.ui_value
77 return q.ui_value
78
78
79 def repo_scan(self, repos_path, baseui):
79 def repo_scan(self, repos_path, baseui):
80 """Listing of repositories in given path. This path should not be a
80 """Listing of repositories in given path. This path should not be a
81 repository itself. Return a dictionary of repository objects
81 repository itself. Return a dictionary of repository objects
82
82
83 :param repos_path: path to directory containing repositories
83 :param repos_path: path to directory containing repositories
84 :param baseui: baseui instance to instantiate MercurialRepostitory with
84 :param baseui: baseui instance to instantiate MercurialRepostitory with
85 """
85 """
86
86
87 log.info('scanning for repositories in %s', repos_path)
87 log.info('scanning for repositories in %s', repos_path)
88
88
89 if not isinstance(baseui, ui.ui):
89 if not isinstance(baseui, ui.ui):
90 baseui = make_ui('db')
90 baseui = make_ui('db')
91 repos_list = {}
91 repos_list = {}
92
92
93 for name, path in get_filesystem_repos(repos_path, recursive=True):
93 for name, path in get_filesystem_repos(repos_path, recursive=True):
94 try:
94 try:
95 if repos_list.has_key(name):
95 if repos_list.has_key(name):
96 raise RepositoryError('Duplicate repository name %s '
96 raise RepositoryError('Duplicate repository name %s '
97 'found in %s' % (name, path))
97 'found in %s' % (name, path))
98 else:
98 else:
99
99
100 klass = get_backend(path[0])
100 klass = get_backend(path[0])
101
101
102 if path[0] == 'hg' and path[0] in BACKENDS.keys():
102 if path[0] == 'hg' and path[0] in BACKENDS.keys():
103 repos_list[name] = klass(path[1], baseui=baseui)
103 repos_list[name] = klass(path[1], baseui=baseui)
104
104
105 if path[0] == 'git' and path[0] in BACKENDS.keys():
105 if path[0] == 'git' and path[0] in BACKENDS.keys():
106 repos_list[name] = klass(path[1])
106 repos_list[name] = klass(path[1])
107 except OSError:
107 except OSError:
108 continue
108 continue
109
109
110 return repos_list
110 return repos_list
111
111
112 def get_repos(self, all_repos=None):
112 def get_repos(self, all_repos=None):
113 """Get all repos from db and for each repo create it's backend instance.
113 """Get all repos from db and for each repo create it's backend instance.
114 and fill that backed with information from database
114 and fill that backed with information from database
115
115
116 :param all_repos: give specific repositories list, good for filtering
116 :param all_repos: give specific repositories list, good for filtering
117 """
117 """
118
118
119 if all_repos is None:
119 if all_repos is None:
120 all_repos = self.sa.query(Repository)\
120 all_repos = self.sa.query(Repository)\
121 .order_by(Repository.repo_name).all()
121 .order_by(Repository.repo_name).all()
122
122
123 #get the repositories that should be invalidated
123 #get the repositories that should be invalidated
124 invalidation_list = [str(x.cache_key) for x in \
124 invalidation_list = [str(x.cache_key) for x in \
125 self.sa.query(CacheInvalidation.cache_key)\
125 self.sa.query(CacheInvalidation.cache_key)\
126 .filter(CacheInvalidation.cache_active == False)\
126 .filter(CacheInvalidation.cache_active == False)\
127 .all()]
127 .all()]
128
128
129 for r in all_repos:
129 for r in all_repos:
130
130
131 repo = self.get(r.repo_name, invalidation_list)
131 repo = self.get(r.repo_name, invalidation_list)
132
132
133 if repo is not None:
133 if repo is not None:
134 last_change = repo.last_change
134 last_change = repo.last_change
135 tip = h.get_changeset_safe(repo, 'tip')
135 tip = h.get_changeset_safe(repo, 'tip')
136
136
137 tmp_d = {}
137 tmp_d = {}
138 tmp_d['name'] = repo.name
138 tmp_d['name'] = r.repo_name
139 tmp_d['name_sort'] = tmp_d['name'].lower()
139 tmp_d['name_sort'] = tmp_d['name'].lower()
140 tmp_d['description'] = repo.dbrepo.description
140 tmp_d['description'] = repo.dbrepo.description
141 tmp_d['description_sort'] = tmp_d['description']
141 tmp_d['description_sort'] = tmp_d['description']
142 tmp_d['last_change'] = last_change
142 tmp_d['last_change'] = last_change
143 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
143 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
144 tmp_d['tip'] = tip.raw_id
144 tmp_d['tip'] = tip.raw_id
145 tmp_d['tip_sort'] = tip.revision
145 tmp_d['tip_sort'] = tip.revision
146 tmp_d['rev'] = tip.revision
146 tmp_d['rev'] = tip.revision
147 tmp_d['contact'] = repo.dbrepo.user.full_contact
147 tmp_d['contact'] = repo.dbrepo.user.full_contact
148 tmp_d['contact_sort'] = tmp_d['contact']
148 tmp_d['contact_sort'] = tmp_d['contact']
149 tmp_d['repo_archives'] = list(repo._get_archives())
149 tmp_d['repo_archives'] = list(repo._get_archives())
150 tmp_d['last_msg'] = tip.message
150 tmp_d['last_msg'] = tip.message
151 tmp_d['repo'] = repo
151 tmp_d['repo'] = repo
152 yield tmp_d
152 yield tmp_d
153
153
154 def get_repo(self, repo_name):
154 def get_repo(self, repo_name):
155 return self.get(repo_name)
155 return self.get(repo_name)
156
156
157 def get(self, repo_name, invalidation_list=None):
157 def get(self, repo_name, invalidation_list=None):
158 """Get's repository from given name, creates BackendInstance and
158 """Get's repository from given name, creates BackendInstance and
159 propagates it's data from database with all additional information
159 propagates it's data from database with all additional information
160
160
161 :param repo_name:
161 :param repo_name:
162 :param invalidation_list: if a invalidation list is given the get
162 :param invalidation_list: if a invalidation list is given the get
163 method should not manually check if this repository needs
163 method should not manually check if this repository needs
164 invalidation and just invalidate the repositories in list
164 invalidation and just invalidate the repositories in list
165
165
166 """
166 """
167 if not HasRepoPermissionAny('repository.read', 'repository.write',
167 if not HasRepoPermissionAny('repository.read', 'repository.write',
168 'repository.admin')(repo_name, 'get repo check'):
168 'repository.admin')(repo_name, 'get repo check'):
169 return
169 return
170
170
171 #======================================================================
171 #======================================================================
172 # CACHE FUNCTION
172 # CACHE FUNCTION
173 #======================================================================
173 #======================================================================
174 @cache_region('long_term')
174 @cache_region('long_term')
175 def _get_repo(repo_name):
175 def _get_repo(repo_name):
176
176
177 repo_path = os.path.join(self.repos_path, repo_name)
177 repo_path = os.path.join(self.repos_path, repo_name)
178
178
179 try:
179 try:
180 alias = get_scm(repo_path)[0]
180 alias = get_scm(repo_path)[0]
181
181
182 log.debug('Creating instance of %s repository', alias)
182 log.debug('Creating instance of %s repository', alias)
183 backend = get_backend(alias)
183 backend = get_backend(alias)
184 except VCSError:
184 except VCSError:
185 log.error(traceback.format_exc())
185 log.error(traceback.format_exc())
186 return
186 return
187
187
188 if alias == 'hg':
188 if alias == 'hg':
189 from pylons import app_globals as g
189 from pylons import app_globals as g
190 repo = backend(repo_path, create=False, baseui=g.baseui)
190 repo = backend(repo_path, create=False, baseui=g.baseui)
191 #skip hidden web repository
191 #skip hidden web repository
192 if repo._get_hidden():
192 if repo._get_hidden():
193 return
193 return
194 else:
194 else:
195 repo = backend(repo_path, create=False)
195 repo = backend(repo_path, create=False)
196
196
197 dbrepo = self.sa.query(Repository)\
197 dbrepo = self.sa.query(Repository)\
198 .options(joinedload(Repository.fork))\
198 .options(joinedload(Repository.fork))\
199 .options(joinedload(Repository.user))\
199 .options(joinedload(Repository.user))\
200 .filter(Repository.repo_name == repo_name)\
200 .filter(Repository.repo_name == repo_name)\
201 .scalar()
201 .scalar()
202
202
203 make_transient(dbrepo)
203 make_transient(dbrepo)
204 if dbrepo.user:
204 if dbrepo.user:
205 make_transient(dbrepo.user)
205 make_transient(dbrepo.user)
206 if dbrepo.fork:
206 if dbrepo.fork:
207 make_transient(dbrepo.fork)
207 make_transient(dbrepo.fork)
208
208
209 repo.dbrepo = dbrepo
209 repo.dbrepo = dbrepo
210 return repo
210 return repo
211
211
212 pre_invalidate = True
212 pre_invalidate = True
213 if invalidation_list is not None:
213 if invalidation_list is not None:
214 pre_invalidate = repo_name in invalidation_list
214 pre_invalidate = repo_name in invalidation_list
215
215
216 if pre_invalidate:
216 if pre_invalidate:
217 invalidate = self._should_invalidate(repo_name)
217 invalidate = self._should_invalidate(repo_name)
218
218
219 if invalidate:
219 if invalidate:
220 log.info('invalidating cache for repository %s', repo_name)
220 log.info('invalidating cache for repository %s', repo_name)
221 region_invalidate(_get_repo, None, repo_name)
221 region_invalidate(_get_repo, None, repo_name)
222 self._mark_invalidated(invalidate)
222 self._mark_invalidated(invalidate)
223
223
224 return _get_repo(repo_name)
224 return _get_repo(repo_name)
225
225
226
226
227
227
228 def mark_for_invalidation(self, repo_name):
228 def mark_for_invalidation(self, repo_name):
229 """Puts cache invalidation task into db for
229 """Puts cache invalidation task into db for
230 further global cache invalidation
230 further global cache invalidation
231
231
232 :param repo_name: this repo that should invalidation take place
232 :param repo_name: this repo that should invalidation take place
233 """
233 """
234
234
235 log.debug('marking %s for invalidation', repo_name)
235 log.debug('marking %s for invalidation', repo_name)
236 cache = self.sa.query(CacheInvalidation)\
236 cache = self.sa.query(CacheInvalidation)\
237 .filter(CacheInvalidation.cache_key == repo_name).scalar()
237 .filter(CacheInvalidation.cache_key == repo_name).scalar()
238
238
239 if cache:
239 if cache:
240 #mark this cache as inactive
240 #mark this cache as inactive
241 cache.cache_active = False
241 cache.cache_active = False
242 else:
242 else:
243 log.debug('cache key not found in invalidation db -> creating one')
243 log.debug('cache key not found in invalidation db -> creating one')
244 cache = CacheInvalidation(repo_name)
244 cache = CacheInvalidation(repo_name)
245
245
246 try:
246 try:
247 self.sa.add(cache)
247 self.sa.add(cache)
248 self.sa.commit()
248 self.sa.commit()
249 except (DatabaseError,):
249 except (DatabaseError,):
250 log.error(traceback.format_exc())
250 log.error(traceback.format_exc())
251 self.sa.rollback()
251 self.sa.rollback()
252
252
253
253
254 def toggle_following_repo(self, follow_repo_id, user_id):
254 def toggle_following_repo(self, follow_repo_id, user_id):
255
255
256 f = self.sa.query(UserFollowing)\
256 f = self.sa.query(UserFollowing)\
257 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
257 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
258 .filter(UserFollowing.user_id == user_id).scalar()
258 .filter(UserFollowing.user_id == user_id).scalar()
259
259
260 if f is not None:
260 if f is not None:
261
261
262 try:
262 try:
263 self.sa.delete(f)
263 self.sa.delete(f)
264 self.sa.commit()
264 self.sa.commit()
265 action_logger(UserTemp(user_id),
265 action_logger(UserTemp(user_id),
266 'stopped_following_repo',
266 'stopped_following_repo',
267 RepoTemp(follow_repo_id))
267 RepoTemp(follow_repo_id))
268 return
268 return
269 except:
269 except:
270 log.error(traceback.format_exc())
270 log.error(traceback.format_exc())
271 self.sa.rollback()
271 self.sa.rollback()
272 raise
272 raise
273
273
274
274
275 try:
275 try:
276 f = UserFollowing()
276 f = UserFollowing()
277 f.user_id = user_id
277 f.user_id = user_id
278 f.follows_repo_id = follow_repo_id
278 f.follows_repo_id = follow_repo_id
279 self.sa.add(f)
279 self.sa.add(f)
280 self.sa.commit()
280 self.sa.commit()
281 action_logger(UserTemp(user_id),
281 action_logger(UserTemp(user_id),
282 'started_following_repo',
282 'started_following_repo',
283 RepoTemp(follow_repo_id))
283 RepoTemp(follow_repo_id))
284 except:
284 except:
285 log.error(traceback.format_exc())
285 log.error(traceback.format_exc())
286 self.sa.rollback()
286 self.sa.rollback()
287 raise
287 raise
288
288
289 def toggle_following_user(self, follow_user_id , user_id):
289 def toggle_following_user(self, follow_user_id , user_id):
290 f = self.sa.query(UserFollowing)\
290 f = self.sa.query(UserFollowing)\
291 .filter(UserFollowing.follows_user_id == follow_user_id)\
291 .filter(UserFollowing.follows_user_id == follow_user_id)\
292 .filter(UserFollowing.user_id == user_id).scalar()
292 .filter(UserFollowing.user_id == user_id).scalar()
293
293
294 if f is not None:
294 if f is not None:
295 try:
295 try:
296 self.sa.delete(f)
296 self.sa.delete(f)
297 self.sa.commit()
297 self.sa.commit()
298 return
298 return
299 except:
299 except:
300 log.error(traceback.format_exc())
300 log.error(traceback.format_exc())
301 self.sa.rollback()
301 self.sa.rollback()
302 raise
302 raise
303
303
304 try:
304 try:
305 f = UserFollowing()
305 f = UserFollowing()
306 f.user_id = user_id
306 f.user_id = user_id
307 f.follows_user_id = follow_user_id
307 f.follows_user_id = follow_user_id
308 self.sa.add(f)
308 self.sa.add(f)
309 self.sa.commit()
309 self.sa.commit()
310 except:
310 except:
311 log.error(traceback.format_exc())
311 log.error(traceback.format_exc())
312 self.sa.rollback()
312 self.sa.rollback()
313 raise
313 raise
314
314
315 def is_following_repo(self, repo_name, user_id):
315 def is_following_repo(self, repo_name, user_id):
316 r = self.sa.query(Repository)\
316 r = self.sa.query(Repository)\
317 .filter(Repository.repo_name == repo_name).scalar()
317 .filter(Repository.repo_name == repo_name).scalar()
318
318
319 f = self.sa.query(UserFollowing)\
319 f = self.sa.query(UserFollowing)\
320 .filter(UserFollowing.follows_repository == r)\
320 .filter(UserFollowing.follows_repository == r)\
321 .filter(UserFollowing.user_id == user_id).scalar()
321 .filter(UserFollowing.user_id == user_id).scalar()
322
322
323 return f is not None
323 return f is not None
324
324
325 def is_following_user(self, username, user_id):
325 def is_following_user(self, username, user_id):
326 u = UserModel(self.sa).get_by_username(username)
326 u = UserModel(self.sa).get_by_username(username)
327
327
328 f = self.sa.query(UserFollowing)\
328 f = self.sa.query(UserFollowing)\
329 .filter(UserFollowing.follows_user == u)\
329 .filter(UserFollowing.follows_user == u)\
330 .filter(UserFollowing.user_id == user_id).scalar()
330 .filter(UserFollowing.user_id == user_id).scalar()
331
331
332 return f is not None
332 return f is not None
333
333
334 def get_followers(self, repo_id):
334 def get_followers(self, repo_id):
335 return self.sa.query(UserFollowing)\
335 return self.sa.query(UserFollowing)\
336 .filter(UserFollowing.follows_repo_id == repo_id).count()
336 .filter(UserFollowing.follows_repo_id == repo_id).count()
337
337
338 def get_forks(self, repo_id):
338 def get_forks(self, repo_id):
339 return self.sa.query(Repository)\
339 return self.sa.query(Repository)\
340 .filter(Repository.fork_id == repo_id).count()
340 .filter(Repository.fork_id == repo_id).count()
341
341
342
342
343 def get_unread_journal(self):
343 def get_unread_journal(self):
344 return self.sa.query(UserLog).count()
344 return self.sa.query(UserLog).count()
345
345
346
346
347 def _should_invalidate(self, repo_name):
347 def _should_invalidate(self, repo_name):
348 """Looks up database for invalidation signals for this repo_name
348 """Looks up database for invalidation signals for this repo_name
349
349
350 :param repo_name:
350 :param repo_name:
351 """
351 """
352
352
353 ret = self.sa.query(CacheInvalidation)\
353 ret = self.sa.query(CacheInvalidation)\
354 .options(FromCache('sql_cache_short',
354 .options(FromCache('sql_cache_short',
355 'get_invalidation_%s' % repo_name))\
355 'get_invalidation_%s' % repo_name))\
356 .filter(CacheInvalidation.cache_key == repo_name)\
356 .filter(CacheInvalidation.cache_key == repo_name)\
357 .filter(CacheInvalidation.cache_active == False)\
357 .filter(CacheInvalidation.cache_active == False)\
358 .scalar()
358 .scalar()
359
359
360 return ret
360 return ret
361
361
362 def _mark_invalidated(self, cache_key):
362 def _mark_invalidated(self, cache_key):
363 """ Marks all occurences of cache to invaldation as already invalidated
363 """ Marks all occurences of cache to invaldation as already invalidated
364
364
365 :param cache_key:
365 :param cache_key:
366 """
366 """
367
367
368 if cache_key:
368 if cache_key:
369 log.debug('marking %s as already invalidated', cache_key)
369 log.debug('marking %s as already invalidated', cache_key)
370 try:
370 try:
371 cache_key.cache_active = True
371 cache_key.cache_active = True
372 self.sa.add(cache_key)
372 self.sa.add(cache_key)
373 self.sa.commit()
373 self.sa.commit()
374 except (DatabaseError,):
374 except (DatabaseError,):
375 log.error(traceback.format_exc())
375 log.error(traceback.format_exc())
376 self.sa.rollback()
376 self.sa.rollback()
377
377
General Comments 0
You need to be logged in to leave comments. Login now