martes, abril 12, 2011

.hgignore para proyectos Turbogears 2+

Subversion es mi VCS favorito, sin embargo uso mucho Mercurial para cambios pequeños, pruebas de código y parches. Tengo repositorios híbridos (SVN/HG) que me funcionan muy bien. Copio mi archivo .hgignore para futura referencia.
# use glob syntax.
syntax: glob
*.svn   # repositorio híbrido
*.pyc   # compilados
*~      # temporales

syntax: regexp
^lib$   # librería específica del proyecto
* Tengo que hacer una referencia rápida para Mercurial

lunes, abril 04, 2011

Multiple relación a la misma tabla con Declarative SQLAlchemy 0.6

En el manual de SQLAlchemy la solución no es muy clara para la forma declarative, me arrojaba el siguiente error
sqlalchemy.exc.ArgumentError: Column-based expression object expected for argument 'primaryjoin'; got: 'False'
Al ver Column-based expression supuse que era el mismo tipo que en los filtros y así fué. La solución es simple como siempre, la pongo aquí para referencia.
class Registro(DeclarativeBase):
    __tablename__ = 'registro'
    
    #{ Columns    
    id = Column(Integer, primary_key=True)
    # folio único
    folio = Column(Integer, nullable=True)
    medic_id = Column(u'medic_id', Integer, ForeignKey('medic.id'))
    nursea_id = Column(u'nursea_id', Integer, ForeignKey('nurse.id'), nullable=True)
    nurseb_id = Column(u'nurseb_id', Integer, ForeignKey('nurse.id'), nullable=True)
    room_id = Column(u'room_id', Integer, ForeignKey('room.id'))
    status_id = Column(u'status_id', Integer, ForeignKey('status.id'))
    fecha = Column(u'fecha', Date(timezone=False), primary_key=False, nullable=True)
    patient = Column(u'patient', Unicode(255), nullable=False)
    patient_age = Column(u'patient_age', Integer, nullable=True)
    mat = Column(u'mat', Unicode(255), nullable=False)
    kit_id = Column(u'kit_id', Integer, ForeignKey('kit.id'))
    hour_in = Column(u'hour_in', Unicode(16), nullable=False)
    hour_out = Column(u'hour_out', Unicode(16), nullable=False)
    diagnosis = Column(u'diagnosis', Text(length=None, convert_unicode=True, \
        assert_unicode=None), primary_key=False)
    procedure = Column(u'procedure', Unicode(255), nullable=False)
    notes = Column(u'notes', Text(length=None, convert_unicode=True, \
        assert_unicode=None), primary_key=False)
la relación dentro la misma clase queraría así
    #{ Relations
    
    medic = relation(Medic)
    kit = relation(Kit)
    status = relation(Status)
    room = relation(Room)
    nursea = relation(Nurse, primaryjoin=nursea_id == Nurse.id)
    nurseb = relation(Nurse, primaryjoin=nurseb_id == Nurse.id)
    
    #}

viernes, abril 01, 2011

Python y locale.setlocale

Quiero desplegar las fechas de mis páginas hechas con Turbogears2.0 en español.  Ahora tengo esta salida.

In [5]: datetime.datetime.now().strftime("%a %d de %B del %Y")
Out[5]: 'Fri 01 de April del 2011'

Reviso los locale que tengo instalados en mi servidor, en este caso un Ubuntu Lucid (10.04)

root@kirk:~# locale -a
C
POSIX
en_AG
en_AU.utf8
en_BW.utf8
en_CA.utf8
en_DK.utf8
en_GB.utf8
en_HK.utf8
en_IE.utf8
en_IN
en_NG
en_NZ.utf8
en_PH.utf8
en_SG.utf8
en_US.utf8
en_ZA.utf8
en_ZW.utf8
root@kirk:~#

Si no está el locale que requiero, lo genero, en este caso es: es_MX.UTF-8

root@kirk:~# /usr/sbin/locale-gen es_MX.UTF-8
Generating locales...
es_MX.UTF-8... done
Generation complete.
root@kirk:~#
La generación de locales en Lucid difiere un poco de como lo hacía en Intrepid
Confirmo la generación del locale

root@kirk:~# locale -a
C
POSIX
en_AG
en_AU.utf8
en_BW.utf8
en_CA.utf8
en_DK.utf8
en_GB.utf8
en_HK.utf8
en_IE.utf8
en_IN
en_NG
en_NZ.utf8
en_PH.utf8
en_SG.utf8
en_US.utf8
en_ZA.utf8
en_ZW.utf8
es_MX.utf8
root@kirk:~#

Bien, ya tengo un locale en español que puedo utilizar. Lo activo dentro de mi ambiente python y compruebo.

In [6]: import locale

In [7]: locale.setlocale(locale.LC_ALL, 'es_MX.utf8')
Out[7]: 'es_MX.utf8'

In [8]: datetime.datetime.now().strftime("%a %d de %B del %Y")
Out[8]: 'vie 01 de abril del 2011'


Algo realmente simple me entretuvo mucho tiempo, ya que obtenía errores como:

In [9]: locale.setlocale(locale.LC_ALL, 'es_MX')
---------------------------------------------------------------------------
Error Traceback (most recent call last)

/usr/lib/python2.6/locale.pyc in setlocale(category, locale)
511 # convert to string

512 locale = normalize(_build_localename(locale))
--> 513 return _setlocale(category, locale)
514
515 def resetlocale(category=LC_ALL):

Error: unsupported locale setting

... y encontré poca documentación al respecto, lo pongo aquí para futuras referencias.

sábado, julio 03, 2010

rsync - Guía Rápida

Linux tiene muchas herramientas para copiar y respaldar información. rsync es la favorita para muchos administradores.

rsync utiliza un algoritmo que permite transmitir eficientemente una estructura (como un archivo) a través de un canal de comunicación cuando el receptor ya tiene una versión diferente de la misma estructura. (wikipedia)

Copiar localmente

En su forma más simple lo podemos utilizar para copiar directorios enteros de forma local.
$ rsync -av /home/user/Docs /media/usb/backup/
-av lo ejecuta en modo verbose y archivo, práctica común para copia recursiva, conservar permisos y symlinks.

Copiar remotamente

Tanto el origen como el destino pueden ser otra computadora en la red.
$ rsync -avz /home/user/Docs user@online.backups.com:/backups/
agregamos -z para comprimir los datos sobre la red.

rsync puede tambier borrar archivos que ya no se encuentran en el origen pero si en el destino causa de alguna copia anterior. Es decir, que pasa si ya borramos algunos archivos de nuestro directorio de trabajo y queremos que también sean borrados de nuestro respaldo.
$ rsync -avz --del /home/user/Docs user@online.backups.com:/backups/
--del indica a rsync que borre los archivos que ya no están en el origen.

Directorio o archivos

Importante el último slash '/'. en:
$ rsync -avz --del /home/user/Docs user@online.backups.com:/backups/
estoy copiando el directorio 'Docs' dentro del directorio 'backups' pero si
$ rsync -avz --del /home/user/Docs/ user@online.backups.com:/backups/
estaré copiando los archivos dentro del directorio 'Docs' al directorio 'backups'

Copia incremental

$ rsync --backup --backup-dir=`date +%Y.%m.%d` -av /home/user/Docs/ /media/usb/backup/Docs/
--backup crea un respaldo --backup-dir crea el respaldo dentro de la jerarquía del directorio de destino. También se puede usar --suffix=SUFFIX para agregar un sufijo.

Apenas rascando la superficie de esta poderosa utilería. Para más información manpage.

lunes, marzo 22, 2010

MVC en Python

Últimamente he utilizado herramientas como SQLAlchemy y Turbogears en Python. La verdad me ha gustado mucho la flexibilidad y facilidad con la que puedo escribir scripts para procesar información con SQLAlchemy y me ha gustado mucho el modelo MVC de Turbogears 2, parecido al de Spring en Java, por lo que voy a comenzar a implementar más cosas en python.
Comenzaré con un blog/cms en Turbogears, yo sé que existen muchísimas opciones ya desarrolladas que seguramente son mejores a cualquiera que yo pueda hacer, sin embargo, me sirve para familiarizarme en ambiente de trabajo con TG2.

Instalando Turbogears.
La mejor opción es con Virtualenv, que crea un "ambiente virtual" de python para cargar dependencias, librerías y versiones, etc. Sin embargo en producción a mi me ha funcionado instalarlo junto con el resto de python del servidor. Las instrucciones para instalarlo son sencillas y están aquí.
Creando un proyecto
Es muy simple iniciar un proyecto en Turbogears.

$ paster quickstart
Enter project name: Altadefinicion
Enter package name [altadefinicion]:
Do you need authentication and authorization in this project? [yes]
Selected and implied templates:
tg.devtools#turbogears2 TurboGears 2.0 Standard Quickstart Template

Variables:
auth: sqlalchemy
egg: Altadefinicion
geo: None
package: altadefinicion
project: Altadefinicion
sqlalchemy: True
sqlobject: False
tgversion: 2.0.3
Creating template turbogears2
Creating directory ./Altadefinicion
...
...
...
reading manifest template 'MANIFEST.in'
writing manifest file 'Altadefinicion.egg-info/SOURCES.txt'

Si no recibimos ningún mensaje de error, podemos iniciar la aplicación con:

$ paster serve --reload development.ini

Y apuntando nuestro explorador a http://localhost:8080 podremos ver una página de bienvenida.



Entrando al directorio del proyecto podemos observar una simple estructura:

$ tree -d
.
|-- Altadefinicion.egg-info
|-- altadefinicion
| |-- config
| |-- controllers
| |-- i18n
| | `-- ru
| | `-- LC_MESSAGES
| |-- lib
| |-- model
| |-- public
| | |-- css
| | `-- images
| |-- templates
| `-- tests
| |-- functional
| `-- models
|-- data
| `-- sessions
`-- ez_setup

25 directories

Por default TG2 usa SQLite como motor de base de datos y almacena todo en un archivo llamado devdata.db para crear la base de datos por default con datos de prueba hay que correr otro comando de paster:

$ paster setup-app development.ini

Con esto ya tenemos lista la cimentación para construir una aplicación con python, usando Turbogears 2 y SQLalchemy

martes, febrero 02, 2010

Neither BindingResult nor plain target object for bean name "command"...

Este error me sale frecuentemente y sobre todo después de muchas correcciones o adecuaciones a nuevos controllers en spring-mvc.

Siempre pierdo mucho tiempo tratando de encontrar el error, por que cuando veo el código tanto del jsp como del controllores todo parace perfecto, sin embargo el error persiste.

Cuando encuentro la solución siempre recuerdo que ya lo había hecho así antes y ahora quiero ponerlo aquí para que no se me olvide.

El error sale cuando inserto un nuevo objeto para persistir, no cuando edito uno ya existente y la respuesta está en la salida con succesView
return new ModelAndView(success);

Si successView no está definido me arroja el error, cuando se modifica el objeto successView es editado para mostrar nuevamente la forma de edición, sin embargo cuando el objeto es nuevo, pasa el parámetro de successView que está definido en el dispatcher. Entonces, si no hay successView definido en el dispatcher, me encuentro con ese error.

    <bean id="catalogoEstrategiasFormController" class="com.deuxbits.ssaspps.webapp.controller.CatalogoEstrategiasFormController">
        <!-- property name="validator" ref="beanValidator"/ -->
        <property name="successView" value="redirect:catalogoestrategias.html"/>
        <property name="catalogoEstrategiasManager" ref="catalogoEstrategiasManager"/>
        <property name="userManager" ref="userManager" />
        <property name="catalogoProgramasManager" ref="catalogoProgramasManager" />    
    </bean>





viernes, marzo 20, 2009

Arreglando locale en Ubuntu 8.10 Intrepid

En una nueva instalación del Ubuntu Intrepid, en su versión a 64 bits, batallé dos días para arreglar el problema con el locale, principalmente con perl.

El mensaje era el siguiente:

perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
LANGUAGE = "en_US:en",
LC_ALL = (unset),
LANG = "en_US.UTF-8"
are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").

Intenté de varias formas arreglar el locale con:

# locale-gen en_US.UTF-8
# update-locale en_US.UTF-8
# update-locale-config en_US.UTF-8

Ajustando la configuración de "/etc/environment":
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"
LANGUAGE="en_US:en"
LANG="en_US.UTF-8"


y la configuración de "/etc/default/locale":
LANG=en_US.UTF-8

Pero el error seguía, en mi estación también tengo el Intrepid instalado pero en su versión de 32 bits, y llegué a pensar que el problema venía en los paquetes de 64 bits.

Buscando en google nunca dí con una solución clara al problema y entonces decidí buscar sin especificar la distribución de linuz, para obtener resultados más genéricos y encontré la línea que me ayudó a resolver el problema.

# localedef --no-archive -i en_US -c -f UTF-8 en_US.UTF-8

y después las subsecuentes...

# locale-gen en_US.UTF-8
# update-locale en_US.UTF-8
# update-locale-config en_US.UTF-8

y problema resuelto.

Lo copio aquí para próximas instalaciones.