##// END OF EJS Templates
added percentage of progress of gathered commit activity statistics
marcink -
r1181:36b12336 beta
parent child Browse files
Show More
@@ -1,171 +1,180
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) 2009-2011 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
14 14 # modify it under the terms of the GNU General Public License
15 15 # as published by the Free Software Foundation; version 2
16 16 # of the License or (at your opinion) any later version of the license.
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, write to the Free Software
25 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 26 # MA 02110-1301, USA.
27 27
28 28 import calendar
29 29 import logging
30 30 from time import mktime
31 31 from datetime import datetime, timedelta, date
32 32
33 33 from vcs.exceptions import ChangesetError
34 34
35 35 from pylons import tmpl_context as c, request, url
36 36 from pylons.i18n.translation import _
37 37
38 38 from rhodecode.model.db import Statistics, Repository
39 39 from rhodecode.model.repo import RepoModel
40 40
41 41 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
42 42 from rhodecode.lib.base import BaseRepoController, render
43 43 from rhodecode.lib.utils import OrderedDict, EmptyChangeset
44 44
45 45 from rhodecode.lib.celerylib import run_task
46 46 from rhodecode.lib.celerylib.tasks import get_commits_stats
47 47 from rhodecode.lib.helpers import RepoPage
48 48
49 49 try:
50 50 import json
51 51 except ImportError:
52 52 #python 2.5 compatibility
53 53 import simplejson as json
54 54 log = logging.getLogger(__name__)
55 55
56 56 class SummaryController(BaseRepoController):
57 57
58 58 @LoginRequired()
59 59 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
60 60 'repository.admin')
61 61 def __before__(self):
62 62 super(SummaryController, self).__before__()
63 63
64 64 def index(self, repo_name):
65 65
66 66 e = request.environ
67 67 c.dbrepo = dbrepo = Repository.by_repo_name(repo_name)
68 68
69 69 c.following = self.scm_model.is_following_repo(repo_name,
70 70 self.rhodecode_user.user_id)
71 71 def url_generator(**kw):
72 72 return url('shortlog_home', repo_name=repo_name, **kw)
73 73
74 74 c.repo_changesets = RepoPage(c.rhodecode_repo, page=1, items_per_page=10,
75 75 url=url_generator)
76 76
77 77
78 78
79 79 if self.rhodecode_user.username == 'default':
80 80 #for default(anonymous) user we don't need to pass credentials
81 81 username = ''
82 82 password = ''
83 83 else:
84 84 username = str(self.rhodecode_user.username)
85 85 password = '@'
86 86
87 87 uri = u'%(protocol)s://%(user)s%(password)s%(host)s%(prefix)s/%(repo_name)s' % {
88 88 'protocol': e.get('wsgi.url_scheme'),
89 89 'user':username,
90 90 'password':password,
91 91 'host':e.get('HTTP_HOST'),
92 92 'prefix':e.get('SCRIPT_NAME'),
93 93 'repo_name':repo_name, }
94 94 c.clone_repo_url = uri
95 95 c.repo_tags = OrderedDict()
96 96 for name, hash in c.rhodecode_repo.tags.items()[:10]:
97 97 try:
98 98 c.repo_tags[name] = c.rhodecode_repo.get_changeset(hash)
99 99 except ChangesetError:
100 100 c.repo_tags[name] = EmptyChangeset(hash)
101 101
102 102 c.repo_branches = OrderedDict()
103 103 for name, hash in c.rhodecode_repo.branches.items()[:10]:
104 104 try:
105 105 c.repo_branches[name] = c.rhodecode_repo.get_changeset(hash)
106 106 except ChangesetError:
107 107 c.repo_branches[name] = EmptyChangeset(hash)
108 108
109 109 td = date.today() + timedelta(days=1)
110 110 td_1m = td - timedelta(days=calendar.mdays[td.month])
111 111 td_1y = td - timedelta(days=365)
112 112
113 113 ts_min_m = mktime(td_1m.timetuple())
114 114 ts_min_y = mktime(td_1y.timetuple())
115 115 ts_max_y = mktime(td.timetuple())
116 116
117 117 if dbrepo.enable_statistics:
118 118 c.no_data_msg = _('No data loaded yet')
119 119 run_task(get_commits_stats, c.dbrepo.repo_name, ts_min_y, ts_max_y)
120 120 else:
121 121 c.no_data_msg = _('Statistics are disabled for this repository')
122 122 c.ts_min = ts_min_m
123 123 c.ts_max = ts_max_y
124 124
125 125 stats = self.sa.query(Statistics)\
126 126 .filter(Statistics.repository == dbrepo)\
127 127 .scalar()
128 128
129 c.stats_percentage = 0
129 130
130 131 if stats and stats.languages:
131 132 c.no_data = False is dbrepo.enable_statistics
132 133 lang_stats = json.loads(stats.languages)
133 134 c.commit_data = stats.commit_activity
134 135 c.overview_data = stats.commit_activity_combined
135 136 c.trending_languages = json.dumps(OrderedDict(
136 137 sorted(lang_stats.items(), reverse=True,
137 138 key=lambda k: k[1])[:10]
138 139 )
139 140 )
141 last_rev = stats.stat_on_revision
142 c.repo_last_rev = c.rhodecode_repo.count() - 1 \
143 if c.rhodecode_repo.revisions else 0
144 if last_rev == 0 or c.repo_last_rev == 0:
145 pass
146 else:
147 c.stats_percentage = '%.2f' % ((float((last_rev)) /
148 c.repo_last_rev) * 100)
140 149 else:
141 150 c.commit_data = json.dumps({})
142 151 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10] ])
143 152 c.trending_languages = json.dumps({})
144 153 c.no_data = True
145 154
146 155 c.enable_downloads = dbrepo.enable_downloads
147 156 if c.enable_downloads:
148 157 c.download_options = self._get_download_links(c.rhodecode_repo)
149 158
150 159 return render('summary/summary.html')
151 160
152 161
153 162
154 163 def _get_download_links(self, repo):
155 164
156 165 download_l = []
157 166
158 167 branches_group = ([], _("Branches"))
159 168 tags_group = ([], _("Tags"))
160 169
161 170 for name, chs in c.rhodecode_repo.branches.items():
162 171 #chs = chs.split(':')[-1]
163 172 branches_group[0].append((chs, name),)
164 173 download_l.append(branches_group)
165 174
166 175 for name, chs in c.rhodecode_repo.tags.items():
167 176 #chs = chs.split(':')[-1]
168 177 tags_group[0].append((chs, name),)
169 178 download_l.append(tags_group)
170 179
171 180 return download_l
@@ -1,699 +1,703
1 1 <%inherit file="/base/base.html"/>
2 2
3 3 <%def name="title()">
4 4 ${c.repo_name} ${_('Summary')} - ${c.rhodecode_name}
5 5 </%def>
6 6
7 7 <%def name="breadcrumbs_links()">
8 8 ${h.link_to(u'Home',h.url('/'))}
9 9 &raquo;
10 10 ${h.link_to(c.dbrepo.just_name,h.url('summary_home',repo_name=c.repo_name))}
11 11 &raquo;
12 12 ${_('summary')}
13 13 </%def>
14 14
15 15 <%def name="page_nav()">
16 16 ${self.menu('summary')}
17 17 </%def>
18 18
19 19 <%def name="main()">
20 20 <div class="box box-left">
21 21 <!-- box / title -->
22 22 <div class="title">
23 23 ${self.breadcrumbs()}
24 24 </div>
25 25 <!-- end box / title -->
26 26 <div class="form">
27 27 <div class="fields">
28 28
29 29 <div class="field">
30 30 <div class="label">
31 31 <label>${_('Name')}:</label>
32 32 </div>
33 33 <div class="input-short">
34 34 %if c.rhodecode_user.username != 'default':
35 35 %if c.following:
36 36 <span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
37 37 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
38 38 </span>
39 39 %else:
40 40 <span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
41 41 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
42 42 </span>
43 43 %endif
44 44 %endif:
45 45
46 46 ##REPO TYPE
47 47 %if c.dbrepo.repo_type =='hg':
48 48 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url("/images/icons/hgicon.png")}"/>
49 49 %endif
50 50 %if c.dbrepo.repo_type =='git':
51 51 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url("/images/icons/giticon.png")}"/>
52 52 %endif
53 53
54 54 ##PUBLIC/PRIVATE
55 55 %if c.dbrepo.private:
56 56 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url("/images/icons/lock.png")}"/>
57 57 %else:
58 58 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url("/images/icons/lock_open.png")}"/>
59 59 %endif
60 60
61 61 ##REPO NAME
62 62 <span style="font-size: 1.6em;font-weight: bold;vertical-align: baseline;clear:right">${h.repo_link(c.dbrepo.groups_and_repo)}</span>
63 63
64 64 ##FORK
65 65 %if c.dbrepo.fork:
66 66 <div style="margin-top:5px;clear:both"">
67 67 <a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}">
68 68 <img class="icon" alt="${_('public')}"
69 69 title="${_('Fork of')} ${c.dbrepo.fork.repo_name}"
70 70 src="${h.url("/images/icons/arrow_divide.png")}"/>
71 71 ${_('Fork of')} ${c.dbrepo.fork.repo_name}
72 72 </a>
73 73 </div>
74 74 %endif
75 75 ##REMOTE
76 76 %if c.dbrepo.clone_uri:
77 77 <div style="margin-top:5px;clear:both">
78 78 <a href="${h.url(str(c.dbrepo.clone_uri))}">
79 79 <img class="icon" alt="${_('remote clone')}"
80 80 title="${_('Clone from')} ${c.dbrepo.clone_uri}"
81 81 src="${h.url("/images/icons/connect.png")}"/>
82 82 ${_('Clone from')} ${c.dbrepo.clone_uri}
83 83 </a>
84 84 </div>
85 85 %endif
86 86 </div>
87 87 </div>
88 88
89 89
90 90 <div class="field">
91 91 <div class="label">
92 92 <label>${_('Description')}:</label>
93 93 </div>
94 94 <div class="input-short">
95 95 ${c.dbrepo.description}
96 96 </div>
97 97 </div>
98 98
99 99
100 100 <div class="field">
101 101 <div class="label">
102 102 <label>${_('Contact')}:</label>
103 103 </div>
104 104 <div class="input-short">
105 105 <div class="gravatar">
106 106 <img alt="gravatar" src="${h.gravatar_url(c.dbrepo.user.email)}"/>
107 107 </div>
108 108 ${_('Username')}: ${c.dbrepo.user.username}<br/>
109 109 ${_('Name')}: ${c.dbrepo.user.name} ${c.dbrepo.user.lastname}<br/>
110 110 ${_('Email')}: <a href="mailto:${c.dbrepo.user.email}">${c.dbrepo.user.email}</a>
111 111 </div>
112 112 </div>
113 113
114 114 <div class="field">
115 115 <div class="label">
116 116 <label>${_('Last change')}:</label>
117 117 </div>
118 118 <div class="input-short">
119 119 <b>${'r%s:%s' % (h.get_changeset_safe(c.rhodecode_repo,'tip').revision,
120 120 h.get_changeset_safe(c.rhodecode_repo,'tip').short_id)}</b> -
121 121 <span class="tooltip" title="${c.rhodecode_repo.last_change}">
122 122 ${h.age(c.rhodecode_repo.last_change)}</span><br/>
123 123 ${_('by')} ${h.get_changeset_safe(c.rhodecode_repo,'tip').author}
124 124
125 125 </div>
126 126 </div>
127 127
128 128 <div class="field">
129 129 <div class="label">
130 130 <label>${_('Clone url')}:</label>
131 131 </div>
132 132 <div class="input-short">
133 133 <input type="text" id="clone_url" readonly="readonly" value="hg clone ${c.clone_repo_url}" size="70"/>
134 134 </div>
135 135 </div>
136 136
137 137 <div class="field">
138 138 <div class="label">
139 139 <label>${_('Trending source files')}:</label>
140 140 </div>
141 141 <div class="input-short">
142 142 <div id="lang_stats"></div>
143 143 </div>
144 144 </div>
145 145
146 146 <div class="field">
147 147 <div class="label">
148 148 <label>${_('Download')}:</label>
149 149 </div>
150 150 <div class="input-short">
151 151 %if len(c.rhodecode_repo.revisions) == 0:
152 152 ${_('There are no downloads yet')}
153 153 %elif c.enable_downloads is False:
154 154 ${_('Downloads are disabled for this repository')}
155 155 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
156 156 [${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name))}]
157 157 %endif
158 158 %else:
159 159 ${h.select('download_options',c.rhodecode_repo.get_changeset().raw_id,c.download_options)}
160 160 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
161 161 %if cnt >=1:
162 162 |
163 163 %endif
164 164 <span class="tooltip" title="${_('Download %s as %s') %('tip',archive['type'])}"
165 165 id="${archive['type']+'_link'}">${h.link_to(archive['type'],
166 166 h.url('files_archive_home',repo_name=c.dbrepo.repo_name,
167 167 fname='tip'+archive['extension']),class_="archive_icon")}</span>
168 168 %endfor
169 169 %endif
170 170 </div>
171 171 </div>
172 172
173 173 <div class="field">
174 174 <div class="label">
175 175 <label>${_('Feeds')}:</label>
176 176 </div>
177 177 <div class="input-short">
178 178 %if c.rhodecode_user.username != 'default':
179 179 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='rss_icon')}
180 180 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='atom_icon')}
181 181 %else:
182 182 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name),class_='rss_icon')}
183 183 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name),class_='atom_icon')}
184 184 %endif
185 185 </div>
186 186 </div>
187 187 </div>
188 188 </div>
189 189 <script type="text/javascript">
190 190 YUE.onDOMReady(function(e){
191 191 id = 'clone_url';
192 192 YUE.on(id,'click',function(e){
193 193 YUD.get('clone_url').select();
194 194 })
195 195 })
196 196 var data = ${c.trending_languages|n};
197 197 var total = 0;
198 198 var no_data = true;
199 199 for (k in data){
200 200 total += data[k];
201 201 no_data = false;
202 202 }
203 203 var tbl = document.createElement('table');
204 204 tbl.setAttribute('class','trending_language_tbl');
205 205 var cnt =0;
206 206 for (k in data){
207 207 cnt+=1;
208 208 var hide = cnt>2;
209 209 var tr = document.createElement('tr');
210 210 if (hide){
211 211 tr.setAttribute('style','display:none');
212 212 tr.setAttribute('class','stats_hidden');
213 213 }
214 214 var percentage = Math.round((data[k]/total*100),2);
215 215 var value = data[k];
216 216 var td1 = document.createElement('td');
217 217 td1.width=150;
218 218 var trending_language_label = document.createElement('div');
219 219 trending_language_label.innerHTML = k;
220 220 td1.appendChild(trending_language_label);
221 221
222 222 var td2 = document.createElement('td');
223 223 td2.setAttribute('style','padding-right:14px !important');
224 224 var trending_language = document.createElement('div');
225 225 var nr_files = value+" ${_('files')}";
226 226
227 227 trending_language.title = k+" "+nr_files;
228 228
229 229 if (percentage>20){
230 230 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
231 231 }
232 232 else{
233 233 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
234 234 }
235 235
236 236 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
237 237 trending_language.style.width=percentage+"%";
238 238 td2.appendChild(trending_language);
239 239
240 240 tr.appendChild(td1);
241 241 tr.appendChild(td2);
242 242 tbl.appendChild(tr);
243 243 if(cnt == 2){
244 244 var show_more = document.createElement('tr');
245 245 var td=document.createElement('td');
246 246 lnk = document.createElement('a');
247 247 lnk.href='#';
248 248 lnk.innerHTML = "${_("show more")}";
249 249 lnk.id='code_stats_show_more';
250 250 td.appendChild(lnk);
251 251 show_more.appendChild(td);
252 252 show_more.appendChild(document.createElement('td'));
253 253 tbl.appendChild(show_more);
254 254 }
255 255
256 256 }
257 257 if(no_data){
258 258 var tr = document.createElement('tr');
259 259 var td1 = document.createElement('td');
260 260 td1.innerHTML = "${c.no_data_msg}";
261 261 tr.appendChild(td1);
262 262 tbl.appendChild(tr);
263 263 }
264 264 YUD.get('lang_stats').appendChild(tbl);
265 265 YUE.on('code_stats_show_more','click',function(){
266 266 l = YUD.getElementsByClassName('stats_hidden')
267 267 for (e in l){
268 268 YUD.setStyle(l[e],'display','');
269 269 };
270 270 YUD.setStyle(YUD.get('code_stats_show_more'),
271 271 'display','none');
272 272 })
273 273
274 274
275 275 YUE.on('download_options','change',function(e){
276 276 var new_cs = e.target.options[e.target.selectedIndex];
277 277 var tmpl_links = {}
278 278 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
279 279 tmpl_links['${archive['type']}'] = '${h.link_to(archive['type'],
280 280 h.url('files_archive_home',repo_name=c.dbrepo.repo_name,
281 281 fname='__CS__'+archive['extension']),class_="archive_icon")}';
282 282 %endfor
283 283
284 284
285 285 for(k in tmpl_links){
286 286 var s = YUD.get(k+'_link')
287 287 title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__',archive['type'])}";
288 288 s.title = title_tmpl.replace('__CS_NAME__',new_cs.text)
289 289 s.innerHTML = tmpl_links[k].replace('__CS__',new_cs.value);
290 290 }
291 291
292 292 })
293 293
294 294 </script>
295 295 </div>
296 296
297 297 <div class="box box-right" style="min-height:455px">
298 298 <!-- box / title -->
299 299 <div class="title">
300 300 <h5>${_('Commit activity by day / author')}</h5>
301 301 </div>
302 302
303 303 <div class="table">
304 <div style="padding:0 10px 10px 15px;font-size: 1.2em;">
304 305 %if c.no_data:
305 <div style="padding:0 10px 10px 15px;font-size: 1.2em;">${c.no_data_msg}
306 ${c.no_data_msg}
306 307 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
307 308 [${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name))}]
308 309 %endif
309 </div>
310 %endif:
310
311 %else:
312 ${_('Loaded in')} ${c.stats_percentage} %
313 %endif
314 </div>
311 315 <div id="commit_history" style="width:460px;height:300px;float:left"></div>
312 316 <div style="clear: both;height: 10px"></div>
313 317 <div id="overview" style="width:460px;height:100px;float:left"></div>
314 318
315 319 <div id="legend_data" style="clear:both;margin-top:10px;">
316 320 <div id="legend_container"></div>
317 321 <div id="legend_choices">
318 322 <table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
319 323 </div>
320 324 </div>
321 325 <script type="text/javascript">
322 326 /**
323 327 * Plots summary graph
324 328 *
325 329 * @class SummaryPlot
326 330 * @param {from} initial from for detailed graph
327 331 * @param {to} initial to for detailed graph
328 332 * @param {dataset}
329 333 * @param {overview_dataset}
330 334 */
331 335 function SummaryPlot(from,to,dataset,overview_dataset) {
332 336 var initial_ranges = {
333 337 "xaxis":{
334 338 "from":from,
335 339 "to":to,
336 340 },
337 341 };
338 342 var dataset = dataset;
339 343 var overview_dataset = [overview_dataset];
340 344 var choiceContainer = YUD.get("legend_choices");
341 345 var choiceContainerTable = YUD.get("legend_choices_tables");
342 346 var plotContainer = YUD.get('commit_history');
343 347 var overviewContainer = YUD.get('overview');
344 348
345 349 var plot_options = {
346 350 bars: {show:true,align:'center',lineWidth:4},
347 351 legend: {show:true, container:"legend_container"},
348 352 points: {show:true,radius:0,fill:false},
349 353 yaxis: {tickDecimals:0,},
350 354 xaxis: {
351 355 mode: "time",
352 356 timeformat: "%d/%m",
353 357 min:from,
354 358 max:to,
355 359 },
356 360 grid: {
357 361 hoverable: true,
358 362 clickable: true,
359 363 autoHighlight:true,
360 364 color: "#999"
361 365 },
362 366 //selection: {mode: "x"}
363 367 };
364 368 var overview_options = {
365 369 legend:{show:false},
366 370 bars: {show:true,barWidth: 2,},
367 371 shadowSize: 0,
368 372 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
369 373 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
370 374 grid: {color: "#999",},
371 375 selection: {mode: "x"}
372 376 };
373 377
374 378 /**
375 379 *get dummy data needed in few places
376 380 */
377 381 function getDummyData(label){
378 382 return {"label":label,
379 383 "data":[{"time":0,
380 384 "commits":0,
381 385 "added":0,
382 386 "changed":0,
383 387 "removed":0,
384 388 }],
385 389 "schema":["commits"],
386 390 "color":'#ffffff',
387 391 }
388 392 }
389 393
390 394 /**
391 395 * generate checkboxes accordindly to data
392 396 * @param keys
393 397 * @returns
394 398 */
395 399 function generateCheckboxes(data) {
396 400 //append checkboxes
397 401 var i = 0;
398 402 choiceContainerTable.innerHTML = '';
399 403 for(var pos in data) {
400 404
401 405 data[pos].color = i;
402 406 i++;
403 407 if(data[pos].label != ''){
404 408 choiceContainerTable.innerHTML += '<tr><td>'+
405 409 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
406 410 +data[pos].label+
407 411 '</td></tr>';
408 412 }
409 413 }
410 414 }
411 415
412 416 /**
413 417 * ToolTip show
414 418 */
415 419 function showTooltip(x, y, contents) {
416 420 var div=document.getElementById('tooltip');
417 421 if(!div) {
418 422 div = document.createElement('div');
419 423 div.id="tooltip";
420 424 div.style.position="absolute";
421 425 div.style.border='1px solid #fdd';
422 426 div.style.padding='2px';
423 427 div.style.backgroundColor='#fee';
424 428 document.body.appendChild(div);
425 429 }
426 430 YUD.setStyle(div, 'opacity', 0);
427 431 div.innerHTML = contents;
428 432 div.style.top=(y + 5) + "px";
429 433 div.style.left=(x + 5) + "px";
430 434
431 435 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
432 436 anim.animate();
433 437 }
434 438
435 439 /**
436 440 * This function will detect if selected period has some changesets
437 441 for this user if it does this data is then pushed for displaying
438 442 Additionally it will only display users that are selected by the checkbox
439 443 */
440 444 function getDataAccordingToRanges(ranges) {
441 445
442 446 var data = [];
443 447 var keys = [];
444 448 for(var key in dataset){
445 449 var push = false;
446 450
447 451 //method1 slow !!
448 452 //*
449 453 for(var ds in dataset[key].data){
450 454 commit_data = dataset[key].data[ds];
451 455 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
452 456 push = true;
453 457 break;
454 458 }
455 459 }
456 460 //*/
457 461
458 462 /*//method2 sorted commit data !!!
459 463
460 464 var first_commit = dataset[key].data[0].time;
461 465 var last_commit = dataset[key].data[dataset[key].data.length-1].time;
462 466
463 467 if (first_commit >= ranges.xaxis.from && last_commit <= ranges.xaxis.to){
464 468 push = true;
465 469 }
466 470 //*/
467 471
468 472 if(push){
469 473 data.push(dataset[key]);
470 474 }
471 475 }
472 476 if(data.length >= 1){
473 477 return data;
474 478 }
475 479 else{
476 480 //just return dummy data for graph to plot itself
477 481 return [getDummyData('')];
478 482 }
479 483
480 484 }
481 485
482 486 /**
483 487 * redraw using new checkbox data
484 488 */
485 489 function plotchoiced(e,args){
486 490 var cur_data = args[0];
487 491 var cur_ranges = args[1];
488 492
489 493 var new_data = [];
490 494 var inputs = choiceContainer.getElementsByTagName("input");
491 495
492 496 //show only checked labels
493 497 for(var i=0; i<inputs.length; i++) {
494 498 var checkbox_key = inputs[i].name;
495 499
496 500 if(inputs[i].checked){
497 501 for(var d in cur_data){
498 502 if(cur_data[d].label == checkbox_key){
499 503 new_data.push(cur_data[d]);
500 504 }
501 505 }
502 506 }
503 507 else{
504 508 //push dummy data to not hide the label
505 509 new_data.push(getDummyData(checkbox_key));
506 510 }
507 511 }
508 512
509 513 var new_options = YAHOO.lang.merge(plot_options, {
510 514 xaxis: {
511 515 min: cur_ranges.xaxis.from,
512 516 max: cur_ranges.xaxis.to,
513 517 mode:"time",
514 518 timeformat: "%d/%m",
515 519 },
516 520 });
517 521 if (!new_data){
518 522 new_data = [[0,1]];
519 523 }
520 524 // do the zooming
521 525 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
522 526
523 527 plot.subscribe("plotselected", plotselected);
524 528
525 529 //resubscribe plothover
526 530 plot.subscribe("plothover", plothover);
527 531
528 532 // don't fire event on the overview to prevent eternal loop
529 533 overview.setSelection(cur_ranges, true);
530 534
531 535 }
532 536
533 537 /**
534 538 * plot only selected items from overview
535 539 * @param ranges
536 540 * @returns
537 541 */
538 542 function plotselected(ranges,cur_data) {
539 543 //updates the data for new plot
540 544 data = getDataAccordingToRanges(ranges);
541 545 generateCheckboxes(data);
542 546
543 547 var new_options = YAHOO.lang.merge(plot_options, {
544 548 xaxis: {
545 549 min: ranges.xaxis.from,
546 550 max: ranges.xaxis.to,
547 551 mode:"time",
548 552 timeformat: "%d/%m",
549 553 },
550 554 yaxis: {
551 555 min: ranges.yaxis.from,
552 556 max: ranges.yaxis.to,
553 557 },
554 558
555 559 });
556 560 // do the zooming
557 561 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
558 562
559 563 plot.subscribe("plotselected", plotselected);
560 564
561 565 //resubscribe plothover
562 566 plot.subscribe("plothover", plothover);
563 567
564 568 // don't fire event on the overview to prevent eternal loop
565 569 overview.setSelection(ranges, true);
566 570
567 571 //resubscribe choiced
568 572 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
569 573 }
570 574
571 575 var previousPoint = null;
572 576
573 577 function plothover(o) {
574 578 var pos = o.pos;
575 579 var item = o.item;
576 580
577 581 //YUD.get("x").innerHTML = pos.x.toFixed(2);
578 582 //YUD.get("y").innerHTML = pos.y.toFixed(2);
579 583 if (item) {
580 584 if (previousPoint != item.datapoint) {
581 585 previousPoint = item.datapoint;
582 586
583 587 var tooltip = YUD.get("tooltip");
584 588 if(tooltip) {
585 589 tooltip.parentNode.removeChild(tooltip);
586 590 }
587 591 var x = item.datapoint.x.toFixed(2);
588 592 var y = item.datapoint.y.toFixed(2);
589 593
590 594 if (!item.series.label){
591 595 item.series.label = 'commits';
592 596 }
593 597 var d = new Date(x*1000);
594 598 var fd = d.toDateString()
595 599 var nr_commits = parseInt(y);
596 600
597 601 var cur_data = dataset[item.series.label].data[item.dataIndex];
598 602 var added = cur_data.added;
599 603 var changed = cur_data.changed;
600 604 var removed = cur_data.removed;
601 605
602 606 var nr_commits_suffix = " ${_('commits')} ";
603 607 var added_suffix = " ${_('files added')} ";
604 608 var changed_suffix = " ${_('files changed')} ";
605 609 var removed_suffix = " ${_('files removed')} ";
606 610
607 611
608 612 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
609 613 if(added==1){added_suffix=" ${_('file added')} ";}
610 614 if(changed==1){changed_suffix=" ${_('file changed')} ";}
611 615 if(removed==1){removed_suffix=" ${_('file removed')} ";}
612 616
613 617 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
614 618 +'<br/>'+
615 619 nr_commits + nr_commits_suffix+'<br/>'+
616 620 added + added_suffix +'<br/>'+
617 621 changed + changed_suffix + '<br/>'+
618 622 removed + removed_suffix + '<br/>');
619 623 }
620 624 }
621 625 else {
622 626 var tooltip = YUD.get("tooltip");
623 627
624 628 if(tooltip) {
625 629 tooltip.parentNode.removeChild(tooltip);
626 630 }
627 631 previousPoint = null;
628 632 }
629 633 }
630 634
631 635 /**
632 636 * MAIN EXECUTION
633 637 */
634 638
635 639 var data = getDataAccordingToRanges(initial_ranges);
636 640 generateCheckboxes(data);
637 641
638 642 //main plot
639 643 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
640 644
641 645 //overview
642 646 var overview = YAHOO.widget.Flot(overviewContainer, overview_dataset, overview_options);
643 647
644 648 //show initial selection on overview
645 649 overview.setSelection(initial_ranges);
646 650
647 651 plot.subscribe("plotselected", plotselected);
648 652
649 653 overview.subscribe("plotselected", function (ranges) {
650 654 plot.setSelection(ranges);
651 655 });
652 656
653 657 plot.subscribe("plothover", plothover);
654 658
655 659 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
656 660 }
657 661 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
658 662 </script>
659 663
660 664 </div>
661 665 </div>
662 666
663 667 <div class="box">
664 668 <div class="title">
665 669 <div class="breadcrumbs">${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}</div>
666 670 </div>
667 671 <div class="table">
668 672 <div id="shortlog_data">
669 673 <%include file='../shortlog/shortlog_data.html'/>
670 674 </div>
671 675 ##%if c.repo_changesets:
672 676 ## ${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
673 677 ##%endif
674 678 </div>
675 679 </div>
676 680 <div class="box">
677 681 <div class="title">
678 682 <div class="breadcrumbs">${h.link_to(_('Tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
679 683 </div>
680 684 <div class="table">
681 685 <%include file='../tags/tags_data.html'/>
682 686 %if c.repo_changesets:
683 687 ${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
684 688 %endif
685 689 </div>
686 690 </div>
687 691 <div class="box">
688 692 <div class="title">
689 693 <div class="breadcrumbs">${h.link_to(_('Branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
690 694 </div>
691 695 <div class="table">
692 696 <%include file='../branches/branches_data.html'/>
693 697 %if c.repo_changesets:
694 698 ${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
695 699 %endif
696 700 </div>
697 701 </div>
698 702
699 703 </%def>
General Comments 0
You need to be logged in to leave comments. Login now