<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><title>Nuno Mariz Weblog - Latest Posts</title><link>http://mariz.org/blog/</link><description>Latest Posts</description><language>en-us</language><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/mariz" type="application/rss+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item><title>Getting the client IP address behind a proxy in Apache
</title><link>http://feedproxy.google.com/~r/mariz/~3/v4ol2FQN_sw/</link><description>Typically in my &lt;a href="http://www.djangoproject.com"&gt;Django&lt;/a&gt; projects deployments I use &lt;a href="http://nginx.net/"&gt;Nginx&lt;/a&gt; as a front-end web server to handle static content while proxying all other requests to &lt;a href="http://httpd.apache.org/"&gt;Apache&lt;/a&gt;.&lt;br /&gt;
When the request arrives to &lt;a href="http://httpd.apache.org/"&gt;Apache&lt;/a&gt;, the client IP address is &lt;code&gt;127.0.0.1&lt;/code&gt;. We need to configure &lt;a href="http://httpd.apache.org/"&gt;Apache&lt;/a&gt; to accept the IP address from X-Real-IP or X-Forwarded-For headers set by &lt;a href="http://nginx.net/"&gt;Nginx&lt;/a&gt;.&lt;br /&gt;
To solve this problem I use &lt;code&gt;&lt;a href="http://stderr.net/apache/rpaf/"&gt;mod_rpaf&lt;/a&gt;&lt;/code&gt; that does exactly the opposite of &lt;code&gt;&lt;a href="http://develooper.com/code/mpaf/"&gt;mod_proxy_add_forward&lt;/a&gt;&lt;/code&gt;.

&lt;p&gt;In my &lt;a href="http://nginx.net/"&gt;Nginx&lt;/a&gt; virtualhost configuration I have something like:&lt;/p&gt;

&lt;pre class="code"&gt;
server {
    ...
    location  / {
        proxy_pass        http://127.0.0.1:8080;
        proxy_set_header  Host             $http_host;
        proxy_set_header  X-Real-IP        $remote_addr;
        proxy_set_header  X-Forwarded-For  $proxy_add_x_forwarded_for;
        ...
    }
    ...
}
&lt;/pre&gt;

&lt;p&gt;This also applies if your are using a different webserver as front-end such as  &lt;a href="http://www.lighttpd.net"&gt;Lighttpd&lt;/a&gt; or another instance of &lt;a href="http://httpd.apache.org/"&gt;Apache&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/mariz/~4/v4ol2FQN_sw" height="1" width="1"/&gt;</description><pubDate>Wed, 20 May 2009 08:45:21 -0000</pubDate><guid isPermaLink="false">http://mariz.org/blog/2009/may/20/getting-client-ip-address-behind-proxy-apache/</guid><feedburner:origLink>http://mariz.org/blog/2009/may/20/getting-client-ip-address-behind-proxy-apache/</feedburner:origLink></item><item><title>Portuguese Python User Group
</title><link>http://feedproxy.google.com/~r/mariz/~3/uiUsm0APzqI/</link><description>&lt;img src="http://mariz.org/media/images/posts/python_pt-logo.gif" alt="Python Portugal" /&gt;&lt;br /&gt;
I've created a Python User Group in Portugal. The idea is to join people who enjoy programming in Python.&lt;br /&gt;
No membership is required to participate, experienced programmers and absolute beginners are both welcome!&lt;br /&gt;
You can visit the &lt;a href="http://python.pt"&gt;site&lt;/a&gt; or follow us on &lt;a href="http://twitter.com/python_pt"&gt;Twitter&lt;/a&gt;.&lt;img src="http://feeds.feedburner.com/~r/mariz/~4/uiUsm0APzqI" height="1" width="1"/&gt;</description><pubDate>Mon, 18 May 2009 07:45:53 -0000</pubDate><guid isPermaLink="false">http://mariz.org/blog/2009/may/18/portuguese-python-user-group/</guid><feedburner:origLink>http://mariz.org/blog/2009/may/18/portuguese-python-user-group/</feedburner:origLink></item><item><title>Django 1.0 released!
</title><link>http://feedproxy.google.com/~r/mariz/~3/9mpiVJH3Em8/</link><description>Yes, is finally &lt;a href="http://www.djangoproject.com/weblog/2008/sep/03/1/"&gt;here&lt;/a&gt;.&lt;img src="http://feeds.feedburner.com/~r/mariz/~4/9mpiVJH3Em8" height="1" width="1"/&gt;</description><pubDate>Thu, 04 Sep 2008 00:23:26 -0000</pubDate><guid isPermaLink="false">http://mariz.org/blog/2008/sep/04/django-10-released/</guid><feedburner:origLink>http://mariz.org/blog/2008/sep/04/django-10-released/</feedburner:origLink></item><item><title>Portuguese translation of Django 1.0
</title><link>http://feedproxy.google.com/~r/mariz/~3/J_RNSeDWClU/</link><description>I've just submitted a &lt;a href="http://code.djangoproject.com/ticket/8685"&gt;patch&lt;/a&gt; for the Portuguese translation.&lt;br /&gt;
If you are a &lt;a href="http://www.djangoproject.com"&gt;Django&lt;/a&gt; user and know Portuguese, please take a look to the translation strings and submit any fix before the &lt;a href="http://code.djangoproject.com/wiki/VersionOneRoadmap#schedule"&gt;final 1.0 release&lt;/a&gt;.&lt;br /&gt;
Meanwhile, you can &lt;a href="http://mariz.org/downloads/file/4/"&gt;download&lt;/a&gt; the unofficial version in the &lt;a href="http://mariz.org/downloads/"&gt;downloads section&lt;/a&gt;.&lt;br /&gt;
&lt;strong&gt;Update:&lt;/strong&gt; The patch is already in SVN.&lt;img src="http://feeds.feedburner.com/~r/mariz/~4/J_RNSeDWClU" height="1" width="1"/&gt;</description><pubDate>Fri, 29 Aug 2008 14:09:06 -0000</pubDate><guid isPermaLink="false">http://mariz.org/blog/2008/aug/29/portuguese-translation-django-10/</guid><feedburner:origLink>http://mariz.org/blog/2008/aug/29/portuguese-translation-django-10/</feedburner:origLink></item><item><title>Rafael Barbosa Mariz
</title><link>http://feedproxy.google.com/~r/mariz/~3/r-8NOFpW6IM/</link><description>&lt;img src="http://mariz.org/media/images/posts/rafael.jpg"  alt="Rafael" /&gt;&lt;br /&gt;
Rafael Barbosa Mariz was born at 00h48, July 26, 2008. He weighed 3.580 Kg and was 50 cm long.&lt;br /&gt;
Rafael and &lt;a href="http://wedding.mariz.org"&gt;Marta&lt;/a&gt; are doing well.&lt;img src="http://feeds.feedburner.com/~r/mariz/~4/r-8NOFpW6IM" height="1" width="1"/&gt;</description><pubDate>Sat, 26 Jul 2008 04:48:33 -0000</pubDate><guid isPermaLink="false">http://mariz.org/blog/2008/jul/26/rafael-barbosa-mariz/</guid><feedburner:origLink>http://mariz.org/blog/2008/jul/26/rafael-barbosa-mariz/</feedburner:origLink></item><item><title>Django 1.0 roadmap and timeline
</title><link>http://feedproxy.google.com/~r/mariz/~3/oybzCV6AXLA/</link><description>Finally we have a Django 1.0 roadmap and timeline. &lt;a href="http://www.jacobian.org/"&gt;Jacob Kaplan-Moss&lt;/a&gt; posted today on &lt;a href="http://groups.google.com/group/django-developers/browse_thread/thread/5ce124e7526dad"&gt;Django devolopers&lt;/a&gt;.&lt;br /&gt;
I must say this roadmap seems very realistic and the only thing that I will miss is the aggregation support, that is promised to be released in a future version, maybe in 1.1.&lt;img src="http://feeds.feedburner.com/~r/mariz/~4/oybzCV6AXLA" height="1" width="1"/&gt;</description><pubDate>Thu, 12 Jun 2008 08:38:56 -0000</pubDate><guid isPermaLink="false">http://mariz.org/blog/2008/jun/12/django-1-roadmap-and-timeline/</guid><feedburner:origLink>http://mariz.org/blog/2008/jun/12/django-1-roadmap-and-timeline/</feedburner:origLink></item><item><title>Internet lifestream with Django
</title><link>http://feedproxy.google.com/~r/mariz/~3/YlkNh9gxTSA/</link><description>My goal was to archive and display my internet lifestream. My first approach was writing a client for each API of the social networks that I'm in.&lt;br /&gt;
This turned out to be a complete waste of time and effort. All that I needed after all was a &lt;a href="http://friendfeed.com"&gt;FriendFeed&lt;/a&gt; account that would centralize all my feeds.&lt;br /&gt;
&lt;p&gt;Archiving and displaying your entries with Django is quite simple.&lt;br /&gt;
First of all, you need to download the &lt;a href="http://code.google.com/p/friendfeed-api/downloads/list"&gt;Python FriendFeed API&lt;/a&gt; client. Then start a new application in your project, lets call it &lt;span class="code"&gt;lifestream&lt;/span&gt;:&lt;/p&gt;

&lt;pre class="code"&gt;./manage.py startapp lifestream&lt;/pre&gt;

&lt;p&gt;On the &lt;span class="code"&gt;settings.py&lt;/span&gt; add the &lt;span class="code"&gt;lifestream&lt;/span&gt; project to the INSTALLED_APPS and a variable to store your FriendFeed username:

&lt;pre class="code"&gt;FRIENDFEED_USERNAME = 'your_username'&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;span class="code"&gt;models.py&lt;/span&gt; add a model named &lt;span class="code"&gt;Entry&lt;/span&gt;:

&lt;pre class="code"&gt;from django.db import models

class Entry(models.Model):
    id = models.CharField(max_length=255, primary_key=True)
    service_id = models.CharField(max_length=50, null=True, blank=True)
    service_name = models.CharField(max_length=50, null=True, blank=True)
    service_icon = models.URLField(max_length=255, verify_exists=False, null=True, blank=True)
    service_profile = models.URLField(max_length=255, verify_exists=False, null=True, blank=True)
    title = models.CharField(max_length=255, null=True, blank=True)
    link = models.URLField(max_length=255, verify_exists=False, null=True, blank=True)
    updated = models.DateTimeField(null=True, blank=True)
    published = models.DateTimeField(null=True, blank=True)
    media_title = models.CharField(max_length=255, null=True, blank=True)
    media_link = models.URLField(max_length=255, verify_exists=False, null=True, blank=True)
    media_thumbnail = models.URLField(max_length=255, verify_exists=False, null=True, blank=True)
    created = models.DateTimeField(auto_now_add=True)

    def __unicode__(self):
        return self.title

    class Meta:
        ordering = ['-published']
        verbose_name = 'Entry'
        verbose_name_plural = 'Entries'

    class Admin:
        list_display = ['title', 'service_name', 'published']
        list_filter = ['service_name']
        date_hierarchy = 'published'&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;Create an &lt;span class="code"&gt;url.py&lt;/span&gt; on the &lt;span class="code"&gt;lifestream&lt;/span&gt; folder:

&lt;pre class="code"&gt;from django.conf.urls.defaults import *
from lifestream.models import Entry
 
entry_list_dict = {
    'queryset' : Entry.objects.all(),
    'paginate_by' : 30,
}

urlpatterns = patterns('',   
    (r'^$', 'django.views.generic.list_detail.object_list', entry_list_dict),
)&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;As you can see, I've used a &lt;a href="http://www.djangoproject.com/documentation/generic_views/"&gt;generic view&lt;/a&gt;. You can also use the &lt;a href="http://www.djangoproject.com/documentation/generic_views/#date-based-generic-views"&gt;date based generic views&lt;/a&gt; and &lt;a href="http://www.djangoproject.com/documentation/generic_views/#notes-on-pagination"&gt;pagination&lt;/a&gt; to build an archive like &lt;a href="http://mariz.org/lifestream/"&gt;mine&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Add to your project root &lt;span class="code"&gt;urls.py&lt;/span&gt;:

&lt;pre class="code"&gt;(r'^lifestream/', include('lifestream.urls'))&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;Create a template &lt;span class="code"&gt;lifestream/entry_list.html&lt;/span&gt;:

&lt;pre class="code"&gt;{% for entry in object_list %}
&amp;lt;div class="source"&amp;gt;
  &amp;lt;a href="{{ entry.service_profile }}" title="{{ entry.service_name }}"&amp;gt;&amp;lt;img src="{{ entry.service_icon }}" alt="{{ entry.service_name }}" alt="{{ entry.service_name }}" /&amp;gt;&amp;lt;/a&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class="details"&amp;gt;
  &amp;lt;ul&amp;gt;
    &amp;lt;li&amp;gt;&amp;lt;a href="{{ entry.link }}"&amp;gt;{{ entry.title }}&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
    &amp;lt;li&amp;gt;{{ entry.published|timesince }} ago&amp;lt;/li&amp;gt;
    {% if entry.media_thumbnail %}&amp;lt;li&amp;gt;&amp;lt;a href="{{ entry.media_link }}"&amp;gt;&amp;lt;img src="{{ entry.media_thumbnail }}" alt="{{ entry.media_title }}" /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;{% endif %}
  &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
{% endfor %}&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;Finally, create a script to synchronize your feeds:&lt;br /&gt;
&lt;pre class="code"&gt;#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import os

ROOT_PATH = os.path.realpath(os.path.dirname(__file__))
PROJECT_PATH, PROJECT_DIR = os.path.split(ROOT_PATH)

sys.path.insert(0, ROOT_PATH)
sys.path.insert(1, PROJECT_PATH)

os.environ['DJANGO_SETTINGS_MODULE'] = '%s.settings' % PROJECT_DIR

from friendfeed import FriendFeed
from django.conf import settings
from lifestream.models import Entry

ff = FriendFeed()
feed = ff.fetch_user_feed(settings.FRIENDFEED_USERNAME)

for e in feed.get('entries'):
    entry, created = Entry.objects.get_or_create(id=e.get('id'))
    if created:
        service = e.get('service')
        entry.service_id = service.get('id')
        entry.service_name = service.get('name')
        entry.service_icon = service.get('iconUrl')
        entry.service_profile = service.get('profileUrl')
        entry.title = e.get('title')
        entry.link = e.get('link')
        entry.updated = e.get('updated')
        entry.published = e.get('published')
        media = e.get('media')
        if media:
            entry.media_title = media[0].get('title')
            entry.media_link = media[0].get('player') or entry.link
            thumbnails = media[0].get('thumbnails')
            entry.media_thumbnail = thumbnails[0].get('url')
        entry.save()&lt;/pre&gt;&lt;/p&gt; 

&lt;p&gt;If you want, you can add a job in your &lt;span class="code"&gt;crontab&lt;/span&gt;:
&lt;pre class="code"&gt;# synchronize every 15 mins
*/15 * * * *   root   /path/to/your/application/lifestream_cron.py&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;See my &lt;a href="http://mariz.org/lifestream/"&gt;lifestream&lt;/a&gt; as the working example.&lt;/p&gt;

&lt;p&gt;&lt;span class="bold"&gt;UPDATE:&lt;/span&gt; &lt;a href="http://friendfeed.com/"&gt;Friendfeed&lt;/a&gt; sends the time in UTC, if you want to use your timezone you have do some hacking:&lt;/p&gt;
&lt;p&gt;Install &lt;span class="code"&gt;&lt;a href="http://pytz.sourceforge.net/"&gt;pytz&lt;/a&gt;&lt;/span&gt;:&lt;/p&gt;
&lt;pre class="code"&gt;easy_install pytz&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;Import and assign your timezone to a variable:&lt;/p&gt;
&lt;pre class="code"&gt;import pytz
tz = pytz.timezone(settings.TIME_ZONE)&lt;/pre&gt;
&lt;p&gt;And replace &lt;span class="code"&gt;entry.updated&lt;/span&gt; and &lt;span class="code"&gt;entry.published&lt;/span&gt; with:
&lt;pre class="code"&gt;updated = e.get('updated')
updated = updated.replace(tzinfo=pytz.utc).astimezone(tz)
published = e.get('published')
published = published.replace(tzinfo=pytz.utc).astimezone(tz)
if settings.DATABASE_ENGINE == 'mysql': # http://code.djangoproject.com/ticket/5304
    updated = updated.replace(tzinfo=None)
    published = published.replace(tzinfo=None)
entry.updated = updated
entry.published = published&lt;/pre&gt;
&lt;p&gt;Thanks to &lt;a href="http://ckelly.net"&gt;Chris Kelly&lt;/a&gt; that send me an email reporting this.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/mariz/~4/YlkNh9gxTSA" height="1" width="1"/&gt;</description><pubDate>Fri, 04 Apr 2008 00:30:50 -0000</pubDate><guid isPermaLink="false">http://mariz.org/blog/2008/apr/04/internet-lifestream-with-django/</guid><feedburner:origLink>http://mariz.org/blog/2008/apr/04/internet-lifestream-with-django/</feedburner:origLink></item><item><title>Django error notification with jabber
</title><link>http://feedproxy.google.com/~r/mariz/~3/GqK_EJrnqCg/</link><description>Django has a code error notifications mechanism when a view raises an exception. It will email the people in &lt;span class="code"&gt;ADMIN&lt;/span&gt; tuple(&lt;a href="http://www.djangoproject.com/documentation/settings/#admins" title="Django Settings"&gt;settings documentation&lt;/a&gt;) in &lt;span class="code"&gt;settings.py&lt;/span&gt; with the full exception information and displays the default 500.html template.&lt;br /&gt;
This only happens when &lt;span class="code"&gt;DEBUG=False&lt;/span&gt; in the &lt;span class="code"&gt;settings.py&lt;/span&gt;.

&lt;p&gt;
It's possible to set a handle that change this behavior with a &lt;span class="code"&gt;handler500&lt;/span&gt; variable in the root &lt;span class="code"&gt;urls.py&lt;/span&gt;.&lt;br /&gt;
So, we can easily write a simple view that sends an error notification to our &lt;a href="http://www.jabber.org/" title="Jabber"&gt;jabber&lt;/a&gt; account.
&lt;/p&gt;

&lt;p&gt;
First of all, you need to install &lt;span class="code"&gt;&lt;a href="http://xmpppy.sourceforge.net/" title="xmpppy"&gt;xmpppy&lt;/a&gt;&lt;/span&gt; and &lt;span class="code"&gt;&lt;a href="http://www.dnspython.org/" title="dnspython"&gt;dnspython&lt;/a&gt;&lt;/span&gt;:
&lt;pre class="code"&gt;$ easy_install xmpppy
$ easy_install dnspython&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;
Add to &lt;span class="code"&gt;settings.py&lt;/span&gt; the jabber parameters such as the jabber id, password, recipient, etc.:
&lt;pre class="code"&gt;JABBER_ERROR_NOTIFICATION = True
JABBER_ID = 'your_jabberid@jabberdomain.com'
JABBER_PASSWORD = 'your_jabber_password'
JABBER_RECIPIENT = 'recipient@jabberdomain.com'
JABBER_ERROR_TEXT = 'An error occurred in "Project Name", please check your email.'&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;
Start a new app named &lt;span class="code"&gt;errors&lt;/span&gt; or something else and add it to the &lt;span class="code"&gt;INSTALLED_APPS&lt;/span&gt; tuple in the &lt;span class="code"&gt;settings.py&lt;/span&gt;:
&lt;pre class="code"&gt;python manage.py startapp errors&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;

Add a &lt;span class="code"&gt;handler500&lt;/span&gt; variable with the view in the root &lt;span class="code"&gt;urls.py&lt;/span&gt;:
&lt;pre class="code"&gt;handler500 = 'errors.views.server_error_jabber'&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;
Finally add the view in &lt;span class="code"&gt;errors.views&lt;/span&gt; that sends a jabber notification and returns a 500 error page:
&lt;pre class="code"&gt;
from django.views.defaults import server_error
from django.conf import settings
import xmpp, time

def server_error_jabber(request, template_name='500.html'):
   if settings.JABBER_ERROR_NOTIFICATION:
       jid = xmpp.protocol.JID(settings.JABBER_ID)
       cl = xmpp.Client(jid.getDomain(), debug=[])
       conn = cl.connect()
       if conn:
           auth = cl.auth(jid.getNode(), settings.JABBER_PASSWORD, resource=jid.getResource())
           if auth:
               id = cl.send(xmpp.protocol.Message(settings.JABBER_RECIPIENT, settings.JABBER_ERROR_TEXT))
               # Some older servers will not send the message if you disconnect immediately after sending
               time.sleep(1)
   return server_error(request, template_name)
&lt;/pre&gt;
&lt;/p&gt;
&lt;strong&gt;NOTE:&lt;/strong&gt; Don't forget to set &lt;span class="code"&gt;DEBUG=False&lt;/span&gt; in the &lt;span class="code"&gt;settings.py&lt;/span&gt;.&lt;img src="http://feeds.feedburner.com/~r/mariz/~4/GqK_EJrnqCg" height="1" width="1"/&gt;</description><pubDate>Fri, 07 Mar 2008 13:26:46 -0000</pubDate><guid isPermaLink="false">http://mariz.org/blog/2008/mar/07/django-error-notification-with-jabber/</guid><feedburner:origLink>http://mariz.org/blog/2008/mar/07/django-error-notification-with-jabber/</feedburner:origLink></item><item><title>I'm addicted
</title><link>http://feedproxy.google.com/~r/mariz/~3/YFbYu4C2qdU/</link><description>&lt;a href="http://nikeplus.nike.com/nikeplus/" tittle="Nike Plus"&gt;&lt;img src="http://mariz.org/media/images/posts/nikeplus.gif" alt="Nike Plus"/&gt;&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/mariz/~4/YFbYu4C2qdU" height="1" width="1"/&gt;</description><pubDate>Thu, 28 Feb 2008 14:46:23 -0000</pubDate><guid isPermaLink="false">http://mariz.org/blog/2008/feb/28/im-addicted/</guid><feedburner:origLink>http://mariz.org/blog/2008/feb/28/im-addicted/</feedburner:origLink></item><item><title>Twitter user timeline with a Django templatetag
</title><link>http://feedproxy.google.com/~r/mariz/~3/8sjfMlOnU-8/</link><description>&lt;a href="http://www.twitter.com" tittle="Twitter"&gt;&lt;img src="http://mariz.org/media/images/posts/twitter.gif" alt="Twitter"/&gt;&lt;/a&gt;&lt;br /&gt;
A simple &lt;a href="http://www.djangoproject.com/documentation/templates/"&gt;templatetag&lt;/a&gt; for adding to the template context a variable with the user timeline from &lt;a href="http://www.twitter.com"&gt;Twitter&lt;/a&gt;.&lt;br /&gt;
It uses the &lt;span class="code"&gt;CachedContextUpdatingNode&lt;/span&gt; &lt;a href="http://www.djangosnippets.org/snippets/223/"&gt;snippet&lt;/a&gt;
 for caching from &lt;a href="http://www.jacobian.org"&gt;Jacob Kaplan-Moss&lt;/a&gt;.&lt;br /&gt;
The reason that is necessary to cache content is because &lt;a href="http://www.twitter.com" tittle="Twitter"&gt;Twitter&lt;/a&gt; limits the number of accesses to the &lt;a href="http://groups.google.com/group/twitter-development-talk/web/api-documentation"&gt;API&lt;/a&gt;.&lt;br /&gt;
This only works if the &lt;a href="http://www.djangoproject.com/documentation/cache/"&gt;cache&lt;/a&gt; is enabled on your &lt;span class="code"&gt;settings.py&lt;/span&gt;.
&lt;pre class="code"&gt;class TwitterNode(CachedContextUpdatingNode):

    cache_timeout = 1800 # 30 Minutes, maybe you want to change this
    
    def __init__(self, username, varname):
        self.username = username
        self.varname = varname

    def make_datetime(self, created_at):
        return datetime.fromtimestamp(mktime(strptime(created_at, '%a %b %d %H:%M:%S +0000 %Y')))

    def get_cache_key(self, context):
        return 'twitter_user_timeline_cache'

    def get_content(self, context):
        try:
            response = urllib.urlopen('http://twitter.com/statuses/user_timeline/%s.json' % self.username).read()
            json = simplejson.loads(response)
        except:
            return {self.varname : None}
        for i in range(len(json)):
            json[i]['created_at'] = self.make_datetime(json[i]['created_at'])
        return {self.varname : json}
    
@register.tag
def twitter_user_timeline(parser, token):
    bits = token.contents.split()
    if len(bits) != 4:
        raise TemplateSyntaxError, "twitter_user_timeline tag takes exactly three arguments"
    if bits[2] != 'as':
        raise TemplateSyntaxError, "second argument to twitter_user_timeline tag must be 'as'"
    return TwitterNode(bits[1], bits[3])&lt;/pre&gt;
Usage:
&lt;pre class="code"&gt;{% twitter_user_timeline username as twitter_entries %}
{% if twitter_entries %}
  {% for entry in twitter_entries %}
  {{ entry.created_at|date:"d M Y H:i" }} - {{ entry.text }}
  {% endfor %}
{% endif %}&lt;/pre&gt;
Use the &lt;a href="http://mariz.org/downloads/file/3/"&gt;source&lt;/a&gt;, Luke.&lt;img src="http://feeds.feedburner.com/~r/mariz/~4/8sjfMlOnU-8" height="1" width="1"/&gt;</description><pubDate>Mon, 11 Feb 2008 10:45:44 -0000</pubDate><guid isPermaLink="false">http://mariz.org/blog/2008/feb/11/twitter-user-timeline-django-templatetag/</guid><feedburner:origLink>http://mariz.org/blog/2008/feb/11/twitter-user-timeline-django-templatetag/</feedburner:origLink></item><item><title>My best project ever!
</title><link>http://feedproxy.google.com/~r/mariz/~3/ZbVp523dy4Y/</link><description>&lt;a href="http://mariz.org/media/images/posts/baby_1.jpg"&gt;&lt;img src="http://mariz.org/media/images/posts/baby_1_thumbnail.gif" alt="Baby" /&gt;&lt;/a&gt; &lt;a href="http://mariz.org/media/images/posts/baby_2.jpg"&gt;&lt;img src="http://mariz.org/media/images/posts/baby_2_thumbnail.gif" alt="Baby" /&gt;&lt;/a&gt;&lt;br /&gt;
The project is still under development. My &lt;a href="http://wedding.mariz.org"&gt;co-worker&lt;/a&gt; is doing her best to provide the most beautiful layout.&lt;img src="http://feeds.feedburner.com/~r/mariz/~4/ZbVp523dy4Y" height="1" width="1"/&gt;</description><pubDate>Mon, 14 Jan 2008 23:14:39 -0000</pubDate><guid isPermaLink="false">http://mariz.org/blog/2008/jan/14/my-best-project-ever/</guid><feedburner:origLink>http://mariz.org/blog/2008/jan/14/my-best-project-ever/</feedburner:origLink></item><item><title>Fullread updates
</title><link>http://feedproxy.google.com/~r/mariz/~3/Tq2E9VQD37w/</link><description>New features on &lt;a href="http://fullread.com"&gt;Fullread&lt;/a&gt;:
&lt;ul class="squares"&gt;
  &lt;li&gt;Feeds in the latest and user bookmarks.        
        &lt;ul class="circles"&gt;
          &lt;li&gt;&lt;span class="code"&gt;http://fullread.com/feeds/latest/[rss/atom]/&lt;/span&gt; (By latest - all users)&lt;/li&gt;
          &lt;li&gt;&lt;span class="code"&gt;http://fullread.com/feeds/[username]/[rss/atom]/&lt;/span&gt; (By user)&lt;/li&gt;
        &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;List of users that bookmarked the same URL.&lt;/li&gt;
  &lt;li&gt;Multiple bookmarking service support.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span class="bold"&gt;NOTE:&lt;/span&gt; If you are using a bookmarking service that is not available, please let me know: &lt;a href="http://fullread.com/contacts/?subject=Bookmarking%20service%20support"&gt;http://fullread.com/contacts/?subject=Bookmarking%20service%20support&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/mariz/~4/Tq2E9VQD37w" height="1" width="1"/&gt;</description><pubDate>Thu, 10 Jan 2008 10:34:34 -0000</pubDate><guid isPermaLink="false">http://mariz.org/blog/2008/jan/10/fullread-updates/</guid><feedburner:origLink>http://mariz.org/blog/2008/jan/10/fullread-updates/</feedburner:origLink></item><item><title>A quote from Theo Schlossnagle
</title><link>http://feedproxy.google.com/~r/mariz/~3/JlT-JRUqPZo/</link><description>&lt;blockquote&gt;The language is mostly irrelevant. I can’t count the number of times I’ve hear people say "Java doesn’t scale" or "PHP doesn’t scale". No languages scales; solutions scale.&lt;/blockquote&gt;
Theo Schlossnagle in "Scalable Internet Architectures"&lt;img src="http://feeds.feedburner.com/~r/mariz/~4/JlT-JRUqPZo" height="1" width="1"/&gt;</description><pubDate>Wed, 09 Jan 2008 14:30:32 -0000</pubDate><guid isPermaLink="false">http://mariz.org/blog/2008/jan/09/quote-theo-schlossnagle/</guid><feedburner:origLink>http://mariz.org/blog/2008/jan/09/quote-theo-schlossnagle/</feedburner:origLink></item><item><title>Fullread
</title><link>http://feedproxy.google.com/~r/mariz/~3/VdsJXTboXHI/</link><description>&lt;a href="http://fullread.com"&gt;&lt;img src="http://mariz.org/media/images/posts/fullread.gif" alt="fullread.com" /&gt;&lt;/a&gt;
&lt;br /&gt;
&lt;a href="http://fullread.com"&gt;Fullread&lt;/a&gt; is online. It's just a
simple tool that helps you organize your online readings.&lt;br /&gt;
I use &lt;a href="http://del.icio.us"&gt;del.icio.us&lt;/a&gt; a lot, mainly for bookmarking, but right
now my &lt;a href="http://del.icio-us"&gt;account&lt;/a&gt; is a complete mess because I’m constantly adding links that I want to read later and I don't delete them.&lt;br /&gt;
&lt;a href="http://fullread.com"&gt;Fullread&lt;/a&gt; uses a simple concept: I add an URL to my account to read later, then I decide to archive, delete or 
add it to my &lt;a href="http://del.icio.us"&gt;del.icio.us&lt;/a&gt;
account.&lt;br /&gt;
Feel free to &lt;a href="http://mariz.org/contacts/"&gt;send me&lt;/a&gt; any feature request or something else.&lt;br /&gt;
I hope you enjoy it.&lt;img src="http://feeds.feedburner.com/~r/mariz/~4/VdsJXTboXHI" height="1" width="1"/&gt;</description><pubDate>Thu, 03 Jan 2008 19:53:02 -0000</pubDate><guid isPermaLink="false">http://mariz.org/blog/2008/jan/03/fullread/</guid><feedburner:origLink>http://mariz.org/blog/2008/jan/03/fullread/</feedburner:origLink></item><item><title>Swim across the Atlantic Ocean
</title><link>http://feedproxy.google.com/~r/mariz/~3/Z-ScrSwbzF0/</link><description>&lt;img src="http://mariz.org/media/images/posts/google-maps.gif" alt="Google Maps" /&gt;
&lt;br /&gt;
Try to get directions from "Belo Horizonte" to "Miami" on &lt;a href="http://maps.google.com"&gt;Google Maps&lt;/a&gt;.&lt;br /&gt;
Here is a shortcut: &lt;a href="http://smallr.net/google-maps"&gt;http://smallr.net/google-maps&lt;/a&gt;.&lt;img src="http://feeds.feedburner.com/~r/mariz/~4/Z-ScrSwbzF0" height="1" width="1"/&gt;</description><pubDate>Fri, 30 Nov 2007 14:53:27 -0000</pubDate><guid isPermaLink="false">http://mariz.org/blog/2007/nov/30/swim-across-atlantic-ocean/</guid><feedburner:origLink>http://mariz.org/blog/2007/nov/30/swim-across-atlantic-ocean/</feedburner:origLink></item><item><title>Smallr updates
</title><link>http://feedproxy.google.com/~r/mariz/~3/TJwuWwRA--Q/</link><description>After I've messed up about the domain issue(see this &lt;a href="http://mariz.org/blog/2007/nov/22/smallr/#comments"&gt;comments&lt;/a&gt;), I've just updated &lt;a href="http://smallr.net"&gt;smallr&lt;/a&gt; with some features. Now provides an &lt;a href="http://smallr.net/api/"&gt;API&lt;/a&gt; to access the service by &lt;a href="http://en.wikipedia.org/wiki/REST"&gt;REST&lt;/a&gt;.&lt;br /&gt;
Right now &lt;s&gt;only&lt;/s&gt; supports &lt;a href="http://en.wikipedia.org/wiki/Json"&gt;JSON&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Xml"&gt;XML&lt;/a&gt; &lt;s&gt;will be&lt;/s&gt; is also available &lt;s&gt;soon&lt;/s&gt;.&lt;br /&gt;
A &lt;a href="http://smallr.net/buttons/"&gt;bookmarklet&lt;/a&gt; is also available.&lt;img src="http://feeds.feedburner.com/~r/mariz/~4/TJwuWwRA--Q" height="1" width="1"/&gt;</description><pubDate>Fri, 23 Nov 2007 17:56:27 -0000</pubDate><guid isPermaLink="false">http://mariz.org/blog/2007/nov/23/smallr-updates/</guid><feedburner:origLink>http://mariz.org/blog/2007/nov/23/smallr-updates/</feedburner:origLink></item><item><title>Smallr
</title><link>http://feedproxy.google.com/~r/mariz/~3/md306fwZeLY/</link><description>&lt;a href="http://smallr.net"&gt;&lt;img src="http://mariz.org/media/images/posts/smallr.gif" alt="small.net" /&gt;&lt;/a&gt;
&lt;br /&gt;
I've just released &lt;a href="http://smallr.net"&gt;smallr&lt;/a&gt;. It lets you generate small web addresses with a keyword that make it easy to remember, will not break in emails and will never expire.&lt;br /&gt;
Basically is a &lt;a href="http://tinyurl.com/"&gt;TinyURL&lt;/a&gt; clone, with a keyword addon.&lt;br /&gt; There will be an &lt;a href="http://en.wikipedia.org/wiki/Api"&gt;API&lt;/a&gt; for webservices with &lt;a href="http://en.wikipedia.org/wiki/Json"&gt;JSON&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Xml"&gt;XML&lt;/a&gt;, eventually a &lt;a href="http://www.mozilla.com/firefox/"&gt;Firefox&lt;/a&gt; extension.&lt;br /&gt;
Test it and send me some feedback.&lt;br /&gt;
I hope you enjoy it.&lt;img src="http://feeds.feedburner.com/~r/mariz/~4/md306fwZeLY" height="1" width="1"/&gt;</description><pubDate>Thu, 22 Nov 2007 19:51:01 -0000</pubDate><guid isPermaLink="false">http://mariz.org/blog/2007/nov/22/smallr/</guid><feedburner:origLink>http://mariz.org/blog/2007/nov/22/smallr/</feedburner:origLink></item><item><title>Weekend in Sintra
</title><link>http://feedproxy.google.com/~r/mariz/~3/2WErSeFhgPc/</link><description>&lt;a href="http://flickr.com/photos/nmariz/1526278293/in/set-72157602337207286/"&gt;&lt;img src="http://farm3.static.flickr.com/2066/1526278293_8becf00efb_s.jpg" alt="Sintra" class="float-left" /&gt;&lt;/a&gt;
&lt;a href="http://flickr.com/photos/nmariz/1527126226/in/set-72157602337207286/"&gt;&lt;img src="http://farm3.static.flickr.com/2300/1527126226_05eaba6361_s.jpg" alt="Quinta das Sequóias" class="float-left" /&gt;&lt;/a&gt;
&lt;br /&gt;

We went to &lt;a href="http://www.visitportugal.com/NR/exeres/6BEF1189-D6BD-4473-856B-604FCEF22106,frameless.htm"&gt;Sintra&lt;/a&gt; this weekend. &lt;br /&gt;
&lt;a href="http://www.visitportugal.com/NR/exeres/6BEF1189-D6BD-4473-856B-604FCEF22106,frameless.htm"&gt;Sintra&lt;/a&gt; is a magical place where you feel like you've just entered a time machine and you find yourself in a land of castles and palaces surrounded by a luxurious vegetation. 
It's inevitable to lose yourself in all the historical details that are related to this place and the numerous points of interest. &lt;br /&gt;
To put the cherry on top of the cake, our choice for accommodation could not have been better! 
If you are looking for tranquility and be surrounded by all that magical atmosphere and... brace yourselves... have a room view to Pena Palace you will want to discover &lt;a href="http://www.quintadasequoias.com/"&gt;Quinta das Sequóias&lt;/a&gt;. A cosy and warm reception are expecting you as well as a delicious breakfast. We are absolutely going to return there. &lt;br /&gt;
Some photos &lt;a href="http://flickr.com/photos/nmariz/sets/72157602337207286/"&gt;here&lt;/a&gt;.&lt;img src="http://feeds.feedburner.com/~r/mariz/~4/2WErSeFhgPc" height="1" width="1"/&gt;</description><pubDate>Wed, 10 Oct 2007 23:15:24 -0000</pubDate><guid isPermaLink="false">http://mariz.org/blog/2007/oct/11/weekend-in-sintra/</guid><feedburner:origLink>http://mariz.org/blog/2007/oct/11/weekend-in-sintra/</feedburner:origLink></item><item><title>iPhone says Welcome to Django
</title><link>http://feedproxy.google.com/~r/mariz/~3/P-ID_5geKII/</link><description>&lt;a href="http://www.flickr.com/photos/jacobian/1160698795/"&gt;&lt;img src="http://farm2.static.flickr.com/1203/1160698795_9ff6c12a1f.jpg?v=0" alt="Django on iPhone"  /&gt;&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/mariz/~4/P-ID_5geKII" height="1" width="1"/&gt;</description><pubDate>Mon, 20 Aug 2007 19:50:16 -0000</pubDate><guid isPermaLink="false">http://mariz.org/blog/2007/aug/20/iphone-says-welcome-to-django/</guid><feedburner:origLink>http://mariz.org/blog/2007/aug/20/iphone-says-welcome-to-django/</feedburner:origLink></item><item><title>A simple WSGI middleware dispatcher
</title><link>http://feedproxy.google.com/~r/mariz/~3/rpu_VbjVP7w/</link><description>&lt;blockquote&gt;The Web Server Gateway Interface (&lt;a href="http://www.python.org/dev/peps/pep-0333/"&gt;WSGI&lt;/a&gt;) is a standard interface between web server software and web applications written in Python. Having a standard interface makes it easy to use an application that supports WSGI with a number of different web servers.&lt;/blockquote&gt;


One implementation is &lt;a href="http://docs.python.org/lib/module-wsgiref.html"&gt;wsgiref&lt;/a&gt; that was added to Python 2.5 Standard Library.&lt;br /&gt;
Here is a simple web application that writes "Hello World":

&lt;pre class="code"&gt;
def index(environ, start_response):
    start_response("200 Ok", [('content-type', 'text/html')])
    return ['Hello World!']

if __name__ == "__main__":
    from wsgiref.simple_server import make_server
    server = make_server(HOST, PORT, index)
    print 'Starting up HTTP server on port %i...' % PORT
    server.serve_forever()
&lt;/pre&gt;

&lt;br /&gt;
This is nice, but is not very useful for us, so lets add some kind of controller(or url dispatcher):

&lt;pre class="code"&gt;
from dispatcher import Dispatcher

dispatcher = Dispatcher()
dispatcher.add(r'^/$', 'views.index')
dispatcher.add(r'^/hello/(?P&amp;lt;username&amp;gt;\w+)/$', 'views.hello')

HOST = 'localhost'
PORT = 8000

if __name__ == "__main__": 
    from wsgiref.simple_server import make_server
    server = make_server(HOST, PORT, dispatcher)
    print 'Starting up HTTP server on port %i...' % PORT
    server.serve_forever()
&lt;/pre&gt;

&lt;br /&gt;
As you can see I've used a regular expression for the url mapping, just like &lt;a href="http://www.djangoproject.com"&gt;Django&lt;/a&gt; uses.&lt;br /&gt;
Here is the dispatcher(dispatcher.py):

&lt;pre class="code"&gt;
import re

class Dispatcher(object):
    def __init__(self, handle404 = None):
        self.urls = dict()
        self.request_path = ''
        if handle404:
            self.handle404 = handle404
        else:
            self.handle404 = self._404

    def __call__(self, environ, start_response):
        self.request_path = environ.get('PATH_INFO', '')
        for url in self.urls:            
            regex = re.compile(url)
            if regex.match(self.request_path):
                m = regex.match(self.request_path)                        
                mod_name, func_name = self._get_mod_func(self.urls[url])                
                try:
                    callback = getattr(__import__(mod_name, {}, {}, ['']), func_name)
                except ImportError, e:
                    raise Exception, "Could not import %s. Error was: %s" % (mod_name, str(e))
                except AttributeError, e:
                    raise Exception, "Tried %s in module %s. Error was: %s" % (func_name, mod_name, str(e))                
                args = (environ, start_response)
                kwargs = dict()                
                for i in regex.groupindex:
                    kwargs[i] = m.group(i)
                # Run callback with environ, start_response and args
                return callback(*args, **kwargs)
        # No match with the defined urls
        return self.handle404(environ, start_response)
            
    def _get_mod_func(self, callback):
        """
        Converts 'path.to.module.funtion' to ['path.to.module', 'function']
        """
        try:
            d = callback.rindex('.')
        except ValueError:
            return callback, ''
        return callback[:d], callback[d+1:]
    
    def _404(self, environ, start_response):
        start_response("404 Not Found", [('content-type', 'text/html')])
        return ['Not Found']

    def add(self, regex, handler):
        self.urls[regex] = handler
&lt;/pre&gt;

&lt;br /&gt;
And here is the views(views.py):

&lt;pre class="code"&gt;
def index(environ, start_response):
    start_response("200 Ok", [('content-type', 'text/html')])
    return ['Index']

def hello(environ, start_response, username):
    start_response("200 Ok", [('content-type', 'text/html')])
    return ['Hello %s' % username]
&lt;/pre&gt;

&lt;br/&gt;
Simple eh?&lt;br /&gt;
This is just a start, now we can add more features, like encapsulate the &lt;span class="code"&gt;start_response&lt;/span&gt; method and add some kind of &lt;span class="code"&gt;HttpResponse&lt;/span&gt;.&lt;br /&gt;
I don't what do reinvent the wheel here and add another web framework to &lt;a href="http://www.python.org"&gt;Python&lt;/a&gt;, just like Joe Gregorio says in &lt;a href="http://bitworking.org/news/Why_so_many_Python_web_frameworks"&gt;this article&lt;/a&gt;. But to prove how trivial is to make a simple framework with &lt;a href="http://www.python.org/dev/peps/pep-0333/"&gt;WSGI&lt;/a&gt; and don't worry about the deployment at development phase.&lt;img src="http://feeds.feedburner.com/~r/mariz/~4/rpu_VbjVP7w" height="1" width="1"/&gt;</description><pubDate>Fri, 08 Jun 2007 09:41:25 -0000</pubDate><guid isPermaLink="false">http://mariz.org/blog/2007/jun/08/simple-wsgi-middleware-dispatcher/</guid><feedburner:origLink>http://mariz.org/blog/2007/jun/08/simple-wsgi-middleware-dispatcher/</feedburner:origLink></item></channel></rss>
