author |
Steve Losh <steve@stevelosh.com> |
date |
Sun, 13 Jun 2010 04:43:25 -0400 |
parents |
757e193ec1b1 |
children |
647c0ea1bce7 |
from __future__ import with_statement
"""The review extension's web UI."""
import sys, os
from hashlib import md5
from mercurial import commands, templatefilters
from mercurial.node import short
from mercurial.util import email
import api
def unbundle():
package_path = os.path.split(os.path.realpath(__file__))[0]
template_path = os.path.join(package_path, 'web_templates')
media_path = os.path.join(package_path, 'web_media')
top_path = os.path.split(package_path)[0]
bundled_path = os.path.join(top_path, 'bundled')
flask_path = os.path.join(bundled_path, 'flask')
jinja2_path = os.path.join(bundled_path, 'jinja2')
werkzeug_path = os.path.join(bundled_path, 'werkzeug')
simplejson_path = os.path.join(bundled_path, 'simplejson')
sys.path.insert(0, flask_path)
sys.path.insert(0, werkzeug_path)
sys.path.insert(0, jinja2_path)
sys.path.insert(0, simplejson_path)
unbundle()
from flask import Flask
from flask import abort, redirect, render_template, request
app = Flask(__name__)
LOG_PAGE_LEN = 1000000
def _item_gravatar(item):
return 'http://www.gravatar.com/avatar/%s/' % md5(email(item.author)).hexdigest()
def _line_type(line):
return 'rem' if line[0] == '-' else 'add' if line[0] == '+' else 'con'
def _categorize_signoffs(signoffs):
return { 'yes': len(filter(lambda s: s.opinion == 'yes', signoffs)),
'no': len(filter(lambda s: s.opinion == 'no', signoffs)),
'neutral': len(filter(lambda s: s.opinion == '', signoffs)),}
utils = {
'node_short': short,
'basename': os.path.basename,
'md5': md5,
'email': email,
'templatefilters': templatefilters,
'len': len,
'item_gravatar': _item_gravatar,
'line_type': _line_type,
'categorize_signoffs': _categorize_signoffs,
'map': map,
'str': str,
}
datastore = None
site_read_only = False
def _render(template, **kwargs):
return render_template(template, read_only=site_read_only, utils=utils,
datastore=datastore, **kwargs)
@app.route('/')
def index():
rev_max = datastore.target['tip'].rev()
rev_min = rev_max - LOG_PAGE_LEN if rev_max >= LOG_PAGE_LEN else 0
rcsets = [datastore[r] for r in xrange(rev_max, rev_min, -1)]
return _render('index.html', title='', rcsets=rcsets)
def _handle_signoff(revhash):
signoff = request.form.get('signoff', None)
if signoff not in ['yes', 'no', 'neutral']:
abort(400)
if signoff == 'neutral':
signoff = ''
body = request.form.get('new-signoff-body', '')
rcset = datastore[revhash]
rcset.add_signoff(body, signoff, force=True)
return redirect("/changeset/%s/" % revhash)
def _handle_comment(revhash):
filename = request.form.get('filename', '')
lines = str(request.form.get('lines', ''))
if lines:
lines = filter(None, [l.strip() for l in lines.split(',')])
body = request.form['new-comment-body']
if body:
rcset = datastore[revhash]
rcset.add_comment(body, filename, lines)
return redirect("/changeset/%s/" % revhash)
@app.route('/changeset/<revhash>/', methods=['GET', 'POST'])
def changeset(revhash):
if request.method == 'POST' and not site_read_only:
signoff = request.form.get('signoff', None)
if signoff:
return _handle_signoff(revhash)
else:
return _handle_comment(revhash)
rcset = datastore[revhash]
rev = rcset.target[revhash]
cu_signoffs = rcset.signoffs_for_current_user()
cu_signoff = cu_signoffs[0] if cu_signoffs else None
return _render('changeset.html',
title='%s:%s' % (rev.rev(), short(rev.node())),
rcset=rcset, rev=rev, cu_signoff=cu_signoff
)
@app.route('/pull/', methods=['POST'])
def pull():
if not site_read_only:
path = request.form['path']
commands.pull(datastore.repo.ui, datastore.repo, path, update=True)
return redirect('/')
@app.route('/push/', methods=['POST'])
def push():
if not site_read_only:
path = request.form['path']
commands.push(datastore.repo.ui, datastore.repo, path)
return redirect('/')
def load_interface(ui, repo, read_only=False, open=False,
address='127.0.0.1', port=8080):
if open:
import webbrowser
webbrowser.open('http://localhost:%d/' % port)
global datastore, site_read_only
datastore = api.ReviewDatastore(ui, repo)
site_read_only = read_only
app.debug = ui.debugflag
if app.debug:
from flaskext.lesscss import lesscss
lesscss(app)
app.run(host=address, port=port)