36b37712c670 webpy-sucks
Get a bare Cherrypy server up and running.
author | Steve Losh <steve@stevelosh.com> |
---|---|
date | Tue, 02 Mar 2010 20:23:36 -0500 |
parents | 256716e3a3d7 |
children | 8e18e2c40a06 |
branches/tags | webpy-sucks |
files | review/web_templates/changeset.html review/web_templates/review.html review/web_ui.py |
Changes
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/review/web_templates/changeset.html Tue Mar 02 20:23:36 2010 -0500 @@ -0,0 +1,135 @@ +$def with (rd, rcset) + +$ ctx = rd.target[rcset.node] +<h2>Changeset ${ ctx.rev() }: ${ ctx.description() }</h2> + +$ review_level_comments = rcset.review_level_comments() +$for comment in review_level_comments: + <div class="comment"> + <div class="avatar"><img height="52" width="52" src="http://www.gravatar.com/avatar/${ md5(email(comment.author)).hexdigest() }?s=52"/></div> + <div> + <div class="author"><a href="mailto:${ email(comment.author) }">${ templatefilters.person(comment.author) }</a> said:</div> + <div class="message">${ comment.message }</div> + </div> + </div> + +<div id="comment-review"> + <p class="comment-activate"><a href="">Add a comment on this changeset</a></p> + <form id="comment-review-form" method="post" action=""> + <div class="field"> + <label for="body">Add a comment on this changeset:</label> + <textarea cols="60" rows="6" name="body"></textarea> + </div> + <div class="buttons"> + <input type="submit" class="button" value="Submit" /> + </div> + </form> +</div> + +<h2>Files</h2> + +$for filename, diff in rcset.diffs().iteritems(): + <div class="file-review"> + <div class="filename-header"> + <a class="fold-file" href=""> ↓</a> + <h3>${ filename }</h3> + </div> + + <div class="file-review-contents"> + $ file_level_comments = rcset.file_level_comments(filename) + $for comment in file_level_comments: + <div class="comment"> + <div class="avatar"> + <img height="52" width="52" src="http://www.gravatar.com/avatar/${ md5(email(comment.author)).hexdigest() }?s=52"/> + </div> + <div> + <div class="author"><a href="mailto:${ email(comment.author) }">${ templatefilters.person(comment.author) }</a> said:</div> + <div class="message">${ comment.message }</div> + </div> + </div> + + + <div id="comment-file"> + <p class="comment-activate"><a href="">Add a comment on this file</a></p> + <form id="comment-file-form" method="post" action=""> + <div class="field"> + <label for="body">Add a comment on this file:</label> + <textarea cols="60" rows="6" name="body"></textarea> + </div> + <div class="buttons"> + <input type="submit" class="button" value="Submit" /> + </div> + <input type="hidden" name="filename" value="${ filename }" /> + </form> + </div> + + <div class="diff"> + <table> + $ max_line = diff['max'] + $ content = diff['content'] + $ line_level_comments = rcset.line_level_comments(filename) + $ previous_n = -1 + $for n, line in content: + $if n - 1 > previous_n: + $ skipped_count = n - previous_n + $if previous_n == -1: + $ skipped_count -= 1 + <tr class="skipped"> + <td><code>… skipped ${ skipped_count } lines …</code></td> + </tr> + $ skipped_comments = filter(lambda c: max(c.lines) in range(previous_n + 1, n), line_level_comments) + $for comment in skipped_comments: + <tr><td class="comment"> + <div class="avatar"><img height="52" width="52" src="http://www.gravatar.com/avatar/${ md5(email(comment.author)).hexdigest() }?s=52"/></div> + <div> + <div class="author"><a href="mailto:${ email(comment.author) }">${ templatefilters.person(comment.author) }</a> said (on a skipped line):</div> + <div class="message">${ comment.message }</div> + </div> + </td></tr> + $ kind = 'rem' if line[0] == '-' else 'add' if line[0] == '+' else 'con' + <tr class="${ kind }"> + <td class="diff-line"><code>${ line[1:] or ' ' }</code></td> + </tr> + $ line_comments = filter(lambda c: max(c.lines) == n, line_level_comments) + $for comment in line_comments: + <tr><td class="comment"> + <div class="avatar"><img height="52" width="52" src="http://www.gravatar.com/avatar/${ md5(email(comment.author)).hexdigest() }?s=52"/></div> + <div> + <div class="author"><a href="mailto:${ email(comment.author) }">${ templatefilters.person(comment.author) }</a> said:</div> + <div class="message">${ comment.message }</div> + </div> + </td></tr> + <tr class="comment-line"> + <td> + <form id="comment-line-form" method="post" action=""> + <div class="field"> + <label for="body">Add a comment on this line:</label> + <textarea cols="60" rows="6" name="body"></textarea> + </div> + <div class="buttons"> + <input type="submit" class="button" value="Submit" /> + </div> + <input type="hidden" name="filename" value="${ filename }" /> + <input type="hidden" name="lines" value="${ n }" /> + </form> + </td> + </tr> + $ previous_n = n + $if previous_n < max_line: + $ skipped_count = max_line - previous_n + <tr class="skipped"> + <td><code>… skipped ${ skipped_count } lines …</code></td> + </tr> + $ skipped_comments = filter(lambda c: max(c.lines) in range(previous_n + 1, max_line), line_level_comments) + $for comment in skipped_comments: + <tr><td class="comment"> + <div class="avatar"><img height="52" width="52" src="http://www.gravatar.com/avatar/${ md5(email(comment.author)).hexdigest() }?s=52"/></div> + <div> + <div class="author"><a href="mailto:${ email(comment.author) }">${ templatefilters.person(comment.author) }</a> said (on a skipped line):</div> + <div class="message">${ comment.message }</div> + </div> + </td></tr> + </table> + </div> + </div> + </div> \ No newline at end of file
--- a/review/web_templates/review.html Tue Mar 02 19:49:35 2010 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,135 +0,0 @@ -$def with (rd, rcset) - -$ ctx = rd.target[rcset.node] -<h2>Changeset ${ ctx.rev() }: ${ ctx.description() }</h2> - -$ review_level_comments = rcset.review_level_comments() -$for comment in review_level_comments: - <div class="comment"> - <div class="avatar"><img height="52" width="52" src="http://www.gravatar.com/avatar/${ md5(email(comment.author)).hexdigest() }?s=52"/></div> - <div> - <div class="author"><a href="mailto:${ email(comment.author) }">${ templatefilters.person(comment.author) }</a> said:</div> - <div class="message">${ comment.message }</div> - </div> - </div> - -<div id="comment-review"> - <p class="comment-activate"><a href="">Add a comment on this changeset</a></p> - <form id="comment-review-form" method="post" action=""> - <div class="field"> - <label for="body">Add a comment on this changeset:</label> - <textarea cols="60" rows="6" name="body"></textarea> - </div> - <div class="buttons"> - <input type="submit" class="button" value="Submit" /> - </div> - </form> -</div> - -<h2>Files</h2> - -$for filename, diff in rcset.diffs().iteritems(): - <div class="file-review"> - <div class="filename-header"> - <a class="fold-file" href=""> ↓</a> - <h3>${ filename }</h3> - </div> - - <div class="file-review-contents"> - $ file_level_comments = rcset.file_level_comments(filename) - $for comment in file_level_comments: - <div class="comment"> - <div class="avatar"> - <img height="52" width="52" src="http://www.gravatar.com/avatar/${ md5(email(comment.author)).hexdigest() }?s=52"/> - </div> - <div> - <div class="author"><a href="mailto:${ email(comment.author) }">${ templatefilters.person(comment.author) }</a> said:</div> - <div class="message">${ comment.message }</div> - </div> - </div> - - - <div id="comment-file"> - <p class="comment-activate"><a href="">Add a comment on this file</a></p> - <form id="comment-file-form" method="post" action=""> - <div class="field"> - <label for="body">Add a comment on this file:</label> - <textarea cols="60" rows="6" name="body"></textarea> - </div> - <div class="buttons"> - <input type="submit" class="button" value="Submit" /> - </div> - <input type="hidden" name="filename" value="${ filename }" /> - </form> - </div> - - <div class="diff"> - <table> - $ max_line = diff['max'] - $ content = diff['content'] - $ line_level_comments = rcset.line_level_comments(filename) - $ previous_n = -1 - $for n, line in content: - $if n - 1 > previous_n: - $ skipped_count = n - previous_n - $if previous_n == -1: - $ skipped_count -= 1 - <tr class="skipped"> - <td><code>… skipped ${ skipped_count } lines …</code></td> - </tr> - $ skipped_comments = filter(lambda c: max(c.lines) in range(previous_n + 1, n), line_level_comments) - $for comment in skipped_comments: - <tr><td class="comment"> - <div class="avatar"><img height="52" width="52" src="http://www.gravatar.com/avatar/${ md5(email(comment.author)).hexdigest() }?s=52"/></div> - <div> - <div class="author"><a href="mailto:${ email(comment.author) }">${ templatefilters.person(comment.author) }</a> said (on a skipped line):</div> - <div class="message">${ comment.message }</div> - </div> - </td></tr> - $ kind = 'rem' if line[0] == '-' else 'add' if line[0] == '+' else 'con' - <tr class="${ kind }"> - <td class="diff-line"><code>${ line[1:] or ' ' }</code></td> - </tr> - $ line_comments = filter(lambda c: max(c.lines) == n, line_level_comments) - $for comment in line_comments: - <tr><td class="comment"> - <div class="avatar"><img height="52" width="52" src="http://www.gravatar.com/avatar/${ md5(email(comment.author)).hexdigest() }?s=52"/></div> - <div> - <div class="author"><a href="mailto:${ email(comment.author) }">${ templatefilters.person(comment.author) }</a> said:</div> - <div class="message">${ comment.message }</div> - </div> - </td></tr> - <tr class="comment-line"> - <td> - <form id="comment-line-form" method="post" action=""> - <div class="field"> - <label for="body">Add a comment on this line:</label> - <textarea cols="60" rows="6" name="body"></textarea> - </div> - <div class="buttons"> - <input type="submit" class="button" value="Submit" /> - </div> - <input type="hidden" name="filename" value="${ filename }" /> - <input type="hidden" name="lines" value="${ n }" /> - </form> - </td> - </tr> - $ previous_n = n - $if previous_n < max_line: - $ skipped_count = max_line - previous_n - <tr class="skipped"> - <td><code>… skipped ${ skipped_count } lines …</code></td> - </tr> - $ skipped_comments = filter(lambda c: max(c.lines) in range(previous_n + 1, max_line), line_level_comments) - $for comment in skipped_comments: - <tr><td class="comment"> - <div class="avatar"><img height="52" width="52" src="http://www.gravatar.com/avatar/${ md5(email(comment.author)).hexdigest() }?s=52"/></div> - <div> - <div class="author"><a href="mailto:${ email(comment.author) }">${ templatefilters.person(comment.author) }</a> said (on a skipped line):</div> - <div class="message">${ comment.message }</div> - </div> - </td></tr> - </table> - </div> - </div> - </div> \ No newline at end of file
--- a/review/web_ui.py Tue Mar 02 19:49:35 2010 -0500 +++ b/review/web_ui.py Tue Mar 02 20:23:36 2010 -0500 @@ -2,127 +2,145 @@ from __future__ import with_statement import sys, os +from hashlib import md5 + +from mercurial import cmdutil, hg, templatefilters +from mercurial.node import short +from mercurial.util import email + import api -from mercurial import cmdutil, hg 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') -webpy_path = os.path.join(bundled_path, 'webpy') - -sys.path.insert(0, webpy_path) -import web - +cherrypy_path = os.path.join(bundled_path, 'cherrypy') +jinja2_path = os.path.join(bundled_path, 'jinja2') -_rd = None -urls = ( - '/', 'index', - '/media/([^/]*)', 'media', - '/review/([\da-f]{12})/?', 'review', - '/push/', 'push', - '/pull/', 'pull', -) +sys.path.insert(0, cherrypy_path) +sys.path.insert(0, jinja2_path) + +import cherrypy +from jinja2 import Environment, FileSystemLoader -from mercurial.node import short -from mercurial.util import email -from mercurial import templatefilters -from hashlib import md5 -g = { - 'node_short': short, - 'basename': os.path.basename, - 'md5': md5, - 'email': email, - 'templatefilters': templatefilters, -} -render = web.template.render(template_path, globals=g) +TEMPLATE_DIR = os.path.join(package_path, 'web_templates') +jinja_env = Environment(loader=FileSystemLoader(TEMPLATE_DIR)) -LOG_PAGE_LEN = 25 - -def render_in_base(fn): - def _fn(*args, **kwargs): - title, content = fn(*args, **kwargs) - return render.base(_rd, content, title) - return _fn - -class index: - @render_in_base - def GET(self): - rev_max = _rd.target['tip'].rev() - rev_min = rev_max - LOG_PAGE_LEN if rev_max >= LOG_PAGE_LEN else 0 - revs = (_rd.target[r] for r in xrange(rev_max, rev_min, -1)) - return ('', render.index(_rd, revs)) +class ReviewWebUI(object): + def __init__(self, datastore): + self.datastore = datastore + + + @cherrypy.expose + def index(self): + return jinja_env.get_template('index.html').render( + datastore=self.datastore + ) -class review: - @render_in_base - def GET(self, node_short): - title = '/ %s:%s – %s' % ( - _rd[node_short].target[node_short].rev(), - node_short, - _rd[node_short].target[node_short].description().splitlines()[0]) - return (title, render.review(_rd, _rd[node_short])) - - def POST(self, node_short): - i = web.input() - body = i['body'] - filename = i['filename'] if 'filename' in i else '' - lines = i['lines'].split(',') if 'lines' in i else '' - print filename, lines - - if body: - rcset = _rd[node_short] - rcset.add_comment(body, filename, lines) - - raise web.seeother('/review/%s/' % node_short) - -class push: - def GET(self): - path = web.input()['path'] - dest, revs, checkout = hg.parseurl(_rd.repo.ui.expandpath(path, path), None) - other = hg.repository(cmdutil.remoteui(_rd.repo, {}), dest) - - _rd.repo.push(other, True, revs=revs) - - raise web.seeother('/') - - -class pull: - def GET(self): - path = web.input()['path'] - source, revs, checkout = hg.parseurl(_rd.repo.ui.expandpath(path, path), None) - other = hg.repository(cmdutil.remoteui(_rd.repo, {}), source) - - modheads = _rd.repo.pull(other, heads=revs, force=True) - - if modheads: - hg.update(_rd.repo, 'tip') - - raise web.seeother('/') - - -class media: - def GET(self, fname): - if '..' in fname: - return '' - else: - with open(os.path.join(media_path, fname)) as f: - content = f.read() - return content - - -def load_interface(ui, repo, open=False): - global _rd - _rd = api.ReviewDatastore(ui, repo) - - sys.argv = sys.argv[:1] # Seriously, web.py? This is such a hack. - app = web.application(urls, globals()) - +def load_interface(ui, repo, open=False, port=8080): if open: import webbrowser - webbrowser.open(app.browser().url) + webbrowser.open('http://localhost:%d/' % port) + + cherrypy.quickstart(ReviewWebUI(api.ReviewDatastore(ui, repo))) + + - app.run() +# _rd = None +# urls = ( +# '/', 'index', +# '/media/([^/]*)', 'media', +# '/review/([\da-f]{12})/?', 'review', +# '/push/', 'push', +# '/pull/', 'pull', +# ) +# +# g = { +# 'node_short': short, +# 'basename': os.path.basename, +# 'md5': md5, +# 'email': email, +# 'templatefilters': templatefilters, +# } +# render = web.template.render(template_path, globals=g) +# +# LOG_PAGE_LEN = 25 +# +# def render_in_base(fn): +# def _fn(*args, **kwargs): +# title, content = fn(*args, **kwargs) +# return render.base(_rd, content, title) +# return _fn +# +# class index: +# @render_in_base +# def GET(self): +# rev_max = _rd.target['tip'].rev() +# rev_min = rev_max - LOG_PAGE_LEN if rev_max >= LOG_PAGE_LEN else 0 +# revs = (_rd.target[r] for r in xrange(rev_max, rev_min, -1)) +# return ('', render.index(_rd, revs)) +# +# +# class review: +# @render_in_base +# def GET(self, node_short): +# title = '/ %s:%s – %s' % ( +# _rd[node_short].target[node_short].rev(), +# node_short, +# _rd[node_short].target[node_short].description().splitlines()[0]) +# return (title, render.review(_rd, _rd[node_short])) +# +# def POST(self, node_short): +# i = web.input() +# body = i['body'] +# filename = i['filename'] if 'filename' in i else '' +# lines = i['lines'].split(',') if 'lines' in i else '' +# print filename, lines +# +# if body: +# rcset = _rd[node_short] +# rcset.add_comment(body, filename, lines) +# +# raise web.seeother('/review/%s/' % node_short) +# +# +# class push: +# def GET(self): +# path = web.input()['path'] +# dest, revs, checkout = hg.parseurl(_rd.repo.ui.expandpath(path, path), None) +# other = hg.repository(cmdutil.remoteui(_rd.repo, {}), dest) +# +# _rd.repo.push(other, True, revs=revs) +# +# raise web.seeother('/') +# +# +# class pull: +# def GET(self): +# path = web.input()['path'] +# source, revs, checkout = hg.parseurl(_rd.repo.ui.expandpath(path, path), None) +# other = hg.repository(cmdutil.remoteui(_rd.repo, {}), source) +# +# modheads = _rd.repo.pull(other, heads=revs, force=True) +# +# if modheads: +# hg.update(_rd.repo, 'tip') +# +# raise web.seeother('/') +# +# +# class media: +# def GET(self, fname): +# if '..' in fname: +# return '' +# else: +# with open(os.path.join(media_path, fname)) as f: +# content = f.read() +# return content +# +# +