##// END OF EJS Templates
fixed sorting for repo switcher, fixed length for code stats
marcink -
r693:90ac3255 beta
parent child Browse files
Show More
@@ -1,239 +1,240 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # Model for RhodeCode
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on April 9, 2010
22 22 Model for RhodeCode
23 23 @author: marcink
24 24 """
25 25 from beaker.cache import cache_region, region_invalidate
26 26 from mercurial import ui
27 27 from rhodecode.lib import helpers as h
28 28 from rhodecode.lib.auth import HasRepoPermissionAny
29 29 from rhodecode.lib.utils import get_repos
30 30 from rhodecode.model import meta
31 31 from rhodecode.model.db import Repository, User, RhodeCodeUi, CacheInvalidation
32 32 from rhodecode.model.caching_query import FromCache
33 33 from sqlalchemy.orm import joinedload
34 34 from sqlalchemy.orm.session import make_transient
35 35 from vcs import get_backend
36 36 from vcs.utils.helpers import get_scm
37 37 from vcs.exceptions import RepositoryError, VCSError
38 38 from vcs.utils.lazy import LazyProperty
39 39 import traceback
40 40 import logging
41 41 import os
42 42 import time
43 43
44 44 log = logging.getLogger(__name__)
45 45
46 46 class ScmModel(object):
47 47 """
48 48 Mercurial Model
49 49 """
50 50
51 51 def __init__(self):
52 52 self.sa = meta.Session()
53 53
54 54 @LazyProperty
55 55 def repos_path(self):
56 56 """
57 57 Get's the repositories root path from database
58 58 """
59 59 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
60 60
61 61 return q.ui_value
62 62
63 63 def repo_scan(self, repos_path, baseui, initial=False):
64 64 """
65 65 Listing of repositories in given path. This path should not be a
66 66 repository itself. Return a dictionary of repository objects
67 67
68 68 :param repos_path: path to directory containing repositories
69 69 :param baseui
70 70 :param initial: initial scan
71 71 """
72 72 log.info('scanning for repositories in %s', repos_path)
73 73
74 74 if not isinstance(baseui, ui.ui):
75 75 baseui = ui.ui()
76 76 repos_list = {}
77 77
78 78 for name, path in get_repos(repos_path):
79 79 try:
80 80 if repos_list.has_key(name):
81 81 raise RepositoryError('Duplicate repository name %s '
82 82 'found in %s' % (name, path))
83 83 else:
84 84
85 85 klass = get_backend(path[0])
86 86
87 87 if path[0] == 'hg':
88 88 repos_list[name] = klass(path[1], baseui=baseui)
89 89
90 90 if path[0] == 'git':
91 91 repos_list[name] = klass(path[1])
92 92 except OSError:
93 93 continue
94 94
95 95 return repos_list
96 96
97 97 def get_repos(self, all_repos=None):
98 98 """
99 99 Get all repos from db and for each repo create it's backend instance.
100 100 and fill that backed with information from database
101 101
102 102 :param all_repos: give specific repositories list, good for filtering
103 103 """
104 104 if not all_repos:
105 all_repos = self.sa.query(Repository).all()
105 all_repos = self.sa.query(Repository)\
106 .order_by(Repository.repo_name).all()
106 107
107 108 for r in all_repos:
108 109
109 110 repo = self.get(r.repo_name)
110 111
111 112 if repo is not None:
112 113 last_change = repo.last_change
113 114 tip = h.get_changeset_safe(repo, 'tip')
114 115
115 116 tmp_d = {}
116 117 tmp_d['name'] = repo.name
117 118 tmp_d['name_sort'] = tmp_d['name'].lower()
118 119 tmp_d['description'] = repo.dbrepo.description
119 120 tmp_d['description_sort'] = tmp_d['description']
120 121 tmp_d['last_change'] = last_change
121 122 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
122 123 tmp_d['tip'] = tip.raw_id
123 124 tmp_d['tip_sort'] = tip.revision
124 125 tmp_d['rev'] = tip.revision
125 126 tmp_d['contact'] = repo.dbrepo.user.full_contact
126 127 tmp_d['contact_sort'] = tmp_d['contact']
127 128 tmp_d['repo_archives'] = list(repo._get_archives())
128 129 tmp_d['last_msg'] = tip.message
129 130 tmp_d['repo'] = repo
130 131 yield tmp_d
131 132
132 133 def get_repo(self, repo_name):
133 134 return self.get(repo_name)
134 135
135 136 def get(self, repo_name):
136 137 """
137 138 Get's repository from given name, creates BackendInstance and
138 139 propagates it's data from database with all additional information
139 140 :param repo_name:
140 141 """
141 142 if not HasRepoPermissionAny('repository.read', 'repository.write',
142 143 'repository.admin')(repo_name, 'get repo check'):
143 144 return
144 145
145 146 @cache_region('long_term')
146 147 def _get_repo(repo_name):
147 148
148 149 repo_path = os.path.join(self.repos_path, repo_name)
149 150 alias = get_scm(repo_path)[0]
150 151
151 152 log.debug('Creating instance of %s repository', alias)
152 153 backend = get_backend(alias)
153 154
154 155 if alias == 'hg':
155 156 repo = backend(repo_path, create=False, baseui=None)
156 157 #skip hidden web repository
157 158 if repo._get_hidden():
158 159 return
159 160 else:
160 161 repo = backend(repo_path, create=False)
161 162
162 163 dbrepo = self.sa.query(Repository)\
163 164 .options(joinedload(Repository.fork))\
164 165 .options(joinedload(Repository.user))\
165 166 .filter(Repository.repo_name == repo_name)\
166 167 .scalar()
167 168 make_transient(dbrepo)
168 169 repo.dbrepo = dbrepo
169 170 return repo
170 171
171 172 invalidate = self._should_invalidate(repo_name)
172 173 if invalidate:
173 174 log.info('invalidating cache for repository %s', repo_name)
174 175 region_invalidate(_get_repo, None, repo_name)
175 176 self._mark_invalidated(invalidate)
176 177
177 178 return _get_repo(repo_name)
178 179
179 180
180 181
181 182 def mark_for_invalidation(self, repo_name):
182 183 """
183 184 Puts cache invalidation task into db for
184 185 further global cache invalidation
185 186
186 187 :param repo_name: this repo that should invalidation take place
187 188 """
188 189 log.debug('marking %s for invalidation', repo_name)
189 190 cache = self.sa.query(CacheInvalidation)\
190 191 .filter(CacheInvalidation.cache_key == repo_name).scalar()
191 192
192 193 if cache:
193 194 #mark this cache as inactive
194 195 cache.cache_active = False
195 196 else:
196 197 log.debug('cache key not found in invalidation db -> creating one')
197 198 cache = CacheInvalidation(repo_name)
198 199
199 200 try:
200 201 self.sa.add(cache)
201 202 self.sa.commit()
202 203 except:
203 204 log.error(traceback.format_exc())
204 205 self.sa.rollback()
205 206
206 207
207 208
208 209
209 210
210 211 def _should_invalidate(self, repo_name):
211 212 """
212 213 Looks up database for invalidation signals for this repo_name
213 214 :param repo_name:
214 215 """
215 216
216 217 ret = self.sa.query(CacheInvalidation)\
217 218 .options(FromCache('sql_cache_short',
218 219 'get_invalidation_%s' % repo_name))\
219 220 .filter(CacheInvalidation.cache_key == repo_name)\
220 221 .filter(CacheInvalidation.cache_active == False)\
221 222 .scalar()
222 223
223 224 return ret
224 225
225 226 def _mark_invalidated(self, cache_key):
226 227 """
227 228 Marks all occurences of cache to invaldation as already invalidated
228 229 @param repo_name:
229 230 """
230 231 if cache_key:
231 232 log.debug('marking %s as already invalidated', cache_key)
232 233 try:
233 234 cache_key.cache_active = True
234 235 self.sa.add(cache_key)
235 236 self.sa.commit()
236 237 except:
237 238 log.error(traceback.format_exc())
238 239 self.sa.rollback()
239 240
@@ -1,593 +1,594 b''
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.repo_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 <script type="text/javascript">
21 21 var E = YAHOO.util.Event;
22 22 var D = YAHOO.util.Dom;
23 23
24 24 E.onDOMReady(function(e){
25 25 id = 'clone_url';
26 26 E.addListener(id,'click',function(e){
27 27 D.get('clone_url').select();
28 28 })
29 29 })
30 30 </script>
31 31 <div class="box box-left">
32 32 <!-- box / title -->
33 33 <div class="title">
34 34 ${self.breadcrumbs()}
35 35 </div>
36 36 <!-- end box / title -->
37 37 <div class="form">
38 38 <div class="fields">
39 39
40 40 <div class="field">
41 41 <div class="label">
42 42 <label>${_('Name')}:</label>
43 43 </div>
44 44 <div class="input-short">
45 45 %if c.repo_info.dbrepo.repo_type =='hg':
46 46 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="/images/icons/hgicon.png"/>
47 47 %endif
48 48 %if c.repo_info.dbrepo.repo_type =='git':
49 49 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="/images/icons/giticon.png"/>
50 50 %endif
51 51
52 52 %if c.repo_info.dbrepo.private:
53 53 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="/images/icons/lock.png"/>
54 54 %else:
55 55 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="/images/icons/lock_open.png"/>
56 56 %endif
57 57 <span style="font-size: 1.6em;font-weight: bold;vertical-align: baseline;">${c.repo_info.name}</span>
58 58 <br/>
59 59 %if c.repo_info.dbrepo.fork:
60 60 <span style="margin-top:5px">
61 61 <a href="${h.url('summary_home',repo_name=c.repo_info.dbrepo.fork.repo_name)}">
62 62 <img class="icon" alt="${_('public')}"
63 63 title="${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}"
64 64 src="/images/icons/arrow_divide.png"/>
65 65 ${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}
66 66 </a>
67 67 </span>
68 68 %endif
69 69 </div>
70 70 </div>
71 71
72 72
73 73 <div class="field">
74 74 <div class="label">
75 75 <label>${_('Description')}:</label>
76 76 </div>
77 77 <div class="input-short">
78 78 ${c.repo_info.dbrepo.description}
79 79 </div>
80 80 </div>
81 81
82 82
83 83 <div class="field">
84 84 <div class="label">
85 85 <label>${_('Contact')}:</label>
86 86 </div>
87 87 <div class="input-short">
88 88 <div class="gravatar">
89 89 <img alt="gravatar" src="${h.gravatar_url(c.repo_info.dbrepo.user.email)}"/>
90 90 </div>
91 91 ${_('Username')}: ${c.repo_info.dbrepo.user.username}<br/>
92 92 ${_('Name')}: ${c.repo_info.dbrepo.user.name} ${c.repo_info.dbrepo.user.lastname}<br/>
93 93 ${_('Email')}: <a href="mailto:${c.repo_info.dbrepo.user.email}">${c.repo_info.dbrepo.user.email}</a>
94 94 </div>
95 95 </div>
96 96
97 97 <div class="field">
98 98 <div class="label">
99 99 <label>${_('Last change')}:</label>
100 100 </div>
101 101 <div class="input-short">
102 102 ${h.age(c.repo_info.last_change)} - ${c.repo_info.last_change}
103 103 ${_('by')} ${h.get_changeset_safe(c.repo_info,'tip').author}
104 104
105 105 </div>
106 106 </div>
107 107
108 108 <div class="field">
109 109 <div class="label">
110 110 <label>${_('Clone url')}:</label>
111 111 </div>
112 112 <div class="input-short">
113 113 <input type="text" id="clone_url" readonly="readonly" value="hg clone ${c.clone_repo_url}" size="70"/>
114 114 </div>
115 115 </div>
116 116
117 117 <div class="field">
118 118 <div class="label">
119 119 <label>${_('Trending languages')}:</label>
120 120 </div>
121 121 <div class="input-short">
122 122 <div id="lang_stats">
123 123
124 124 </div>
125 125 <script type="text/javascript">
126 126 var data = ${c.trending_languages|n};
127 127 var total = 0;
128 128 var no_data = true;
129 129 for (k in data){
130 130 total += data[k];
131 131 no_data = false;
132 132 }
133 133 var tbl = document.createElement('table');
134 134 tbl.setAttribute('class','trending_language_tbl');
135 135 for (k in data){
136 136 var tr = document.createElement('tr');
137 137 var percentage = Math.round((data[k]/total*100),2);
138 138 var value = data[k];
139 139 var td1 = document.createElement('td');
140 140 td1.width=150;
141 141 var trending_language_label = document.createElement('div');
142 142 trending_language_label.innerHTML = k;
143 143 td1.appendChild(trending_language_label);
144 144
145 145 var td2 = document.createElement('td');
146 td2.setAttribute('style','padding-right:14px !important');
146 147 var trending_language = document.createElement('div');
147 148 trending_language.title = k;
148 149 trending_language.innerHTML = "<b>"+percentage+"% "+value+" ${_('files')}</b>";
149 150 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
150 151 trending_language.style.width=percentage+"%";
151 152 td2.appendChild(trending_language);
152 153
153 154 tr.appendChild(td1);
154 155 tr.appendChild(td2);
155 156 tbl.appendChild(tr);
156 157
157 158 }
158 159 if(no_data){
159 160 var tr = document.createElement('tr');
160 161 var td1 = document.createElement('td');
161 162 td1.innerHTML = "${_('No data loaded yet')}";
162 163 tr.appendChild(td1);
163 164 tbl.appendChild(tr);
164 165 }
165 166 YAHOO.util.Dom.get('lang_stats').appendChild(tbl);
166 167 </script>
167 168
168 169 </div>
169 170 </div>
170 171
171 172 <div class="field">
172 173 <div class="label">
173 174 <label>${_('Download')}:</label>
174 175 </div>
175 176 <div class="input-short">
176 177 %for cnt,archive in enumerate(c.repo_info._get_archives()):
177 178 %if cnt >=1:
178 179 |
179 180 %endif
180 181 ${h.link_to(c.repo_info.name+'.'+archive['type'],
181 182 h.url('files_archive_home',repo_name=c.repo_info.name,
182 183 revision='tip',fileformat=archive['extension']),class_="archive_icon")}
183 184 %endfor
184 185 </div>
185 186 </div>
186 187
187 188 <div class="field">
188 189 <div class="label">
189 190 <label>${_('Feeds')}:</label>
190 191 </div>
191 192 <div class="input-short">
192 193 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.repo_info.name),class_='rss_icon')}
193 194 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.repo_info.name),class_='atom_icon')}
194 195 </div>
195 196 </div>
196 197 </div>
197 198 </div>
198 199 </div>
199 200
200 201 <div class="box box-right" style="min-height:455px">
201 202 <!-- box / title -->
202 203 <div class="title">
203 204 <h5>${_('Commit activity by day / author')}</h5>
204 205 </div>
205 206
206 207 <div class="table">
207 208 <div id="commit_history" style="width:460px;height:300px;float:left"></div>
208 209 <div style="clear: both;height: 10px"></div>
209 210 <div id="overview" style="width:460px;height:100px;float:left"></div>
210 211
211 212 <div id="legend_data" style="clear:both;margin-top:10px;">
212 213 <div id="legend_container"></div>
213 214 <div id="legend_choices">
214 215 <table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
215 216 </div>
216 217 </div>
217 218 <script type="text/javascript">
218 219 /**
219 220 * Plots summary graph
220 221 *
221 222 * @class SummaryPlot
222 223 * @param {from} initial from for detailed graph
223 224 * @param {to} initial to for detailed graph
224 225 * @param {dataset}
225 226 * @param {overview_dataset}
226 227 */
227 228 function SummaryPlot(from,to,dataset,overview_dataset) {
228 229 var initial_ranges = {
229 230 "xaxis":{
230 231 "from":from,
231 232 "to":to,
232 233 },
233 234 };
234 235 var dataset = dataset;
235 236 var overview_dataset = [overview_dataset];
236 237 var choiceContainer = YAHOO.util.Dom.get("legend_choices");
237 238 var choiceContainerTable = YAHOO.util.Dom.get("legend_choices_tables");
238 239 var plotContainer = YAHOO.util.Dom.get('commit_history');
239 240 var overviewContainer = YAHOO.util.Dom.get('overview');
240 241
241 242 var plot_options = {
242 243 bars: {show:true,align:'center',lineWidth:4},
243 244 legend: {show:true, container:"legend_container"},
244 245 points: {show:true,radius:0,fill:false},
245 246 yaxis: {tickDecimals:0,},
246 247 xaxis: {
247 248 mode: "time",
248 249 timeformat: "%d/%m",
249 250 min:from,
250 251 max:to,
251 252 },
252 253 grid: {
253 254 hoverable: true,
254 255 clickable: true,
255 256 autoHighlight:true,
256 257 color: "#999"
257 258 },
258 259 //selection: {mode: "x"}
259 260 };
260 261 var overview_options = {
261 262 legend:{show:false},
262 263 bars: {show:true,barWidth: 2,},
263 264 shadowSize: 0,
264 265 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
265 266 yaxis: {ticks: 3, min: 0,},
266 267 grid: {color: "#999",},
267 268 selection: {mode: "x"}
268 269 };
269 270
270 271 /**
271 272 *get dummy data needed in few places
272 273 */
273 274 function getDummyData(label){
274 275 return {"label":label,
275 276 "data":[{"time":0,
276 277 "commits":0,
277 278 "added":0,
278 279 "changed":0,
279 280 "removed":0,
280 281 }],
281 282 "schema":["commits"],
282 283 "color":'#ffffff',
283 284 }
284 285 }
285 286
286 287 /**
287 288 * generate checkboxes accordindly to data
288 289 * @param keys
289 290 * @returns
290 291 */
291 292 function generateCheckboxes(data) {
292 293 //append checkboxes
293 294 var i = 0;
294 295 choiceContainerTable.innerHTML = '';
295 296 for(var pos in data) {
296 297
297 298 data[pos].color = i;
298 299 i++;
299 300 if(data[pos].label != ''){
300 301 choiceContainerTable.innerHTML += '<tr><td>'+
301 302 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
302 303 +data[pos].label+
303 304 '</td></tr>';
304 305 }
305 306 }
306 307 }
307 308
308 309 /**
309 310 * ToolTip show
310 311 */
311 312 function showTooltip(x, y, contents) {
312 313 var div=document.getElementById('tooltip');
313 314 if(!div) {
314 315 div = document.createElement('div');
315 316 div.id="tooltip";
316 317 div.style.position="absolute";
317 318 div.style.border='1px solid #fdd';
318 319 div.style.padding='2px';
319 320 div.style.backgroundColor='#fee';
320 321 document.body.appendChild(div);
321 322 }
322 323 YAHOO.util.Dom.setStyle(div, 'opacity', 0);
323 324 div.innerHTML = contents;
324 325 div.style.top=(y + 5) + "px";
325 326 div.style.left=(x + 5) + "px";
326 327
327 328 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
328 329 anim.animate();
329 330 }
330 331
331 332 /**
332 333 * This function will detect if selected period has some changesets for this user
333 334 if it does this data is then pushed for displaying
334 335 Additionally it will only display users that are selected by the checkbox
335 336 */
336 337 function getDataAccordingToRanges(ranges) {
337 338
338 339 var data = [];
339 340 var keys = [];
340 341 for(var key in dataset){
341 342 var push = false;
342 343 //method1 slow !!
343 344 ///*
344 345 for(var ds in dataset[key].data){
345 346 commit_data = dataset[key].data[ds];
346 347 //console.log(key);
347 348 //console.log(new Date(commit_data.time*1000));
348 349 //console.log(new Date(ranges.xaxis.from*1000));
349 350 //console.log(new Date(ranges.xaxis.to*1000));
350 351 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
351 352 push = true;
352 353 break;
353 354 }
354 355 }
355 356 //*/
356 357 /*//method2 sorted commit data !!!
357 358 var first_commit = dataset[key].data[0].time;
358 359 var last_commit = dataset[key].data[dataset[key].data.length-1].time;
359 360
360 361 console.log(first_commit);
361 362 console.log(last_commit);
362 363
363 364 if (first_commit >= ranges.xaxis.from && last_commit <= ranges.xaxis.to){
364 365 push = true;
365 366 }
366 367 */
367 368 if(push){
368 369 data.push(dataset[key]);
369 370 }
370 371 }
371 372 if(data.length >= 1){
372 373 return data;
373 374 }
374 375 else{
375 376 //just return dummy data for graph to plot itself
376 377 return [getDummyData('')];
377 378 }
378 379
379 380 }
380 381
381 382 /**
382 383 * redraw using new checkbox data
383 384 */
384 385 function plotchoiced(e,args){
385 386 var cur_data = args[0];
386 387 var cur_ranges = args[1];
387 388
388 389 var new_data = [];
389 390 var inputs = choiceContainer.getElementsByTagName("input");
390 391
391 392 //show only checked labels
392 393 for(var i=0; i<inputs.length; i++) {
393 394 var checkbox_key = inputs[i].name;
394 395
395 396 if(inputs[i].checked){
396 397 for(var d in cur_data){
397 398 if(cur_data[d].label == checkbox_key){
398 399 new_data.push(cur_data[d]);
399 400 }
400 401 }
401 402 }
402 403 else{
403 404 //push dummy data to not hide the label
404 405 new_data.push(getDummyData(checkbox_key));
405 406 }
406 407 }
407 408
408 409 var new_options = YAHOO.lang.merge(plot_options, {
409 410 xaxis: {
410 411 min: cur_ranges.xaxis.from,
411 412 max: cur_ranges.xaxis.to,
412 413 mode:"time",
413 414 timeformat: "%d/%m",
414 415 }
415 416 });
416 417 if (!new_data){
417 418 new_data = [[0,1]];
418 419 }
419 420 // do the zooming
420 421 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
421 422
422 423 plot.subscribe("plotselected", plotselected);
423 424
424 425 //resubscribe plothover
425 426 plot.subscribe("plothover", plothover);
426 427
427 428 // don't fire event on the overview to prevent eternal loop
428 429 overview.setSelection(cur_ranges, true);
429 430
430 431 }
431 432
432 433 /**
433 434 * plot only selected items from overview
434 435 * @param ranges
435 436 * @returns
436 437 */
437 438 function plotselected(ranges,cur_data) {
438 439 //updates the data for new plot
439 440 data = getDataAccordingToRanges(ranges);
440 441 generateCheckboxes(data);
441 442
442 443 var new_options = YAHOO.lang.merge(plot_options, {
443 444 xaxis: {
444 445 min: ranges.xaxis.from,
445 446 max: ranges.xaxis.to,
446 447 mode:"time",
447 448 timeformat: "%d/%m",
448 449 }
449 450 });
450 451 // do the zooming
451 452 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
452 453
453 454 plot.subscribe("plotselected", plotselected);
454 455
455 456 //resubscribe plothover
456 457 plot.subscribe("plothover", plothover);
457 458
458 459 // don't fire event on the overview to prevent eternal loop
459 460 overview.setSelection(ranges, true);
460 461
461 462 //resubscribe choiced
462 463 YAHOO.util.Event.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
463 464 }
464 465
465 466 var previousPoint = null;
466 467
467 468 function plothover(o) {
468 469 var pos = o.pos;
469 470 var item = o.item;
470 471
471 472 //YAHOO.util.Dom.get("x").innerHTML = pos.x.toFixed(2);
472 473 //YAHOO.util.Dom.get("y").innerHTML = pos.y.toFixed(2);
473 474 if (item) {
474 475 if (previousPoint != item.datapoint) {
475 476 previousPoint = item.datapoint;
476 477
477 478 var tooltip = YAHOO.util.Dom.get("tooltip");
478 479 if(tooltip) {
479 480 tooltip.parentNode.removeChild(tooltip);
480 481 }
481 482 var x = item.datapoint.x.toFixed(2);
482 483 var y = item.datapoint.y.toFixed(2);
483 484
484 485 if (!item.series.label){
485 486 item.series.label = 'commits';
486 487 }
487 488 var d = new Date(x*1000);
488 489 var fd = d.toDateString()
489 490 var nr_commits = parseInt(y);
490 491
491 492 var cur_data = dataset[item.series.label].data[item.dataIndex];
492 493 var added = cur_data.added;
493 494 var changed = cur_data.changed;
494 495 var removed = cur_data.removed;
495 496
496 497 var nr_commits_suffix = " ${_('commits')} ";
497 498 var added_suffix = " ${_('files added')} ";
498 499 var changed_suffix = " ${_('files changed')} ";
499 500 var removed_suffix = " ${_('files removed')} ";
500 501
501 502
502 503 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
503 504 if(added==1){added_suffix=" ${_('file added')} ";}
504 505 if(changed==1){changed_suffix=" ${_('file changed')} ";}
505 506 if(removed==1){removed_suffix=" ${_('file removed')} ";}
506 507
507 508 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
508 509 +'<br/>'+
509 510 nr_commits + nr_commits_suffix+'<br/>'+
510 511 added + added_suffix +'<br/>'+
511 512 changed + changed_suffix + '<br/>'+
512 513 removed + removed_suffix + '<br/>');
513 514 }
514 515 }
515 516 else {
516 517 var tooltip = YAHOO.util.Dom.get("tooltip");
517 518
518 519 if(tooltip) {
519 520 tooltip.parentNode.removeChild(tooltip);
520 521 }
521 522 previousPoint = null;
522 523 }
523 524 }
524 525
525 526 /**
526 527 * MAIN EXECUTION
527 528 */
528 529
529 530 var data = getDataAccordingToRanges(initial_ranges);
530 531 generateCheckboxes(data);
531 532
532 533 //main plot
533 534 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
534 535
535 536 //overview
536 537 var overview = YAHOO.widget.Flot(overviewContainer, overview_dataset, overview_options);
537 538
538 539 //show initial selection on overview
539 540 overview.setSelection(initial_ranges);
540 541
541 542 plot.subscribe("plotselected", plotselected);
542 543
543 544 overview.subscribe("plotselected", function (ranges) {
544 545 plot.setSelection(ranges);
545 546 });
546 547
547 548 plot.subscribe("plothover", plothover);
548 549
549 550 YAHOO.util.Event.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
550 551 }
551 552 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
552 553 </script>
553 554
554 555 </div>
555 556 </div>
556 557
557 558 <div class="box">
558 559 <div class="title">
559 560 <div class="breadcrumbs">${h.link_to(_('Last ten changes'),h.url('changelog_home',repo_name=c.repo_name))}</div>
560 561 </div>
561 562 <div class="table">
562 563 <div id="shortlog_data">
563 564 <%include file='../shortlog/shortlog_data.html'/>
564 565 </div>
565 566 ##%if c.repo_changesets:
566 567 ## ${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
567 568 ##%endif
568 569 </div>
569 570 </div>
570 571 <div class="box">
571 572 <div class="title">
572 573 <div class="breadcrumbs">${h.link_to(_('Last ten tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
573 574 </div>
574 575 <div class="table">
575 576 <%include file='../tags/tags_data.html'/>
576 577 %if c.repo_changesets:
577 578 ${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
578 579 %endif
579 580 </div>
580 581 </div>
581 582 <div class="box">
582 583 <div class="title">
583 584 <div class="breadcrumbs">${h.link_to(_('Last ten branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
584 585 </div>
585 586 <div class="table">
586 587 <%include file='../branches/branches_data.html'/>
587 588 %if c.repo_changesets:
588 589 ${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
589 590 %endif
590 591 </div>
591 592 </div>
592 593
593 594 </%def> No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now