Planeta

  • Nuno Mariz
    Python Portugal
    I've created a Python User Group in Portugal. The idea is to join people who enjoy programming in Python.
    No membership is required to participate, experienced programmers and absolute beginners are both welcome!
    You can visit the site or follow us on Twitter.
  • Hi folks,

    I’m happy to announce the first OCRFeeder’s tarball release.
    Because not all of you are programmers and a packaged release makes more sense to you, today I did it.

    I wanted to have done this longer ago but there was some bugs (simple but annoying ones)  that I wanted to have fixed before a tarball release.

    This first tarball represents the version 0.1-beta. Although named like that, don’t fear and go try it and report bugs/improvements.
    Why the beta on top of such a low version number? Because I wanted to make sure that it was “runnable” in most computers out there. This means that I haven’t tried to run OCRFeeder from scratch in other boxes but mine.
    So, if you’re gonna give it a try, please be so kind to write down every step until you can run it. Then you can fill in bug reports on the project’s Google Code.

    Before I released this tarball I fixed the following two bugs:
    * All the temporary pictures are now being removed after they’re no longer needed;
    * The “Pages to Export” dialog is no longer being shown if there’s only one image/document loaded;

    Today I also created an entry for OCRFeeder in GNOME Live as I think the project is related to our favorite DE.

    So go on and enjoy OCR made easy on GNOME!

  • Even though I got up later today (don’t really understand why the damn time should change… would we die if there was a fix hour for summer/winter time?) but wanted to do a bit of hacking in OCRFeeder.

    So, I did some tiny enhancements that you can check out by updating your SVN repo.

    Here you got a couple of screenshots cause everybody loves them:

    OCRFeeder         OCRFeeder Unpaper frontend

  • So, it’s the middle of the week and I’m posting about last weekend (yeah, it’s sad…).

    Last weekend I could do some work on Rancho and I committed a patch to make its width fluid, that is, to be relative to your screen resolution instead of having a fixed size. (let me know if it’s working right in your machine)
    The rest of the day was dedicated to watching some movies with my girlfriend :)

    On Sunday I had the pleasure to show the beautiful city of A Coruña to my parents who came to visit me.
    We had a great lunch at La Penela, a restaurant in the main city’s square.
    They really liked the city and the people and specially enjoyed the landscape where the sea meets land and the mix of buildings with traditional tapas cafes and small grocery stores.

    You should also take a break and visit this kind city where, as the people here say, “No one is an outsider”.

  • OCRFeeder Video

    Por jrocha, em 19 Março 2009 16:14 - Mais entradas deste utilizador

    Everybody loves screencasts, they’re the new screenshots for say.

    Here’s a video of OCRFeeder in action so you see what it can do for you.

    Enjoy:


    OCRFeeder from Joaquim Rocha on Vimeo.

  • Nuno Mariz
    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.
    This turned out to be a complete waste of time and effort. All that I needed after all was a FriendFeed account that would centralize all my feeds.

    Archiving and displaying your entries with Django is quite simple.
    First of all, you need to download the Python FriendFeed API client. Then start a new application in your project, lets call it lifestream:

    ./manage.py startapp lifestream

    On the settings.py add the lifestream project to the INSTALLED_APPS and a variable to store your FriendFeed username:

    FRIENDFEED_USERNAME = 'your_username'

    In the models.py add a model named Entry:

    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'

    Create an url.py on the lifestream folder:

    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),
    )

    As you can see, I've used a generic view. You can also use the date based generic views and pagination to build an archive like mine.

    Add to your project root urls.py:

    (r'^lifestream/', include('lifestream.urls'))

    Create a template lifestream/entry_list.html:

    {% for entry in object_list %}
    <div class="source">
      <a href="{{ entry.service_profile }}" title="{{ entry.service_name }}"><img src="{{ entry.service_icon }}" alt="{{ entry.service_name }}" alt="{{ entry.service_name }}" /></a>
    </div>
    <div class="details">
      <ul>
        <li><a href="{{ entry.link }}">{{ entry.title }}</a></li>
        <li>{{ entry.published|timesince }} ago</li>
        {% if entry.media_thumbnail %}<li><a href="{{ entry.media_link }}"><img src="{{ entry.media_thumbnail }}" alt="{{ entry.media_title }}" /></a></li>{% endif %}
      </ul>
    </div>
    {% endfor %}

    Finally, create a script to synchronize your feeds:

    #!/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()

    If you want, you can add a job in your crontab:

    # synchronize every 15 mins
    */15 * * * *   root   /path/to/your/application/lifestream_cron.py

    See my lifestream as the working example.

    UPDATE: Friendfeed sends the time in UTC, if you want to use your timezone you have do some hacking:

    Install pytz:

    easy_install pytz

    Import and assign your timezone to a variable:

    import pytz
    tz = pytz.timezone(settings.TIME_ZONE)

    And replace entry.updated and entry.published with:

    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

    Thanks to Chris Kelly that send me an email reporting this.

  • Nuno Mariz
    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.
    This turned out to be a complete waste of time and effort. All that I needed after all was a FriendFeed account that would centralize all my feeds.

    Archiving and displaying your entries with Django is quite simple.
    First of all, you need to download the Python FriendFeed API client. Then start a new application in your project, lets call it lifestream:

    ./manage.py startapp lifestream

    On the settings.py add the lifestream project to the INSTALLED_APPS and a variable to store your FriendFeed username:

    FRIENDFEED_USERNAME = 'your_username'

    In the models.py add a model named Entry:

    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'

    Create an url.py on the lifestream folder:

    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),
    )

    As you can see, I've used a generic view. You can also use the date based generic views and pagination to build an archive like mine.

    Add to your project root urls.py:

    (r'^lifestream/', include('lifestream.urls'))

    Create a template lifestream/entry_list.html:

    {% for entry in object_list %}
    <div class="source">
      <a href="{{ entry.service_profile }}" title="{{ entry.service_name }}"><img src="{{ entry.service_icon }}" alt="{{ entry.service_name }}" alt="{{ entry.service_name }}" /></a>
    </div>
    <div class="details">
      <ul>
        <li><a href="{{ entry.link }}">{{ entry.title }}</a></li>
        <li>{{ entry.published|timesince }} ago</li>
        {% if entry.media_thumbnail %}<li><a href="{{ entry.media_link }}"><img src="{{ entry.media_thumbnail }}" alt="{{ entry.media_title }}" /></a></li>{% endif %}
      </ul>
    </div>
    {% endfor %}

    Finally, create a script to synchronize your feeds:

    #!/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()

    If you want, you can add a job in your crontab:

    # synchronize every 15 mins
    */15 * * * *   root   /path/to/your/application/lifestream_cron.py

    See my lifestream as the working example.

    UPDATE: Friendfeed sends the time in UTC, if you want to use your timezone you have do some hacking:

    Install pytz:

    easy_install pytz

    Import and assign your timezone to a variable:

    import pytz
    tz = pytz.timezone(settings.TIME_ZONE)

    And replace entry.updated and entry.published with:

    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

    Thanks to Chris Kelly that send me an email reporting this.

  • Nuno Mariz
    Django has a code error notifications mechanism when a view raises an exception. It will email the people in ADMIN tuple(settings documentation) in settings.py with the full exception information and displays the default 500.html template.
    This only happens when DEBUG=False in the settings.py.

    It's possible to set a handle that change this behavior with a handler500 variable in the root urls.py.
    So, we can easily write a simple view that sends an error notification to our jabber account.

    First of all, you need to install xmpppy and dnspython:

    $ easy_install xmpppy
    $ easy_install dnspython

    Add to settings.py the jabber parameters such as the jabber id, password, recipient, etc.:

    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.'

    Start a new app named errors or something else and add it to the INSTALLED_APPS tuple in the settings.py:

    python manage.py startapp errors

    Add a handler500 variable with the view in the root urls.py:

    handler500 = 'errors.views.server_error_jabber'

    Finally add the view in errors.views that sends a jabber notification and returns a 500 error page:

    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)
    

    NOTE: Don't forget to set DEBUG=False in the settings.py.
  • Nuno Mariz
    Django has a code error notifications mechanism when a view raises an exception. It will email the people in ADMIN tuple(settings documentation) in settings.py with the full exception information and displays the default 500.html template.
    This only happens when DEBUG=False in the settings.py.

    It's possible to set a handle that change this behavior with a handler500 variable in the root urls.py.
    So, we can easily write a simple view that sends an error notification to our jabber account.

    First of all, you need to install xmpppy and dnspython:

    $ easy_install xmpppy
    $ easy_install dnspython

    Add to settings.py the jabber parameters such as the jabber id, password, recipient, etc.:

    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.'

    Start a new app named errors or something else and add it to the INSTALLED_APPS tuple in the settings.py:

    python manage.py startapp errors

    Add a handler500 variable with the view in the root urls.py:

    handler500 = 'errors.views.server_error_jabber'

    Finally add the view in errors.views that sends a jabber notification and returns a 500 error page:

    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)
    

    NOTE: Don't forget to set DEBUG=False in the settings.py.
  • Nuno Mariz
    Twitter
    A simple templatetag for adding to the template context a variable with the user timeline from Twitter.
    It uses the CachedContextUpdatingNode snippet for caching from Jacob Kaplan-Moss.
    The reason that is necessary to cache content is because Twitter limits the number of accesses to the API.
    This only works if the cache is enabled on your settings.py.
    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])
    Usage:
    {% 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 %}
    Use the source, Luke.