Publicado el 11 de enero de 2012 | Categoría/s: Web 2.0

Desde hace muchísimo tengo en mi otro blog una sección que se llama "De por ahí, cosas que me parecieron interesantes". Es un feed de links a contenidos de toda índole (política, literatura, cine, fotografía, deportes, programación: intereses anchos) con un brevisimo extracto como descripción que fui alimentando de diversas formas a lo largo del tiempo: usé delicious, usé mi twitter incorporó la opción de "Compartir" se convirtió en la manera por defecto: si algo me gustaba, un click en compartir y listo. Eso permitia que otros usuarios de Reader que me "seguian" pudieran ver mis posts compartidos pero a la vez generaba un feed rss que alimentaba la sección de mi blog.

Con la aparición de Google+ aquella simple y muy usada funcionalidad se vió desplazada por el botón "+1" y por un "compartir" engañoso que sólo permite compartir en Google+

Para colmo, cuando uno hace "+1" en algun contenido (dentro de Google Reader o en cualquier lado donde esté el botón) se lista en una página publica pero que no tiene feed RSS! . Larry Page, (doblemente) te odio.

Frustración colectiva

La bronca por los cambios en Reader no es sólo mia, sino que generó una petición firmada por más de 13000 usuarios que manifiestan que "algunos ven estos cambios como un intento errado para forzarnos a usar Google Plus, pero nosotros amamos la utilidad, simplicidad y funcionalidades sociales limitadas que ofrece Google Reader".

Mientras tanto, muchos (1, 2, 3) intentan parchar el atropello.

Mi solución

"¿Así que no me dejás compartir donde yo quiera, el algoritmo de tu madre!?" vociferé, y no sin premeditación agarré Yahoo! Pipes (una genialidad, hay que decirlo) para scrappearlos a puro golpe de mouse y sin salir del navegador. El resultado fue este pipe del que se puede obtener, por ejemplo, un feed RSS 2.0 de mis +1s . Podés obtener el feed de otros usuarios así:

http://pipes.yahoo.com/pipes/pipe.run?_id=770fc166fd07a0f6e67fe63f513b05dc&_render=rss&google_plus_id=[GOOGLE_PLUS_ID] donde [GOOGLE_PLUS_ID] es el numero de identificación del usuario en cuestión.

Agregás tu Google Reader el RSS de los usuarios a los que queres seguir y listo, hemos vuelto a compartir con un solo click (y donde queramos).

Comentarios () | Link

Publicado el 11 de noviembre de 2011 |

Mi computadora de escritorio, la primera que compré trabajando como desarrollador de software, es del 2005. La "morocha", una AMD64 con placa madre Asus K8N que se la sigue bancando.

JPEG - 91.9 KB

Aquel año me enamoré, no por primera vez, pero sí mucho. Aunque resultó una historia complicada, es lindo recordar cuanto uno es capaz de amar y ser amado. Y reirse, también, de cuán estúpido se puede ser en ese estado.

El tatuaje digital

Hay gestos de amor que no miden consecuencias y son dificiles de comprender. Yo, por ejemplo, decidí "hackear" la BIOS de mi maquina recién comprada y poner una foto de aquella mujer amada omo pantalla de splash, esa que se muestra apenas se enciende la computadora, mucho antes de cargar cualquier sistema operativo.

Si bien por entonces ya usaba linux, para hacer esto usé una aplicación oficial de Asus que venía en el Windows instalado, que pronto, a falta de espacio en disco y necesidad, desapareció.

La historia siguió, por supuesto. Aquel amor terminó pero esa imagen quedó allí, tatuada. Mientras vivía solo mucho no me importaba, quizas porque reiniciaba muy poco la computadora, quizas porque uno naturaliza una cicatriz hasta volverse indiferente.

Cuando un amor nuevo llegó y más tarde la convivencia, encender la Morocha cada mañana empezó a hacerse un momento gracioso pero incómodo. Para colmo, aun estando dispuesto a reinstalar Windows con este único propósito, aquel programita de Asus se me hizo inconseguible .

Hasta que hoy, seis años despues, me animé a la cirugía.

Si no podés cambiar el tatuaje, cambiá de piel

Encontré un firmware actualizado para la BIOS. Aunque no hubiera problemas con la que tenía, actualizarlo traería el splash original de Asus.

El problema, felación habitual a Microsoft de las fabricantes de hardware, es que la utilidad para actualizar BIOS es para D.O.S.. WTF!

La solución es usar FreeDOS

  1. # obtener una imagen booteable de freeDOS y montarla en un directorio
  2. wget http://www.fdos.org/bootdisks/autogen/FDOEM.144.gz
  3. gunzip FDOEM.144.gz
  4. mkdir /tmp/floppy
  5. mount -t vfat -o loop FDOEM.144 /tmp/floppy
  6. # obtener la herramienta para actualizar el bios y la imagen, y moverlos
  7. wget http://dlcdnet.asus.com/pub/ASUS/mb/flash/AFUDOS211.zip
  8. unzip AFUDOS211.zip
  9. mv AFUDOS.exe /tmp/floppy
  10. wget http://dlcdnet.asus.com/pub/ASUS/mb/sock754/K8N/K8n1011.zip
  11. unzip K8n1011.zip
  12. mv 1011.rom /tmp/floppy
  13. # desmontar
  14. umount /tmp/floppy
  15. # crear una ISO y grabar a un disco virgen
  16. # (tambien se podría grabar a un pendrive)
  17. mkisofs -o bootcd.iso -b FDOEM.144 FDOEM.144
  18. cdrecord -v bootcd.iso

Reinciar, bootear el CD de FreeDOS y ejecutar

afudos /i1011.rom

y a disfrutar del amor, geeks!

Comentarios () | Link

Publicado el 3 de agosto de 2011 | Categoría/s: Python

Un colega al que no conozco personalmente, Martín Chikilian, se ofreció a desarrollar una interfaz gráfica para Cuevanalinks.

Luego de algunos dias y teniendo una paciencia descomunal para soportar siempre nuevos requerimientos de mi parte, obtuvo una versión de Cuevanalinks-GUI, basado en PyQt

Todavía está en desarrollo (hay algunos bugcitos y decisiones de diseño que no resolvemos del todo) pero ya ofrece ciertas ventajas sobre la versión para línea de comandos:

- Permite elegir qué se descarga cuando se hace una búsqueda de películas
- Te deja armar una cola de descargas de muchas cosas distintas haciendo muchas busquedas
- Y, lo más importante, lo puede instalar y usar un ser humano normal. :D

Para Windows, hay un instalador de la versión 0.5dev (el dev denota que está en desarrollo, se entiende) que se puede descargar desde acá:

Binary Data - 7.6 MB
cuevanalinks-0.5dev-installer.exe
para Windows XP o superior

Para los linuxeros, Martín puso algunas instrucciones para hacerlo andar desde los fuentes.

Disfruten, reporten bugs y difundan.

Comentarios () | Link

Publicado el 7 de julio de 2011 | Categoría/s: Software Libre, SPIP, Web 2.0
GIF - 9.5 KB

Hace tiempo me interesa el servicio disqus.com que provee una plataforma de comentarios muy poderosa y fácil de usar.

Comencé un pequeño plugin para integrar Disqus a Spip.

Zip - 9.2 KB
Plugin Disqus para SPIP
version 0.1

Por ahora tiene dos componentes básicos:

  1. Un esqueleto inc-forum.html que reemplaza el esqueleto por defecto de SPIP (y su formulario) por Disqus
  2. Un esqueleto para exportar comentarios existentes (genera un XML importable a traves del panel de disqus)

La configuración se realiza a través de una página (requiere el plugin CFG) donde se define el "Disqus shortname" (la identificación unica que asignamos al sitio donde usaremos Diqus).

Para generar el XML con la exportación de comentarios preexistentes, basta acceder a /?page=export_comments_to_disqus y luego importar ese archivo desde el panel de Disqus (Tools -> Import/Export )

¿Quién estrena el nuevo sistema ?

Comentarios () | Link

Publicado el 1ro de julio de 2011 | Categoría/s: Python

JPEG Python Argentina se complace en informar que está disponible la versión 0.7 de CDPedia , un proyecto de la comunidad para disponer de los contenidos de Wikipedia en español sin acceso a internet, usando un CD o DVD como soporte de la información.

La imágen de CD (.iso) se puede descargar desde acá:

Binary Data - 700.7 MB
CDPedia 0.7 - CD
701Mb - 93500 artículos, 10% de imágenes
Binary Data - 2047.9 MB
CDPedia 0.7 - DVD
4.4gb - Todos los artículos (886 mil), 45% de imágenes.

Más información en http://cdpedia.python.org.ar/

Esta versión de CDPedia es la que se distribuirá a todas las escuelas de Argentina a través de Educ.ar [1]

Felicitaciones a todos los colaboradores por este logro de la comunidad. En especial, a Diego, SAn, Facu y Alecu que son los principales impulsores de este proyecto.

Comentarios () | Link

Publicado el 10 de junio de 2011 |

De nuevo soy distribuidor de la Orsai en la ciudad de Córdoba. Si estás interesado date una vuelta por esta página

Comentarios () | Link

Publicado el 6 de junio de 2011 |

[Sigo publicando cosas con delay de meses]

El sábado 30 de mayo se realizó el PyDay Córdoba 2011 con la participación de más de 200 personas y casi 20 charlas en dos salas simultáneas. Un éxito rotundo organizado por Juan y un gran equipo de colaboradores/as . Felicitaciones.

Mi participación

Participé dando una charla sobre MyHDL, un paquete que convierte Python en un lenguaje para describir hardware (HDL).

Usé MyHDL para hacer pymips, una implementación [2] de un procesador DLX completo. Pueden leer un poco más sobre el trabajo en el informe

Espero poder escribir un artículo explayando un poco la charla y contando más sobre MyHDL . Si estás interesado, te recomiendo este artículo (en portugués) de Ramiro Peixoto.

Comentarios () | Link

Publicado el 3 de junio de 2011 |

Oh, cuantos planes elucubrados para cuando ya no tuviera un exámen cercano para obstaculizar la felicidad. Y resulta que ahora, que ya no tengo, tampoco encuentro el tiempo de empezar a darle forma de realidad a esos planes.

Esta es la información: el 12 de mayo pasado me recibí de Ingeniero en Computación.

¿y ahora?

Comentarios () | Link

Publicado el 27 de abril de 2011 | Categoría/s: Python
JPEG - 30.1 KB

El Zen de Python es también el Zen del Barça, el de Pep Guardiola. La "Pep" 20 [3]:

Bello es mejor que feo.
Explícito es mejor que implícito.
Simple es mejor que complejo.
Complejo es mejor que complicado.
Plano es mejor que anidado.
Disperso es mejor que denso.
La legibilidad cuenta.
Los casos especiales no son tan especiales como para quebrantar las reglas.
Aunque lo práctico gana a la pureza.
Los errores nunca deberían dejarse pasar silenciosamente.
A menos que hayan sido silenciados explícitamente.
Frente a la ambigüedad, rechaza la tentación de adivinar.
Debería haber una —y preferiblemente sólo una— manera obvia de hacerlo.
Aunque esa manera puede no ser obvia al principio a menos que usted sea Messi, Xavi o Iniesta.
Ahora es mejor que nunca.
Aunque nunca es a menudo mejor que ya mismo.
Si la implementación es difícil de explicar, es una mala idea.
Si la implementación es fácil de explicar, puede que sea una buena idea.
Los espacios son una gran idea ¡Hagamos más de esas cosas!
 
Comentarios () | Link

Publicado el 10 de abril de 2011 | Categoría/s: Software Libre, Python, Metodologías ágiles

Sabido es, aunque muchas veces se ignora, que un software sin documentación está incompleto [4].

Si bien el manifiesto ágil proclama "Software funcionando sobre documentación extensiva", yo subrayaría extensiva como eufemismo de documentación burocrática e inútil (opiné de esto acá) que evidentemente no es la que hace falta. Pero la documentación (sobre todo la buena) es indispensable y para algunos, la parte que más los enorgullece del proyecto (y con razón).

Desde el punto de vista técnico, escribir documentación (no sólo para Python!) es bastante fácil con restructuredText (qué feo el sitio de docutils, che!) que es el markup estándar de los pythonistas.

Sobre este markup funciona Sphinx, el generador de documentación más utilizado (por lejos) en el ecosistema de Python. Es lo que usa la documentación de Python misma, la de Django y casi todo proyecto conocido o por conocer.

Entonces usamos restructuredText, usamos Sphinx, pero para nuestro proyectito de morondanga que no tiene web propia ni nada, ¿dónde subimos la documentación generada? Veamos.

Usando Readthedocs.org

Read the docs es un sitio para hospedar documentación realizada con Sphinx. Sólo se necesita indicarle el repositorio público del proyecto (svn, git, mercurial, bazaar), subir los fuentes .rst y contenido estático (imágenes) aptos para Sphinx en una carpeta /doc o /docs y el sitio se encarga de bajar los fuentes de documentación y renderizarlos a HTML a través de Sphinx.

Estrictamente, usando rtfd.org (como le dicen los amigos) ni siquiera hace falta tener Sphinx instalado localmente.

Más aun, por defecto actualiza diariamente, pero se puede utilizar un "hook" para indicarle que actulice cuando "pusheamos" (o "commiteamos") al repo, de manera de tener la documentación actualizada al instante. Para usuarios de GitHub la activación del "web hook" se explica acá . Para BitBucket.org es parecido:

  1. Vas a tu proyecto , click en Admin -> Services
  2. Agregás el servicio "POST"
  3. Completás el campo de texto con la URL que te da ReadTheDocs en la página de descripción de tu proyecto (estándo logueado). Por ejemplo:

Y listo. Tu docu al instante.

Como el sitio genera el html en vez de servir una versión generada previamente, la documentación que requiere introspección del código ( todas las directivas .. auto* :: de Sphinx) este debe poder ejecutarse. Para eso el paquete debe ser instalable via setup.py y hay marcar desde la página de configuración del proyecto en RTFD.org, que instale en un virtualenv.

Para ver si hubo algún problema en la generación, podés fijarte en "build" donde te muestra el stdout y el stderr de la corrida de Sphinx.

Subir la docu a PyPi

Una forma buenísima de compartir tu trabajo pythónico es a través del Python Package Index, pypi, que es el índice que usan las herramientas pip e easy_install. Si bien no necesarimente los paquetes deben estar hospedados allí (indicando en el setup.py la URL de descarga) es muy común y fácil hacerlo con el comando upload del setup.py.

Lo que muchos no saben es que PyPi también ofrece hostear la documentación. La forma canónica es ir a la página de administración de tu proyecto en PyPi y adjuntar un .zip con la documentación (que no necesariamente tiene que ser hecha con Sphinx)

Pero si usamos Sphinx hay una manera más fácil, manteniendosé en el "ecosistema" de desarrollo: usar esta extension de setuptools que permite generar el html a través Sphinx y subirlo automáticamente. Se instala, obviamente, vía pypi:

$ easy_install sphinx-pypi-upload

Hay que condigurar un setup.cfg (ubicado al nivel raiz, junto con setup.py) indicandole dónde está la docu fuente y dónde el resultado. Más o menos así:

Luego se usa:

$ python setup.py build_sphinx
$ python setup.py upload_sphinx

Y docu subida a la dire http://packages.python.org/tu-proyecto . ¡Charaaán!

Usando tu repositorio SVN

Si usas SVN y tu servidor lo permite, podés servir contenido estático (html y todo lo que produce y necesita Sphinx) directamente desde el repositorio.

Para que el servidor Subversion muestre el html renderizado en vez del código (como texto plano) hay que indicarle el tipo mime de cada archivo.

$ svn propset svn:mime-type 'text/html' FILENAME
$ svn propset svn:mime-type 'image/jpeg' FILENAME

Para que esto se haga automático, se puede modificar el archivo de configuración ~/.subversion/config

Un ejemplo de esto es el reporte de mi proyecto integrador que está hospedado en Google Code

Aprovechándote de GitHub

GitHub hospeda páginas estáticas, tanto del desarrollador/a como de tus proyectos. Bien sirve eso para subir la documentación y eso hacen mas o menos automáticamente estas opciones que no he probado pero las dejo como referencia:

- Hosting sphinx doc in github de Luca Sbardella.
- Usando github-tools.

¿Una excusa menos? ¡A escribir algo de docu para esos bichitos que codearon alguna vez!

Comentarios () | Link

Publicado el 9 de abril de 2011 | Categoría/s: Python

UPDATE Hay una nueva versión con interfaz gráfica acá

Parece que se me hizo hobby chupar links de cuevana.tv y el experimento ahora tiene su versión pythonica: Cuevanalinks .

Es un pequeño proyecto que permite "conseguir links" de contenidos ofrecidos en cuevana, sirviendo estos como fuentes a manejadores de descargas como Tucan o JDownloader.

Más allá de los resultados, me ha tenido entretenido algunas horas y me permitió aprender (o al menos resolver) algunas cosas: cómo se usa mercurial (y en particular un merge conflictivo con Meld), cómo se escribe un setup.py, como se distribuye via PyPi, etc.

También, por supuesto, me permitió usar dos utilidades que me gustan mucho: PyQuery y plac.

La aplicación tiene dos módulos/componentes:

  1. Una biblioteca que intenta funcionar como API de Cuevana y es la encargada de scrappear la web en busca de la info interesante.
  1. Una interfaz de línea de comandos que permite buscar links y bajar subtitulos de contenidos específicos

Para qué sirve

Por ejemplo, para bajar la temporada 4 completa de Mad Men, con subtitulos, se podría hacer esto:

$ cuevanalinks 'mad men' s04 -s > madmen.txt && tucan -d -i madmen.txt

Instalación

Para instalarlo, basta con usar pip

$ sudo pip install cuevanalinks

O con easy_install:

$ sudo easy_install cuevanalinks

O bien, bajar el paquete y ejecutar :

$ tar xvfz CuevanaLinks-0.1.tar.gz
$ cd CuevanaLinks-0.1
$ sudo python setup.py install

Algunas posibilidades de la API

El CLI por ahora sólo expone una parte pequeña de la API, pero hay varias posibilidades. Por ejemplo:

>>> from cuevanalinks import cuevanaapi
>>> api = cuevanaapi.CuevanaAPI ()
>>> house = api.get_show ('house')
>>> house.plot
u'El doctor Gregory House, especialista en el tratamiento de enfermedades infecciosas, trabaja en un hospital universitario de Princetown, donde dirige una unidad especial encargada de pacientes afectados por dolencias extrañas y en la que colabora con un selecto grupo de aventajados ayudantes.'
>>> house7x1 = house.get_episode (7, 1)
>>> house7x1.title
'Now What?'
>>> house7x1.cast
['Hugh Laurie',
'Lisa Edelstein',
'Omar Epps',
'Jesse Spencer',
'Jennifer Morrison',
'Robert Sean Leonard',
'Olivia Wilde',
'Peter Jacobson']
>>> house7x1.sources
['http://www.megaupload.com/?d=DM58TA0J',
'http://www.filesonic.com/file/36841721/?',
'http://bitshare.com/?f=67z435xm',
'http://www.filefactory.com/file/caf85b9']
>>> house7x1.subs
{'ES': 'http://www.cuevana.tv/download_sub?file=s/sub/7888_ES.srt'}

Lo que falta

Si bien es lo más prolijito que he hecho, todavía está lejos de ser un trabajo completo, tanto en funcionalidades como en SQA

Un aspecto esencial es hacer test (perdón Nati Bidart ;-) ), para lo cual tengo que aprender a usar nose y minimock.

Respecto a funciones, algo importante es definir cómo resuelve el CLI el manejo de múltiples resultados para una búsqueda. Actualmente devuelve el resultado "más relevante" sin anoticiar al usuario de otras opciones.

También falta documentación! Veremos la próxima.

Comentarios, sugerencias, bugs

Todo aporte (ideas, código, etc.) es bienvenido. Si encuentran problemas, reportenlos.

No estoy seguro cuánto puede durar esto funcionando, ya que depende de que el funcionamiento del sitio no cambie o yo tenga mucho tiempo y ganas de andar parchando detrás. Por eso, si hay más interesados y usuarios, seguramente podremos hacerlo durar más.

saludos!

Comentarios () | Link

Publicado el 8 de abril de 2011 | Categoría/s: Linux

Suse 7.0 fue la primera distro Linux que mi gloriosa Pentium II cobijó. Al tiempo usé Mandrake, después Debian y desde su primera versión (2004) Ubuntu.

Un largo camino de aprendizaje... que alguna vez recorreré ;-)

Feliz cumple, Linux

Comentarios () | Link

Publicado el 6 de abril de 2011 | Categoría/s: Metodologías ágiles

La manera más fácil para aprobar una tesis de ingeniería es mentir. Llenar páginas y páginas, preferentemente con tablas y viñetas, de contenidos vacuos y tediosos que seguramente nadie, mucho menos los evaluadores, leerán. Sólo hay que prestar atención a que sean muchas páginas y lo más simétricas posibles.

El ejemplo paradigmático de esto son las especificaciones formales de casos de uso, pasión de burócratas que tienen cabida donde no hace falta demostrar mucho.

Pero también el UML, me atrevo a decir, en su mayor parte y nivel de detalle es secuaz de esta ignominia que pauperiza el nivel del software académico: en vez de saber programar y poder leer código, se presta atención a los dibujitos, poniendo el grito en el cielo si la flechita es negra en vez de blanca, como dice Sommerville que debe ser.

Como no me gusta mentir, la facilidad no me seduce de más (tampoco la dificultad) y estoy bastante conforme con lo que hice me animé a ser sensato en expresar cómo hice mi trabajo.

No me encerré 6 meses a hacer dibujitos que resultarían perfectos, inescrutables y luego harían la programación trivial. Eso, al menos en mi experiencia, no existe (sospecho que ellos todos lo saben, lo que exacerba la hipocresía). Sí, en cambio, hice algunos dibujitos que me ayudaran a entender (y dar a entender) cómo iba la cosa, mientras iba programando y evaluando si estaba bien encaminado.

Este espíritu es el que cobijan las prácticas ágiles, que es el marco conceptual (metodológico) que adopté.

La cuestión es que, para ganarle al unknow how de los hombres de corbata, hay que justificar con nombres que les suenen admirables. Hay que mencionar mucho IBM, Microsoft, C#, Intel. Esas son las voces que respetan, aunque sean pura falacia

Yo justifiqué así:

Según afirma Terry Quatrani, evangelizadora de las metodologías ágiles de IBM, en The Truth About Agile Modeling :

Aunque sigas un proceso ágil, estarás realizando cierto grado de modelado – sólo que no lo realizarás tanto como si utilizaras un proceso tradicional. La falta de formalidad en el modelado ágil no significa que no estás modelando, sino que te pones el foco en los beneficios de este sin las desventajas y confusiones de documentos extraños y burocráticos.

Por su parte, Robert Martin sostiene en Agile Principles, Patterns, and Practices in C# que el modelado basado en UML en el desarrollo ágil es útil como instrumento de comunicación, pero su detalle no aporta valor significativo:

No gastes mucho tiempo en esta tarea, no necesitas tanto detalle. Los modelos y los planos son necesarios en la arquitectura y la construcción civil porque es caro construir una casa para demostrar que su diseño funciona. El software no es así – puedes validar tu idea con sólo codificarla, en igual tiempo que el que insume hacer un modelo UML que nada prueba por sí mismo.

Aun más escéptico, Alans Stevens, reconocido ingeniero en software [5] y conferencista, opina en un artículo:

No uso UML y noto que ninguno de mis colegas lo usa. Tengo sensaciones mezcladas acerca de su necesidad. Parece perfectamente razonable que debamos acordar como industria un conjunto de símbolos comunes para representar la programación orientada a objetos, pero UML tiene la típica apariencia de "diseñado por un comité".

(...) El aspecto más crítico en un diseño inicial, en mi experiencia, es la interfaz entre la UI y el modelo de objetos. Lamentablemente UML no aborda este problema y en cambio parece obsesionado por las minucias en una parodia de distracción académica.

¡Venceremos!

Comentarios () | Link

Publicado el 30 de marzo de 2011 | Categoría/s: Software Libre, Python

Casi se hizo jueves y todavía no escribí nada sobre el PyCamp. Roberto Alsina se va a enojar.

La excusa, vaga como yo, es que la devolución de uno de mis directores de tesis fue más hinchapelotas minuciosa que lo esperado. Así que para cumplir el objetivo tuve un amansador refactoring de 5pm a 6am cuyo resultado me permitió conseguir un lacónico:

Recibido, pero recién lo puedo ver el viernes

Inmediatamente demostré que el lenguaje soez en arameo es un conocimiento latente.

A modo de intro

El PyCamp es un encuentro de la comunidad Python Argentina. Monos (y pronto monas) con laptops afiladas se juntan en el algun rincón del país con internet, preferentemente lejos de las grandes urbes (para darle énfasis a eso de camp) a programar software libre, hackear de lo lindo, jugar jueguitos electrónicos y de los otros, tomar y beber en bucle continuo y hacer cuanto le plazca a cada uno, en un ambiente totalmente inclusivo, distendido y buena onda.

JPEG - 172 KB

Python Argentina abarca el amplio espectro que va desde programadores reconocidos internacionalmente (gran parte de ese Ubuntu que seguro estas usando — o deberías — lo hacen pythonistas argentinos) hasta "newbies" que se anotaron en la lista con el único y nada despreciable objetivo de callar al amigo que ya sabe un poco más y no para de hincharle para que use Python (resulta que a los pocos dias, el (ex)newbie ya está molestando a un tercero).

Esa población tiene fiel reflejo en el PyCamp, pero grosos y finitos tienen las mismas posibilidades de proponer, opinar y divertirse. Por supuesto, es impagable lo que se aprende y son increíbles las cosas que hacen los geeks cuando están divertidos.

De estos encuentro salieron proyectos como Cocos2d, la CDPedia y lalita.

Mi experiencia

El PyCamp 2011, el cuarto consecutivo, se realizó en La Falda y fue el primero en el que participo. Sabía, por referencias, fotos y repositorios, de qué se trataba, pero como dice el adagio... del dicho al hecho ....

JPEG - 126.7 KB

Nati tenía un congreso de germanística en Buenos Aires y después se iba a La Plata a visitar a una amiga, así que las opciones eran ir a la marcha el jueves (24 de marzo) y cuevanear el resto del finde o sumarme a la movida. Perrito ofreció movilidad así que lo tuve servido.

Salimos el jueves a la mañana y con nosotros venía Malev y el organizador del evento, de pie señores, Nueces.

A lo largo de los 4 días de PyCamp, participé en la CDpedia (mejoré la funcionalidad de "artículo al azar" y estuve haciendo unos benchmarks de optimización en el peso de las imágenes), amagué con un filtro para Pep8fy que dejé medio inconcluso, comí al menos el doble de lo que venía comiendo (bien la comida, che!), jugué mi primer sesión de rol guiado por el señor ordenador (Alecu, un maestro!), malabareé con las bolas (las de malabares) de Humitos, presencié en vivo la gestación de un delirio mágico de Roberto Alsina, me metí a la pileta y tomé sombra, perdí 0-5 0-5 los dos partidos de metegol y me cagué de risa igual con mi coequiper (¡fiesta!), escribí "puto" (con onda, Lipe) desde un android con un librería para multitouching que estos bestias hicieron en una tarde, quedé pasmado con el vuelo de la discusión sobre los motivos (y posibles acciones correctoras) de la baja participación femenina en Python y el Software Libre en general, bebí vino, fernet, cerveza y mistela (me colé en un contingente más adelantado que el nuestro en la excursión del Hotel Edén, y después me volví con Flecox a ver el partido de Argentina), jugué con Tato (el pythonista más peque del encuentro, hijo de Roberto Alsina) y me enternecí viendo a otros geeks (Huguito — Pilas rulez! —, John, Achuni, todos) jugar como niños.

JPEG - 241.6 KB

Detalles faltan pero estos someros recuerdos sobran para afirmar que el balance es positivísimo.

Una reflexión final

PyCamp contrasta totalmente con los grises, aburridos y mediocres congresos académicos que en el ámbito universitario se difunden y en los que alguna vez he participado, donde los papelitos cuentan y donde (casi) todo se vuelve una ficción impulsada por la inercia y la necesidad de "publicar" (no importa mucho que), para mantener la beca, el statu quo y el viático.

En PyCamp no se piden papeles ni se aplauden corbatas, la gente se llama por el sobrenombre y no por el título, se admira el conocimiento pero más se festeja su colectivización, y eso lo saben (y lo disfrutan) los picantes y los tiernitos.

Cuanta falta le hace a la universidad visiones y experiencias como esta.

¡Hasta el próximo encuentro pythonistas!

Comentarios () | Link

Publicado el 21 de marzo de 2011 |

Durante los ultimos años de facultad me he ganado ciertos apodos, algunos más buena leche que otros, relacionados con mi postura y militancia respecto al Software Libre.

Esta postura se mezcla (se complementa) con otras posiciones políticas que he manifestado y defendido reiteradamente con profesores y compañeros, que son intrínsecas a la formación y el perfil de ingenier@s que se forma en la universidad pública, con la que suelo disentir. Muchas de esas discusiones, están publicadas en mi otro blog

La semana pasada una amiga comentó un mensaje que dejé en Facebook avisando que había terminado la tésis.

Inspirado en ese comentario de Lu, Nati (mi novia) me pintó esta remera como regalo de cumpleaños (que es mañana).

Claro que yo no salgo muy favorecido (cansancio y flash no hacen buen equipo). Pero la remera está buenísima. Acá el detalle:

En la espalda podría ir esta cita :

"La libertad, Sancho, es uno de los más preciosos dones que a los hombres dieron los cielos; con ella no pueden igualarse los tesoros que encierra la tierra ni el mar encubre; por la libertad, así como por la honra, se puede y debe aventurar la vida, y, por el contrario, el cautiverio es el mayor mal que puede venir a los hombres."

Don Quijote, capítulo LVIII, segunda parte

Comentarios () | Link

Publicado el 19 de marzo de 2011 | Categoría/s: Python

El viernes presenté el draft para la revisión final de mi proyecto integrador (a.k.a. tesis). Tentativamente (¡a esta altura y todavía no es seguro!) se titula "Software para graficación de digramas termodinámicos" y se puede chusmear acá.

Como verán, lo hice con Sphinx, generando LaTeX y compilando luego a PDF. Estoy más que satisfecho del resultado.

Sin embargo, un problema de Sphinx/Docsutils (y esto viene a ser el verdadero tema de este post) es es que si bien están llenos de pulentez y se la bancan mil, no están "pensados" para escribir tesis sino documentación/manuales de software. Esto se nota, sobre todo, en la subutilización de recursos de LaTeX a los que "no se accede" desde restructuredText.

El caso paradigmático es la referenciación de figuras. En las versiones "imprimibles" que se generan con LaTeX suele pasar que una figura no quepa en una página y el compilador, al medir y darse cuenta, la manda a una página siguiente. Esto hace indispensable ser preciso en la referencia ya que un mensaje como "en la siguiente figura" puede estar seguido de toda una página sin figuras.

Para hacer una referenciar una figura en Sphinx hay que declarar un target

.. _ok:

.. figure:: ok.png

  Una imágen

y luego usar el rol :ref:. Por ejemplo:

Referenciando la figura :ref:`ok` se evidencia el problema.

El problema es que el LaTeX generado por Sphinx no hace exáctamente una referencia a la figura, sino un hipervínculo (hyperref). Como texto de ese hipevinculo usa el epígrafe de la figura, lo cual es horrible e inútil para textos cientificos/académicos. El issue, está reportado hace más de 2 años.

Con el estilo gronchohacker que se me está volviendo costumbre, hice unos cambiecitos para que funcaran las referencias como tienen que ser, por número. La directiva es particularmente fácil en LaTeX: ~\ref{}. En concreto, el "gronchohackeo" se trata de cambiar {\hyperref[label]{\emph{epigrafe}}} por ~\ref{label}.

Para garantizar compatibilidad con los que les gusta que el título de la referencia sea el epígrafe y no el número, lo hice opcional: toda referencia a un destino con sufijo -num usa una referencia númerica en vez de un hipervinculo con texto.

Acá el patch (para la versión 1.1 —en desarrollo— )

texte - 2.2 KB
Diff
Parche para el Issue #76 de Sphinx

Y acá un ejemplo del problema y la gronchosolución

PDF - 58.1 KB

El código del ejemplo es este:

Ejemplo
*******

Referenciando la figura :ref:`ok` se evidencia el problema. Aplicando mi
rústico parche en cambio, se puede referenciar a la Figura :ref:`ok-num`.


.. _ok:

.. figure:: ok.png

  Una imágen


.. _ok-num:

.. figure:: ok.png

  Misma imágen pero otra figura, referenciable por número
Comentarios () | Link

Publicado el 28 de febrero de 2011 | Categoría/s: jQuery, Javascript

UPDATE : la ultima version funciona con el nuevo sitio de cuevana.

Dos cosas me gustan mucho últimamente: cuevana.tv y GreaseMonkey

Cuevana es un sitio para ver series y películas online, en streaming de alta calidad, con subtitulos y todo. El truco está en un plugincito que hace instalar en el navegador que permite reproducir (en un reproductor basado en flash comunacho) un archivo que se descarga de megaupload, filefactory o sitios por el estilo. El ancho de banda lo ponen otros, la alegría es toda de cuevana (y nuestra). Todos unos pillos.

Lamentablemente, mi conexión no se banca un streaming de alta calidad (como los videos de cuevana) sin bufferear (?) y entonces pierde sentido (o gana mala sangre) intentar ver directamente "online".

Y acá entra Greasemonkey, que es una extensión para Firefox (y otros navegadores) que permite agregar scripts (en javascript) para modificar localmente las páginas que indiquemos.

Con esta herramienta, programadores voluntarios crean cientos de scripts para mejorar la estética o funcionalidad de muchos sitios, conocidos o no.

Mi primer aporte a la "comunidad Greasemonkera" (y cuevanera) intenta subsanar la discriminación que el streaming de alta calidad tiene con los que tenemos un ancho de banda no muy ancho.

Una vez instalado el script muestra links a la fuente original de descarga, justo abajo de los iconos de selección.

Así podemos bajar los videos y verlos offline.

Como complemento, se pueden copiar y pegar los links en un gestor de descargas como Tucan

Respecto a los subtítulos, todavía soy principiante con javascript, pero espero poder obtener el link a los subtitulos en futuras versiones. Mientras tanto ArgenTeam y Subdivx. ¡Hecho!

Comentarios () | Link

Publicado el 24 de febrero de 2011 |

Anoche, en un arrebato procrastinador, armé Oscars Awards y su clon Oscars Winners .

Aprovechando una promo de netfirms.com y con un poco de suerte, consegui los dos dominios por u$s 9 que pagué con PayPal. Vuelve la burbuja .com ?

El "streaming de tweets" está basado en juitter, pequeñamente hackeado.

¿Quién gana en los Oscars 2011 ?

Comentarios () | Link

Publicado el 27 de noviembre de 2010 | Categoría/s: Python

Si el sábado a la noche antes de rendir tu última materia en vez de estar estudiando estás emborranchandote por ahí, sos un inconciente y/o la tenés muy clara. Si en cambio no estás estudiando ni emborrachando, pero te ponés a pensar ejercicios para el curso de python que estás dando, sos un nerd. Mereces un brindis de chocolatada a tu salud de todos los de este foro.

Si encima blogueas al respecto, sos un nerd intergaláctico. Te explico por qué.

Resulta que tenés este archivo de datos. Cada número representa la intensidad de luz de un punto y te informan que la imágen es cuadrada. Se supone que son datos que te dió un profesor una vez, para que resuelvas este mismo problema en ¡ensamblador!

Hacerlo en Python, con Numpy y Matplotlib, significa estas líneas

import numpy as np
import matplotlib.pyplot as plt

data = np.fromfile ('data.txt', sep=' ')
SIZE = int(len(data) ** .5)
data.shape = SIZE, SIZE
plt.imshow (data)
plt.show ()

Y se obtiene este resultado:

Se puede limitar los valores de luz a un valor entre 0 y 255 (truncando valores de saturación extrema), haciendo :

plt.imshow (data.clip(0,255) )

Obtenemos este resultado:

De otra galaxia.

Comentarios () | Link

Publicado el 19 de octubre de 2010 | Categoría/s: Empresa

Con mucho esfuerzo propio y ajeno, estoy terminando la carrera Ingeniería en Computación en la Universidad Nacional de Córdoba.

Una particularidad de mi carrera, entre varias, es que hay mucha discusión y participación estudiantil. Una herramienta estratégica para esto ha sido un grupo de correo en el que participan estudiantes, profesores y graduados.

Allí he mantenido algunas discusiones con compañeros y/o profesores sobre temas diferentes. Los más interesantes, para mí, son los que evidencian concepciones filosóficos, ideológicas y políticas de cada uno.

La última, que empezó el lunes, comenzó con una discusión sobre le voto electrónico (a raiz de la charla relámpago de Federico Heinz en la PyConAr ) que fue derivando a una más de fondo, sobre la concepción de ingeniero que yo quiero ser y la que los profesores con los que discuto en el hilo (y casi todos los otros) me enseñan.

Como ya casi tiene telarañas, y además porque allí están todas las discusiones previas (las que me parecieron interesantes, bah), lo publiqué en mi otro blog.

Pueden leerlo aquí

Comentarios () | Link

Publicado el 18 de octubre de 2010 | Categoría/s: Python

Otra cosa que hice en PyConAr fue presentar un poster sobre GPEC 2010, mi proyecto integrador para recibirme de ingeniero en computación, originalmente pensado para presentar en Riteq la semana que viene. El poster fue presentado en coautoría con Martín Cismondi y Gustavo Wolfmann, los directores del trabajo.

JPEG - 48.7 KB
Foto de Elías Andrawos

Como no tuve tiempo, la de PyCon es una versión que (espero) será mejorada para la semana que viene, con algunas figuras más.

PDF - 462.6 KB
Poster GPEC 2010
Formato A4 (reducido)

También un resumen extendido (2 páginas) aprobado por los revisores de RITEQ, que explica algún detalle más.

PDF - 233.6 KB
Resumen aprobado Riteq 2010

Los fuentes están disponibles en en el repo:

svn checkout https://gpec2010.googlecode.com/svn/trunk/docs/poster

Baje, use y difunda, si le inspira merecimiento

Comentarios () | Link

Publicado el 18 de octubre de 2010 | Categoría/s: Python

A lo largo de esta vida he amado, he llorado y reído, me han salido canas y dí algunas charlas y talleres.

Me gusta la docencia, probable herencia de una grosa. Me gusta compartir lo que sé, instarme a aprender para compartir con otros y aprender cuando enseño. Educación horizontal, educación popular.

JPEG - 7.7 KB

El viernes pasado, en la PyConAr debía dar una de las primeras charlas, IPython, la interactividad al poder . Entró al cronograma en una segunda tanda de selección, con más sorpresa que el chino Garcé a Sudáfrica 2010. El problema es que para cuando me invitaron yo ya había descartado mi participación en PyCon y aceptado dar una en mi facu, también sobre Python pero otra. Ergo, ¡tenía que preparar 2 charlas!

En el medio, solucionar horribles bugs de GPEC para la release de la semana que viene, un casamiento a 1500 km de mi casa (por suerte no mío), aprender a diseñar hardware, malabarear estres con las remeras de PyAr y terminar el poster que se me ocurrió presentar.

El contexto no favoreció a preparar la charla con anticipación. De hecho, pude hacerme lugar el jueves de 0:05 a 4 am para leer el manual y escribir lo poco que escribí. Mucha audacia teniendo en cuenta que algunas de las cosas que quería (de alguna manera debía) mostrar, no las uso/aba realmente (eso de instarme a aprender es posta) y más aun, considerando que las presentaciones con "demo" incluída son una fiesta orgiástica para Murphy

Por eso, para descompimir la ansiedad y la casi certeza de que algo podía salir mal, lo primero que hice fue echarle la culpa a John Lenton, que fue el que incitó a la vergüenza. La charla comienza con este disclaimer:

Para colmo de riesgos, descubrí algo que supongo que es un bug (TODO: check and report this) con la utilidad Demo de IPython que usé, en la versión estable (0.10) y la que está en desarrollo (0.11). Por eso tuve que hacer un downgrade a la 0.9, que no tiene algunas cosas interesantes que quería mostrar (%paste, por ejemplo).

Por supuesto, Murphy vino y se divirtió, pero no se burló sarcasticamente. Hasta que alguien me muestre un video y me ponga rojo de vergüenza voy a quedarme con la idea de que no estuvo tan mal. Alguno seguro no descrubrió nada nuevo, pero otros parecieron satisfechos y probablemente jueguen un ratito con IPython.

Archivos / Descarga

Como es una presentación interactiva, no hay "diapositivas". O sí, pero es un módulo python.

Todo lo que usé (con nimios retoques respecto a lo mostrado el viernes) se puede bajar (o forkear) de GitHub .

git clone git@github.com:nqnwebs/IPython-interactive-talk.git

O desde este Zip

Zip - 3.8 KB
IPython, la imaginacion al poder
Licencia CC-by-nc-sa 2.5 Argentina

Todo tiene licencia CC-by-nc-sa 2.5 Argentina

Comentarios () | Link

Publicado el 17 de octubre de 2010 | Categoría/s: Python

Ya está, ya pasó. Y estoy muy feliz de eso que pasó.

Conocí Python oliendo la pasión con la que hablaban de este lenguaje gente a la que admiro mucho y empujado demandas del trabajo, la academia y el hartazgo, de poner un pie fuera de la web, cosa que PHP permite de mentirita.

Me acerqué al lenguaje facilmente (simple is better than complex) pero no tan fácil a la comunidad. De hecho, acabo de fijarme en gmail y encontré el mail de un bonito bot y mi feo hola mundo a la lista de Python Argentina fechados el 17 de enero de 2007. Es bastante tiempo para saber tan poco de Python y para que Python sepa tan poco de mí.

Lamentablemente nunca pude ir a los PyCamp, que parecen ser el ámbito ideal para encontrar y fortalecer esa cuestión humana que a mi me faltaba para pertenecer y disfrutar, no sólo del lenguaje sino de su comunidad. Así que el año pasado, despues de la PyCon 2009 en Buenos Aires, me propuse tomar alguna responsabilidad si se hacía de nuevo.

Cumplí a medias: me anoté como revisor y ni siquiera revisé la mía. Pero (haciendo volar libres dos pajaros de un sueño, hospedé a Manu, presenté un poster más grandote que grandioso y di una charla que salió no demasiado mal.

La alegría, más allá de las poquitas charlas que logré ver, viene por el lado social. Conocí, conversé, me reí y casi que me emborraché con gente muy linda, mucha de la cual no sólo es técnicamente brillante sino humanamente querible.

Como apasionado de la tecnología sé que no es neutral. La tecnología, en esta época, es parte de la ideología.

Elegir una tecnología, un lenguaje de programación, y meterle tiempo, ingenio y muchas veces plata a su difusión y mejora, para que muchos se beneficien (y se tienten a meterle tiempo, ingenio ...) es un gesto de solidaridad que hay que aplaudir de pie.

En un sistema cuyo leitmotiv es "vos o yo" hay gente que dice "vos y yo", y mejor aún, dice "nosotros".

Y a mi se me infla el orgullo de pertenecer. Gracias Python Argentina.

Comentarios () | Link

Publicado el 15 de octubre de 2010 | Categoría/s: Python

From: Elena Del Balzo
Date: 2010/10/15
Subject: Conferencia!!!
To: Martín Gaitán

Hijo Queridísimo!!!

Hoy estuvimos en la universidad porque Juli recibió un título de postgrado. Charlando con tu padre me recordó que hoy das tu conferencia. Cuántas cosas lindas nos pasan!!! Deseo que logres transmitir todo lo que te apasiona y defiendas siempre, en cada circunstancia de la vida, tus convicciones, tus valores y principios. Tenemos obligación de llegar a muchos con nuestros conocimientos. El saber es poder. Vos tenés en tu "poder" una hermosa herramienta y Sé de tu capacidad para socializarla.¡¡ A multiplicar, a Sumar, a Compartir!!! Abrazo. te amo. Ma´

Mi vieja. Una grosa.

Comentarios () | Link

Publicado el 8 de octubre de 2010 | Categoría/s: Empresa

El lunes a las 8am empecé mis trámites de (re)empadronamiento al Monotributo. Recién a las 12hs pude saber que ese día fracasaría, porque las "pruebas" de mi domicilio fiscal no eran válidas.

El martes, flamantemente concubinado con Nati para que me sirvan las facturas a su nombre, después de esperar que el burócrata que te saca la fotito y te hace firmar en el sensor (datos "biométricos", le llaman, pomposos) se parara cada 5 minutos, jugara con el aire acondicionado a su espalda y mandara mensajitos por celular mientras la gente de la cola (yo incluído) se inflaba de rabia, pude obtener la inscripción y la clave, la rebuscada combinación alfanumérica "hola1234", que es igual para todos (podrían, ya que están, imprimirla en el formulario, cierto?).

Se suponía que con ese trámite de sólo 2 mañanas, al otro día (porque la era digital lleva su tiempo) podría terminar los trámites via web. Pero se suponía nomás, porque la plataforma virtual-> de AFIP no funciona con navegadores que no sean Internet Explorer. En Firefox (o cualquiera de los navegadores libres) uno hace click y el formulario nunca se envía.

Esto es, por supuesto, porque está programada fuera de estándares. Vean qué boludez:

Element referenced by ID/NAME in the global scope. Use W3C standard document.getElementById() instead.

Este chorizo tira Firebug:

Es un doble esfuerzo indignarse con los burócratas (que llenan formularios de papel o scripts javascript erróneos) y también con los que en la cola, indignados, bociferan en voz alta frases noventistas como "esto no va a mejorar hasta que lo privaticen", buscando alguna complicidad, imagino.

Pero me niego a naturalizar que las cosas del Estado deban andar mal. No sólo porque las pagamos entre todos (mucha veces, más de lo que valen) sino porque necesitamos un Estado que funcione para que todas las demás cosas funcionen bien.

El discurso (y el avance) privatizador, comprobadamente perjudicial para el pueblo, ya no tiene tanta cancha como antes. Pero es hora de que empecemos, como Estado, pero sobre todo como ciudadanos concientes de que lo necesitamos, a sanarlo, quererlo, exigirlo y fortalecerlo.

Que el monotributo me funcione en Firefox, y que no haya burócratas generando violencia en la gente que espera ser atendida, son objetivos que hay que darse. Y apoyar estos proyectos, primeros pasos.

Comentarios () | Link

Publicado el 7 de octubre de 2010 | Categoría/s: Python

Víctima de la ausencia de tiempo y la mala costumbre, a ultísimo momento terminé de preparar las diapositivas para la charla que dí hoy en el marco de las Conferencias de Ingeniería en Computación, Electrónica y Biomédica 2010 de mi facu.

La charla se tituló Python a los bifes: casos reales de aplicación en Ingeniería. Las diapositivas se pueden ver acá.

(F11 para ver a pantalla completa)

El resúmen de dos líneas decía:

Basta de hablar y hablar: en la cancha se ven los pingos. Mostraré problemas reales de la carrera resueltos con este poderoso (y simple) lenguaje. Trabajos de Sistemas de Computación, Sistemas Operativos II, Paradigmas de programación y mi proyecto integrador en curso"

Hubo unas 40 personas, y estuvo, creo, bastante bien. Como no practiqué (me siento estúpido hablando solo, y mi novia me mira desorbitada — aburrida — cuando le hablo de computadoras) no calculé bien el tiempo y los ejemplos, que era el quid de la cuestión, fueron a paso raudo. Pero creo que a la gente le gustó y ojalá alguno se sienta motivado para leer el tutorial de python y sumarse a PyAr.

Fuentes

La presentación fue escrita en RST y procesada con la herramienta rst2s5.

Tiene una licencia cc-By-NC-SA. Podés descargar todos los fuentes con las imágenes y algunos ejemplos de este tar:

GZ - 3.3 MB
http://lab.nqnwebs.com/charlas/alos...

En general, los proyectos mencionados están en google code

Comentarios () | Link

Publicado el 14 de septiembre de 2010 | Categoría/s: E-Commerce, Javascript

Continuando con el artículo anterior hay tener en cuenta que cuando decimos "sistema de e-commerce simple" implica que tiene que ser simple también para el comprador.

En Argentina (y varios otros paises de latinoamérica) PayPal o GoogleCheckout no son de uso masivo, porque el e-commerce no es una práctica común y desde nuestro país es realmente imposible "poner" plata en una cuenta de esas — salvo que sea un pago efectuado desde el extranjero — , y es bastante engorroso sacarla. Además, quizas lo más importante, no aceptan pagos la mayoría de las monedas locales latinoamericanas.

JPEG - 10.3 KB

Por eso he integrado a SimpleCart (js) el sistema de pagos virtuales Dineromail.com que es similar a los otros pero orientado al mercado latinoamericano. Se puede utilizar desde Argentina, Brasil, Chile, México y (al parecer) próximamente Colombia, pagando en moneda local de cada país o en dólares.

La implementación está integrada al código original en javascript, no sobre SPIP, por lo que puede ser útil para usarlo independientemente o integrarlo con otro CMS.

El código está disponible en GitHub

Instalación rápida

Obviamente, se incluye el código javascript en la cabecera de la página HTML.

<script type="text/javascript" src="simpleCart.js"></script>

y se definen, al menos, las variables requeridas:

<script type="text/javascript">
    simpleCart.checkoutTo = DineroMail;
    simpleCart.dmMerchantId = 'XXXXXXX'; //Número de cuenta
    simpleCart.dmCountryId = 1; //1: Argentina, 2: Brasil, 3: Chile, 4: México
</script>

¡Listo! Al hacer Checkout, el contenido del carrito será enviado a DineroMail para procesar el pago.

Otros parámetros

El carrito de DineroMail tiene muchísimos parámetros opcionales que en su mayoría he omitido. Mirá el manual de integración para conocer la API completa. Sin embargo, algunas variables opcionales las tuve en cuenta. Acá listo todas las implementadas:

Variable JS Descripción
dmMerchantId Número de cuenta de DineroMail (los primeros 7 digitos sin el dígito verificador /X). (requerido)
dmCountryId Código de país del vendedor. 1: Argentina, 2: Brasil, 3: Chile, 4: México. (1 por defecto)
dmSellerName Leyenda que el vendedor quiere mostrar en lugar de su email.
dmHeaderImage URL de la imagen a mostrar en el Header
dmCurrency Moneda de pago. Puede ser USD (dolares) . Si no se define, se usar la moneda local del país del vendedor.
dmOkUrl URL donde se re direcciona al comprador en caso de transacción exitosa
dmErrorUrl URL donde se re direcciona al comprador en caso de transacción errónea
dmPendingUrl URL donde se re direcciona al comprador en caso de transacción pendiente.
dmPaymentMethods Cadena de medios de pago permitidos. Por defecto elige todos los medios disponibles para el país, pero se puede configurar específicamente. Por ejemplo para permitir Pago Fácil, Rápipago y Visa en 1, 3 y 6 cuotas: ar_dm;ar_pagofacil;ar_rapipago;ar_visa,1,3,6

Nota: Todas las variables son atributos del objeto simpleCart, por ejemplo simpleCart.dmSellerName = "Vendedor Loco";

Además se puede asignar un código de producto al item.

<li class="simpleCart_shelfItem">
<h2 class="item_name">Producto X</h2>
 <span class="item_price">$18.00</span>
 <span class="item_code">X18A</span>
</li>

El valor ’X18A’ se envía a DineroMail para futuros controles del vendedor.

Es todo por ahora. ¡A vender!

Actualización

Agregué unos selectores asociados al evento "click" para permitir cambiar dinámicamente el método de pago, de manera de darle más libertad al comprador.

Basta con declarar un elemento que acepte el evento onclick y asociarlo a alguna de estas clases

class descripción
simpleCart_to_paypal Activa PayPal como medio de pago
simpleCart_to_googlecheckout Activa Google Checkout como medio de pago
simpleCart_to_dineromail Activa Google Checkout como medio de pago

Un ejemplo de uso podría ser:

<h3>Elija su método de pago</h3>
<label>Paypal</label> <input type="radio" name="method" class="simpleCart_to_paypal">
<label>Google Checkout</label><input type="radio" name="method" class="simpleCart_to_googlecheckout">
<label>DineroMail</label><input type="radio" name="method" class="simpleCart_to_dineromail">

Cuando el usuario hace click en el botón de opción automáticamente se configura el atributo simplecart.checkoutTo al valor correspondiente.

Comentarios () | Link

Publicado el 12 de septiembre de 2010 | Categoría/s: SPIP, E-Commerce, Javascript

SimpleCart es un sistema para e-commerce muy sencillo íntegramente desarrollado en javascript. Permite realizar ventas mediante PayPal o Google Checkout y es muy extensible para implementar otros sistemas.

En este domingo de procrastinación, decidí integrarlo a SPIP [6]. Lo usaré, espero que pronto, para vender los productos del taller de serígrafía de Mazamorra.

Cómo funciona

Se instala como un plugin común y se configura mediante el plugin CFG. Por ahora sólo soporta PayPal como medio de pago, pero incluiré otros.

SimpleCart usa simples señaladores html, que yo he convertido en balizas.

Baliza Descripción
#SIMPLECART_QUANTITY Indica la cantidad de productos
#SIMPLECART_ITEMS Muestra los items en el carrito según las cabeceras definidas en la página de configuración
#SIMPLECART_TAX_RATE Indica la tasa impositiva indicada en la configuración
#SIMPLECART_TAX_COST Indica el total de impuestos
#SIMPLECART_SHIPPING_COST Indica el costo de envío en función de la configuración definida
#SIMPLECART_TOTAL La suma del precio de los productos en el carrito (Subtotal)
#SIMPLECART_FINAL_TOTAL Total considerando impuestos y envío
#SIMPLECART_CHECKOUT Genera el botón para finalizar compra (redirige al sistema de pago)
#SIMPLECART_EMPTY Genera el botón para vaciar el carrito

Con estas balizas puedes armar el carrito en cualquier lado. Los datos del carritos son persistentes usando cookies (incluso se conservan durante un tiempo aunque se abandone la página).

Por ejemplo, este puede ser tu carrito .

  1. <h3><:simplecart:your_cart:> (#SIMPLECART_QUANTITY items)</h3>
  2. #SIMPLECART_ITEMS
  3. <div id="cartTotal">
  4. <strong><:simplecart:subtotal:>:</strong> #SIMPLECART_TOTAL<br />
  5. <strong><:simplecart:tax_cost:></strong> (#SIMPLECART_TAX_RATE): #SIMPLECART_TAX_COST <br />
  6. <strong><:simplecart:shipping_cost:>: </strong> #SIMPLECART_SHIPPING_COST <br />
  7. <strong><:simplecart:final_total:>: #SIMPLECART_FINAL_TOTAL</strong> <br />
  8. </div>
  9. #SIMPLECART_CHECKOUT
  10. #SIMPLECART_EMPTY
  11. <div class="simpleCart_clear" />

Cómo agregar productos

Muy simple. Se sube una imágen como documento definiendo su título y descripción y se la incrusta con el modelo item. El precio se indica con el parámetro price.

Por ejemplo, el documento 111 se vuelve un item si se lo incluye así

<item111|price=23.4>

Un atajo más

El código para el carrito de arriba lo incluí como un modelo para poder incrustarlo directamente desde la redacción de un artículo con <cart1> .

Demostración

Si alguien completa la transacción, lo interpretaré como una donación :D.

DVD demo 1

Linda peli

$ 23.4 Agregar al carrito local/cache-vignettes/L71xH100/the-incredibles-92309.png
Wolwerine DVD

Descripción de ejemplo

$ 15.4 Agregar al carrito local/cache-vignettes/L71xH100/wolverine-3p3852-162d4.png

Tu carrito ( items)

Subtotal:
Impuestos ():
Costo de envío:
Total:
Finalizar la compra Vaciar

Descarga

El plugin está en desarrollo sobre spip-zone. Podés hacer un ckeckout SVN

Comentarios () | Link

Publicado el 26 de agosto de 2010 | Categoría/s: Empresa

Distintos laboratorios de investigación y vinculación de mi facultad del área química y del área computación, junto a algunas empresas de software locales (VATES y otra) y del sector petroquímico (Petroquímica Rio Tercero) están conformando un "Consorcio Asociativo Público Privado (CAPP)" en el marco del programa FSTICS del Ministerio de Ciencia y Tecnología de la Nación, con el fin de desarrollar una plataforma de desarrollo I+D de software para la industria. Para que contextualicemos de que hablamos: el programa estipula un aporte estatal de hasta el 60% para un proyecto de hasta 38 millones de pesos.

Mi participación es minúscula y aledaña: el impulsor de este proyecto es el Dr. Martín Cismondi, director de mi proyecto integrador que se trata de un prototipo del (tipo de) software que se desarrollaría desde esta plataforma.

Cismondi es Phd en ingenieria quimica y sus investigaciones en el campo del equilibrio de fases tienen mucha aplicación en la industria. En particular en el petróleo, donde el impacto que puede tener mejorar el rendimiento de un proceso (por ejemplo de destilación) se mide en millones de morlacos.

Ayer tuve oportunidad de participar de una reunión, sirviendo de una especie de "traductor" entre los quimicos y los informáticos.

Si bien, repito, no tengo voz (mucho menos voto) en nada de lo que allí se resuelva, tengo interés en investigar y transmitir de la manera más clara posible otros modelos de negocio que, por lo que presencié ayer, están lejos de ser tenidos en cuenta. Me refiero, claro, a que, sobre todo (no muy increíblemente) desde la facultad, no se ve más allá de un modelo de negocio de software cerrado de venta por licencia.

Apunto algunas cuestiones sin orden:

- El nicho de mercado es bastante acotado: industrias de gran escala mayormente transnacionales. Separadamente tiene mucha utilidad y aceptación en centros de investigación académicos.

- Desde el grupo conformado, no existe conocimiento cabal de las soluciones de software específicas que ese mercado, el industrial, demanda. Sí se sabe que el -*expertise-* y el -*know how-* que el grupo de investigación de Cismondi tiene son el valor diferencial.

- No existe hoy peso específico suficiente, desde el punto de vista del producto existente, para competir con las empresas de software proveedoras de este nicho, sobre todo en los mercados de USA y Europa. Por ejemplo Aspen Tech.

- Es difícil, a priori inimaginable, desarrollar un producto genérico que satisfaga necesidades de diversos clientes potenciales. La especifidad de los problemas de cada industria se supone grande.

- Preguntas para hacerse ¿cuantos clientes reales existen en el mercado? ¿qué "llegada" se tiene con ellos? ¿que posibilidades reales de venderle un software cerrado existen, teniendo en cuenta que no se sabe cabalmente cuales son sus demandas? Supongiendo que se conoce un problema específico ¿cuanto sale desarrollar una solución ? ¿se puede correr el riesgo de desarrollarla sin tener asegurada su comercialización?

Soy un novato en el área negocios con FLOSS (y en negocios, a secas, también) pero lo que se me ocurre viene más o menos por este lado:

1. Generar una estrategia para una fuerte inserción en el ámbito académico internacional

  • Para esto es indispensable la libre disponibilidad del código fuente de manera que los métodos numérico-científicos sean transparentes, reproducibles y verificables. Ver este post en OpenScience Project.
  • Para evitar practicas predatorias sobre este trabajo, que alguien cierre y comercialice, hay que orientar a una licencia FLOSS vírica, donde los trabajos derivados mantengan la condicion libre.
  • De esta manera se apunta a constituir una comunidad tecnologico-científica donde el nucleo de desarrollo se mantendrá en los autores originales pero permitiendo y aprovechando código y feedback de los usuarios.
  • En esta etapa el beneficio no es a priori monetario y en cambio sí en cuanto a calidad del software, inserción y difusión del/los producto/s, fidelización de usuarios y prestigio del equipo de desarrollo.

2. La repercusión de la masa crítica generada abre la puerta al mercado por las siguientes razones:

  • Muchos de los académicos que utilicen y formen parte de un "comunidad" alrededor del sofware FLOSS desde las universidades o llevaran su conocimiento a la industria.
  • Tambien los investigadores dentro de la industria tienen vínculos fluidos con la comunidad científica, sobre todo en estas áreas de investigación de vanguardia de directa aplicabilidad.
  • El mercado a priori imposible de acceder se puede hacer más permeable: los responsables de investigación demandan la utilización del software en los procesos de la industria o sus labores particulares.
  • Si el costo para la industria es nulo o bajo y de calidad, hay muchas probabilidades de que sea aceptado.
  • La ventaja diferencial del cliente es evitar el vendor lock-in. Sin embargo, la ventaja competitiva del equipo de desarrollo original, por su -*know how-* adquirido es insuperable para cualquier potencial competidor.

3. Insertado en el mercado se generan posibilidades de lucro en dos áreas

  • Customización y extensión del software bajo requerimientos específicos. Entra en el marco de SaaS
  • Capacitación y entrenamiento a equipos técnicos de la industria y la academia, tanto en el área de aplicación (quimicos) como de desarrollodo de software.

Muchísimo material para leer sobre el tópico hay en este sitio. Yo empezaré en cuanto tenga tiempo. Invito también, de manera enfática, a leer la justificación de que el software desarrollado en el marco de mi proyecto integrador lo libero como Software Libre

Más allá del negocio

Además de lo expuesto se me cruzan cuestionamientos sobre el rol de la universidad y el estado en este escenario.

Como ciudadano y futuro profesional festejo la inversión que en los últimos años el Estado ha realizado para ciencia y tecnología. Apostar a generar valor en áreas tan estrategicas como el software es necesario, y plantearlo desde el punto de vista de fortalecer la industria nacional (pymes) es entonces doblemente valioso.

Pero...

¿son los laboratorios de investigación de las universidades actores que deben generar recursos por sí mismos? ¿A quien pertenece el conocimiento que se produce en la universidad? ¿A quién debe pertenecer? ¿La universidad actual responde a un proyecto de país ? ¿Existe tal cosa? ¿No debiera el Estado, formar parte societaria en los consorcios para los cuales está aportando grandes recursos, además de ser agente regulador? ¿quién y cómo regula la relación e intereses existentes entre los grupos de investigación y el sector privado? ¿no se puede prestar a malversaciones y abuso del sello de la universidad?

Me desperté preguntón (como nací).

Comentarios () | Link

Publicado el 19 de agosto de 2010 | Categoría/s: Python

Hace 5 años que laburo en una organización de trabajo barrial. Como no recibimos aportes privados y rara vez del Estado (a veces presentamos proyectos a programas) siempre andamos buscando formas de sostener económicamente nuestras actividades.

Un recurso, no por viejo en desuso, es el del "bono contribución", que muchas veces incluye un sorteo con algún premio.

Además de la algo ingrata tarea de venderlos, hay que hacerlos. Alguno tiene uno de esos sellos numeradores (un mecanimismo que va incrementando el contador automáticamente ) pero igual cualquiera se vuelve un burócrata (más cuando hay que sellar dos veces - el bono y talón -).

Así que ayer, en 20 minutos hice Bonito, un programa feo pero efectivo.

Primero hice una plantilla en Inkscape donde entran 6 de estos bonitos en un A4.

Como el SVG es XML que es TEXTO, la marca XXXX se puede reemplazar fácilmente por el número que corresponda. Yo quería que me quedaran así:

De esta manera, simplemente tengo que meter 6 broches a la izquierda y recortar, ya quedan ordenados para repartir entre los compañeros y compañeras.

Para algunas cosas, Inkscape se puede usar por línea de comandos, por ejemplo para convertir entre los formatos que soporta. Así paso el SVG con los números reemplazados a un PDF. Después concateno todos los PDF de una tanda (por ejemplo agrupados de a 10, como en el dibujo) con Ghostscript.

Acá el código:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#       bonito.py
#      
#       Copyright 2010 Martin Gaitán <gaitan(at)gmail.com>
#      
#       This program is free software; you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation; either version 2 of the License, or
#       (at your option) any later version.
#      
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#       GNU General Public License for more details.
#      
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.
import sys, os
import subprocess
import glob
def markToNum(content, num, mark='XXXX'):
    num_as_string = '0' * (4 - len(str(num))) + str(num)
    return content.replace(mark, num_as_string, 2)
def main():
   
    if len(sys.argv) <= 1:
        print "Modo de uso:"
        print sys.argv[0] + " archivo.svg"
        sys.exit(1)
    else:
        with open(sys.argv[1]) as input:
            svg_content = input.read()
       
        index_from = int(raw_input('Desde [0]') or 0)
        number_by_page = int(raw_input('Num por pagina [6]') or 6)
        grouped_by =  int(raw_input('agrupar de a [20]') or 20)
        planchas = int(raw_input('Planchas [1]') or 1)
       
        for plancha in range(planchas):
            counter_from = index_from + plancha*number_by_page*grouped_by
            for pagina in range(grouped_by):
                page_content = svg_content
                for bono in range(number_by_page):
                    num_remplazo = counter_from + bono*grouped_by + pagina
                    page_content = markToNum(page_content, num_remplazo)
       
                with open('temp.svg', 'w') as output_svg:
                   
                    output_svg.write(page_content)
                subprocess.call(["inkscape", "-f", 'temp.svg', '--export-dpi=150', '-A', 'temp%s.pdf' % ("0"*(4 - len(str(pagina))) + str(pagina)) ])
            generator = ['gs',
                         '-q',
                         '-sPAPERSIZE=a4',
                         '-dNOPAUSE',
                         '-dBATCH',
                         '-sDEVICE=pdfwrite',
                         '-sOutputFile=%s-%i-%i.pdf' % (sys.argv[1][:-4], counter_from, num_remplazo),] + \
                        ['temp%s.pdf' % ("0"*(4 - len(str(pagina))) + \
                            str(pagina)) for pagina in range(grouped_by)]
            subprocess.call(generator)
            for temp in glob.glob('temp*'):
                os.remove(temp)
if __name__ == '__main__':
    main()

Nada que no se pueda hacer con Bash, cierto, pero mucho más fácil de escribir (y de leer).

De paso, acá está la plantilla, por si a alguno le sirve.

Scalable Vector Graphics - 149.3 KB
Comentarios () | Link

Publicado el 6 de agosto de 2010 | Categoría/s: Python

Leemos en la web

The Django Dash is a chance for Django enthusiasts to flex their coding skills a little and put a fine point on “perfectionists with deadlines” by giving you a REAL deadline. 48 hours from start to stop to produce the best app you can and have a little fun in the process.

Suena a PyWeek [7], pero en 2 dias. ¿Será indicio de lo rápido que se programa con Django?

Atenti que la inscripción cierra el 8 de agosto (mañana).

¡A ver esos perfeccionistas!

Comentarios () | Link

Publicado el 3 de agosto de 2010 | Categoría/s: Python

La aplicación que estoy desarrollando, GPEC, genera muchos mensajes que pueden ser útiles para el usuario.

En softwares con GUI’s sencillas suele utilizarse la barra de estado para mostrar mensajes contextuales e información sobre el resultado de una acción. Pero si estos mensajes son muchos y de diversa índole, este espacio puede no bastar, sobre todo por la volatilidad de la información que la barra de estado muestra.

Una solución posible es usar un panel con un ListCtrl de manera de poder agregar los mensajes quedando un registro completo y cronológico; un log propiamente dicho.

PNG - 27.3 KB
Ejemplo
Panel de log de Mayavi2

Surge acá un detalle: si los mensajes se generan desde "cualquier parte" del programa, todas esas "partes" deberían tener referencia de la instancia del panel/widget de log.

Un ejemplo: todos las demostraciones de la aplicación de demo de wxPython tienen la siguiente estructura:

class TestPanel(wx.Panel):
    def __init__(self, parent, log):
        self.log = log
        wx.Panel.__init__(self, parent, -1)
        ...
        self.log.WriteText(' message ... ')

Esto vuelve la aplicación muy acoplada: la instancia log (que es un caja de texto en el demo) se pasea por distintos namespaces para estar disponible en todos lados.

Denotemos la falta de flexibilidad: ¿que pasa si queremos ’loggear’ mensajes desde un objeto donde no estaba previsto? ¿qué pasa si queremos cambiar el widget que muestra los mensajes y el método para anexar mensajes tiene otro nombre? ¿y si además de mostrarlos, con algunos los mensajes queremos hacer alguna otra cosa (ejecutar un simple sonido de alarma, por ejemplo) ?

PubSub

Una manera más elegante y eficiente es utilizar PubSub una implentación en Python del paradigma de publicación-suscripción.

Su implementación es trivial. Incluso viene incorporado dentro de wxPython.

El Publisher (generalmente importado como pub) envía mensajes (cualquier objeto python) asociados a un tópico (una cadena)

from wx.lib.pubsub import Publisher as pub
pub.sendMessage('log', ('ok', 'Ready! You can send any message from anywhere.') )

En este ejemplo, el tópico, que elegí arbritrariamente, es ’log’, y el mensaje es la tupla ('ok', 'Ready! You can send any message from anywhere.')

Del otro lado del mostrador, cualquiera puede suscribirse a los mensajes con determinado tópico y asociarlos a un método/función.

class LogMessagesPanel(wx.Panel):
    def __init__(self, parent, id):
        wx.Panel.__init__(self, parent, id)
        pub.subscribe(self.OnAppendLog, 'log')
    def OnAppendLog(self, msg):
        data = msg.data<img105|center>
        #do your things with the data!  

pub.subscribe bindea los mensajes con tópico ’log’ al método OnAppendLog pasando un objeto msg. Nuestro mensaje real, la tupla que enviamos, está en msg.data

Nada impide que sean muchos los objetos que envien mensajes con tópico ’log’ y muchos otros estén suscriptos a él. Y esto funciona sin importar dónde ocurra cada cosa! [8].

Como ejemplo completo dejo el panel log. Podés probarlo creando otro frame independiente que envie mensajes de log.

#       Log Panel: example of PubSub implementation
#      
#       Copyright 2010 Martin Gaitán <gaitan(at)gmail.com>
#      
#       This program is free software; you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation; either version 2 of the License, or
#       (at your option) any later version.
#      
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#       GNU General Public License for more details.
#      
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.
import wx
from wx.lib.pubsub import Publisher as pub
import time
import sys
from wx.lib.embeddedimage import PyEmbeddedImage
icons = {}
icons['ok'] = PyEmbeddedImage(
    "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAAXNSR0IArs4c6QAAAK5QTFRF"
    "AAAA////2vO/+P3z+f31VJkEVJcEU5UEUpQEcc0GcMsGcMkGb8gGbcYGbcQGbMEGZLYFXKYF"
    "UJAET48ETo0ETYwEasAGddALd9ANeNERetETe9IVfdIXfdMZftMbf9Mci9cwlNpAlttElttF"
    "l9xHmtxLm91NnN1Pnd1Rnt5ToN9Xod9ZpuFhp+FjqOFlquJnr+RyseR0veiKwuqSy+2j4PTI"
    "9vzv9/zxidYs3fPCGN7g1AAAAAF0Uk5TAEDm2GYAAAABYktHRACIBR1IAAAACXBIWXMAAAsT"
    "AAALEwEAmpwYAAAAB3RJTUUH2gcMBC0Irn+MQAAAAINJREFUGNNjYCAJ6Bui8vXkpAxQ+LJa"
    "ylx6CL6utKYGJyszkGVpAuZLaahzsYH4phYKxgwMOpJqatzsID6ThbIGt5G+hKoKDweIz8Ai"
    "YaGtIiOnrMIrxAwxjJlXXl1VSYlPmBlmPLOAuLIivwgzwkJzQV4xUWZkJ5mzi7CgOtqMBbcH"
    "AVouCiZO5Tf/AAAAAElFTkSuQmCC")
#----------------------------------------------------------------------
icons['error'] = PyEmbeddedImage(
    "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAAAAAAAD5Q7t/AAAA"
    "CXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH1QsKCTIOk1O0sAAAAhpJREFUOMudk71Pk1EU"
    "xn/3bQvlo76gxBpNoGibArYLxJREh466MmnSgRH+IjsydGYjspBAWEQHMJIWmmhj+LAkpC1N"
    "Wyj2PcfhfUsQiRpPcnOTm/P8nuQ59xhuVA5SwDyQBsa95xKwDixlYOt6v7khfjscCi1MJpME"
    "w2F8AwOoKp2zM87LZYqFAtVmM5uBxd8AOXg3nUy+HJmZ4XJ7G+f0FBwHVUUMWEN38U9NUt3d"
    "5VOxuJqBVwC+rvN0Mvn6XixGe3MTbTRABFVBRUEEabboHHyjPxbDtqzoi2r1wTKsmBykhkOh"
    "98/m5rhYW0OdDqqKKqh6YgXFhakxBFMpPm9sUGu1Zi1gfjKR4HJnB3UcEFABFXEP6oEUVMFx"
    "uMjneTwWAZj3A+lg+D4/PnxERRkrf+dPtW/fQao1Ag8fAaT9wLjVP4iKG9jfSkRR7WB6AgDj"
    "FnCV9r8A8PpUBQA/UOrUzyYUg6rDl5ERt0HEvVE3PDdVRN0gtdkCKFnA+vnJCcYeuiL/Iu46"
    "iidWwRocpF2rAaxbwNL+3h6BeNwly3WRNzrtTkNQY/BHIpQODwGWrAxs1VqtbKWQp2fqqQu5"
    "5op3qygYQ080RuPggHq7nc3Alg9gGVaeVyqzts8XHUgkcBoN5KJ99RMVMHaI3vgEjXKZwvHx"
    "agbe3LpMdl/fwpPRUQK2jentRUVwmk069Tpfj466zou3buP/rPNPwkdmHrlYdncAAAAASUVO"
    "RK5CYII=")
#----------------------------------------------------------------------
icons['info'] = PyEmbeddedImage(
    "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAAAAAAAD5Q7t/AAAA"
    "CXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH1QMCBiAyOlCc9wAAADV0RVh0Q29tbWVudAAo"
    "YykgMjAwNCBKYWt1YiBTdGVpbmVyCgpDcmVhdGVkIHdpdGggVGhlIEdJTVCQ2YtvAAACq0lE"
    "QVQ4y5WSS0hUcRTGv//cO+O9OtdHJllqZlI+CEUiE8ShBiXUpKKQbGLKcGG0CBOyRcsQMapF"
    "j02ii4igAgMDJcgQSXQmSkrNijSVTJ1xdJy5d6739W/loDURfqtz4Pt+nHM4BBFUXNnUTCkc"
    "BiUpIISYiDHLEPq0/2Vr459edn1TVN6UwTCk72JtZVq5vQBxAg9VN+DxBVN7ej9cISbmLKV6"
    "SX9X69eIAJYlb++11G/fk7ljLBBSRmd9khJaVVlF06NtJflZ6enJe1tuPXYBiF/LMGtFSdXV"
    "2/Xnj5YVH8z5rKi6m7eYFQBEpyCqTg3vSmiWi+YQzXM7dSYlc3L8bScAmMIkhj1Xbi9AIKSO"
    "sSxDLWbGIIRQUBiabiiUwuganOwuKswBw3Kn1nJhwKpC4wUrD1nRV1XNSDQzpguJsZxzV7Lg"
    "yN+91QElkDrlEf08zyEgqVxtXYNpww0IIUTRdEiyajIMKiZYo+D1Sy5RCjErQUkbnpaGFUU3"
    "qaqxFuEBiGFADM9KXl8gRpRVy4qk+jOSYzH6w/deVHQ6tyQp7m+LHsFqSfD6g4gTeLWjrVnc"
    "sIKZ0QZ63gyDj2JzJ34tiwDw0yfqHye8/t7hmYUlUTZX7E+rGHKNwcph4q8bbBNCxzu7+uWZ"
    "6fnstCTroWvtAzfPHM665P4yH2JYkui0Z1X7F5dy3IMjqsD6SsOrr/+DY6cbq2QqPC8rLbIU"
    "HshGNM9B1XX4lkUMucbxzjWixXHS5SftNx5EBABAbV1DYohucQVkpDtOJDGEACwLdHXPBRnN"
    "v6+j7c4U/qeaGrvV6bRRz2IfFWUX/T75jDqdNhrJy/6DoQLA/bsPkZ2bjempaWxaTqeNzsy+"
    "MgLSEP00+sLY7AQAUFV98nqNIMQfWVhYeJ2XF/sokuk3Vkw2XnyKHQQAAAAASUVORK5CYII=")
#----------------------------------------------------------------------
icons['warning'] = PyEmbeddedImage(
    "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAA"
    "CXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH1gIQDictt+6SdwAAAehJREFUOMuVk8FKG2EU"
    "hb87mc40iTODEJlK7UZDxCCGQUqaMASCSGODdBe6ECJddOcwG/EJXBt0ZcBCwEVJVgWtr1AI"
    "PoBQXAQKhRZc1EBJMX831hLItPbAvYvL4Ttnc4UIhfDIgLcAA3jdgC/jfHoUwISjeXgOcAFH"
    "QHWcT4tI91KwWlhY0J6kUloKVkPw7g0wYS8NemJ3l0yzSRp0E/buBQihOAXFbLksej6Pns+T"
    "LZdlCoohFPmXdqB7AsNep6M8z1Oe56lep6NOYLgD3b82CGHFhaWM74vh+ziOg+M4GL5PxvfF"
    "haUQViIBJuxnwUgEAaJpWJaFZVmIppEIArJgmLA/FhDC+jSk5woFjFIJEcG2bWzbRkQwSiXm"
    "CgWmIR3C+gggBDGgsXibruk6IvKngQiarpMIAhbBMKARgsDtCuHVLLReLC8bk2dnEIuhlKLf"
    "7wOQTCYREbi54WptjQ/n54NLqDfgnYQQewxXT8GaPz7mYbWKUgqlFLVaDYB2u42IICL8OD3l"
    "YmODLnz/DJOawGYczJlcjnilcmcUEVzXxXXdkVu8UmEmlyMOpsCmbMPXl5CaPThgol6/S/89"
    "wAhARLhutbjc2uI9fJNDGD4DecD/6SfwEZT+CQ4H8GYi4i+idA3DHjR/AZfefQgctOETAAAA"
    "AElFTkSuQmCC")
class LogMessagesPanel(wx.Panel):
    def __init__(self, parent, id):
        wx.Panel.__init__(self, parent, id)
        self.list = wx.ListCtrl(self, -1,  style=  wx.LC_REPORT|wx.SUNKEN_BORDER)
        self.setupList()
   
        sizer = wx.BoxSizer()
        sizer.Add(self.list, 1, wx.EXPAND)
        self.SetSizerAndFit(sizer)
   
        pub.subscribe(self.OnAppendLog, 'log')
       
    def setupList(self):
        """sets columns and append a imagelist """
       
         #setup first column (which accept icons)
        info = wx.ListItem()
        info.m_mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT
        info.m_image = -1
        info.m_format = 0
        info.m_text = "Message"
        self.list.InsertColumnInfo(0, info)
        self.list.SetColumnWidth(0, 550)
        #insert second column
        self.list.InsertColumn(1, 'Time')  
        self.list.SetColumnWidth(1, 70)
        #setup imagelist and an associated dict to map status->image_index
        imgList = wx.ImageList(16, 16)
       
       
        self.icon_map = {}
        for key, bitmap in icons.iteritems():
            indx = imgList.Add( bitmap.GetBitmap() )
            self.icon_map[key] = indx
        self.list.AssignImageList(imgList, wx.IMAGE_LIST_SMALL)
       
    def OnAppendLog(self, msg):
        ico = self.icon_map[msg.data[0]]
        message = msg.data[1]
        index = self.list.InsertImageStringItem(sys.maxint, message, ico)
        self.list.SetStringItem(index, 1, time.strftime('%H:%M:%S'))
        self.list.EnsureVisible(index) #keep scroll at bottom
class TestFrame(wx.Frame):
    def __init__(self, parent, id):
        wx.Frame.__init__(self, parent, id, "Log Panel demo")
        self.log = LogMessagesPanel(self, -1)
        self.SetSize((620,150))
        self.SetMinSize((620,150))
if __name__ == "__main__":
    app = wx.PySimpleApp(0)
    wx.InitAllImageHandlers()
    main_frame = TestFrame(None, -1)
    app.SetTopWindow(main_frame)
    main_frame.Show()
    pub.sendMessage('log', ('ok', 'Ready! You can send any message from anywhere.') )
    pub.sendMessage('log', ('info', "Just import pubsub.Publisher and send a 'log' message") )
    pub.sendMessage('log', ('warning', "The message data is a tuple ('icon', 'message') ") )
   
    app.MainLoop()
Comentarios () | Link

Publicado el 13 de julio de 2010 | Categoría/s: Python

Un típico uso de un ListCtrl es usarlo para mostrar un Log de la aplicación que muestra mensajes al usuario, similar a lo que puede hacer la barra de estada, pero con la ventaja (complementaria, si se quiere), de mantener un historial de los eventos ocurridos.

En tal caso, primero, hay una decisión de diseño que hacer: ¿los nuevos mensajes se agregan al inicio (como un blog) o al final de la lista (como los comentarios) ?

No sé si existe una respuesta canónica a la cuestión, pero infiriendo (de los pelos) el sentido de lectura occidental (de izquierda a derecha y de arriba hacia abajo) decidí que "un nuevo contenido" debe estar abajo del anterior.

Eso trae aparejado un nuevo problema: si se agrega un nuevo item a ListCtrl al final y ya hay más de los que caben en el espacio visible del control, el último (el más importante) no se verá en pantalla y el usuario debería hacer un scroll hasta el final para verlo. Es decir, para un panel de Log, el scroll vertical siempre debería mantenerse abajo.

Por suerte no hay que liarse con generar eventos programaticamente, ni manipular el scroll, ni obtener dimensiones del widget. He aquí una solución "fits your brain": el método EnsureVisible(index)

Por ejemplo:

    def OnAppendLog(self, msg):
        ico = self.icon_map[msg.data[0]]
        message = msg.data[1]
        index = self.list.InsertImageStringItem(sys.maxint, message, ico)
        self.list.SetStringItem(index, 1, time.strftime('%H:%M:%S'))
        self.list.EnsureVisible(index) #keep scroll at bottom

Al agregar un nuevo mensaje de log, obtenemos el índice y wx nos asegura que la fila con ese índice estará visible en pantalla.

Comentarios () | Link

Publicado el 13 de julio de 2010 | Categoría/s: Python

Hace aproximadamente dos meses que estoy trabajando en lo que será mi proyecto final, para recibir el título de ingeniero en computación.

El proyecto es una aplicación de uso "científico y académico" implementada en Python que sirve para obtener diagramas de equilibro termodinámico entre fases fluidas de sistemas binarios. Toda la batahola está en este repo.

En concreto, el proyecto se circunscribe a la generación y [análisis sintáctico] de archivos de texto con el que la aplicación se comunica con los programas que implementan los algoritmos de cálculo, que están implementados en Fortran, y son, literalmente, unas cajas negras. [9]. Se procesan esos archivos para obtener arrays con los que se plotean las distintas curvas.

Utilizo principalmente wxPython, Matplotlib y Numpy. Vale destacar, ya que mucho de un trabajo final es documentación, que escribo en restructuredText, renderizando con Sphinx y el genial rst2pdf

Esta perorata para contar que iré apuntando en este abandonado blog (no más que el otro, y en general, el resto de mi vida) algunos descubrimientos informáticos y de otro tipo, si es que ocurren, en el desarrollo de este proyecto.

Comentarios () | Link

Publicado el 10 de mayo de 2010 | Categoría/s: Linux

Esto es una ayuda memoria para mi yo dubitativo del futuro: para hacer funcionar el video de la notebook que es SiS 671/771 en Ubuntu (a partir de la versión 9.10) instalar este paquete y este xorg.conf este xorg.conf.

Para pantalla dual con LCD externo a resolución full 1280x1024

Comentarios () | Link

Publicado el 23 de marzo de 2010 | Categoría/s: Python

Python es un gran lenguaje de programación, y como tal se merece la comunidad que tiene, donde expertos y novatos se sienten parte.

Comentarios () | Link

Publicado el 11 de diciembre de 2009 |
This is perhaps the most beautiful time in human history;
it is really pregnant with all kinds of creative possibilities made possible
by science and technology which now constitute the slave of man,
if man is not enslaved by it.

Jonas Salk

No sólo de la técnica vive el hombre.

Mi otro blog ganó una mención especial en el concurso El Blogazo del Centro Cultural España-Córdoba, en la categoría literatura. El jurado, entre otros, lo integraban Hernán Casciari y José Playo

Comentarios () | Link

Publicado el 2 de diciembre de 2009 | Categoría/s: Python, Empresa

Para los trabajadores online freelance (programadores, diseñadores, traductores, etc) trabajar para el exterior es un objetivo primordial. Aunque en algunos sectores la competencia es leonina y se pauperizan los honorarios, el tipo de cambio del peso respecto a otra monedas fuertes hace que siga siendo muy provechoso.

En traducción, que tiene un sistema de presupuesto lineal basado en cantidad de palabras del texto del idioma origen, es fácil cuantificar la diferencia. En Argentina se cobra entre $0,10 y $0,15 (pesos argentinos) por palabra, mientras que en europa se cobra entre 0.05 € y 0.07€. O sea, se gana hasta el triple por realizar el mismo trabajo.

El problema, al menos desde Argentina, es cobrar. Aquí comparto mi experiencia esperando ayudar a otros.

De Adsense a la billetera

JPEG - 1.9 KB

Una forma simple y rápida pero bastante onerosa es Western Union. Es un servicio de transferencias de dinero persona a persona (física o jurídica), con sedes en casi todo el mundo. Tiene dos tipos de servicio: Quick Cash y 24 horas.

Western Union tiene multiples instancias de lucro, que lo vuelven un ser. Se le cobra entre el 2.5 y 5% al emisor (desde Europa a Argentina el límite de transferencia es 500€) y además, el receptor cobra en moneda local a un tipo de cambio definido por WU que siempre está entre un 3 y 6% por debajo del tipo de cambio del dia. Sobre todo los días que hay corridas en suba, el tipo de cambio queda desfasado (tiene una actualización muy lenta) y es un mal momento para cobrar.

JPEG - 2.7 KB

Existe una forma de salvar este cargo solapado para el receptor: si el envío es tipo Quick Cash y en dólares estadounidenses, se puede retirar en dólares en los locales Western Union de los supermercados Walmart (que es el representante oficial de WU en Argentina). Este servicio no tiene costo para el receptor.

Esto es muy útil para quienes utilizan Google Adsense en sus sitios web, ya que Google ofrece este servicio de pago. Basta anotar el número de transferencia (MCU) y documento en mano aclarar en Wester Union de Walmart que queremos cobrar en dólares.

Mucho más fácil, rápido y sin perder un sope que pedir el cheque y venderlo en una financiera.

Cobrar del extranjero

GIF - 1 KB

Internacionalmente está casi estandarizado el uso de Paypal como medio de pago de honorarios (y casi cualquier otro tipo de transacción en internet) pero este sistema no tiene integración con el sistema bancario argentino lo cual complica la tarea de monetizar los fondos.

Además, un usuario no puede cargar crédito a su cuenta paypal con una tarjeta de crédito, aunque esta sea válida internacionalmente. Un detalle que complica a quienes quieren comprar o donar en el extranjero.

Sin embargo, Paypal sigue siendo interesante para todos los que necesitamos cobrar desde el extranjero. Además de facilitarle la vida al emisor (cliente) que seguramente ya está acostumbrado a este sistema, la transferencia entre cuentas paypal personales no tiene costo. Esto significa que si el cliente tiene una cuenta paypal con fondos (que en la mayoría de los paises pudo haber cargado con tarjeta o desde su cuenta bancaria sin costo) la transacción total es gratuita. Además, la operación es instantánea.

Si el cliente envia el dinero a través de paypal pero habiendolo cargado desde una cuenta bancaria o tarjeta de crédito específicamente para dicha transacción, el costo de la operanción es 3,4% + 0,30 USD. El emisor decide quien abona este cargo.

Una observación importante es que predeterminadamente las cuentas PayPal son en una moneda principal (en dólares estadounidenses para Argentina) y al recibir un pago en otra moneda (euros, por ejemplo) la conversión de divisa se realiza al tipo de cambio del día con una retención del 2.5%. El tipo de cambio es actualizado 2 veces por día.

Una sólución es abrir cuentas en distintas monedas. En vez de convertir el pago a la divisa principal, se mantiene en nuestra cuenta en la moneda en que se recibió. Cuando necesitamos realizar un pago o transferencia en esta moneda, nos ahorraremos tener que reconvertirla nuevamente (ahorrando un 5% en total). Claro que a la hora de monetizar los fondos se deberán convertir, pero esto se puede hacer en cualquier momento que creamos conveniente.

Paypal aclara que ofrece este servicio (Multiples divisas) para facilitar el comercio electrónico internacional y no debe utilizarse con fines especulativos. Pero los timberos decidirán.

De Paypal a la billetera

GIF - 1.2 KB

Pero, como decía un amigo, la plata no es plata hasta que no sirve para comprar fernet en la despensa.

Como dije más arriba, no se puede asociar una cuenta bancaria argentina una cuenta Paypal (ni para añadir ni para retirar fondos). Esto deja como única alternativa oficial para retirar fondos el envio de un cheque.

Paypal envia un cheque via correo postal certificado por el monto que solicitemos con un costo de u$s 15 fijo, independientemente del monto retirado. El envío para Argentina se realiza a través de Ocasa, y tiene una demora aproximada de 10 días hábiles.

El problema es que este cheque tampoco se puede cobrar en ningún banco, por lo que hay que venderlo en una financiera o agencia de viajes, que cobran una comisión de entre el 2 y el 5%.

Por suerte existe xoom.com. Es un servicio de transferencia de dinero que intenta competir con Western Union, pero a diferencia de este, la operación se realiza únicamente online, el destino puede ser una cuenta bancaria argentina o efectivo en pesos o dólares, y, hete aquí la clave, el origen de los fondos puede ser una cuenta Paypal.

Problema solucionado: nos hacemos una transferencia a nosotros mismos, retirando fondos de Paypal y enviandolos a nuestra cuenta bancaria, o mejor, en dólares que se cobran en cualquier sucursal de Banco Francés.

A diferencia de Wester Union, los cargos de servicio no son porcentuales fijos sino que están tabulados en rangos del monto enviado lo que significa un ahorro considerable en montos grandes. Para cifras montos menores a u$s500, el costo de servicio ronda le 4.5%

JPEG - 1.5 KB

Además hay que considerar que al cobrar en pesos, Xoom realiza una conversión de divisa a un tipo de cambio que, al igual que Wester Union, está un par de puntos por debajo de la cotización de mercado. La opción de cobrar en dolares evita esta pérdida, pero tiene un cargo de servicio un 40% mayor, lo que significa un costo de operación real del más de 6%.

JPEG - 1.7 KB

Pero hay una forma de salvar esta plata, que convertida a pesos seguramente significarán varios fernés: conseguir un código de descuento. Retailmenot suele tener cupones válidos, ¡de hasta el 100%!

Conclusión: si cobramos desde Paypal (entre cuentas personales) y retiramos los fondos a través de Xoom.com en dólares consiguiendo un cupón del 100%, tendremos toda nuestra platita en el bolsillo, en dólares y sin perder nada en el camino.

¡Espero sirva!

Comentarios () | Link

Publicado el 19 de octubre de 2009 | Categoría/s: Linux

Ya somos avispados usuarios de la línea de comandos y hacemos nuestros pinitos administrando un servidor: movemos, bajamos y hasta compilamos directamente en una maquina remota, muchas veces más potente y con más ancho de banda que la nuestra.

Nuestra terminal cliente sólo ejecuta OpenSSH (o Putty en Windows)

Pero cuando se trata de ejecutar un proceso largo y de nula interactividad no tiene mucho sentido seguir conectados ¿Para qué gastar tiempo y recursos (dejar la PC cliente prendida, por ejemplo) si el trabajo sólo se hace en el servidor?

Este caso se da muchas veces, sobre todo (y aquí lo que me interesa) cuando bajamos archivos muy pesados o hacer un mirror de un repositorio completo.

Y aquí cómo resolverlo: usa GNU Screen.

Screen es un multiplexor/virtualizador de terminales, y entre sus muchas e interesantes características incluye el desacople de su proceso padre.

Esto significa que se puede ejecutar una terminal virtual con screen (en una terminal remota por ssh, por ejemplo), ejecutar cualquier proceso en ese entorno y desacoplarlo, pudiendo entonces desloguearse y cerrar la conexión remota dejando el proceso en curso.

Receta paso a paso

  • ejecuta una consola virtual con screen
    1. $ screen
  • Ejecuta tu proceso largo. Por ejemplo, bajar la beta de Ubuntu Karmic Koala en DVD
    1. $ wget http://mirror.mcs.anl.gov/pub/ubuntu-iso/DVDs/ubuntu/9.10/beta/ubuntu-9.10-beta-dvd-i386.iso
  • Desacoplar el proceso con la combinación de teclas Control+A D
  • Desconectarse, tomar unos mates y volver cuando quieras

Para retomar el proceso

  • Conectarnos de nuevo al servidor mediante SSH
  • Listar las consolas virtuales para averiguar el PID
  • Traer a primer plano el proceso en cuestión con

y allí estará el proceso que dejamos corriendo (o al menos los rastros que dejó en su paso).

Y a propósito, los chicos de Tucan Project agregaron soporte para línea de comandos, un buen motivo para usar este truco. ¡A descargar!

Comentarios () | Link

Publicado el 30 de septiembre de 2009 | Categoría/s: Linux

Para el Mundial del 2006, compré, como tantos otros que no tenían televisor pero sí computadora, una plaquita sintonizadora de TV: una Kozumi con el chip sintonizador BT878, uno de los más genéricos y bastante bien soportado en linux.

Tuvo poca utilidad en aquel 2006, primero porque argentina no duró demasiado y segundo y principal porque un vecino reclamó potestad sobre la señal del cable coaxil que alimentaba mi chip y mis ilusiones mundialistas.

Quedó cajoneada en algun rincón, hasta que rescaté del olvido la videofilmadora de la familia, una vieja y noble JVC , con un montón de casetitos Compact VHS (que es el formato con el que funciona).

Así que después de mucho patear la tarea, decidí conectar los cables y encontrar la forma de convertir en bytes los recuerdos archivados en esos casettes polvorientos.

Lo que hice

Primero, configurar la placa. El chip bt878 se usa en muchísimas sintonizadoras que tienen configuraciones diferentes. Para asignarle los correctos, se pasa un código card que se obtiene de esta lista. En mi caso, para una Kozumi ktv-01c, el código es 151. El comando completo con el que configuro el módulo es:

  1. sudo modprobe bttv card=151 pll=1 tuner=38 radio=1 bttv_verbose=1 gbuffers=4

Luego, el supercomando mencoder:

  1. mencoder tv:// -tv driver=v4l2:device=/dev/video0:input=1:width=480:height=360:norm=ntsc:alsa:adevice=hw.0:amode=1:audiorate=44100:forceaudio:forcechan=1:buffersize=300 -oac mp3lame -lameopts cbr:br=96:mode=3 -af volume=-6:0,channels=1 -ovc lavc -lavcopts vcodec=mpeg4:vbitrate=5000:keyint=125:mbd=2 -vf yadif,crop=464:352:8:2  -o salida.avi

Algunos detalles:

- tv:// indica que se va a capturar video, y se le pasan paramétros luego del flag -tv: driver v4l2, un tamaño de 480x360 (aunque después se recorta), preconfigurado para ntsc (29.97 fps).
- Un aspecto importante son los flags referidos al audio: se captura el audio alsa, indicando la placa de sonido, el modo y la calidad (mono a 44100hz de ancho de banda)
- El audio se comprime con lame a mp3 de 96kps constante.
- con -af volume le bajo el volumen 6db, porque de otra manera me salía saturado (estoy capturando desde el microfono). El parametro forceraudio fuerza la captura aunque la entrada esté silenciada en la mezcladora. También se fuerza a grabar un solo canal con forcechan=1.
- Con -ovc lavc se usa libavcodec (ffmpeg), comprimiendo en mpeg4 a un bitrate de 5000.
- Se aplican 2 filtros de video al vuelo: yadif que es el mejor filtro desentralazador (necesario para captura analógica) y un crop que recorta 8 pixeles de los costados, 2 de arriba y 6 de abajo, para evitar la distorción que producen los cabezales con las cintas viejas.

El resultado es un video de unos 250kb/s de calidad más que aceptable.

Por ejemplo, el casamiento de mi hermano Juan, en 1998:

Se me pianta un lagrimón. Cuanta juventud, cuantos recuerdos. ¡Gracias gente!

Comentarios () | Link

Publicado el 29 de septiembre de 2009 | Categoría/s: Python

Learning Python de Mark Lutz, ha sido, desde su primera versión en 1999, uno de los libros de cabecera para aprender Python.

Hace unos días salió la 4ta edición, adaptada a las versiones 2.6 y 3.1 de Python y ampliada en varios capítulos.

Lamentablemente, tardará mucho en llegar a la librerías argentinas, pero podés ir leyendo la versión en PDF.

PDF - 7.3 MB
Learning Python 4th Edition
by Mark Lutz, OReilly Oct-2009
Comentarios () | Link

Publicado el 20 de septiembre de 2009 | Categoría/s: Python

El problema nº 2 pide obtener la suma de todos los números impares pertenencientes a la sucesión de Fibonacci menores a 4 millones.

El código con el que lo resolví es este:

def fibo(max):
    a, b = 0, 1
    n = a + b
    while n < max:
        yield n
        a, b = b, n
        n = a + b
sum([i for i in fibo(4000000) if i%2 == 0])

El resultado es 4613732.

Explicación

Fibonacci es el "hola mundo matemático" y Python luce su elegancia con este problema.

El aspecto interesante de la función fibo() definida en el código de arriba es que no se trata de una función común sino de un generador. Sintácticamente la diferencia está en que no utiliza la sentencia return sino yield .

La definición de yield. en su acepción de verbo, dice «end resistance, especially under pressure or force;». Ceder, no oponer resistencia.

La diferencia sustancial entre un yield y un return es que el yield devuelve el resultado parcial de cada iteración y hace una "marca de entrada" desde donde se comenzará a ejecutar la próxima vez que la función (el generador) sea invocado.

Esto permite una recursividad con "evaluación perezosa", mucho más eficiente en términos computacionales.

El Filtrado de los numeros impares de la sucesión y la sumatoria es la misma solución que se apliqué en el problema 1.

Comentarios () | Link

Publicado el 18 de septiembre de 2009 | Categoría/s: Python

A través del blog de Juanjo Conti descubrí Project Euler, una serie de desafíos matemáticos de enunciación sencilla para resolver con computadoras.

Juanjo estuvo resolviendo los primeros problemas, y yo quise hacer mi intento. El primero plantea obtener la suma de todos los multiplos de 3 o de 5 menores a 1000.

Lo resolví con esta pythonica (lo es?) línea:

sum([i for i in range(1000) if (i % 3 == 0 or i % 5==0)])

El resultado es 233168.

Breve explicación

Se basa en el uso de list comprehensions, una de las "joyas de la corona" de las características de Python.

Es una manera de generar listas de una manera concisa, compuesta por una expresión seguida de uno a más for y una condicionalidad al final

La lista [i for i in range(1000) if (i % 3 == 0 or i % 5==0)] se lee así: crear una lista con todos los elementos (expresión i) en el rango de 0 a 999 (for) cuyo resto de dividirlo por 3 o 5 sea 0 (if).

La función builtin sum() hace la sumatoria de esa lista y... listo. ;-)

Comentarios () | Link

Publicado el 9 de septiembre de 2009 |

The principle of successful freelancing, de Miles Burke, es el libro (digital) que estoy leyendo.

This book is intended as a guide to approaching the decision to be your own boss, effecting a smooth transition into a freelance career, and making it a success once you’re there. The book’s holistic approach ensures that it not only covers how to make your freelancing journey a financial success, but also how to do it without risking your health and sanity.

Ser tu propio jefe. No está mal para este mundo donde ese rol tiene connotación negativa.

Quienes quieran chusmear, acá tienen.

PDF - 4.2 MB
The principle of successful freelancing
by Miles Burke
Comentarios () | Link

Publicado el 10 de agosto de 2009 |

Administrar muchos sitios SPIP tiene sus bemoles. Yo administro, fácil, unos 30. Cuando hay que actualizar la versión (por cuestiones de funcionalidad, o bien, como la semana pasada, por cuestiones de seguridad), es un trabajo engorroso y aburrido hacer 30 veces lo mismo. Y el trabajo se vuelve exponencial si hay que actualizar los plugins para cada uno

Para evitar eso existe la Mutualización, que desde la versión 2 es realmente muy fácil.

El concepto es simple: un sólo núcleo de SPIP (/ecrire, /prive, /squelettes-dist, /plugins) se comparte para motorizar diferentes sitios, que tendrán los directorios con contenido propio (/squelettes, /config , /IMG, etc), separado del resto

Así, a la hora de actualizar SPIP sólo tenemos que actualizar una sola instalación, tarea que se vuelve trivial si además usamos svn.

Requisito

En general, la mutualización funciona sin muchos pormenores en un servidor LAMP, pero nuestro servidor de hospedaje debe permitir apuntar diferentes dominios a un mismo directorio publico.

Por ejemplo, mi sitio textosypretextos.com.ar apunta a /home/tin/public_html/spip y nqnwebs.com apunta también a /home/tin/public_html/spip.

Es una configuración simple, pero no todos los servicios de hosting lo permiten.

Puesta en marcha

Zip - 39.2 KB
plugin mutualisation

La "mutualización fácil" funciona a través de este plugin, pero que particularmente debe ser instalado en la raiz del sitio y no en la carpeta /plugins

Al descomprimirlo crea una nueva carpeta, /mutualisation, en la raiz de nuestro SPIP mutualizado

Además, incluye un archivo mes_options.php de ejemplo, pero yo te propongo este, que debe ser instalado en /config

  1. <?php
  2.     if (!defined("_ECRIRE_INC_VERSION")) return;
  3.     require _DIR_RACINE.'mutualisation/mutualiser.php';
  4.  
  5.     if (substr($site = $_SERVER['HTTP_HOST'],0,4)!='www.')
  6.         $site = 'www.' . $site;
  7.    
  8.     define ('_INSTALL_SERVER_DB', 'mysql');
  9.     define ('_INSTALL_HOST_DB', 'server');
  10.     define ('_INSTALL_USER_DB', 'user');  
  11.     define ('_INSTALL_PASS_DB', 'pass');
  12.     define ('_INSTALL_NAME_DB', 'db');
  13.    
  14.     /* Comentario la línea siguiente si utilizará la configuración de prefijos de tabla de más abajo */
  15.    
  16.     //define ('_INSTALL_TABLE_PREFIX', 'granja');
  17.    
  18.     /*
  19.     * Si el nombre del servidor es diferente al DNS ,
  20.     * a veces puede ser problemático y debe ser
  21.     * declarado aquí
  22.     */
  23.     // define ('_INSTALL_HOST_DB_LOCALNAME', 'nom_serveur');
  24.    
  25.     /*
  26.      *  Si el servidor de base de datos no es mysql, debe especificar
  27.      * # define ('_INSTALL_SERVER_DB', 'pg'); // mysql|pg|sqlite2|sqlite3
  28.      *
  29.      * /!\  En PG, es recomendable utilizar la opción creación de usuarios de SQL
  30.      */
  31.    
  32.     /*
  33.      * Crear automáticamente los usuarios SQL (pg|mysql)
  34.      *
  35.      * Esto permite
  36.      * - Tener un usuario root como titular de los derechos de
  37.      * Creación de bases de datos (el usuario con una fase obligatoria de
  38.      * Un nombre es PG - PG no se conecta, sin dar un nombre a BDD)
  39.      * - Para crear los usuarios automáticamente sql
  40.      * Con sólo los derechos de los directores
  41.      * De su base de datos que se creará
  42.      *
  43.      * A continuación, debe sustituir
  44.      *
  45.      * _INSTALL_(USER|PASS)_DB par _INSTALL_(USER|PASS)_DB_ROOT
  46.      *
  47.      * et ajouter dans demarrer_site l'option
  48.      * 'creer_user_base' => true
  49.      */
  50.     # define ('_INSTALL_USER_DB_ROOT', 'mon_root');
  51.     # define ('_INSTALL_PASS_DB_ROOT', '********');
  52.  
  53.     /*
  54.      * Crear las bases de dato via un ping a una URL (metodo AlternC)
  55.      *
  56.      * Il suffit de renseigner l'option url_creer_base, en lui passant les bons parametres :
  57.      * 'url_creer_base' => 'https://bureau.tld/admin/sql_doadd.php?username=USER&password=PASS&dbn='.prefixe_mutualisation($site)
  58.      */
  59.      
  60.      
  61.     /*
  62.      * Transformar en las paginas publicas las URL de imagenes
  63.      * /sites/mon_site/IMG/* -> /IMG/*
  64.      * /sites/mon_site/local/* -> /local/*
  65.      *
  66.      * - Necesita el mod_rewrite (reescritura de url) de apache
  67.      * - Sólo funciona con nombres de dominio mutualización
  68.      * ('http_host' : http://mi_sitio_mutualizado.com)
  69.      * (por lo tanto, no funciona con un subdirectorio - http://mi_sitio_mutualizado.com/misitio/)
  70.      *
  71.      * y añadir en la opción
  72.      * 'url_img_courtes' => true
  73.      *
  74.      * Es posible generar los  .htaccess
  75.      * automaticamente en /IMG y /local
  76.      * gracias a ?var_mode=creer_htaccess_img
  77.      *
  78.      */
  79.    
  80.     /*
  81.      * Indique aqui el nombre del sitio de administración de esta mutualización
  82.      * (o varios, separados por oma)
  83.      * (dpor ejemplo, 'scriibe.net' es el dominio de nivel maximo)
  84.      * para autorizar todos los sitios no definir la constante ;
  85.      * Si el propietario del sitio no se encuentra en los sites/ sino en la razi,
  86.      * escriba '' y agregue 'mutualisation' en $dossier_squelettes
  87.      */
  88.     define ('_SITES_ADMIN_MUTUALISATION', 'nqnwebs.com');
  89.    
  90.     demarrer_site($site,
  91.         array(
  92.             'creer_site' => true,        // Creer ou non le site s'il n'existe pas (defaut: false)
  93.             'creer_base' => false,        // Creer ou non la base de donnee si elle n'existe pas (false)
  94.             'creer_user_base' => false,  // Creer ou non un utilisateur pour la nouvelle base de donnee (false)
  95.             'mail' => '',                // Adresse mail pour recevoir un mail lors d'une creation de site mutualise ('')
  96.             'code' => 'miclave',  // Code d'activation ('ecureuil')
  97.             'table_prefix' => true,     // Definir automatiquement le prefixe de table (false) ... mettre true si tous les sites dans la meme base
  98.             'cookie_prefix' => true,     // Definir automatiquement le prefixe de cookie (false)
  99.             'repertoire' => 'sites',     // Nom du repertoire contenant les sites mutualises ('sites')
  100.             'url_img_courtes' => true,   // Utiliser la redirection des URL d'images courtes dans la partie publique (false)
  101.             # 'utiliser_panel' => false, // Utiliser une table externe pour recuperer des identifiants ... (code, user, pass) permettant a un utilisateur d'installer le site (false)
  102.             'url_creer_base' => ''       // Creer la base de donnees via une URL (methode AlternC)
  103.         )
  104.     );
  105.    
  106. ?>

Este mes_options.php (retocado por mi) permite que los sitios funcionen con o sin ’www’. También apunta a una única base de datos, donde se encargará de crear las tablas necesarias para cada sitio, diferenciadas por un prefijo.

Sólo resta crear la carpeta /sites en la raiz del sitio, donde irán nuestros diferentes sitios mutualizados.

Al acceder via web a un dominio de un sitio sobre esta mutualización, lo primero que aparecerá es una solicitud de contraseña, definida en array de configuración de mes_options.php (en el ejemplo, la contraseña es miclave)

Luego siguen dos etapas que nos crean la estructura de directorios para este sitio dentro de /sites y las tablas para este sitio en la base de datos compartida. Lo que sigue es el proceso de instalación estándar de SPIP.

¡Listo!

Hay que tener en cuenta que ahora los archivos para nuestro sitio estarán dentro de /sites/dominio/ y es allí donde deberán subirse los esqueletos y demás.

Mutualizar un sitio en producción

¿Qué pasa si el sitio que quiero mutualizar ya está funcionando sobre un spip propio? No problem.

Basta indicar (desde el panel, o solicitando al administrador del hosting) que nuestro dominio debe apuntar ahora a la carpeta donde tenemos el spip mutualizado (en mi ejemplo /home/tin/public_html/spip ), crear el directorio /sites/dominio (donde dominio es la url del sitio que queremos migrar) y mover alli las carpetas /config, /IMG, /squelettes, /local y /tmp (o crear una /tmp nueva) del SPIP en uso al nuevo directorio. [[Estas carpetas, bien podrian ser link simbólicos]. Magia!

Conclusión

Si administrás más de dos sitios spip sobre el mismo servidor, esta configuración te ahorrará trabajo. Avanti: ¡Todos para uno y uno para todos!

Comentarios () | Link

Publicado el 10 de junio de 2009 | Categoría/s: SPIP, jQuery

Si Twitter funciona con 140 caracteres los comentarios a nuestros artículos podrían tener 200, ¿no?

Hay una forma fácil de limitar el largo permitido en un campo de texto mediante jQuery, que vaya mostrando de manera interactiva la cantidad de caracteres restantes.

Por ejemplo, esto limita el tamaño de un comentario en SPIP.

<script type="text/javascript">
 $(document).ready(function () {  
     
     $("#texte").before("<div id='chars' style='float:right;font-weigth:strong;margin-right:5px;'></div>");
$(function(){
    $('#texte').keyup(function(){
        limitChars('texte', 200, 'chars');
    })
    });
 });  
 
function limitChars(textid, limit, infodiv) {
     var text = $('#'+textid).val();
     var textlength = text.length;
     if(textlength > limit) {
         $('#' + infodiv).html('No puede ingresar m&aacute;s de '+limit+' caracteres');
         $('#'+textid).val(text.substr(0,limit));
         return false;
     } else {
         $('#' + infodiv).html('Restan '+ (limit - textlength) +' caracteres.');
         return true;
     }
}
</script>

Intrucciones para SPIP

  1. Copiar el código
  2. Pegarlo luego de la baliza #INSERT_HEAD de tu cabecera
  3. (opcional) Cambiarle el límite (el número 200),

¡Listo!

Intrucciones generales

Es igual al anterior, sólo debes asegurarte que tiene en jquery en la página. Luego cambia el selector #texte por el que corresponda al campo que querés limitar.

Ejemplo

Mirá un ejemplo en La Voz de Lanzarote. (click en ’opinar’, en la barra posterior al artículo)

Comentarios () | Link

Publicado el 22 de mayo de 2009 | Categoría/s: SPIP, Linux

Como sabemos, el desarrollo de SPIP se realiza a través del sistema de control de versiones Subversion.

A través de Subversion, podemos obtener la ultima version de desarrollo considerada estable

Una de las ventajas de obtener SPIP a través de SVN, es que luego es muy fácil de actualizar.

Subversion asigna un numero de revision a cada cambio realizado al conjunto de archivos. Así, cuando los desarrolladores lo evaluan pertinente, en un determinado momento del desarrollo se empaqueta una nueva version "oficial"

Por ejemplo, la revisión 13982 es la version 2.08 de Spip.

Podemos obtener una revision específica con el parámetro -r. Por ejemplo, el siguiente comando obtendría una copia de SPIP 2.08

Genial, pero para para asegurarnos estar en una revision que se corresponda con una "version oficial" tenemos que saber el número de ID.

En el Trac de Spip se mantiene una tabla donde se especifica esta correspondencia para cada nueva version, pero a decir verdad, es un engorro tener que ingresar sólo para averiguar un número. Mejor hagámoslo automático!

Automatizando la obtención de un SPIP oficial por SVN

  1. #!/bin/bash
  2. #script para una instalacion de spip a traves de svn
  3. if [ $# -lt 1 ]
  4. then
  5. echo "Debe indicar al menos un parametro:"
  6. echo "$0 destino [rev]"
  7. echo
  8. exit 1
  9. fi
  10. if [ $# -eq 2 ]
  11. then
  12. REV=$2
  13. else
  14. echo "Averiguando revision de la ultima release..."
  15. REV=$(svn info svn://trac.rezo.net/spip/archivelist.txt | sed -n '9p' | cut -d' ' -f5)
  16. fi
  17. echo "Recuperando la version $REV de SPIP"
  18. svn checkout -r `echo $REV` svn://trac.rezo.net/spip/branches/spip-2.0/ $1 1>/dev/null
  19. echo "Configurando permisos..."
  20. cd $1
  21. chmod 657 IMG local tmp config
  22. echo "Lanzando el navegador para continuar la instalacion..."
  23. firefox http://localhost/~tin/$1/ecrire

Este script hace el trabajo aburrido por nosotros. Averigua cuál fue la última modificación de archivelist.txt que es un archivo que se modifica en cada nuevo lanzamiento de paquete. ¡Es el dato que nos hacía falta!

Una solución más genuina

Atención: Ya me hicieron caso. Ver actualización

Aunque el hacking (como este) es sano y divertido (y a veces también útil), la solución más genuina sería que los desarrolladores de SPIP mantengan un tag por cada nuevo empaquetamiento y uno que apunte siempre al último.

Por ejemplo, con el siguiente comando deberiamos obtener la version 2.05 (sin saber a qué numero de revision corresponde)

y con este otro obtener la última versión

Esto, por ahora, no está implementado, pero ya hice llegar mi propuesta a l’equipe de SPIP, y lo están discutiendo

Actualización

Gracias a Gilles el Dev Team adoptó mi propuesta y ahora existen Tags en el arbol SVN de spip. En particular,

siempre devolverá la versión estable de la rama 2 de Spip. ¡Bien!

Comentarios () | Link

Publicado el 24 de abril de 2009 | Categoría/s: Python

Para suscribirse a la lista de Pyar, una de las tareas (opcional, pero muy bienvenida) es enviar tu "Hola Mundo".

El 17 de enero de 2007 yo me suscribí y mandé el siguiente mensaje.

Estimada gente:

Cumpliendo solemnemente con el paso 2 de las indicaciones de suscripción a la lista, haciendo alarde de mi nerdismo, les dejo mi "hola mundo" (mi primero programa en python!). Se trata de humilde juego via consola que implemento en cada lenguaje en el que intento incursionar. Hete aqui:

Dicha sea la verdad: vengo con muchas ganas de aprender, y por ahora muy poco que aportar (pago solidaridad con sinceridad).

Decidir aprender un nuevo lenguaje es una gran decisión y creo que no me he equivocado con mi elección. Es que son mis primeros pinitos en la programación orientada a objetos, y aunque la facu exige Java (pero no enseña Java), creo que Python es una herramienta mucho mas versatil, accesible y apropiada para el nivel de software que tengo intención/necesidad de desarrollar.

Ahora la parte aburrida. Me llamo Martin, soy de Neuquen y estudio Ingenieria en computacion en Cordoba. Soy programador PHP (uno del montón, que bah) y en particular laburo con un CMS/framework llamado SPIP, que me da de comer y beber. Tengo un weblog, aunque no sobre cuestiones técnicas. www.textosypretextos.com.ar . Y lo más importante: soy hincha de boca.

saludos desde Neuquén a 32ºC. (quien dijo que en el sur hace frio?)

Martín

Mi "Hola Mundo" fue El Número.

Mi primer juego

El Número es un juego de lógica y habilidad mental. Consiste en encontrar el número escondido (generado aleatoriamente) a través de la información

que brinda la máquina en cada intento. El número escondido tiene 4 cifras no repetidas.

- Un digito ’bien’ significa que el hay un acierto en número y posición.

- Un digito ’regular’ significa que el digito existe en el número incógnita, pero no está en la posición correcta.

Por ejemplo si el numero incógnita fuese el 1234 y se arriesga el 2031 el resultado será de dos dígitos regulares (el 2 y el 1) y 1 dígito bien (el 3).

Descarga e instrucciones

Zip - 2 KB
El Número
Martín Gaitán - Enero de 2007

Sobre Linux podés copiar y pegar este listado de comandos en la consola:

Sobre Windows, asegurate tener instalado Python (cualquier versión < 3) y dale hacé doble click sobre elnumero.py

¡Que lo disfrutes!

Comentarios () | Link

Publicado el 22 de abril de 2009 | Categoría/s: Empresa

El miércoles 15 de abril de 2009 participé como disertante en las 3º Jornadas Abiertas de Ingeniería en Computación que reune a estudiantes, profesores y profesionales invitados para dar charlas técnicas o de interés para la comunidad de la carrera.

En la anterior edición, diserté sobre Control de Versiones con Subversion. En esta oportunidad pretendí un tema menos técnico pero de interés para un vasto sector de mis compañeros: cómo animarse a dar los primeros pasos profesionalmente sin ser empleado.

La charla se basó en una presentación de Sebastián Desimone, publicada bajo Creative Commons by-nc-ar que escuché en las 7mas Jornadas Regionales de Software Libre, realizadas en Córdoba, en 2007.

Como corresponde ética y legalmente, este trabajo está bajo la misma licencia.

opendocument presentation - 284.8 KB
Ser Freelance: Cómo trabajar en ojotas desde el living de tu casa
Martín Gaitán, abril de 2009

El documento ODP incluye notas y comentarios.

Slidecast

Lamentablemente, la calidad del audio es paupérrima.

Video

Comentarios () | Link

Publicado el 19 de abril de 2009 | Categoría/s: Linux

Radiolina es a mi jornada laboral lo que la Spika a la de mi abuelo, en aquellas mañanas de viñas mendocinas.

Es un simple script bash que se vale del mágico mplayer para reproducir las radios más importantes de argentina (y otras mejores que esas).

Para bajar y empezar a disfrutar Radiolina puedes ejecutar los siguientes comandos desde tu consola.

Como frecuentemente lo actualizo, te recomiendo que utilices SVN para bajar el programa. En vez de la primer linea (wget...) utilizar

texte - 3.2 KB
radiolina
script bash
Comentarios () | Link

Publicado el 31 de marzo de 2009 | Categoría/s: Linux

Un comando muy práctico para hacer backups de una base de datos MySQL es mysqldump.

Eso hace un backup completo de la base base_de_datos (incluyendo la creación de todas las tablas.

Para restaurar ese backup se hace directamente inyectando el sql al interprete de mysql.

Respaldar sólo algunas tablas de la base

A menudo tenemos multiples sistemas instalados sobre la misma base de datos, Si sólo queremos respaldar algunas tablas bastaría enumerar las tablas luego del nombre de la base de datos

Pero si estas tablas son muchas, es engorroso definirlas una por una. Por suerte, en general, un prefijo asocia todas las tablas de un sistema.

En ese caso, se puede usar el siguiente compando que respalda todas las tablas con un prefijo, En este ejemplo, las que comienzan con "spip_":

¡Que se aproveche!

Comentarios () | Link

Publicado el 26 de marzo de 2009 | Categoría/s: SPIP, jQuery

Lorem ipsum es el texto que se usa habitualmente en diseño gráfico en demostraciones de tipografías o de borradores de diseño para probar el diseño visual antes de insertar el texto final.

Cuando estamos desarrollando necesitamos ver cómo quedará el contenido, y debemos recurrir a un copy & paste de caulquier cosa que tengamos a mano.

La ventaja de un Lorem Ipsum sobre otros textos, es que este tiene una distribución del largo de las palabras bastante natural de las palabras lo que en textos largos queda mucho mejor que repetir un mismo fragmento una y otra vez.

Hay muchos generadores de texto Lorem Ipsum en internet, pero lo podemos hacer aún más fácil.

Un Lorem Ipsum automático

Con la ayuda del plugin jQuery Lorem Ipsum de Sander Korve hice un modelo (instalable como plugin) que permite insertar un texto del tamaño que queramos (por omisión, de 4 párrafos).

Podés bajarlo desde acá:

Zip - 32.5 KB
Lorem Ipsum Plugin
version 0.2

O por SVN:

svn checkout svn://zone.spip.org/spip-zone/_plugins_/_test_/loremipsum

Instalación

La instalación es igual que con la de cualquier plugin: descomprimirlo en la carpeta /plugins y activarlo.

Requiere además que los esqueletos tengan la baliza #INSERT_HEAD dentro del header, ya que allí se incluirá el javascript necesario para su funcionamiento.

Funcionamiento básico

La inclusión de un Lorem Ipsum se basa en el modelo lorem.html incluído con el plugin, por lo que hay dos maneras de llamarlo:

- En el cuerpo (texto, descripción, etc) de un artículo: <lorem>

- Predefinido en un esqueleto: #MODELE{lorem}

¡y listo!

Parámetros

El modelo acepta algunos parámetros para configurar la longitud del texto. Por ejemplo <lorem5> creará 5 párrafos.

Pero también se puede especificar más datos:

- type: permite especificar que tipo de resultado se quiere. Las opciones válidas son paragraphs, words o characters para párrafos, palabras o characteres. Así: creará un texto de 25 palabras. El valor por omisión es paragraphs.

- amount: Es lo mismo que el ID pasado al modelo: es lo mismo que . Por ejemplo

[(#MODELE{lorem}{amount=5})]

 [10].

- ptags: Permite agregar las etiquetas <code<>P<>/code> alrededor de cada párrafo. Los valores aceptados son true o false. Por omisión es true.

Una astucia

Una forma de mostrar contenido en un artículo (o cualquier elemento) sin tener que editar cada artículo sólo para poner <lorem> es invocar el modelo siempre y cuando no exista contenido en la baliza

Por ejemplo [11]:

<BOUCLE_article(ARTICLES){id_article}>
<h1 class="#EDIT{titre}">[(#TITRE|supprimer_numero)]</h1>
[<div class="#EDIT{chapo} chapo">(#CHAPO|image_reduire{740,400}|sinon{#MODELE{lorem}{amount=2}})</div>]
[<div class="#EDIT{texte} texte">(#TEXTE|image_reduire{740,400}|sinon{#MODELE{lorem}{amount=5}})</div>]
</BOUCLE_article>
Comentarios () | Link

Publicado el 21 de marzo de 2009 | Categoría/s: Linux

Efecto Tábano es el programa radial de mi amigo Fernando Barraza. Objetivamente, uno de los mejores programas radiales de Argentina.

Lamentablemente, desde que me vine a Córdoba tuve que conformarme con los fragmentos de entrevistas o especiales que Fernando subía a la web. Pero ahora que la Radio Calf-UNC está online, la alegría está completa.

El único problema que queda es que no todos los días tengo el tiempo de escucharlo de 16 a 18hs, Necesitaba grabarlo para escucharlo offline.

Bien podría haber hecho esto localmente pero como al sitio lo administro yo, dije... ¿por qué no grabar el programa directamente desde el servidor, y que cualquiera pueda bajarlo cuando quiera?

Aquí está mi podcaster automático para Efecto Tábano.

La idea

Todo se basa en el glorioso Mplayer, Mplayer es capaz de reproducir casi cualquier formato en streaming.

Por supuesto, no habrá sonido en esa "reproducción" sobre el servidor, pero lo que queremos es la info para grabarla.

En vez de enviarla a la placa de sonidos, la información cruda (el sonido WAV) lo enviamos a un archivo fifo desde donde lame obtendrá su fuente de datos para comprimir a MP3 on the fly.

Todo esto, claro, se ejecutará al horario del programa (y durante el tiempo que se indique, en este caso casi 2 horas) mediante una tarea cron

Instalando lo necesario

Como anticipé, hace falta mplayer y lame. Estos programas en general no se encuentran en el servidor (por ejemplo Dreamhost) así que hay que instalarlos. Como tampoco somos root (la gran mayoría de las veces) habrá que compilarlos desde las fuentes e instalarlos a nivel usuario.

primero crear, si no existiése, un directorio /bin donde irán nuestros programas.

mkdir bin
chmod 775 bin

Mplayer y mencoder

wget http://www3.mplayerhq.hu/MPlayer/releases/codecs/essential-20061022.tar.bz2
bunzip2 essential-20061022.tar.bz2
tar -xf essential-20061022.tar
mv essential-20061022 $HOME/lib
wget http://www3.mplayerhq.hu/MPlayer/releases/MPlayer-1.0rc1.tar.bz2
bunzip2 MPlayer-1.0rc1.tar.bz2
tar -xf MPlayer-1.0rc1.tar
cd MPlayer-1.0rc1
./configure --prefix=$HOME --codecsdir=$HOME/lib/essential-20061022
make
make install

LAME

wget http://nchc.dl.sourceforge.net/sourceforge/lame/lame-3.97.tar.gz
tar -zxvf lame-3.97.tar.gz
cd lame-3.97
./configure "--prefix=$HOME" "--enable-shared"
make
make install

Con eso ya tendríamos los programas necesarios en ./bin

El script podcaster-efectotabano.sh

#!/bin/sh
#
# recorder &#8212; wrapper to pull remote audio stream and convert contents
# to mp3

# Path and arguments to lame (mp3 encoder)
lame="$HOME/bin/lame -S -b 32 -m m"

# Path and arguments to mplayer (stream decoder)
mplayer="$HOME/bin/mplayer -quiet"

# Where to put the output files
odir="$HOME/efectotabano.com.ar/podcast"

showname="efectotabano-`date +%Y-%m-%d`"
duration="6660"
url="http://78.159.108.83:8690/"

fifo="${showname}_fifo"
ofile="$odir/${showname}.mp3"

#- end config ----------------------------------------------------

mkfifo $fifo
$lame $fifo $ofile &
$mplayer  -vc null -vo null -ao pcm:file=$fifo $url &

sleep 5
pids=`ps auxww | grep $fifo | awk '{print $duration}'`

sleep `echo ${duration} | bc`

kill $pids
rm $fifo

La primera parte permite configurar las rutas y parámetros de los programas a usar, así como el formato y destino del mp3 de salida.

En este caso los parámetros de lame indican que se grabará a 32kbps en mono.

Los mp3 de salida irán a este destino

Este script se ejecuta de lunes a viernes a las 12:09 hora del servidor (16:09 en argentina) y durante 6660 segundo, de modo que termina tentativamente junto con la finalización del programa.

Para configurar la tarea en cron ejecutar crontab -e. La tarea a agregar luce así:

09 12 * * 1-5 ~/bin/podcaster-efectotabano.sh

Eso es todo. Todos los días, de lunes a viernes tendré mi programa para descargar.

TO DO

Por supuesto, para que esto sea un podcast le falta la sindicación. Por lo tanto, habría que agregar la tarea de injectar información pertinente en una base de datos del cual generar el XML de sindicación.

Lo dejo para una segunda etapa. Me tengo que ir a escuchar o melhor programa do mundo.

Comentarios () | Link

Publicado el 15 de marzo de 2009 | Categoría/s: SPIP

Hace algunos días, en spip-es se planteó la duda de cómo controlar una cabecera diferente para algunas secciones en particular.

Xabi aportó la solución canónica, que es crear esqueletos ad-hoc para cada sección difente, valiendose de los esqueletos con forma rubrique-X.html (donde X es el ID de la sección).

Esto se describe en el apartado "Para ir más lejos" de la documentación.

En este caso particular, sólo se quería mostrar el logo en la cabecera para algunas secciones. El problema es que las secciones "diferentes" serían iguales entre sí, pero invocando distintos esqueletos. Esto contradice el principio DRY siempre deseable en todo desarrollo de software.

Una forma sencilla, pero a la vez parcial de resolver esto, es hacer un enlace duro entre los esqueletos que son iguales.

Por ejemplo, la sección 2, la 4 y la 6, tendrán el mismo esqueleto. Entonces creamos el rubrique-2.html y definimos rubrique-4.html y rubrique-6.html como enlaces duros al primero. Desde la línea de comandos:

$ ln rubrique-2.html rubrique-4.html
$ ln rubrique-2.html rubrique-6.html

Esta solución funciona, pero es sólo para servidores *nix y teniendo acceso a la consola (por SSH, por ejemplo). Además, hay que documentar bien qué se está haciendo, porque da lugar a errores cuando dos esqueletos "linkeados" intentan ser iguales.

Por suerte, hay varias otras maneras de resolver esto,

Filtros de condición o filtro match

Esta solución, es la que aportó Juan Pablo Portugau.

Lo que plantea, es usar la baliza #ID_RUBRIQUE (que obviamente, devuelve el número de la sección) con filtros de comparación. Es, hablando en geek, un simple if: Si es la sección 2, mostrar el logo. Sino, nada:

En código SPIP:

[(#ID_RUBRIQUE|=={2}|?{'#LOGO_RUBRIQUE',''})]

La limitación de esta solución se da cuando son múltiples los valores posibles. En nuestro caso, se debe mostrar el logo cuando se trata de la sección 2, de la 4 o de la 6. Juan Pablo propone anidar esta estructura.

[(#ID_RUBRIQUE|=={2}|?{'#LOGO_RUBRIQUE',[(#ID_RUBRIQUE|=={4}|?{'#LOGO_RUBRIQUE',[(#ID_RUBRIQUE|=={6}|?{'#LOGO_RUBRIQUE',''})]})]})]

Aunque es sintácticamente correcto, el código resultante es bastante inmantenible para un ser humano con cerebro normal y el monito parseador de SPIP queda agitado.

Para eso existe el filtro match, que se vale de las expresiones regulares para que hagamos nuestro festín.

Lo de arriba, puede reducirse a esto:

[(#ID_RUBRIQUE|match{^(2|4|6)$}|?{'#LOGO_RUBRIQUE',''})]

El filtro devuelve VERDADERO si el valor de #ID_RUBRIQUE empieza con 2, 4 o 6 y termina con nada. Que es, dicho de otra manera, lo que queremos.

Y aún más claro: bucles como filtros

Las expresiones regulares no son moco de pavo, diría mi abuela. Super potentes, pero poco escalables en la curva complejidad-comprensibilidad.

Hay una solución que es muy útil, sencilla pero no trivial: usar un bucle como filtro.

<BOUCLE_muestra_logo(RUBRIQUES){id_rubrique IN (1,2,4)}{id_rubrique}>
    #LOGO_RUBRIQUE
</BOUCLE_muestra_logo>

La explicación es de la lógica de conjuntos: los criterios de un bucle funcionan como condiciones AND. Deben cumplirse todas. En este caso, el primer criterio {id_rubrique IN (1,2,4)} exige que el id_rubrique sea 1, 2 o 4. En el segundo {id_rubrique}, se exige que sea el que viene en contexto. Por lo tanto, este bucle sólo funcionará (mostrará el logo) si el id_rubrique del contexto es 1, 2 o 4. En lógica, es una operación O exclusiva

Funciona, es más legible que cualquiera de las otras soluciones y además permite una clausula ELSE mucho más accesible, simplemente usando la estructura completa del bucle.

<BOUCLE_muestra_logo(RUBRIQUES){id_rubrique IN (1,2,4)}{id_rubrique}>
    #LOGO_RUBRIQUE
</BOUCLE_muestra_logo>
</B_muestra_logo>
   [código si no es 1, 2 o 4]
</B_muestra_logo>

Como siempre, espero sirva mi aporte.

Comentarios () | Link

Publicado el 12 de marzo de 2009 | Categoría/s: SPIP, jQuery

Muchas balizas SPIP generan código HTML como salida (las balizas #LOGO_* y los filtros |image_* son los más notorios) y frecuentemente necesitamos o queremos modificar este código autogenerado en nuestros esqueletos. Hay diversos filtros para ayudarnos a esto (inserer_attribut para insertar atributos y extraire_attribut para extraer el valor de uno generado) pero son particularmente largos y tediosos de tipear.

Más allá de eso, aunque uso estos filtros bastantes seguido para implementar cosas como galerías de imágenes, todavía pienso lo difícil que es escribir las palabras en francés sin equivocarme. Por suerte, un pequeño wrapper, puede suavizar este inconveniente.

Cuando se trata de acceder y modificar atributos de valores de atributos nada, en mi opinión, llega tan lejos como la API de atributos de jQuery. El método de jQuery attr por sí solo, permite leer y escribir el valor de cualquier atributo en cualquier nodo. Recibe uno o dos parámetros, el nombre del atributo, y opcionalmente, el valor.

Si sólo se especifica el nombre, entonces attr extrae y devuelve el valor de ese atributo. Si ambos parámetros son pasados al método, entonces attr modifica el objeto configurando el atributo en cuestión con el valor dado. Es jerga de Programación Orientada a Objetos, es un estilo de interfaz "polimórfica" — cuando un solo método tiene dos comportamientos complementarios que son distinguidos por el número y/o el tipo de los argumentos dados — que está por todos lados en jQuery y es una de las razones por las que este framework es tan conciso y productivo.

Considerandola como la mejor estructura para este tipo de interfaces que conozco (y también, que uso diariamente), decidí ue mi wrapper tendría que copiarlo. Así fue como el attr nació.

Como el método de jQuery, el filtro attr de SPIP toma uno o dos parámetros (y un objeto "implícito", pero lo ignoraremos por el momento). Si sólo pasamos uno, este lo remite como entrada a extraire_attribut para obtener el valor correspondiente. Si se lo llama con dos parámetros, entonces llama a inserer_attribut para modificar el objeto.

Como la idea, el código es razonablemente sencillo; la únca

Like the idea, the code is reasonably straightforward; quizas lo único un poco inusual el uso de func_get_args para obtener el conjunto de los argumentos pasados a la función llamada. Con ese array podermos usar count para saber cuantos argumentos se pasaron y decidir qué se debe hacer. Esto es más seguro que especificar y evaluar valores por defecto (FALSE o NULL, por ejemplo) porque algunos usuarios genuinamente podrían querer usar esos valores (supongamos que NULL signifique eliminar el atributo en una futura versión).

include_spip("inc/filtres");
 
/**
 * The `attr` function allows the user to get and set the attributes of an HTML tag.
 * It is intended to be used as a SPIP filter and depends on existing SPIP functionality.
 *
 * @param $tag
 *     The HTML code.
 * @param $name
 *     The name of the attribute.
 * @param $val...
 *     The new value for the attribute $nom. Optional.
 * @return
 *     If $val was given, the code for tag with $name=$val
 *     Otherwise, the value for the $name attribute in $tag.
 */

function attr($tag, $name){
        $args = func_get_args();
 
        if (count($args) > 2) {
                // SET
                return  inserer_attribut($tag, $name, $args[2]);
        } else {
                // GET
                return extraire_attribut($tag, $name);
        }
}

Simplemente copia el código en mes_fonctions.php (Mira "Agregar funciones propias" en la documentación sobre filtros de spip) y luego usa el filtro attr en tus esqueletos:

<a href="[(#FICHIER|attr{src})]" class="lightbox" title="#TITRE">
    [(#FICHIER|image_reduire{100,100}|attr{alt,#TITRE})]
</a>

Hay algunos cambios que se puede hacer a esta función: pasar $args directamente a inserer_attribut y extraire_attribut en vez de las variables individuales, agregar un parámetro $value=FALSE que sólo sirva para la claridad de la documentación , y el mencionado, eliminar atributos cuando se pase por ejemplo NULL como parámetro.

Una nota final: probablemente necesites correr PHP 5 para que esto funciones — la docu de func_get_args menciona la versión 5.3.0 — y el código de arriba fue modificado luego del último testeo, pero debería funcionar de cualquier manera.

Comentarios () | Link

Publicado el 10 de marzo de 2009 | Categoría/s: SPIP

Desde la versión 1.4, SPIP tiene una baliza calculada #INTRODUCTION que como su nombre indica, sirve para presentar una introducción al artículo completo.

En la documentación explica

#INTRODUCTION: [SPIP 1.4] si el artículo contiene una descripción, esta se utiliza aquí; si no, SPIP muestra los 600 primeros caracteres del inicio del artículo (del epígrafe, y luego del texto).

En la versión 1.92 se incorporó otro atajo para indicar el largo del texto mostrado (sobrecargando los 600 caracteres por omisión).

Así, #INTRODUCTION{150} mostrará la descripción, si existe, o los primeros 150 caracteres del epigrafe y/o el texto.

Pero con la versión 2 llegó por fin un control más profundo para los redactores. Es posible definir cual es el texto que se mostrará con #INTRODUCTION encerrandolo entre la etiquetas <intro>...</intro> en el cuerpo del artículo. [12]

Así podemos redactar nuestro artículo con la siguiente estructura:

Suponiendo que la baliza #INTRODUCTION se muestra en el esqueleto de seccion, allí se vería el texto

Esta oración es la introducción al articulo.

Y donde esté la baliza #TEXTE:

Esta oración es la introducción al articulo. Y acá sigue mi artículo completo.

¡Espero sirva!

Comentarios () | Link

Publicado el 9 de marzo de 2009 | Categoría/s: Python

Python es un lenguaje de programación interpretado, interactivo y multiplataforma, que con su facilidad y potencia hace las delicias de miles de programadores (y sin saberlo, también de los usuarios de los programas que estos desarrollan) alrededor del mundo.

La comunidad de Python Argentina es particularmente activa, solidaria y muy capaz técnicamente.

Esta charla fue dada por Facundo Batista, uno de los fundadores de PyAr, en las 7mas Jornada Regionales de Software Libre realizadas en Córdoba, Argentina, en 2007.

Aquí la presentación

Si te interesa saber un poco más, instala Python en tu computadora (en caso de que no esté ya instalado, claro) y lee el tutorial. Luego podés ver las recomendaciones de la comunidad para saber como seguir. Y por supuesto, ¡inscribite en la lista de correo!

Comentarios () | Link

Publicado el 7 de marzo de 2009 | Categoría/s: SPIP

Más por costumbre que utilidad solemos poner la antigüedad de un sitio web al pie de página, asociado al símbolo ©, o (cc) si usamos Creative Commons.

En el 2007 hicimos el sitio, y en este 2009 todavía muestra.

© 2007

¡Muy feo! Aunque no hayamos actualizado el sitio desde entonces, nuestro deber, como buenos mentirosos, es lograr que el visitante no se entere.

Así que podemos hacer que el año, o el período Año inicio - Año actual se actualice automáticamente.

Muy simple:

  1. &copy;
  2. <BOUCLE_yearbegin(ARTICLES){par date}{0,1}>
  3.    #SET{year, #DATE|annee} #GET{year}
  4. </BOUCLE_yearbegin>
  5. [(#GET{year}|=={#DATE|annee}|?{'' , - #DATE|annee} )]

Explicación

- Por convención, asumimos que el año de inicio del sitio es el año del primer artículo publicado, lo cual a mi me suena bastante lógico.
- Un bucle recupera esta información, la muestra y a la vez la guarda en una variable spip

#SET{year, #DATE|annee}

.
- Fuera del bucle comparamos el año ya mostrado con el actual, obtenida de la baliza #DATE sin contexto. Si son iguales, no se muestra nada más (para que no quede algo como © 2009 - 2009, que quedaría bien feo). Pero si son distintos, se muestra el año actual

El resultado, será

© 2007 - 2009

Y si la lógica o el reloj del servidor no fallan, en cada año nuevo se incrementará el año final del período sin que toquemos una línea de código.

Comentarios () | Link

Publicado el 27 de febrero de 2009 | Categoría/s: SPIP

Con SPIP 2.0, más precisamente en la revisión 13407 se agregaron algunos esqueletos al juego de esqueletos estándar.

Unos muy interesantes son los que permiten suscribirse a los comentarios.

squelettes-dist/rss_forum_article.html
squelettes-dist/rss_forum_breve.html
squelettes-dist/rss_forum_rubrique.html
squelettes-dist/rss_forum_syndic.html
squelettes-dist/rss_forum_thread.html

Por ejemplo, en el foro de un articulo, podriamos incluir el siguiente código, antes del bucle de comentarios de un artículo:

<a title="Sindicar los comentarios de este artículo" rel="alternate" href="[(#URL_PAGE{rss_forum_article}|parametre_url{id_article,#ID_ARTICLE})]">[<img width="16" height="16" alt="Seguir la vida del sitio" src="(#CHEMIN{feed.png})"/>] Suscribite a los comentarios de este artículo </a>

Un código análogo, llamando al esqueleto correspondiente y pasando el parámetro correcto, sirve para las breves, las secciones, los artículos sindicados y los hilos de discusión (en foros con comentarios anidados).

Lamentablemente, el RSS aun no es de uso masivo, pero es una buen incentivo ofrecerlo a quien lo desee.

Comentarios () | Link

Publicado el 26 de febrero de 2009 | Categoría/s: SPIP, jQuery

Uno pequeño truco con jQuery para mostrar u ocultar un bloque de información. Basado en una respuesta que dí en la lista Spip-es.

Comentarios () | Link

Publicado el 23 de febrero de 2009 | Categoría/s: SPIP, Web 2.0

Un screencast y algunos ejemplos de esta potente herramienta para manipular fuentes de información

Comentarios () | Link

Publicado el 23 de febrero de 2009 | Categoría/s: SPIP

Un juego de esqueletos con énfasis en la usabilidad y la accesibilidad

Comentarios () | Link

Publicado el 21 de febrero de 2009 | Categoría/s: SPIP, Desarrollo web

El trabajo de un desarrollador web cuesta mechones de cabello a la hora de enfrentar al inmundo, malnacido e infeliz Internet Explorer 6. Horas y nervios perdemos intentando que nuestros sitios se vean decentemente en este navegador, que pese a tener casi 8 años de anitgüedad, aun tiene cuota de mercado. ¡Es hora de decir basta! Tomemos las armas y declaremosle la guerra a Internet explorer.

¿Cuáles son nuestras armas?

Te propongo dos, faciles y potentes:

- El script modificado por mí de la campaña End 6!. Sólo debes copiar este código y pegarlo en el header de cada página de tu sitio, o sea entre <head> y </head>.

Si un visitante llega usando el vetusto y abominable navegador, verá un cartelito como este:

- El plugin para spip Cambia el navegador! que al instalarlo y activarlo, muestra un mensaje en la cabecera de todo el sitio público si el visitante usa IE6 (o más viejo!)

Zip - 89.4 KB
Cambia el Navegador!

Puedes bajar este plugin (multilingüe) desde aquí.

Basta de ’caldo gordo’

Como desarrolladores, tenemos cosas mejores que hacer que continuar garantizando la compatibilidad con lo arcaico, perdiendo el tiempo en torno a errores ajenos a nuestros desarrollos. Todo el tiempo dispensado allí debería ser concentrada en conquistar los nuevos horizontes que requieren nuestra atención como, por ejemplo, la web móvil.

Y en gran parte, esta situación es nuestra responsabilidad. Todos los que trabajamos en internet (y los que la usamos con pasión) conocemos y odiamos con fundamentos el Internet Explorer 6. Pero justamente nosotros, por tener razones, somos responsables de difundirlas tanto como podamos para educar tecnológicamente a los usuarios más inexperimentados.

¡A ganar la batalla final!

Comentarios () | Link

Publicado el 14 de febrero de 2009 | Categoría/s: Software Libre, SPIP

Una presentación que hice con mi amigo Pablo Martínez sobre Control de Versiones con Subversion

Comentarios () | Link

Publicado el 12 de febrero de 2009 | Categoría/s: SPIP

Con un poco de coca-cola en la sangre, retomé esta madrugada un proyectito que tenía: adjuntar fotos de un album de Picasa a un artículo.

Comentarios () | Link

Publicado el 18 de marzo de 2008 |

jQuery es la más elegante librería javascript de las muchas que circulan por la red. Aquí una breve introducción a sus características y potencialidades

Comentarios () | Link

Publicado el 12 de febrero de 2005 | Categoría/s: SPIP

Los esqueletos de http://www.aten.org.ar disponibles para la descarga

Comentarios () | Link

Publicado el 2 de septiembre de 2004 | Categoría/s: SPIP

SPIP (Sistema de Publicación para una Internet Participativa, de sus siglas en francés) es un software libre (GPL) en PHP y MySql con la estructura ideal para desarrollar sitios tipo revista en línea, pero también potencia y sencillez sufiente para lograr cualquier objetivo. Spip se comoporta como un espacio donde un grupo de colaboradores escribe textos de contenido y los/as lectores/as pueden comentarlos.

Spip es más un espacio activista, permite la generación de debates con su propio sistema de foros asociados a artículos secciones y noticias breves. Como ya hay cientos de este tipo de software muy buenos.. ¿por que es mejor SPIP?.

Comentarios () | Link



NqN/webs :: Desarrollo, Diseño y Hosting de Sitios Web por Martín Gaitán
Neuquén & Córdoba - Argentina - © Año 2004/2009 - Tel (+54) 0351 460·1524 - Celular: (+54) 351 153·308454
Contacto | Pagos | RSS | SPIP | Usa Firefox