Instalar PIL en un virtualenv de python, y que importe correctamente las librerías de jpeg, png, etc.

PIL es una librería bastante útil en muchos casos. Se apoya en varias librerías externas para manejar diferentes formatos de imagen:

  • libjpeg para manejar imágenes JPEG (en Ubuntu 11.04 aptitude install libjpeg62 libjpeg62-dev).
  • zlib para manejar imágenes PNG (en Ubuntu 11.04 aptitude install zlib1g zlib1g-dev).
  • etc.

La manera más lógica de instalarlo debería ser instalar primero esas dependencias en el sistema, como explica aquí: http://www.eddiewelker.com/2010/03/31/installing-pil-virtualenv-ubuntu/, y luego instalar PIL con “pip install PIL”. Es necesario instalar la versión “-dev” para que incluya las cabeceras C. En una distribución tipo Debian sería:

$ sudo apt-get install libjpeg62 libjpeg62-dev
$ sudo apt-get install zlib1g-dev
$ sudo apt-get install libfreetype6 libfreetype6-dev

El problema es que el instalador tiene un algoritmo bastante cutre para localizar estas librerías en el sistema, y a menudo no las encuentra. Por ejemplo en Arch las encuentra bien, pero en Ubuntu no. Mi problema ahora mismo es instalarlo dentro de un virtualenv con pip install. El comando pip se descarga correctamente la librería y la compila bien, pero al no encontrar esas dependencias, me sale el siguiente mensaje:

--------------------------------------------------------------------
*** TKINTER support not available
*** JPEG support not available
*** ZLIB (PNG/ZIP) support not available
*** FREETYPE2 support not available
*** LITTLECMS support not available
--------------------------------------------------------------------

He visto tres soluciones, de más complicada a menos:

    1. Descargar PIL de http://effbot.org/downloads/Imaging-1.1.7.tar.gz e instalarlo manualmente con python setup.py install. Si sigue sin encontrar las librerías, trucar el setup como se explica aquí: http://effbot.org/zone/pil-decoder-jpeg-not-available.htm.(esta no la recomiendo normalmente)
    2. Instalar PIL con pip pero diciéndole que no lo compile, y luego editar el setup y compilarlo a mano, tal como explica aquí: http://ubuntuforums.org/showpost.php?p=10804763&postcount=2.(esta es la más recomendable)
       1/ Find the path where libjpeg.so is installed, with
          dpkg -L libjpeg62
          dpkg -L zlib1g
          dpkg -L libfreetype6
       2/ Call 'pip install -I pil --no-install' to download and unpack the PIL source
          into your build directory;
       3/ Get into your build directory (<virtualenv_path>/build/pil) and edit setup.py;
       4/ Find a line that says 'add_directory(library_dirs, "/usr/lib")' (line 214 here)
       5/ Add the line 'add_directory(library_dirs, "<library_path>")' afterwards;
       6/ Call 'pip install -I pil --no-download' to finish the installation.
    3. Instalar PIL globalmente en el sistema con aptitude install python-imaging (al ser un paquete de Ubuntu ya sí que resuelve todas las dependencias) y luego copiarlo o enlazarlo al virtualenv, como explica éste: http://menudoproblema.es/blog/entries/2011/04/26/soporte-para-jpeg-zlib-y-freetype2-en-pil/.(esta es una opción rápida si no funciona la anterior)
      $ cd /path/to/virtualenv
      $ ln -s /usr/lib/python2.7/dist-packages/PIL/ lib/python2.7/site-packages/
Anuncios

Desplegar Django bajo Apache con WSGI en un virtualenv, *y que funcione*

El despliegue de Django es una de las pocas cosas claramente mejorables de este estupendo framework. Suele dar más de un dolor de cabeza porque es un poco lío establecer correctamente las variables de entorno. Un caso concreto que me ha dado bastantes problemas es desplegarlo con mod_wsgi de Apache, pero estando el Django dentro de un python con virtualenv.

En la documentación de Django explican el caso general de usar Django con Apache y mod_wsgi, incluyendo enlaces a otros documentos más extensos. Pero no explican el caso del virtualenv. Buscando en la web he encontrado muchos tutoriales pero ninguno me terminaba de aclarar. El problema es cómo decirle a mod_wsgi que coja el python del virtualenv en vez del global del sistema. Este tutorial es el que parecía más prometedor. Básicamente dice que hay que añadir el path del site-packages de nuestro virtualenv con una llamada site.addsitedir() y definir el path de python añadiendo WSGIPythonHome al fichero de configuración de Apache.

Sin embargo, la parte del site.addsitedir() sí funciona, pero el WSGPythonHome no. Después de mucho probar, descubrí que mod_wsgi necesita que el python del virtualenv esté en el path del sistema, si no, no lo encuentra aunque pongamos el WSGIPythonHome apuntando a él. Así que la solución es añadir /mi/path/virtualenv/bin al path, por ejemplo en el script  que lanza apache (normalmente será /etc/init.d/apache o /etc/init.d/httpd). Ojo con esto, que puede afectar a otros proyectos que usen python en el mismo apache.

Y observar una cosa importante: WSGI usa el mismo python para todos los virtual hosts, con lo cual se pierde una de las razones principales para usar virtualenv. Ahora esto sólo servirá si queremos aislar nuestro django de otros proyectos python que haya en el sistema, pero que no usen mod_wsgi.

Y otra cosa más: el mod_wsgi lanza el servidor en un directorio cualquiera, que no tiene por qué ser la raíz de Django. Para que encuentre las cosas, hay dos opciones: una es poner todos los paths absolutos en el settings.py, y la otra es añadir al principio del script django.wsgi las siguientes líneas:

import os
os.chdir(‘/mi/path/virtualenv/mi_proyecto/src’)

En este otro documento explica con bastante más detalle el comportamiento del wsgi script con Django.

Aplicación en Grails que se reinicia sin parar

Hace un rato me ha ocurrido algo muy curioso con grails. He parado el servidor y al volver a ejecutar grails run-app una vez que terminaba de arrancar se ponía a recompilar unas clases y volvía a arrancar en un bucle infinito.

Server running. Browse to http://localhost:8080/
  [groovyc] Compiling 1 source file to /home/ivan/workspace-sts/myApp/target/classes
  [groovyc] Compiling 1 source file to /home/ivan/workspace-sts/myApp/target/classes
   [delete] Deleting directory /home/ivan/.grails/1.3.7/projects/myApp/tomcat
Running Grails application..
2011-10-17 16:42:19,231 [main] INFO  spring.BeanBuilder  - [RuntimeConfiguration] Configuring data source for environment: DEVELOPMENT

He ejecutado varias veces grails-clean por si se había quedado algo raro pero seguía sin funcionar. Al final, la solución es arrancar con grails run-app –verboseCompile (cuidado que son dos guiones) para obtener información adicional. Así, una vez arrancado muestra las clases que intenta recompilar (en negrita):

Server running. Browse to http://localhost:8080/
  [groovyc] Compiling 1 source file to /home/ivan/workspace-sts/myApp/target/classes
  [groovyc] /home/ivan/workspace-sts/myApp/src/groovy/com/auth/MySuccessHandler.groovy
  [groovyc] Compiling 1 source file to /home/ivan/workspace-sts/myApp/target/classes
  [groovyc] /home/ivan/workspace-sts/myApp/src/groovy/com/auth/MySuccessHandler.groovy
   [delete] Deleting directory /home/ivan/.grails/1.3.7/projects/myApp/tomcat
Running Grails application..

Era una clase de una prueba anterior, así que la he borrado y todo se ha solucionado 🙂

Activar Auditoría en Alfresco 3.3

[Actualizado el 7/10/2010 a las 15:30]

La documentación sobre auditoría en Alfresco 3.3 es bastante pobre porque llegó en un momento de transición de la 3.2 al nuevo sistema con servicios y de todo en la 3.4.

Para activar la auditoría se añadirán las siguientes opciones en

alfresco-global.properties

# Auditing
# Audit configuration
# from: alfresco/repository.properties
audit.enabled=true
audit.tagging.enabled=true
audit.repository.enabled=true
audit.cmischangelog.enabled=true
# Setting this flag to true will cause alfresco/auditConfig.xml to be ignored
# when audit is enabled. When false both alfresco/auditConfig.xml and
# alfresco/audit/*.xml will be used.
audit.useNewConfig=false
# Setting this flag to true will force startup failure when invalid audit configurations are detected
audit.config.strict=false

y activar también la auditoría en el siguiente fichero:

auditConfig.xml

<Audit xmlns="http://www.alfresco.org/model/audit/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" enabled="true" auditInternal="false" mode="all">

 

Esto activa la auditoría antigua, pues la nueva no he conseguido que funcione.

La información de auditoría en Alfresco 3.3 se almacena en base de datos.

Para ver estos datos se pueden usar estas consultas de ejemplo:

(Queries para Informix, sí Informix, qué se le va a hacer)

Encontrar usuarios que nunca han entrado o hecho nada para ser auditado:

select *
 from alf_audit_fact fact
 right outer join alf_node_properties prop on fact.user_id = prop.string_value
 where prop.qname_id = 44
 and fact.user_id is null ;

Encontrar todo el contenido que ha sido “visto por un usuario en los últimos 7 días:

select *
 from alf_audit_fact fact
 join alf_audit_source source
 on fact.audit_source_id = source.id
 and (
 (source.service = 'ContentService' and source.method = 'getReader')
 or (source.service = 'FileFolderService' and source.method = 'getReader')
)
 where
 fact.user_id = 'admin'
 and fact.timestamp > DATE(DATE(CURRENT)-7)
 order by fact.id asc ;

Para el metodo crearNodo sería:

or (source.service = 'NodeService' and source.method = 'createNode')

Lista de metodos disponibles desde la tabla alf_audit_source

id application service methd
15 SystemMethodInterceptor NodeService createNode
 17 SystemMethodInterceptor RuleService disableRules
 28 SystemMethodInterceptor RuleService enableRules
 47 SystemMethodInterceptor ContentService getWriter
 64 SystemMethodInterceptor PermissionService setPermission
 66 SystemMethodInterceptor NodeService addAspect
 98 SystemMethodInterceptor PermissionService setInheritParentPermissions
 244 SystemMethodInterceptor NodeService setProperty
 2468 SystemMethodInterceptor NodeService addChild
 2698 SystemMethodInterceptor WorkflowService deployDefinition
 2998 SystemMethodInterceptor NodeService setProperties
 3220 SystemMethodInterceptor AuthenticationService authenticate
 3222 SystemMethodInterceptor AuthenticationService invalidateTicket
 3251 SystemMethodInterceptor AuthorityService createAuthority
 3282 SystemMethodInterceptor PersonService createPerson
 3286 SystemMethodInterceptor AuthenticationService createAuthentication
 3288 SystemMethodInterceptor AuthenticationService setAuthenticationEnabled
 3293 SystemMethodInterceptor AuthorityService addAuthority
 3370 SystemMethodInterceptor AuthenticationService validate
 24934 SystemMethodInterceptor AuthenticationService authenticateAsGuest
 25156 SystemMethodInterceptor FileFolderService getReader
 25174 SystemMethodInterceptor NodeService deleteNode
 25780 SystemMethodInterceptor TemplateService buildDefaultModel
 25782 SystemMethodInterceptor ContentService getReader
 25784 SystemMethodInterceptor TemplateService processTemplate

Más información de Auditoría para Alfresco 3.3
http://wiki.alfresco.com/wiki/Auditing_%28from_V3.2r%29

Espero que haya servido de ayuda.

Establecer el idioma para el cliente de test de Django

Si necesitas especificar un idioma para el cliente de test de Django (django.test.client.Client), puedes hacerlo especificandolo en el settings, mediante la variable:

LANGUAGE_CODE=”en-us”

Redireccionar después del login con spring-security-core

En un proyecto personal estoy utilizando Grails junto con spring security core para la autenticación de los usuarios.
Cuando intentamos acceder a una url que hemos configurado para permitir el acceso sólo a usuarios autenticados, el plugin nos redirige a la vista de login. Una vez autenticados nos redirecciona a la url a la que queríamos acceder inicialmente. Pero ¿qué ocurre si queremos redireccionar siempre a una url concreta?.

La solución pasa por añadir en la vista auth.gsp lo que marco en negrita:

<form action='${postUrl}?spring-security-redirect=/url-a-redireccionar'
method='POST' id='loginForm' class='cssform' autocomplete='off'>

Y eso es todo. Después de autenticarse, el usuario es redireccionado a la url elegida.

Explicación de la arquitectura de Alfresco

Recientemente en un cliente hemos necesitado explicar cómo es la arquitectura de sistemas de Alfresco.

La estructura de espacios en Alfresco no es la estructura “física” con la que los documentos se graban en disco. No replica exactamente la distribución que hagamos por carpetas en el sistema de ficheros. (Ver presentación más abajo)

La estructura de carpetas es “lógica” por tanto, pero de cara al usuario es así como los documentos se distribuyen por el repositorio y así los mostrará en un posible acceso por unidad de red de windows, o webdav, o un arbol de directorios.

Otras vistas que podemos realizar a partir de otros elementos de clasificación como las categorías, nubes de tags, o búsquedas prediseñadas en función de tipos de documento o un valor de un metadato, son visibles para el usuario, pero a través de otros sistemas.
Estas vistas podrían ser la la propia interfaz web de Alfresco Share o vistas diseñadas por nosotros a partir de los servicios que ofrece.

En todo caso no habría nunca ningún perjuicio en el rendimiento de Alfresco por clasificar los documentos en carpetas o por nube de tags, todo es transparente para las búsquedas, que únicamente se basan en los índices que se generan automáticamente.

La única recomendación de rendimiento a este nivel que nos da Alfresco es respetar en la estructura de carpetas el número máximo de 1000 nodos por nivel. Es importante en accesos que muestren el árbol de directorios como acceder con Alfresco Share, Alfresco Explorer, CIFS, Webdav, FTP, etc.

Adjunto una pequeña presentación que espero aclare estos conceptos.