martes 26 de agosto de 2008

Como lanzar informes pesados contra OpenERP

En algunas ocasiones necesitamos ejecutar informes extremadamente pesados contra una instalación de Open(Tiny)ERP. Las buenas noticias son que en la próxima versión la parte más pesada de estos informes (sobre todo contables y de información sobre stocks) será agilizada notablemente mediante nuevos algoritmos de consulta en la base de datos. La mala noticia es que todavía estamos en la versión 4.2.2. y que algunos de estos informes pueden tardar un tiempo en ejecutarse e incluso dar un error de Timeout en el servidor.

El problema de fondo es que por defecto el lanzamiento de informes desde el cliente espera a que la ejecución del informe termine y presentarlo en pantalla. Durante este tiempo el cliente queda "frito", y si llegamos al fatídico Timeout nos quedamos en ascuas. Lo cierto es que el servidor continua trabajando y elabora el informe.... pero no tenemos forma de recuperarlo....¡diantre!

Hay varias estrategias para resolver este problema. Una de ellas consiste en lanzar un nuevo servidor de OpenERP contra la base de datos original en un equipo potente y ocioso y lanzar el informe contra ese servidor. A mí me ha funcionado razonablemente bien ya que en estas condiciones es más difícil llegar a ver que el período de gracia "caduca".

Otra opción consiste en ampliar el tiempo de espera del cliente lo suficiente (linea 69 del fichero bin/modules/action/main.py), reemplazando los 200 intentos/segundos por un número más alto.

Pero hay otra solución que es la que voy a exponer aquí. Consiste en hacer uso del XML-RPC y lanzar el informe y su recuperación desde un programa externo para almacenarlo en un fichero de nuestro sistema de archivos.

Pongo aquí código de ejemplo y paso a comentarlo a continuación:

import xmlrpclib
import base64

sock = xmlrpclib.ServerProxy('http://localhost:8069/xmlrpc/report')
id_informe = sock.report('terp', 3, 'password', 'module.report.name', [ids])
resultado = {'state':False}
while not resultado['state']:
resultado = sock.report_get('terp', 3, 'password', id_informe)
cadena_pdf = resultado['result']
cadena_pdf = base64.decodestring(cadena_pdf)
fichero_pdf = open('/tmp/fichero.pdf','w')
fichero_pdf.write(cadena_pdf)
fichero_pdf.close()

Las lineas fundamentales son donde se lanza la ejecución del informe (sock.report) donde terp es la base de datos, 3 es el id del usuario admin (podría ser otro usuario), password su contraseña, module.report.name es el nombre interno del informe e ids son los identificadores de los registros para los que lanzar el informe. Así por ejemplo, si fuesemos a lanzar como informe la factura con id 1, esta línea sería:

id_informe = sock.report('terp', 3, 'admin', 'account.invoice', [1])

A continuación se inicia un bucle que comprueba si la variable state devuelta por el método report_get es verdadera. Mientras el informe se está calculando, el valor de dicha variable es False. Podríamos incorporar una línea haciendo que el programa espere 1 o más segundos antes de volver a lanzar la petición.

Al final el informe se escribe en el fichero /tmp/fichero.pdf desde donde podremos recuperarlo.