From 63001a7279e6fa3a1cd0828d77420749934beb09 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Fri, 23 Jul 2010 13:03:29 +0100 Subject: [PATCH] More API --- flask_website/database.py | 22 +++++++++++++++++++++- flask_website/listings/extensions.py | 5 +++++ flask_website/listings/projects.py | 5 +++++ flask_website/twitter.py | 8 +++++++- flask_website/utils.py | 5 +++++ flask_website/views/community.py | 8 +++++++- flask_website/views/extensions.py | 11 ++++------- flask_website/views/mailinglist.py | 26 +++++++++++++++++++++++--- flask_website/views/snippets.py | 10 ++++++++-- 9 files changed, 85 insertions(+), 15 deletions(-) diff --git a/flask_website/database.py b/flask_website/database.py index da362879..5b3bd464 100644 --- a/flask_website/database.py +++ b/flask_website/database.py @@ -4,7 +4,7 @@ from sqlalchemy import create_engine, MetaData, Table, Column, Integer, \ from sqlalchemy.orm import scoped_session, sessionmaker, backref, relation from sqlalchemy.ext.declarative import declarative_base -from werkzeug import cached_property +from werkzeug import cached_property, http_date from flask import url_for from flask_website import config @@ -32,6 +32,9 @@ class User(Model): self.name = name self.openid = openid + def to_json(self): + return dict(name=self.name, is_admin=self.is_admin) + @property def is_admin(self): return self.openid in config.ADMINS @@ -53,6 +56,9 @@ class Category(Model): self.name = name self.slug = '-'.join(name.split()).lower() + def to_json(self): + return dict(name=self.name, slug=self.slug, count=self.count) + @cached_property def count(self): return self.snippets.count() @@ -81,6 +87,14 @@ class Snippet(Model): self.category = category self.pub_date = datetime.utcnow() + def to_json(self): + return dict(id=self.id, title=self.title, + body=unicode(self.rendered_body), + pub_date=http_date(self.pub_date), + comments=[c.to_json() for c in self.comments], + author=self.author.to_json(), + category=self.category.slug) + @property def url(self): return url_for('snippets.show', id=self.id) @@ -110,6 +124,12 @@ class Comment(Model): self.text = text self.pub_date = datetime.utcnow() + def to_json(self): + return dict(author=self.author.to_json(), + title=self.title, + pub_date=http_date(self.pub_date), + text=unicode(self.rendered_text)) + @property def rendered_text(self): from flask_website.utils import format_creole diff --git a/flask_website/listings/extensions.py b/flask_website/listings/extensions.py index 35fa72ac..3f0224a3 100644 --- a/flask_website/listings/extensions.py +++ b/flask_website/listings/extensions.py @@ -15,6 +15,11 @@ class Extension(object): self.docs = docs self.website = website + def to_json(self): + rv = vars(self).copy() + rv['description'] = unicode(rv['description']) + return rv + @property def pypi(self): return 'http://pypi.python.org/pypi/%s' % url_quote(self.name) diff --git a/flask_website/listings/projects.py b/flask_website/listings/projects.py index 92bb5238..6e011b39 100644 --- a/flask_website/listings/projects.py +++ b/flask_website/listings/projects.py @@ -20,6 +20,11 @@ class Project(object): if self.source is not None: return urlparse(self.source)[1] + def to_json(self): + rv = vars(self).copy() + rv['description'] = unicode(rv['description']) + return rv + projects = { 'websites': [ diff --git a/flask_website/twitter.py b/flask_website/twitter.py index d5d915a2..5df4ac5d 100644 --- a/flask_website/twitter.py +++ b/flask_website/twitter.py @@ -3,7 +3,7 @@ import urllib2 import time import threading from flask import json, Markup -from werkzeug import url_encode, parse_date +from werkzeug import url_encode, parse_date, http_date class SearchResult(object): @@ -17,6 +17,12 @@ class SearchResult(object): self.type = result['metadata']['result_type'] self.retweets = result['metadata'].get('recent_retweets') or 0 + def to_json(self): + rv = vars(self).copy() + rv['pub_date'] = http_date(rv['pub_date']) + rv['via'] = unicode(rv['via']) + return rv + class SearchQuery(object): fetch_timeout = 10 diff --git a/flask_website/utils.py b/flask_website/utils.py index 56d0750a..e2553c6c 100644 --- a/flask_website/utils.py +++ b/flask_website/utils.py @@ -91,6 +91,11 @@ def split_lines_wrapping(text, width=74, threshold=82): return result +def request_wants_json(): + return request.accept_mimetypes \ + .best_match(['application/json', 'text/html']) == 'application/json' + + def requires_login(f): @wraps(f) def decorated_function(*args, **kwargs): diff --git a/flask_website/views/community.py b/flask_website/views/community.py index 5e9c5e0c..f4aeb42e 100644 --- a/flask_website/views/community.py +++ b/flask_website/views/community.py @@ -1,5 +1,6 @@ -from flask import Module, render_template +from flask import Module, render_template, jsonify from flask_website.twitter import flask_tweets +from flask_website.utils import request_wants_json from flask_website.listings.projects import projects mod = Module(__name__, url_prefix='/community') @@ -17,6 +18,8 @@ def irc(): @mod.route('/twitter/') def twitter(): + if request_wants_json(): + return jsonify(tweets=[t.to_json() for t in flask_tweets]) return render_template('community/twitter.html', tweets=flask_tweets) @@ -27,6 +30,9 @@ def badges(): @mod.route('/poweredby/') def poweredby(): + if request_wants_json(): + return jsonify((k, [p.to_json() for p in v]) + for k, v in projects.iteritems()) return render_template('community/poweredby.html', projects=projects) diff --git a/flask_website/views/extensions.py b/flask_website/views/extensions.py index 5b92919b..91757de5 100644 --- a/flask_website/views/extensions.py +++ b/flask_website/views/extensions.py @@ -1,17 +1,14 @@ -from flask import Module, render_template, jsonify, request +from flask import Module, render_template, jsonify +from flask_website.utils import request_wants_json from flask_website.listings.extensions import extensions mod = Module(__name__, url_prefix='/extensions') -def wants_json(): - return request.accept_mimetypes \ - .best_match(['application/json', 'text/html']) == 'application/json' - @mod.route('/') def index(): - if wants_json(): - return jsonify(extensions=map(vars, extensions)) + if request_wants_json(): + return jsonify(extensions=[ext.to_json() for ext in extensions]) return render_template('extensions/index.html', extensions=extensions) diff --git a/flask_website/views/mailinglist.py b/flask_website/views/mailinglist.py index 5fbd5003..5476beff 100644 --- a/flask_website/views/mailinglist.py +++ b/flask_website/views/mailinglist.py @@ -2,10 +2,11 @@ from __future__ import with_statement import os from math import ceil from hashlib import md5 -from werkzeug import parse_date +from werkzeug import parse_date, http_date from jinja2.utils import urlize -from flask import Module, render_template, json, url_for, abort, Markup -from flask_website.utils import split_lines_wrapping +from flask import Module, render_template, json, url_for, abort, Markup, \ + jsonify +from flask_website.utils import split_lines_wrapping, request_wants_json from flask_website import config mod = Module(__name__, url_prefix='/mailinglist') @@ -36,6 +37,12 @@ class Mail(object): result.append(urlize(line)) return Markup(u'\n'.join(result)) + def to_json(self): + rv = vars(self).copy() + rv['date'] = http_date(rv['date']) + rv['children'] = [c.to_json() for c in rv['children']] + return rv + @property def id(self): return md5(self.msgid.encode('utf-8')).hexdigest() @@ -72,6 +79,13 @@ class Thread(object): month=self.date.month, day=self.date.day, slug=self.slug) + def to_json(self): + rv = vars(self).copy() + rv['date'] = http_date(rv['date']) + if 'root' in rv: + rv['root'] = rv['root'].to_json() + return rv + @mod.route('/') def index(): @@ -87,6 +101,10 @@ def archive(page): if page != 1 and not threads: abort(404) page_count = int(ceil(len(all_threads) // float(config.THREADS_PER_PAGE))) + if request_wants_json(): + return jsonify(offset=offset, + total=len(all_threads), + threads=[x.to_json() for x in threads]) return render_template('mailinglist/archive.html', page_count=page_count, page=page, threads=threads) @@ -96,4 +114,6 @@ def show_thread(year, month, day, slug): thread = Thread.get(year, month, day, slug) if thread is None: abort(404) + if request_wants_json(): + return jsonify(thread=thread.to_json()) return render_template('mailinglist/show_thread.html', thread=thread) diff --git a/flask_website/views/snippets.py b/flask_website/views/snippets.py index 4c13849e..d5fb2d45 100644 --- a/flask_website/views/snippets.py +++ b/flask_website/views/snippets.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- from urlparse import urljoin from flask import Module, render_template, request, flash, abort, redirect, \ - g, url_for + g, url_for, jsonify from werkzeug.contrib.atom import AtomFeed -from flask_website.utils import requires_login, requires_admin, format_creole +from flask_website.utils import requires_login, requires_admin, \ + format_creole, request_wants_json from flask_website.database import Category, Snippet, Comment, db_session mod = Module(__name__, url_prefix='/snippets') @@ -52,6 +53,8 @@ def show(id): snippet = Snippet.query.get(id) if snippet is None: abort(404) + if request_wants_json(): + return jsonify(snippet=snippet.to_json()) if request.method == 'POST': title = request.form['title'] text = request.form['text'] @@ -140,6 +143,9 @@ def category(slug): if category is None: abort(404) snippets = category.snippets.order_by(Snippet.title).all() + if request_wants_json(): + return jsonify(category=category.to_json(), + snippets=[s.id for s in snippets]) return render_template('snippets/category.html', category=category, snippets=snippets)