Changeset - ab1afe7444f3
[Not reviewed]
default
1 4 0
Marcin Kuzminski - 15 years ago 2010-06-23 00:18:10
marcin@python-works.com
Initial graph release.
5 files changed with 120 insertions and 137 deletions:
0 comments (0 inline, 0 general)
pylons_app/controllers/changelog.py
Show inline comments
 
#!/usr/bin/env python
 
# encoding: utf-8
 
# changelog controller for pylons
 
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
 
from json import dumps
 
from mercurial.graphmod import colored, CHANGESET
 
from mercurial.node import short
 
from mercurial.templatefilters import person
 
from pylons import request, session, tmpl_context as c
 
from pylons_app.lib.auth import LoginRequired
 
from pylons_app.lib.base import BaseController, render
 
from pylons_app.model.hg_model import HgModel
 
from webhelpers.paginate import Page
 
import logging
 
from mercurial.graphmod import revisions as graph_rev
 
 
 
# This program is free software; you can redistribute it and/or
 
# modify it under the terms of the GNU General Public License
 
# as published by the Free Software Foundation; version 2
 
# of the License or (at your opinion) any later version of the license.
 
# 
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
# 
 
# You should have received a copy of the GNU General Public License
 
# along with this program; if not, write to the Free Software
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
# MA  02110-1301, USA.
 
"""
 
Created on April 21, 2010
 
changelog controller for pylons
 
@author: marcink
 
"""
 
from pylons import request, session, tmpl_context as c
 
from pylons_app.lib.auth import LoginRequired
 
from pylons_app.lib.base import BaseController, render
 
from pylons_app.model.hg_model import HgModel
 
from webhelpers.paginate import Page
 
import logging
 
log = logging.getLogger(__name__)     
 

	
 
class ChangelogController(BaseController):
 
    
 
    @LoginRequired()
 
    def __before__(self):
 
        super(ChangelogController, self).__before__()
 
                
 
    def index(self):
 
        limit = 100
 
        default = 20
 
        if request.params.get('size'):
 
            try:
 
                int_size = int(request.params.get('size'))
 
            except ValueError:
 
                int_size = default
 
            int_size = int_size if int_size <= limit else limit 
 
            c.size = int_size
 
            session['changelog_size'] = c.size
 
            session.save()
 
        else:
 
            c.size = int(session.get('changelog_size', default))
 

	
 
        changesets = HgModel().get_repo(c.repo_name)
 
            
 
        p = int(request.params.get('page', 1))
 
        c.total_cs = len(changesets)
 
        c.pagination = Page(changesets, page=p, item_count=c.total_cs,
 
                            items_per_page=c.size)
 
            
 
        #self._graph(c.repo, c.size,p)
 
        self._graph(changesets, c.size, p)
 
        
 
        return render('changelog/changelog.html')
 

	
 

	
 
    def _graph(self, repo, size, p):
 
        pass
 
#        revcount = size
 
#        if not repo.revisions:return dumps([]), 0
 
#        
 
#        max_rev = repo.revisions[-1]
 
#        offset = 1 if p == 1 else  ((p - 1) * revcount)
 
#        rev_start = repo.revisions[(-1 * offset)]
 
#        c.bg_height = 120
 
#        
 
#        revcount = min(max_rev, revcount)
 
#        rev_end = max(0, rev_start - revcount)
 
#        dag = graph_rev(repo.repo, rev_start, rev_end)
 
#        
 
#        c.dag = tree = list(colored(dag))
 
#        canvasheight = (len(tree) + 1) * c.bg_height - 27
 
#        data = []
 
#        for (id, type, ctx, vtx, edges) in tree:
 
#            if type != CHANGESET:
 
#                continue
 
#            node = short(ctx.node())
 
#            age = _age(ctx.date())
 
#            desc = ctx.description()
 
#            user = person(ctx.user())
 
#            branch = ctx.branch()
 
#            branch = branch, repo.repo.branchtags().get(branch) == ctx.node()
 
#            data.append((node, vtx, edges, desc, user, age, branch, ctx.tags()))
 
#    
 
#        c.jsdata = dumps(data) 
 
#        c.canvasheight = canvasheight 
 
        revcount = size
 
        if not repo.revisions:return dumps([]), 0
 
        
 
        max_rev = repo.revisions[-1]
 
        offset = 1 if p == 1 else  ((p - 1) * revcount)
 
        rev_start = repo.revisions[(-1 * offset)]
 
        
 
        revcount = min(max_rev, revcount)
 
        rev_end = max(0, rev_start - revcount)
 
        dag = graph_rev(repo.repo, rev_start, rev_end)
 
        
 
        c.dag = tree = list(colored(dag))
 
        data = []
 
        for (id, type, ctx, vtx, edges) in tree:
 
            if type != CHANGESET:
 
                continue
 
            node = short(ctx.node())
 
            age = ctx.date()
 
            desc = ctx.description()
 
            user = person(ctx.user())
 
            branch = ctx.branch()
 
            branch = branch, repo.repo.branchtags().get(branch) == ctx.node()
 
            data.append((node, vtx, edges, desc, user, age, branch, ctx.tags()))
 
    
 
        c.jsdata = dumps(data) 
 

	
pylons_app/public/css/monoblue_custom.css
Show inline comments
 
@@ -624,52 +624,50 @@ p.files {
 
	padding-left: 20px;
 
	margin-top: 7px;
 
	text-align: left;	
 
}
 
.cs_files .cs_changed{
 
	background: url("/images/icons/page_white_edit.png") no-repeat scroll 3px;
 
	/*background-color: #FFDD88;*/
 
	height: 16px;
 
	padding-left: 20px;
 
	margin-top: 7px;
 
	text-align: left;	
 
}
 
.cs_files .cs_removed{
 
	background: url("/images/icons/page_white_delete.png") no-repeat scroll 3px;
 
	/*background-color: #FF8888;*/
 
	height: 16px;
 
	padding-left: 20px;
 
	margin-top: 7px;
 
	text-align: left;	
 
}
 

	
 
/** end of changeset **/ 
 

	
 
/** canvas **/
 
canvas {
 
	position: absolute;
 
	z-index: 5;
 
	top: -0.7em;
 
#graph_nodes{
 
	margin-top:8px;
 
}
 
#graph{
 
	overflow: hidden;
 

	
 
}
 
#graph_nodes{
 
	width:160px;
 
	float:left;
 
}
 

	
 
#graph_content{
 
	width:800px;
 
	float:left;
 
}
 
#graph_content .container_header{
 
	border:1px solid #CCCCCC;
 
	height:30px;
 
	background: #EEEEEE;
 
}
 

	
 

	
 
#graph_content .container .wrapper{
 
	width: 600px;
 
}
pylons_app/public/js/excanvas.js
Show inline comments
 
deleted file
pylons_app/public/js/graph.js
Show inline comments
 
// branch_renderer.js - Rendering of branch DAGs on the client side
 
//
 
// Copyright 2008 Jesper Noehr <jesper AT noehr DOT org>
 
// Copyright 2008 Dirkjan Ochtman <dirkjan AT ochtman DOT nl>
 
// Copyright 2006 Alexander Schremmer <alex AT alexanderweb DOT de>
 
//
 
// derived from code written by Scott James Remnant <scott@ubuntu.com>
 
// Copyright 2005 Canonical Ltd.
 
//
 
// This software may be used and distributed according to the terms
 
// of the GNU General Public License, incorporated herein by reference.
 

	
 
var colors = [
 
	[ 1.0, 0.0, 0.0 ],
 
	[ 1.0, 1.0, 0.0 ],
 
	[ 0.0, 1.0, 0.0 ],
 
	[ 0.0, 1.0, 1.0 ],
 
	[ 0.0, 0.0, 1.0 ],
 
	[ 1.0, 0.0, 1.0 ]
 
	[ 1.0, 0.0, 1.0 ],
 
	[ 1.0, 1.0, 0.0 ],
 
	[ 0.0, 0.0, 0.0 ]
 
];
 

	
 
function Graph() {
 
function BranchRenderer() {
 
	
 
	this.canvas = document.getElementById('graph');
 
	if (navigator.userAgent.indexOf('MSIE') >= 0) this.canvas = window.G_vmlCanvasManager.initElement(this.canvas);
 
	this.canvas = document.getElementById("graph_canvas");
 
	
 
	//if ($.browser.msie)
 
	//	this.canvas = window.G_vmlCanvasManager.initElement(this.canvas);
 
	this.ctx = this.canvas.getContext('2d');
 
	this.ctx.strokeStyle = 'rgb(0, 0, 0)';
 
	this.ctx.fillStyle = 'rgb(0, 0, 0)';
 
	this.cur = [0, 0];
 
	this.max_column = 1;
 
	this.line_width = 3;
 
	this.bg = [0, 4];
 
	this.cell = [2, 0];
 
	this.columns = 0;
 
	this.revlink = '';
 
	
 
	this.scale = function(height) {
 
		this.box_size = Math.floor(height/1.2);
 
		this.cell_height = this.box_size;
 
		this.bg_height = height;
 
		this.box_size = Math.floor(this.bg_height / 1.2);
 
		this.cell_height = this.box_size;
 
	}
 
	
 
	function colorPart(num) {
 
		num *= 255
 
		num = num < 0 ? 0 : num;
 
		num = num > 255 ? 255 : num;
 
		var digits = Math.round(num).toString(16);
 
		if (num < 16) {
 
			return '0' + digits;
 
		} else {
 
			return digits;
 
		}
 
	}
 

	
 
	this.setColor = function(color, bg, fg) {
 
		
 
		// Set the colour.
 
		//
 
		// Picks a distinct colour based on an internal wheel; the bg
 
		// parameter provides the value that should be assigned to the 'zero'
 
		// colours and the fg parameter provides the multiplier that should be
 
		// applied to the foreground colours.
 
		
 
		color %= colors.length;
 
		var red = (colors[color][0] * fg) || bg;
 
		var green = (colors[color][1] * fg) || bg;
 
		var blue = (colors[color][2] * fg) || bg;
 
		red = Math.round(red * 255);
 
		green = Math.round(green * 255);
 
		blue = Math.round(blue * 255);
 
		var s = 'rgb(' + red + ', ' + green + ', ' + blue + ')';
 
		this.ctx.strokeStyle = s;
 
		this.ctx.fillStyle = s;
 
		return s;
 
		
 
	}
 

	
 
	this.render = function(data) {
 
		
 
		var backgrounds = '';
 
		var nodedata = '';
 
		var idx = 1;
 
		var rela = document.getElementById('graph');
 
		var pad = 160;
 
		var scale = 20;
 
		
 
		for (var i in data) {
 
			this.scale(scale);
 
			var row = document.getElementById("chg_"+idx);
 
			var	next = document.getElementById("chg_"+idx+1);
 
			var extra = 0;
 
			
 
			var parity = i % 2;
 
			this.cell[1] += this.bg_height;
 
			//skip this since i don't have DATE in my app
 
			//if (next.is('.changesets-date')) {
 
			//	extra = next.outerHeight();
 
			//}
 
						
 
			
 
			this.cell[1] += row.clientWidth;
 
			this.bg[1] += this.bg_height;
 
			
 
			var cur = data[i];
 
			var node = cur[1];
 
			var edges = cur[2];
 
			var fold = false;
 
			cur = data[i];
 
			nodeid = cur[0];
 
			node = cur[1];
 
			in_l = cur[2];
 
			
 
			for (var j in edges) {
 
			for (var j in in_l) {
 
				
 
				line = edges[j];
 
				line = in_l[j];
 
				start = line[0];
 
				end = line[1];
 
				color = line[2];
 

	
 
				if (end > this.columns || start > this.columns) {
 
					this.columns += 1;
 
				if (start > this.max_column) {
 
					this.max_column = start;
 
				}
 
				
 
				if (start == this.columns && start > end) {
 
					var fold = true;
 
				if (end > this.max_column) {
 
					this.max_column = end;
 
				}
 
				
 
				x0 = this.cell[0] + this.box_size * start + this.box_size / 2;
 
				y0 = this.bg[1] - this.bg_height / 2;
 
				x1 = this.cell[0] + this.box_size * end + this.box_size / 2;
 
				y1 = this.bg[1] + this.bg_height / 2;
 
				this.setColor(color, 0.0, 0.65);
 
				
 
				
 
				this.edge(x0, y0, x1, y1, color);
 
				y = row.offsetTop-rela.offsetTop+4;
 
				x = pad-((this.cell[0] + this.box_size * start - 1) + this.bg_height-2);
 
				this.ctx.beginPath();
 
				this.ctx.moveTo(x, y);
 
				
 
				y += row.clientHeight;
 
				x = pad-((1 + this.box_size * end) + this.bg_height-2);
 
				this.ctx.lineTo(x,y+extra);
 
				this.ctx.stroke();
 
			}
 
			
 
			// Draw the revision node in the right column
 
			
 
			column = node[0]
 
			color = node[1]
 
			
 
			radius = this.box_size / 8;
 
			x = this.cell[0] + this.box_size * column + this.box_size / 2;
 
			y = this.bg[1] - this.bg_height / 2;
 
			var add = this.vertex(x, y, color, parity, cur);
 
			backgrounds += add[0];
 
			nodedata += add[1];
 
			radius = 4;
 
			y = row.offsetTop-rela.offsetTop+4;
 
			x = pad-(Math.round(this.cell[0] * scale/2 * column + radius) + 15 - (column*4));
 
		
 
			this.ctx.beginPath();
 
			this.setColor(color, 0.25, 0.75);
 
			this.ctx.arc(x, y, radius, 0, Math.PI * 2, true);
 
			this.ctx.fill();
 
			
 
			if (fold) this.columns -= 1;
 
			
 
			idx++;
 
		}
 
		
 
		document.getElementById('nodebgs').innerHTML += backgrounds;
 
		document.getElementById('graphnodes').innerHTML += nodedata;
 
		
 
				
 
	}
 

	
 
}
pylons_app/templates/changelog/changelog.html
Show inline comments
 
@@ -3,93 +3,102 @@
 
<%def name="title()">
 
    ${_('Changelog - %s') % c.repo_name}
 
</%def>
 
<%def name="breadcrumbs()">
 
    ${h.link_to(u'Home',h.url('/'))}
 
    / 
 
    ${h.link_to(c.repo_name,h.url('changelog_home',repo_name=c.repo_name))}
 
    /
 
    ${_('changelog')}
 
</%def>
 
<%def name="page_nav()">
 
	${self.menu('changelog')}     
 
</%def>
 

	
 
<%def name="main()">
 

	
 
    <h2 class="no-link no-border">${_('Changelog')} - ${_('showing ')} 
 
    	${c.size if c.size <= c.total_cs else c.total_cs}
 
    	${_('out of')} ${c.total_cs} ${_('revisions')}
 
    </h2>
 
	<noscript>${_('The revision graph only works with JavaScript-enabled browsers.')}</noscript>
 
% if c.pagination:
 

	
 
<div id="graph">
 
	##<div id="graph_nodes" style="height:1000px">
 
	##	<canvas id="graph" width="160"></canvas>
 
	##</div>
 
	<div id="graph_nodes">
 
		<canvas id="graph_canvas"></canvas>
 
	</div>
 
	<div id="graph_content">
 
		<div class="container_header">
 
			${h.form(h.url.current(),method='get')}
 
				${_('Show')}: ${h.text('size',size=2,value=c.size)} ${_('revisions')}
 
				${h.submit('','set')}
 
			${h.end_form()}
 
		</div>
 
	%for cnt,cs in enumerate(c.pagination):
 
		<div class="container">
 
		<div id="chg_${cnt+1}" class="container">
 
			<div class="left">
 
				<div class="date">${_('commit')} ${cs.revision}: ${cs.raw_id}@${cs.date}</div>
 
				<div class="author">${cs.author}</div>
 
				<div id="chg_${cnt}" class="message">
 
					${h.link_to(h.wrap_paragraphs(cs.message),
 
					h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}
 
				</div>
 
					<span class="logtags">
 
						<span class="branchtag">${cs.branch}</span>
 
						%for tag in cs.tags:
 
							<span class="tagtag">${tag}</span>
 
						%endfor
 
					</span>
 
			</div>	
 
			<div class="right">
 
						<div class="changes">
 
							<span class="removed" title="${_('removed')}">${len(cs.removed)}</span>
 
							<span class="changed" title="${_('changed')}">${len(cs.changed)}</span>
 
							<span class="added" title="${_('added')}">${len(cs.added)}</span>
 
						</div>					
 
							%if len(cs.parents)>1:
 
							<div class="merge">
 
								${_('merge')}<img alt="merge" src="/images/icons/arrow_join.png">
 
							</div>
 
							%endif						
 
						%for p_cs in reversed(cs.parents):
 
							<div class="parent">${_('Parent')} ${p_cs.revision}: ${h.link_to(p_cs.raw_id,
 
								h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}
 
							</div>
 
						%endfor								
 
			</div>				
 
		</div>
 
		
 
	%endfor
 
	</div>
 
</div>
 
<script type="text/javascript" src="/js/graph.js"></script>
 

	
 
##<script type="text/javascript" src="/js/graph2.js"></script>
 
##<script type="text/javascript" src="http://bitbucket-assets.s3.amazonaws.com/js/lib/bundle.160310Mar.js"></script>
 
##
 
##<script>
 
##<!-- hide script content
 
##
 
##var jsdata = ${c.jsdata|n};
 
##var r = new BranchRenderer();
 
##r.render(jsdata); 
 
<script type="text/javascript">
 
YAHOO.util.Event.onDOMReady(function(){
 
	function set_canvas() {
 
		var c = document.getElementById('graph_nodes');
 
		var t = document.getElementById('graph_content');
 
		canvas = document.getElementById('graph_canvas');
 
		var div_h = t.clientHeight;
 
		c.style.height=div_h+'px';
 
		canvas.setAttribute('height',div_h);
 
		canvas.setAttribute('width',160);
 
	};
 

	
 
##// stop hiding script -->
 
##</script>
 
	set_canvas();
 
	
 
	var jsdata = ${c.jsdata|n};
 
	var r = new BranchRenderer();
 
	r.render(jsdata); 
 

	
 
});
 
</script>
 

	
 
<div>
 
	<h2>${c.pagination.pager('$link_previous ~2~ $link_next')}</h2>
 
</div>	
 
%else:
 
	${_('There are no changes yet')}
 
%endif
 
</%def>    
 
\ No newline at end of file
0 comments (0 inline, 0 general)