Automágica: durante 2017 estoy trabajando bastante en Automágica, mi software para editar libros: Más información - Posts relacionados


The Bing Bang Theory Theme Song

The Bing Bang Theory Theme Song - Bare Naked LadiesOur whole universe was in a hot dense state,

Then nearly fourteen billion years ago expansion started. Wait...

The Earth began to cool,

The autotrophs began to drool,

Neanderthals developed tools,

We built a wall (we built the pyramids),

Math, science, history, unraveling the mysteries,

That all started with the big bang!

"Since the dawn of man" is really not that long,

As every galaxy was formed in less time than it takes to sing this song.

A fraction of a second and the elements were made.

The bipeds stood up straight,

The dinosaurs all met their fate,

They tried to leap but they were late

And they all died (they froze their asses off)

The oceans and pangea

See ya, wouldn't wanna be ya

Set in motion by the same big bang!

It all started with the big BANG!

It's expanding ever outward but one day

It will cause the stars to go the other way,

Collapsing ever inward, we won't be here, it wont be hurt

Our best and brightest figure that it'll make an even bigger bang!

Australopithecus would really have been sick of us

Debating out while here they're catching deer (we're catching viruses)

Religion or astronomy, Encarta, Deuteronomy

It all started with the big bang!

Music and mythology, Einstein and astrology

It all started with the big bang!

It all started with the big BANG!



Decoradores en Python (I) - Introducción

Este artículo es el primero de un plan de 3 artículos. Empezamos con una introducción a los decoradores en Python.

Funciones

Cómo todo en Python, las funciones son objetos. La forma más común de crear un objeto de tipo <function> es mediante el keyword def:

def saludo():

    print "Hola"

Al realizar esta definición, el cuerpo de la función es compilado pero no ejecutado y el objeto de tipo <function> es asociado al nombre 'saludo'. Mediante este nombre podemos referirnos al objeto:

>>> saludo

<function saludo at 0xb7d82fb4>

y utilizando la notación de paréntesis podemos llamar a (ejecutar) la función.

>>> saludo()

Hola

Una función puede tener parámetros (los parámetros son nombres a los que podemos referirnos en el cuerpo de la función):

def saludo2(nombre):

    print "Hola %s" % nombre
def saludo3(nombre, apellido):

    print "Hola %s %s" % (nombre, apellido)

Cuando llamamos a la función con argumentos (los argumentos son valores que en principio se asocian uno a uno a los parámetros de la función):

>>> saludo2("Ceci")

Hola Ceci
>>> saludo3("Ceci", "Pucci")

Hola Ceci Pucci

Los últimos n parámetros pueden tener valores por defecto, entonces estas definiciones y sus consiguientes ejecuciones son válidas:

def saludo4(nombre, apellido="Conti"):

    print "Hola %s %s" % (nombre, apellido)
>>> saludo4("Juanjo")

Hola Juanjo Conti
>>> saludo4("Juanjo", "Garau")

Hola Juanjo Garau
def saludo5(nombre="Juanjo", apellido="Conti"):

    print "Hola %s %s" % (nombre, apellido)
>>> saludo5()

Hola Juanjo Conti
>>> saludo5("Mary")

Hola Mary Conti

Los últimos n argumentos pueden ser argumentos nombrados, es decir utilizando el nombre de los parámetros con los que el argumento se debe asociar. En las siguientes ejecuciones se pueden ver ejemplos de esto:

def saludo6(tratamiento, nombre, apellido):

    print "Hola %s %s %s" % (tratamiento, nombre, apellido)
>>> saludo6("Sr.", apellido="Conti", nombre="Juanjo")

Hola Sr. Juanjo Conti
>>> saludo6("Sr.", "Juanjo", apellido="Conti")

Hola Sr. Juanjo Conti

Los parámetros de una función pueden terminar con <nombre> (una tupla con los últimos argumentos posicionales) y/o *<nombre> (un diccionario con los últimos argumentos nombrados).

def saludo7(tratamiento, *args):

    print "Hola %s %s" % (tratamiento, " ".join(args))
>>> saludo7("Sr.", "Juanjo", "Conti")

Hola Sr. Juanjo Conti
>>> saludo7("Sr.", "Juanjo", "Conti", "Garau")

Hola Sr. Juanjo Conti Garau

Notemos que esta forma de definir una función es bastante útil cuando no sabemos el número de argumentos que se recibirán.

La siguiente es la forma más genérica de definir una función:

def saludo8(*args, **kwargs):

    pass

Decoradores

Un decorador es una función 'd' que recibe como argumento otra función 'a' y retorna una nueva función 'b'. La nueva función 'b' es la función 'a' decorada con 'd'.

Supongamos que queremos avisarle a un sistema de seguridad cada vez que se ejecutan las funciones abrir_puerta y cerrar_puerta. Para hacer una simplificación, el aviso simplemente será imprimir por un mensaje en la pantalla. Podemos escribir el siguiente 'decorador':

def avisar(f):

    def inner(*args, **kwargs):

        f(*args, **kwargs)

        print "Se ha ejecutado %s" % f.__name__

    return inner

Las siguientes son las funciones a decorar:

def abrir_puerta():

    print "Abrir puerta"



def cerrar_puerta():

    print "Cerrar puerta"
>>> abrir_puerta()

Abrir puerta

>>> cerrar_puerta()

Cerrar puerta

Y ahora solo nos limitamos a seguir la definción que di al principio de un decorador:

abrir_puerta = avisar(abrir_puerta)

cerrar_puerta = avisar(cerrar_puerta)

Listo!, ambas funciones han sido decoradas:

>>> abrir_puerta()

Abrir puerta

Se ha ejecutado abrir_puerta

>>> cerrar_puerta()

Cerrar puerta

Se ha ejecutado cerrar_puerta

Azúca sintáctica

En Python 2.3, la anterior era la forma de decorar una función. A partir de Python 2.4 se a añadido azúcar sintáctica al lenguaje que nos permite hacer lo mismo de esta forma:

@avisar

def abrir_puerta():

    print "Abrir puerta"



@avisar

def cerrar_puerta():

    print "Cerrar puerta"

Esta es una forma mucho más visual de hacerlo.

Encadenando decoradores

La decoración de funciones puede encadenarse. Para ejemplificarlo vamos a suponer ahora que solo usuarios autenticados en el sistema pueden ejecutar las funciones abrir_puerta y cerrar puerta.

Nuevamente hacemos una simplificación. Existe la variable AUTHENTICATED que indica el estado del usuario actual. Si el usuario no está autenticado y se intente ejecutar alguna de las funciones, una excepción es lanzada.

def autenticado(f):

    def inner(*args, **kwargs):

        if AUTHENTICATED:

            f(*args, **kwargs)

       else:

           raise Exception

    return inner

Luego, la definición de abrir_puerta y cerrar_puerta debería ser:

@autenticado

@avisar

def abrir_puerta():

    print "Abrir puerta"



@autenticado

@avisar

def cerrar_puerta():

    print "Cerrar puerta"

Con AUTHENTICATED = True:

>>> cerrar_puerta()

Cerrar puerta

Se ha ejecutado cerrar_puerta

Pero si AUTHENTICATED = False:

>>> cerrar_puerta()

Traceback (most recent call last):

File "<stdin> ", line 1, in <module>

File "<stdin> ", line 6, in inner

Exception

update: 2° entrega.


django-messages en español

django-messages es una aplicación que podemos integrar en nuestro proyecto Django para permitir a los usuarios enviarse mensajes entre ellos.

Hoy la traducimos con Ceci al español (es_AR, en realidad) mientras por IRC uno de los miembros del proyecto nos indicaba dónde tocar (nunca habíamos traducido una aplicación Django). Disponible desde la revisión 41.

Nuestra traducción, por si alguien tiene sugerencias o correcciones: es_AR.


Usando django-notification

django-notification es una aplicación para Django que permite al resto de las aplicaciones de un proyecto enviar notas a los usuarios. Así, cuando la aplicación Stock detecta que el número de bulones que hay en el depósito es menor límite de reposición puede enviar una nota de tipo "Avisos prioritarios" al usuario responsable del depósito. Esta nota puede ser recibida por mail, feed o mostrarse en el sistema cuando el usuario inicia su sesión.

El siguiente es un artículo del proyecto que hemos traducido yo y Cecilia.

Integrar Notification

Integrar notification a tu aplicación es un proceso simple de dos pasos.

  1. crear tus tipos de notas (Notice Type)
  2. enviar notificaciones

Crear tipos de notas (Notice Type)

Debes ejecutar create_notice_type(label, display, description) una vez para crear los tipos de notas para tu aplicación en la base de datos. label es un nombre interno, corto, que será usado para el tipo, display es lo que el usuario verá como el nombre del tipo de nota y description es una descripción corta.

Por ejemplo:


from django.dispatch import dispatcher

from django.db.models import signals



try:

    from notification import models as notification



    def create_notice_types(app, created_models, verbosity, **kwargs):

        notification.create_notice_type("invitacion_amigo", u"Invitación recibida",

                                                     u"has recibido una invitación")

        notification.create_notice_type("invitacion_aceptada", u"Invitación aceptada",

                                                     u"una invitación que enviaste fue aceptada")



    dispatcher.connect(create_notice_types, signal=signals.post_syncdb, sender=notification)

except ImportError:

    print "No se encontró la aplicación notification, no se crearán objetos del tipo NoticeTypes"

Notar que el código está envuelto con un try para que si django-notification no está instalado, tu aplicación funcione de todas formas.

Enviar notificaciones

Para enviar mensajes se usa send(users, notice_type_label, message_template, object_list, issue_notice) dónde object_list y issue_notice son opcionales.

users es una lista de usuarios que deben recibir la nota. notice_type_label es la etiqueta que usaste en el paso previo para identificar un tipo de nota. message_template es solo un string, aunque si object_list no está vacía, entonces message_template debe contener tantos %s como objetos en object_list. Entonces serán reemplazados con referencias a los correspondientes objetos de object_list.

Por ejemplo:


notification.send([to_user], "invitacion_amigo", "%s solicita ser tu amigo.", [from_user])

enviará una nota de tipo invitacion_amigo a to_user con el mensaje XXX solicita ser tu amigo. dónde XXX es una referencia al objeto from_user. Dependiendo del medio de notificación, esta referencia será un link o solo texto plano.

issue_notice es True por defecto pero se puede cambiar a False si querés que una notificación se envíe pero no se persista como un objeto de tipo Notice en la base de datos.

Para permitir que tu aplicación funcione sin notification, podés envolver el import en un try y testear si el módulo fue cargado antes de enviar la nota.

Por ejemplo:


try:

    from notification import models as notification

except ImportError:

    notification = None

y luego:


if notification:

    notification.send([to_user], "invitacion_amigo", "%s solicita ser tu amigo.", [from_user])


Generar archivos rtf en forma dinámica desde Django

En mi anterior artículo les conté sobre pyrtf-ng, una librería para generar archivos rtf en forma fácil desde un programa escrito en Python. Fue la primer alternativa que manejamos a la hora de plantearnos el problema de generar archivos rtf en forma dinámica desde Django.

En este artículo les cuento el aproach que finalmente adoptamos. Si la naturaleza dinámica del documento que queremos generar radica en que ciertas partes del texto tendrán valores dinámicos o ciertas partes pueden estar o no dependiendo de alguna condición, lo que podemos hacer es utilizar un sistema de templates. El formato rtf es un lenguaje de marcas (es texto, no binario!). Por lo que fácilmente podemos usar el subsistema de templates de Django para lograr nuestro objetivo.

Un ejemplo de lo que queremos obtener

Simplificando, si en el sistema la variable nombre vale "Juan" y la variable tratamiento es False, el resultado esperado en el archivo rtf es:

Hola Juan

Si la variable nombre vale "Raúl", la variable tratamiento es "Sr." el resultado esperado en el archivo rtf es:

Hola Sr. Raúl

Para lograrlo necesitamos un archivo hola.rtf (puede tener cualquier nombre y cualquier extensión, pero estos parecen apropiados para el ejemplo) ubicado en algún directorio accesible por el subsistema de templates. Tip: crear un archivo rtf con OpenOffice Writer o WordPad y luego editarlo con un editor de texto para agregar las marcas necesarias para que Django lo procese.

El template

Así se ve el template del ejemplo cuando lo abrimos con un editor de texto, luego de agregar las marcas correspondientes:

{% load filtros %}{\rtf1\ansi\deff0\adeflang1025

{\fonttbl{\f0\froman\fprq2\fcharset0 Bitstream Vera Sans;}{\f1\froman\fprq2\fcharset0 Bitstream Vera Sans;}{\f2\fswiss\fprq2\fcharset0 Bitstream Vera Sans;}{\f3\fnil\fprq2\fcharset0 Bitstream Vera Sans;}}

{\colortbl;\red0\green0\blue0;\red128\green128\blue128;}

[....]

{\rtlch \ltrch\loch\f0\fs24\lang11274\i0\b0 Hola {% if tratamiento %}{{ tratamiento }} {% endif %}{{ nombre|rtf }}}

\par }

El encabezado suele ser largo (esto depende del programa utilizado para crear el documento, con WordPad se obtienen los encabezados más cortos). Las partes importantes están resaltadas en negrita.

El bloque load carga el filtro de nombre rtf que luego es usado. Notar que este bloque debe incluirse en la misma línea que la primer línea del documento o en alguna línea siguiente, pero nunca en la primer línea del archivo y solo. Esto provocará una línea en blanco al principio del documento resultante, un error de sintaxis en el formato rtf, y por lo tanto no podrá ser interpretado por los procesadores de palabras.

Un filtro para codificar caracteres Unicode en rtf

Como rtf no soporta nativamente caracteres Unicode (tildes y otros caracteres no ascii), estos deben ser codificados para ser correctamente interpretados. El siguiente filtro hace esa tarea, es un string filter de Django:


@register.filter(name='rtf')

@stringfilter

def rtf(value):

    if isinstance(value, UnicodeType):

        return "".join(["u%s?" % str(ord(c)) for c in value])

    return value


Soporte Unicode para pyrtf-ng

pyrtf-ng es un módulo para Python que permite generar archivos en formato RTF en forma dinámica. Esta semana empecé a probarlo con la idea de usarlo para generar documentos de texto con formato (Word-like) en forma dinámica.

Al intentar instalarlo tuve algunos problemas , se los comenté al autor y le sugerí soluciones. Rápidamente estas modificaciones estuvieron hechas en el repositorio del proyecto.

Una vez que tuve la librería instalada y los ejemplos corriendo, empecé a modificar algunos para experimentar un poco. Cuando quise generar un documento que contenga una palabra con tilde (cómo canción), el programa tiró una horrible excepción. Esto hacía que desechara totalmente la idea de usar este software.

Leí sobre la codificación utilizada por el formato RTF y descubrí que no soporta Unicode en forma nativa, pero si mediante una secuencia de escape de la forma: \uxxxx? en dónde xxxx es un entero con signo de 16 bits correspondiente al caracter que se quiere representar. Ejemplo: á.

Pedí instrucciones al autor de la librería, quien me indicó que partes del código tendría que revisar par incluir la funcionalidad de soportar Unicode. Luego de algunas horas empapándome en la arquitectura de clases de pyrtf, tenía un parche listo para ser aplicado e incorporar la funcionalidad. Hoy fue incluido en la versión en desarrollo de la librería.

Código

Un pequeño snippet que muestra el algoritmo básico para pasar de un string Unicode a su representación codificada en RTF:

encoded = ''.join(['\u%s?' % str(ord(b)) for b in base])

Datos

  • pyrtf-ng es mantenido por Duncan Mc Greggor y está basado en PyRTF, un proyecto que ya no es mantenido.
  • Se encuentra en activo desarrollo: modificación del estilo del código y refactoring.
  • También puede ser modificado mediante bazaar en lauchpad.


Un meta chiste

Un español, un mexicano y un argentino entran a un bar. El barman se

da vuelta, los mira unos segundos y dice:

— ¿Qué es esto, un chiste?