##// END OF EJS Templates
fixes issue #271...
marcink -
r1890:a3efaaa6 beta
parent child Browse files
Show More
@@ -1,235 +1,233 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) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 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 traceback
26 import traceback
27 import calendar
27 import calendar
28 import logging
28 import logging
29 from time import mktime
29 from time import mktime
30 from datetime import timedelta, date
30 from datetime import timedelta, date
31 from itertools import product
31 from itertools import product
32 from urlparse import urlparse
32 from urlparse import urlparse
33
33
34 from vcs.exceptions import ChangesetError, EmptyRepositoryError, \
34 from vcs.exceptions import ChangesetError, EmptyRepositoryError, \
35 NodeDoesNotExistError
35 NodeDoesNotExistError
36
36
37 from pylons import tmpl_context as c, request, url, config
37 from pylons import tmpl_context as c, request, url, config
38 from pylons.i18n.translation import _
38 from pylons.i18n.translation import _
39
39
40 from beaker.cache import cache_region, region_invalidate
40 from beaker.cache import cache_region, region_invalidate
41
41
42 from rhodecode.model.db import Statistics, CacheInvalidation
42 from rhodecode.model.db import Statistics, CacheInvalidation
43 from rhodecode.lib import ALL_READMES, ALL_EXTS
43 from rhodecode.lib import ALL_READMES, ALL_EXTS
44 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
44 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
45 from rhodecode.lib.base import BaseRepoController, render
45 from rhodecode.lib.base import BaseRepoController, render
46 from rhodecode.lib.utils import EmptyChangeset
46 from rhodecode.lib.utils import EmptyChangeset
47 from rhodecode.lib.markup_renderer import MarkupRenderer
47 from rhodecode.lib.markup_renderer import MarkupRenderer
48 from rhodecode.lib.celerylib import run_task
48 from rhodecode.lib.celerylib import run_task
49 from rhodecode.lib.celerylib.tasks import get_commits_stats, \
49 from rhodecode.lib.celerylib.tasks import get_commits_stats, \
50 LANGUAGES_EXTENSIONS_MAP
50 LANGUAGES_EXTENSIONS_MAP
51 from rhodecode.lib.helpers import RepoPage
51 from rhodecode.lib.helpers import RepoPage
52 from rhodecode.lib.compat import json, OrderedDict
52 from rhodecode.lib.compat import json, OrderedDict
53
53
54 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
55
55
56 README_FILES = [''.join([x[0][0], x[1][0]]) for x in
56 README_FILES = [''.join([x[0][0], x[1][0]]) for x in
57 sorted(list(product(ALL_READMES, ALL_EXTS)),
57 sorted(list(product(ALL_READMES, ALL_EXTS)),
58 key=lambda y:y[0][1] + y[1][1])]
58 key=lambda y:y[0][1] + y[1][1])]
59
59
60
60
61 class SummaryController(BaseRepoController):
61 class SummaryController(BaseRepoController):
62
62
63 @LoginRequired()
63 @LoginRequired()
64 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
64 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
65 'repository.admin')
65 'repository.admin')
66 def __before__(self):
66 def __before__(self):
67 super(SummaryController, self).__before__()
67 super(SummaryController, self).__before__()
68
68
69 def index(self, repo_name):
69 def index(self, repo_name):
70 c.dbrepo = dbrepo = c.rhodecode_db_repo
70 c.dbrepo = dbrepo = c.rhodecode_db_repo
71 c.following = self.scm_model.is_following_repo(repo_name,
71 c.following = self.scm_model.is_following_repo(repo_name,
72 self.rhodecode_user.user_id)
72 self.rhodecode_user.user_id)
73
73
74 def url_generator(**kw):
74 def url_generator(**kw):
75 return url('shortlog_home', repo_name=repo_name, size=10, **kw)
75 return url('shortlog_home', repo_name=repo_name, size=10, **kw)
76
76
77 c.repo_changesets = RepoPage(c.rhodecode_repo, page=1,
77 c.repo_changesets = RepoPage(c.rhodecode_repo, page=1,
78 items_per_page=10, url=url_generator)
78 items_per_page=10, url=url_generator)
79
79
80 if self.rhodecode_user.username == 'default':
80 if self.rhodecode_user.username == 'default':
81 # for default(anonymous) user we don't need to pass credentials
81 # for default(anonymous) user we don't need to pass credentials
82 username = ''
82 username = ''
83 password = ''
83 password = ''
84 else:
84 else:
85 username = str(self.rhodecode_user.username)
85 username = str(self.rhodecode_user.username)
86 password = '@'
86 password = '@'
87
87
88 parsed_url = urlparse(url.current(qualified=True))
88 parsed_url = urlparse(url.current(qualified=True))
89
89
90 default_clone_uri = '{scheme}://{user}{pass}{netloc}{path}'
90 default_clone_uri = '{scheme}://{user}{pass}{netloc}{path}'
91
91
92 uri_tmpl = config.get('clone_uri', default_clone_uri)
92 uri_tmpl = config.get('clone_uri', default_clone_uri)
93 uri_tmpl = uri_tmpl.replace('{', '%(').replace('}', ')s')
93 uri_tmpl = uri_tmpl.replace('{', '%(').replace('}', ')s')
94
94
95 uri_dict = {
95 uri_dict = {
96 'user': username,
96 'user': username,
97 'pass': password,
97 'pass': password,
98 'scheme': parsed_url.scheme,
98 'scheme': parsed_url.scheme,
99 'netloc': parsed_url.netloc,
99 'netloc': parsed_url.netloc,
100 'path': parsed_url.path
100 'path': parsed_url.path
101 }
101 }
102 uri = uri_tmpl % uri_dict
102 uri = uri_tmpl % uri_dict
103 # generate another clone url by id
103 # generate another clone url by id
104 uri_dict.update({'path': '/_%s' % c.dbrepo.repo_id})
104 uri_dict.update({'path': '/_%s' % c.dbrepo.repo_id})
105 uri_id = uri_tmpl % uri_dict
105 uri_id = uri_tmpl % uri_dict
106
106
107 c.clone_repo_url = uri
107 c.clone_repo_url = uri
108 c.clone_repo_url_id = uri_id
108 c.clone_repo_url_id = uri_id
109 c.repo_tags = OrderedDict()
109 c.repo_tags = OrderedDict()
110 for name, hash in c.rhodecode_repo.tags.items()[:10]:
110 for name, hash in c.rhodecode_repo.tags.items()[:10]:
111 try:
111 try:
112 c.repo_tags[name] = c.rhodecode_repo.get_changeset(hash)
112 c.repo_tags[name] = c.rhodecode_repo.get_changeset(hash)
113 except ChangesetError:
113 except ChangesetError:
114 c.repo_tags[name] = EmptyChangeset(hash)
114 c.repo_tags[name] = EmptyChangeset(hash)
115
115
116 c.repo_branches = OrderedDict()
116 c.repo_branches = OrderedDict()
117 for name, hash in c.rhodecode_repo.branches.items()[:10]:
117 for name, hash in c.rhodecode_repo.branches.items()[:10]:
118 try:
118 try:
119 c.repo_branches[name] = c.rhodecode_repo.get_changeset(hash)
119 c.repo_branches[name] = c.rhodecode_repo.get_changeset(hash)
120 except ChangesetError:
120 except ChangesetError:
121 c.repo_branches[name] = EmptyChangeset(hash)
121 c.repo_branches[name] = EmptyChangeset(hash)
122
122
123 td = date.today() + timedelta(days=1)
123 td = date.today() + timedelta(days=1)
124 td_1m = td - timedelta(days=calendar.mdays[td.month])
124 td_1m = td - timedelta(days=calendar.mdays[td.month])
125 td_1y = td - timedelta(days=365)
125 td_1y = td - timedelta(days=365)
126
126
127 ts_min_m = mktime(td_1m.timetuple())
127 ts_min_m = mktime(td_1m.timetuple())
128 ts_min_y = mktime(td_1y.timetuple())
128 ts_min_y = mktime(td_1y.timetuple())
129 ts_max_y = mktime(td.timetuple())
129 ts_max_y = mktime(td.timetuple())
130
130
131 if dbrepo.enable_statistics:
131 if dbrepo.enable_statistics:
132 c.show_stats = True
132 c.show_stats = True
133 c.no_data_msg = _('No data loaded yet')
133 c.no_data_msg = _('No data loaded yet')
134 run_task(get_commits_stats, c.dbrepo.repo_name, ts_min_y, ts_max_y)
134 run_task(get_commits_stats, c.dbrepo.repo_name, ts_min_y, ts_max_y)
135 else:
135 else:
136 c.show_stats = False
136 c.show_stats = False
137 c.no_data_msg = _('Statistics are disabled for this repository')
137 c.no_data_msg = _('Statistics are disabled for this repository')
138 c.ts_min = ts_min_m
138 c.ts_min = ts_min_m
139 c.ts_max = ts_max_y
139 c.ts_max = ts_max_y
140
140
141 stats = self.sa.query(Statistics)\
141 stats = self.sa.query(Statistics)\
142 .filter(Statistics.repository == dbrepo)\
142 .filter(Statistics.repository == dbrepo)\
143 .scalar()
143 .scalar()
144
144
145 c.stats_percentage = 0
145 c.stats_percentage = 0
146
146
147 if stats and stats.languages:
147 if stats and stats.languages:
148 c.no_data = False is dbrepo.enable_statistics
148 c.no_data = False is dbrepo.enable_statistics
149 lang_stats_d = json.loads(stats.languages)
149 lang_stats_d = json.loads(stats.languages)
150 c.commit_data = stats.commit_activity
150 c.commit_data = stats.commit_activity
151 c.overview_data = stats.commit_activity_combined
151 c.overview_data = stats.commit_activity_combined
152
152
153 lang_stats = ((x, {"count": y,
153 lang_stats = ((x, {"count": y,
154 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
154 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
155 for x, y in lang_stats_d.items())
155 for x, y in lang_stats_d.items())
156
156
157 c.trending_languages = json.dumps(OrderedDict(
157 c.trending_languages = json.dumps(
158 sorted(lang_stats, reverse=True,
158 sorted(lang_stats, reverse=True, key=lambda k: k[1])[:10]
159 key=lambda k: k[1])[:10]
159 )
160 )
161 )
162 last_rev = stats.stat_on_revision + 1
160 last_rev = stats.stat_on_revision + 1
163 c.repo_last_rev = c.rhodecode_repo.count()\
161 c.repo_last_rev = c.rhodecode_repo.count()\
164 if c.rhodecode_repo.revisions else 0
162 if c.rhodecode_repo.revisions else 0
165 if last_rev == 0 or c.repo_last_rev == 0:
163 if last_rev == 0 or c.repo_last_rev == 0:
166 pass
164 pass
167 else:
165 else:
168 c.stats_percentage = '%.2f' % ((float((last_rev)) /
166 c.stats_percentage = '%.2f' % ((float((last_rev)) /
169 c.repo_last_rev) * 100)
167 c.repo_last_rev) * 100)
170 else:
168 else:
171 c.commit_data = json.dumps({})
169 c.commit_data = json.dumps({})
172 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10]])
170 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10]])
173 c.trending_languages = json.dumps({})
171 c.trending_languages = json.dumps({})
174 c.no_data = True
172 c.no_data = True
175
173
176 c.enable_downloads = dbrepo.enable_downloads
174 c.enable_downloads = dbrepo.enable_downloads
177 if c.enable_downloads:
175 if c.enable_downloads:
178 c.download_options = self._get_download_links(c.rhodecode_repo)
176 c.download_options = self._get_download_links(c.rhodecode_repo)
179
177
180 c.readme_data, c.readme_file = self.__get_readme_data(c.rhodecode_repo)
178 c.readme_data, c.readme_file = self.__get_readme_data(c.rhodecode_repo)
181 return render('summary/summary.html')
179 return render('summary/summary.html')
182
180
183 def __get_readme_data(self, repo):
181 def __get_readme_data(self, repo):
184
182
185 @cache_region('long_term')
183 @cache_region('long_term')
186 def _get_readme_from_cache(key):
184 def _get_readme_from_cache(key):
187 readme_data = None
185 readme_data = None
188 readme_file = None
186 readme_file = None
189 log.debug('Fetching readme file')
187 log.debug('Fetching readme file')
190 try:
188 try:
191 cs = repo.get_changeset('tip')
189 cs = repo.get_changeset('tip')
192 renderer = MarkupRenderer()
190 renderer = MarkupRenderer()
193 for f in README_FILES:
191 for f in README_FILES:
194 try:
192 try:
195 readme = cs.get_node(f)
193 readme = cs.get_node(f)
196 readme_file = f
194 readme_file = f
197 readme_data = renderer.render(readme.content, f)
195 readme_data = renderer.render(readme.content, f)
198 log.debug('Found readme %s' % readme_file)
196 log.debug('Found readme %s' % readme_file)
199 break
197 break
200 except NodeDoesNotExistError:
198 except NodeDoesNotExistError:
201 continue
199 continue
202 except ChangesetError:
200 except ChangesetError:
203 pass
201 pass
204 except EmptyRepositoryError:
202 except EmptyRepositoryError:
205 pass
203 pass
206 except Exception:
204 except Exception:
207 log.error(traceback.format_exc())
205 log.error(traceback.format_exc())
208
206
209 return readme_data, readme_file
207 return readme_data, readme_file
210
208
211 key = repo.name + '_README'
209 key = repo.name + '_README'
212 inv = CacheInvalidation.invalidate(key)
210 inv = CacheInvalidation.invalidate(key)
213 if inv is not None:
211 if inv is not None:
214 region_invalidate(_get_readme_from_cache, None, key)
212 region_invalidate(_get_readme_from_cache, None, key)
215 CacheInvalidation.set_valid(inv.cache_key)
213 CacheInvalidation.set_valid(inv.cache_key)
216 return _get_readme_from_cache(key)
214 return _get_readme_from_cache(key)
217
215
218 def _get_download_links(self, repo):
216 def _get_download_links(self, repo):
219
217
220 download_l = []
218 download_l = []
221
219
222 branches_group = ([], _("Branches"))
220 branches_group = ([], _("Branches"))
223 tags_group = ([], _("Tags"))
221 tags_group = ([], _("Tags"))
224
222
225 for name, chs in c.rhodecode_repo.branches.items():
223 for name, chs in c.rhodecode_repo.branches.items():
226 #chs = chs.split(':')[-1]
224 #chs = chs.split(':')[-1]
227 branches_group[0].append((chs, name),)
225 branches_group[0].append((chs, name),)
228 download_l.append(branches_group)
226 download_l.append(branches_group)
229
227
230 for name, chs in c.rhodecode_repo.tags.items():
228 for name, chs in c.rhodecode_repo.tags.items():
231 #chs = chs.split(':')[-1]
229 #chs = chs.split(':')[-1]
232 tags_group[0].append((chs, name),)
230 tags_group[0].append((chs, name),)
233 download_l.append(tags_group)
231 download_l.append(tags_group)
234
232
235 return download_l
233 return download_l
@@ -1,379 +1,381 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.compat
3 rhodecode.lib.compat
4 ~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~
5
5
6 Python backward compatibility functions and common libs
6 Python backward compatibility functions and common libs
7
7
8
8
9 :created_on: Oct 7, 2011
9 :created_on: Oct 7, 2011
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2010-2010 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2010-2010 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
26
27 import os
27 import os
28 from rhodecode import __platform__, PLATFORM_WIN
28 from rhodecode import __platform__, PLATFORM_WIN
29
29
30 #==============================================================================
30 #==============================================================================
31 # json
31 # json
32 #==============================================================================
32 #==============================================================================
33 try:
33 try:
34 import json
34 import json
35 except ImportError:
35 except ImportError:
36 import simplejson as json
36 import simplejson as json
37
37
38
38
39 #==============================================================================
39 #==============================================================================
40 # izip_longest
40 # izip_longest
41 #==============================================================================
41 #==============================================================================
42 try:
42 try:
43 from itertools import izip_longest
43 from itertools import izip_longest
44 except ImportError:
44 except ImportError:
45 import itertools
45 import itertools
46
46
47 def izip_longest(*args, **kwds): # noqa
47 def izip_longest(*args, **kwds): # noqa
48 fillvalue = kwds.get("fillvalue")
48 fillvalue = kwds.get("fillvalue")
49
49
50 def sentinel(counter=([fillvalue] * (len(args) - 1)).pop):
50 def sentinel(counter=([fillvalue] * (len(args) - 1)).pop):
51 yield counter() # yields the fillvalue, or raises IndexError
51 yield counter() # yields the fillvalue, or raises IndexError
52
52
53 fillers = itertools.repeat(fillvalue)
53 fillers = itertools.repeat(fillvalue)
54 iters = [itertools.chain(it, sentinel(), fillers)
54 iters = [itertools.chain(it, sentinel(), fillers)
55 for it in args]
55 for it in args]
56 try:
56 try:
57 for tup in itertools.izip(*iters):
57 for tup in itertools.izip(*iters):
58 yield tup
58 yield tup
59 except IndexError:
59 except IndexError:
60 pass
60 pass
61
61
62
62
63 #==============================================================================
63 #==============================================================================
64 # OrderedDict
64 # OrderedDict
65 #==============================================================================
65 #==============================================================================
66
66
67 # Python Software Foundation License
67 # Python Software Foundation License
68
68
69 # XXX: it feels like using the class with "is" and "is not" instead of "==" and
69 # XXX: it feels like using the class with "is" and "is not" instead of "==" and
70 # "!=" should be faster.
70 # "!=" should be faster.
71 class _Nil(object):
71 class _Nil(object):
72
72
73 def __repr__(self):
73 def __repr__(self):
74 return "nil"
74 return "nil"
75
75
76 def __eq__(self, other):
76 def __eq__(self, other):
77 if (isinstance(other, _Nil)):
77 if (isinstance(other, _Nil)):
78 return True
78 return True
79 else:
79 else:
80 return NotImplemented
80 return NotImplemented
81
81
82 def __ne__(self, other):
82 def __ne__(self, other):
83 if (isinstance(other, _Nil)):
83 if (isinstance(other, _Nil)):
84 return False
84 return False
85 else:
85 else:
86 return NotImplemented
86 return NotImplemented
87
87
88 _nil = _Nil()
88 _nil = _Nil()
89
89
90
90 class _odict(object):
91 class _odict(object):
91 """Ordered dict data structure, with O(1) complexity for dict operations
92 """Ordered dict data structure, with O(1) complexity for dict operations
92 that modify one element.
93 that modify one element.
93
94
94 Overwriting values doesn't change their original sequential order.
95 Overwriting values doesn't change their original sequential order.
95 """
96 """
96
97
97 def _dict_impl(self):
98 def _dict_impl(self):
98 return None
99 return None
99
100
100 def __init__(self, data=(), **kwds):
101 def __init__(self, data=(), **kwds):
101 """This doesn't accept keyword initialization as normal dicts to avoid
102 """This doesn't accept keyword initialization as normal dicts to avoid
102 a trap - inside a function or method the keyword args are accessible
103 a trap - inside a function or method the keyword args are accessible
103 only as a dict, without a defined order, so their original order is
104 only as a dict, without a defined order, so their original order is
104 lost.
105 lost.
105 """
106 """
106 if kwds:
107 if kwds:
107 raise TypeError("__init__() of ordered dict takes no keyword "
108 raise TypeError("__init__() of ordered dict takes no keyword "
108 "arguments to avoid an ordering trap.")
109 "arguments to avoid an ordering trap.")
109 self._dict_impl().__init__(self)
110 self._dict_impl().__init__(self)
110 # If you give a normal dict, then the order of elements is undefined
111 # If you give a normal dict, then the order of elements is undefined
111 if hasattr(data, "iteritems"):
112 if hasattr(data, "iteritems"):
112 for key, val in data.iteritems():
113 for key, val in data.iteritems():
113 self[key] = val
114 self[key] = val
114 else:
115 else:
115 for key, val in data:
116 for key, val in data:
116 self[key] = val
117 self[key] = val
117
118
118 # Double-linked list header
119 # Double-linked list header
119 def _get_lh(self):
120 def _get_lh(self):
120 dict_impl = self._dict_impl()
121 dict_impl = self._dict_impl()
121 if not hasattr(self, '_lh'):
122 if not hasattr(self, '_lh'):
122 dict_impl.__setattr__(self, '_lh', _nil)
123 dict_impl.__setattr__(self, '_lh', _nil)
123 return dict_impl.__getattribute__(self, '_lh')
124 return dict_impl.__getattribute__(self, '_lh')
124
125
125 def _set_lh(self, val):
126 def _set_lh(self, val):
126 self._dict_impl().__setattr__(self, '_lh', val)
127 self._dict_impl().__setattr__(self, '_lh', val)
127
128
128 lh = property(_get_lh, _set_lh)
129 lh = property(_get_lh, _set_lh)
129
130
130 # Double-linked list tail
131 # Double-linked list tail
131 def _get_lt(self):
132 def _get_lt(self):
132 dict_impl = self._dict_impl()
133 dict_impl = self._dict_impl()
133 if not hasattr(self, '_lt'):
134 if not hasattr(self, '_lt'):
134 dict_impl.__setattr__(self, '_lt', _nil)
135 dict_impl.__setattr__(self, '_lt', _nil)
135 return dict_impl.__getattribute__(self, '_lt')
136 return dict_impl.__getattribute__(self, '_lt')
136
137
137 def _set_lt(self, val):
138 def _set_lt(self, val):
138 self._dict_impl().__setattr__(self, '_lt', val)
139 self._dict_impl().__setattr__(self, '_lt', val)
139
140
140 lt = property(_get_lt, _set_lt)
141 lt = property(_get_lt, _set_lt)
141
142
142 def __getitem__(self, key):
143 def __getitem__(self, key):
143 return self._dict_impl().__getitem__(self, key)[1]
144 return self._dict_impl().__getitem__(self, key)[1]
144
145
145 def __setitem__(self, key, val):
146 def __setitem__(self, key, val):
146 dict_impl = self._dict_impl()
147 dict_impl = self._dict_impl()
147 try:
148 try:
148 dict_impl.__getitem__(self, key)[1] = val
149 dict_impl.__getitem__(self, key)[1] = val
149 except KeyError, e:
150 except KeyError:
150 new = [dict_impl.__getattribute__(self, 'lt'), val, _nil]
151 new = [dict_impl.__getattribute__(self, 'lt'), val, _nil]
151 dict_impl.__setitem__(self, key, new)
152 dict_impl.__setitem__(self, key, new)
152 if dict_impl.__getattribute__(self, 'lt') == _nil:
153 if dict_impl.__getattribute__(self, 'lt') == _nil:
153 dict_impl.__setattr__(self, 'lh', key)
154 dict_impl.__setattr__(self, 'lh', key)
154 else:
155 else:
155 dict_impl.__getitem__(
156 dict_impl.__getitem__(
156 self, dict_impl.__getattribute__(self, 'lt'))[2] = key
157 self, dict_impl.__getattribute__(self, 'lt'))[2] = key
157 dict_impl.__setattr__(self, 'lt', key)
158 dict_impl.__setattr__(self, 'lt', key)
158
159
159 def __delitem__(self, key):
160 def __delitem__(self, key):
160 dict_impl = self._dict_impl()
161 dict_impl = self._dict_impl()
161 pred, _ , succ = self._dict_impl().__getitem__(self, key)
162 pred, _, succ = self._dict_impl().__getitem__(self, key)
162 if pred == _nil:
163 if pred == _nil:
163 dict_impl.__setattr__(self, 'lh', succ)
164 dict_impl.__setattr__(self, 'lh', succ)
164 else:
165 else:
165 dict_impl.__getitem__(self, pred)[2] = succ
166 dict_impl.__getitem__(self, pred)[2] = succ
166 if succ == _nil:
167 if succ == _nil:
167 dict_impl.__setattr__(self, 'lt', pred)
168 dict_impl.__setattr__(self, 'lt', pred)
168 else:
169 else:
169 dict_impl.__getitem__(self, succ)[0] = pred
170 dict_impl.__getitem__(self, succ)[0] = pred
170 dict_impl.__delitem__(self, key)
171 dict_impl.__delitem__(self, key)
171
172
172 def __contains__(self, key):
173 def __contains__(self, key):
173 return key in self.keys()
174 return key in self.keys()
174
175
175 def __len__(self):
176 def __len__(self):
176 return len(self.keys())
177 return len(self.keys())
177
178
178 def __str__(self):
179 def __str__(self):
179 pairs = ("%r: %r" % (k, v) for k, v in self.iteritems())
180 pairs = ("%r: %r" % (k, v) for k, v in self.iteritems())
180 return "{%s}" % ", ".join(pairs)
181 return "{%s}" % ", ".join(pairs)
181
182
182 def __repr__(self):
183 def __repr__(self):
183 if self:
184 if self:
184 pairs = ("(%r, %r)" % (k, v) for k, v in self.iteritems())
185 pairs = ("(%r, %r)" % (k, v) for k, v in self.iteritems())
185 return "odict([%s])" % ", ".join(pairs)
186 return "odict([%s])" % ", ".join(pairs)
186 else:
187 else:
187 return "odict()"
188 return "odict()"
188
189
189 def get(self, k, x=None):
190 def get(self, k, x=None):
190 if k in self:
191 if k in self:
191 return self._dict_impl().__getitem__(self, k)[1]
192 return self._dict_impl().__getitem__(self, k)[1]
192 else:
193 else:
193 return x
194 return x
194
195
195 def __iter__(self):
196 def __iter__(self):
196 dict_impl = self._dict_impl()
197 dict_impl = self._dict_impl()
197 curr_key = dict_impl.__getattribute__(self, 'lh')
198 curr_key = dict_impl.__getattribute__(self, 'lh')
198 while curr_key != _nil:
199 while curr_key != _nil:
199 yield curr_key
200 yield curr_key
200 curr_key = dict_impl.__getitem__(self, curr_key)[2]
201 curr_key = dict_impl.__getitem__(self, curr_key)[2]
201
202
202 iterkeys = __iter__
203 iterkeys = __iter__
203
204
204 def keys(self):
205 def keys(self):
205 return list(self.iterkeys())
206 return list(self.iterkeys())
206
207
207 def itervalues(self):
208 def itervalues(self):
208 dict_impl = self._dict_impl()
209 dict_impl = self._dict_impl()
209 curr_key = dict_impl.__getattribute__(self, 'lh')
210 curr_key = dict_impl.__getattribute__(self, 'lh')
210 while curr_key != _nil:
211 while curr_key != _nil:
211 _, val, curr_key = dict_impl.__getitem__(self, curr_key)
212 _, val, curr_key = dict_impl.__getitem__(self, curr_key)
212 yield val
213 yield val
213
214
214 def values(self):
215 def values(self):
215 return list(self.itervalues())
216 return list(self.itervalues())
216
217
217 def iteritems(self):
218 def iteritems(self):
218 dict_impl = self._dict_impl()
219 dict_impl = self._dict_impl()
219 curr_key = dict_impl.__getattribute__(self, 'lh')
220 curr_key = dict_impl.__getattribute__(self, 'lh')
220 while curr_key != _nil:
221 while curr_key != _nil:
221 _, val, next_key = dict_impl.__getitem__(self, curr_key)
222 _, val, next_key = dict_impl.__getitem__(self, curr_key)
222 yield curr_key, val
223 yield curr_key, val
223 curr_key = next_key
224 curr_key = next_key
224
225
225 def items(self):
226 def items(self):
226 return list(self.iteritems())
227 return list(self.iteritems())
227
228
228 def sort(self, cmp=None, key=None, reverse=False):
229 def sort(self, cmp=None, key=None, reverse=False):
229 items = [(k, v) for k, v in self.items()]
230 items = [(k, v) for k, v in self.items()]
230 if cmp is not None:
231 if cmp is not None:
231 items = sorted(items, cmp=cmp)
232 items = sorted(items, cmp=cmp)
232 elif key is not None:
233 elif key is not None:
233 items = sorted(items, key=key)
234 items = sorted(items, key=key)
234 else:
235 else:
235 items = sorted(items, key=lambda x: x[1])
236 items = sorted(items, key=lambda x: x[1])
236 if reverse:
237 if reverse:
237 items.reverse()
238 items.reverse()
238 self.clear()
239 self.clear()
239 self.__init__(items)
240 self.__init__(items)
240
241
241 def clear(self):
242 def clear(self):
242 dict_impl = self._dict_impl()
243 dict_impl = self._dict_impl()
243 dict_impl.clear(self)
244 dict_impl.clear(self)
244 dict_impl.__setattr__(self, 'lh', _nil)
245 dict_impl.__setattr__(self, 'lh', _nil)
245 dict_impl.__setattr__(self, 'lt', _nil)
246 dict_impl.__setattr__(self, 'lt', _nil)
246
247
247 def copy(self):
248 def copy(self):
248 return self.__class__(self)
249 return self.__class__(self)
249
250
250 def update(self, data=(), **kwds):
251 def update(self, data=(), **kwds):
251 if kwds:
252 if kwds:
252 raise TypeError("update() of ordered dict takes no keyword "
253 raise TypeError("update() of ordered dict takes no keyword "
253 "arguments to avoid an ordering trap.")
254 "arguments to avoid an ordering trap.")
254 if hasattr(data, "iteritems"):
255 if hasattr(data, "iteritems"):
255 data = data.iteritems()
256 data = data.iteritems()
256 for key, val in data:
257 for key, val in data:
257 self[key] = val
258 self[key] = val
258
259
259 def setdefault(self, k, x=None):
260 def setdefault(self, k, x=None):
260 try:
261 try:
261 return self[k]
262 return self[k]
262 except KeyError:
263 except KeyError:
263 self[k] = x
264 self[k] = x
264 return x
265 return x
265
266
266 def pop(self, k, x=_nil):
267 def pop(self, k, x=_nil):
267 try:
268 try:
268 val = self[k]
269 val = self[k]
269 del self[k]
270 del self[k]
270 return val
271 return val
271 except KeyError:
272 except KeyError:
272 if x == _nil:
273 if x == _nil:
273 raise
274 raise
274 return x
275 return x
275
276
276 def popitem(self):
277 def popitem(self):
277 try:
278 try:
278 dict_impl = self._dict_impl()
279 dict_impl = self._dict_impl()
279 key = dict_impl.__getattribute__(self, 'lt')
280 key = dict_impl.__getattribute__(self, 'lt')
280 return key, self.pop(key)
281 return key, self.pop(key)
281 except KeyError:
282 except KeyError:
282 raise KeyError("'popitem(): ordered dictionary is empty'")
283 raise KeyError("'popitem(): ordered dictionary is empty'")
283
284
284 def riterkeys(self):
285 def riterkeys(self):
285 """To iterate on keys in reversed order.
286 """To iterate on keys in reversed order.
286 """
287 """
287 dict_impl = self._dict_impl()
288 dict_impl = self._dict_impl()
288 curr_key = dict_impl.__getattribute__(self, 'lt')
289 curr_key = dict_impl.__getattribute__(self, 'lt')
289 while curr_key != _nil:
290 while curr_key != _nil:
290 yield curr_key
291 yield curr_key
291 curr_key = dict_impl.__getitem__(self, curr_key)[0]
292 curr_key = dict_impl.__getitem__(self, curr_key)[0]
292
293
293 __reversed__ = riterkeys
294 __reversed__ = riterkeys
294
295
295 def rkeys(self):
296 def rkeys(self):
296 """List of the keys in reversed order.
297 """List of the keys in reversed order.
297 """
298 """
298 return list(self.riterkeys())
299 return list(self.riterkeys())
299
300
300 def ritervalues(self):
301 def ritervalues(self):
301 """To iterate on values in reversed order.
302 """To iterate on values in reversed order.
302 """
303 """
303 dict_impl = self._dict_impl()
304 dict_impl = self._dict_impl()
304 curr_key = dict_impl.__getattribute__(self, 'lt')
305 curr_key = dict_impl.__getattribute__(self, 'lt')
305 while curr_key != _nil:
306 while curr_key != _nil:
306 curr_key, val, _ = dict_impl.__getitem__(self, curr_key)
307 curr_key, val, _ = dict_impl.__getitem__(self, curr_key)
307 yield val
308 yield val
308
309
309 def rvalues(self):
310 def rvalues(self):
310 """List of the values in reversed order.
311 """List of the values in reversed order.
311 """
312 """
312 return list(self.ritervalues())
313 return list(self.ritervalues())
313
314
314 def riteritems(self):
315 def riteritems(self):
315 """To iterate on (key, value) in reversed order.
316 """To iterate on (key, value) in reversed order.
316 """
317 """
317 dict_impl = self._dict_impl()
318 dict_impl = self._dict_impl()
318 curr_key = dict_impl.__getattribute__(self, 'lt')
319 curr_key = dict_impl.__getattribute__(self, 'lt')
319 while curr_key != _nil:
320 while curr_key != _nil:
320 pred_key, val, _ = dict_impl.__getitem__(self, curr_key)
321 pred_key, val, _ = dict_impl.__getitem__(self, curr_key)
321 yield curr_key, val
322 yield curr_key, val
322 curr_key = pred_key
323 curr_key = pred_key
323
324
324 def ritems(self):
325 def ritems(self):
325 """List of the (key, value) in reversed order.
326 """List of the (key, value) in reversed order.
326 """
327 """
327 return list(self.riteritems())
328 return list(self.riteritems())
328
329
329 def firstkey(self):
330 def firstkey(self):
330 if self:
331 if self:
331 return self._dict_impl().__getattribute__(self, 'lh')
332 return self._dict_impl().__getattribute__(self, 'lh')
332 else:
333 else:
333 raise KeyError("'firstkey(): ordered dictionary is empty'")
334 raise KeyError("'firstkey(): ordered dictionary is empty'")
334
335
335 def lastkey(self):
336 def lastkey(self):
336 if self:
337 if self:
337 return self._dict_impl().__getattribute__(self, 'lt')
338 return self._dict_impl().__getattribute__(self, 'lt')
338 else:
339 else:
339 raise KeyError("'lastkey(): ordered dictionary is empty'")
340 raise KeyError("'lastkey(): ordered dictionary is empty'")
340
341
341 def as_dict(self):
342 def as_dict(self):
342 return self._dict_impl()(self.items())
343 return self._dict_impl()(self.items())
343
344
344 def _repr(self):
345 def _repr(self):
345 """_repr(): low level repr of the whole data contained in the odict.
346 """_repr(): low level repr of the whole data contained in the odict.
346 Useful for debugging.
347 Useful for debugging.
347 """
348 """
348 dict_impl = self._dict_impl()
349 dict_impl = self._dict_impl()
349 form = "odict low level repr lh,lt,data: %r, %r, %s"
350 form = "odict low level repr lh,lt,data: %r, %r, %s"
350 return form % (dict_impl.__getattribute__(self, 'lh'),
351 return form % (dict_impl.__getattribute__(self, 'lh'),
351 dict_impl.__getattribute__(self, 'lt'),
352 dict_impl.__getattribute__(self, 'lt'),
352 dict_impl.__repr__(self))
353 dict_impl.__repr__(self))
353
354
355
354 class OrderedDict(_odict, dict):
356 class OrderedDict(_odict, dict):
355
357
356 def _dict_impl(self):
358 def _dict_impl(self):
357 return dict
359 return dict
358
360
359
361
360 #==============================================================================
362 #==============================================================================
361 # OrderedSet
363 # OrderedSet
362 #==============================================================================
364 #==============================================================================
363 from sqlalchemy.util import OrderedSet
365 from sqlalchemy.util import OrderedSet
364
366
365
367
366 #==============================================================================
368 #==============================================================================
367 # kill FUNCTIONS
369 # kill FUNCTIONS
368 #==============================================================================
370 #==============================================================================
369 if __platform__ in PLATFORM_WIN:
371 if __platform__ in PLATFORM_WIN:
370 import ctypes
372 import ctypes
371
373
372 def kill(pid, sig):
374 def kill(pid, sig):
373 """kill function for Win32"""
375 """kill function for Win32"""
374 kernel32 = ctypes.windll.kernel32
376 kernel32 = ctypes.windll.kernel32
375 handle = kernel32.OpenProcess(1, 0, pid)
377 handle = kernel32.OpenProcess(1, 0, pid)
376 return (0 != kernel32.TerminateProcess(handle, 0))
378 return (0 != kernel32.TerminateProcess(handle, 0))
377
379
378 else:
380 else:
379 kill = os.kill
381 kill = os.kill
@@ -1,695 +1,697 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 <%
20 <%
21 summary = lambda n:{False:'summary-short'}.get(n)
21 summary = lambda n:{False:'summary-short'}.get(n)
22 %>
22 %>
23 %if c.show_stats:
23 %if c.show_stats:
24 <div class="box box-left">
24 <div class="box box-left">
25 %else:
25 %else:
26 <div class="box">
26 <div class="box">
27 %endif
27 %endif
28 <!-- box / title -->
28 <!-- box / title -->
29 <div class="title">
29 <div class="title">
30 ${self.breadcrumbs()}
30 ${self.breadcrumbs()}
31 </div>
31 </div>
32 <!-- end box / title -->
32 <!-- end box / title -->
33 <div class="form">
33 <div class="form">
34 <div id="summary" class="fields">
34 <div id="summary" class="fields">
35
35
36 <div class="field">
36 <div class="field">
37 <div class="label-summary">
37 <div class="label-summary">
38 <label>${_('Name')}:</label>
38 <label>${_('Name')}:</label>
39 </div>
39 </div>
40 <div class="input ${summary(c.show_stats)}">
40 <div class="input ${summary(c.show_stats)}">
41 <div style="float:right;padding:5px 0px 0px 5px">
41 <div style="float:right;padding:5px 0px 0px 5px">
42 %if c.rhodecode_user.username != 'default':
42 %if c.rhodecode_user.username != 'default':
43 ${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')}
43 ${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')}
44 ${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')}
44 ${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')}
45 %else:
45 %else:
46 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name),class_='rss_icon')}
46 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name),class_='rss_icon')}
47 ${h.link_to(_('ATOM'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name),class_='atom_icon')}
47 ${h.link_to(_('ATOM'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name),class_='atom_icon')}
48 %endif
48 %endif
49 </div>
49 </div>
50 %if c.rhodecode_user.username != 'default':
50 %if c.rhodecode_user.username != 'default':
51 %if c.following:
51 %if c.following:
52 <span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
52 <span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
53 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
53 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
54 </span>
54 </span>
55 %else:
55 %else:
56 <span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
56 <span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
57 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
57 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
58 </span>
58 </span>
59 %endif
59 %endif
60 %endif:
60 %endif:
61 ##REPO TYPE
61 ##REPO TYPE
62 %if c.dbrepo.repo_type =='hg':
62 %if c.dbrepo.repo_type =='hg':
63 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/>
63 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/>
64 %endif
64 %endif
65 %if c.dbrepo.repo_type =='git':
65 %if c.dbrepo.repo_type =='git':
66 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
66 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
67 %endif
67 %endif
68
68
69 ##PUBLIC/PRIVATE
69 ##PUBLIC/PRIVATE
70 %if c.dbrepo.private:
70 %if c.dbrepo.private:
71 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
71 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
72 %else:
72 %else:
73 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
73 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
74 %endif
74 %endif
75
75
76 ##REPO NAME
76 ##REPO NAME
77 <span class="repo_name" title="${_('Non changable ID %s') % c.dbrepo.repo_id}">${h.repo_link(c.dbrepo.groups_and_repo)}</span>
77 <span class="repo_name" title="${_('Non changable ID %s') % c.dbrepo.repo_id}">${h.repo_link(c.dbrepo.groups_and_repo)}</span>
78
78
79 ##FORK
79 ##FORK
80 %if c.dbrepo.fork:
80 %if c.dbrepo.fork:
81 <div style="margin-top:5px;clear:both"">
81 <div style="margin-top:5px;clear:both"">
82 <a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}"><img class="icon" alt="${_('public')}" title="${_('Fork of')} ${c.dbrepo.fork.repo_name}" src="${h.url('/images/icons/arrow_divide.png')}"/>
82 <a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}"><img class="icon" alt="${_('public')}" title="${_('Fork of')} ${c.dbrepo.fork.repo_name}" src="${h.url('/images/icons/arrow_divide.png')}"/>
83 ${_('Fork of')} ${c.dbrepo.fork.repo_name}
83 ${_('Fork of')} ${c.dbrepo.fork.repo_name}
84 </a>
84 </a>
85 </div>
85 </div>
86 %endif
86 %endif
87 ##REMOTE
87 ##REMOTE
88 %if c.dbrepo.clone_uri:
88 %if c.dbrepo.clone_uri:
89 <div style="margin-top:5px;clear:both">
89 <div style="margin-top:5px;clear:both">
90 <a href="${h.url(str(h.hide_credentials(c.dbrepo.clone_uri)))}"><img class="icon" alt="${_('remote clone')}" title="${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}" src="${h.url('/images/icons/connect.png')}"/>
90 <a href="${h.url(str(h.hide_credentials(c.dbrepo.clone_uri)))}"><img class="icon" alt="${_('remote clone')}" title="${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}" src="${h.url('/images/icons/connect.png')}"/>
91 ${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}
91 ${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}
92 </a>
92 </a>
93 </div>
93 </div>
94 %endif
94 %endif
95 </div>
95 </div>
96 </div>
96 </div>
97
97
98 <div class="field">
98 <div class="field">
99 <div class="label-summary">
99 <div class="label-summary">
100 <label>${_('Description')}:</label>
100 <label>${_('Description')}:</label>
101 </div>
101 </div>
102 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(c.dbrepo.description)}</div>
102 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(c.dbrepo.description)}</div>
103 </div>
103 </div>
104
104
105 <div class="field">
105 <div class="field">
106 <div class="label-summary">
106 <div class="label-summary">
107 <label>${_('Contact')}:</label>
107 <label>${_('Contact')}:</label>
108 </div>
108 </div>
109 <div class="input ${summary(c.show_stats)}">
109 <div class="input ${summary(c.show_stats)}">
110 <div class="gravatar">
110 <div class="gravatar">
111 <img alt="gravatar" src="${h.gravatar_url(c.dbrepo.user.email)}"/>
111 <img alt="gravatar" src="${h.gravatar_url(c.dbrepo.user.email)}"/>
112 </div>
112 </div>
113 ${_('Username')}: ${c.dbrepo.user.username}<br/>
113 ${_('Username')}: ${c.dbrepo.user.username}<br/>
114 ${_('Name')}: ${c.dbrepo.user.name} ${c.dbrepo.user.lastname}<br/>
114 ${_('Name')}: ${c.dbrepo.user.name} ${c.dbrepo.user.lastname}<br/>
115 ${_('Email')}: <a href="mailto:${c.dbrepo.user.email}">${c.dbrepo.user.email}</a>
115 ${_('Email')}: <a href="mailto:${c.dbrepo.user.email}">${c.dbrepo.user.email}</a>
116 </div>
116 </div>
117 </div>
117 </div>
118
118
119 <div class="field">
119 <div class="field">
120 <div class="label-summary">
120 <div class="label-summary">
121 <label>${_('Clone url')}:</label>
121 <label>${_('Clone url')}:</label>
122 </div>
122 </div>
123 <div class="input ${summary(c.show_stats)}">
123 <div class="input ${summary(c.show_stats)}">
124 <div style="display:none" id="clone_by_name" class="ui-btn clone">${_('Show by Name')}</div>
124 <div style="display:none" id="clone_by_name" class="ui-btn clone">${_('Show by Name')}</div>
125 <div id="clone_by_id" class="ui-btn clone">${_('Show by ID')}</div>
125 <div id="clone_by_id" class="ui-btn clone">${_('Show by ID')}</div>
126 <input style="width:80%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="${c.clone_repo_url}"/>
126 <input style="width:80%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="${c.clone_repo_url}"/>
127 <input style="display:none;width:80%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="${c.clone_repo_url_id}"/>
127 <input style="display:none;width:80%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="${c.clone_repo_url_id}"/>
128 </div>
128 </div>
129 </div>
129 </div>
130
130
131 <div class="field">
131 <div class="field">
132 <div class="label-summary">
132 <div class="label-summary">
133 <label>${_('Trending files')}:</label>
133 <label>${_('Trending files')}:</label>
134 </div>
134 </div>
135 <div class="input ${summary(c.show_stats)}">
135 <div class="input ${summary(c.show_stats)}">
136 %if c.show_stats:
136 %if c.show_stats:
137 <div id="lang_stats"></div>
137 <div id="lang_stats"></div>
138 %else:
138 %else:
139 ${_('Statistics are disabled for this repository')}
139 ${_('Statistics are disabled for this repository')}
140 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
140 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
141 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
141 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
142 %endif
142 %endif
143 %endif
143 %endif
144 </div>
144 </div>
145 </div>
145 </div>
146
146
147 <div class="field">
147 <div class="field">
148 <div class="label-summary">
148 <div class="label-summary">
149 <label>${_('Download')}:</label>
149 <label>${_('Download')}:</label>
150 </div>
150 </div>
151 <div class="input ${summary(c.show_stats)}">
151 <div class="input ${summary(c.show_stats)}">
152 %if len(c.rhodecode_repo.revisions) == 0:
152 %if len(c.rhodecode_repo.revisions) == 0:
153 ${_('There are no downloads yet')}
153 ${_('There are no downloads yet')}
154 %elif c.enable_downloads is False:
154 %elif c.enable_downloads is False:
155 ${_('Downloads are disabled for this repository')}
155 ${_('Downloads are disabled for this repository')}
156 %if h.HasPermissionAll('hg.admin')('enable downloads on from summary'):
156 %if h.HasPermissionAll('hg.admin')('enable downloads on from summary'):
157 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
157 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
158 %endif
158 %endif
159 %else:
159 %else:
160 ${h.select('download_options',c.rhodecode_repo.get_changeset().raw_id,c.download_options)}
160 ${h.select('download_options',c.rhodecode_repo.get_changeset().raw_id,c.download_options)}
161 <span id="${'zip_link'}">${h.link_to('Download as zip',h.url('files_archive_home',repo_name=c.dbrepo.repo_name,fname='tip.zip'),class_="archive_icon ui-btn")}</span>
161 <span id="${'zip_link'}">${h.link_to('Download as zip',h.url('files_archive_home',repo_name=c.dbrepo.repo_name,fname='tip.zip'),class_="archive_icon ui-btn")}</span>
162 <span style="vertical-align: bottom">
162 <span style="vertical-align: bottom">
163 <input id="archive_subrepos" type="checkbox" name="subrepos"/> <span class="tooltip" title="${_('Check this to download archive with subrepos')}" >${_('with subrepos')}</span>
163 <input id="archive_subrepos" type="checkbox" name="subrepos"/> <span class="tooltip" title="${_('Check this to download archive with subrepos')}" >${_('with subrepos')}</span>
164 </span>
164 </span>
165 %endif
165 %endif
166 </div>
166 </div>
167 </div>
167 </div>
168 </div>
168 </div>
169 </div>
169 </div>
170 </div>
170 </div>
171
171
172 %if c.show_stats:
172 %if c.show_stats:
173 <div class="box box-right" style="min-height:455px">
173 <div class="box box-right" style="min-height:455px">
174 <!-- box / title -->
174 <!-- box / title -->
175 <div class="title">
175 <div class="title">
176 <h5>${_('Commit activity by day / author')}</h5>
176 <h5>${_('Commit activity by day / author')}</h5>
177 </div>
177 </div>
178
178
179 <div class="graph">
179 <div class="graph">
180 <div style="padding:0 10px 10px 15px;font-size: 1.2em;">
180 <div style="padding:0 10px 10px 15px;font-size: 1.2em;">
181 %if c.no_data:
181 %if c.no_data:
182 ${c.no_data_msg}
182 ${c.no_data_msg}
183 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
183 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
184 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
184 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
185 %endif
185 %endif
186 %else:
186 %else:
187 ${_('Loaded in')} ${c.stats_percentage} %
187 ${_('Loaded in')} ${c.stats_percentage} %
188 %endif
188 %endif
189 </div>
189 </div>
190 <div id="commit_history" style="width:450px;height:300px;float:left"></div>
190 <div id="commit_history" style="width:450px;height:300px;float:left"></div>
191 <div style="clear: both;height: 10px"></div>
191 <div style="clear: both;height: 10px"></div>
192 <div id="overview" style="width:450px;height:100px;float:left"></div>
192 <div id="overview" style="width:450px;height:100px;float:left"></div>
193
193
194 <div id="legend_data" style="clear:both;margin-top:10px;">
194 <div id="legend_data" style="clear:both;margin-top:10px;">
195 <div id="legend_container"></div>
195 <div id="legend_container"></div>
196 <div id="legend_choices">
196 <div id="legend_choices">
197 <table id="legend_choices_tables" class="noborder" style="font-size:smaller;color:#545454"></table>
197 <table id="legend_choices_tables" class="noborder" style="font-size:smaller;color:#545454"></table>
198 </div>
198 </div>
199 </div>
199 </div>
200 </div>
200 </div>
201 </div>
201 </div>
202 %endif
202 %endif
203
203
204 <div class="box">
204 <div class="box">
205 <div class="title">
205 <div class="title">
206 <div class="breadcrumbs">
206 <div class="breadcrumbs">
207 %if c.repo_changesets:
207 %if c.repo_changesets:
208 ${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}
208 ${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}
209 %else:
209 %else:
210 ${_('Quick start')}
210 ${_('Quick start')}
211 %endif
211 %endif
212 </div>
212 </div>
213 </div>
213 </div>
214 <div class="table">
214 <div class="table">
215 <div id="shortlog_data">
215 <div id="shortlog_data">
216 <%include file='../shortlog/shortlog_data.html'/>
216 <%include file='../shortlog/shortlog_data.html'/>
217 </div>
217 </div>
218 </div>
218 </div>
219 </div>
219 </div>
220
220
221 %if c.readme_data:
221 %if c.readme_data:
222 <div class="box" style="background-color: #FAFAFA">
222 <div class="box" style="background-color: #FAFAFA">
223 <div class="title">
223 <div class="title">
224 <div class="breadcrumbs"><a href="${h.url('files_home',repo_name=c.repo_name,revision='tip',f_path=c.readme_file)}">${c.readme_file}</a></div>
224 <div class="breadcrumbs"><a href="${h.url('files_home',repo_name=c.repo_name,revision='tip',f_path=c.readme_file)}">${c.readme_file}</a></div>
225 </div>
225 </div>
226 <div class="readme">
226 <div class="readme">
227 <div class="readme_box">
227 <div class="readme_box">
228 ${c.readme_data|n}
228 ${c.readme_data|n}
229 </div>
229 </div>
230 </div>
230 </div>
231 </div>
231 </div>
232 %endif
232 %endif
233
233
234 <script type="text/javascript">
234 <script type="text/javascript">
235 var clone_url = 'clone_url';
235 var clone_url = 'clone_url';
236 YUE.on(clone_url,'click',function(e){
236 YUE.on(clone_url,'click',function(e){
237 if(YUD.hasClass(clone_url,'selected')){
237 if(YUD.hasClass(clone_url,'selected')){
238 return
238 return
239 }
239 }
240 else{
240 else{
241 YUD.addClass(clone_url,'selected');
241 YUD.addClass(clone_url,'selected');
242 YUD.get(clone_url).select();
242 YUD.get(clone_url).select();
243 }
243 }
244 })
244 })
245
245
246 YUE.on('clone_by_name','click',function(e){
246 YUE.on('clone_by_name','click',function(e){
247 // show url by name and hide name button
247 // show url by name and hide name button
248 YUD.setStyle('clone_url','display','');
248 YUD.setStyle('clone_url','display','');
249 YUD.setStyle('clone_by_name','display','none');
249 YUD.setStyle('clone_by_name','display','none');
250
250
251 // hide url by id and show name button
251 // hide url by id and show name button
252 YUD.setStyle('clone_by_id','display','');
252 YUD.setStyle('clone_by_id','display','');
253 YUD.setStyle('clone_url_id','display','none');
253 YUD.setStyle('clone_url_id','display','none');
254
254
255 })
255 })
256 YUE.on('clone_by_id','click',function(e){
256 YUE.on('clone_by_id','click',function(e){
257
257
258 // show url by id and hide id button
258 // show url by id and hide id button
259 YUD.setStyle('clone_by_id','display','none');
259 YUD.setStyle('clone_by_id','display','none');
260 YUD.setStyle('clone_url_id','display','');
260 YUD.setStyle('clone_url_id','display','');
261
261
262 // hide url by name and show id button
262 // hide url by name and show id button
263 YUD.setStyle('clone_by_name','display','');
263 YUD.setStyle('clone_by_name','display','');
264 YUD.setStyle('clone_url','display','none');
264 YUD.setStyle('clone_url','display','none');
265 })
265 })
266
266
267
267
268 var tmpl_links = {};
268 var tmpl_links = {};
269 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
269 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
270 tmpl_links["${archive['type']}"] = '${h.link_to('__NAME__', h.url('files_archive_home',repo_name=c.dbrepo.repo_name, fname='__CS__'+archive['extension'],subrepos='__SUB__'),class_='archive_icon ui-btn')}';
270 tmpl_links["${archive['type']}"] = '${h.link_to('__NAME__', h.url('files_archive_home',repo_name=c.dbrepo.repo_name, fname='__CS__'+archive['extension'],subrepos='__SUB__'),class_='archive_icon ui-btn')}';
271 %endfor
271 %endfor
272
272
273 YUE.on(['download_options','archive_subrepos'],'change',function(e){
273 YUE.on(['download_options','archive_subrepos'],'change',function(e){
274 var sm = YUD.get('download_options');
274 var sm = YUD.get('download_options');
275 var new_cs = sm.options[sm.selectedIndex];
275 var new_cs = sm.options[sm.selectedIndex];
276
276
277 for(k in tmpl_links){
277 for(k in tmpl_links){
278 var s = YUD.get(k+'_link');
278 var s = YUD.get(k+'_link');
279 if(s){
279 if(s){
280 var title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}";
280 var title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}";
281 title_tmpl= title_tmpl.replace('__CS_NAME__',new_cs.text);
281 title_tmpl= title_tmpl.replace('__CS_NAME__',new_cs.text);
282 title_tmpl = title_tmpl.replace('__CS_EXT__',k);
282 title_tmpl = title_tmpl.replace('__CS_EXT__',k);
283
283
284 var url = tmpl_links[k].replace('__CS__',new_cs.value);
284 var url = tmpl_links[k].replace('__CS__',new_cs.value);
285 var subrepos = YUD.get('archive_subrepos').checked;
285 var subrepos = YUD.get('archive_subrepos').checked;
286 url = url.replace('__SUB__',subrepos);
286 url = url.replace('__SUB__',subrepos);
287 url = url.replace('__NAME__',title_tmpl);
287 url = url.replace('__NAME__',title_tmpl);
288 s.innerHTML = url
288 s.innerHTML = url
289 }
289 }
290 }
290 }
291 });
291 });
292 </script>
292 </script>
293 %if c.show_stats:
293 %if c.show_stats:
294 <script type="text/javascript">
294 <script type="text/javascript">
295 var data = ${c.trending_languages|n};
295 var data = ${c.trending_languages|n};
296 var total = 0;
296 var total = 0;
297 var no_data = true;
297 var no_data = true;
298 for (k in data){
299 total += data[k].count;
300 no_data = false;
301 }
302 var tbl = document.createElement('table');
298 var tbl = document.createElement('table');
303 tbl.setAttribute('class','trending_language_tbl');
299 tbl.setAttribute('class','trending_language_tbl');
304 var cnt = 0;
300 var cnt = 0;
305 for (k in data){
301
302 for (var i=0;i<data.length;i++){
303 total += data[i][1].count;
306 cnt += 1;
304 cnt += 1;
305 no_data = false;
306
307 var hide = cnt>2;
307 var hide = cnt>2;
308 var tr = document.createElement('tr');
308 var tr = document.createElement('tr');
309 if (hide){
309 if (hide){
310 tr.setAttribute('style','display:none');
310 tr.setAttribute('style','display:none');
311 tr.setAttribute('class','stats_hidden');
311 tr.setAttribute('class','stats_hidden');
312 }
312 }
313 var percentage = Math.round((data[k].count/total*100),2);
313 var k = data[i][0];
314 var value = data[k].count;
314 var obj = data[i][1];
315 var percentage = Math.round((obj.count/total*100),2);
316
315 var td1 = document.createElement('td');
317 var td1 = document.createElement('td');
316 td1.width = 150;
318 td1.width = 150;
317 var trending_language_label = document.createElement('div');
319 var trending_language_label = document.createElement('div');
318 trending_language_label.innerHTML = data[k].desc+" ("+k+")";
320 trending_language_label.innerHTML = obj.desc+" ("+k+")";
319 td1.appendChild(trending_language_label);
321 td1.appendChild(trending_language_label);
320
322
321 var td2 = document.createElement('td');
323 var td2 = document.createElement('td');
322 td2.setAttribute('style','padding-right:14px !important');
324 td2.setAttribute('style','padding-right:14px !important');
323 var trending_language = document.createElement('div');
325 var trending_language = document.createElement('div');
324 var nr_files = value+" ${_('files')}";
326 var nr_files = obj.count+" ${_('files')}";
325
327
326 trending_language.title = k+" "+nr_files;
328 trending_language.title = k+" "+nr_files;
327
329
328 if (percentage>22){
330 if (percentage>22){
329 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
331 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
330 }
332 }
331 else{
333 else{
332 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
334 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
333 }
335 }
334
336
335 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
337 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
336 trending_language.style.width=percentage+"%";
338 trending_language.style.width=percentage+"%";
337 td2.appendChild(trending_language);
339 td2.appendChild(trending_language);
338
340
339 tr.appendChild(td1);
341 tr.appendChild(td1);
340 tr.appendChild(td2);
342 tr.appendChild(td2);
341 tbl.appendChild(tr);
343 tbl.appendChild(tr);
342 if(cnt == 3){
344 if(cnt == 3){
343 var show_more = document.createElement('tr');
345 var show_more = document.createElement('tr');
344 var td = document.createElement('td');
346 var td = document.createElement('td');
345 lnk = document.createElement('a');
347 lnk = document.createElement('a');
346
348
347 lnk.href='#';
349 lnk.href='#';
348 lnk.innerHTML = "${_('show more')}";
350 lnk.innerHTML = "${_('show more')}";
349 lnk.id='code_stats_show_more';
351 lnk.id='code_stats_show_more';
350 td.appendChild(lnk);
352 td.appendChild(lnk);
351
353
352 show_more.appendChild(td);
354 show_more.appendChild(td);
353 show_more.appendChild(document.createElement('td'));
355 show_more.appendChild(document.createElement('td'));
354 tbl.appendChild(show_more);
356 tbl.appendChild(show_more);
355 }
357 }
356
358
357 }
359 }
358
360
359 YUD.get('lang_stats').appendChild(tbl);
361 YUD.get('lang_stats').appendChild(tbl);
360 YUE.on('code_stats_show_more','click',function(){
362 YUE.on('code_stats_show_more','click',function(){
361 l = YUD.getElementsByClassName('stats_hidden')
363 l = YUD.getElementsByClassName('stats_hidden')
362 for (e in l){
364 for (e in l){
363 YUD.setStyle(l[e],'display','');
365 YUD.setStyle(l[e],'display','');
364 };
366 };
365 YUD.setStyle(YUD.get('code_stats_show_more'),
367 YUD.setStyle(YUD.get('code_stats_show_more'),
366 'display','none');
368 'display','none');
367 });
369 });
368 </script>
370 </script>
369 <script type="text/javascript">
371 <script type="text/javascript">
370 /**
372 /**
371 * Plots summary graph
373 * Plots summary graph
372 *
374 *
373 * @class SummaryPlot
375 * @class SummaryPlot
374 * @param {from} initial from for detailed graph
376 * @param {from} initial from for detailed graph
375 * @param {to} initial to for detailed graph
377 * @param {to} initial to for detailed graph
376 * @param {dataset}
378 * @param {dataset}
377 * @param {overview_dataset}
379 * @param {overview_dataset}
378 */
380 */
379 function SummaryPlot(from,to,dataset,overview_dataset) {
381 function SummaryPlot(from,to,dataset,overview_dataset) {
380 var initial_ranges = {
382 var initial_ranges = {
381 "xaxis":{
383 "xaxis":{
382 "from":from,
384 "from":from,
383 "to":to,
385 "to":to,
384 },
386 },
385 };
387 };
386 var dataset = dataset;
388 var dataset = dataset;
387 var overview_dataset = [overview_dataset];
389 var overview_dataset = [overview_dataset];
388 var choiceContainer = YUD.get("legend_choices");
390 var choiceContainer = YUD.get("legend_choices");
389 var choiceContainerTable = YUD.get("legend_choices_tables");
391 var choiceContainerTable = YUD.get("legend_choices_tables");
390 var plotContainer = YUD.get('commit_history');
392 var plotContainer = YUD.get('commit_history');
391 var overviewContainer = YUD.get('overview');
393 var overviewContainer = YUD.get('overview');
392
394
393 var plot_options = {
395 var plot_options = {
394 bars: {show:true,align:'center',lineWidth:4},
396 bars: {show:true,align:'center',lineWidth:4},
395 legend: {show:true, container:"legend_container"},
397 legend: {show:true, container:"legend_container"},
396 points: {show:true,radius:0,fill:false},
398 points: {show:true,radius:0,fill:false},
397 yaxis: {tickDecimals:0,},
399 yaxis: {tickDecimals:0,},
398 xaxis: {
400 xaxis: {
399 mode: "time",
401 mode: "time",
400 timeformat: "%d/%m",
402 timeformat: "%d/%m",
401 min:from,
403 min:from,
402 max:to,
404 max:to,
403 },
405 },
404 grid: {
406 grid: {
405 hoverable: true,
407 hoverable: true,
406 clickable: true,
408 clickable: true,
407 autoHighlight:true,
409 autoHighlight:true,
408 color: "#999"
410 color: "#999"
409 },
411 },
410 //selection: {mode: "x"}
412 //selection: {mode: "x"}
411 };
413 };
412 var overview_options = {
414 var overview_options = {
413 legend:{show:false},
415 legend:{show:false},
414 bars: {show:true,barWidth: 2,},
416 bars: {show:true,barWidth: 2,},
415 shadowSize: 0,
417 shadowSize: 0,
416 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
418 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
417 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
419 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
418 grid: {color: "#999",},
420 grid: {color: "#999",},
419 selection: {mode: "x"}
421 selection: {mode: "x"}
420 };
422 };
421
423
422 /**
424 /**
423 *get dummy data needed in few places
425 *get dummy data needed in few places
424 */
426 */
425 function getDummyData(label){
427 function getDummyData(label){
426 return {"label":label,
428 return {"label":label,
427 "data":[{"time":0,
429 "data":[{"time":0,
428 "commits":0,
430 "commits":0,
429 "added":0,
431 "added":0,
430 "changed":0,
432 "changed":0,
431 "removed":0,
433 "removed":0,
432 }],
434 }],
433 "schema":["commits"],
435 "schema":["commits"],
434 "color":'#ffffff',
436 "color":'#ffffff',
435 }
437 }
436 }
438 }
437
439
438 /**
440 /**
439 * generate checkboxes accordindly to data
441 * generate checkboxes accordindly to data
440 * @param keys
442 * @param keys
441 * @returns
443 * @returns
442 */
444 */
443 function generateCheckboxes(data) {
445 function generateCheckboxes(data) {
444 //append checkboxes
446 //append checkboxes
445 var i = 0;
447 var i = 0;
446 choiceContainerTable.innerHTML = '';
448 choiceContainerTable.innerHTML = '';
447 for(var pos in data) {
449 for(var pos in data) {
448
450
449 data[pos].color = i;
451 data[pos].color = i;
450 i++;
452 i++;
451 if(data[pos].label != ''){
453 if(data[pos].label != ''){
452 choiceContainerTable.innerHTML += '<tr><td>'+
454 choiceContainerTable.innerHTML += '<tr><td>'+
453 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
455 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
454 +data[pos].label+
456 +data[pos].label+
455 '</td></tr>';
457 '</td></tr>';
456 }
458 }
457 }
459 }
458 }
460 }
459
461
460 /**
462 /**
461 * ToolTip show
463 * ToolTip show
462 */
464 */
463 function showTooltip(x, y, contents) {
465 function showTooltip(x, y, contents) {
464 var div=document.getElementById('tooltip');
466 var div=document.getElementById('tooltip');
465 if(!div) {
467 if(!div) {
466 div = document.createElement('div');
468 div = document.createElement('div');
467 div.id="tooltip";
469 div.id="tooltip";
468 div.style.position="absolute";
470 div.style.position="absolute";
469 div.style.border='1px solid #fdd';
471 div.style.border='1px solid #fdd';
470 div.style.padding='2px';
472 div.style.padding='2px';
471 div.style.backgroundColor='#fee';
473 div.style.backgroundColor='#fee';
472 document.body.appendChild(div);
474 document.body.appendChild(div);
473 }
475 }
474 YUD.setStyle(div, 'opacity', 0);
476 YUD.setStyle(div, 'opacity', 0);
475 div.innerHTML = contents;
477 div.innerHTML = contents;
476 div.style.top=(y + 5) + "px";
478 div.style.top=(y + 5) + "px";
477 div.style.left=(x + 5) + "px";
479 div.style.left=(x + 5) + "px";
478
480
479 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
481 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
480 anim.animate();
482 anim.animate();
481 }
483 }
482
484
483 /**
485 /**
484 * This function will detect if selected period has some changesets
486 * This function will detect if selected period has some changesets
485 for this user if it does this data is then pushed for displaying
487 for this user if it does this data is then pushed for displaying
486 Additionally it will only display users that are selected by the checkbox
488 Additionally it will only display users that are selected by the checkbox
487 */
489 */
488 function getDataAccordingToRanges(ranges) {
490 function getDataAccordingToRanges(ranges) {
489
491
490 var data = [];
492 var data = [];
491 var new_dataset = {};
493 var new_dataset = {};
492 var keys = [];
494 var keys = [];
493 var max_commits = 0;
495 var max_commits = 0;
494 for(var key in dataset){
496 for(var key in dataset){
495
497
496 for(var ds in dataset[key].data){
498 for(var ds in dataset[key].data){
497 commit_data = dataset[key].data[ds];
499 commit_data = dataset[key].data[ds];
498 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
500 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
499
501
500 if(new_dataset[key] === undefined){
502 if(new_dataset[key] === undefined){
501 new_dataset[key] = {data:[],schema:["commits"],label:key};
503 new_dataset[key] = {data:[],schema:["commits"],label:key};
502 }
504 }
503 new_dataset[key].data.push(commit_data);
505 new_dataset[key].data.push(commit_data);
504 }
506 }
505 }
507 }
506 if (new_dataset[key] !== undefined){
508 if (new_dataset[key] !== undefined){
507 data.push(new_dataset[key]);
509 data.push(new_dataset[key]);
508 }
510 }
509 }
511 }
510
512
511 if (data.length > 0){
513 if (data.length > 0){
512 return data;
514 return data;
513 }
515 }
514 else{
516 else{
515 //just return dummy data for graph to plot itself
517 //just return dummy data for graph to plot itself
516 return [getDummyData('')];
518 return [getDummyData('')];
517 }
519 }
518 }
520 }
519
521
520 /**
522 /**
521 * redraw using new checkbox data
523 * redraw using new checkbox data
522 */
524 */
523 function plotchoiced(e,args){
525 function plotchoiced(e,args){
524 var cur_data = args[0];
526 var cur_data = args[0];
525 var cur_ranges = args[1];
527 var cur_ranges = args[1];
526
528
527 var new_data = [];
529 var new_data = [];
528 var inputs = choiceContainer.getElementsByTagName("input");
530 var inputs = choiceContainer.getElementsByTagName("input");
529
531
530 //show only checked labels
532 //show only checked labels
531 for(var i=0; i<inputs.length; i++) {
533 for(var i=0; i<inputs.length; i++) {
532 var checkbox_key = inputs[i].name;
534 var checkbox_key = inputs[i].name;
533
535
534 if(inputs[i].checked){
536 if(inputs[i].checked){
535 for(var d in cur_data){
537 for(var d in cur_data){
536 if(cur_data[d].label == checkbox_key){
538 if(cur_data[d].label == checkbox_key){
537 new_data.push(cur_data[d]);
539 new_data.push(cur_data[d]);
538 }
540 }
539 }
541 }
540 }
542 }
541 else{
543 else{
542 //push dummy data to not hide the label
544 //push dummy data to not hide the label
543 new_data.push(getDummyData(checkbox_key));
545 new_data.push(getDummyData(checkbox_key));
544 }
546 }
545 }
547 }
546
548
547 var new_options = YAHOO.lang.merge(plot_options, {
549 var new_options = YAHOO.lang.merge(plot_options, {
548 xaxis: {
550 xaxis: {
549 min: cur_ranges.xaxis.from,
551 min: cur_ranges.xaxis.from,
550 max: cur_ranges.xaxis.to,
552 max: cur_ranges.xaxis.to,
551 mode:"time",
553 mode:"time",
552 timeformat: "%d/%m",
554 timeformat: "%d/%m",
553 },
555 },
554 });
556 });
555 if (!new_data){
557 if (!new_data){
556 new_data = [[0,1]];
558 new_data = [[0,1]];
557 }
559 }
558 // do the zooming
560 // do the zooming
559 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
561 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
560
562
561 plot.subscribe("plotselected", plotselected);
563 plot.subscribe("plotselected", plotselected);
562
564
563 //resubscribe plothover
565 //resubscribe plothover
564 plot.subscribe("plothover", plothover);
566 plot.subscribe("plothover", plothover);
565
567
566 // don't fire event on the overview to prevent eternal loop
568 // don't fire event on the overview to prevent eternal loop
567 overview.setSelection(cur_ranges, true);
569 overview.setSelection(cur_ranges, true);
568
570
569 }
571 }
570
572
571 /**
573 /**
572 * plot only selected items from overview
574 * plot only selected items from overview
573 * @param ranges
575 * @param ranges
574 * @returns
576 * @returns
575 */
577 */
576 function plotselected(ranges,cur_data) {
578 function plotselected(ranges,cur_data) {
577 //updates the data for new plot
579 //updates the data for new plot
578 var data = getDataAccordingToRanges(ranges);
580 var data = getDataAccordingToRanges(ranges);
579 generateCheckboxes(data);
581 generateCheckboxes(data);
580
582
581 var new_options = YAHOO.lang.merge(plot_options, {
583 var new_options = YAHOO.lang.merge(plot_options, {
582 xaxis: {
584 xaxis: {
583 min: ranges.xaxis.from,
585 min: ranges.xaxis.from,
584 max: ranges.xaxis.to,
586 max: ranges.xaxis.to,
585 mode:"time",
587 mode:"time",
586 timeformat: "%d/%m",
588 timeformat: "%d/%m",
587 },
589 },
588 });
590 });
589 // do the zooming
591 // do the zooming
590 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
592 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
591
593
592 plot.subscribe("plotselected", plotselected);
594 plot.subscribe("plotselected", plotselected);
593
595
594 //resubscribe plothover
596 //resubscribe plothover
595 plot.subscribe("plothover", plothover);
597 plot.subscribe("plothover", plothover);
596
598
597 // don't fire event on the overview to prevent eternal loop
599 // don't fire event on the overview to prevent eternal loop
598 overview.setSelection(ranges, true);
600 overview.setSelection(ranges, true);
599
601
600 //resubscribe choiced
602 //resubscribe choiced
601 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
603 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
602 }
604 }
603
605
604 var previousPoint = null;
606 var previousPoint = null;
605
607
606 function plothover(o) {
608 function plothover(o) {
607 var pos = o.pos;
609 var pos = o.pos;
608 var item = o.item;
610 var item = o.item;
609
611
610 //YUD.get("x").innerHTML = pos.x.toFixed(2);
612 //YUD.get("x").innerHTML = pos.x.toFixed(2);
611 //YUD.get("y").innerHTML = pos.y.toFixed(2);
613 //YUD.get("y").innerHTML = pos.y.toFixed(2);
612 if (item) {
614 if (item) {
613 if (previousPoint != item.datapoint) {
615 if (previousPoint != item.datapoint) {
614 previousPoint = item.datapoint;
616 previousPoint = item.datapoint;
615
617
616 var tooltip = YUD.get("tooltip");
618 var tooltip = YUD.get("tooltip");
617 if(tooltip) {
619 if(tooltip) {
618 tooltip.parentNode.removeChild(tooltip);
620 tooltip.parentNode.removeChild(tooltip);
619 }
621 }
620 var x = item.datapoint.x.toFixed(2);
622 var x = item.datapoint.x.toFixed(2);
621 var y = item.datapoint.y.toFixed(2);
623 var y = item.datapoint.y.toFixed(2);
622
624
623 if (!item.series.label){
625 if (!item.series.label){
624 item.series.label = 'commits';
626 item.series.label = 'commits';
625 }
627 }
626 var d = new Date(x*1000);
628 var d = new Date(x*1000);
627 var fd = d.toDateString()
629 var fd = d.toDateString()
628 var nr_commits = parseInt(y);
630 var nr_commits = parseInt(y);
629
631
630 var cur_data = dataset[item.series.label].data[item.dataIndex];
632 var cur_data = dataset[item.series.label].data[item.dataIndex];
631 var added = cur_data.added;
633 var added = cur_data.added;
632 var changed = cur_data.changed;
634 var changed = cur_data.changed;
633 var removed = cur_data.removed;
635 var removed = cur_data.removed;
634
636
635 var nr_commits_suffix = " ${_('commits')} ";
637 var nr_commits_suffix = " ${_('commits')} ";
636 var added_suffix = " ${_('files added')} ";
638 var added_suffix = " ${_('files added')} ";
637 var changed_suffix = " ${_('files changed')} ";
639 var changed_suffix = " ${_('files changed')} ";
638 var removed_suffix = " ${_('files removed')} ";
640 var removed_suffix = " ${_('files removed')} ";
639
641
640
642
641 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
643 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
642 if(added==1){added_suffix=" ${_('file added')} ";}
644 if(added==1){added_suffix=" ${_('file added')} ";}
643 if(changed==1){changed_suffix=" ${_('file changed')} ";}
645 if(changed==1){changed_suffix=" ${_('file changed')} ";}
644 if(removed==1){removed_suffix=" ${_('file removed')} ";}
646 if(removed==1){removed_suffix=" ${_('file removed')} ";}
645
647
646 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
648 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
647 +'<br/>'+
649 +'<br/>'+
648 nr_commits + nr_commits_suffix+'<br/>'+
650 nr_commits + nr_commits_suffix+'<br/>'+
649 added + added_suffix +'<br/>'+
651 added + added_suffix +'<br/>'+
650 changed + changed_suffix + '<br/>'+
652 changed + changed_suffix + '<br/>'+
651 removed + removed_suffix + '<br/>');
653 removed + removed_suffix + '<br/>');
652 }
654 }
653 }
655 }
654 else {
656 else {
655 var tooltip = YUD.get("tooltip");
657 var tooltip = YUD.get("tooltip");
656
658
657 if(tooltip) {
659 if(tooltip) {
658 tooltip.parentNode.removeChild(tooltip);
660 tooltip.parentNode.removeChild(tooltip);
659 }
661 }
660 previousPoint = null;
662 previousPoint = null;
661 }
663 }
662 }
664 }
663
665
664 /**
666 /**
665 * MAIN EXECUTION
667 * MAIN EXECUTION
666 */
668 */
667
669
668 var data = getDataAccordingToRanges(initial_ranges);
670 var data = getDataAccordingToRanges(initial_ranges);
669 generateCheckboxes(data);
671 generateCheckboxes(data);
670
672
671 //main plot
673 //main plot
672 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
674 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
673
675
674 //overview
676 //overview
675 var overview = YAHOO.widget.Flot(overviewContainer,
677 var overview = YAHOO.widget.Flot(overviewContainer,
676 overview_dataset, overview_options);
678 overview_dataset, overview_options);
677
679
678 //show initial selection on overview
680 //show initial selection on overview
679 overview.setSelection(initial_ranges);
681 overview.setSelection(initial_ranges);
680
682
681 plot.subscribe("plotselected", plotselected);
683 plot.subscribe("plotselected", plotselected);
682 plot.subscribe("plothover", plothover)
684 plot.subscribe("plothover", plothover)
683
685
684 overview.subscribe("plotselected", function (ranges) {
686 overview.subscribe("plotselected", function (ranges) {
685 plot.setSelection(ranges);
687 plot.setSelection(ranges);
686 });
688 });
687
689
688 // user choices on overview
690 // user choices on overview
689 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
691 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
690 }
692 }
691 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
693 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
692 </script>
694 </script>
693 %endif
695 %endif
694
696
695 </%def>
697 </%def>
General Comments 0
You need to be logged in to leave comments. Login now