October 18, 2010

Synchronizing django with twitter using django-syncr

Ok so you have your first django powered application running (let me guess - CMS ? Blog ? :) ) and now you'd like to make it more "web 2.0"-ish by adding your twitter feeds. Althought I personally think this app is useless and dumb people who pay me think differently. So let's start. First you'll need some python's dependencies.

python-oauth2
python-twitter and stay alert, cause you need the latest build (so don't use easy_install as you'll get the deprecated 0.6 version)
django-syncr

Then go to dev.twitter and (if you already haven't) register a new application. This will generate you "consumer_key" and "consumer_secret". Write them down. Now let's get back to our application.
Iinstall both python dependencies but django-syncr add as an application in settings.INSTALLED_APPS. The reason for this is that you need to edit a bit of code, since currently available syncr does not support OAuth recently introduced on Twitter. Next either find "get_access_token.py" in python-twitter package, or download this function from sources and send somewhere within the scope of python. Before sending it though, open and edit :

AUTHORIZATION_URL = 'https://api.twitter.com/oauth/authorize'
SIGNIN_URL        = 'https://api.twitter.com/oauth/authenticate'

consumer_key    = None
consumer_secret = None

if consumer_key is None or consumer_secret is None:

In place of consumer_key and consumer_secret insert proper (previously saved) values as strings. Save and close and execute it with :

"python get_access_token.py run"

You will get a unique link printed in the console, that you need to visit. Visit it and press "Enter". Now you will get two more values to store somewhere : "access_token_key" and "access_token_secret".

Okay, now open syncr.app.tweet and change `__init__` function to this :

def __init__(self, username, key, secret, token_key, token_secret):

        self.username = username
        self.api = twitter.Api(
                            consumer_key=key, 
                            consumer_secret=secret, 
                            access_token_key=token_key,
                            access_token_secret=token_secret)
        self.user_cache = dict()

Here you can have different approach of course. You can hardcode your key values in it, add them to settings or send them each time when synchronizing with Twitter. Decision is up to you. Okay, last improvements to django-syncr. Open syncr.twitter folder, and edit models.py file. Here change Tweet.tweeter_id field from PositiveIntegerField to either IntegerField(if you're running < 1.2) or BigIntegerField. We're finished with edits, so sync your database and add this code to the view/context_processor that will serve your tweets :

#1. create TwitterSyncr object
t = TwitterSyncr('twitter_username',
'your_consumer_key',
'your_consumer_secret',
'your_access_token_key',
'your_access_token_secret',)
#2. synchronize your TwitterSyncr object with existing Twitter user
t.syncUser('twitter_username')
#3. get tweets for the Twitter user (so it will import tweets from Twitter to your DB)
t.syncTwitterUserTweets('twitter_username')

#4. grab user from database and it's tweets
user = TwitterUser.objects.get(screen_name="twitter_username")
tweets = Tweet.objects.filter(user=user)

And voila ! You have a list of your most recent tweets available. Of course you can limit the amount of parsed tweets with [:number].

October 11, 2010

Recent months list in django

After a while I'm back with hot new stuff for you and me :) Recently I needed to select blog entries from 5 consecutive months (counting from current) to my footer archive list. The biggest problem was with the range of months in different years. After a lot of struggle I've came across a python module (probably you will have it in your default setup) `dateutil`, which gives new functionality to date related functions. Ok, so let's get to the code. My function was added to context processors to provide me functionality across whole site. Then we build a date object from current date. Next step is to run a generator expression (which I will speak about some more in the future as they're one of the most important features of python language together with coroutines and generator functions) for a increasing range of months (you can also use a list comprehension here if you need some additional lists functionality or a generator function. but I've tried to keep it simple). This gives us a generator object so we create a dictionary of it, with years as keys and corresponding months as dates. Because this dictionary provides us with numbers only, I've added additional function to translate them to string months values (note the brilliant Python's switch-case funcionality) here shown with Polish dictionary.

from datetime import datetime
from dateutil.relativedelta import relativedelta

def get_blog_archive(request):
    now = datetime.today()
    first_month = datetime(now.year, now.month, 1)
    previous_months = (first_month - relativedelta(months = months) for months in range(0, 5, 1))
    
    news_archive = {}
    for pm in previous_months:
        m = translate_month(pm.month)
        if news_archive.has_key(pm.year):
            news_archive[pm.year].append(m)
        else:
            news_archive[pm.year] = [m]
    m = translate_month(current_month)
    news_archive[current_year].append(m)
    
    return {'NEWS_ARCHIVE': news_archive,}

def translate_month(month):
    ret_month = ""
    months = {
            1: "Styczeń",
            2: "Luty",
            3: "Marzec",
            4: "Kwiecień",
            5: "Maj",
            6: "Czerwiec",
            7: "Lipiec",
            8: "Sierpień",
            9: "Wrzesień",
            10: "Październik",
            11: "Listopad",
            12: "Grudzień",
        }
    ret_month = months.get(month)
    return ret_month

Okay this code is nice but not really functional, beacuse we get months names as String (here with utf-8 chars) and its troublesome to parse them as urls. So here's a bit more functional approach :

    news_archive = []
    for pm in previous_months:
        d = datetime(pm.year,pm.month,1)
        news_archive.append(d)
    
    return {'NEWS_ARCHIVE': news_archive,}

And now in our template we just parse with for loop through list elements.
So that's all and stay tooned for some more advanced django examples. I'm doing a really big project right now but there's a ready shoutbox for django example, few implementations of django-registration, something about handling files and more :)