##// END OF EJS Templates
fixed issue #165 trending source files are now stored in cache as ext only, and translated to description only when displaying, so future changes of mappings will take affect right away....
marcink -
r1244:0eceb478 beta
parent child Browse files
Show More
@@ -1,176 +1,183 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 OrderedDict, EmptyChangeset
41 from rhodecode.lib.utils import OrderedDict, 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 from rhodecode.lib.helpers import RepoPage
46 from rhodecode.lib.helpers import RepoPage
46
47
47 try:
48 try:
48 import json
49 import json
49 except ImportError:
50 except ImportError:
50 #python 2.5 compatibility
51 #python 2.5 compatibility
51 import simplejson as json
52 import simplejson as json
52 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
53
54
54
55
55 class SummaryController(BaseRepoController):
56 class SummaryController(BaseRepoController):
56
57
57 @LoginRequired()
58 @LoginRequired()
58 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
59 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
59 'repository.admin')
60 'repository.admin')
60 def __before__(self):
61 def __before__(self):
61 super(SummaryController, self).__before__()
62 super(SummaryController, self).__before__()
62
63
63 def index(self, repo_name):
64 def index(self, repo_name):
64
65
65 e = request.environ
66 e = request.environ
66 c.dbrepo = dbrepo = Repository.by_repo_name(repo_name)
67 c.dbrepo = dbrepo = Repository.by_repo_name(repo_name)
67
68
68 c.following = self.scm_model.is_following_repo(repo_name,
69 c.following = self.scm_model.is_following_repo(repo_name,
69 self.rhodecode_user.user_id)
70 self.rhodecode_user.user_id)
70
71
71 def url_generator(**kw):
72 def url_generator(**kw):
72 return url('shortlog_home', repo_name=repo_name, **kw)
73 return url('shortlog_home', repo_name=repo_name, **kw)
73
74
74 c.repo_changesets = RepoPage(c.rhodecode_repo, page=1,
75 c.repo_changesets = RepoPage(c.rhodecode_repo, page=1,
75 items_per_page=10, url=url_generator)
76 items_per_page=10, url=url_generator)
76
77
77 if self.rhodecode_user.username == 'default':
78 if self.rhodecode_user.username == 'default':
78 #for default(anonymous) user we don't need to pass credentials
79 #for default(anonymous) user we don't need to pass credentials
79 username = ''
80 username = ''
80 password = ''
81 password = ''
81 else:
82 else:
82 username = str(self.rhodecode_user.username)
83 username = str(self.rhodecode_user.username)
83 password = '@'
84 password = '@'
84
85
85 uri = u'%(proto)s://%(user)s%(pass)s%(host)s%(prefix)s/%(repo_name)s' \
86 uri = u'%(proto)s://%(user)s%(pass)s%(host)s%(prefix)s/%(repo_name)s' \
86 % {'proto': e.get('wsgi.url_scheme'),
87 % {'proto': e.get('wsgi.url_scheme'),
87 'user': username,
88 'user': username,
88 'pass': password,
89 'pass': password,
89 'host': e.get('HTTP_HOST'),
90 'host': e.get('HTTP_HOST'),
90 'prefix': e.get('SCRIPT_NAME'),
91 'prefix': e.get('SCRIPT_NAME'),
91 'repo_name': repo_name, }
92 'repo_name': repo_name, }
92 c.clone_repo_url = uri
93 c.clone_repo_url = uri
93 c.repo_tags = OrderedDict()
94 c.repo_tags = OrderedDict()
94 for name, hash in c.rhodecode_repo.tags.items()[:10]:
95 for name, hash in c.rhodecode_repo.tags.items()[:10]:
95 try:
96 try:
96 c.repo_tags[name] = c.rhodecode_repo.get_changeset(hash)
97 c.repo_tags[name] = c.rhodecode_repo.get_changeset(hash)
97 except ChangesetError:
98 except ChangesetError:
98 c.repo_tags[name] = EmptyChangeset(hash)
99 c.repo_tags[name] = EmptyChangeset(hash)
99
100
100 c.repo_branches = OrderedDict()
101 c.repo_branches = OrderedDict()
101 for name, hash in c.rhodecode_repo.branches.items()[:10]:
102 for name, hash in c.rhodecode_repo.branches.items()[:10]:
102 try:
103 try:
103 c.repo_branches[name] = c.rhodecode_repo.get_changeset(hash)
104 c.repo_branches[name] = c.rhodecode_repo.get_changeset(hash)
104 except ChangesetError:
105 except ChangesetError:
105 c.repo_branches[name] = EmptyChangeset(hash)
106 c.repo_branches[name] = EmptyChangeset(hash)
106
107
107 td = date.today() + timedelta(days=1)
108 td = date.today() + timedelta(days=1)
108 td_1m = td - timedelta(days=calendar.mdays[td.month])
109 td_1m = td - timedelta(days=calendar.mdays[td.month])
109 td_1y = td - timedelta(days=365)
110 td_1y = td - timedelta(days=365)
110
111
111 ts_min_m = mktime(td_1m.timetuple())
112 ts_min_m = mktime(td_1m.timetuple())
112 ts_min_y = mktime(td_1y.timetuple())
113 ts_min_y = mktime(td_1y.timetuple())
113 ts_max_y = mktime(td.timetuple())
114 ts_max_y = mktime(td.timetuple())
114
115
115 if dbrepo.enable_statistics:
116 if dbrepo.enable_statistics:
116 c.no_data_msg = _('No data loaded yet')
117 c.no_data_msg = _('No data loaded yet')
117 run_task(get_commits_stats, c.dbrepo.repo_name, ts_min_y, ts_max_y)
118 run_task(get_commits_stats, c.dbrepo.repo_name, ts_min_y, ts_max_y)
118 else:
119 else:
119 c.no_data_msg = _('Statistics are disabled for this repository')
120 c.no_data_msg = _('Statistics are disabled for this repository')
120 c.ts_min = ts_min_m
121 c.ts_min = ts_min_m
121 c.ts_max = ts_max_y
122 c.ts_max = ts_max_y
122
123
123 stats = self.sa.query(Statistics)\
124 stats = self.sa.query(Statistics)\
124 .filter(Statistics.repository == dbrepo)\
125 .filter(Statistics.repository == dbrepo)\
125 .scalar()
126 .scalar()
126
127
127 c.stats_percentage = 0
128 c.stats_percentage = 0
128
129
129 if stats and stats.languages:
130 if stats and stats.languages:
130 c.no_data = False is dbrepo.enable_statistics
131 c.no_data = False is dbrepo.enable_statistics
131 lang_stats = json.loads(stats.languages)
132 lang_stats = json.loads(stats.languages)
132 c.commit_data = stats.commit_activity
133 c.commit_data = stats.commit_activity
133 c.overview_data = stats.commit_activity_combined
134 c.overview_data = stats.commit_activity_combined
135
136 lang_stats = [(x, {"count":y,
137 "desc":LANGUAGES_EXTENSIONS_MAP.get(x)})
138 for x, y in lang_stats.items()]
139 print lang_stats
140
134 c.trending_languages = json.dumps(OrderedDict(
141 c.trending_languages = json.dumps(OrderedDict(
135 sorted(lang_stats.items(), reverse=True,
142 sorted(lang_stats, reverse=True,
136 key=lambda k: k[1])[:10]
143 key=lambda k: k[1])[:10]
137 )
144 )
138 )
145 )
139 last_rev = stats.stat_on_revision
146 last_rev = stats.stat_on_revision
140 c.repo_last_rev = c.rhodecode_repo.count() - 1 \
147 c.repo_last_rev = c.rhodecode_repo.count() - 1 \
141 if c.rhodecode_repo.revisions else 0
148 if c.rhodecode_repo.revisions else 0
142 if last_rev == 0 or c.repo_last_rev == 0:
149 if last_rev == 0 or c.repo_last_rev == 0:
143 pass
150 pass
144 else:
151 else:
145 c.stats_percentage = '%.2f' % ((float((last_rev)) /
152 c.stats_percentage = '%.2f' % ((float((last_rev)) /
146 c.repo_last_rev) * 100)
153 c.repo_last_rev) * 100)
147 else:
154 else:
148 c.commit_data = json.dumps({})
155 c.commit_data = json.dumps({})
149 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10]])
156 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10]])
150 c.trending_languages = json.dumps({})
157 c.trending_languages = json.dumps({})
151 c.no_data = True
158 c.no_data = True
152
159
153 c.enable_downloads = dbrepo.enable_downloads
160 c.enable_downloads = dbrepo.enable_downloads
154 if c.enable_downloads:
161 if c.enable_downloads:
155 c.download_options = self._get_download_links(c.rhodecode_repo)
162 c.download_options = self._get_download_links(c.rhodecode_repo)
156
163
157 return render('summary/summary.html')
164 return render('summary/summary.html')
158
165
159 def _get_download_links(self, repo):
166 def _get_download_links(self, repo):
160
167
161 download_l = []
168 download_l = []
162
169
163 branches_group = ([], _("Branches"))
170 branches_group = ([], _("Branches"))
164 tags_group = ([], _("Tags"))
171 tags_group = ([], _("Tags"))
165
172
166 for name, chs in c.rhodecode_repo.branches.items():
173 for name, chs in c.rhodecode_repo.branches.items():
167 #chs = chs.split(':')[-1]
174 #chs = chs.split(':')[-1]
168 branches_group[0].append((chs, name),)
175 branches_group[0].append((chs, name),)
169 download_l.append(branches_group)
176 download_l.append(branches_group)
170
177
171 for name, chs in c.rhodecode_repo.tags.items():
178 for name, chs in c.rhodecode_repo.tags.items():
172 #chs = chs.split(':')[-1]
179 #chs = chs.split(':')[-1]
173 tags_group[0].append((chs, name),)
180 tags_group[0].append((chs, name),)
174 download_l.append(tags_group)
181 download_l.append(tags_group)
175
182
176 return download_l
183 return download_l
@@ -1,404 +1,393 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
31
32 from time import mktime
32 from time import mktime
33 from operator import itemgetter
33 from operator import itemgetter
34 from pygments import lexers
35 from string import lower
34
36
35 from pylons import config
37 from pylons import config
36 from pylons.i18n.translation import _
38 from pylons.i18n.translation import _
37
39
38 from rhodecode.lib.celerylib import run_task, locked_task, str2bool
40 from rhodecode.lib.celerylib import run_task, locked_task, str2bool
39 from rhodecode.lib.helpers import person
41 from rhodecode.lib.helpers import person
40 from rhodecode.lib.smtp_mailer import SmtpMailer
42 from rhodecode.lib.smtp_mailer import SmtpMailer
41 from rhodecode.lib.utils import OrderedDict, add_cache
43 from rhodecode.lib.utils import OrderedDict, add_cache
42 from rhodecode.model import init_model
44 from rhodecode.model import init_model
43 from rhodecode.model import meta
45 from rhodecode.model import meta
44 from rhodecode.model.db import RhodeCodeUi
46 from rhodecode.model.db import RhodeCodeUi
45
47
46 from vcs.backends import get_repo
48 from vcs.backends import get_repo
47
49
48 from sqlalchemy import engine_from_config
50 from sqlalchemy import engine_from_config
49
51
50 add_cache(config)
52 add_cache(config)
51
53
52 try:
54 try:
53 import json
55 import json
54 except ImportError:
56 except ImportError:
55 #python 2.5 compatibility
57 #python 2.5 compatibility
56 import simplejson as json
58 import simplejson as json
57
59
58 __all__ = ['whoosh_index', 'get_commits_stats',
60 __all__ = ['whoosh_index', 'get_commits_stats',
59 'reset_user_password', 'send_email']
61 'reset_user_password', 'send_email']
60
62
61 CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
63 CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
62
64
65 LANGUAGES_EXTENSIONS_MAP = {}
66
67
68 def __clean(s):
69
70 s = s.lstrip('*')
71 s = s.lstrip('.')
72
73 if s.find('[') != -1:
74 exts = []
75 start, stop = s.find('['), s.find(']')
76
77 for suffix in s[start + 1:stop]:
78 exts.append(s[:s.find('[')] + suffix)
79 return map(lower, exts)
80 else:
81 return map(lower, [s])
82
83 for lx, t in sorted(lexers.LEXERS.items()):
84 m = map(__clean, t[-2])
85 if m:
86 m = reduce(lambda x, y: x + y, m)
87 for ext in m:
88 desc = lx.replace('Lexer', '')
89 if ext in LANGUAGES_EXTENSIONS_MAP:
90 if desc not in LANGUAGES_EXTENSIONS_MAP[ext]:
91 LANGUAGES_EXTENSIONS_MAP[ext].append(desc)
92 else:
93 LANGUAGES_EXTENSIONS_MAP[ext] = [desc]
94
95 #Additional mappings that are not present in the pygments lexers
96 # NOTE: that this will overide any mappings in LANGUAGES_EXTENSIONS_MAP
97 ADDITIONAL_MAPPINGS = {'xaml': 'XAML'}
98
99 LANGUAGES_EXTENSIONS_MAP.update(ADDITIONAL_MAPPINGS)
100
101
63 def get_session():
102 def get_session():
64 if CELERY_ON:
103 if CELERY_ON:
65 engine = engine_from_config(config, 'sqlalchemy.db1.')
104 engine = engine_from_config(config, 'sqlalchemy.db1.')
66 init_model(engine)
105 init_model(engine)
67 sa = meta.Session()
106 sa = meta.Session()
68 return sa
107 return sa
69
108
109
70 def get_repos_path():
110 def get_repos_path():
71 sa = get_session()
111 sa = get_session()
72 q = sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
112 q = sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
73 return q.ui_value
113 return q.ui_value
74
114
115
75 @task(ignore_result=True)
116 @task(ignore_result=True)
76 @locked_task
117 @locked_task
77 def whoosh_index(repo_location, full_index):
118 def whoosh_index(repo_location, full_index):
78 #log = whoosh_index.get_logger()
119 #log = whoosh_index.get_logger()
79 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
120 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
80 index_location = config['index_dir']
121 index_location = config['index_dir']
81 WhooshIndexingDaemon(index_location=index_location,
122 WhooshIndexingDaemon(index_location=index_location,
82 repo_location=repo_location, sa=get_session())\
123 repo_location=repo_location, sa=get_session())\
83 .run(full_index=full_index)
124 .run(full_index=full_index)
84
125
126
85 @task(ignore_result=True)
127 @task(ignore_result=True)
86 @locked_task
128 @locked_task
87 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
129 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
88 try:
130 try:
89 log = get_commits_stats.get_logger()
131 log = get_commits_stats.get_logger()
90 except:
132 except:
91 log = logging.getLogger(__name__)
133 log = logging.getLogger(__name__)
92
134
93 from rhodecode.model.db import Statistics, Repository
135 from rhodecode.model.db import Statistics, Repository
94
136
95 #for js data compatibilty
137 #for js data compatibilty
96 author_key_cleaner = lambda k: person(k).replace('"', "")
138 akc = lambda k: person(k).replace('"', "")
97
139
98 commits_by_day_author_aggregate = {}
140 co_day_auth_aggr = {}
99 commits_by_day_aggregate = {}
141 commits_by_day_aggregate = {}
100 repos_path = get_repos_path()
142 repos_path = get_repos_path()
101 p = os.path.join(repos_path, repo_name)
143 p = os.path.join(repos_path, repo_name)
102 repo = get_repo(p)
144 repo = get_repo(p)
103
145
104 skip_date_limit = True
146 skip_date_limit = True
105 parse_limit = int(config['app_conf'].get('commit_parse_limit'))
147 parse_limit = int(config['app_conf'].get('commit_parse_limit'))
106 last_rev = 0
148 last_rev = 0
107 last_cs = None
149 last_cs = None
108 timegetter = itemgetter('time')
150 timegetter = itemgetter('time')
109
151
110 sa = get_session()
152 sa = get_session()
111
153
112 dbrepo = sa.query(Repository)\
154 dbrepo = sa.query(Repository)\
113 .filter(Repository.repo_name == repo_name).scalar()
155 .filter(Repository.repo_name == repo_name).scalar()
114 cur_stats = sa.query(Statistics)\
156 cur_stats = sa.query(Statistics)\
115 .filter(Statistics.repository == dbrepo).scalar()
157 .filter(Statistics.repository == dbrepo).scalar()
116
158
117 if cur_stats is not None:
159 if cur_stats is not None:
118 last_rev = cur_stats.stat_on_revision
160 last_rev = cur_stats.stat_on_revision
119
161
120 #return if repo is empty
162 #return if repo is empty
121 if not repo.revisions:
163 if not repo.revisions:
122 return True
164 return True
123
165
124 if last_rev == repo.get_changeset().revision and len(repo.revisions) > 1:
166 if last_rev == repo.get_changeset().revision and len(repo.revisions) > 1:
125 #pass silently without any work if we're not on first revision or
167 #pass silently without any work if we're not on first revision or
126 #current state of parsing revision(from db marker) is the last revision
168 #current state of parsing revision(from db marker) is the last revision
127 return True
169 return True
128
170
129 if cur_stats:
171 if cur_stats:
130 commits_by_day_aggregate = OrderedDict(
172 commits_by_day_aggregate = OrderedDict(
131 json.loads(
173 json.loads(
132 cur_stats.commit_activity_combined))
174 cur_stats.commit_activity_combined))
133 commits_by_day_author_aggregate = json.loads(cur_stats.commit_activity)
175 co_day_auth_aggr = json.loads(cur_stats.commit_activity)
134
176
135 log.debug('starting parsing %s', parse_limit)
177 log.debug('starting parsing %s', parse_limit)
136 lmktime = mktime
178 lmktime = mktime
137
179
138 last_rev = last_rev + 1 if last_rev > 0 else last_rev
180 last_rev = last_rev + 1 if last_rev > 0 else last_rev
139
181
140 for cs in repo[last_rev:last_rev + parse_limit]:
182 for cs in repo[last_rev:last_rev + parse_limit]:
141 last_cs = cs #remember last parsed changeset
183 last_cs = cs # remember last parsed changeset
142 k = lmktime([cs.date.timetuple()[0], cs.date.timetuple()[1],
184 k = lmktime([cs.date.timetuple()[0], cs.date.timetuple()[1],
143 cs.date.timetuple()[2], 0, 0, 0, 0, 0, 0])
185 cs.date.timetuple()[2], 0, 0, 0, 0, 0, 0])
144
186
145 if commits_by_day_author_aggregate.has_key(author_key_cleaner(cs.author)):
187 if akc(cs.author) in co_day_auth_aggr:
146 try:
188 try:
147 l = [timegetter(x) for x in commits_by_day_author_aggregate\
189 l = [timegetter(x) for x in
148 [author_key_cleaner(cs.author)]['data']]
190 co_day_auth_aggr[akc(cs.author)]['data']]
149 time_pos = l.index(k)
191 time_pos = l.index(k)
150 except ValueError:
192 except ValueError:
151 time_pos = False
193 time_pos = False
152
194
153 if time_pos >= 0 and time_pos is not False:
195 if time_pos >= 0 and time_pos is not False:
154
196
155 datadict = commits_by_day_author_aggregate\
197 datadict = co_day_auth_aggr[akc(cs.author)]['data'][time_pos]
156 [author_key_cleaner(cs.author)]['data'][time_pos]
157
198
158 datadict["commits"] += 1
199 datadict["commits"] += 1
159 datadict["added"] += len(cs.added)
200 datadict["added"] += len(cs.added)
160 datadict["changed"] += len(cs.changed)
201 datadict["changed"] += len(cs.changed)
161 datadict["removed"] += len(cs.removed)
202 datadict["removed"] += len(cs.removed)
162
203
163 else:
204 else:
164 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
205 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
165
206
166 datadict = {"time":k,
207 datadict = {"time": k,
167 "commits":1,
208 "commits": 1,
168 "added":len(cs.added),
209 "added": len(cs.added),
169 "changed":len(cs.changed),
210 "changed": len(cs.changed),
170 "removed":len(cs.removed),
211 "removed": len(cs.removed),
171 }
212 }
172 commits_by_day_author_aggregate\
213 co_day_auth_aggr[akc(cs.author)]['data']\
173 [author_key_cleaner(cs.author)]['data'].append(datadict)
214 .append(datadict)
174
215
175 else:
216 else:
176 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
217 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
177 commits_by_day_author_aggregate[author_key_cleaner(cs.author)] = {
218 co_day_auth_aggr[akc(cs.author)] = {
178 "label":author_key_cleaner(cs.author),
219 "label": akc(cs.author),
179 "data":[{"time":k,
220 "data": [{"time":k,
180 "commits":1,
221 "commits":1,
181 "added":len(cs.added),
222 "added":len(cs.added),
182 "changed":len(cs.changed),
223 "changed":len(cs.changed),
183 "removed":len(cs.removed),
224 "removed":len(cs.removed),
184 }],
225 }],
185 "schema":["commits"],
226 "schema": ["commits"],
186 }
227 }
187
228
188 #gather all data by day
229 #gather all data by day
189 if commits_by_day_aggregate.has_key(k):
230 if k in commits_by_day_aggregate:
190 commits_by_day_aggregate[k] += 1
231 commits_by_day_aggregate[k] += 1
191 else:
232 else:
192 commits_by_day_aggregate[k] = 1
233 commits_by_day_aggregate[k] = 1
193
234
194 overview_data = sorted(commits_by_day_aggregate.items(), key=itemgetter(0))
235 overview_data = sorted(commits_by_day_aggregate.items(), key=itemgetter(0))
195 if not commits_by_day_author_aggregate:
236 if not co_day_auth_aggr:
196 commits_by_day_author_aggregate[author_key_cleaner(repo.contact)] = {
237 co_day_auth_aggr[akc(repo.contact)] = {
197 "label":author_key_cleaner(repo.contact),
238 "label": akc(repo.contact),
198 "data":[0, 1],
239 "data": [0, 1],
199 "schema":["commits"],
240 "schema": ["commits"],
200 }
241 }
201
242
202 stats = cur_stats if cur_stats else Statistics()
243 stats = cur_stats if cur_stats else Statistics()
203 stats.commit_activity = json.dumps(commits_by_day_author_aggregate)
244 stats.commit_activity = json.dumps(co_day_auth_aggr)
204 stats.commit_activity_combined = json.dumps(overview_data)
245 stats.commit_activity_combined = json.dumps(overview_data)
205
246
206 log.debug('last revison %s', last_rev)
247 log.debug('last revison %s', last_rev)
207 leftovers = len(repo.revisions[last_rev:])
248 leftovers = len(repo.revisions[last_rev:])
208 log.debug('revisions to parse %s', leftovers)
249 log.debug('revisions to parse %s', leftovers)
209
250
210 if last_rev == 0 or leftovers < parse_limit:
251 if last_rev == 0 or leftovers < parse_limit:
211 log.debug('getting code trending stats')
252 log.debug('getting code trending stats')
212 stats.languages = json.dumps(__get_codes_stats(repo_name))
253 stats.languages = json.dumps(__get_codes_stats(repo_name))
213
254
214 try:
255 try:
215 stats.repository = dbrepo
256 stats.repository = dbrepo
216 stats.stat_on_revision = last_cs.revision if last_cs else 0
257 stats.stat_on_revision = last_cs.revision if last_cs else 0
217 sa.add(stats)
258 sa.add(stats)
218 sa.commit()
259 sa.commit()
219 except:
260 except:
220 log.error(traceback.format_exc())
261 log.error(traceback.format_exc())
221 sa.rollback()
262 sa.rollback()
222 return False
263 return False
223 if len(repo.revisions) > 1:
264 if len(repo.revisions) > 1:
224 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
265 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
225
266
226 return True
267 return True
227
268
269
228 @task(ignore_result=True)
270 @task(ignore_result=True)
229 def reset_user_password(user_email):
271 def reset_user_password(user_email):
230 try:
272 try:
231 log = reset_user_password.get_logger()
273 log = reset_user_password.get_logger()
232 except:
274 except:
233 log = logging.getLogger(__name__)
275 log = logging.getLogger(__name__)
234
276
235 from rhodecode.lib import auth
277 from rhodecode.lib import auth
236 from rhodecode.model.db import User
278 from rhodecode.model.db import User
237
279
238 try:
280 try:
239 try:
281 try:
240 sa = get_session()
282 sa = get_session()
241 user = sa.query(User).filter(User.email == user_email).scalar()
283 user = sa.query(User).filter(User.email == user_email).scalar()
242 new_passwd = auth.PasswordGenerator().gen_password(8,
284 new_passwd = auth.PasswordGenerator().gen_password(8,
243 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
285 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
244 if user:
286 if user:
245 user.password = auth.get_crypt_password(new_passwd)
287 user.password = auth.get_crypt_password(new_passwd)
246 user.api_key = auth.generate_api_key(user.username)
288 user.api_key = auth.generate_api_key(user.username)
247 sa.add(user)
289 sa.add(user)
248 sa.commit()
290 sa.commit()
249 log.info('change password for %s', user_email)
291 log.info('change password for %s', user_email)
250 if new_passwd is None:
292 if new_passwd is None:
251 raise Exception('unable to generate new password')
293 raise Exception('unable to generate new password')
252
294
253 except:
295 except:
254 log.error(traceback.format_exc())
296 log.error(traceback.format_exc())
255 sa.rollback()
297 sa.rollback()
256
298
257 run_task(send_email, user_email,
299 run_task(send_email, user_email,
258 "Your new rhodecode password",
300 "Your new rhodecode password",
259 'Your new rhodecode password:%s' % (new_passwd))
301 'Your new rhodecode password:%s' % (new_passwd))
260 log.info('send new password mail to %s', user_email)
302 log.info('send new password mail to %s', user_email)
261
303
262
263 except:
304 except:
264 log.error('Failed to update user password')
305 log.error('Failed to update user password')
265 log.error(traceback.format_exc())
306 log.error(traceback.format_exc())
266
307
267 return True
308 return True
268
309
310
269 @task(ignore_result=True)
311 @task(ignore_result=True)
270 def send_email(recipients, subject, body):
312 def send_email(recipients, subject, body):
271 """
313 """
272 Sends an email with defined parameters from the .ini files.
314 Sends an email with defined parameters from the .ini files.
273
315
274
316
275 :param recipients: list of recipients, it this is empty the defined email
317 :param recipients: list of recipients, it this is empty the defined email
276 address from field 'email_to' is used instead
318 address from field 'email_to' is used instead
277 :param subject: subject of the mail
319 :param subject: subject of the mail
278 :param body: body of the mail
320 :param body: body of the mail
279 """
321 """
280 try:
322 try:
281 log = send_email.get_logger()
323 log = send_email.get_logger()
282 except:
324 except:
283 log = logging.getLogger(__name__)
325 log = logging.getLogger(__name__)
284
326
285 email_config = config
327 email_config = config
286
328
287 if not recipients:
329 if not recipients:
288 recipients = [email_config.get('email_to')]
330 recipients = [email_config.get('email_to')]
289
331
290 mail_from = email_config.get('app_email_from')
332 mail_from = email_config.get('app_email_from')
291 user = email_config.get('smtp_username')
333 user = email_config.get('smtp_username')
292 passwd = email_config.get('smtp_password')
334 passwd = email_config.get('smtp_password')
293 mail_server = email_config.get('smtp_server')
335 mail_server = email_config.get('smtp_server')
294 mail_port = email_config.get('smtp_port')
336 mail_port = email_config.get('smtp_port')
295 tls = str2bool(email_config.get('smtp_use_tls'))
337 tls = str2bool(email_config.get('smtp_use_tls'))
296 ssl = str2bool(email_config.get('smtp_use_ssl'))
338 ssl = str2bool(email_config.get('smtp_use_ssl'))
297 debug = str2bool(config.get('debug'))
339 debug = str2bool(config.get('debug'))
298
340
299 try:
341 try:
300 m = SmtpMailer(mail_from, user, passwd, mail_server,
342 m = SmtpMailer(mail_from, user, passwd, mail_server,
301 mail_port, ssl, tls, debug=debug)
343 mail_port, ssl, tls, debug=debug)
302 m.send(recipients, subject, body)
344 m.send(recipients, subject, body)
303 except:
345 except:
304 log.error('Mail sending failed')
346 log.error('Mail sending failed')
305 log.error(traceback.format_exc())
347 log.error(traceback.format_exc())
306 return False
348 return False
307 return True
349 return True
308
350
351
309 @task(ignore_result=True)
352 @task(ignore_result=True)
310 def create_repo_fork(form_data, cur_user):
353 def create_repo_fork(form_data, cur_user):
311 try:
354 try:
312 log = create_repo_fork.get_logger()
355 log = create_repo_fork.get_logger()
313 except:
356 except:
314 log = logging.getLogger(__name__)
357 log = logging.getLogger(__name__)
315
358
316 from rhodecode.model.repo import RepoModel
359 from rhodecode.model.repo import RepoModel
317 from vcs import get_backend
360 from vcs import get_backend
318
361
319 repo_model = RepoModel(get_session())
362 repo_model = RepoModel(get_session())
320 repo_model.create(form_data, cur_user, just_db=True, fork=True)
363 repo_model.create(form_data, cur_user, just_db=True, fork=True)
321 repo_name = form_data['repo_name']
364 repo_name = form_data['repo_name']
322 repos_path = get_repos_path()
365 repos_path = get_repos_path()
323 repo_path = os.path.join(repos_path, repo_name)
366 repo_path = os.path.join(repos_path, repo_name)
324 repo_fork_path = os.path.join(repos_path, form_data['fork_name'])
367 repo_fork_path = os.path.join(repos_path, form_data['fork_name'])
325 alias = form_data['repo_type']
368 alias = form_data['repo_type']
326
369
327 log.info('creating repo fork %s as %s', repo_name, repo_path)
370 log.info('creating repo fork %s as %s', repo_name, repo_path)
328 backend = get_backend(alias)
371 backend = get_backend(alias)
329 backend(str(repo_fork_path), create=True, src_url=str(repo_path))
372 backend(str(repo_fork_path), create=True, src_url=str(repo_path))
330
373
374
331 def __get_codes_stats(repo_name):
375 def __get_codes_stats(repo_name):
332 LANGUAGES_EXTENSIONS_MAP = {'scm': 'Scheme', 'asmx': 'VbNetAspx', 'Rout':
333 'RConsole', 'rest': 'Rst', 'abap': 'ABAP', 'go': 'Go', 'phtml': 'HtmlPhp',
334 'ns2': 'Newspeak', 'xml': 'EvoqueXml', 'sh-session': 'BashSession', 'ads':
335 'Ada', 'clj': 'Clojure', 'll': 'Llvm', 'ebuild': 'Bash', 'adb': 'Ada',
336 'ada': 'Ada', 'c++-objdump': 'CppObjdump', 'aspx':
337 'VbNetAspx', 'ksh': 'Bash', 'coffee': 'CoffeeScript', 'vert': 'GLShader',
338 'Makefile.*': 'Makefile', 'di': 'D', 'dpatch': 'DarcsPatch', 'rake':
339 'Ruby', 'moo': 'MOOCode', 'erl-sh': 'ErlangShell', 'geo': 'GLShader',
340 'pov': 'Povray', 'bas': 'VbNet', 'bat': 'Batch', 'd': 'D', 'lisp':
341 'CommonLisp', 'h': 'C', 'rbx': 'Ruby', 'tcl': 'Tcl', 'c++': 'Cpp', 'md':
342 'MiniD', '.vimrc': 'Vim', 'xsd': 'Xml', 'ml': 'Ocaml', 'el': 'CommonLisp',
343 'befunge': 'Befunge', 'xsl': 'Xslt', 'pyx': 'Cython', 'cfm':
344 'ColdfusionHtml', 'evoque': 'Evoque', 'cfg': 'Ini', 'htm': 'Html',
345 'Makefile': 'Makefile', 'cfc': 'ColdfusionHtml', 'tex': 'Tex', 'cs':
346 'CSharp', 'mxml': 'Mxml', 'patch': 'Diff', 'apache.conf': 'ApacheConf',
347 'scala': 'Scala', 'applescript': 'AppleScript', 'GNUmakefile': 'Makefile',
348 'c-objdump': 'CObjdump', 'lua': 'Lua', 'apache2.conf': 'ApacheConf', 'rb':
349 'Ruby', 'gemspec': 'Ruby', 'rl': 'RagelObjectiveC', 'vala': 'Vala', 'tmpl':
350 'Cheetah', 'bf': 'Brainfuck', 'plt': 'Gnuplot', 'G': 'AntlrRuby', 'xslt':
351 'Xslt', 'flxh': 'Felix', 'asax': 'VbNetAspx', 'Rakefile': 'Ruby', 'S': 'S',
352 'wsdl': 'Xml', 'js': 'Javascript', 'autodelegate': 'Myghty', 'properties':
353 'Ini', 'bash': 'Bash', 'c': 'C', 'g': 'AntlrRuby', 'r3': 'Rebol', 's':
354 'Gas', 'ashx': 'VbNetAspx', 'cxx': 'Cpp', 'boo': 'Boo', 'prolog': 'Prolog',
355 'sqlite3-console': 'SqliteConsole', 'cl': 'CommonLisp', 'cc': 'Cpp', 'pot':
356 'Gettext', 'vim': 'Vim', 'pxi': 'Cython', 'yaml': 'Yaml', 'SConstruct':
357 'Python', 'diff': 'Diff', 'txt': 'Text', 'cw': 'Redcode', 'pxd': 'Cython',
358 'plot': 'Gnuplot', 'java': 'Java', 'hrl': 'Erlang', 'py': 'Python',
359 'makefile': 'Makefile', 'squid.conf': 'SquidConf', 'asm': 'Nasm', 'toc':
360 'Tex', 'kid': 'Genshi', 'rhtml': 'Rhtml', 'po': 'Gettext', 'pl': 'Prolog',
361 'pm': 'Perl', 'hx': 'Haxe', 'ascx': 'VbNetAspx', 'ooc': 'Ooc', 'asy':
362 'Asymptote', 'hs': 'Haskell', 'SConscript': 'Python', 'pytb':
363 'PythonTraceback', 'myt': 'Myghty', 'hh': 'Cpp', 'R': 'S', 'aux': 'Tex',
364 'rst': 'Rst', 'cpp-objdump': 'CppObjdump', 'lgt': 'Logtalk', 'rss': 'Xml',
365 'flx': 'Felix', 'b': 'Brainfuck', 'f': 'Fortran', 'rbw': 'Ruby',
366 '.htaccess': 'ApacheConf', 'cxx-objdump': 'CppObjdump', 'j': 'ObjectiveJ',
367 'mll': 'Ocaml', 'yml': 'Yaml', 'mu': 'MuPAD', 'r': 'Rebol', 'ASM': 'Nasm',
368 'erl': 'Erlang', 'mly': 'Ocaml', 'mo': 'Modelica', 'def': 'Modula2', 'ini':
369 'Ini', 'control': 'DebianControl', 'vb': 'VbNet', 'vapi': 'Vala', 'pro':
370 'Prolog', 'spt': 'Cheetah', 'mli': 'Ocaml', 'as': 'ActionScript3', 'cmd':
371 'Batch', 'cpp': 'Cpp', 'io': 'Io', 'tac': 'Python', 'haml': 'Haml', 'rkt':
372 'Racket', 'st':'Smalltalk', 'inc': 'Povray', 'pas': 'Delphi', 'cmake':
373 'CMake', 'csh':'Tcsh', 'hpp': 'Cpp', 'feature': 'Gherkin', 'html': 'Html',
374 'php':'Php', 'php3':'Php', 'php4':'Php', 'php5':'Php', 'xhtml': 'Html',
375 'hxx': 'Cpp', 'eclass': 'Bash', 'css': 'Css',
376 'frag': 'GLShader', 'd-objdump': 'DObjdump', 'weechatlog': 'IrcLogs',
377 'tcsh': 'Tcsh', 'objdump': 'Objdump', 'pyw': 'Python', 'h++': 'Cpp',
378 'py3tb': 'Python3Traceback', 'jsp': 'Jsp', 'sql': 'Sql', 'mak': 'Makefile',
379 'php': 'Php', 'mao': 'Mako', 'man': 'Groff', 'dylan': 'Dylan', 'sass':
380 'Sass', 'cfml': 'ColdfusionHtml', 'darcspatch': 'DarcsPatch', 'tpl':
381 'Smarty', 'm': 'ObjectiveC', 'f90': 'Fortran', 'mod': 'Modula2', 'sh':
382 'Bash', 'lhs': 'LiterateHaskell', 'sources.list': 'SourcesList', 'axd':
383 'VbNetAspx', 'sc': 'Python'}
384
385 repos_path = get_repos_path()
376 repos_path = get_repos_path()
386 p = os.path.join(repos_path, repo_name)
377 p = os.path.join(repos_path, repo_name)
387 repo = get_repo(p)
378 repo = get_repo(p)
388 tip = repo.get_changeset()
379 tip = repo.get_changeset()
389 code_stats = {}
380 code_stats = {}
390
381
391 def aggregate(cs):
382 def aggregate(cs):
392 for f in cs[2]:
383 for f in cs[2]:
393 ext = f.extension
384 ext = lower(f.extension)
394 key = LANGUAGES_EXTENSIONS_MAP.get(ext, ext)
395 key = key or ext
396 if ext in LANGUAGES_EXTENSIONS_MAP.keys() and not f.is_binary:
385 if ext in LANGUAGES_EXTENSIONS_MAP.keys() and not f.is_binary:
397 if code_stats.has_key(key):
386 if ext in code_stats:
398 code_stats[key] += 1
387 code_stats[ext] += 1
399 else:
388 else:
400 code_stats[key] = 1
389 code_stats[ext] = 1
401
390
402 map(aggregate, tip.walk('/'))
391 map(aggregate, tip.walk('/'))
403
392
404 return code_stats or {}
393 return code_stats or {}
@@ -1,704 +1,704 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${c.repo_name} ${_('Summary')} - ${c.rhodecode_name}
4 ${c.repo_name} ${_('Summary')} - ${c.rhodecode_name}
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(u'Home',h.url('/'))}
8 ${h.link_to(u'Home',h.url('/'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(c.dbrepo.just_name,h.url('summary_home',repo_name=c.repo_name))}
10 ${h.link_to(c.dbrepo.just_name,h.url('summary_home',repo_name=c.repo_name))}
11 &raquo;
11 &raquo;
12 ${_('summary')}
12 ${_('summary')}
13 </%def>
13 </%def>
14
14
15 <%def name="page_nav()">
15 <%def name="page_nav()">
16 ${self.menu('summary')}
16 ${self.menu('summary')}
17 </%def>
17 </%def>
18
18
19 <%def name="main()">
19 <%def name="main()">
20 <div class="box box-left">
20 <div class="box box-left">
21 <!-- box / title -->
21 <!-- box / title -->
22 <div class="title">
22 <div class="title">
23 ${self.breadcrumbs()}
23 ${self.breadcrumbs()}
24 </div>
24 </div>
25 <!-- end box / title -->
25 <!-- end box / title -->
26 <div class="form">
26 <div class="form">
27 <div class="fields">
27 <div class="fields">
28
28
29 <div class="field">
29 <div class="field">
30 <div class="label">
30 <div class="label">
31 <label>${_('Name')}:</label>
31 <label>${_('Name')}:</label>
32 </div>
32 </div>
33 <div class="input-short">
33 <div class="input-short">
34 %if c.rhodecode_user.username != 'default':
34 %if c.rhodecode_user.username != 'default':
35 %if c.following:
35 %if c.following:
36 <span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
36 <span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
37 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
37 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
38 </span>
38 </span>
39 %else:
39 %else:
40 <span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
40 <span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
41 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
41 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
42 </span>
42 </span>
43 %endif
43 %endif
44 %endif:
44 %endif:
45
45
46 ##REPO TYPE
46 ##REPO TYPE
47 %if c.dbrepo.repo_type =='hg':
47 %if c.dbrepo.repo_type =='hg':
48 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url("/images/icons/hgicon.png")}"/>
48 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url("/images/icons/hgicon.png")}"/>
49 %endif
49 %endif
50 %if c.dbrepo.repo_type =='git':
50 %if c.dbrepo.repo_type =='git':
51 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url("/images/icons/giticon.png")}"/>
51 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url("/images/icons/giticon.png")}"/>
52 %endif
52 %endif
53
53
54 ##PUBLIC/PRIVATE
54 ##PUBLIC/PRIVATE
55 %if c.dbrepo.private:
55 %if c.dbrepo.private:
56 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url("/images/icons/lock.png")}"/>
56 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url("/images/icons/lock.png")}"/>
57 %else:
57 %else:
58 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url("/images/icons/lock_open.png")}"/>
58 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url("/images/icons/lock_open.png")}"/>
59 %endif
59 %endif
60
60
61 ##REPO NAME
61 ##REPO NAME
62 <span style="font-size: 1.6em;font-weight: bold;vertical-align: baseline;clear:right">${h.repo_link(c.dbrepo.groups_and_repo)}</span>
62 <span style="font-size: 1.6em;font-weight: bold;vertical-align: baseline;clear:right">${h.repo_link(c.dbrepo.groups_and_repo)}</span>
63
63
64 ##FORK
64 ##FORK
65 %if c.dbrepo.fork:
65 %if c.dbrepo.fork:
66 <div style="margin-top:5px;clear:both"">
66 <div style="margin-top:5px;clear:both"">
67 <a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}">
67 <a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}">
68 <img class="icon" alt="${_('public')}"
68 <img class="icon" alt="${_('public')}"
69 title="${_('Fork of')} ${c.dbrepo.fork.repo_name}"
69 title="${_('Fork of')} ${c.dbrepo.fork.repo_name}"
70 src="${h.url("/images/icons/arrow_divide.png")}"/>
70 src="${h.url("/images/icons/arrow_divide.png")}"/>
71 ${_('Fork of')} ${c.dbrepo.fork.repo_name}
71 ${_('Fork of')} ${c.dbrepo.fork.repo_name}
72 </a>
72 </a>
73 </div>
73 </div>
74 %endif
74 %endif
75 ##REMOTE
75 ##REMOTE
76 %if c.dbrepo.clone_uri:
76 %if c.dbrepo.clone_uri:
77 <div style="margin-top:5px;clear:both">
77 <div style="margin-top:5px;clear:both">
78 <a href="${h.url(str(c.dbrepo.clone_uri))}">
78 <a href="${h.url(str(c.dbrepo.clone_uri))}">
79 <img class="icon" alt="${_('remote clone')}"
79 <img class="icon" alt="${_('remote clone')}"
80 title="${_('Clone from')} ${c.dbrepo.clone_uri}"
80 title="${_('Clone from')} ${c.dbrepo.clone_uri}"
81 src="${h.url("/images/icons/connect.png")}"/>
81 src="${h.url("/images/icons/connect.png")}"/>
82 ${_('Clone from')} ${c.dbrepo.clone_uri}
82 ${_('Clone from')} ${c.dbrepo.clone_uri}
83 </a>
83 </a>
84 </div>
84 </div>
85 %endif
85 %endif
86 </div>
86 </div>
87 </div>
87 </div>
88
88
89
89
90 <div class="field">
90 <div class="field">
91 <div class="label">
91 <div class="label">
92 <label>${_('Description')}:</label>
92 <label>${_('Description')}:</label>
93 </div>
93 </div>
94 <div class="input-short">
94 <div class="input-short">
95 ${c.dbrepo.description}
95 ${c.dbrepo.description}
96 </div>
96 </div>
97 </div>
97 </div>
98
98
99
99
100 <div class="field">
100 <div class="field">
101 <div class="label">
101 <div class="label">
102 <label>${_('Contact')}:</label>
102 <label>${_('Contact')}:</label>
103 </div>
103 </div>
104 <div class="input-short">
104 <div class="input-short">
105 <div class="gravatar">
105 <div class="gravatar">
106 <img alt="gravatar" src="${h.gravatar_url(c.dbrepo.user.email)}"/>
106 <img alt="gravatar" src="${h.gravatar_url(c.dbrepo.user.email)}"/>
107 </div>
107 </div>
108 ${_('Username')}: ${c.dbrepo.user.username}<br/>
108 ${_('Username')}: ${c.dbrepo.user.username}<br/>
109 ${_('Name')}: ${c.dbrepo.user.name} ${c.dbrepo.user.lastname}<br/>
109 ${_('Name')}: ${c.dbrepo.user.name} ${c.dbrepo.user.lastname}<br/>
110 ${_('Email')}: <a href="mailto:${c.dbrepo.user.email}">${c.dbrepo.user.email}</a>
110 ${_('Email')}: <a href="mailto:${c.dbrepo.user.email}">${c.dbrepo.user.email}</a>
111 </div>
111 </div>
112 </div>
112 </div>
113
113
114 <div class="field">
114 <div class="field">
115 <div class="label">
115 <div class="label">
116 <label>${_('Last change')}:</label>
116 <label>${_('Last change')}:</label>
117 </div>
117 </div>
118 <div class="input-short">
118 <div class="input-short">
119 <b>${'r%s:%s' % (h.get_changeset_safe(c.rhodecode_repo,'tip').revision,
119 <b>${'r%s:%s' % (h.get_changeset_safe(c.rhodecode_repo,'tip').revision,
120 h.get_changeset_safe(c.rhodecode_repo,'tip').short_id)}</b> -
120 h.get_changeset_safe(c.rhodecode_repo,'tip').short_id)}</b> -
121 <span class="tooltip" title="${c.rhodecode_repo.last_change}">
121 <span class="tooltip" title="${c.rhodecode_repo.last_change}">
122 ${h.age(c.rhodecode_repo.last_change)}</span><br/>
122 ${h.age(c.rhodecode_repo.last_change)}</span><br/>
123 ${_('by')} ${h.get_changeset_safe(c.rhodecode_repo,'tip').author}
123 ${_('by')} ${h.get_changeset_safe(c.rhodecode_repo,'tip').author}
124
124
125 </div>
125 </div>
126 </div>
126 </div>
127
127
128 <div class="field">
128 <div class="field">
129 <div class="label">
129 <div class="label">
130 <label>${_('Clone url')}:</label>
130 <label>${_('Clone url')}:</label>
131 </div>
131 </div>
132 <div class="input-short">
132 <div class="input-short">
133 <input type="text" id="clone_url" readonly="readonly" value="hg clone ${c.clone_repo_url}" size="70"/>
133 <input type="text" id="clone_url" readonly="readonly" value="hg clone ${c.clone_repo_url}" size="70"/>
134 </div>
134 </div>
135 </div>
135 </div>
136
136
137 <div class="field">
137 <div class="field">
138 <div class="label">
138 <div class="label">
139 <label>${_('Trending source files')}:</label>
139 <label>${_('Trending source files')}:</label>
140 </div>
140 </div>
141 <div class="input-short">
141 <div class="input-short">
142 <div id="lang_stats"></div>
142 <div id="lang_stats"></div>
143 </div>
143 </div>
144 </div>
144 </div>
145
145
146 <div class="field">
146 <div class="field">
147 <div class="label">
147 <div class="label">
148 <label>${_('Download')}:</label>
148 <label>${_('Download')}:</label>
149 </div>
149 </div>
150 <div class="input-short">
150 <div class="input-short">
151 %if len(c.rhodecode_repo.revisions) == 0:
151 %if len(c.rhodecode_repo.revisions) == 0:
152 ${_('There are no downloads yet')}
152 ${_('There are no downloads yet')}
153 %elif c.enable_downloads is False:
153 %elif c.enable_downloads is False:
154 ${_('Downloads are disabled for this repository')}
154 ${_('Downloads are disabled for this repository')}
155 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
155 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
156 [${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name))}]
156 [${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name))}]
157 %endif
157 %endif
158 %else:
158 %else:
159 ${h.select('download_options',c.rhodecode_repo.get_changeset().raw_id,c.download_options)}
159 ${h.select('download_options',c.rhodecode_repo.get_changeset().raw_id,c.download_options)}
160 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
160 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
161 %if cnt >=1:
161 %if cnt >=1:
162 |
162 |
163 %endif
163 %endif
164 <span class="tooltip" title="${_('Download %s as %s') %('tip',archive['type'])}"
164 <span class="tooltip" title="${_('Download %s as %s') %('tip',archive['type'])}"
165 id="${archive['type']+'_link'}">${h.link_to(archive['type'],
165 id="${archive['type']+'_link'}">${h.link_to(archive['type'],
166 h.url('files_archive_home',repo_name=c.dbrepo.repo_name,
166 h.url('files_archive_home',repo_name=c.dbrepo.repo_name,
167 fname='tip'+archive['extension']),class_="archive_icon")}</span>
167 fname='tip'+archive['extension']),class_="archive_icon")}</span>
168 %endfor
168 %endfor
169 %endif
169 %endif
170 </div>
170 </div>
171 </div>
171 </div>
172
172
173 <div class="field">
173 <div class="field">
174 <div class="label">
174 <div class="label">
175 <label>${_('Feeds')}:</label>
175 <label>${_('Feeds')}:</label>
176 </div>
176 </div>
177 <div class="input-short">
177 <div class="input-short">
178 %if c.rhodecode_user.username != 'default':
178 %if c.rhodecode_user.username != 'default':
179 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='rss_icon')}
179 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='rss_icon')}
180 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='atom_icon')}
180 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='atom_icon')}
181 %else:
181 %else:
182 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name),class_='rss_icon')}
182 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name),class_='rss_icon')}
183 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name),class_='atom_icon')}
183 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name),class_='atom_icon')}
184 %endif
184 %endif
185 </div>
185 </div>
186 </div>
186 </div>
187 </div>
187 </div>
188 </div>
188 </div>
189 <script type="text/javascript">
189 <script type="text/javascript">
190 YUE.onDOMReady(function(e){
190 YUE.onDOMReady(function(e){
191 id = 'clone_url';
191 id = 'clone_url';
192 YUE.on(id,'click',function(e){
192 YUE.on(id,'click',function(e){
193 YUD.get('clone_url').select();
193 YUD.get('clone_url').select();
194 })
194 })
195 })
195 })
196 var data = ${c.trending_languages|n};
196 var data = ${c.trending_languages|n};
197 var total = 0;
197 var total = 0;
198 var no_data = true;
198 var no_data = true;
199 for (k in data){
199 for (k in data){
200 total += data[k];
200 total += data[k].count;
201 no_data = false;
201 no_data = false;
202 }
202 }
203 var tbl = document.createElement('table');
203 var tbl = document.createElement('table');
204 tbl.setAttribute('class','trending_language_tbl');
204 tbl.setAttribute('class','trending_language_tbl');
205 var cnt =0;
205 var cnt = 0;
206 for (k in data){
206 for (k in data){
207 cnt+=1;
207 cnt += 1;
208 var hide = cnt>2;
208 var hide = cnt>2;
209 var tr = document.createElement('tr');
209 var tr = document.createElement('tr');
210 if (hide){
210 if (hide){
211 tr.setAttribute('style','display:none');
211 tr.setAttribute('style','display:none');
212 tr.setAttribute('class','stats_hidden');
212 tr.setAttribute('class','stats_hidden');
213 }
213 }
214 var percentage = Math.round((data[k]/total*100),2);
214 var percentage = Math.round((data[k].count/total*100),2);
215 var value = data[k];
215 var value = data[k].count;
216 var td1 = document.createElement('td');
216 var td1 = document.createElement('td');
217 td1.width=150;
217 td1.width = 150;
218 var trending_language_label = document.createElement('div');
218 var trending_language_label = document.createElement('div');
219 trending_language_label.innerHTML = k;
219 trending_language_label.innerHTML = data[k].desc+" ("+k+")";
220 td1.appendChild(trending_language_label);
220 td1.appendChild(trending_language_label);
221
221
222 var td2 = document.createElement('td');
222 var td2 = document.createElement('td');
223 td2.setAttribute('style','padding-right:14px !important');
223 td2.setAttribute('style','padding-right:14px !important');
224 var trending_language = document.createElement('div');
224 var trending_language = document.createElement('div');
225 var nr_files = value+" ${_('files')}";
225 var nr_files = value+" ${_('files')}";
226
226
227 trending_language.title = k+" "+nr_files;
227 trending_language.title = k+" "+nr_files;
228
228
229 if (percentage>20){
229 if (percentage>22){
230 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
230 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
231 }
231 }
232 else{
232 else{
233 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
233 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
234 }
234 }
235
235
236 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
236 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
237 trending_language.style.width=percentage+"%";
237 trending_language.style.width=percentage+"%";
238 td2.appendChild(trending_language);
238 td2.appendChild(trending_language);
239
239
240 tr.appendChild(td1);
240 tr.appendChild(td1);
241 tr.appendChild(td2);
241 tr.appendChild(td2);
242 tbl.appendChild(tr);
242 tbl.appendChild(tr);
243 if(cnt == 2){
243 if(cnt == 2){
244 var show_more = document.createElement('tr');
244 var show_more = document.createElement('tr');
245 var td=document.createElement('td');
245 var td=document.createElement('td');
246 lnk = document.createElement('a');
246 lnk = document.createElement('a');
247 lnk.href='#';
247 lnk.href='#';
248 lnk.innerHTML = "${_("show more")}";
248 lnk.innerHTML = "${_('show more')}";
249 lnk.id='code_stats_show_more';
249 lnk.id='code_stats_show_more';
250 td.appendChild(lnk);
250 td.appendChild(lnk);
251 show_more.appendChild(td);
251 show_more.appendChild(td);
252 show_more.appendChild(document.createElement('td'));
252 show_more.appendChild(document.createElement('td'));
253 tbl.appendChild(show_more);
253 tbl.appendChild(show_more);
254 }
254 }
255
255
256 }
256 }
257 if(no_data){
257 if(no_data){
258 var tr = document.createElement('tr');
258 var tr = document.createElement('tr');
259 var td1 = document.createElement('td');
259 var td1 = document.createElement('td');
260 td1.innerHTML = "${c.no_data_msg}";
260 td1.innerHTML = "${c.no_data_msg}";
261 tr.appendChild(td1);
261 tr.appendChild(td1);
262 tbl.appendChild(tr);
262 tbl.appendChild(tr);
263 }
263 }
264 YUD.get('lang_stats').appendChild(tbl);
264 YUD.get('lang_stats').appendChild(tbl);
265 YUE.on('code_stats_show_more','click',function(){
265 YUE.on('code_stats_show_more','click',function(){
266 l = YUD.getElementsByClassName('stats_hidden')
266 l = YUD.getElementsByClassName('stats_hidden')
267 for (e in l){
267 for (e in l){
268 YUD.setStyle(l[e],'display','');
268 YUD.setStyle(l[e],'display','');
269 };
269 };
270 YUD.setStyle(YUD.get('code_stats_show_more'),
270 YUD.setStyle(YUD.get('code_stats_show_more'),
271 'display','none');
271 'display','none');
272 })
272 })
273
273
274
274
275 YUE.on('download_options','change',function(e){
275 YUE.on('download_options','change',function(e){
276 var new_cs = e.target.options[e.target.selectedIndex];
276 var new_cs = e.target.options[e.target.selectedIndex];
277 var tmpl_links = {}
277 var tmpl_links = {}
278 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
278 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
279 tmpl_links['${archive['type']}'] = '${h.link_to(archive['type'],
279 tmpl_links['${archive['type']}'] = '${h.link_to(archive['type'],
280 h.url('files_archive_home',repo_name=c.dbrepo.repo_name,
280 h.url('files_archive_home',repo_name=c.dbrepo.repo_name,
281 fname='__CS__'+archive['extension']),class_="archive_icon")}';
281 fname='__CS__'+archive['extension']),class_="archive_icon")}';
282 %endfor
282 %endfor
283
283
284
284
285 for(k in tmpl_links){
285 for(k in tmpl_links){
286 var s = YUD.get(k+'_link')
286 var s = YUD.get(k+'_link')
287 title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}";
287 title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}";
288 s.title = title_tmpl.replace('__CS_NAME__',new_cs.text);
288 s.title = title_tmpl.replace('__CS_NAME__',new_cs.text);
289 s.title = s.title.replace('__CS_EXT__',k);
289 s.title = s.title.replace('__CS_EXT__',k);
290 s.innerHTML = tmpl_links[k].replace('__CS__',new_cs.value);
290 s.innerHTML = tmpl_links[k].replace('__CS__',new_cs.value);
291 }
291 }
292
292
293 })
293 })
294
294
295 </script>
295 </script>
296 </div>
296 </div>
297
297
298 <div class="box box-right" style="min-height:455px">
298 <div class="box box-right" style="min-height:455px">
299 <!-- box / title -->
299 <!-- box / title -->
300 <div class="title">
300 <div class="title">
301 <h5>${_('Commit activity by day / author')}</h5>
301 <h5>${_('Commit activity by day / author')}</h5>
302 </div>
302 </div>
303
303
304 <div class="table">
304 <div class="table">
305 <div style="padding:0 10px 10px 15px;font-size: 1.2em;">
305 <div style="padding:0 10px 10px 15px;font-size: 1.2em;">
306 %if c.no_data:
306 %if c.no_data:
307 ${c.no_data_msg}
307 ${c.no_data_msg}
308 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
308 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
309 [${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name))}]
309 [${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name))}]
310 %endif
310 %endif
311
311
312 %else:
312 %else:
313 ${_('Loaded in')} ${c.stats_percentage} %
313 ${_('Loaded in')} ${c.stats_percentage} %
314 %endif
314 %endif
315 </div>
315 </div>
316 <div id="commit_history" style="width:460px;height:300px;float:left"></div>
316 <div id="commit_history" style="width:460px;height:300px;float:left"></div>
317 <div style="clear: both;height: 10px"></div>
317 <div style="clear: both;height: 10px"></div>
318 <div id="overview" style="width:460px;height:100px;float:left"></div>
318 <div id="overview" style="width:460px;height:100px;float:left"></div>
319
319
320 <div id="legend_data" style="clear:both;margin-top:10px;">
320 <div id="legend_data" style="clear:both;margin-top:10px;">
321 <div id="legend_container"></div>
321 <div id="legend_container"></div>
322 <div id="legend_choices">
322 <div id="legend_choices">
323 <table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
323 <table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
324 </div>
324 </div>
325 </div>
325 </div>
326 <script type="text/javascript">
326 <script type="text/javascript">
327 /**
327 /**
328 * Plots summary graph
328 * Plots summary graph
329 *
329 *
330 * @class SummaryPlot
330 * @class SummaryPlot
331 * @param {from} initial from for detailed graph
331 * @param {from} initial from for detailed graph
332 * @param {to} initial to for detailed graph
332 * @param {to} initial to for detailed graph
333 * @param {dataset}
333 * @param {dataset}
334 * @param {overview_dataset}
334 * @param {overview_dataset}
335 */
335 */
336 function SummaryPlot(from,to,dataset,overview_dataset) {
336 function SummaryPlot(from,to,dataset,overview_dataset) {
337 var initial_ranges = {
337 var initial_ranges = {
338 "xaxis":{
338 "xaxis":{
339 "from":from,
339 "from":from,
340 "to":to,
340 "to":to,
341 },
341 },
342 };
342 };
343 var dataset = dataset;
343 var dataset = dataset;
344 var overview_dataset = [overview_dataset];
344 var overview_dataset = [overview_dataset];
345 var choiceContainer = YUD.get("legend_choices");
345 var choiceContainer = YUD.get("legend_choices");
346 var choiceContainerTable = YUD.get("legend_choices_tables");
346 var choiceContainerTable = YUD.get("legend_choices_tables");
347 var plotContainer = YUD.get('commit_history');
347 var plotContainer = YUD.get('commit_history');
348 var overviewContainer = YUD.get('overview');
348 var overviewContainer = YUD.get('overview');
349
349
350 var plot_options = {
350 var plot_options = {
351 bars: {show:true,align:'center',lineWidth:4},
351 bars: {show:true,align:'center',lineWidth:4},
352 legend: {show:true, container:"legend_container"},
352 legend: {show:true, container:"legend_container"},
353 points: {show:true,radius:0,fill:false},
353 points: {show:true,radius:0,fill:false},
354 yaxis: {tickDecimals:0,},
354 yaxis: {tickDecimals:0,},
355 xaxis: {
355 xaxis: {
356 mode: "time",
356 mode: "time",
357 timeformat: "%d/%m",
357 timeformat: "%d/%m",
358 min:from,
358 min:from,
359 max:to,
359 max:to,
360 },
360 },
361 grid: {
361 grid: {
362 hoverable: true,
362 hoverable: true,
363 clickable: true,
363 clickable: true,
364 autoHighlight:true,
364 autoHighlight:true,
365 color: "#999"
365 color: "#999"
366 },
366 },
367 //selection: {mode: "x"}
367 //selection: {mode: "x"}
368 };
368 };
369 var overview_options = {
369 var overview_options = {
370 legend:{show:false},
370 legend:{show:false},
371 bars: {show:true,barWidth: 2,},
371 bars: {show:true,barWidth: 2,},
372 shadowSize: 0,
372 shadowSize: 0,
373 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
373 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
374 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
374 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
375 grid: {color: "#999",},
375 grid: {color: "#999",},
376 selection: {mode: "x"}
376 selection: {mode: "x"}
377 };
377 };
378
378
379 /**
379 /**
380 *get dummy data needed in few places
380 *get dummy data needed in few places
381 */
381 */
382 function getDummyData(label){
382 function getDummyData(label){
383 return {"label":label,
383 return {"label":label,
384 "data":[{"time":0,
384 "data":[{"time":0,
385 "commits":0,
385 "commits":0,
386 "added":0,
386 "added":0,
387 "changed":0,
387 "changed":0,
388 "removed":0,
388 "removed":0,
389 }],
389 }],
390 "schema":["commits"],
390 "schema":["commits"],
391 "color":'#ffffff',
391 "color":'#ffffff',
392 }
392 }
393 }
393 }
394
394
395 /**
395 /**
396 * generate checkboxes accordindly to data
396 * generate checkboxes accordindly to data
397 * @param keys
397 * @param keys
398 * @returns
398 * @returns
399 */
399 */
400 function generateCheckboxes(data) {
400 function generateCheckboxes(data) {
401 //append checkboxes
401 //append checkboxes
402 var i = 0;
402 var i = 0;
403 choiceContainerTable.innerHTML = '';
403 choiceContainerTable.innerHTML = '';
404 for(var pos in data) {
404 for(var pos in data) {
405
405
406 data[pos].color = i;
406 data[pos].color = i;
407 i++;
407 i++;
408 if(data[pos].label != ''){
408 if(data[pos].label != ''){
409 choiceContainerTable.innerHTML += '<tr><td>'+
409 choiceContainerTable.innerHTML += '<tr><td>'+
410 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
410 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
411 +data[pos].label+
411 +data[pos].label+
412 '</td></tr>';
412 '</td></tr>';
413 }
413 }
414 }
414 }
415 }
415 }
416
416
417 /**
417 /**
418 * ToolTip show
418 * ToolTip show
419 */
419 */
420 function showTooltip(x, y, contents) {
420 function showTooltip(x, y, contents) {
421 var div=document.getElementById('tooltip');
421 var div=document.getElementById('tooltip');
422 if(!div) {
422 if(!div) {
423 div = document.createElement('div');
423 div = document.createElement('div');
424 div.id="tooltip";
424 div.id="tooltip";
425 div.style.position="absolute";
425 div.style.position="absolute";
426 div.style.border='1px solid #fdd';
426 div.style.border='1px solid #fdd';
427 div.style.padding='2px';
427 div.style.padding='2px';
428 div.style.backgroundColor='#fee';
428 div.style.backgroundColor='#fee';
429 document.body.appendChild(div);
429 document.body.appendChild(div);
430 }
430 }
431 YUD.setStyle(div, 'opacity', 0);
431 YUD.setStyle(div, 'opacity', 0);
432 div.innerHTML = contents;
432 div.innerHTML = contents;
433 div.style.top=(y + 5) + "px";
433 div.style.top=(y + 5) + "px";
434 div.style.left=(x + 5) + "px";
434 div.style.left=(x + 5) + "px";
435
435
436 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
436 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
437 anim.animate();
437 anim.animate();
438 }
438 }
439
439
440 /**
440 /**
441 * This function will detect if selected period has some changesets
441 * This function will detect if selected period has some changesets
442 for this user if it does this data is then pushed for displaying
442 for this user if it does this data is then pushed for displaying
443 Additionally it will only display users that are selected by the checkbox
443 Additionally it will only display users that are selected by the checkbox
444 */
444 */
445 function getDataAccordingToRanges(ranges) {
445 function getDataAccordingToRanges(ranges) {
446
446
447 var data = [];
447 var data = [];
448 var keys = [];
448 var keys = [];
449 for(var key in dataset){
449 for(var key in dataset){
450 var push = false;
450 var push = false;
451
451
452 //method1 slow !!
452 //method1 slow !!
453 //*
453 //*
454 for(var ds in dataset[key].data){
454 for(var ds in dataset[key].data){
455 commit_data = dataset[key].data[ds];
455 commit_data = dataset[key].data[ds];
456 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
456 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
457 push = true;
457 push = true;
458 break;
458 break;
459 }
459 }
460 }
460 }
461 //*/
461 //*/
462
462
463 /*//method2 sorted commit data !!!
463 /*//method2 sorted commit data !!!
464
464
465 var first_commit = dataset[key].data[0].time;
465 var first_commit = dataset[key].data[0].time;
466 var last_commit = dataset[key].data[dataset[key].data.length-1].time;
466 var last_commit = dataset[key].data[dataset[key].data.length-1].time;
467
467
468 if (first_commit >= ranges.xaxis.from && last_commit <= ranges.xaxis.to){
468 if (first_commit >= ranges.xaxis.from && last_commit <= ranges.xaxis.to){
469 push = true;
469 push = true;
470 }
470 }
471 //*/
471 //*/
472
472
473 if(push){
473 if(push){
474 data.push(dataset[key]);
474 data.push(dataset[key]);
475 }
475 }
476 }
476 }
477 if(data.length >= 1){
477 if(data.length >= 1){
478 return data;
478 return data;
479 }
479 }
480 else{
480 else{
481 //just return dummy data for graph to plot itself
481 //just return dummy data for graph to plot itself
482 return [getDummyData('')];
482 return [getDummyData('')];
483 }
483 }
484
484
485 }
485 }
486
486
487 /**
487 /**
488 * redraw using new checkbox data
488 * redraw using new checkbox data
489 */
489 */
490 function plotchoiced(e,args){
490 function plotchoiced(e,args){
491 var cur_data = args[0];
491 var cur_data = args[0];
492 var cur_ranges = args[1];
492 var cur_ranges = args[1];
493
493
494 var new_data = [];
494 var new_data = [];
495 var inputs = choiceContainer.getElementsByTagName("input");
495 var inputs = choiceContainer.getElementsByTagName("input");
496
496
497 //show only checked labels
497 //show only checked labels
498 for(var i=0; i<inputs.length; i++) {
498 for(var i=0; i<inputs.length; i++) {
499 var checkbox_key = inputs[i].name;
499 var checkbox_key = inputs[i].name;
500
500
501 if(inputs[i].checked){
501 if(inputs[i].checked){
502 for(var d in cur_data){
502 for(var d in cur_data){
503 if(cur_data[d].label == checkbox_key){
503 if(cur_data[d].label == checkbox_key){
504 new_data.push(cur_data[d]);
504 new_data.push(cur_data[d]);
505 }
505 }
506 }
506 }
507 }
507 }
508 else{
508 else{
509 //push dummy data to not hide the label
509 //push dummy data to not hide the label
510 new_data.push(getDummyData(checkbox_key));
510 new_data.push(getDummyData(checkbox_key));
511 }
511 }
512 }
512 }
513
513
514 var new_options = YAHOO.lang.merge(plot_options, {
514 var new_options = YAHOO.lang.merge(plot_options, {
515 xaxis: {
515 xaxis: {
516 min: cur_ranges.xaxis.from,
516 min: cur_ranges.xaxis.from,
517 max: cur_ranges.xaxis.to,
517 max: cur_ranges.xaxis.to,
518 mode:"time",
518 mode:"time",
519 timeformat: "%d/%m",
519 timeformat: "%d/%m",
520 },
520 },
521 });
521 });
522 if (!new_data){
522 if (!new_data){
523 new_data = [[0,1]];
523 new_data = [[0,1]];
524 }
524 }
525 // do the zooming
525 // do the zooming
526 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
526 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
527
527
528 plot.subscribe("plotselected", plotselected);
528 plot.subscribe("plotselected", plotselected);
529
529
530 //resubscribe plothover
530 //resubscribe plothover
531 plot.subscribe("plothover", plothover);
531 plot.subscribe("plothover", plothover);
532
532
533 // don't fire event on the overview to prevent eternal loop
533 // don't fire event on the overview to prevent eternal loop
534 overview.setSelection(cur_ranges, true);
534 overview.setSelection(cur_ranges, true);
535
535
536 }
536 }
537
537
538 /**
538 /**
539 * plot only selected items from overview
539 * plot only selected items from overview
540 * @param ranges
540 * @param ranges
541 * @returns
541 * @returns
542 */
542 */
543 function plotselected(ranges,cur_data) {
543 function plotselected(ranges,cur_data) {
544 //updates the data for new plot
544 //updates the data for new plot
545 data = getDataAccordingToRanges(ranges);
545 data = getDataAccordingToRanges(ranges);
546 generateCheckboxes(data);
546 generateCheckboxes(data);
547
547
548 var new_options = YAHOO.lang.merge(plot_options, {
548 var new_options = YAHOO.lang.merge(plot_options, {
549 xaxis: {
549 xaxis: {
550 min: ranges.xaxis.from,
550 min: ranges.xaxis.from,
551 max: ranges.xaxis.to,
551 max: ranges.xaxis.to,
552 mode:"time",
552 mode:"time",
553 timeformat: "%d/%m",
553 timeformat: "%d/%m",
554 },
554 },
555 yaxis: {
555 yaxis: {
556 min: ranges.yaxis.from,
556 min: ranges.yaxis.from,
557 max: ranges.yaxis.to,
557 max: ranges.yaxis.to,
558 },
558 },
559
559
560 });
560 });
561 // do the zooming
561 // do the zooming
562 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
562 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
563
563
564 plot.subscribe("plotselected", plotselected);
564 plot.subscribe("plotselected", plotselected);
565
565
566 //resubscribe plothover
566 //resubscribe plothover
567 plot.subscribe("plothover", plothover);
567 plot.subscribe("plothover", plothover);
568
568
569 // don't fire event on the overview to prevent eternal loop
569 // don't fire event on the overview to prevent eternal loop
570 overview.setSelection(ranges, true);
570 overview.setSelection(ranges, true);
571
571
572 //resubscribe choiced
572 //resubscribe choiced
573 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
573 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
574 }
574 }
575
575
576 var previousPoint = null;
576 var previousPoint = null;
577
577
578 function plothover(o) {
578 function plothover(o) {
579 var pos = o.pos;
579 var pos = o.pos;
580 var item = o.item;
580 var item = o.item;
581
581
582 //YUD.get("x").innerHTML = pos.x.toFixed(2);
582 //YUD.get("x").innerHTML = pos.x.toFixed(2);
583 //YUD.get("y").innerHTML = pos.y.toFixed(2);
583 //YUD.get("y").innerHTML = pos.y.toFixed(2);
584 if (item) {
584 if (item) {
585 if (previousPoint != item.datapoint) {
585 if (previousPoint != item.datapoint) {
586 previousPoint = item.datapoint;
586 previousPoint = item.datapoint;
587
587
588 var tooltip = YUD.get("tooltip");
588 var tooltip = YUD.get("tooltip");
589 if(tooltip) {
589 if(tooltip) {
590 tooltip.parentNode.removeChild(tooltip);
590 tooltip.parentNode.removeChild(tooltip);
591 }
591 }
592 var x = item.datapoint.x.toFixed(2);
592 var x = item.datapoint.x.toFixed(2);
593 var y = item.datapoint.y.toFixed(2);
593 var y = item.datapoint.y.toFixed(2);
594
594
595 if (!item.series.label){
595 if (!item.series.label){
596 item.series.label = 'commits';
596 item.series.label = 'commits';
597 }
597 }
598 var d = new Date(x*1000);
598 var d = new Date(x*1000);
599 var fd = d.toDateString()
599 var fd = d.toDateString()
600 var nr_commits = parseInt(y);
600 var nr_commits = parseInt(y);
601
601
602 var cur_data = dataset[item.series.label].data[item.dataIndex];
602 var cur_data = dataset[item.series.label].data[item.dataIndex];
603 var added = cur_data.added;
603 var added = cur_data.added;
604 var changed = cur_data.changed;
604 var changed = cur_data.changed;
605 var removed = cur_data.removed;
605 var removed = cur_data.removed;
606
606
607 var nr_commits_suffix = " ${_('commits')} ";
607 var nr_commits_suffix = " ${_('commits')} ";
608 var added_suffix = " ${_('files added')} ";
608 var added_suffix = " ${_('files added')} ";
609 var changed_suffix = " ${_('files changed')} ";
609 var changed_suffix = " ${_('files changed')} ";
610 var removed_suffix = " ${_('files removed')} ";
610 var removed_suffix = " ${_('files removed')} ";
611
611
612
612
613 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
613 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
614 if(added==1){added_suffix=" ${_('file added')} ";}
614 if(added==1){added_suffix=" ${_('file added')} ";}
615 if(changed==1){changed_suffix=" ${_('file changed')} ";}
615 if(changed==1){changed_suffix=" ${_('file changed')} ";}
616 if(removed==1){removed_suffix=" ${_('file removed')} ";}
616 if(removed==1){removed_suffix=" ${_('file removed')} ";}
617
617
618 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
618 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
619 +'<br/>'+
619 +'<br/>'+
620 nr_commits + nr_commits_suffix+'<br/>'+
620 nr_commits + nr_commits_suffix+'<br/>'+
621 added + added_suffix +'<br/>'+
621 added + added_suffix +'<br/>'+
622 changed + changed_suffix + '<br/>'+
622 changed + changed_suffix + '<br/>'+
623 removed + removed_suffix + '<br/>');
623 removed + removed_suffix + '<br/>');
624 }
624 }
625 }
625 }
626 else {
626 else {
627 var tooltip = YUD.get("tooltip");
627 var tooltip = YUD.get("tooltip");
628
628
629 if(tooltip) {
629 if(tooltip) {
630 tooltip.parentNode.removeChild(tooltip);
630 tooltip.parentNode.removeChild(tooltip);
631 }
631 }
632 previousPoint = null;
632 previousPoint = null;
633 }
633 }
634 }
634 }
635
635
636 /**
636 /**
637 * MAIN EXECUTION
637 * MAIN EXECUTION
638 */
638 */
639
639
640 var data = getDataAccordingToRanges(initial_ranges);
640 var data = getDataAccordingToRanges(initial_ranges);
641 generateCheckboxes(data);
641 generateCheckboxes(data);
642
642
643 //main plot
643 //main plot
644 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
644 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
645
645
646 //overview
646 //overview
647 var overview = YAHOO.widget.Flot(overviewContainer, overview_dataset, overview_options);
647 var overview = YAHOO.widget.Flot(overviewContainer, overview_dataset, overview_options);
648
648
649 //show initial selection on overview
649 //show initial selection on overview
650 overview.setSelection(initial_ranges);
650 overview.setSelection(initial_ranges);
651
651
652 plot.subscribe("plotselected", plotselected);
652 plot.subscribe("plotselected", plotselected);
653
653
654 overview.subscribe("plotselected", function (ranges) {
654 overview.subscribe("plotselected", function (ranges) {
655 plot.setSelection(ranges);
655 plot.setSelection(ranges);
656 });
656 });
657
657
658 plot.subscribe("plothover", plothover);
658 plot.subscribe("plothover", plothover);
659
659
660 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
660 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
661 }
661 }
662 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
662 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
663 </script>
663 </script>
664
664
665 </div>
665 </div>
666 </div>
666 </div>
667
667
668 <div class="box">
668 <div class="box">
669 <div class="title">
669 <div class="title">
670 <div class="breadcrumbs">${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}</div>
670 <div class="breadcrumbs">${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}</div>
671 </div>
671 </div>
672 <div class="table">
672 <div class="table">
673 <div id="shortlog_data">
673 <div id="shortlog_data">
674 <%include file='../shortlog/shortlog_data.html'/>
674 <%include file='../shortlog/shortlog_data.html'/>
675 </div>
675 </div>
676 ##%if c.repo_changesets:
676 ##%if c.repo_changesets:
677 ## ${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
677 ## ${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
678 ##%endif
678 ##%endif
679 </div>
679 </div>
680 </div>
680 </div>
681 <div class="box">
681 <div class="box">
682 <div class="title">
682 <div class="title">
683 <div class="breadcrumbs">${h.link_to(_('Tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
683 <div class="breadcrumbs">${h.link_to(_('Tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
684 </div>
684 </div>
685 <div class="table">
685 <div class="table">
686 <%include file='../tags/tags_data.html'/>
686 <%include file='../tags/tags_data.html'/>
687 %if c.repo_changesets:
687 %if c.repo_changesets:
688 ${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
688 ${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
689 %endif
689 %endif
690 </div>
690 </div>
691 </div>
691 </div>
692 <div class="box">
692 <div class="box">
693 <div class="title">
693 <div class="title">
694 <div class="breadcrumbs">${h.link_to(_('Branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
694 <div class="breadcrumbs">${h.link_to(_('Branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
695 </div>
695 </div>
696 <div class="table">
696 <div class="table">
697 <%include file='../branches/branches_data.html'/>
697 <%include file='../branches/branches_data.html'/>
698 %if c.repo_changesets:
698 %if c.repo_changesets:
699 ${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
699 ${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
700 %endif
700 %endif
701 </div>
701 </div>
702 </div>
702 </div>
703
703
704 </%def>
704 </%def>
General Comments 0
You need to be logged in to leave comments. Login now