author |
Steve Losh <steve@stevelosh.com> |
date |
Sun, 13 Jun 2010 13:39:58 -0400 |
parents |
cec804c97165 |
children |
bcdc35f68d5d |
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?s=30' % 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,
'md5': md5,
'email': email,
'templatefilters': templatefilters,
'len': len,
'item_gravatar': _item_gravatar,
'line_type': _line_type,
'categorize_signoffs': _categorize_signoffs,
'map': map,
'str': str,
'decode': lambda s: s.decode('utf-8'),
}
def _render(template, **kwargs):
return render_template(template, read_only=app.read_only,
allow_anon=app.allow_anon, utils=utils, datastore=app.datastore,
title=app.title, **kwargs)
@app.route('/')
def index():
rev_max = app.datastore.target['tip'].rev()
rev_min = rev_max - LOG_PAGE_LEN if rev_max >= LOG_PAGE_LEN else 0
rcsets = [app.datastore[r] for r in xrange(rev_max, rev_min, -1)]
return _render('index.html', 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 = app.datastore[revhash]
rcset.add_signoff(body, signoff, force=True)
return redirect("%s/changeset/%s/" % (app.site_root, 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 = app.datastore[revhash]
rcset.add_comment(body, filename, lines)
return redirect("%s/changeset/%s/" % (app.site_root, revhash))
@app.route('/changeset/<revhash>/', methods=['GET', 'POST'])
def changeset(revhash):
if request.method == 'POST':
signoff = request.form.get('signoff', None)
if signoff and not app.read_only:
return _handle_signoff(revhash)
elif not app.read_only or app.allow_anon:
return _handle_comment(revhash)
rcset = app.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',
rcset=rcset, rev=rev, cu_signoff=cu_signoff
)
@app.route('/pull/', methods=['POST'])
def pull():
if not app.read_only:
path = request.form['path']
commands.pull(app.datastore.repo.ui, app.datastore.repo, path, update=True)
return redirect('%s/' % app.site_root)
@app.route('/push/', methods=['POST'])
def push():
if not app.read_only:
path = request.form['path']
commands.push(app.datastore.repo.ui, app.datastore.repo, path)
return redirect('%s/' % app.site_root)
def load_interface(ui, repo, read_only=False, allow_anon=False,
open=False, address='127.0.0.1', port=8080):
if open:
import webbrowser
webbrowser.open('http://localhost:%d/' % port)
app.read_only = read_only
app.debug = ui.debugflag
app.allow_anon = allow_anon
app.site_root = ''
if app.allow_anon:
ui.setconfig('ui', 'username', 'Anonymous <anonymous@example.com>')
app.datastore = api.ReviewDatastore(ui, repo)
app.title = os.path.basename(repo.root)
if app.debug:
from flaskext.lesscss import lesscss
lesscss(app)
app.run(host=address, port=port)