javisantana.com

mejor me quedo como estoy

Hace hoy 9 años me operaron de corazón. Era un problema congénito que en palabras textuales del médico “lo resolvía con una mano atada a la espalda” a pesar de que ya era un caso grave (de hecho mi parte derecha del corazón es más grande de lo normal).

Momentos antes de entrar a darme una sesión de rayos X un día antes de la operación, miré por la ventana del hospital y me pegó un “flasazo” de cordura: “y si mañana estiro las punteras en la operación?”. Por suerte (*) estuve dentro del 99% de pacientes que salen vivos y coleando.

Sin embargo, ese momento se quedó bien fijado en mi cabeza y evolucionó a peor, tuve unos meses en los que pensé que aquello no podía haber ido tan bien como los médicos decían y que tal vez el parche que me pusieron se soltaría y moriría el día menos pensado.

Los meses que aquella idea peregrina duraron (el tiempo dio la razón a los médicos) han sido, con mucho, los meses más felices de mi vida.

Y este post es para leermelo cuando tenga que tomar alguna decisión importante y piense: “mejor me quedo como estoy”.

(*) en realidad no fue suerte, fue gracias al trabajo de mucha gente durante años y posiblemente de muchos muertos con el mismo problema que yo.

5 minutos

Cuando mi familia decidio que yo tenia que estudiar una carrera universitaria decidieron que yo debia a ir a una de esas residencias universitarias. No,

no de esas que estas pensando donde la juerga es asignatura troncal,

era mas bien de esas aburridas donde te semiobligan a hacer lo que realmente debes hacer.

Una de las condiciones para entrar y permanecer en ella era hacer 5 minutos de reflexion en la capilla. Visto asi daba algo mas que miedo,

la palabra secta se podia leer en mi cara cuando me lo comentarom, pero bueno,

el resto de cosas estaba bien, asi que tragamos. Tampoco me llegaba el aire al cuello, no habia muchos mas sitios donde las novatadas estuviesen prohibidas.

anyos despues, habiendo reflexionado durante todos los dias durante 3 anyos, me doy cuenta que aqeullos minutos en los que parabas, mientras contabas los segundos para irte, eran un ejercicio realmente bueno. En esos minutos podia organizar el dia, pensar sobre las metedura de pata y en general sobre cosas que ahora no tengo tiempo de pararme a pensar.

Ahora es cuando deberia darme la vuelta y dar las gracias por aquello,tal vez lo haga.

pd: escrito desde el kindle,siento las tildes y enyes.

El camino corto

Cuando comencé a crear agroguía, hace ya casi 5 años, tuve la suerte de tener claro lo que quería resolver, y digo suerte porque la mayoría de proyectos de tecnología que veo a mi alrededor no tienen casi nunca claro a donde quieren llegar (esto es tema para otro post). El primer paso para resolver un problema es tener claro el problema. Dicho esto desarrollé una aplicación que resolvía un solo problema, sin ningún tipo de funcionalidad añadida.

Esto tiene sus cosas buenas y sus cosas malas. Si desarrollas un producto y lo vendes vas a tener que escuchar muchas veces “el software de la competencia hace X e Y, el tuyo no”, te van a aconsejar miles de veces, de personas que saben y que no saben que deberías hacer tal o cual funcionalidad que será un éxito en el número de ventas. Normalmente la gente te agradece mucho más el hecho de que mantengas la aplicación simple a lo que protesta por no tener tal o cual cosa, que, aunque lo veas como fundamental para tu negocio, normalmente se puede pasar sin ello si el producto te resuelve tu problema principal. Somos así de tontos.

Sin embargo pasa el tiempo y ves como mucha gente te solicita cosas muy concretas que te encajan. Pero la cuestión aquí no es que encajen porque un señor muy listo haya sacado su bola de cristal y haya pensando cierta funcionalidad que los usuarios necesitarán, ni encajan porque tenga claro que voy a vender mucho más por ello, si no que lo hace porque en parte me siento en deuda con la información que la gente me está dando (esta es otra cosa que cuando cuento a la gente cercana no entienden “esto es un negocio, javi”).

Ves que encaja y la desarrollas, pero entonces piensas en los usuarios de tu producto y te dices: “no voy a joder a esta gente ahora que ya se ha acostumbrado a usar el producto”. Así que hace un tiempo decidí dos cosas:

- nunca cambiaría el “camino corto”, esto es, la forma de usar la aplicación para resolver el problema original.
- nunca añadiría funcionalidad que entorpeciese la resolución del problema original, esta siempre debe ser “opcional”.

En resumen, de las releases de agroguía nunca se ha tocado el planteamiento original de empezar a funcionar dando a un solo botón, ni como se interpretan los datos, eso sí, se ha añadido funcionalidad, pero siempre opcional (de hecho un tiempo la vendimos aparte), el cliente si se actualizaba iba a seguir funcionando exactamente igual pero si quería podía usar las características añadidas. También tengo que destacar que las releases de agroguía no suelen ser para añadir funcionalidad, si no para mejorar la que hay.

Escribo este post ahora como “autocastigo”. La última release que tenía planeada de agroguía rompía una de esas reglas, no de forma muy importante en mi opinión, pero que tras las primeras pruebas con gente real resultó no ser la mejor idea.

Son cosas como estas las que te recuerdan lo importante que es seguir una filosofía.

scrapper multiproceso en python

Nota inicial: Si no te gusta python puede que este post te haga cambiar de opinión :)

Una de las mejoras de Python 2.6 (en estos momentos vamos por la 2.7, que será la última de la rama 2.x) es el módulo multiprocessing. En pocas palabras viene a ser un módulo para trabajar con procesos de la misma forma que se hace con threads, de hecho en un subconjunto de la funcionalidad puedes cambiar threads por procesos cambiando un solo import.

Sin embargo el módulo multiprocessing añade cosas muy interesantes como la posibilidad de trabajar con pool de procesos. Veamos un ejemplo.

Imaginemos que tenemos que bajar una serie de ficheros pdf para posteriormente extraer información de ellos. Una primera aproximación sería esta:

  
import urllib  
import urllib2  
  
reg\_nos = \[16738, 17288, 18162, 18776, 18868, 19116, 19223, 19505\];  
pdf\_url = 'http://www.mapa.es/agricultura/pags/fitos/registro/sustancias/pdf/%s.pdf'  
  
def fetch\_url(url, params={}):   
    return urllib2.urlopen(url).read()   
  
def save\_url\_as\_file(url, filename):  
    open(filename,'wb').write(fetch\_url(url))  
      
def download\_pdf(reg\_no):  
    f = '%d.pdf' % reg\_no  
    save\_url\_as\_file(pdf\_url % reg\_no, f)  
    print "\\t- %s downloaded" % f  
  
\# tests  
def single(regs):  
    for u in regs:  
        download\_pdf(u)  
  
single(reg\_nos)  

(puedes verlo mejor con sintáxis coloreada en github)

Para 4 míseros ficheros no merece la pena hacer más, pero imaginemos que queremos bajarnos miles y que además lo tenemos que hacer periódicamente, el tiempo en bajarse todos esos ficheros es alto. Lo primero que se nos ocurre es usar concurrencia: lanzando una serie de hilos/procesos que vayan bajando los ficheros aceleraría sensiblemente el proceso (de hecho así lo hacen los navegadores cuando se bajan los ficheros que referencia el HTML).

En python esto traducido a código ocupa mucho menos que explicarlo:

  
def download\_multi(regs, nprocesses=4):  
    pool = Pool(processes=nprocesses)   
    pool.map\_async(download\_pdf, regs).get()  

Usando multiprocessing.Pool python se encarga de lanzar los procesos y preparar una cola para enviarle a la función que especificamos en el primer parámetro.

Este es un uso de multiprocessing, pero tiene otros muchos muy interesantes.

Podéis ver todo el código en github y ejecutar el pequeño benchmark:

  
q6:smll javi$ python fetch.py   
        - 16738.pdf downloaded  
        - 17288.pdf downloaded  
        - 18162.pdf downloaded  
        - 18776.pdf downloaded  
        - 18868.pdf downloaded  
        - 19116.pdf downloaded  
        - 19223.pdf downloaded  
        - 19505.pdf downloaded  
2.30190205574  
        - 18776.pdf downloaded  
        - 17288.pdf downloaded  
        - 18162.pdf downloaded  
        - 16738.pdf downloaded  
        - 19116.pdf downloaded  
        - 18868.pdf downloaded  
        - 19505.pdf downloaded  
        - 19223.pdf downloaded  
0.807252883911  

Un incremento un poco menor de 4X, el número de procesos que lanzo en el pool.

Últimamente uso este módulo para muchísimas tareas ya que el uso es prácticamente directo si la aplicación está bien modularizada y permite aprovechar la potencia de las máquinas actuales (en mi caso un dual core).

Bonus Track - threads

Con threads también es posible hacerlo, pero lamentablemente el módulo threading no tiene la funcionalidad Pool, así que debemos emularla.

Antes de pasar a la implementeación está bien decir que desde hace cosa de dos años hasta ahora se ha criticado mucho el modelo multithread de python debido a que existe una cosa llamada GIL (Global Interpreter Lock) que hace que solo pueda estar ejecutándose un hilo al mismo tiempo en el intérprete python. A pesar de ser hilos nativos hay un lock que evita que dos hilos se puedan ejecutar al mismo tiempo. Si quieres saber un poco más sobre el GIL hay una presentación excelente de maestro Dave Beazley.

Es para llevarse las manos a la cabeza, pero esto no quiere decir que el desarrollo con hilos en python esté “prohibido”, símplemente hay que saber para qué se puede o no usar. En este caso el uso de threads, a pesar del Lock es muy interesante, ya que al ser tareas fundamentalmente de Entrada/Salida no hay problemas de bloqueo entre hilos (la explicación más en detalle en la presentación que he citado antes).

Sin más, usando Queue (otro módulo python mágico), una cola FIFO sincronizada la tarea es más o menos simple:

  
def threaded(regs, nthreads=4):  
    # ripped from http://www.dabeaz.com/generators/Generators.pdf  
    def consumer(q):   
        while True:  
            item = q.get()   
            if not item: break   
            download\_pdf(item)  
  
    in\_q = Queue.Queue()   
      
    # start threads  
    ths = \[threading.Thread(target=consumer,args=(in\_q,))   
                for th in xrange(nthreads)\]  
    for x in ths: x.start()  
  
    # put files to download  
    for i in regs:  
        in\_q.put(i)  
  
    # put end guards  
    for th in xrange(nthreads): in\_q.put(None)  
  
    # wait to finish  
    for x in ths: x.join()  

El desarrollo por el desarrollo

El otro día estaba leyendo la realmente buena entrevista al creador de C++, Bjarne Stroustrup. No es el tema de este post, pero me ha parecido una entrevista de las que merece la pena repasar de vez en cuando, igual que el famoso discurso de Steve Jobs.

Creo que no podría estar más de acuerdo con lo que dice este hombre y suscribiría cada una de las frases, pero hay una que últimamente me da que pensar. En el último párrafo de la entrevista:

Know some non-computer field of study well — math, biology, history, optics, whatever. Learn to communicate effectively in speech and in writing. Spend an unreasonable amount of time on some difficult topic to really master it. Try to do something that might make a difference in the world.

Y es que en mi últimamente-más-activa faceta de comercial me he dado cuenta como hablando con gente de otros ámbitos, en concreto del agrícola, tienen una serie de problemas que como programador de corazón que soy pienso: “Si este señor supiese programar un mínimo haría maravillas”.

Me considero un privilegiado por tener conocimientos de un área muy diferente al de la programación y creo que es fundamental que el desarrollador tenga esos conocimientos. No pasa un día sin que vea a un desarrollador trabajando en cosas que no van a resolver ningún problema y casi siempre es porque no tienen la visión de la persona que tiene ese problema (o directamente porque no hay problema :). Además NO creo demasiado en el consultor que va al cliente, éste le explica su problema y le surge la solución mágica para su problema, siempre he creído que la solución correcta surge del verdadero entendimiento de la materia y eso solo pasa cuando estás a pie del cañón.

Además, no creo que haya cosa más reconfortante para un desarrollador es ver como algo que ha creado él se use para solucionar un problema.