##// END OF EJS Templates
#574 Show pull request status also in shortlog (if any)
marcink -
r2884:a16f9a76 beta
parent child Browse files
Show More
@@ -1,64 +1,66
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.shortlog
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Shortlog controller for rhodecode
7 7
8 8 :created_on: Apr 18, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27
28 28 from pylons import tmpl_context as c, request, url
29 29
30 30 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
31 31 from rhodecode.lib.base import BaseRepoController, render
32 32 from rhodecode.lib.helpers import RepoPage
33 33 from pylons.controllers.util import redirect
34 34 from rhodecode.lib.utils2 import safe_int
35 35
36 36 log = logging.getLogger(__name__)
37 37
38 38
39 39 class ShortlogController(BaseRepoController):
40 40
41 41 @LoginRequired()
42 42 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
43 43 'repository.admin')
44 44 def __before__(self):
45 45 super(ShortlogController, self).__before__()
46 46
47 47 def index(self, repo_name):
48 48 p = safe_int(request.params.get('page', 1), 1)
49 49 size = safe_int(request.params.get('size', 20), 20)
50 50
51 51 def url_generator(**kw):
52 52 return url('shortlog_home', repo_name=repo_name, size=size, **kw)
53 53
54 54 c.repo_changesets = RepoPage(c.rhodecode_repo, page=p,
55 55 items_per_page=size, url=url_generator)
56 page_revisions = [x.raw_id for x in list(c.repo_changesets)]
57 c.statuses = c.rhodecode_db_repo.statuses(page_revisions)
56 58
57 59 if not c.repo_changesets:
58 60 return redirect(url('summary_home', repo_name=repo_name))
59 61
60 62 c.shortlog_data = render('shortlog/shortlog_data.html')
61 63 if request.environ.get('HTTP_X_PARTIAL_XHR'):
62 64 return c.shortlog_data
63 65 r = render('shortlog/shortlog.html')
64 66 return r
@@ -1,247 +1,249
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.summary
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Summary controller for Rhodecode
7 7
8 8 :created_on: Apr 18, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import traceback
27 27 import calendar
28 28 import logging
29 29 import urllib
30 30 from time import mktime
31 31 from datetime import timedelta, date
32 32 from urlparse import urlparse
33 33 from rhodecode.lib.compat import product
34 34
35 35 from rhodecode.lib.vcs.exceptions import ChangesetError, EmptyRepositoryError, \
36 36 NodeDoesNotExistError
37 37
38 38 from pylons import tmpl_context as c, request, url, config
39 39 from pylons.i18n.translation import _
40 40
41 41 from beaker.cache import cache_region, region_invalidate
42 42
43 43 from rhodecode.config.conf import ALL_READMES, ALL_EXTS, LANGUAGES_EXTENSIONS_MAP
44 44 from rhodecode.model.db import Statistics, CacheInvalidation
45 45 from rhodecode.lib.utils2 import safe_unicode
46 46 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
47 47 from rhodecode.lib.base import BaseRepoController, render
48 48 from rhodecode.lib.vcs.backends.base import EmptyChangeset
49 49 from rhodecode.lib.markup_renderer import MarkupRenderer
50 50 from rhodecode.lib.celerylib import run_task
51 51 from rhodecode.lib.celerylib.tasks import get_commits_stats
52 52 from rhodecode.lib.helpers import RepoPage
53 53 from rhodecode.lib.compat import json, OrderedDict
54 54 from rhodecode.lib.vcs.nodes import FileNode
55 55
56 56 log = logging.getLogger(__name__)
57 57
58 58 README_FILES = [''.join([x[0][0], x[1][0]]) for x in
59 59 sorted(list(product(ALL_READMES, ALL_EXTS)),
60 60 key=lambda y:y[0][1] + y[1][1])]
61 61
62 62
63 63 class SummaryController(BaseRepoController):
64 64
65 65 @LoginRequired()
66 66 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
67 67 'repository.admin')
68 68 def __before__(self):
69 69 super(SummaryController, self).__before__()
70 70
71 71 def index(self, repo_name):
72 72 c.dbrepo = dbrepo = c.rhodecode_db_repo
73 73 c.following = self.scm_model.is_following_repo(repo_name,
74 74 self.rhodecode_user.user_id)
75 75
76 76 def url_generator(**kw):
77 77 return url('shortlog_home', repo_name=repo_name, size=10, **kw)
78 78
79 79 c.repo_changesets = RepoPage(c.rhodecode_repo, page=1,
80 80 items_per_page=10, url=url_generator)
81 page_revisions = [x.raw_id for x in list(c.repo_changesets)]
82 c.statuses = c.rhodecode_db_repo.statuses(page_revisions)
81 83
82 84 if self.rhodecode_user.username == 'default':
83 85 # for default(anonymous) user we don't need to pass credentials
84 86 username = ''
85 87 password = ''
86 88 else:
87 89 username = str(self.rhodecode_user.username)
88 90 password = '@'
89 91
90 92 parsed_url = urlparse(url.current(qualified=True))
91 93
92 94 default_clone_uri = '{scheme}://{user}{pass}{netloc}{path}'
93 95
94 96 uri_tmpl = config.get('clone_uri', default_clone_uri)
95 97 uri_tmpl = uri_tmpl.replace('{', '%(').replace('}', ')s')
96 98 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
97 99 uri_dict = {
98 100 'user': username,
99 101 'pass': password,
100 102 'scheme': parsed_url.scheme,
101 103 'netloc': parsed_url.netloc,
102 104 'path': decoded_path
103 105 }
104 106
105 107 uri = uri_tmpl % uri_dict
106 108 # generate another clone url by id
107 109 uri_dict.update(
108 110 {'path': decoded_path.replace(repo_name, '_%s' % c.dbrepo.repo_id)}
109 111 )
110 112 uri_id = uri_tmpl % uri_dict
111 113
112 114 c.clone_repo_url = uri
113 115 c.clone_repo_url_id = uri_id
114 116 c.repo_tags = OrderedDict()
115 117 for name, hash_ in c.rhodecode_repo.tags.items()[:10]:
116 118 try:
117 119 c.repo_tags[name] = c.rhodecode_repo.get_changeset(hash_)
118 120 except ChangesetError:
119 121 c.repo_tags[name] = EmptyChangeset(hash_)
120 122
121 123 c.repo_branches = OrderedDict()
122 124 for name, hash_ in c.rhodecode_repo.branches.items()[:10]:
123 125 try:
124 126 c.repo_branches[name] = c.rhodecode_repo.get_changeset(hash_)
125 127 except ChangesetError:
126 128 c.repo_branches[name] = EmptyChangeset(hash_)
127 129
128 130 td = date.today() + timedelta(days=1)
129 131 td_1m = td - timedelta(days=calendar.mdays[td.month])
130 132 td_1y = td - timedelta(days=365)
131 133
132 134 ts_min_m = mktime(td_1m.timetuple())
133 135 ts_min_y = mktime(td_1y.timetuple())
134 136 ts_max_y = mktime(td.timetuple())
135 137
136 138 if dbrepo.enable_statistics:
137 139 c.show_stats = True
138 140 c.no_data_msg = _('No data loaded yet')
139 141 run_task(get_commits_stats, c.dbrepo.repo_name, ts_min_y, ts_max_y)
140 142 else:
141 143 c.show_stats = False
142 144 c.no_data_msg = _('Statistics are disabled for this repository')
143 145 c.ts_min = ts_min_m
144 146 c.ts_max = ts_max_y
145 147
146 148 stats = self.sa.query(Statistics)\
147 149 .filter(Statistics.repository == dbrepo)\
148 150 .scalar()
149 151
150 152 c.stats_percentage = 0
151 153
152 154 if stats and stats.languages:
153 155 c.no_data = False is dbrepo.enable_statistics
154 156 lang_stats_d = json.loads(stats.languages)
155 157 c.commit_data = stats.commit_activity
156 158 c.overview_data = stats.commit_activity_combined
157 159
158 160 lang_stats = ((x, {"count": y,
159 161 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
160 162 for x, y in lang_stats_d.items())
161 163
162 164 c.trending_languages = json.dumps(
163 165 sorted(lang_stats, reverse=True, key=lambda k: k[1])[:10]
164 166 )
165 167 last_rev = stats.stat_on_revision + 1
166 168 c.repo_last_rev = c.rhodecode_repo.count()\
167 169 if c.rhodecode_repo.revisions else 0
168 170 if last_rev == 0 or c.repo_last_rev == 0:
169 171 pass
170 172 else:
171 173 c.stats_percentage = '%.2f' % ((float((last_rev)) /
172 174 c.repo_last_rev) * 100)
173 175 else:
174 176 c.commit_data = json.dumps({})
175 177 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10]])
176 178 c.trending_languages = json.dumps({})
177 179 c.no_data = True
178 180
179 181 c.enable_downloads = dbrepo.enable_downloads
180 182 if c.enable_downloads:
181 183 c.download_options = self._get_download_links(c.rhodecode_repo)
182 184
183 185 c.readme_data, c.readme_file = \
184 186 self.__get_readme_data(c.rhodecode_db_repo)
185 187 return render('summary/summary.html')
186 188
187 189 def __get_readme_data(self, db_repo):
188 190 repo_name = db_repo.repo_name
189 191
190 192 @cache_region('long_term')
191 193 def _get_readme_from_cache(key):
192 194 readme_data = None
193 195 readme_file = None
194 196 log.debug('Looking for README file')
195 197 try:
196 198 # get's the landing revision! or tip if fails
197 199 cs = db_repo.get_landing_changeset()
198 200 if isinstance(cs, EmptyChangeset):
199 201 raise EmptyRepositoryError()
200 202 renderer = MarkupRenderer()
201 203 for f in README_FILES:
202 204 try:
203 205 readme = cs.get_node(f)
204 206 if not isinstance(readme, FileNode):
205 207 continue
206 208 readme_file = f
207 209 log.debug('Found README file `%s` rendering...' %
208 210 readme_file)
209 211 readme_data = renderer.render(readme.content, f)
210 212 break
211 213 except NodeDoesNotExistError:
212 214 continue
213 215 except ChangesetError:
214 216 log.error(traceback.format_exc())
215 217 pass
216 218 except EmptyRepositoryError:
217 219 pass
218 220 except Exception:
219 221 log.error(traceback.format_exc())
220 222
221 223 return readme_data, readme_file
222 224
223 225 key = repo_name + '_README'
224 226 inv = CacheInvalidation.invalidate(key)
225 227 if inv is not None:
226 228 region_invalidate(_get_readme_from_cache, None, key)
227 229 CacheInvalidation.set_valid(inv.cache_key)
228 230 return _get_readme_from_cache(key)
229 231
230 232 def _get_download_links(self, repo):
231 233
232 234 download_l = []
233 235
234 236 branches_group = ([], _("Branches"))
235 237 tags_group = ([], _("Tags"))
236 238
237 239 for name, chs in c.rhodecode_repo.branches.items():
238 240 #chs = chs.split(':')[-1]
239 241 branches_group[0].append((chs, name),)
240 242 download_l.append(branches_group)
241 243
242 244 for name, chs in c.rhodecode_repo.tags.items():
243 245 #chs = chs.split(':')[-1]
244 246 tags_group[0].append((chs, name),)
245 247 download_l.append(tags_group)
246 248
247 249 return download_l
@@ -1,88 +1,103
1 1 ## -*- coding: utf-8 -*-
2 2 %if c.repo_changesets:
3 3 <table class="table_disp">
4 4 <tr>
5 5 <th class="left">${_('revision')}</th>
6 6 <th class="left">${_('commit message')}</th>
7 7 <th class="left">${_('age')}</th>
8 8 <th class="left">${_('author')}</th>
9 9 <th class="left">${_('branch')}</th>
10 10 <th class="left">${_('tags')}</th>
11 11 </tr>
12 12 %for cnt,cs in enumerate(c.repo_changesets):
13 13 <tr class="parity${cnt%2}">
14 14 <td>
15 <div><pre><a href="${h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id)}">r${cs.revision}:${h.short_id(cs.raw_id)}</a></pre></div>
15 <div>
16 <div class="changeset-status-container">
17 %if c.statuses.get(cs.raw_id):
18 <div class="changeset-status-ico">
19 %if c.statuses.get(cs.raw_id)[2]:
20 <a class="tooltip" title="${_('Click to open associated pull request')}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(cs.raw_id)[3],pull_request_id=c.statuses.get(cs.raw_id)[2])}">
21 <img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" />
22 </a>
23 %else:
24 <img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" />
25 %endif
26 </div>
27 %endif
28 </div>
29 <pre><a href="${h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id)}">r${cs.revision}:${h.short_id(cs.raw_id)}</a></pre>
30 </div>
16 31 </td>
17 32 <td>
18 33 ${h.link_to(h.truncate(cs.message,50) or _('No commit message'),
19 34 h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id),
20 35 title=cs.message)}
21 36 </td>
22 37 <td><span class="tooltip" title="${h.tooltip(h.fmt_date(cs.date))}">
23 38 ${h.age(cs.date)}</span>
24 39 </td>
25 40 <td title="${cs.author}">${h.person(cs.author)}</td>
26 41 <td>
27 42 <span class="logtags">
28 43 %if cs.branch:
29 44 <span class="branchtag">
30 45 ${cs.branch}
31 46 </span>
32 47 %endif
33 48 </span>
34 49 </td>
35 50 <td>
36 51 <span class="logtags">
37 52 %for tag in cs.tags:
38 53 <span class="tagtag">${tag}</span>
39 54 %endfor
40 55 </span>
41 56 </td>
42 57 </tr>
43 58 %endfor
44 59
45 60 </table>
46 61
47 62 <script type="text/javascript">
48 63 YUE.onDOMReady(function(){
49 64 YUE.delegate("shortlog_data","click",function(e, matchedEl, container){
50 65 ypjax(e.target.href,"shortlog_data",function(){tooltip_activate();});
51 66 YUE.preventDefault(e);
52 67 },'.pager_link');
53 68 });
54 69 </script>
55 70
56 71 <div class="pagination-wh pagination-left">
57 72 ${c.repo_changesets.pager('$link_previous ~2~ $link_next')}
58 73 </div>
59 74 %else:
60 75
61 76 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
62 77 <h4>${_('Add or upload files directly via RhodeCode')}</h4>
63 78 <div style="margin: 20px 30px;">
64 79 <div id="add_node_id" class="add_node">
65 80 <a class="ui-btn" href="${h.url('files_add_home',repo_name=c.repo_name,revision=0,f_path='')}">${_('add new file')}</a>
66 81 </div>
67 82 </div>
68 83 %endif
69 84
70 85
71 86 <h4>${_('Push new repo')}</h4>
72 87 <pre>
73 88 ${c.rhodecode_repo.alias} clone ${c.clone_repo_url}
74 89 ${c.rhodecode_repo.alias} add README # add first file
75 90 ${c.rhodecode_repo.alias} commit -m "Initial" # commit with message
76 91 ${c.rhodecode_repo.alias} push ${'origin master' if h.is_git(c.rhodecode_repo) else ''} # push changes back
77 92 </pre>
78 93
79 94 <h4>${_('Existing repository?')}</h4>
80 95 <pre>
81 96 %if h.is_git(c.rhodecode_repo):
82 97 git remote add origin ${c.clone_repo_url}
83 98 git push -u origin master
84 99 %else:
85 100 hg push ${c.clone_repo_url}
86 101 %endif
87 102 </pre>
88 103 %endif
General Comments 0
You need to be logged in to leave comments. Login now