hg_model.py
178 lines
| 6.5 KiB
| text/x-python
|
PythonLexer
Marcin Kuzminski
|
r58 | #!/usr/bin/env python | ||
# encoding: utf-8 | ||||
r252 | # Model for hg app | |||
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | ||||
r373 | # | |||
r252 | # This program is free software; you can redistribute it and/or | |||
# modify it under the terms of the GNU General Public License | ||||
# as published by the Free Software Foundation; version 2 | ||||
# of the License or (at your opinion) any later version of the license. | ||||
# | ||||
# This program is distributed in the hope that it will be useful, | ||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
# GNU General Public License for more details. | ||||
# | ||||
# You should have received a copy of the GNU General Public License | ||||
# along with this program; if not, write to the Free Software | ||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | ||||
# MA 02110-1301, USA. | ||||
""" | ||||
Created on April 9, 2010 | ||||
Model for hg app | ||||
Marcin Kuzminski
|
r58 | @author: marcink | ||
r252 | """ | |||
r245 | from beaker.cache import cache_region | |||
from mercurial import ui | ||||
from mercurial.hgweb.hgwebdir_mod import findrepos | ||||
r373 | from pylons.i18n.translation import _ | |||
from pylons_app.lib.auth import HasRepoPermissionAny | ||||
r350 | from pylons_app.model import meta | |||
r376 | from pylons_app.model.db import Repository, User | |||
r325 | from sqlalchemy.orm import joinedload | |||
r373 | from vcs.exceptions import RepositoryError, VCSError | |||
r245 | import logging | |||
Marcin Kuzminski
|
r58 | import os | ||
r195 | import sys | |||
r245 | log = logging.getLogger(__name__) | |||
Marcin Kuzminski
|
r58 | try: | ||
r245 | from vcs.backends.hg import MercurialRepository | |||
Marcin Kuzminski
|
r58 | except ImportError: | ||
r195 | sys.stderr.write('You have to import vcs module') | |||
r95 | raise Exception('Unable to import vcs') | |||
Marcin Kuzminski
|
r58 | |||
r368 | def _get_repos_cached_initial(app_globals, initial): | |||
r265 | """ | |||
return cached dict with repos | ||||
""" | ||||
g = app_globals | ||||
r368 | return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui, initial) | |||
r245 | ||||
@cache_region('long_term', 'cached_repo_list') | ||||
def _get_repos_cached(): | ||||
""" | ||||
return cached dict with repos | ||||
""" | ||||
r276 | log.info('getting all repositories list') | |||
r265 | from pylons import app_globals as g | |||
r245 | return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui) | |||
r373 | @cache_region('super_short_term', 'cached_repos_switcher_list') | |||
def _get_repos_switcher_cached(cached_repo_list): | ||||
repos_lst = [] | ||||
for repo in sorted(x.name.lower() for x in cached_repo_list.values()): | ||||
if HasRepoPermissionAny('repository.write', 'repository.read', 'repository.admin')(repo, 'main page check'): | ||||
repos_lst.append(repo) | ||||
return repos_lst | ||||
r245 | @cache_region('long_term', 'full_changelog') | |||
def _full_changelog_cached(repo_name): | ||||
log.info('getting full changelog for %s', repo_name) | ||||
return list(reversed(list(HgModel().get_repo(repo_name)))) | ||||
Marcin Kuzminski
|
r58 | class HgModel(object): | ||
""" | ||||
Mercurial Model | ||||
""" | ||||
def __init__(self): | ||||
""" | ||||
Constructor | ||||
""" | ||||
r245 | ||||
@staticmethod | ||||
r368 | def repo_scan(repos_prefix, repos_path, baseui, initial=False): | |||
r245 | """ | |||
Listing of repositories in given path. This path should not be a | ||||
repository itself. Return a dictionary of repository objects | ||||
:param repos_path: path to directory it could take syntax with | ||||
* or ** for deep recursive displaying repositories | ||||
""" | ||||
r350 | sa = meta.Session() | |||
r245 | def check_repo_dir(path): | |||
""" | ||||
Checks the repository | ||||
:param path: | ||||
""" | ||||
repos_path = path.split('/') | ||||
if repos_path[-1] in ['*', '**']: | ||||
repos_path = repos_path[:-1] | ||||
if repos_path[0] != '/': | ||||
repos_path[0] = '/' | ||||
if not os.path.isdir(os.path.join(*repos_path)): | ||||
raise RepositoryError('Not a valid repository in %s' % path[0][1]) | ||||
if not repos_path.endswith('*'): | ||||
raise VCSError('You need to specify * or ** at the end of path ' | ||||
'for recursive scanning') | ||||
check_repo_dir(repos_path) | ||||
log.info('scanning for repositories in %s', repos_path) | ||||
repos = findrepos([(repos_prefix, repos_path)]) | ||||
if not isinstance(baseui, ui.ui): | ||||
baseui = ui.ui() | ||||
repos_list = {} | ||||
for name, path in repos: | ||||
try: | ||||
r248 | #name = name.split('/')[-1] | |||
if repos_list.has_key(name): | ||||
raise RepositoryError('Duplicate repository name %s found in' | ||||
' %s' % (name, path)) | ||||
else: | ||||
r265 | ||||
r248 | repos_list[name] = MercurialRepository(path, baseui=baseui) | |||
repos_list[name].name = name | ||||
r368 | ||||
dbrepo = None | ||||
if not initial: | ||||
dbrepo = sa.query(Repository)\ | ||||
.filter(Repository.repo_name == name).scalar() | ||||
r265 | if dbrepo: | |||
r368 | log.info('Adding db instance to cached list') | |||
r303 | repos_list[name].dbrepo = dbrepo | |||
r265 | repos_list[name].description = dbrepo.description | |||
r376 | if dbrepo.user: | |||
repos_list[name].contact = dbrepo.user.full_contact | ||||
else: | ||||
repos_list[name].contact = sa.query(User)\ | ||||
.filter(User.admin == True).first().full_contact | ||||
r245 | except OSError: | |||
continue | ||||
r350 | meta.Session.remove() | |||
r245 | return repos_list | |||
Marcin Kuzminski
|
r58 | def get_repos(self): | ||
r245 | for name, repo in _get_repos_cached().items(): | |||
if repo._get_hidden(): | ||||
Marcin Kuzminski
|
r73 | #skip hidden web repository | ||
continue | ||||
r245 | last_change = repo.last_change | |||
r136 | try: | |||
r245 | tip = repo.get_changeset('tip') | |||
r136 | except RepositoryError: | |||
from pylons_app.lib.utils import EmptyChangeset | ||||
tip = EmptyChangeset() | ||||
Marcin Kuzminski
|
r58 | tmp_d = {} | ||
r245 | tmp_d['name'] = repo.name | |||
r222 | tmp_d['name_sort'] = tmp_d['name'].lower() | |||
r245 | tmp_d['description'] = repo.description | |||
Marcin Kuzminski
|
r58 | tmp_d['description_sort'] = tmp_d['description'] | ||
Marcin Kuzminski
|
r80 | tmp_d['last_change'] = last_change | ||
Marcin Kuzminski
|
r58 | tmp_d['last_change_sort'] = last_change[1] - last_change[0] | ||
r136 | tmp_d['tip'] = tip.raw_id | |||
tmp_d['tip_sort'] = tip.revision | ||||
tmp_d['rev'] = tip.revision | ||||
r245 | tmp_d['contact'] = repo.contact | |||
Marcin Kuzminski
|
r73 | tmp_d['contact_sort'] = tmp_d['contact'] | ||
r245 | tmp_d['repo_archives'] = list(repo._get_archives()) | |||
r281 | tmp_d['last_msg'] = tip.message | |||
r303 | tmp_d['repo'] = repo | |||
Marcin Kuzminski
|
r58 | yield tmp_d | ||
Marcin Kuzminski
|
r74 | |||
def get_repo(self, repo_name): | ||||
r245 | return _get_repos_cached()[repo_name] | |||