Added Twitter to index page

This commit is contained in:
Armin Ronacher 2010-05-16 01:06:37 +02:00
parent 57fd968fcc
commit 39eb088afc
8 changed files with 197 additions and 35 deletions

View file

@ -34,3 +34,7 @@ app.register_module(snippets)
app.register_module(extensions) app.register_module(extensions)
from flask_website.database import User, db_session from flask_website.database import User, db_session
from flask_website import utils
app.jinja_env.filters['datetimeformat'] = utils.format_datetime
app.jinja_env.filters['timedeltaformat'] = utils.format_timedelta

View file

@ -26,6 +26,12 @@ blockquote { margin: 0; font-style: italic; color: #444; }
.message { background: #DEEBF3; color: #004B6B; padding: 5px 30px; .message { background: #DEEBF3; color: #004B6B; padding: 5px 30px;
margin: 10px -30px; } margin: 10px -30px; }
.actions { margin-top: 0; } .actions { margin-top: 0; }
.twitter:before { background: url(twitter.png) no-repeat; content: " ";
float: right; width: 64px; height: 64px;
margin: -25px 0 0 0; padding: 0 0 10px 10px; }
.twitter li { margin: 15px 0; line-height: 1.2; font-size: 15px; }
.twitter li:before { content: "♯"; padding-left: 4px; }
.twitter .meta, .twitter .meta a { color: #888; font-size: 13px; }
table { border: 1px solid black; border-collapse: collapse; table { border: 1px solid black; border-collapse: collapse;
margin: 15px 0; } margin: 15px 0; }
td, th { border: 1px solid black; padding: 4px 10px; td, th { border: 1px solid black; padding: 4px 10px;

BIN
flask_website/static/twitter.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -5,6 +5,7 @@
h1 { margin: 0 0 30px 0; background: url(/static/logo.png) no-repeat center; height: 165px; } h1 { margin: 0 0 30px 0; background: url(/static/logo.png) no-repeat center; height: 165px; }
h1 span, p.tagline { display: none; } h1 span, p.tagline { display: none; }
</style> </style>
<link href="{{ tweets.feed_url }}" rel="alternate" title="Flask on Twitter" type="application/atom+xml">
{% endblock %} {% endblock %}
{% block body_title %} {% block body_title %}
{{ super() }} {{ super() }}
@ -67,6 +68,22 @@ def hello():
create a new ticket or fork. If you just want to chat with fellow create a new ticket or fork. If you just want to chat with fellow
developers, go to <code>#pocoo</code> on irc.freenode.net. developers, go to <code>#pocoo</code> on irc.freenode.net.
{% if tweets %}
<h2>Recent Tweets</h2>
<div class=twitter>
<p>
What people say about Flask on <a href=http://twitter.com/>Twitter</a>:
<ul>
{%- for tweet in tweets %}
<li>
<a href="http://twitter.com/{{ tweet.user }}">{{ tweet.user }}</a>:
{{ tweet.text|urlize }}
<span class=meta>&mdash; {{ tweet.pub_date|timedeltaformat }} ago via {{ tweet.via }}</span>
{%- endfor %}
</ul>
</div>
{% endif %}
<a href="http://github.com/mitsuhiko/flask"><img style="position: fixed; top: 0; right: 0; border: 0;" <a href="http://github.com/mitsuhiko/flask"><img style="position: fixed; top: 0; right: 0; border: 0;"
src="http://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png" alt="Fork me on GitHub"></a> src="http://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png" alt="Fork me on GitHub"></a>
{% endblock %} {% endblock %}

View file

@ -25,7 +25,7 @@
<dt>From: <dt>From:
<dd class=from>{{ mail.author_name or mail.author_email }} <dd class=from>{{ mail.author_name or mail.author_email }}
<dt>Date: <dt>Date:
<dd>{{ mail.date.strftime('%Y-%m-%d @ %H:%M') }} <dd>{{ mail.date|datetimeformat }}
</dl> </dl>
<pre>{{ mail.rendered_text() }}</pre> <pre>{{ mail.rendered_text() }}</pre>
{% if mail.children %} {% if mail.children %}

91
flask_website/twitter.py Normal file
View file

@ -0,0 +1,91 @@
from __future__ import with_statement
import urllib2
import time
import threading
from flask import json, Markup
from werkzeug import url_encode, parse_date
class SearchResult(object):
def __init__(self, result):
self.text = Markup(result['text']).unescape()
self.user = result['from_user']
self.via = Markup(Markup(result['source']).unescape())
self.pub_date = parse_date(result['created_at'])
self.profile_image = result['profile_image_url']
self.type = result['metadata']['result_type']
self.retweets = result['metadata'].get('recent_retweets') or 0
class SearchQuery(object):
fetch_timeout = 10
def __init__(self, required=None, optional=None, limit=5, timeout=60, lang=None):
self.required = set(x.lower() for x in (required or ()))
self.optional = set(x.lower() for x in (optional or ()))
self.limit = limit
self.lang = lang
self.timeout = timeout
self._last_fetch = 0
self._last_scheduled_fetch = 0
self._cached = None
@property
def query(self):
def _quote_if(x):
if len(x.split()) != 1:
return u'"%s"' % x
return x
q = u' '.join(map(_quote_if, self.required))
q += u' ' + u' OR '.join(map(_quote_if, self.optional))
return q
@property
def feed_url(self):
return self.get_url(kind='atom')
def get_url(self, kind='json'):
return 'http://search.twitter.com/search.%s?%s' % (kind, url_encode({
'q': self.query,
'result_type': 'mixed',
'lang': self.lang
}))
def fetch(self):
def _accept(text):
text = text.lower()
for word in self.required:
if word not in text:
return False
for word in self.optional:
if word in text:
return True
return False
rv = json.load(urllib2.urlopen(self.get_url()))
return [SearchResult(x) for x in rv['results'] if
_accept(x['text'])]
@property
def up_to_date(self):
return time.time() < self._last_fetch + self.timeout
def _try_refresh(self):
if self.up_to_date:
return
if time.time() > self._last_scheduled_fetch + self.fetch_timeout:
self._last_scheduled_fetch = time.time()
threading.Thread(target=self._fetch_and_store).start()
def _fetch_and_store(self):
self._cached = self.fetch()
self._last_fetch = time.time()
def __len__(self):
self._try_refresh()
return len(self._cached or ())
def __iter__(self):
self._try_refresh()
for item in (self._cached or ())[:self.limit]:
yield item

View file

@ -1,5 +1,6 @@
import re import re
import creoleparser import creoleparser
from datetime import datetime, timedelta
from genshi import builder from genshi import builder
from functools import wraps from functools import wraps
from creoleparser.elements import PreBlock from creoleparser.elements import PreBlock
@ -17,6 +18,17 @@ pygments_formatter = HtmlFormatter(style=FlaskyStyle)
_ws_split_re = re.compile(r'(\s+)') _ws_split_re = re.compile(r'(\s+)')
TIMEDELTA_UNITS = (
('year', 3600 * 24 * 365),
('month', 3600 * 24 * 30),
('week', 3600 * 24 * 7),
('day', 3600 * 24),
('hour', 3600),
('minute', 60),
('second', 1)
)
class CodeBlock(PreBlock): class CodeBlock(PreBlock):
def __init__(self): def __init__(self):
@ -94,3 +106,29 @@ def requires_admin(f):
abort(401) abort(401)
return f(*args, **kwargs) return f(*args, **kwargs)
return requires_login(decorated_function) return requires_login(decorated_function)
def format_datetime(dt):
return dt.strftime('%Y-%m-%d @ %H:%M')
def format_timedelta(delta, granularity='second', threshold=.85):
if isinstance(delta, datetime):
delta = datetime.utcnow() - delta
if isinstance(delta, timedelta):
seconds = int((delta.days * 86400) + delta.seconds)
else:
seconds = delta
for unit, secs_per_unit in TIMEDELTA_UNITS:
value = abs(seconds) / secs_per_unit
if value >= threshold or unit == granularity:
if unit == granularity and value > 0:
value = max(1, value)
value = str(int(round(value)))
value += u' ' + unit
if value != 1:
value += u's'
return value
return u''

View file

@ -1,14 +1,20 @@
from flask import Module, render_template, session, redirect, url_for, \ from flask import Module, render_template, session, redirect, url_for, \
request, flash, g, Response request, flash, g, Response
from flask_website import oid from flask_website import oid, twitter
from flask_website.database import db_session, User from flask_website.database import db_session, User
general = Module(__name__) general = Module(__name__)
tweets = twitter.SearchQuery(required=['flask'],
optional=['code', 'web', 'python', 'py',
'pocoo', 'micro', 'mitsuhiko',
'framework', 'django', 'jinja',
'werkzeug', 'pylons'],
lang='en')
@general.route('/') @general.route('/')
def index(): def index():
return render_template('general/index.html') return render_template('general/index.html', tweets=tweets)
@general.route('/logout/') @general.route('/logout/')