2012-04-20 22:27

De Yerbas y Otros Petróleos

Sí, la yerba se fué al carajo. Y por supuesto, cuando un precio sube es culpa del gobierno. Y cuando no sube es malo porque el gobierno oprime a los productores. Y en ambos casos, es este gobierno prepotente, omnipotente, omnisciente y omnímodo el culpable de la suba o no suba.

Bueno, no creo. Habiendo conocido en mis épocas de trabajador de la UNL y/o estudiante a bastante gente que hoy en día tiene cargos políticos de importancia, les puedo decir que en su mayoría son una banda de pelotudos, que están en política por su incapacidad de mantener un laburo honesto y/o productivo. Y los otros son radicales, que es peor.

Sospecho que cuando subió la yerba, Moreno dijo "eh? en serio?", y que se puso a ver como mierda hacía para que bajara antes que le hinchen las pelotas.

Sospecho que el aumento afectó a casi ninguno de los que se quejaron porque subió por 15 días y después bajó, y cuánta yerba consumís en 15 días? O sea, que te costó? $30? $50? Yo justo había comprado 2 kilos de yerba "La Vuelta" en Coto a $4.50 el kilo, así que me sentí por unos días como si fuera Warren Buffett.

Se acuerdan cuando el morrón se había ido de $5 a $18 el kilo y estaba todo el mundo haciendo boycott al morrón rojo? Bueno, resulta que a veces los productos, por lo que puta fuere, se van a la mierda en precio y después bajan. O no bajan y no los consume nadie, y chau. Y sí, el gobierno es el que maneja (hasta ahí) la economía, y sí, a quién le vas a echar la culpa si no? Uy, no hay licuadoras! Yegua montonera, renuncie! Uy, no hay yerba pero no amo a mi mujer entonces me gustaría que hubiera yerba! Ojo, que yo sí la amo. A tu mujer no, a la mía. En fin, esas cosas.

No soy una persona que tenga grandes ilusiones políticas, en el sentido de que sospecho que ser gobierno no te da tanto poder como uno cree, no te hace competente, y si sos un siome con poder sos sobre todo un siome y que ni se levantan a la mañana los poderosos con la idea de cagarnos ni con la idea de salvarnos. Sospecho que se levantan a la mañana con ganas de seguir siendo poderosos, nomás, y que si cagarnos los ayuda en esa misión, alegremente nos cagarán, y si no no.

Pero veo mucha gente que cuando le preguntás como le va te dice que nunca estuvo mejor, pero cuando le preguntás que opina del gbierno dice que son peores que una combinación de Menem, Videla, y Tao-Pai-Pai.

Esa gente no se da cuenta de la tensión entre su idea de un gobierno omnipotente que está intentando continuamente cagarlos, y es responsable de la variación del precio de los palmitos, unos ineficientes, corruptos, una lacra que los persigue pero ellos logran superar gracias a, seguramente, su inefable capacidad, su incorruptibilidad, su agilidad para escaparse.

Como puede ese gobierno omnipotente y omniculpable, omniobstructor de sueños de clase media ser derrotado por ellos? Porqué les está yendo bien si están siendo perseguidos por esas bestias feroces sedientas de sangre de ciudadano de Nuñez/Belgrano/Palermo? Es explicable únicamente por la inmensa capacidad del susodicho Nuñezeño/Belgranense/Palermitaño?

Me imagino que, o no es tan omnipotente, o no los persigue tanto, y no son tan capaces, y si el gobierno fuera otro que hiciera otras cosas capaz que les iría un poco mejor, o un poco peor, o más o menos igual. Sospecho que no es determinante el gobierno en como vive uno. Sospecho que es mucho más determinante uno, pero que el clima ayuda. Sospecho que lo que mata es la humedad, a menos que uno esté nadando, que es el deporte más completo.

Sospecho que ser hincha de un equipo de fútbol hoy en día es preocuparse por el color de la remera de gente que no conozco y corre atrás de una pelota, pero sospecho que ser parte de un partido político es parecido, pero estoy seguro que defender a una empresa es veinte veces más tonto. Sospecho que si le ofrecieran X dólares a Repsol, nos tiraba YPF por el marote, y que si le ofrecíán X/2 es un ultraje, por lo que putas son, lo que falta es discutir precio.

Sospecho que estamos hablando de plata nomás.

Sospecho que van a decir "este Roberto, siempre ranteando", sospecho que es cierto.

Un abrazo.

2012-04-19 23:34

Jinja support in Nikola ready for testing

Since some people really don't like Mako, and prefer Jinja2, I decided to make the template engine modular in Nikola my static site/blog generator.

All things considered, there is very little difference on the API side:

//ralsina.me/galleries/random/jinjavsmako.thumbnail.png

I even ported the default theme to Jinja! And not all that much is different on the template side, either:

//ralsina.me/galleries/random/jinjavsmako1.thumbnail.png

But hey, to each his own. Other template engines are probably easy to plug, too.

Branch up for review here in github and yes, anyone reading this who knows Jinja2 or python is welcome to review it.

2012-04-18 11:50

Tato at 5

Today is the 5th birthday of the most awesome kid I know. So, here he is, on a rollercoaster, a few days ago.

As he would say: it has been GREAT! and INCREDIBLE!

Happy birthday Tatito.

2012-04-17 23:40

A port of blog.txt to Nikola

If you want a very minimalistic theme for a Nikola-based site, I just did a quick and dirty port of Scott Wallick's blog.txt theme

If there is anything nice here, he did it. If there is something wrong or broken, I did it instead.

I did it basically to see if it was possible to port wordpress themes to Nikola. And it is, but it involves reading php files and loosely reinterpreting them into Mako templates.

While the port is far from perfect, it's a reasonable starting point for someone who is really interested in it.

Here is how it looks:

//ralsina.me/galleries/random/blogtxt.png

And here is the download. To use it, just unzip it in your themes folder, set THEME to "blogtxt" in your dodo.py file, and rebuild the site.

At least the CSS files are easily adapted.

This theme is under a LGPL license (see included license.txt), enjoy!

2012-04-16 23:12

Smiljan, a Small Planet Generator

I maintain a couple of small "planet" sites. If you are not familiar with planets, they are sites that aggregate RSS/Atom feeds for a group of people related somehow. It makes for a nice, single, thematic feed.

Recently, when changing them from one server to another, everything broke. Old posts were new, feeds that had not been updated in 2 years were always with all its posts on top... a disaster.

I could have gone to the old server, and started debugging why rawdog was doing that, or switch to planet, or look for other software, or use an online aggregator.

Instead, I started thinking... I had written a few RSS aggregators in the past... Feedparser is again under active development... rawdog and planet seem to be pretty much abandoned... how hard could it be to implement the minimal planet software?

Well, not all that hard, that's how hard it was. Like it took me 4 hours, and was not even difficult.

One reason why this was easier than what planet and rawdog achieved is that I am not doing a static site generator, because I already have one so all I need this program (I called it Smiljan) to do is:

  • Parse a list of feeds and store it in a database if needed.
  • Download those feeds (respecting etag and modified-since).
  • Parse those feeds looking for entries (feedparser does that).
  • Load those entries (or rather, a tiny subset of their data) in the database.
  • Use the entries to generate a set of files to feed Nikola
  • Use nikola to generate and deploy the site.

So, here is the final result: http://planeta.python.org.ar which still needs theming and a lot of other stuff, but works.

I implemented Smiljan as 3 doit tasks, which makes it very easy to integrate with Nikola (if you know Nikola: add "from smiljan import *" in your dodo.py and a feeds file with the feed list in rawdog format) and voilá, running this updates the planet:

doit load_feeds update_feeds generate_posts render_site deploy

Here is the code for smiljan.py, currently at the "gross hack that kinda works" stage. Enjoy!

# -*- coding: utf-8 -*-
import codecs
import datetime
import glob
import os
import sys

from doit.tools import timeout
import feedparser
import peewee


class Feed(peewee.Model):
    name = peewee.CharField()
    url = peewee.CharField(max_length = 200)
    last_status = peewee.CharField()
    etag = peewee.CharField(max_length = 200)
    last_modified = peewee.DateTimeField()

class Entry(peewee.Model):
    date = peewee.DateTimeField()
    feed = peewee.ForeignKeyField(Feed)
    content = peewee.TextField(max_length = 20000)
    link = peewee.CharField(max_length = 200)
    title = peewee.CharField(max_length = 200)
    guid = peewee.CharField(max_length = 200)

Feed.create_table(fail_silently=True)
Entry.create_table(fail_silently=True)

def task_load_feeds():
    feeds = []
    feed = name = None
    for line in open('feeds'):
        line = line.strip()
        if line.startswith('feed'):
            feed = line.split(' ')[2]
        if line.startswith('define_name'):
            name = ' '.join(line.split(' ')[1:])
        if feed and name:
            feeds.append([feed, name])
            feed = name = None

    def add_feed(name, url):
        f = Feed.create(
            name=name,
            url=url,
            etag='caca',
            last_modified=datetime.datetime(1970,1,1),
            )
        f.save()

    def update_feed_url(feed, url):
        feed.url = url
        feed.save()

    for feed, name in feeds:
        f = Feed.select().where(name=name)
        if not list(f):
            yield {
                'name': name,
                'actions': ((add_feed,(name, feed)),),
                'file_dep': ['feeds'],
                }
        elif list(f)[0].url != feed:
            yield {
                'name': 'updating:'+name,
                'actions': ((update_feed_url,(list(f)[0], feed)),),
                }


def task_update_feeds():
    def update_feed(feed):
        modified = feed.last_modified.timetuple()
        etag = feed.etag
        parsed = feedparser.parse(feed.url,
            etag=etag,
            modified=modified
        )
        try:
            feed.last_status = str(parsed.status)
        except:  # Probably a timeout
            # TODO: log failure
            return
        if parsed.feed.get('title'):
            print parsed.feed.title
        else:
            print feed.url
        feed.etag = parsed.get('etag', 'caca')
        modified = tuple(parsed.get('date_parsed', (1970,1,1)))[:6]
        print "==========>", modified
        modified = datetime.datetime(*modified)
        feed.last_modified = modified
        feed.save()
        # No point in adding items from missinfg feeds
        if parsed.status > 400:
            # TODO log failure
            return
        for entry_data in parsed.entries:
            print "========================================="
            date = entry_data.get('updated_parsed', None)
            if date is None:
                date = entry_data.get('published_parsed', None)
            if date is None:
                print "Can't parse date from:"
                print entry_data
                return False
            date = datetime.datetime(*(date[:6]))
            title = "%s: %s" %(feed.name, entry_data.get('title', 'Sin título'))
            content = entry_data.get('description',
                    entry_data.get('summary', 'Sin contenido'))
            guid = entry_data.get('guid', entry_data.link)
            link = entry_data.link
            print repr([date, title])
            entry = Entry.get_or_create(
                date = date,
                title = title,
                content = content,
                guid=guid,
                feed=feed,
                link=link,
            )
            entry.save()
    for feed in Feed.select():
        yield {
            'name': feed.name.encode('utf8'),
            'actions': [(update_feed,(feed,))],
            'uptodate': [timeout(datetime.timedelta(minutes=20))],
            }

def task_generate_posts():

    def generate_post(entry):
        meta_path = os.path.join('posts',str(entry.id)+'.meta')
        post_path = os.path.join('posts',str(entry.id)+'.txt')
        with codecs.open(meta_path, 'wb+', 'utf8') as fd:
            fd.write(u'%s\n' % entry.title.replace('\n', ' '))
            fd.write(u'%s\n' % entry.id)
            fd.write(u'%s\n' % entry.date.strftime('%Y/%m/%d %H:%M'))
            fd.write(u'\n')
            fd.write(u'%s\n' % entry.link)
        with codecs.open(post_path, 'wb+', 'utf8') as fd:
            fd.write(u'.. raw:: html\n\n')
            content = entry.content
            if not content:
                content = u'Sin contenido'
            for line in content.splitlines():
                fd.write(u'    %s\n' % line)

    for entry in Entry.select().order_by(('date', 'desc')):
        yield {
            'name': entry.id,
            'actions': [(generate_post, (entry,))],
            }

2012-04-14 11:33

Nikola 2.1.1 + GitHub

By popular request, Nikola now has its source code at GitHub.

Also, if you tried version 2.1 and it failed, try 2.1.1, because I forgot to add a couple of files in one of the themes in 2.1.

2012-04-13 10:40

Alimento para la Culpa

System Message: ERROR/3 (<string>, line 1)

Document or section may not begin with a transition.

Hace como dos años escribí un pedazo de un libro. De vez en cuando lo miro con cariño y pienso que estaría bueno terminarlo, o redefinirlo y cerrarlo, y cosas así. Hasta hice un plan que nunca pude poner en práctica porque la vida te lleva a hacer cosas distintas.

Resulta que están usando mi humilde cacho de libro como material de estudio en la materia IWI-131 Programación de computadores - 1er Semestre 2012 de la Universidad Técnica Federico Santa María en Chile.

Por un lado, me pone contento. Por otro lado me pone nervioso que un libro que cita esto sea material de estudio para chicos de 18:

Hasta que cumple veinticinco, todo hombre piensa cada tanto que dadas las circunstancias correctas podría ser el más jodido del mundo. Si me mudara a un monasterio de artes marciales en China y estudiara duro por diez años. Si mi familia fuera masacrada por traficantes colombianos y jurara venganza. Si tuviera una enfermedad fatal, me quedara un año de vida y lo dedicara a acabar con el crimen. Si tan sólo abandonara todo y dedicara mi vida a ser jodido.

—Neal Stephenson (Snow Crash)

Por otro lado más, me da ganas de terminarlo. Por el último lado (con lo cual lo que tengo es una sensación cuadrangular), pienso: que raro sería que te den un libro en la facu que está todo incompleto. Es como hacerse fan de The Event y nunca enterarse que era el famoso Event, como haber visto Twin Peaks y nunca sacarse de encima la dudas de qué era toda esa bizarreada. Como seguir todavía esperando que Mel Brooks haga la segunda parte de la historia del mundo para poder entender "Jews in Space".

Ahora podría no terminarlo como decisión artística!

Y por supuesto:

2012-04-12 21:21

Dogfooding a new theme

I am doing a second theme for Nikola, and what better way to test it than migrating this site to it!

In the process, I stopped using custom templates here, organized how a theme is done, how translations work, and other stuff.

Comments about broken / untranslated / missing stuff are much appreciated!

Contents © 2000-2019 Roberto Alsina