sábado, 22 de diciembre de 2012

Use zip

The following examples illustrate typical uses of the command zip for packaging a set of files into an "archive" file, also called "zip file". The command uses the standard zip file format. The archive files can therefore be used to tranfer files and directories between commonly used operating systems. 

 zip archivefile1 doc1 doc2 doc3
This command creates a file "archivefile1.zip" which contains a copy of the files doc1, doc2, and doc3, located in the current directory. 

 zip archivefile1 *
This command creates a file "archivefile1.zip" which contains a copy of all files in the current directory in compressed form. However, files whose name starts with a "." are not included. The extension ".zip" is added by the program. 

 zip archivefile1 .* *
This version includes the files that start with a dot. But subdirectories are still not included. 

 zip -r archivefile1 .
This copies the current directory, including all subdirectories into the archive file. 

 zip -r archivefile2 papers
This copies the directory "papers", located in the current directory, into "archivefile2.zip". 

 zip -r archivefile3 /home/joe/papers
This copies the directory "/home/joe/papers" into "archivefile3.zip". Since in this case the absolute path is given, it doesn't matter what the current directory is, except that the zip file will be created there.
The command unzip extracts the files from the zip file.
 unzip archivefile1.zip
This writes the files extracted from "archivefile1.zip" to the current directory. 

jueves, 22 de noviembre de 2012

git crib

Install git (debian)

sudo apt-get install git-core

Workflow

Generate SSH keys


View: https://help.github.com/articles/generating-ssh-keys

Clone a project (svn checkout equivalent)

git clone https://github.com/username/projectname.git
Creates projectname directory and clones the project.

Add a file to the repository

git add filename

Show project status

git status

Commit to the local repository

git commit -a -m "change description"

Upload to the central repository

git push git@github.com:username/projectname.git
or (see user configuration),
git push

Discard all local changes including newly added files

git reset --hard

Discard options

f you want to revert changes made to your working copy, do this:
git checkout .
If you want to revert changes made to the index (i.e., that you have added), do this:
git reset
If you want to revert a change that you have committed, do this:
git revert ...
Taken from: http://stackoverflow.com/questions/1146973/how-to-revert-all-local-changes-in-a-git-managed-project-to-previous-state

Show differences


Update project, download changes (svn update equivalent)

git pull

Show diff between master and local repository

git diff


Branches


Make a new branch

git branch rama

Show branches

git branch

Go to the branch rama

git checkout rama

Merge with master

git branch master (goto the main branch)
git merge "rama" (merge rama with main)


User configuration


User

To automate the pulls:

View the reponame
git config -l

And then
sudo git config remote.origin.url https://{USERNAME}:{PASSWORD}@github.com/{USERNAME}/{REPONAME}.git

Find all *.pyc and remove from the repository

find . -name "*.pyc" -exec git rm -f {} \;

Ignore some types of files (global)

git config --global core.excludesfile ~/.gitignore_global

Edit ~/.gitignore_global and add patterns like *.pyc

Ignore some types of files (local)

At the local repository edit .gitignore and add patterns.

sábado, 17 de noviembre de 2012

Chuleta de git

Instalar git (debian)

sudo apt-get install git-core

Ciclo de trabajo

Generación de claves SSH


Ver: https://help.github.com/articles/generating-ssh-keys

Clonar un proyecto (equivale a checkout de svn)

git clone https://github.com/nombreusuario/nombreproyecto.git
Crea el directorio nombreproyecto y clona el proyecto en él.

Añadir fichero a repositorio

git add nombrefichero

Ver status del proyecto

git status

Hacer commit al repositorio local

git commit -a -m "descripción de los cambios"

Subir cambios al repositorio central

git push git@github.com:nombreusuario/nombreproyecto.git
o bien,
git push


Descartar cambios locales


i
f you want to revert changes made to your working copy, do this:
git checkout .
If you want to revert changes made to the index (i.e., that you have added), do this:
git reset
If you want to revert a change that you have committed, do this:
git revert ...


Fuente: http://stackoverflow.com/questions/1146973/how-to-revert-all-local-changes-in-a-git-managed-project-to-previous-state



Volver a un commit en concreto

git checkout commit_id

véase: http://stackoverflow.com/questions/4114095/revert-to-previous-git-commit

Ver diferencias


Actualizar el proyecto local, bajar los cambios, hacer "update"

git pull

Ver diferencias entre rama de desarrollo y repositorio local

git diff

Ver diferencias entre repositorio local y HEAD

git diff --cached

Ver diferencias entre rama de desarrollo y HEAD

git diff HEAD

Ramas


Crear rama a partir de la actual

git branch rama

Mostrar ramas y en la que nos encontramos

git branch

Situarnos en una rama

git checkout rama

Merge de una rama "rama" con la principal

git branch master (nos situamos en la rama principal)
git merge "rama" (merge de la rama con la principal)


Configurar usuario


Usuario

Automatizar los pulls:

Ver nombre de repositorio
git config -l

Y con el reponame

sudo git config remote.origin.url https://{USERNAME}:{PASSWORD}@github.com/{USERNAME}/{REPONAME}.git

Encontrar todos los *.pyc y eliminarlos del repositorio
find . -name "*.pyc" -exec git rm -f {} \;

Ignorar algunos tipos de fichero (global)

git config --global core.excludesfile ~/.gitignore_global

Editar ~/.gitignore_global y añadir patrones p.e. *.pyc

Ignorar algunos tipos de fichero (local)

En el repositorio local editar .gitignore y añadir los patrones.

miércoles, 17 de octubre de 2012

Campos calculados en django

Las operaciones sobre los objetos model de django se mapean a SQL mediante el ORM (Object-relational mapping). Un problema que tiene el ORM de django es que no tiene soporte directo para los campos calculados, que son aquellos que se obtienen a partir de los valores de los campos del registro para cada registro.

Por ejemplo, tenemos unos movimientos en los que interviene cantidad, precio y comisión. ¿Qué sentido tiene guardar en la BD el campo importe si en realidad ya tenemos toda la información para calcularlo?.

class Movimiento(models.Model):
   cantidad=models.DecimalField()
   precio=models.DecimalField()
   comision=models.DecimalField()

¿Cómo calculamos entonces el importe?. Hay dos alternativas: Una usando una property de python en el propio objeto model y otra usando extra en el queryset. Veamos:

class Movimiento(models.Model):
   cantidad=models.DecimalField()
   precio=models.DecimalField()
   comision=models.DecimalField()

   def _get_importe(self):
      return self.cantidad*self.cambio*(1-self.comision)
   importe = property(_get_importe)

Y ahí tenemos el importe del movimiento via movimiento.importe

La otra solución es usar extra en el queryset para añadir "a mano" el campo en la consulta SQL, sería:

target=movimiento.objects.extra(select={
          'importe': 'cantidad*cambio*(1-comision)',})
for f in target:
    print f.importe

Entonces, ¿Cuál es el problema?. Pues que todo esto son soluciones "de mentirijilla" puesto que realmente el campo importe no existe como tal en el gestor de BD y no podemos hacer cosas como sumar todos los importes haciendo una agregación, así esto

total_importe=modelo.aggregate(Sum('importe'))

Nos genera un error diciendo que el campo importe no existe.

Sin duda, el soporte para campos calculados es uno de las mejoras del ORM que puede trabajarse.


Fuentes:
http://stackoverflow.com/questions/3690343/django-orm-equivalent-for-this-sql-calculated-field-derived-from-related-table

lunes, 8 de octubre de 2012

Localizar las plantillas

Si queremos que los importes monetarios de nuestras plantillas nos salgan (en España) así:

1.256,56 €

en vez de así:

1256.56 €

tenemos que localizar la plantilla y dejar que django haga el trabajo duro.

Para empezar, en settings.py indicamos que queremos usar la localización añadiendo:

DEFAULT_CHARSET='utf-8'
THOUSAND_SEPARATOR= '.'
DECIMAL_SEPARATOR = ','
NUMBER_GROUPING = 3
USE_THOUSAND_SEPARATOR = True
FIRST_DAY_OF_WEEK = 1
LANGUAGE_CODE = 'es-es'
USE_L10N = True


En la plantilla, cargamos l10n y activamos la localización así:

{% load l10n %}
{% localize on %}

blah, blah, blah...

{% endlocalize %}


Y de forma mágica los importes aparecerán correctamente formateados. Para controlar la cantidad de decimales diferentes del estándar también podemos usar el filtro floatformat:precision así:

{{ importe|floatformat:6 }}


Error de codificación de caracteres al generar pdf con django y pisa

Hacía tiempo que me perseguía un pequeño problema al generar pdf desde django/pisa con caracteres utf8>255. Por defecto, pisa usa latin-1/ISO 8859-1 (un byte) para generar los pdf y al transcodificar los caracteres de la template (p.e. el símbolo euro €) me saltaban errores.

En la doc oficial de pisa tenemos que:


pdf = pisa.pisaDocument(StringIO.StringIO(html.encode("UTF-8")), result)


Pero buscando en stackoverflow he encontrado que pisaDocument acepta además el parámetro encoding con el que en realidad le indicamos la codificación que debe usar con lo que queda:

pdf = pisa.pisaDocument(StringIO.StringIO(html.encode("UTF-8")), result, encoding='UTF-8')  

;-)

Minimizar y maximizar en gnome3

Una forma de configurar los botones que queremos en el marco de la ventana es instalar el programa


sudo apt-get install gnome-tweak-tool

Y desde él configurar este y otros aspectos del entorno.

jueves, 4 de octubre de 2012

Los versos dorados (o áureos) de Pitágoras

Atribuido a Lisis, un alumno de Pitágoras, parece ser un resumen de la forma de vida que profesaban los pitagóricos.

Hay muchas versiones en Internet pero me quedo con esta. El original está escrito en hexámetro dactílico que es un tipo de verso de la poesía griega. La traducción está sacada del libro Les Vers Dorés de Pythagore de Fabre-d’Olivet, Éditions L’Age d’Homme, Collection Delphica, 1991. Traducción del francés por M.A.Aguirre.

PREPARACIÓN. 


RINDE a los Dioses inmortales el culto consagrado;
Guarda después tu fe (2): Reverencia la memoria De los Héroes bienhechores, de los Espíritus semi-Dioses (3).

PURIFICACIÓN. 

Sé buen hijo, hermano justo, esposo cariñoso y buen padre (4).
Escoge por amigo, al amigo de la virtud;
Cede a sus suaves consejos, instrúyete por su vida,
Y por un daño ligero no le dejes nunca (5);
Si lo puedes al menos: pues una ley severa Sujeta la Potestad a la Necesidad (6).
Se te ha dado sin embargo combatir y vencer
Tus locas pasiones: aprende a dominarlas (7).
Sé sobrio, activo y casto; evita la cólera.
En público, en secreto no te permitas nunca
Nada malo; y sobretodo respétate a ti mismo (8).
No hables ni hagas nada sin haber reflexionado.
Sé justo (9).
Acuérdate que un poder invencible
Ordena morir (10);  que los bienes, los honores
Fácilmente adquiridos, son fáciles de perder (11).
En cuanto a los males que arrastra consigo el Destino,
Júzgalos tal cual son: sopórtalos; y trata,
En lo que puedas, de dulcificar sus rasgos:
Los Dioses, a los más crueles, no han entregado a los sabios (12).
Como la Verdad, el Error tiene sus amantes:
El filósofo aprueba, o reprueba con prudencia;
Y si el Error triunfa, se aleja; espera (13).
Escucha, y grava en tu corazón mis palabras:
Cierra el ojo y el oído a la prevención;
Recela del ejemplo ajeno; piensa por ti mismo (14):
Consulta, delibera, y escoge libremente (15).
Deja los locos quehaceres sin fin, sin causa.
Debes, en el presente, contemplar el porvenir (16).
Lo que no sepas, no pretendas hacerlo.
Instrúyete: todo se concede a la constancia, al tiempo (17).
Vigila tu salud (18): dispensa con medida,
Al cuerpo los alimentos, al espíritu el reposo (19).
Hay que evitar demasiados o demasiados pocos cuidados; ya que la envidia,
A uno y otro exceso, se ata igualmente (20).
El lujo y la avaricia tienen consecuencias semejantes.
Es necesario escoger en todo, un medio justo y bueno (21).

PERFECCION. 


Que nunca el sueño cierre tus párpados,
Sin haberte preguntado: ¿Qué he omitido? ¿Qué he hecho? (22)
Si está mal, abstente: si está bien, persevera (23).
Medita mis consejos; ámalos; síguelos todos:
A las divinas virtudes podrán conducirte (24).
Lo juro por aquel que gravó en nuestros corazones,
La Tétrada sagrada, inmenso y puro símbolo,
Fuente de la Naturaleza, y modelo de los Dioses (25).
Pero que ante todo, tu alma, fiel a su deber,
Invoque con fervor a estos Dioses, cuyos socorros
Pueden únicamente acabar tus obras comenzadas (26).
Instrúyete por ellos, entonces nada te engañará:
De los diferentes seres sondearás la esencia;
Conocerás de Todo el principio y el fin (27).
Sabrás, si el Cielo lo quiere, que la Naturaleza,
Semejante en todo, es la misma en todo lugar (28).
De manera que iluminado sobre tus derechos verdaderos,
Tu corazón no se alimentará de vanos deseos (29).
Verás que los males que devoran a los hombres,
Son fruto de su elección (30); y que estos desdichados
Buscan lejos de ellos los bienes de los que llevan la fuente (31).
Pocos saben ser dichosos: juguetes de las pasiones,
zarandeados por el vaivén de las olas,
En una mar sin orilla, dan vueltas, cegados,
Sin poder resistir ni ceder a la tempestad (32).
¡Dios! los salvarías abriéndoles los ojos... (33).
Pero no: corresponde a los humanos, cuya raza es divina,
Discernir el Error, ver la Verdad (34).
La Naturaleza les sirve (35). Tú que la has penetrado,
Hombre sabio, hombre dichoso, respira en el puerto.
Pero observa mis leyes, absteniéndote de las cosas
Que tu alma debe temer, distinguiéndolas bien;
Dejando en el cuerpo reinar a la inteligencia (36):
A fin de que, elevándote en el Éter radiante,
Al seno de los Inmortales, ¡seas tú mismo un Dios! (37)


Filtrar entre fechas con django

Siempre se me olvida la sintaxis de range que selecciona entre dos valores inclusives:


self.modelo.objects.filter(fecha__range=(desde, hasta))

viernes, 31 de agosto de 2012

MSL Curiosity NavCams: Anaglyph Panorama of Bradbury Landing Site/ Panorama anaglifo del sitio de aterrizaje Bradbury

Bradbury Landing Site anaglyph panorama
Click para ampliar / Click to zoom

Este es mi segundo trabajo con las imágenes de las cámaras de navegación (NavCams) del MSL tomadas el Sol22. Se trata del sitio de aterrizaje del Curiosity bautizado como Bradbury Landing Site en honor al escritor de ciencia ficción Ray Bradbury, recientemente fallecido. El monte Aeolis queda a la derecha.

La panorámica se ha realizado uniendo 4 fotografias y generando una proyección esteroscópica. El campo de visión es de unos 136 grados horizontales. He usado el pedrusco central como foco.

Algunos datos de las NavCams (Cámaras de navegación)

El sistema de cámaras de navegación del Curiosity tienen las siguientes características:
  1. Determinar el contexto del terreno para planificar la travesía y ayudar al apuntado de las cámaras Mastcam y Chemcam.
  2. Generar una vista de 360 grados con una resolución angular menor que 1 mrad/pixel.
  3. Generar vistas estéreo hasta unos 100 m.
  4. Uso de un filtro en visible.
Las NavCams están montadas en el mástil, en la misma pletina que las cámaras MastCam/ChemCam y están sincronizadas para tomar fotos en estéreo siendo su línea de base de 42cm y quedando a 1.9 m del suelo de Marte.

Posición de las NavCams en el mástil



Como sistema fundamental del Curiosity, las NavCam están duplicadas para redundancia (pares A y B) estando conectadas a placas electrónicas diferentes. Sólo un par está activo al mismo tiempo.

Las CCD provienen de material sobrante del programa MER y tienen 1024x1024 píxeles cuadrados de 12 micrones. Debido a la calidad de los detectores y a las condiciones de operación (-55 grados C) el ratio de señal-ruido es >200:1. Las lecturas son de 12 bits por pixel y tarda 5.4 s en leer una imagen.

Con la óptica que montan, queda un sistema a f/12 sin relación de aumento y con un campo de 45x45 grados que es casi equivalente a un objetivo de 40 mm en una cámara de 35 mm. La resolución angular en el centro es de 0.82 mrad/pixel. La profundidad de campo va de los 0.5 m a infinito con la distancia hiperfocal a 1 m. Con esta configuración, el tiempo de exposición a medio día es de 0.25 s. El sistema usa un sistema de filtros centrado en los 650 nm (que vendría a ser entre el anaranjado y el rojo).


Fuente: THE MARS SCIENCE LABORATORY (MSL) NAVIGATION CAMERAS (NAVCAMS). J. N. Maki1, D. Thiessen1, A.Pourangi1, P. Kobzeff1, L. Scherr1, T. Elliott1, A. Dingizian1, Beverly St. Ange1, 1Jet Propulsion Laboratory/California Institute of Technology, 4800 Oak Grove Drive, Pasadena, CA 91109. Fecha de consulta: 31/08/2012.



sábado, 25 de agosto de 2012

MSL Navcams: my first anaglyph (3D image) / mi primer anaglifo con las imagenes de las navcams

Here is my first attempt to make an anaglyph from the navcams images. I have taken them from the Front Hazcams on (sol 16)


mars anaglypt MSL navcams sol 16
Anaglyph MSL navcams on sol 16


Comments welcome. I have been inspired by Un geólogo en apuros.

jueves, 2 de agosto de 2012

Aún otro ejemplo de try, except, else, finally / Yet another try, except, else, finally example

Estaba pasando datos de una DB mysql a otra en django y me ha salido este "snippet" que ejemplifica el uso de try-except-else-finally.

try contiene el código que queremos proteger
except captura la excepción (usar la clase genérica exception para capturarlas todas)
else código que se ejecutará si no salta ninguna excepción
finally código que se ejecutará en todo caso tanto si hay como si no hay excepción

Las únicas obligatorias son try y except. Si queremos capturar más de una excepción, las ponemos una detrás de otra:

from django.core.exceptions import ObjectDoesNotExist
try:
    cl=Cliente.objects.get(clave=id)
except ObjectDoesNotExist, ex:
    print "el cliente no existe"
except Exception, ex:
    print "se ha producido un error", str(ex)

Ejemplo:


try:  
   try:  
     c=db_marcajes.cursor(MySQLdb.cursors.DictCursor)  
     c.execute("select id, fecha, event, door, user from logs where fecha>%s", (last_marcaje,))  
     rows=c.fetchall()     
   except Exception, e:  
     print "Impossible efectuar consulta"  
     exit(1)  
   else:  
     elapsed = (time.clock() - start)  
     print "Consulta realitzada en", elapsed, "msegs"  
     num_rows=len(rows)  
     print "Numero de filas ", num_rows  
     i=0    
     while i<num_rows:  
       row=rows[i]     
       print "procesando fila ", repr(row)  
       try:  
         em=Empleado.objects.get(pin=row['user'])  
         pt=Puerta.objects.get(pk=row['door'])  
         print ("guardando acceso %s de %s" % (row['user'],em.descripcion,))  
       except Exception, ex:  
         print ("Error de busqueda empleado %s puerta %s: %s", ( row['user'], row['door'], ex, ))  
       else:  
         try:  
           acc=Acceso(  
             empleado=em,  
             es=row['event'],  
             fecha=row['fecha'],  
             puerta=pt,  
             )  
           #acc.save()  
           print "Guardado"  
         except Exception, ex:  
           print ("Error grabando acceso: %s" % (ex,))  
       finally:  
         i+=1  
         last_marcaje=row['fecha']  
   finally:  
     c.close()           
 finally:  
   # guardam darrer marcatje  
   conv=Convenio.objects.get(pk=1)  
   conv.last_marcaje=last_marcaje  
   conv.save()  

viernes, 27 de julio de 2012

Prevenir el borrado de objetos via clave foránea / Prevent object deletion via foreign key

Una de los comportamientos por defecto más problemáticos de django es el de emular el DELETE CASCADE  cuando borramos una clave foránea. Desde luego, hay que ser animalito. Ya me veo a decenas de usuarios noveles preguntándose adonde han ido a parar sus datos después de efectuar un borrado de una clave foránea que tenía objetos relacionados.

La forma de "proteger" los objetos relacionados es via la definición del ForeignKey.on_delete que puede tomar los siguientes valores

CASCADE: Borrado en cascada, comportamiento por defecto.
PROTECT: Protege al objeto relacionado del borrado haciendo saltar la excepción django.db.models.ProtectedError.
SET_NULL: Asigna el valor null a la clave, sólo posible si null es True.
SET_DEFAULT: Pone la clave foránea a su valor por defecto (debe estar definido).
SET(): Asigna a la clave foránea el valor pasado en el set.
DO_NOTHING: No hace nada y deja el comportamiento por defecto de la db. (Si la hemos creado con syncdb, no hará nada)


Una práctica aconsejable en un entorno de usuario sería capturar la excepción via PROTECT y devolver un mensaje de error con la información para el usuario.Por ejemplo:

Si en la definición del modelo Factura usamos

cliente=models.ForeignKey(Cliente, on_delete=models.PROTECT)

Y ahora queremos borrar un cliente que tiene una factura asociada

try:
  cliente.delete()
except django.db.models.ProtectedError, ex:
  return HttpResponse( ('El cliente %s tiene al menos una factura y no puede borrarse.' % (cliente.descripcion,)), 'text/html')

jueves, 5 de julio de 2012

subversion: volver a una versión anterior

Si estás modificando la versión 189 de tu código y quieres volver a la anterior, sólo tienes que hacer:


svn update
svn merge -r 189:188 .
svn commit -m "Volviendo a la version r188"

jueves, 7 de junio de 2012

ubuntu no arranca una sesión X de usuario

Me ha ocurrido que de un momento para otro ubuntu no me deja abrir mi sesión. En cambio con otros usuarios si lo permite. Después de buscar toda una tarde la solución ha sido entrar por cónsola Ctrl+Alt+F1 y hacer

sudo mv ~/.Xauthority ~/.Xauthority.old

miércoles, 6 de junio de 2012

El Lazarillo de Tormes


Acaeció que llegando a un lugar que llaman Almorox, al tiempo que cogían las uvas, un vendimiador le dio un racimo dellas en limosna, y como suelen ir los cestos maltratados y también porque la uva en aquel tiempo está muy madura, desgranábasele el racimo en la mano; para echarlo en el fardel tornábase mosto, y lo que a él se llegaba. Acordó de hacer un banquete, ansí por no lo poder llevar como por contentarme, que aquel día me había dado muchos rodillazos y golpes. Sentámonos en un valladar y dijo:
—Agora quiero yo usar contigo de una liberalidad, y es que ambos comamos este racimo de uvas, y que hayas dél tanta parte como yo. Partillo hemos desta manera: tú picarás una vez y yo otra; con tal que me prometas no tomar cada vez más de una uva, yo haré lo mesmo hasta que lo acabemos, y desta suerte no habrá engaño.
Hecho ansí el concierto, comenzamos; mas luego al segundo lance; el traidor mudó de propósito y comenzó a tomar de dos en dos, considerando que yo debería hacer lo mismo. Como vi que él quebraba la postura, no me contenté ir a la par con él, mas aun pasaba adelante: dos a dos, y tres a tres, y como podía las comía. Acabado el racimo, estuvo un poco con el escobajo en la mano y meneando la cabeza dijo:
—Lázaro, engañado me has: juraré yo a Dios que has tú comido las uvas tres a tres.
—No comí —dije yo— mas ¿por qué sospecháis eso?
Respondió el sagacísimo ciego:
—¿Sabes en qué veo que las comiste tres a tres? En que comía yo dos a dos y callabas.

jueves, 24 de mayo de 2012

De la idea a la realidad

Disfrutad de este pase de imágenes que resume cómo esta empesa idea, diseña y construye los coche-moto del futuro.

http://litmotors.com/about/how/

sábado, 5 de mayo de 2012

#sinCiencia No al recorte en ciencia

Me sumo también a la iniciativa #sinCiencia lanzada por Amazings para concienciar sobre la catástrofe que suponen los recortes en ciencia para el futuro de España.

1) Es cierto que no se forma a los universitarios ni en la la innovación ni en la forma de desarrollar proyectos propios. Por lo tanto, son muchos conocimientos que generan un valor añadido muy pequeño o nulo. Supongo que el Plan Bolonia mejorará este tema.

2) Los empresarios españoles de mi área geográfica no tienen el más mínimo interés en invertir ni siquiera una pequeña parte de los inmensos excedentes generados durante la época de las vacas gordas en proyectos que se salgan de lo tradicional. Esto provoca que la economía acabe dependiendo de uno o dos sectores con lo que se hace vulnerable. Por otra parte, estos sectores suelen requerir mano de obra de baja cualificación. 

Esto contrasta con algunos países (que entraron antes en la revolución industrial) en los que rápidamente se aplicó con éxito la ciencia a la actividad económica. 

En conclusión, si los que vienen por abajo no son innovadores y los que ya están arriba no se gastan un duro en tirar del carro de la innovación pues como que tenemos un problema.

Mi impresión es que se trata de un problema cultural: si no se valora la ciencia, tampoco se valora la innovación. Sin innovación no hay crecimiento económico. 

Un problema cultural no se soluciona a base de meter pasta, se soluciona con voluntad y con el esfuerzo de educar a la población desde muy pequeñitos en valores como la racionalidad, el método científico y el placer de descubir y conocer lo que nos rodea. 

Hay que decir bien claro que es verdad que lo que hace un futbolista es divertido, pero que también lo es diseñar timones de barcos o antibióticos. Y qué decir de encontrar una nueva necesidad e intentar ganar dinero con ello... ¡aunque a veces no se tenga éxito!.

Estoy hablando de un compromiso de toda la sociedad, a todos los niveles, para cambiar nuestras prioridades en pos de conseguir un mejor futuro.

En una sociedad moderna y cohesionada como la nuestra (toco madera), si empezamos ahora deberíamos conseguir resultados en unos diez o quince años. Entre todos, somos capaces de hacerlo.

miércoles, 2 de mayo de 2012

Copiar bajo ssh : scp

Para copiar ficheros via ssh podemos usar el comando scp, con la siguiente sintaxis:

scp /home/usuario/fichero usuario@host:/destino


lunes, 23 de abril de 2012

Dado un numero de semana ¿Cuál es la fecha del lunes? / Given an (iso) week number, Whats the date of Monday?


¿En qué semana cae el 7 de junio de 2012? / What's the week number of june 7, 2012?
datetime.date(2012, 6, 7).isocalendar()[1]
23
¿Cuál es el lunes de la semana 23 de 2012?. What's the monday of the week 23 of 2012?

time.asctime(time.strptime(('%s %s 1' % (2012, 23,)), '%Y %U %w'))
'Mon Jun  4 00:00:00 2012' 



viernes, 23 de marzo de 2012

Apache redireccionar ciertas ip hacia https


Queremos que sólo ciertas ip tengan acceso a contenido con http, y las demás las queremos redirigir a https. ¿Cómo lo hacemos?.

Podemos usar mod_rewrite con estas reglas:

       RewriteEngine On
       RewriteCond %{REMOTE_ADDR} !^192\.168\.1\.4$
       RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}

En este caso, sólo la dirección 192.168.1.4 tendría acceso via http, los demás serían redireccionados a https.

Apache redirect some ip to https

We want to allow some ip to http an redirect the rest to https. How can we do it?

We can use mod_rewrite with these rules:

       RewriteEngine On
       RewriteCond %{REMOTE_ADDR} !^192\.168\.1\.4$
       RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}


In this case only 192.168.1.4 gets access to http, other ips will be redirected to https.

jueves, 22 de marzo de 2012

Yet another apache ssl installation manual on debian ubuntu / Aún otro manual de instalación de apache ssl en debian ubuntu

Nunca había instalado apache con SSL en debian. El proceso es muy sencillo:

Activar el módulo ssl:
sudo a2enmod ssl

Crear el certificado; primero lo creamos y luego lo copiamos en el directorio por defecto:

$mkdir ssl
cd ssl
openssl genrsa -des3 -out server.key 1024
openssl rsa -in server.key -out server.key.insecure
openssl req -new -key server.key -out server.csr
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

Nota: si no queremos que cada vez que arrancamos el servidor http nos pida la clave del certificado hacemos,

openssl rsa -in server.key -out server.key.nopass

Y ahora "instalamos" el certificado,

sudo cp server.crt /etc/ssl/certs
sudo cp server.key.nopass /etc/ssl/private

En nuestro fichero de configuración de apache tenemos que añadir algo parecido a esto:
virtualhost *:443
DocumentRoot /var/www/blahblah
SSLEngine on
SSLCertificateFile /etc/ssl/certs/server.crt
SSLCertificateKeyFile /etc/ssl/private/server.key.nopass

....

Ahora rearrancamos el servidor con sudo service apache2 restart, y ya tenemos soporte SSL. Naturalmente, como el certificado lo creamos nosotros, los navegadores nos avisarán de que no ha sido emitido por una entidad de confianza pero tendremos las comunicaciones con el servidor aseguradas por https.

domingo, 11 de marzo de 2012

Usar cliente Mysql python en windows

Si has intentado, sin éxito, instalar el módulo MySQLdb en windows tal vez te interese pasar por este sitio web y bajarte el instalador para windows en vez de tener que compilar las librerías en tu PC ;-)

jueves, 8 de marzo de 2012

Paso de parámetros a programas en python / How to pass parameters to a python program

Para pasar parámetros a los programas se usa el módulo getopt, que nos permite trabajar cómodamente con ellos.

En primer lugar tenemos que saber que el contenido de la línea de comandos nos viene en forma de lista en sys.argv.

Si invocamos un programa en python, como parámetros nos interesará todo lo que venga después de la primera posición, es decir sys.argv[1:], así que en general llamaremos a una función main así
if __name__=='__main__':
 main(sys.argv[1:])

Ahora veremos el funcionamiento de getopt, os paso un ejemplo:
def main(argv): 
 try:      
  opts, args = getopt.getopt(argv, "hps:", 
                                                  ["help", "prn", "server="])
 except getopt.GetoptError:          
  print "Opcion no valida"
  usage()                         
  sys.exit(2)                     
  
Vemos que getopt toma tres argumentos:

  1. El primero es la cadena que le pasamos para analizar
  2. El segundo son las opciones en forma corta, las que se usan con la sintaxis -h, -p, -s="algo"
  3. El tercero son las mismas opciones pero en forma larga que se usan con dos guiones --help, --prn, --server="algo"
Observamos que las opciones en forma corta seguidas de dos puntos o en forma larga seguidas del símbolo igual esperan que les pasemos un argumento. Getopt se encarga de que si al programa le pasamos alguna opción que no esté en la lista salte la excepción GetoptError.

Como ahora ya tenemos las opciones y sus posibles valores, el tratamiento será algo similar a esto
for opt, arg in opts:                
  if opt in ("-h", "--help"):      
   usage()                     
   sys.exit()                  
  elif opt in ('-p', "--prn"):                
   p_pantalla=True             
  elif opt in ("-s", "--server"):          
   r_server=arg            

Si hay algún tipo de error durante el parseado de los argumentos, es una buena idea dar una ayuda al usuario antes de terminar el programa, por ejemplo
def usage():
 print """
Opcions:
--help (-h)\t\tTreu aquesta ajuda
--prn (-p)\t\tTreu impressio de tickets per pantalla
--server (-s)\t\tServidor actualització
"""


Y esto es todo por hoy

miércoles, 7 de marzo de 2012

El cierre de library.nu o la quema de la biblioteca de Alejandría

El cierre por parte de un juez de Munich del sitio web library.nu a instancias de un grupo de 17 editoriales, está siendo comparada por algunos de los partidarios de la web con la quema de la biblioteca de Alejandría. 

library.nu era el sitio web de compartición de ebooks técnico-científicos más importante de Internet.

No voy a entrar una vez más en el cansino asunto de si los propietarios legales de los derechos de los textos (no necesariamente los autores) deben poder cerrar un sitio web donde un tercero ha colgado un link. Lo único que si parece probado es que la mayoría de las descargas provenían de países del tercer mundo y ese es el verdadero drama en todo esto.

No estamos hablando de un peliculasyonkis o de un seriespepito, estamos hablando del cierre de un sitio que compartía enlaces a libros de ciencia y tecnología que de ninguna otra manera van a ser accesibles por los habitantes de un tercer mundo hambriento de conocimiento, al que se le van a cortar más si cabe los recursos necesarios para progresar.

Pero es que además la industria editorial sigue sin darse cuenta de que los ricachones del primer mundo van a comprarse el libro en papel si este les es útil. Yo mismo he pedido varios libros en amazon de Estados Unidos que había encontrado y "hojeado" en library.nu. Si no fuera por este sitio nisiquiera tendría idea de su existencia puesto que no se distribuyen en España.

Creedme lectores cuando afirmo que si no se democratiza de alguna forma justa el acceso al conocimiento, e Internet parece el medio idóneo para hacerlo, vamos a observar el aumento de las injusticias en el mundo.

Os dejo este link por si os es de utilidad.

Para leer más:

Artículo de Christopher Kelty, profesor de la U. de California, en Aljazeera: The disappearing virtual library. Donde afirma que "leer será tener una copia ilegal en el cerebro" y que "los bárbaros que pusieron la industria editorial de rodillas no eran otros que estudiantes de cada rincón del planeta".

Artículo del periódico Huffington Post anunciando el cierre: Library.nu, Book Downloading Site, Targeted In Injunctions Requested By 17 Publishers.

lunes, 5 de marzo de 2012

Acceso remoto a servidor Mysql

Para acceder remotamente a un servidor Mysql lo mejor es usar ssh y hacerlo en local desde la shell pero en los casos en que tengamos que conectar necesariamente desde otro host lo podemos hacer de la siguiente forma:

1.- Editamos (/etc/mysql/my.cnf en Debian, /etc/my.cnf en CentOS) y ponemos la bind-address como la IP del propio servidor. Reiniciar el servicio mysql. Esto hará que escuche en el puerto 3306 del propio servidor en vez de sólo el bucle local localhost.

bind-address = ip_servidor

Si está presente la directiva skip-networking hay que comentarla

2.- Crear un usuario remoto. Entar en mysql y hacer

GRANT ALL ON nombre_db.* TO 'usuario'@'ip-usuario' IDENTIFIED BY 'password';

O si queremos otorgar acceso remoto a root:


GRANT ALL PRIVILEGES on *.* TO 'root'@'%' IDENTIFIED BY 'password' WITH GRANT OPTION;
FLUSH PRIVILEGES;


3.- Abrir el puerto 3306 con iptables,
#iptables -A INPUT -i eth0 -s ip_remota -p tcp --destination-port 3306 -j ACCEPT
#service iptables save
O bien, si usamos ufw,
sudo ufw allow proto tcp from ip/mascara to any port 3306

domingo, 4 de marzo de 2012

El ATV3 Edoardo Amaldi, preparado para partir

El Automated Transfer Vehicle 3 (ATV3) "Edoardo Amaldi" está preparado para partir hacia la ISS el día 23 de marzo de 2012 a las 04:31 GMT.

La nave es un cargero espacial de 20 T, con 6.6 T de carga útil que es capaz de navegar por si sola hasta la Espación Espacial Internacional (ISS) y amarrar automáticamente con una precisión inferior a seis centímetros.

El astronauta europeo André Kuipers será el encargado de supervisar la maniobra de atraque.

Siguiendo la estela de sus predecesores, el cargero suministrará a los tripulantes combustible, comida, agua, oxígeno. También se incluye un sistema que recicla la orina en agua potable.

La retirada del transbordador espacial por la NASA ha obligado a la ESA a disponer los procesos de  fabricación de manera que se consiga una cadencia de un cargero al año. Esto es necesario ya que en este momento sólo los ATV y los japoneses HTV están capacitados para llevar grandes cargas hasta la ISS. Junto a las Progress rusas, también pueden aumentar la órbita de la Estación así como hacerla maniobrar en caso de peligro de choque con basura espacial. En estos momentos ya se está trabajando en el ATV4 "Albert Einstein".

El Edoardo Amaldi (bautizado así por el gran físico italiano) será lanzado por un Arianne 5 desde el espaciopuerto de la ESA en la Guayana Francesa. Será la carga más pesada lanzada en toda la larga história del lanzador. Después de 171 días de misión, el ATV3 será desorbitado y se desintegrará en la atmósfera no sin antes haber sido cargado con toda la "basura" que ha ido generado la ISS desde la última visita de un cargero.


jueves, 1 de marzo de 2012

Con qué día empieza la semana en python? What's the first day of the week in python?

Venga, vamos a jugar un poco más con las fechas. ¿Con qué día empezó esta semana?.

d=datetime.date.today()
delta=datetime.timedelta(days=1)
fecha_inicio_semana=d-(delta*(d.isoweekday()-1))

Warning, for the the non-ISO calendars such as UK/USA: the function isoweekday assumes that the first day of the week is Monday.

miércoles, 29 de febrero de 2012

Menor de edad en python / Legal age in python

Hoy día 29/02/2012 (29 de febrero de año bisiesto), me he encontrado con que el algoritmo que usaba para el cálculo de minoría de edad me ha fallado. Después de varias indagaciones he dejado este como bueno:

from datetime import date, datetime, timedelta

def menor_edad(nascut):
  """ accepts a string date in iso format Y-m-d and returns True if
        it is in spanish legal age today """
  t=timedelta(seconds=31556926*18)
  fa_divuit=date.today()-t
  fecha=datetime.strptime(nascut, '%Y-%m-%d').date()
  return (fecha > =fa_divuit)


lunes, 27 de febrero de 2012

Presentación del vector VEGA de la ESA

Aunque sea con un cierto retraso después del lanzamiento inaugural, en este vídeo del constructor aeroespacial italiano Avio podemos ver alguna de las principales características del lanzador. Vega: Un gran trabajo de los italianos con gran participación española.

En este otro vídeo de Euronews hay entrevistas y se profundiza en los detalles más innovadores del proyecto tales como la estructura hecha en materiales compuestos con el método del "filament winding".


viernes, 24 de febrero de 2012

Filtrado fácil de clave foránea en una ficha de django

Es sabido que django mapea las claves foráneas a un ModelChoiceField en ModelForm, pero ¿qué ocurre cuando sólo queremos seleccionar un subconjunto de ellas?.

Por ejemplo, supongamos que estamos dando de alta un empleado y a la hora de seleccionar su categoría sólo queremos que aparezcan las que están asociadas a su sección.

Tenemos la ficha:


class AddForm(forms.ModelForm):
  class Meta:
   model=Empleado
    fields=('clave', 'descripcion', 'categoria', 'jefe_seccion', 'email',)


La propiedad queryset del campo categoría estará definida por defecto como Categoria.objects.all(), así que hacemos:

p=AddForm()
p.fields['categoria'].queryset=Categoria.objects.filter(seccion=self.seccion)

Con lo que en la ficha sólo nos saldrán las categorías de la sección actual.
Otra forma más complicada consiste en pasarle el parámetro al constructor de la clase pero hay que hacerlo con los kwargs por que si no tendremos problemas en cuando queramos usar la ficha para editar.

class AddForm(forms.ModelForm):
  categoria=forms.ModelChoiceField( label=u'Categoría', queryset = Categoria.objects.none())
  def __init__(self, *args, **kwargs):
   sec = kwargs.pop('sec', None)
   super(AddForm, self).__init__(*args, **kwargs)
   if sec:
    self.fields['categoria'].queryset = Categoria.objects.filter(seccion = sec)
  class Meta:
   model=Empleado
   fields=('clave', 'descripcion', 'categoria', 'jefe_seccion', 'email',)

sábado, 11 de febrero de 2012

Procesador de texto "Sublime Text"

No soy yo muy dado a cambiar mi preciado vi para programar. Sin embargo, me he encontrado con este procesador de texto (hecho en python) que tiene versiones para Linux, Win y Mac. Tiene muy buena pinta. Su nombre es Sublime Text.

Para empezar a usarlo ve a una carpeta que tenga código. Haz Project->Save As... luego Project->Add Folder.

 Voy a probarlo una temporada a ver que tal.


martes, 7 de febrero de 2012

Configurar logging en python y django con yaml

Os paso el esquema de logging que utilizo en mis aplicaciones por si os puede servir de ayuda. Lo he ido montando a partir de diversas fuentes y experiencias.

Un punto importante es dejar el fichero de configuración del logging fuera del programa para independizar mejor el sistema de logging de la aplicación.

Este puede ser un ejemplo del fichero de configuración en yaml (logger.conf):


version: 1
formatters:
  brief:
    format: '%(name)s - %(levelname)s - %(message)s'
  simple:
    format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
  detailed:
    format: '%(asctime)s %(module)-17s line:%(lineno)-4d %(levelname)-8s %(message)s'
  email:
    format: 'Timestamp: %(asctime)s\nModule: %(module)s\n Line: %(lineno)d\nMessage: %(message)s'
handlers:
  console:
    class: logging.StreamHandler
    level: DEBUG
    formatter: brief
    stream: ext://sys.stdout
  file:
    class: logging.handlers.RotatingFileHandler
    level: INFO
    formatter: detailed
    filename: app.log
    mode: a
    maxBytes: 10485760
    backupCount: 5
  smtp:
    class: logging.handlers.SMTPHandler
    level: ERROR
    formatter: email
    mailhost: smtp.xxxxx.es
    fromaddr: yyy@xxxx.es
    toaddrs:  yyyy@xxxx.es
    subject:  Error en aplicacion XX
root:
  level: DEBUG
  handlers: [console]

Se observan los formateadores de los mensajes y los handlers para la cónsola, fichero y correo. Por defecto el logging sale por cónsola, si queremos que los errores salgan por email handlers de root sería [console, smtp], para fichero que rotara cada maxBytes [file], etc... Realmente cómodo.

Para cargar la configuración, primero parseamos el yaml (PyYAML) y luego lo enviamos a dictConfig, que realiza la configuración así:


import yaml
import logging


#nos aseguramos de que dictConfig esté presente (python 2.7), sino, lo cargamos de la libreria de django 



try: 
    from logging.config import dictConfig 
except ImportError: 
    from django.utils.dictconfig import dictConfig

# Cargamos la configuracion del fichero, la parseamos y la enviamos a dictConf
try:
    logging.config.dictConfig(yaml.load(file('logger.conf','r')))
except Exception, e:
    print "Error configurating logger ", str(e)

Ahora ya podemos el usar el sistema. En general, es conveniente nombrar cada logger con el nombre de su módulo para que cuando haya muchos mensajes sepamos cuál los está enviando, para ello podemos usar el valor de __name__, así:

logger=logging.getLogger(__name__) # __name__ contiene el nombre del modulo
logger.debug("blah blah")

Si queremos que cada clase tenga su logger privado, tenemos que tomar una pequeña precaución para evitar un error si otros usan una librería nuestra con otro sistema de handles,

Declaramos una clase "nula":

class Nullhandler(logging.Handler):
   def emit(self, record):
      pass

y ahora, en la clase hacemos

class Clase():
   def __init__(self):
      self.__logger=logging.getLogger(__name__)
      self.__logger.addHandler(NullHandler())
      self.__logger.debug("clase iniciada")

Como veis es un tema un poco denso pero que una vez bien configurado nos será imprescindible para trazar correctamente las aplicaciones. Espero que os haya ayudado.

lunes, 30 de enero de 2012

Obtener valor de radiobutton con JQuery

Si tenemos un grupo de radiobutton,s podemos conocer cuál ha sido seleccionado con

  $("input[name='nombre de grupo']:checked").val(); 

Fuente: Blog de Victor Puertas