##// END OF EJS Templates
backported fixes for statistics from beta branch, try to fix issue #271...
marcink -
r1887:3246fcce default
parent child Browse files
Show More
@@ -1,56 +1,56 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.__init__
3 rhodecode.__init__
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 RhodeCode, a web based repository management based on pylons
6 RhodeCode, a web based repository management based on pylons
7 versioning implementation: http://semver.org/
7 versioning implementation: http://semver.org/
8
8
9 :created_on: Apr 9, 2010
9 :created_on: Apr 9, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 import platform
26 import platform
27
27
28 VERSION = (1, 2, 3)
28 VERSION = (1, 2, 4)
29 __version__ = '.'.join((str(each) for each in VERSION[:4]))
29 __version__ = '.'.join((str(each) for each in VERSION[:4]))
30 __dbversion__ = 3 #defines current db version for migrations
30 __dbversion__ = 3 # defines current db version for migrations
31 __platform__ = platform.system()
31 __platform__ = platform.system()
32 __license__ = 'GPLv3'
32 __license__ = 'GPLv3'
33
33
34 PLATFORM_WIN = ('Windows')
34 PLATFORM_WIN = ('Windows')
35 PLATFORM_OTHERS = ('Linux', 'Darwin', 'FreeBSD', 'OpenBSD', 'SunOS')
35 PLATFORM_OTHERS = ('Linux', 'Darwin', 'FreeBSD', 'OpenBSD', 'SunOS')
36
36
37 try:
37 try:
38 from rhodecode.lib import get_current_revision
38 from rhodecode.lib import get_current_revision
39 _rev = get_current_revision(quiet=True)
39 _rev = get_current_revision(quiet=True)
40 except ImportError:
40 except ImportError:
41 #this is needed when doing some setup.py operations
41 #this is needed when doing some setup.py operations
42 _rev = False
42 _rev = False
43
43
44 if len(VERSION) > 3 and _rev:
44 if len(VERSION) > 3 and _rev:
45 __version__ += ' [rev:%s]' % _rev[0]
45 __version__ += ' [rev:%s]' % _rev[0]
46
46
47
47
48 def get_version():
48 def get_version():
49 """Returns shorter version (digit parts only) as string."""
49 """Returns shorter version (digit parts only) as string."""
50
50
51 return '.'.join((str(each) for each in VERSION[:3]))
51 return '.'.join((str(each) for each in VERSION[:3]))
52
52
53 BACKENDS = {
53 BACKENDS = {
54 'hg': 'Mercurial repository',
54 'hg': 'Mercurial repository',
55 #'git': 'Git repository',
55 #'git': 'Git repository',
56 }
56 }
@@ -1,183 +1,185 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.summary
3 rhodecode.controllers.summary
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Summary controller for Rhodecode
6 Summary controller 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-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import calendar
26 import calendar
27 import logging
27 import logging
28 from time import mktime
28 from time import mktime
29 from datetime import datetime, timedelta, date
29 from datetime import datetime, timedelta, date
30
30
31 from vcs.exceptions import ChangesetError
31 from vcs.exceptions import ChangesetError
32
32
33 from pylons import tmpl_context as c, request, url
33 from pylons import tmpl_context as c, request, url
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35
35
36 from rhodecode.model.db import Statistics, Repository
36 from rhodecode.model.db import Statistics, Repository
37 from rhodecode.model.repo import RepoModel
37 from rhodecode.model.repo import RepoModel
38
38
39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
40 from rhodecode.lib.base import BaseRepoController, render
40 from rhodecode.lib.base import BaseRepoController, render
41 from rhodecode.lib.utils import EmptyChangeset
41 from rhodecode.lib.utils import EmptyChangeset
42
42
43 from rhodecode.lib.celerylib import run_task
43 from rhodecode.lib.celerylib import run_task
44 from rhodecode.lib.celerylib.tasks import get_commits_stats, \
44 from rhodecode.lib.celerylib.tasks import get_commits_stats, \
45 LANGUAGES_EXTENSIONS_MAP
45 LANGUAGES_EXTENSIONS_MAP
46 from rhodecode.lib.helpers import RepoPage
46 from rhodecode.lib.helpers import RepoPage
47 from rhodecode.lib.compat import json, OrderedDict
47 from rhodecode.lib.compat import json, OrderedDict
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51
51
52 class SummaryController(BaseRepoController):
52 class SummaryController(BaseRepoController):
53
53
54 @LoginRequired()
54 @LoginRequired()
55 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
55 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
56 'repository.admin')
56 'repository.admin')
57 def __before__(self):
57 def __before__(self):
58 super(SummaryController, self).__before__()
58 super(SummaryController, self).__before__()
59
59
60 def index(self, repo_name):
60 def index(self, repo_name):
61
61
62 e = request.environ
62 e = request.environ
63 c.dbrepo = dbrepo = c.rhodecode_db_repo
63 c.dbrepo = dbrepo = c.rhodecode_db_repo
64
64
65 c.following = self.scm_model.is_following_repo(repo_name,
65 c.following = self.scm_model.is_following_repo(repo_name,
66 self.rhodecode_user.user_id)
66 self.rhodecode_user.user_id)
67
67
68 def url_generator(**kw):
68 def url_generator(**kw):
69 return url('shortlog_home', repo_name=repo_name, size=10, **kw)
69 return url('shortlog_home', repo_name=repo_name, size=10, **kw)
70
70
71 c.repo_changesets = RepoPage(c.rhodecode_repo, page=1,
71 c.repo_changesets = RepoPage(c.rhodecode_repo, page=1,
72 items_per_page=10, url=url_generator)
72 items_per_page=10, url=url_generator)
73
73
74 if self.rhodecode_user.username == 'default':
74 if self.rhodecode_user.username == 'default':
75 #for default(anonymous) user we don't need to pass credentials
75 #for default(anonymous) user we don't need to pass credentials
76 username = ''
76 username = ''
77 password = ''
77 password = ''
78 else:
78 else:
79 username = str(self.rhodecode_user.username)
79 username = str(self.rhodecode_user.username)
80 password = '@'
80 password = '@'
81
81
82 if e.get('wsgi.url_scheme') == 'https':
82 if e.get('wsgi.url_scheme') == 'https':
83 split_s = 'https://'
83 split_s = 'https://'
84 else:
84 else:
85 split_s = 'http://'
85 split_s = 'http://'
86
86
87 qualified_uri = [split_s] + [url.current(qualified=True)\
87 qualified_uri = [split_s] + [url.current(qualified=True)\
88 .split(split_s)[-1]]
88 .split(split_s)[-1]]
89 uri = u'%(proto)s%(user)s%(pass)s%(rest)s' \
89 uri = u'%(proto)s%(user)s%(pass)s%(rest)s' \
90 % {'user': username,
90 % {'user': username,
91 'pass': password,
91 'pass': password,
92 'proto': qualified_uri[0],
92 'proto': qualified_uri[0],
93 'rest': qualified_uri[1]}
93 'rest': qualified_uri[1]}
94 c.clone_repo_url = uri
94 c.clone_repo_url = uri
95 c.repo_tags = OrderedDict()
95 c.repo_tags = OrderedDict()
96 for name, hash in c.rhodecode_repo.tags.items()[:10]:
96 for name, hash in c.rhodecode_repo.tags.items()[:10]:
97 try:
97 try:
98 c.repo_tags[name] = c.rhodecode_repo.get_changeset(hash)
98 c.repo_tags[name] = c.rhodecode_repo.get_changeset(hash)
99 except ChangesetError:
99 except ChangesetError:
100 c.repo_tags[name] = EmptyChangeset(hash)
100 c.repo_tags[name] = EmptyChangeset(hash)
101
101
102 c.repo_branches = OrderedDict()
102 c.repo_branches = OrderedDict()
103 for name, hash in c.rhodecode_repo.branches.items()[:10]:
103 for name, hash in c.rhodecode_repo.branches.items()[:10]:
104 try:
104 try:
105 c.repo_branches[name] = c.rhodecode_repo.get_changeset(hash)
105 c.repo_branches[name] = c.rhodecode_repo.get_changeset(hash)
106 except ChangesetError:
106 except ChangesetError:
107 c.repo_branches[name] = EmptyChangeset(hash)
107 c.repo_branches[name] = EmptyChangeset(hash)
108
108
109 td = date.today() + timedelta(days=1)
109 td = date.today() + timedelta(days=1)
110 td_1m = td - timedelta(days=calendar.mdays[td.month])
110 td_1m = td - timedelta(days=calendar.mdays[td.month])
111 td_1y = td - timedelta(days=365)
111 td_1y = td - timedelta(days=365)
112
112
113 ts_min_m = mktime(td_1m.timetuple())
113 ts_min_m = mktime(td_1m.timetuple())
114 ts_min_y = mktime(td_1y.timetuple())
114 ts_min_y = mktime(td_1y.timetuple())
115 ts_max_y = mktime(td.timetuple())
115 ts_max_y = mktime(td.timetuple())
116
116
117 if dbrepo.enable_statistics:
117 if dbrepo.enable_statistics:
118 c.show_stats = True
118 c.no_data_msg = _('No data loaded yet')
119 c.no_data_msg = _('No data loaded yet')
119 run_task(get_commits_stats, c.dbrepo.repo_name, ts_min_y, ts_max_y)
120 run_task(get_commits_stats, c.dbrepo.repo_name, ts_min_y, ts_max_y)
120 else:
121 else:
122 c.show_stats = False
121 c.no_data_msg = _('Statistics are disabled for this repository')
123 c.no_data_msg = _('Statistics are disabled for this repository')
122 c.ts_min = ts_min_m
124 c.ts_min = ts_min_m
123 c.ts_max = ts_max_y
125 c.ts_max = ts_max_y
124
126
125 stats = self.sa.query(Statistics)\
127 stats = self.sa.query(Statistics)\
126 .filter(Statistics.repository == dbrepo)\
128 .filter(Statistics.repository == dbrepo)\
127 .scalar()
129 .scalar()
128
130
129 c.stats_percentage = 0
131 c.stats_percentage = 0
130
132
131 if stats and stats.languages:
133 if stats and stats.languages:
132 c.no_data = False is dbrepo.enable_statistics
134 c.no_data = False is dbrepo.enable_statistics
133 lang_stats_d = json.loads(stats.languages)
135 lang_stats_d = json.loads(stats.languages)
134 c.commit_data = stats.commit_activity
136 c.commit_data = stats.commit_activity
135 c.overview_data = stats.commit_activity_combined
137 c.overview_data = stats.commit_activity_combined
136
138
137 lang_stats = ((x, {"count": y,
139 lang_stats = ((x, {"count": y,
138 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
140 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
139 for x, y in lang_stats_d.items())
141 for x, y in lang_stats_d.items())
140
142
141 c.trending_languages = json.dumps(OrderedDict(
143 c.trending_languages = json.dumps(OrderedDict(
142 sorted(lang_stats, reverse=True,
144 sorted(lang_stats, reverse=True,
143 key=lambda k: k[1])[:10]
145 key=lambda k: k[1])[:10]
144 )
146 )
145 )
147 )
146 last_rev = stats.stat_on_revision
148 last_rev = stats.stat_on_revision + 1
147 c.repo_last_rev = c.rhodecode_repo.count() - 1 \
149 c.repo_last_rev = c.rhodecode_repo.count()\
148 if c.rhodecode_repo.revisions else 0
150 if c.rhodecode_repo.revisions else 0
149 if last_rev == 0 or c.repo_last_rev == 0:
151 if last_rev == 0 or c.repo_last_rev == 0:
150 pass
152 pass
151 else:
153 else:
152 c.stats_percentage = '%.2f' % ((float((last_rev)) /
154 c.stats_percentage = '%.2f' % ((float((last_rev)) /
153 c.repo_last_rev) * 100)
155 c.repo_last_rev) * 100)
154 else:
156 else:
155 c.commit_data = json.dumps({})
157 c.commit_data = json.dumps({})
156 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10]])
158 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10]])
157 c.trending_languages = json.dumps({})
159 c.trending_languages = json.dumps({})
158 c.no_data = True
160 c.no_data = True
159
161
160 c.enable_downloads = dbrepo.enable_downloads
162 c.enable_downloads = dbrepo.enable_downloads
161 if c.enable_downloads:
163 if c.enable_downloads:
162 c.download_options = self._get_download_links(c.rhodecode_repo)
164 c.download_options = self._get_download_links(c.rhodecode_repo)
163
165
164 return render('summary/summary.html')
166 return render('summary/summary.html')
165
167
166 def _get_download_links(self, repo):
168 def _get_download_links(self, repo):
167
169
168 download_l = []
170 download_l = []
169
171
170 branches_group = ([], _("Branches"))
172 branches_group = ([], _("Branches"))
171 tags_group = ([], _("Tags"))
173 tags_group = ([], _("Tags"))
172
174
173 for name, chs in c.rhodecode_repo.branches.items():
175 for name, chs in c.rhodecode_repo.branches.items():
174 #chs = chs.split(':')[-1]
176 #chs = chs.split(':')[-1]
175 branches_group[0].append((chs, name),)
177 branches_group[0].append((chs, name),)
176 download_l.append(branches_group)
178 download_l.append(branches_group)
177
179
178 for name, chs in c.rhodecode_repo.tags.items():
180 for name, chs in c.rhodecode_repo.tags.items():
179 #chs = chs.split(':')[-1]
181 #chs = chs.split(':')[-1]
180 tags_group[0].append((chs, name),)
182 tags_group[0].append((chs, name),)
181 download_l.append(tags_group)
183 download_l.append(tags_group)
182
184
183 return download_l
185 return download_l
@@ -1,416 +1,417 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.celerylib.tasks
3 rhodecode.lib.celerylib.tasks
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 RhodeCode task modules, containing all task that suppose to be run
6 RhodeCode task modules, containing all task that suppose to be run
7 by celery daemon
7 by celery daemon
8
8
9 :created_on: Oct 6, 2010
9 :created_on: Oct 6, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 from celery.decorators import task
26 from celery.decorators import task
27
27
28 import os
28 import os
29 import traceback
29 import traceback
30 import logging
30 import logging
31 from os.path import dirname as dn, join as jn
31 from os.path import dirname as dn, join as jn
32
32
33 from time import mktime
33 from time import mktime
34 from operator import itemgetter
34 from operator import itemgetter
35 from string import lower
35 from string import lower
36
36
37 from pylons import config, url
37 from pylons import config, url
38 from pylons.i18n.translation import _
38 from pylons.i18n.translation import _
39
39
40 from rhodecode.lib import LANGUAGES_EXTENSIONS_MAP, safe_str
40 from rhodecode.lib import LANGUAGES_EXTENSIONS_MAP, safe_str
41 from rhodecode.lib.celerylib import run_task, locked_task, str2bool, \
41 from rhodecode.lib.celerylib import run_task, locked_task, str2bool, \
42 __get_lockkey, LockHeld, DaemonLock
42 __get_lockkey, LockHeld, DaemonLock
43 from rhodecode.lib.helpers import person
43 from rhodecode.lib.helpers import person
44 from rhodecode.lib.smtp_mailer import SmtpMailer
44 from rhodecode.lib.smtp_mailer import SmtpMailer
45 from rhodecode.lib.utils import add_cache
45 from rhodecode.lib.utils import add_cache
46 from rhodecode.lib.compat import json, OrderedDict
46 from rhodecode.lib.compat import json, OrderedDict
47
47
48 from rhodecode.model import init_model
48 from rhodecode.model import init_model
49 from rhodecode.model import meta
49 from rhodecode.model import meta
50 from rhodecode.model.db import RhodeCodeUi, Statistics, Repository, User
50 from rhodecode.model.db import RhodeCodeUi, Statistics, Repository, User
51
51
52 from vcs.backends import get_repo
52 from vcs.backends import get_repo
53
53
54 from sqlalchemy import engine_from_config
54 from sqlalchemy import engine_from_config
55
55
56 add_cache(config)
56 add_cache(config)
57
57
58 __all__ = ['whoosh_index', 'get_commits_stats',
58 __all__ = ['whoosh_index', 'get_commits_stats',
59 'reset_user_password', 'send_email']
59 'reset_user_password', 'send_email']
60
60
61 CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
61 CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
62
62
63
63
64 def get_session():
64 def get_session():
65 if CELERY_ON:
65 if CELERY_ON:
66 engine = engine_from_config(config, 'sqlalchemy.db1.')
66 engine = engine_from_config(config, 'sqlalchemy.db1.')
67 init_model(engine)
67 init_model(engine)
68
68
69 sa = meta.Session()
69 sa = meta.Session()
70 return sa
70 return sa
71
71
72
72
73 def get_repos_path():
73 def get_repos_path():
74 sa = get_session()
74 sa = get_session()
75 q = sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
75 q = sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
76 return q.ui_value
76 return q.ui_value
77
77
78
78
79 @task(ignore_result=True)
79 @task(ignore_result=True)
80 @locked_task
80 @locked_task
81 def whoosh_index(repo_location, full_index):
81 def whoosh_index(repo_location, full_index):
82 #log = whoosh_index.get_logger()
82 #log = whoosh_index.get_logger()
83 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
83 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
84 index_location = config['index_dir']
84 index_location = config['index_dir']
85 WhooshIndexingDaemon(index_location=index_location,
85 WhooshIndexingDaemon(index_location=index_location,
86 repo_location=repo_location, sa=get_session())\
86 repo_location=repo_location, sa=get_session())\
87 .run(full_index=full_index)
87 .run(full_index=full_index)
88
88
89
89
90 @task(ignore_result=True)
90 @task(ignore_result=True)
91 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
91 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
92 try:
92 try:
93 log = get_commits_stats.get_logger()
93 log = get_commits_stats.get_logger()
94 except:
94 except:
95 log = logging.getLogger(__name__)
95 log = logging.getLogger(__name__)
96
96
97 lockkey = __get_lockkey('get_commits_stats', repo_name, ts_min_y,
97 lockkey = __get_lockkey('get_commits_stats', repo_name, ts_min_y,
98 ts_max_y)
98 ts_max_y)
99 lockkey_path = config['here']
99 lockkey_path = config['here']
100
100
101 log.info('running task with lockkey %s', lockkey)
101 log.info('running task with lockkey %s', lockkey)
102 try:
102 try:
103 sa = get_session()
103 lock = l = DaemonLock(file_=jn(lockkey_path, lockkey))
104 lock = l = DaemonLock(file_=jn(lockkey_path, lockkey))
104
105
105 # for js data compatibilty cleans the key for person from '
106 # for js data compatibilty cleans the key for person from '
106 akc = lambda k: person(k).replace('"', "")
107 akc = lambda k: person(k).replace('"', "")
107
108
108 co_day_auth_aggr = {}
109 co_day_auth_aggr = {}
109 commits_by_day_aggregate = {}
110 commits_by_day_aggregate = {}
110 repos_path = get_repos_path()
111 repos_path = get_repos_path()
111 repo = get_repo(safe_str(os.path.join(repos_path, repo_name)))
112 repo = get_repo(safe_str(os.path.join(repos_path, repo_name)))
112 repo_size = len(repo.revisions)
113 repo_size = repo.count()
113 #return if repo have no revisions
114 # return if repo have no revisions
114 if repo_size < 1:
115 if repo_size < 1:
115 lock.release()
116 lock.release()
116 return True
117 return True
117
118
118 skip_date_limit = True
119 skip_date_limit = True
119 parse_limit = int(config['app_conf'].get('commit_parse_limit'))
120 parse_limit = int(config['app_conf'].get('commit_parse_limit'))
120 last_rev = 0
121 last_rev = None
121 last_cs = None
122 last_cs = None
122 timegetter = itemgetter('time')
123 timegetter = itemgetter('time')
123
124
124 sa = get_session()
125
126 dbrepo = sa.query(Repository)\
125 dbrepo = sa.query(Repository)\
127 .filter(Repository.repo_name == repo_name).scalar()
126 .filter(Repository.repo_name == repo_name).scalar()
128 cur_stats = sa.query(Statistics)\
127 cur_stats = sa.query(Statistics)\
129 .filter(Statistics.repository == dbrepo).scalar()
128 .filter(Statistics.repository == dbrepo).scalar()
130
129
131 if cur_stats is not None:
130 if cur_stats is not None:
132 last_rev = cur_stats.stat_on_revision
131 last_rev = cur_stats.stat_on_revision
133
132
134 if last_rev == repo.get_changeset().revision and repo_size > 1:
133 if last_rev == repo.get_changeset().revision and repo_size > 1:
135 #pass silently without any work if we're not on first revision or
134 # pass silently without any work if we're not on first revision or
136 #current state of parsing revision(from db marker) is the
135 # current state of parsing revision(from db marker) is the
137 #last revision
136 # last revision
138 lock.release()
137 lock.release()
139 return True
138 return True
140
139
141 if cur_stats:
140 if cur_stats:
142 commits_by_day_aggregate = OrderedDict(json.loads(
141 commits_by_day_aggregate = OrderedDict(json.loads(
143 cur_stats.commit_activity_combined))
142 cur_stats.commit_activity_combined))
144 co_day_auth_aggr = json.loads(cur_stats.commit_activity)
143 co_day_auth_aggr = json.loads(cur_stats.commit_activity)
145
144
146 log.debug('starting parsing %s', parse_limit)
145 log.debug('starting parsing %s', parse_limit)
147 lmktime = mktime
146 lmktime = mktime
148
147
149 last_rev = last_rev + 1 if last_rev > 0 else last_rev
148 last_rev = last_rev + 1 if last_rev >= 0 else 0
150
149 log.debug('Getting revisions from %s to %s' % (
150 last_rev, last_rev + parse_limit)
151 )
151 for cs in repo[last_rev:last_rev + parse_limit]:
152 for cs in repo[last_rev:last_rev + parse_limit]:
152 last_cs = cs # remember last parsed changeset
153 last_cs = cs # remember last parsed changeset
153 k = lmktime([cs.date.timetuple()[0], cs.date.timetuple()[1],
154 k = lmktime([cs.date.timetuple()[0], cs.date.timetuple()[1],
154 cs.date.timetuple()[2], 0, 0, 0, 0, 0, 0])
155 cs.date.timetuple()[2], 0, 0, 0, 0, 0, 0])
155
156
156 if akc(cs.author) in co_day_auth_aggr:
157 if akc(cs.author) in co_day_auth_aggr:
157 try:
158 try:
158 l = [timegetter(x) for x in
159 l = [timegetter(x) for x in
159 co_day_auth_aggr[akc(cs.author)]['data']]
160 co_day_auth_aggr[akc(cs.author)]['data']]
160 time_pos = l.index(k)
161 time_pos = l.index(k)
161 except ValueError:
162 except ValueError:
162 time_pos = False
163 time_pos = False
163
164
164 if time_pos >= 0 and time_pos is not False:
165 if time_pos >= 0 and time_pos is not False:
165
166
166 datadict = \
167 datadict = \
167 co_day_auth_aggr[akc(cs.author)]['data'][time_pos]
168 co_day_auth_aggr[akc(cs.author)]['data'][time_pos]
168
169
169 datadict["commits"] += 1
170 datadict["commits"] += 1
170 datadict["added"] += len(cs.added)
171 datadict["added"] += len(cs.added)
171 datadict["changed"] += len(cs.changed)
172 datadict["changed"] += len(cs.changed)
172 datadict["removed"] += len(cs.removed)
173 datadict["removed"] += len(cs.removed)
173
174
174 else:
175 else:
175 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
176 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
176
177
177 datadict = {"time": k,
178 datadict = {"time": k,
178 "commits": 1,
179 "commits": 1,
179 "added": len(cs.added),
180 "added": len(cs.added),
180 "changed": len(cs.changed),
181 "changed": len(cs.changed),
181 "removed": len(cs.removed),
182 "removed": len(cs.removed),
182 }
183 }
183 co_day_auth_aggr[akc(cs.author)]['data']\
184 co_day_auth_aggr[akc(cs.author)]['data']\
184 .append(datadict)
185 .append(datadict)
185
186
186 else:
187 else:
187 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
188 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
188 co_day_auth_aggr[akc(cs.author)] = {
189 co_day_auth_aggr[akc(cs.author)] = {
189 "label": akc(cs.author),
190 "label": akc(cs.author),
190 "data": [{"time":k,
191 "data": [{"time":k,
191 "commits":1,
192 "commits":1,
192 "added":len(cs.added),
193 "added":len(cs.added),
193 "changed":len(cs.changed),
194 "changed":len(cs.changed),
194 "removed":len(cs.removed),
195 "removed":len(cs.removed),
195 }],
196 }],
196 "schema": ["commits"],
197 "schema": ["commits"],
197 }
198 }
198
199
199 #gather all data by day
200 #gather all data by day
200 if k in commits_by_day_aggregate:
201 if k in commits_by_day_aggregate:
201 commits_by_day_aggregate[k] += 1
202 commits_by_day_aggregate[k] += 1
202 else:
203 else:
203 commits_by_day_aggregate[k] = 1
204 commits_by_day_aggregate[k] = 1
204
205
205 overview_data = sorted(commits_by_day_aggregate.items(),
206 overview_data = sorted(commits_by_day_aggregate.items(),
206 key=itemgetter(0))
207 key=itemgetter(0))
207
208
208 if not co_day_auth_aggr:
209 if not co_day_auth_aggr:
209 co_day_auth_aggr[akc(repo.contact)] = {
210 co_day_auth_aggr[akc(repo.contact)] = {
210 "label": akc(repo.contact),
211 "label": akc(repo.contact),
211 "data": [0, 1],
212 "data": [0, 1],
212 "schema": ["commits"],
213 "schema": ["commits"],
213 }
214 }
214
215
215 stats = cur_stats if cur_stats else Statistics()
216 stats = cur_stats if cur_stats else Statistics()
216 stats.commit_activity = json.dumps(co_day_auth_aggr)
217 stats.commit_activity = json.dumps(co_day_auth_aggr)
217 stats.commit_activity_combined = json.dumps(overview_data)
218 stats.commit_activity_combined = json.dumps(overview_data)
218
219
219 log.debug('last revison %s', last_rev)
220 log.debug('last revison %s', last_rev)
220 leftovers = len(repo.revisions[last_rev:])
221 leftovers = len(repo.revisions[last_rev:])
221 log.debug('revisions to parse %s', leftovers)
222 log.debug('revisions to parse %s', leftovers)
222
223
223 if last_rev == 0 or leftovers < parse_limit:
224 if last_rev == 0 or leftovers < parse_limit:
224 log.debug('getting code trending stats')
225 log.debug('getting code trending stats')
225 stats.languages = json.dumps(__get_codes_stats(repo_name))
226 stats.languages = json.dumps(__get_codes_stats(repo_name))
226
227
227 try:
228 try:
228 stats.repository = dbrepo
229 stats.repository = dbrepo
229 stats.stat_on_revision = last_cs.revision if last_cs else 0
230 stats.stat_on_revision = last_cs.revision if last_cs else 0
230 sa.add(stats)
231 sa.add(stats)
231 sa.commit()
232 sa.commit()
232 except:
233 except:
233 log.error(traceback.format_exc())
234 log.error(traceback.format_exc())
234 sa.rollback()
235 sa.rollback()
235 lock.release()
236 lock.release()
236 return False
237 return False
237
238
238 # final release
239 # final release
239 lock.release()
240 lock.release()
240
241
241 # execute another task if celery is enabled
242 # execute another task if celery is enabled
242 if len(repo.revisions) > 1 and CELERY_ON:
243 if len(repo.revisions) > 1 and CELERY_ON:
243 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
244 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
244 return True
245 return True
245 except LockHeld:
246 except LockHeld:
246 log.info('LockHeld')
247 log.info('LockHeld')
247 return 'Task with key %s already running' % lockkey
248 return 'Task with key %s already running' % lockkey
248
249
249 @task(ignore_result=True)
250 @task(ignore_result=True)
250 def send_password_link(user_email):
251 def send_password_link(user_email):
251 try:
252 try:
252 log = reset_user_password.get_logger()
253 log = reset_user_password.get_logger()
253 except:
254 except:
254 log = logging.getLogger(__name__)
255 log = logging.getLogger(__name__)
255
256
256 from rhodecode.lib import auth
257 from rhodecode.lib import auth
257 from rhodecode.model.db import User
258 from rhodecode.model.db import User
258
259
259 try:
260 try:
260 sa = get_session()
261 sa = get_session()
261 user = sa.query(User).filter(User.email == user_email).scalar()
262 user = sa.query(User).filter(User.email == user_email).scalar()
262
263
263 if user:
264 if user:
264 link = url('reset_password_confirmation', key=user.api_key,
265 link = url('reset_password_confirmation', key=user.api_key,
265 qualified=True)
266 qualified=True)
266 tmpl = """
267 tmpl = """
267 Hello %s
268 Hello %s
268
269
269 We received a request to create a new password for your account.
270 We received a request to create a new password for your account.
270
271
271 You can generate it by clicking following URL:
272 You can generate it by clicking following URL:
272
273
273 %s
274 %s
274
275
275 If you didn't request new password please ignore this email.
276 If you didn't request new password please ignore this email.
276 """
277 """
277 run_task(send_email, user_email,
278 run_task(send_email, user_email,
278 "RhodeCode password reset link",
279 "RhodeCode password reset link",
279 tmpl % (user.short_contact, link))
280 tmpl % (user.short_contact, link))
280 log.info('send new password mail to %s', user_email)
281 log.info('send new password mail to %s', user_email)
281
282
282 except:
283 except:
283 log.error('Failed to update user password')
284 log.error('Failed to update user password')
284 log.error(traceback.format_exc())
285 log.error(traceback.format_exc())
285 return False
286 return False
286
287
287 return True
288 return True
288
289
289 @task(ignore_result=True)
290 @task(ignore_result=True)
290 def reset_user_password(user_email):
291 def reset_user_password(user_email):
291 try:
292 try:
292 log = reset_user_password.get_logger()
293 log = reset_user_password.get_logger()
293 except:
294 except:
294 log = logging.getLogger(__name__)
295 log = logging.getLogger(__name__)
295
296
296 from rhodecode.lib import auth
297 from rhodecode.lib import auth
297 from rhodecode.model.db import User
298 from rhodecode.model.db import User
298
299
299 try:
300 try:
300 try:
301 try:
301 sa = get_session()
302 sa = get_session()
302 user = sa.query(User).filter(User.email == user_email).scalar()
303 user = sa.query(User).filter(User.email == user_email).scalar()
303 new_passwd = auth.PasswordGenerator().gen_password(8,
304 new_passwd = auth.PasswordGenerator().gen_password(8,
304 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
305 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
305 if user:
306 if user:
306 user.password = auth.get_crypt_password(new_passwd)
307 user.password = auth.get_crypt_password(new_passwd)
307 user.api_key = auth.generate_api_key(user.username)
308 user.api_key = auth.generate_api_key(user.username)
308 sa.add(user)
309 sa.add(user)
309 sa.commit()
310 sa.commit()
310 log.info('change password for %s', user_email)
311 log.info('change password for %s', user_email)
311 if new_passwd is None:
312 if new_passwd is None:
312 raise Exception('unable to generate new password')
313 raise Exception('unable to generate new password')
313
314
314 except:
315 except:
315 log.error(traceback.format_exc())
316 log.error(traceback.format_exc())
316 sa.rollback()
317 sa.rollback()
317
318
318 run_task(send_email, user_email,
319 run_task(send_email, user_email,
319 "Your new RhodeCode password",
320 "Your new RhodeCode password",
320 'Your new RhodeCode password:%s' % (new_passwd))
321 'Your new RhodeCode password:%s' % (new_passwd))
321 log.info('send new password mail to %s', user_email)
322 log.info('send new password mail to %s', user_email)
322
323
323 except:
324 except:
324 log.error('Failed to update user password')
325 log.error('Failed to update user password')
325 log.error(traceback.format_exc())
326 log.error(traceback.format_exc())
326
327
327 return True
328 return True
328
329
329
330
330 @task(ignore_result=True)
331 @task(ignore_result=True)
331 def send_email(recipients, subject, body):
332 def send_email(recipients, subject, body):
332 """
333 """
333 Sends an email with defined parameters from the .ini files.
334 Sends an email with defined parameters from the .ini files.
334
335
335 :param recipients: list of recipients, it this is empty the defined email
336 :param recipients: list of recipients, it this is empty the defined email
336 address from field 'email_to' is used instead
337 address from field 'email_to' is used instead
337 :param subject: subject of the mail
338 :param subject: subject of the mail
338 :param body: body of the mail
339 :param body: body of the mail
339 """
340 """
340 try:
341 try:
341 log = send_email.get_logger()
342 log = send_email.get_logger()
342 except:
343 except:
343 log = logging.getLogger(__name__)
344 log = logging.getLogger(__name__)
344
345
345 sa = get_session()
346 sa = get_session()
346 email_config = config
347 email_config = config
347
348
348 if not recipients:
349 if not recipients:
349 # if recipients are not defined we send to email_config + all admins
350 # if recipients are not defined we send to email_config + all admins
350 admins = [
351 admins = [
351 u.email for u in sa.query(User).filter(User.admin==True).all()
352 u.email for u in sa.query(User).filter(User.admin==True).all()
352 ]
353 ]
353 recipients = [email_config.get('email_to')] + admins
354 recipients = [email_config.get('email_to')] + admins
354
355
355 mail_from = email_config.get('app_email_from')
356 mail_from = email_config.get('app_email_from')
356 user = email_config.get('smtp_username')
357 user = email_config.get('smtp_username')
357 passwd = email_config.get('smtp_password')
358 passwd = email_config.get('smtp_password')
358 mail_server = email_config.get('smtp_server')
359 mail_server = email_config.get('smtp_server')
359 mail_port = email_config.get('smtp_port')
360 mail_port = email_config.get('smtp_port')
360 tls = str2bool(email_config.get('smtp_use_tls'))
361 tls = str2bool(email_config.get('smtp_use_tls'))
361 ssl = str2bool(email_config.get('smtp_use_ssl'))
362 ssl = str2bool(email_config.get('smtp_use_ssl'))
362 debug = str2bool(config.get('debug'))
363 debug = str2bool(config.get('debug'))
363 smtp_auth = email_config.get('smtp_auth')
364 smtp_auth = email_config.get('smtp_auth')
364
365
365 try:
366 try:
366 m = SmtpMailer(mail_from, user, passwd, mail_server,smtp_auth,
367 m = SmtpMailer(mail_from, user, passwd, mail_server,smtp_auth,
367 mail_port, ssl, tls, debug=debug)
368 mail_port, ssl, tls, debug=debug)
368 m.send(recipients, subject, body)
369 m.send(recipients, subject, body)
369 except:
370 except:
370 log.error('Mail sending failed')
371 log.error('Mail sending failed')
371 log.error(traceback.format_exc())
372 log.error(traceback.format_exc())
372 return False
373 return False
373 return True
374 return True
374
375
375
376
376 @task(ignore_result=True)
377 @task(ignore_result=True)
377 def create_repo_fork(form_data, cur_user):
378 def create_repo_fork(form_data, cur_user):
378 from rhodecode.model.repo import RepoModel
379 from rhodecode.model.repo import RepoModel
379 from vcs import get_backend
380 from vcs import get_backend
380
381
381 try:
382 try:
382 log = create_repo_fork.get_logger()
383 log = create_repo_fork.get_logger()
383 except:
384 except:
384 log = logging.getLogger(__name__)
385 log = logging.getLogger(__name__)
385
386
386 repo_model = RepoModel(get_session())
387 repo_model = RepoModel(get_session())
387 repo_model.create(form_data, cur_user, just_db=True, fork=True)
388 repo_model.create(form_data, cur_user, just_db=True, fork=True)
388 repo_name = form_data['repo_name']
389 repo_name = form_data['repo_name']
389 repos_path = get_repos_path()
390 repos_path = get_repos_path()
390 repo_path = os.path.join(repos_path, repo_name)
391 repo_path = os.path.join(repos_path, repo_name)
391 repo_fork_path = os.path.join(repos_path, form_data['fork_name'])
392 repo_fork_path = os.path.join(repos_path, form_data['fork_name'])
392 alias = form_data['repo_type']
393 alias = form_data['repo_type']
393
394
394 log.info('creating repo fork %s as %s', repo_name, repo_path)
395 log.info('creating repo fork %s as %s', repo_name, repo_path)
395 backend = get_backend(alias)
396 backend = get_backend(alias)
396 backend(str(repo_fork_path), create=True, src_url=str(repo_path))
397 backend(str(repo_fork_path), create=True, src_url=str(repo_path))
397
398
398
399
399 def __get_codes_stats(repo_name):
400 def __get_codes_stats(repo_name):
400 repos_path = get_repos_path()
401 repos_path = get_repos_path()
401 repo = get_repo(safe_str(os.path.join(repos_path, repo_name)))
402 repo = get_repo(safe_str(os.path.join(repos_path, repo_name)))
402 tip = repo.get_changeset()
403 tip = repo.get_changeset()
403 code_stats = {}
404 code_stats = {}
404
405
405 def aggregate(cs):
406 def aggregate(cs):
406 for f in cs[2]:
407 for f in cs[2]:
407 ext = lower(f.extension)
408 ext = lower(f.extension)
408 if ext in LANGUAGES_EXTENSIONS_MAP.keys() and not f.is_binary:
409 if ext in LANGUAGES_EXTENSIONS_MAP.keys() and not f.is_binary:
409 if ext in code_stats:
410 if ext in code_stats:
410 code_stats[ext] += 1
411 code_stats[ext] += 1
411 else:
412 else:
412 code_stats[ext] = 1
413 code_stats[ext] = 1
413
414
414 map(aggregate, tip.walk('/'))
415 map(aggregate, tip.walk('/'))
415
416
416 return code_stats or {}
417 return code_stats or {}
@@ -1,47 +1,47 b''
1 from rhodecode.tests import *
1 from rhodecode.tests import *
2 from rhodecode.model.db import Repository
2 from rhodecode.model.db import Repository
3 from rhodecode.lib.utils import invalidate_cache
3 from rhodecode.lib.utils import invalidate_cache
4
4
5
5 class TestSummaryController(TestController):
6 class TestSummaryController(TestController):
6
7
7 def test_index(self):
8 def test_index(self):
8 self.log_user()
9 self.log_user()
9 response = self.app.get(url(controller='summary',
10 response = self.app.get(url(controller='summary',
10 action='index', repo_name=HG_REPO))
11 action='index', repo_name=HG_REPO))
11
12
12 #repo type
13 #repo type
13 self.assertTrue("""<img style="margin-bottom:2px" class="icon" """
14 self.assertTrue("""<img style="margin-bottom:2px" class="icon" """
14 """title="Mercurial repository" alt="Mercurial """
15 """title="Mercurial repository" alt="Mercurial """
15 """repository" src="/images/icons/hgicon.png"/>"""
16 """repository" src="/images/icons/hgicon.png"/>"""
16 in response.body)
17 in response.body)
17 self.assertTrue("""<img style="margin-bottom:2px" class="icon" """
18 self.assertTrue("""<img style="margin-bottom:2px" class="icon" """
18 """title="public repository" alt="public """
19 """title="public repository" alt="public """
19 """repository" src="/images/icons/lock_open.png"/>"""
20 """repository" src="/images/icons/lock_open.png"/>"""
20 in response.body)
21 in response.body)
21
22
22 #codes stats
23 #codes stats
23 self._enable_stats()
24 self._enable_stats()
24
25
25
26 invalidate_cache('get_repo_cached_%s' % HG_REPO)
26 invalidate_cache('get_repo_cached_%s' % HG_REPO)
27 response = self.app.get(url(controller='summary', action='index',
27 response = self.app.get(url(controller='summary', action='index',
28 repo_name=HG_REPO))
28 repo_name=HG_REPO))
29
29
30 self.assertTrue("""var data = {"py": {"count": 42, "desc": """
30 response.mustcontain(
31 """["Python"]}, "rst": {"count": 11, "desc": """
31 """var data = {"py": {"count": 42, "desc": """
32 """["Rst"]}, "sh": {"count": 2, "desc": ["Bash"]}, """
32 """["Python"]}, "rst": {"count": 11, "desc": """
33 """"makefile": {"count": 1, "desc": ["Makefile", """
33 """["Rst"]}, "sh": {"count": 2, "desc": ["Bash"]}, """
34 """"Makefile"]}, "cfg": {"count": 1, "desc": ["Ini"]},"""
34 """"makefile": {"count": 1, "desc": ["Makefile", """
35 """ "css": {"count": 1, "desc": ["Css"]}, "bat": """
35 """"Makefile"]}, "cfg": {"count": 1, "desc": ["Ini"]},"""
36 """{"count": 1, "desc": ["Batch"]}};"""
36 """ "css": {"count": 1, "desc": ["Css"]}, "bat": """
37 in response.body)
37 """{"count": 1, "desc": ["Batch"]}};"""
38 )
38
39
39 # clone url...
40 # clone url...
40 self.assertTrue("""<input type="text" id="clone_url" readonly="readonly" value="hg clone http://test_admin@localhost:80/%s" size="70"/>""" % HG_REPO in response.body)
41 response.mustcontain("""<input type="text" id="clone_url" readonly="readonly" value="hg clone http://test_admin@localhost:80/%s" size="70"/>""" % HG_REPO)
41
42
42
43 def _enable_stats(self):
43 def _enable_stats(self):
44 r = Repository.get_by_repo_name(HG_REPO)
44 r = Repository.get_by_repo_name(HG_REPO)
45 r.enable_statistics = True
45 r.enable_statistics = True
46 self.sa.add(r)
46 self.sa.add(r)
47 self.sa.commit()
47 self.sa.commit()
@@ -1,126 +1,126 b''
1 import sys
1 import sys
2 from rhodecode import get_version
2 from rhodecode import get_version
3 from rhodecode import __platform__
3 from rhodecode import __platform__
4 from rhodecode import __license__
4 from rhodecode import __license__
5 from rhodecode import PLATFORM_OTHERS
5 from rhodecode import PLATFORM_OTHERS
6
6
7 py_version = sys.version_info
7 py_version = sys.version_info
8
8
9 if py_version < (2, 5):
9 if py_version < (2, 5):
10 raise Exception('RhodeCode requires python 2.5 or later')
10 raise Exception('RhodeCode requires python 2.5 or later')
11
11
12 requirements = [
12 requirements = [
13 "Pylons==1.0.0",
13 "Pylons==1.0.0",
14 "Beaker==1.5.4",
14 "Beaker==1.5.4",
15 "WebHelpers>=1.2",
15 "WebHelpers>=1.2",
16 "formencode==1.2.4",
16 "formencode==1.2.4",
17 "SQLAlchemy==0.7.3",
17 "SQLAlchemy==0.7.3",
18 "Mako==0.5.0",
18 "Mako==0.5.0",
19 "pygments>=1.4",
19 "pygments>=1.4",
20 "mercurial>=2.0,<2.1",
20 "mercurial>=2.0,<2.1",
21 "whoosh<1.8",
21 "whoosh<1.8",
22 "celery>=2.2.5,<2.3",
22 "celery>=2.2.5,<2.3",
23 "babel",
23 "babel",
24 "python-dateutil>=1.5.0,<2.0.0",
24 "python-dateutil>=1.5.0,<2.0.0",
25 "dulwich>=0.8.0,<0.9.0",
25 "dulwich>=0.8.0,<0.9.0",
26 "vcs==0.2.3",
26 "vcs==0.2.2",
27 "webob==1.0.8"
27 "webob==1.0.8"
28 ]
28 ]
29
29
30 dependency_links = [
30 dependency_links = [
31 ]
31 ]
32
32
33 classifiers = ['Development Status :: 5 - Production/Stable',
33 classifiers = ['Development Status :: 5 - Production/Stable',
34 'Environment :: Web Environment',
34 'Environment :: Web Environment',
35 'Framework :: Pylons',
35 'Framework :: Pylons',
36 'Intended Audience :: Developers',
36 'Intended Audience :: Developers',
37 'License :: OSI Approved :: GNU General Public License (GPL)',
37 'License :: OSI Approved :: GNU General Public License (GPL)',
38 'Operating System :: OS Independent',
38 'Operating System :: OS Independent',
39 'Programming Language :: Python',
39 'Programming Language :: Python',
40 'Programming Language :: Python :: 2.5',
40 'Programming Language :: Python :: 2.5',
41 'Programming Language :: Python :: 2.6',
41 'Programming Language :: Python :: 2.6',
42 'Programming Language :: Python :: 2.7', ]
42 'Programming Language :: Python :: 2.7', ]
43
43
44 if py_version < (2, 6):
44 if py_version < (2, 6):
45 requirements.append("simplejson")
45 requirements.append("simplejson")
46 requirements.append("pysqlite")
46 requirements.append("pysqlite")
47
47
48 if __platform__ in PLATFORM_OTHERS:
48 if __platform__ in PLATFORM_OTHERS:
49 requirements.append("py-bcrypt")
49 requirements.append("py-bcrypt")
50
50
51
51
52 # additional files from project that goes somewhere in the filesystem
52 # additional files from project that goes somewhere in the filesystem
53 # relative to sys.prefix
53 # relative to sys.prefix
54 data_files = []
54 data_files = []
55
55
56 # additional files that goes into package itself
56 # additional files that goes into package itself
57 package_data = {'rhodecode': ['i18n/*/LC_MESSAGES/*.mo', ], }
57 package_data = {'rhodecode': ['i18n/*/LC_MESSAGES/*.mo', ], }
58
58
59 description = ('Mercurial repository browser/management with '
59 description = ('Mercurial repository browser/management with '
60 'build in push/pull server and full text search')
60 'build in push/pull server and full text search')
61 keywords = ' '.join(['rhodecode', 'rhodiumcode', 'mercurial', 'git',
61 keywords = ' '.join(['rhodecode', 'rhodiumcode', 'mercurial', 'git',
62 'code review', 'repo groups', 'ldap'
62 'code review', 'repo groups', 'ldap'
63 'repository management', 'hgweb replacement'
63 'repository management', 'hgweb replacement'
64 'hgwebdir', 'gitweb replacement', 'serving hgweb', ])
64 'hgwebdir', 'gitweb replacement', 'serving hgweb', ])
65 # long description
65 # long description
66 try:
66 try:
67 readme_file = 'README.rst'
67 readme_file = 'README.rst'
68 changelog_file = 'docs/changelog.rst'
68 changelog_file = 'docs/changelog.rst'
69 long_description = open(readme_file).read() + '\n\n' + \
69 long_description = open(readme_file).read() + '\n\n' + \
70 open(changelog_file).read()
70 open(changelog_file).read()
71
71
72 except IOError, err:
72 except IOError, err:
73 sys.stderr.write("[WARNING] Cannot find file specified as "
73 sys.stderr.write("[WARNING] Cannot find file specified as "
74 "long_description (%s)\n or changelog (%s) skipping that file" \
74 "long_description (%s)\n or changelog (%s) skipping that file" \
75 % (readme_file, changelog_file))
75 % (readme_file, changelog_file))
76 long_description = description
76 long_description = description
77
77
78
78
79 try:
79 try:
80 from setuptools import setup, find_packages
80 from setuptools import setup, find_packages
81 except ImportError:
81 except ImportError:
82 from ez_setup import use_setuptools
82 from ez_setup import use_setuptools
83 use_setuptools()
83 use_setuptools()
84 from setuptools import setup, find_packages
84 from setuptools import setup, find_packages
85 # packages
85 # packages
86 packages = find_packages(exclude=['ez_setup'])
86 packages = find_packages(exclude=['ez_setup'])
87
87
88 setup(
88 setup(
89 name='RhodeCode',
89 name='RhodeCode',
90 version=get_version(),
90 version=get_version(),
91 description=description,
91 description=description,
92 long_description=long_description,
92 long_description=long_description,
93 keywords=keywords,
93 keywords=keywords,
94 license=__license__,
94 license=__license__,
95 author='Marcin Kuzminski',
95 author='Marcin Kuzminski',
96 author_email='marcin@python-works.com',
96 author_email='marcin@python-works.com',
97 dependency_links=dependency_links,
97 dependency_links=dependency_links,
98 url='http://rhodecode.org',
98 url='http://rhodecode.org',
99 install_requires=requirements,
99 install_requires=requirements,
100 classifiers=classifiers,
100 classifiers=classifiers,
101 setup_requires=["PasteScript>=1.6.3"],
101 setup_requires=["PasteScript>=1.6.3"],
102 data_files=data_files,
102 data_files=data_files,
103 packages=packages,
103 packages=packages,
104 include_package_data=True,
104 include_package_data=True,
105 test_suite='nose.collector',
105 test_suite='nose.collector',
106 package_data=package_data,
106 package_data=package_data,
107 message_extractors={'rhodecode': [
107 message_extractors={'rhodecode': [
108 ('**.py', 'python', None),
108 ('**.py', 'python', None),
109 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
109 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
110 ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}),
110 ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}),
111 ('public/**', 'ignore', None)]},
111 ('public/**', 'ignore', None)]},
112 zip_safe=False,
112 zip_safe=False,
113 paster_plugins=['PasteScript', 'Pylons'],
113 paster_plugins=['PasteScript', 'Pylons'],
114 entry_points="""
114 entry_points="""
115 [paste.app_factory]
115 [paste.app_factory]
116 main = rhodecode.config.middleware:make_app
116 main = rhodecode.config.middleware:make_app
117
117
118 [paste.app_install]
118 [paste.app_install]
119 main = pylons.util:PylonsInstaller
119 main = pylons.util:PylonsInstaller
120
120
121 [paste.global_paster_command]
121 [paste.global_paster_command]
122 make-index = rhodecode.lib.indexers:MakeIndex
122 make-index = rhodecode.lib.indexers:MakeIndex
123 upgrade-db = rhodecode.lib.dbmigrate:UpgradeDb
123 upgrade-db = rhodecode.lib.dbmigrate:UpgradeDb
124 celeryd=rhodecode.lib.celerypylons.commands:CeleryDaemonCommand
124 celeryd=rhodecode.lib.celerypylons.commands:CeleryDaemonCommand
125 """,
125 """,
126 )
126 )
General Comments 0
You need to be logged in to leave comments. Login now