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

Streaming de datos en Django

En PHP uno pude ir mandando datos al cliente (navegador) a medida que los va procesando en el servidor. En Django siempre creé el texto de la respuesta para luego pasársela al objeto HttpResponse. A pesar de que había preguntado varias veces en el canal de chat de Django si podía hacer algo así y me habían respondido que no, tenía la intuición de que algo se podría hacer y pensé en iteradores.

Por suerte este fin de semana tuvimos un gran evento de Python y alojé en mi casa a John, quien me explicó en 5 minutos cómo hacerlo. Aquí una recreación de su ejemplo (views.py):




from django.http import HttpResponse

from time import sleep



def gen():

    for x in range(80):

        sleep(1)

        yield '*' * x + '<br/>'



def test(request):

    return HttpResponse(gen(), mimetype='text/html')



Queda para el lector ver la salida en su navegador. gen es una función que al llamarla devuelve un generador. El primer parámetro de HttpResponse puede ser un generador o cualquier objeto con el método __iter__.

En la misma jornada Nubis, estuvo explorando esto mismo desde distintos ángulos.


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


Workaround para el bug 7233 de Django

El bug se describe en http://code.djangoproject.com/ticket/7233, pero básicamente consiste en la imposibilidad de guardar objetos de tipo QueryDict en una sesión. Los usuarios de Django suelen toparse con el problema al intentar guardar en la sesión el objeto request.POST, yo lo hago de esta forma:

request.session['POST'] = dict(request.POST.items())

No sirve si se tienen múltiples valores para una clave porque items devuelve para cada clave el último valor almacenado.

En este caso, se puede usar lists:

request.session['POST'] = dict(request.POST.lists())


DjangoDash

Mi amigo Pupeno, sabe que estoy trabajando en Django y me contó sobre este concurso: DjangoDash. Consiste en crear una aplicación web en Django, en 48 hs, en equipos de hasta dos personas. ¿Quienes lanzaron la competencia? Toast Driven. A continuación una resumen en español de la página principal del concurso.

¿Qué es esto?

Django Dash es una oportunidad para que los entusiastas de Django pongan a prueba sus habilidades y se acerquen más al slogan “perfectionists with deadlines” (perfeccionistas con fechas límite) dándoles una REAL fecha límite. 48 horas desde el comienzo al final para producir la mejor aplicación que puedan, divirtiéndose en el proceso.

Reglas

Queremos que Dash sea divertido y que todos la pasen bien. Con esto en mente y para que las expectativas sean claras, tenemos 5 reglas simples para la competencia:

  1. No crear nada antes de tiempo
  2. 48 horas para construir
  3. Máximo 2 personas por equipo
  4. Se permite material de terceros (aplicaciones Django queno superen el 10% de lo que vas a construir, frameworks de javascript y css)
  5. Control de versiones (Subversion) por parte de los organizadores