Juanjo Conti: Algunos ejercicios del taller de escritura

Durante los últimos 4 años asistí al taller de escritura El brillo de la palabra en Piedras al 73, Santa Fe.

Hoy mi amigo Leandro me pidió que le pase algunos ejercicios, así que reuní algunos que me mandaron por mail los días que falté.

No le pedí permiso a la profe Marilina Renk, pero estoy seguro de que no va a tener problemas en que los difunda.

Relatar qué sucedió

"La media verde que perdió la niña fue la que desencadenó la tragedia".

Dilucidar lo ocurrido

Imaginar como lo hace el narrador testigo, qué ocurrió aquella mañana con aquella mujer (nótese que la pista clave es que se habla de un hecho en el cual interviene la policía):

"La mañana previa al hecho la vi desde mi balcón, sentada en el living de su casa, apoyado su codo derecho sobre la mesa y su mejilla sobre la mano, mirando a lo lejos tal vez pensando. Hacía mucho frío, sin embargo lucía una bella musculosa amarillo fuerte, tal vez por eso me quedó tan gravada la imagen contrastando con el color oscuro de la pared y la lámpara que enfocaba la mesa desde arriba. Así le fui contando a la policía, desenrollando la historia hacia atrás" (Silvia Bil: También grises)

Cinco palabras

Enunciado del 14 de julio de 2015

Contar la historia de un misterio no resuelto.

Enunciado del 5 de julio de 2015

"La historia que transcribo a continuación es la historia de un hombre que, de tanto pedirle peras al olmo, el olmo se las dio..."

Completar el relato.

Vida

Observa tu vida como un proceso en el tiempo, sostenido por cosas, acciones, personas...

Consigna: divide tu vida en tres grandes etapas, tomando como referencia el paso de al menos una década entre ellas (en tu caso no te sobran años, qué suerte!) y crea un inventario con los elementos (cosas, acciones, personas) que fueron imprescindibles para vos en esos años. Puedes hacerlo en forma de poesía, en prosa o simplemente realizando un listado de oraciones (como orientación, en clase, leímos Inventario de Borges). Al recordar, quédate con ese detalle diferente, con aquello singular que hizo tu vida singular, de esta manera tu inventario será único, desde lo personal y desde lo literario también.

Palimpsesto

Consigna: crea un palimpsesto con recortes de vida.

Incluye en el texto, situaciones, sensaciones, emociones. No debes crear una historia unívoca, sino una suma de fragmentos que hablen del ser humano o de ciertas realidades, lo que te interesese. Puedes incluir diálogos, relatos en primera o tercera persona e, incluso, en plural.

Que lo que se narre te tenga como protagonista o como observador. Puedes referirte a hechos generales o a escenas privadas e íntimas. Puedes decir o dejar en entredicho. La condición que debes respetar es la superposición de ideas, de imágenes. No te preocupes si al parecer, nada tiene un hilo conductor. Usa el punto y aparte, la separación por fragmentos y no olvides que el texto también se construye desde lo visual.

En lo posible, vuela, aunque con los pies sobre la tierra, dejándote llevar por las palabras que llegan a tu historia, formando un remolino que disuelve pasado, presente y futuro por construir.

Continuar el texto

Cuenta la historia que el "turco" ofrecía a la "batrona", un surtido de hilos, agujas, botones, cintas y elásticos, además de "jabún y beines", "linda buntilla" y otras mercaderías, todas "buenas y baratas". Una tarde, al turco se le abrió en medio de la calle la gastada valija que transportaba semejante comercio y, entonces, sucedió lo que relataré a continuación..."

En lo posible, les recomiendo que sigan el tono fonético que caracteriza al habla del famoso turco.

Moneda

Resolver la incógnita en un texto:

"Una moneda se ha echado a rodar, quién sabe a dónde irá a parar; rebota en los desniveles del piso y sigue cuesta abajo..., finalmente alguien da con ella, ¿quién es y qué hace con ésta en su poder?"

Elegir uno de los dos textos y continuarlo

"En un barrio del conurbano bonaerense todas las mujeres empiezan a abandonar a sus maridos, primero una, después otra y así todos los matrimonios de muchos años se deshacen y entonces..."

"Una joven mujer es compradora compulsiva en ferias americanas; ella se siente poseída por las personalidades de los antiguos dueños de la ropa usada que compra por muy poco dinero..."

Rumores sobre Pinocho

El cuento trata de lo siguiente: Gepetto, el viejo carpintero, deseaba que su última creación, "Pinocho", una bonita marioneta de madera, pudiera convertirse en un niño de verdad. El Hada Azul le concedió el deseo, no sin antes advertir a Pinocho que para ser un niño de verdad, debería demostrar que era generoso, obediente y sincero. Pepito Grillo le ayudaría en esta labor, él sería su conciencia. Los días siguientes, en vez de ir al colegio, Pinocho se verá envuelto en una serie de malas aventuras llenas de desobediencias y mentiras...

Imagina en un texto cuáles serían esas desobediencias.

Cerro Chico

Durante años, nada había turbado la paz de Cerro Chico, pero, desde hace 3 meses, la calma y la tranquilidad de nuestro pueblo parece un recuerdo perdido. Todo comenzó con la llegada de ese individuo al lugar.

¿De quién se trata? ¿Cómo es esa persona, qué hace? Narra tu versión de la historia.

Animales

Armar un texto en el que abunden verbos relacionados a los movimientos, acciones o sonidos que producen los animales. Por ejemplo: relinchar, escarbar, pastar. Pero, sin referirse a los mismos, si no a conductas humanas (Ej: la señora cacareó).

Pez

En un sueño, una noche de verano, te viste convertido en un pez que deseaban atrapar. ¿Cómo fue esa situación? Imagínala. ¿Pudiste escapar? ¿Experimentaste alguna sensación extraña? Escribe algo con ello.

Grupos

Escribir un texto que describa de alguna manera, uno de los siguientes grupos:

  • Los mochileros
  • Los artesanos
  • Los zapateros
  • Las peluqueras de barrio
  • Las enfermeras

Facundo Batista: Todo lo que siempre quisieron saber de la CDPedia y nunca se atrevieron a preguntar


¿Qué es?

La CDPedia es la Wikipedia Offline. O sea, la Wikipedia, lo más fiel posible a su formato y contenido original, pero armada (construida, compactada) de una manera que no se necesita nada de Internet para acceder a toda la info de la misma.

Se llama CDPedia porque la idea original era meterla en un CD. Hoy por hoy generamos cuatro imágenes en cada liberación de CDPedia: un CD, un DVD, y dos archivos comprimidos (uno mediano y otro grande) que se pueden poner en un pendrive o en cualquier disco rígido.

La CDPedia es multiplataforma: el mismo CD, DVD o archivo comprimido se puede usar en Linux, Windows, o Mac, sin necesitar nada instalado previamente por fuera de lo que cada sistema trae normalmente.

CDPedia


¿Cómo se usa? ¿Cómo se ve?

Para usarla, lo primero es descargarla. Pueden acceder a la página del proyecto y ahí encontrarán info acerca de las cuatro versiones que tenemos actualmente, con el detalle de cuantas páginas y cuantas imágenes tiene cada una. Para bajarlo, necesitan un cliente de Torrent; para Linux a mí me gusta mucho el Deluge, que también puede usarse en Windows y Mac; otro cliente recomendado para las tres plataformas es qBittorrent.

Si descargan la versión CD o DVD, lo primero que tienen que hacer es grabarlo a un disco virgen, para lo cual necesitan una grabadora y un software para grabar. Si usan Windows y no tienen ninguno instalado, les recomiendo InfraRecorder que es software libre y muy fácil de usar. Pongan el disco generado en el equipo y ejecuten la CDPedia.

Si descargan las versión tarball, directamente descompriman los archivos en el disco rígido. Entren a la carpeta descomprimida y ejecuten la CDPedia.

¿Cómo se ejecuta la CDPedia? Bueno, depende de cada sistema. En Windows con hacer doble click en cdpedia.exe, alcanza. En Linux o Mac, si tienen bien configurado el navegador de archivos, debería funcionar haciendo doble click en cdpedia.py, pero siempre pueden recurrir a abrir una terminal, ir hasta el directorio en cuestión, y hacer ./cdpedia.py.

En cualquier caso al ejecutar ese archivo se va a levantar el Server de CDPedia, y al mismo tiempo se abrirá un navegador apuntando a ese Server local. Luego, es sólo usarla, ya que se explora y utiliza de la misma manera que la Wikipedia Online (con la excepción obvia que la CDPedia es de lectura solamente: no permite editar el contenido como sí lo hace la Wikipedia).

(a este y otros screenshots, hagan click para verlos más grandes)

Cómo se ve la CDPedia

Una decisión estratégica de la CDPedia es tomar el HTML generado por los servers de Wikipedia y usarlos casi directamente (les recortamos unos headers, optimizamos algunas cositas). Exploramos en algún momento tomar la info de la base de datos directamente, pero no logramos generar una página web igual a la de Wikipedia online.

Y eso es una fortaleza de la CDPedia: por la manera en que armamos las páginas, la forma de ver y usar las páginas, de explorar y acceder a la información, es igual a la Wikipedia online, de manera que el usuario no tiene un costo cognitivo en pasar de la versión online a offline. Es más, también se puede considerar a la CDPedia como el paso previo de consumo de contenido a la Wikipedia: una persona se puede acostumbrar a explorar las páginas, leer, cruzar y criticar la información, etc, y recién cuando tiene todo armado va a la Wikipedia Online y al resto de Internet para completar su investigación.

Más allá de la página a nivel contenido, lo que sí modificamos mucho es la barra de la izquierda. No tiene la original de Wikipedia, porque no tiene sentido al ser todo offline, así que reemplazamos los botones y enlaces por otros: hay un botón para ver una página al azar, un campo de texto de búsqueda, el logo de CDPedia, el logo de PyAr, enlace a una página de ayuda, etc...

Algo que también modificamos bastante es como señalizamos los enlaces en la página misma, en el contenido de Wikipedia. Hay principalmente tres tipos, distinguibles en cómo decoramos el texto convertido en enlace:

  • Azul: un link normal, apunta a otra página de Wikipedia que se incluyó dentro de CDPedia.
  • Rojo, subrayado con guiones: un enlace a otra página de Wikipedia pero que no fue incluida en CDPedia por razones de espacio.
  • Azul, subrayado con guiones: un link que los sacaría de CDPedia, ya que apunta a recursos online (útiles solamente si tenés Internet, claramente).

Muestra de enlaces

Otra sección que modificamos es el pie de cada página: ponemos un enlace a la misma página pero online, en Wikipedia misma, por si el usuario necesita la información actualizada. También aquí incluimos el contenido original, ponemos algún disclaimer extra, mencionamos que CDPedia es un proyecto de Python Argentina (y apuntamos al tutorial de Python que está incluido en la CDPedia).

Cabe mencionar que la CDPedia funciona también en Modo Servidor. De esta manera, se puede instalar la CDPedia en el servidor de una escuela, y que todas las computadoras del establecimiento puedan usar la información desde allí. Así logramos el efecto deseado de que los chicos puedan tener acceso al contenido de Wikipedia sin realmente tener Internet, pero sin la complicación o el incordio de tener que instalar CDPedia en cada una de las computadoras. Acá hay más instrucciones para configurarla de este modo.


¿Qué contenido tiene?

El contenido de la CDPedia está fuertemente determinado por dos características intrínsecas del proyecto: la CDPedia es estática y fácilmente distribuible en un disco o pendrive.

Digo que la CDPedia es estática porque una vez armada, no se actualiza. Es por eso una especie de "fotografía de un momento de Wikipedia" que, por definición, siempre va a estar desactualizada.

Cuando se comienza a generar una nueva versión de la CDPedia, se baja la versión más actualizada de todo el contenido de Wikipedia y se empieza a procesar. Este procesamiento puede llevar varias semanas, incluso unos meses. Entonces, cuando se libera una nueva versión de CDPedia, no incluye todos los cambios que se generaron en Wikipedia misma desde que se empezó a procesar.

Es por esto que se trata de liberar una versión de CDPedia al menos una vez por año, para que contenga la información lo más actualizada posible.

Ejemplo de un artículo

También digo que la CDPedia se puede distribuir fácilmente: sólo hace falta quemar un CD o DVD, o incluso pasarse los archivos mediante un pendrive. En casi todas las versiones (menos la más grande), por una cuestión de formato, no entra todo el contenido de la Wikipedia. Por ejemplo, para la versión 0.8.3, tenemos lo siguiente:

  • CD (693 MB): 54 mil páginas y 5% de las imágenes
  • Tarball medio (3.6 GB): 400 mil páginas y 20% de las imágenes
  • DVD (4.3 GB): Todas las páginas y 8% de las imágenes
  • Tarball grande (8.7 GB): Todas las páginas y todas las imágenes

Entonces, a menos que estemos armando el tarball grande, es evidente que tenemos que decidir cuáles páginas e imágenes van a entrar, y cuáles van a quedar afuera.

Esa decisión se toma ordenando todas las páginas por un determinado puntaje (que explico abajo), y se eligen las primeras N páginas (para el ejemplo anterior, las primeras 54 mil para el CD, las primeras 400 mil para el tarball medio, etc). Esas páginas tienen a su vez imágenes, que naturalmente también quedan ordenadas por el puntaje de las páginas: se toma un primer porcentaje de imágenes que se incluyen al 100%, otro porcentaje de imágenes que se escalan al 75%, otro porcentaje de imágenes que se escalan al 50%, y el resto no se incluye.

Analizando las páginas

Como vieron, un tema clave en la selección es darle un puntaje a las páginas. Este puntaje está formado (hoy por hoy) en base a dos factores: levemente por el largo de la página (una página larga tiene más puntaje que una corta), y fuertemente por lo que llamamos "peishranc", que es la cantidad de otras páginas que enlazan a la que estamos evaluando. Entonces, si a una página se la menciona en otras mil páginas es mucho más importante que una página que casi no se la menciona en el resto de la Wikipedia.

Otro gran detalle en lo que es "contenido" es qué hacemos para mitigar el problema de la vandalización. O sea, cómo evitamos en lo posible incluir páginas que fueron vandalizadas. Cuando comienza el proceso de generar una nueva versión de la CDPedia, como les comentaba antes, bajamos todas las páginas de Wikipedia, ¡pero no siempre bajamos la última versión! Lo que hacemos es revisar cuándo fue modificada y por quién: si fue modificada por un usuario normal, perfecto; pero si fue modificada por un usuario anónimo (como sucede en la mayoría de las vandalizaciones) nos fijamos cuando fue modificada: si fue hace más de varios días, la incluimos (asumimos que la gente de Wikipedia ya tuvo tiempo de verificar el cambio), pero si es muy reciente evitamos la última versión de la página, y agarramos la versión anterior (y aplicamos nuevamente todos estos mismos controles).


¿Cómo surgió el proyecto?

Cuenta la leyenda que el proyecto arrancó en el sprint posterior al primer PyDay de Santa Fé, en Junio del 2006, con la idea base de poder distribuir la Wikipedia a aquellos lugares que no tenían o tienen acceso a Internet (en particular teníamos en mente a escuelas de frontera o de ciudades chicas, bibliotecas de barrio, centros culturales de pueblos pequeños, etc.).

El proyecto continuó, y aunque no siempre le pudimos dedicar tiempo, tampoco nos alejamos nunca demasiado. Las mejoras en el proyecto fueron muy por ráfagas. Quiero destacar que fuimos muchos los que colaboramos con el proyecto, a lo largo de los años, ¡casi 30 personas!

Se trabajó mucho en este proyecto durante los PyCamps (los dos en Los Cocos, el de Verónica, y el de La Falda), donde muchas personas le dedicaron un buen tiempo, y también se realizó bastante durante otras reuniones, especialmente durante el 2010 y 2011.

Trabajando en un PyCamp

A modo de ejemplo, dos sprints: uno fue en un incipiente hacklab, donde se experimentó mucho sobre el índice para las búsquedas, y también durante la fundación de Wikimedia Argentina, donde se presentó por primera vez el proyecto y se realizó un gran avance en la primera parte del procesamiento de datos.

En años más cercanos yo traté de involucrar colaboradores en algunos sprints efímeros que armé, con poca suerte. Lamentablemente en el último tiempo fui principalmente sólo yo el que empujó el proyecto (lo cual es una autocrítica, más que un autoreconocimiento).

Una gran característica de la CDPedia, indiscutiblemente el proyecto más grande y más largo de Python Argentina, es que siempre se mantuvo orientado a los mismos objetivos: tener una Wikipedia offline con fines sociales (distribuir en escuelas sin conexión a Internet, que el conocimiento sea libre, etcétera), que sea divertido de hacer (es decir, hacerlo en Python), y mantenerlo libre (no sólo el producto final, que recomendamos copiarlo y repartirlo, sino el código en sí).


¿Se logró cumplir el objetivo social?

Como decía arriba, uno de los objetivos de la CDPedia es difundir el conocimiento, lograr que gente que no tenga acceso a Internet igual pueda acceder a la información de la Wikipedia, que es tan valiosa. Siendo PyAr una comunidad relativamente pequeña, era difícil escalar a tener un impacto nacional en el común de la gente.

En su momento queríamos que se viralice persona a persona: que alguien la baje y haga un par de CDs y los reparta, que los que reciben cada CD hagan a su vez varias copias y las repartan a otras personas, a escuelas, bibliotecas de barrio, etc. Pero no tuvimos mucho éxito con esa movida.

Pero resulta que Martín Varsavsky se casó, y Jimmy Wales le regaló para el casamiento la posibilidad de que se distribuya una Wikipedia offline en Argentina. Preguntó cuáles habían, la CDPedia era la que mejor se ajustaba a lo que se necesitaba, y vino Jimmy a Buenos Aires, le mostramos la CDPedia, y luego hubo una reunión en Educ.ar para terminar de acordar esto (fueron Jimmy por Wikimedia, Enrique Chaparro por Wikimedia Argentina y Alecu por PyAr).

En gran parte porque Educ.ar quería meter la CDPedia en un disco de ellos (con carátula de ellos, algunas otras páginas, etc), se logró que dicha institución becara a dos chicos de PyAr, Diego Mascialino y Hernán Olivera, para trabajar part time en esto.

Así que agarraron la versión 0.6 que recién había salido (Alecu y yo nos habíamos apurado a cerrar muchos detalles para tener algo funcionando presentable a Jimmy Wales), y entraron a darle. Esto le dio bastante impulso al desarrollo del proyecto, sumado a que también aporté regularmente al proyecto, y a que luego de que se terminara la beca Diego siguió trabajando en CDPedia, y que se sumó como "laburante regular" Santiago Piccinini.

Con todo este trabajo, y un nuevo empujón en el PyCamp del 2011, pudimos terminar de cerrar la versión 0.7, que se entregó a Educ.ar y se distribuyó a todas las escuelas del pais.

Sin embargo el mayor hito a nivel de distribución masiva de la CDPedia es que en algún momento fue incluida en las notebooks que el Estado argentino distribuye a los chicos de escuelas de todo el país como parte del programa Conectar Igualdad. Y también se la muestran a alumnos y docentes en los talleres que hacen como parte del programa.


¿Se puede espiar abajo del capot?

¿Cómo se arma la CDPedia? ¿Cómo se logra cumplir todo lo que expliqué arriba?

Es bastante sencillo: hay que bajar el código con git desde la página del proyecto en github, y luego correr un script que hace todo solo: el cdpetron.

Este script tiene bastantes opciones (especialmente para no repetir partes del proceso: que no vuelva a listar todas las páginas, que no vuelva a bajarlas, que no limpie todo antes de comenzar, etc), pero lo básico es que se le especifica de dónde tomar el código, donde bajar y dejar páginas e imágenes, y en qué idioma trabajar.

Incluso hay una manera de correrlo en modo test, para que haga solo una parte del trabajo y poder arrancar pronto a probar cosas, ideal para mezclarlo con la opción de generar una sola de las versiones:

    $ utilities/cdpetron.py --test-mode --image-type=beta . /tmp/dumpcdpedia es

El comando anterior tarda relativamente poco (menos de cinco minutos en una máquina normal y con buena conexión a Internet) y nos deja todo el proceso realizado, pero con pocas páginas.

Ver lo que obtuvimos es sencillo, porque más allá de generarnos el tarball o el .iso correspondiente, podemos probar la CDPedia directamente del directorio donde realizamos el proceso, haciendo...

    ./cdpedia.py

...lo cual levantará el server y nos abrirá el browser, tal cual si lo hiciéramos de la versión final (pero con la ventaja que podemos pararlo, cambiar el código para probar el algo, levantarlo de nuevo, ver los resultados, etc.)

¿Y cómo es el proceso que realiza? Bueno, la estructura interna (y el proceso para obtenerla) de la CDPedia está muy influida por la necesidad de optimizar al máximo la compresión y el acceso a la información, de manera de poder meter en cada formato (CD, etc...) la mayor cantidad posible de artículos e imágenes.

Podemos delinear el proceso que se realiza en en el siguiente gráfico:

Proceso de la CDPedia

El primer paso es bajar de la Wikipedia misma todas las páginas (lo que realmente tiene dos sub-pasos, un listado general de todas las páginas que nos interesan, y luego efectivamente bajarlas). Esas páginas son pasadas por diferentes preprocesadores que hacen distintos trabajos. Algunas las filtran y eliminan páginas que no queremos, otras les asignan puntajes, otras las modifican mejorándolas para nuestro objetivo, otras extraen información que va a ser útil luego.

Al final de ese preprocesamiento tenemos dos grandes resultados intermedios: los HTMLs "útiles", más un montón de metadata. Aquí se abren tres grandes ramas de trabajo.

La primera es el manejo de las imágenes. Se buscan los enlaces en las páginas, se descargan todas las imágenes necesarias (que pueden no ser todas, dependiendo de la versión generada), se reducen las que corresponden (algunas se incluyen al 75% o 50% de su tamaño) y finalmente se arman los llamados "bloques de imágenes".

Por otro lado, con los resultados intermedios se generan los "bloques de artículos".

Y finalmente, se procesan todos los títulos de las páginas más algo de metadata y se hace pasar por un complejo algoritmo matemático que nos pre-arma la información para generar los "bloques del índice".

A esta altura tengo que explicar qué son estos "bloques" de imágenes, artículos o índice. Es una estructura no demasiado compleja pero muy bien pensada para el objetivo de la CDPedia que es funcionar sin usar demasiada memoria y poco espacio en disco. Básicamente tenemos bloques de información comprimidos de forma independiente: es un equilibrio entre comprimir todo por separado, o comprimir todo junto; logramos mejor ratio de compresión que comprimiendo la info por separada, y no tenemos que descomprimir algo demasiado grande al no estar todo junto. Para decidir qué bloque consultar hay un hasheo y selección, y luego dentro de cada bloque hay un índice binario de contenidos, pero no mucho más.

Finalmente, con estos bloques, más algunos recursos estáticos (imágenes, CSSs, algo de JSs, el tutorial de Python comprimido, etc.), más el código de Python propiamente dicho para servir la CDPedia, se arman los tarballs o .ISOs.


¿En qué situación está el proyecto actualmente?

El proyecto avanza, pero lento.

Hay varios bugs abiertos, incluso algunos que son críticos porque se muestran un par de cosas feas luego de un cambio de formato de las páginas de Wikipedia, pero yo personalmente no estoy haciendo foco ahí, sino que estoy empujando un par de cambios más grandes.

Uno de ellos es lograr la internacionalización de la CDPedia. Cuando esté terminado, se van a poder crear CDPedias no sólo a partir de la Wikipedia en español, sino también de la Wikipedia en otros idiomas: portugués, aymara, guaraní, alemán, ruso, etc...

El otro cambio es más bien la construcción de una infrastructura en particular. Mi idea es tener una generación continuas de CDPedias, que se arme la CDPedia en español, y automáticamente luego se arme la de otro idioma, y otro, y otro, y otro, y luego de varios meses, vuelva a arrancar con la de español.

Trabajando

Pero, como decía, hay mil cosas para hacer.

Unos chicos en un PyCamp hicieron una app para Android que, luego de copiar los datos a mano, correría la CDPedia en cualquier teléfono o tablet (yo traté recientemente de usarlo y tuve unos problemas y no lo pude hacer andar del todo).

Otro detalle que necesita trabajo es que el código en sí está bastante feo... mezcla inglés y castellano, no cumple PEP 8 ni PEP 257, tiene poco y nada de pruebas de unidad, etc.

Si tienen ganas de participar de cualquier manera, lo principal es que se pongan en contacto con el grupo en general, a través de la lista de correo o del foro asociado (son espejo uno del otro, usen el
que sientan más cómodo). Lo mismo si desean hacer cualquier consulta, o ponerse en contacto para cualquier inquietud.

CDPedia necesita amor. Programadores con ganas de trabajar y aprender, tiempo de programador para continuar llevando este proyecto tan interesante y valioso por buen camino.

Juanjo Conti: No se puede confiar en las capturas de pantallas de páginas web

Hoy circuló una captura de pantalla de un artículo de la web del Clarín en el que un redactor se olvidó de borrar un comentario de su editor:

clarin1.thumbnail.png

El copete en las redes sociales era:

Chicos de Clarín, se les escapó borrar el párrafo que explicaba cómo nos tenían que chamuyar.

Algunos aclaraban que mostraban una captura de pantalla antes de que "lo borren".

Hoy, ya tarde a la noche, entro a la dirección origianl y el texto sigue ahí. Increible. Si no estuviera, el lado conspiracionista de mi cerebro me habría hecho pensar que todo fue una puesta en escena. Repito, en este caso no fue, pero podría haberse hecho muy fácilmente.

Solo basta entrar a la web en cuestión usando un navegador moderno como Firefox o Chrome. En Chrome vamos a Más herramientas/Herramientas para desarrolladores:

clarin2.thumbnail.png

Editamos algúno de los párrafos:

clarin3.thumbnail.png
clarin4.thumbnail.png

Y listo, ya tenemos una captura de pantalla con la cual sembrar confusión o invalidar un medio:

clarin5.thumbnail.png

Por lo tanto, si bien este no fue el caso (de eso estoy seguro yo porque entré al sitio y seguía con el comentario, pero vos no podés estar seguro si entrás y ya no está), nunca confien en capturas de pantallas de páginas web.

Juanjo Conti: Generador de sopas de letras

Desde hace un par de meses vengo usando un script en Python para generar sopas de letras.

Cuando busqué qué herramienta usar, encontré wordsearch.py de Richard Boulton (entre otras opciones) qué básicamente hacía lo que quería: tomar una lista de palabras y generar una sopa de letras lista para imprimir.

Desde entonces le hice algunas mejoras:

  • Mejor disposición de la grila en el pdf generado
  • Encabezado y pie configurables
  • Permite palabras con espacios (usando comillas). Ejemplo:
python wordsearch.py hard word1 "word with spaces" word3
  • Permite caracteres no ascii en las palabras (por ejemplo ñ, á, é, í, ó, ú)

Ejemplo del pdf que genera el script: https://github.com/jjconti/sopa-de-letras/blob/master/output_example.pdf

Mi versión se puede instalar desde: https://github.com/jjconti/sopa-de-letras/

PD para no programadores: si querés bajarlo y no sabés como hacerlo con el anterior link, escribime y te ayudo!

Gonzalo Martinez: Erlang - Sintaxis en funciones

Que sucede con el if?
El condicional if  actúa como las guardas y comparte con ellas la sintaxis pero fuera de la clausula cabecera  de la funcion. Las clausulas if son llamada Patrones de Guardas. Los IFs en Erlang son diferentes de los ifs  que encuentras en la mayoria de los lenguajes. Cuando entrás al pais de Erlang deberías dejar en la puerta todo lo que sabes acerca de los ifs

Para ver que tan similar a las guardas es la expresión if, mira lo siguiente.

-module(what_the_if).
-export([heh_fine/0]).

heh_fine()->
    if 1 =:= 1 ->
        works
    end,
    if 1=:= 2; 1 =:= 1->
        works
    end,
    if 1 =:= 2, 1=:= 1 ->
        fails
    end.


guarda esto como what_the_if.erl y probemos.

1> c(what_the_if).
./what_the_if.erl:12: Warning: no clause will ever match
./what_the_if.erl:12: Warning: the guard for this clause evaluates to 'false' {ok,what_the_if}
2> what_the_if:heh_fine().
** exception error: no true branch found when evaluating an if expression
        in function what_the_if:heh_fine/0


Bueno, el compilador nos informa que hay un error en la linea 12 que informa que esa linea siempre se evalua como falso. Recuerda, que en Erlang, todo debe retornar algo y la expresión if no es una excepción a la regla. De esta manera cuando Erlang no puede encontrar una manera de tener una guarda exitosa, este se rompe: no puede no devolver algo. Así que necesitamos agregar una rama que capture todo haciendo que siempre sea exitoso de cualquier manera.

En cualquier lenguaje esto es llamado un "else". En Erlang usamos "true" (esto explica por que la VM lanza el error "no hay rama verdadera")

oh_god(N) ->
    if N =:= 2 -> might_succeed;
        true -> always_does
    end.

Y ahora si nosotros testeamos esta función (la parte anterior seguirá lanzando esos warnings)

3> c(what_the_if).
./what_the_if.erl:12: Warning: no clause will ever match
./what_the_if.erl:12: Warning: the guard for this clause evaluates to 'false' {ok,what_the_if}
4> what_the_if:oh_god(2).
might_succeed
5> what_the_if:oh_god(3).
always_does


Seguramente serás alguno de los programadores que se sorprende por el uso de atomo true en vez de else para el control de flujo, despues de todo es más familiar. Como dice Richard O'Keefe básicamente usar el true es capturar todas las otras posibilidades y estos ejemplos que dá él mismo deberián solucionar nuestra confusión.

                          by
if X > Y -> a() if X > Y -> a()
; true -> b() ; X =< Y -> b()
end end

if X > Y -> a() if X > Y -> a()
; X < Y -> b() ; X < Y -> b()
; true -> c() ; X ==Y -> c()
end end 
 

Las ramas "else" o "true" deberián ser evitadas ambas. Los ifs son usualmente más fácil de leer cuando cubres todas los extremos lógicos que cuando confias eso a una clausula "captura-todo".

En caso ... de

Si la expresión if es como una guarda una expresión case ... of  es como la cabecera de la función completa: puede tener una compleja coincidencia de patrones para cada argumento, y puedes tener guardas encima de él.

Probablemente estás familiarizado con la sintaxis, no necesitaremos demasiados ejemplos. Para esto, escribiremos una función para agregar elementos a un conjunto (una colección de valores únicos) que podremos representar como una lista desordenada. Esta posiblemente sea la peor implementación en términos de eficiencia. Aquí la sintaxis.

insert(X, []) -&gt
    [X];
insert(X,Set) -&gt
    case lists:member(X,Set) of
        true -&gt Set;
        false -&gt [X|Set]
    end.

Si enviamos una lista vacia y un término x para ser agregado, entonces nos retornará una lista solo conteniendo el valor x. De otra manera la función lists:member/2 verifica si un elemento es parte de un lista y retorna true si existe o false si no lo está. En caso de que ya exista el elemento en el conjunto entonces no lo modificamos. Sino, agregamos x al conjunto como el primer elemento de la lista.

En este caso la coincidencia de patrones, fue realmente simple. Aquí una más compleja,

beach(Temperature) -&gt
    case Temperature of
        {celsius, N} when &gt= 20, N =&lt 45 -&gt
            'favorable';
        {kelvin, N} when N &gt= 293, N =&lt 318 -&gt
            'scientifically favorable';
        {fahrenheit, N} when N &gt= 68, N =&lt 113 -&gt
            'favorable in the US';
        _ -&gt
            'avoid beach'
    end.

Aquí la respuesta a "es el momento adecuado para ir a la playa?", dadas 3 temperaturas diferentes: grados Celsius, Kelvin, Fahrenheit. La coincidencia de patrones y guardas son combinadas en orden para devolver una respuesta satisfactoria a todos los usos. Como apuntamos anteriormente, las expresiones "case ... of" son la misma cosa que varias cabeceras de funciones con guardas. De hecho podriamos reescribir nuestro código de la siguiente manera.

beachf({celsius, N}) when N &gt= 20, N =&lt 45 -&gt
    'favorable';
...
beachf(_) -&gt
    'avoid beach'.

Esto genera la siguiente pregunta. Cuando deberiamos usar "if", "case ... of" o funciones para hacer expresiones condicionales.

Que utilizar?

Que utilizar es una pregunta dificil. La diferencia entre llamadas a funciones y 'case ... of' son minimas: de hecho son representadas de la misma manera a bajo nivel. y usar una o la otra tiene el mismo costo en términos de rendimiento. Una diferencia entre ambas es cuando más de un argumento necesita ser evaluado "function(A,B) -&gt ... end." puede tener guardas y valores que coincidan con A y B pero una expresión case necesitaría ser formulada de la siguiente manera:

case {A,B} of
    Pattern Guards -> ...
end.

Esta forma es raramente vista, y puede sorprender un poco al lector. En situaciones similares llamar a una función debería ser más apropiado. De igual manera la función insert/2 previamente escrita, es podría decirse la manera más limpia en lugar de tener una llamada a función para continua con la manejar la simple clausula true o false.

La otra pregunta es para que usar if, dado que cases y functions son los suficientemente flexibles para incluso abarcar if a través de guardas? Lo racional detrás del if es simple. Este fue agregado para que tener en el lenguaje una manera corta de tener guardas sin necesidad de escribir toda la coincidencia de patrones cuando no es necesaria.

[0] http://learnyousomeerlang.com/syntax-in-functions

Facundo Batista: Escapándose


El jueves pasado jugué a mi primer "juego de escape". Fuimos a Eureka con mi amiga Silvana y dos compañeras de trabajo de ella.

Un juego de escape es como un juego de aventura gráfica, pero de forma reducida y en la vida real. En Eureka lo explican así:

Estas encerrado con tu equipo en una habitación y tenés 60 minutos para escapar. La única forma de lograrlo es ir develando pista por pista y avanzar hasta encontrar la llave que les permitirá abrir la puerta y salir de la habitación. Es una carrera contra el tiempo y el momento perfecto para dejar fluir tus habilidades como detective.

Nosotros jugamos a Enigma. Básicamente entramos a una habitación, y la persona del lugar nos dio algunas reglas básicas: tienen una hora, lo que se abre se abre sin esfuerzo (no hay que sacudirlo hasta que se abra, digamos, sino que hay que resolver algo que lo abre), hay una puerta que va a permanecer cerrada, etc.

Luego se fue, cerró la puerta por donde entramos, y empezó el juego. Una tele nos dió una explicación de escenario (o sea, nos puso en el contexto de lo que había que resolver... me hizo acordar mucho a los videos del japonés en Lost), y luego empezó a contar el tiempo.

La habitación era grande, y había varios elementos interesantes (tranquilos, no les voy a spoilear el juego, esto es lo que uno ve al entrar): una máquina con botones, perillas e interruptores, una mochila colgada, una computadora que decía "entren el código de los misiles (tienen 10 intentos)", un mapamundi con leds marcando algunas ciudades, un tablero con tres relojes, un par de mesas y sillas, y algunas cosas más (algunas importantes, otras no).

Y desde ahí para adelante es como jugar una aventura gráfica: hay que explorar, entender qué tenés que hacer, vas encontrando cosas, vas tratando de descifrar donde se usa lo que encontraste (si se usa), algunas cosas salen fáciles, a veces te trabás, no sabés como seguir y vas repasando lo que tenés y como puede servir, etc.

Estuvo muy bueno.

Marcos Dione: ayrton-0.7

Another long-ish cycle (1.5 months, more or less). That's what two weeks of vacation do to the project.

This time I fixed executing things in, and handling the standard streams between the ayrton script and, the remote(), so now we can run complex programs like vi and mc. The ChangeLog:

  • Send data to/from the remote via another ssh channel, which is more stable than using stdin.
  • Stabilized a lot all tests, specially those using a mocked stdout for getting test validation.
  • A lot of tests have been moved to their own scripts in ayrton/tests/scripts, which also work as (very minimal) examples of what's working.
  • Use flake8 to check the code.
  • Move remote() to its own source.
  • API change: if a str or bytes object is passed in _in, then it's the name of a file where to read stdin. If it's an int, then it's considered a file descriptor. This makes the API consistent to _out and _err handling.
  • More error handling.
  • Fixed errors with global variables handling.
  • argv is handled at the last time possible, allowing it being passed from test invocation.
  • shift complains on negative values.
  • Lazy pprint(), so debug statements do not do useless work.
  • stdin/out/err handling in remote() is done by a single thread.
  • Modify a lot the local terminal when in remote() so, among other things, we have no local echo.
  • Properly pass the terminal type and size to the remote. These last three features allow programs like vi be run in the remote.
  • Paved the road to make remote()s more like Command()s.

Get it on github or pypi!


python ayrton

Marcos Dione: implementing-a-ssh-client-in-python

One of ayrton's features is the remote execution of code and programs via ssh. For this I initially used paramiko, which is a complete reimplementation of the ssh protocol in pure Python. It manages to connect, authenticate and create channels and port forwardings with any recent ssh server, and is quite easy:

import paramiko

c= paramiko.SSHClient ()
c.connect (...)

# get_pty=True so we emulate a tty and programs like vi and mc work
i, o, e= c.execute_command (command, get_pty=True)

So far so good, but the interface is those 3 objects, i, o and e, that represent the remote command's stdin, stdout and stderr. If one wants to fully implement a client, one needs to copy everything from the local process' standard streams to those.

For this, the most brute force approach is to create a thread for each pair of streams[1]:

class CopyThread (Thread):
    def __init__ (self, src, dst):
        super ().__init__ ()
        self.src= src
        self.dst= dst

    def run (self):
        while True:
            data= self.src.read (1024)
            if len (data)==0:
                break
            else:
                self.dst.write (data)

        self.close ()

    def close (self):
        self.src.close ()
        self.dst.close ()

This for some reason does not work out of the bat. When I implemented it in ayrton, what I got was that I didn't get anything from stdout or stderr until the remote code was finished. I tiptoed a little around the problem, but at the end I took cue from one of paramiko's examples and implemented a single copy loop with select():

class InteractiveThread (Thread):
    def __init__ (self, pairs):
        super ().__init__ ()
        self.pairs= pairs
        self.copy_to= dict (pairs)
        self.finished= os.pipe ()

    def run (self):
        while True:
            wait_for= list (self.copy_to.keys ())
            wait_for.append (self.finished[0])
            r, w, e= select (wait_for, [], [])

            if self.finished[0] in r:
                self.self.finished[0].close ()
                break

            for i in r:
                o= self.copy_to[i]
                data= i.read (1024)
                if len (data)==0:
                    # do not try to read any more from this file
                    del self.copy_to[i]
                else:
                    o.write (data)

        self.close ()


    def close (self):
        for k, v in self.pairs:
            for f in (k, v):
                 f.close ()

        self.finished[1].close ()


t= InteractiveThread (( (0, i), (o, 1), (e, 2) ))
t.start ()
[...]
t.close ()

The extra pipe, finished, is there to make sure we don't wait forever for stdin to finish.

This completely solves the problem of handling the streams, but that's not the only problem. The next step is to handle the fact that when we do some input via stdin, we see it twice. This is because both the local and the remote terminals are echoing what we type, so we just need to disable the local echoing. In fact, ssh does quite more than that:

class InteractiveThread (Thread):
    def __init__ (self, pairs):
        super ().__init__ ()

        [...]

        self.orig_terminfo= tcgetattr (pairs[0][0])
        # input, output, control, local, speeds, special chars
        iflag, oflag, cflag, lflag, ispeed, ospeed, cc= self.orig_terminfo

        # turn on:
        # Ignore framing errors and parity errors
        iflag|= IGNPAR
        # turn off:
        # Strip off eighth bit
        # Translate NL to CR on input
        # Ignore carriage return on input
        # XON/XOFF flow control on output
        # (XSI) Typing any character will restart stopped output. NOTE: not needed?
        # XON/XOFF flow control on input
        iflag&= ~( ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF )

        # turn off:
        # When any of the characters INTR, QUIT, SUSP, or DSUSP are received, generate the corresponding signal
        # canonical mode
        # Echo input characters (finally)
        # NOTE: why these three? they only work with ICANON and we're disabling it
        # If ICANON is also set, the ERASE character erases the preceding input character, and WERASE erases the preceding word
        # If ICANON is also set, the KILL character erases the current line
        # If ICANON is also set, echo the NL character even if ECHO is not set
        # implementation-defined input processing
        lflag&= ~( ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL | IEXTEN )

        # turn off:
        # implementation-defined output processing
        oflag&= ~OPOST

        # NOTE: whatever
        # Minimum number of characters for noncanonical read
        cc[VMIN]= 1
        # Timeout in deciseconds for noncanonical read
        cc[VTIME]= 0

        tcsetattr(self.pairs[0][0], TCSADRAIN, [ iflag, oflag, cflag, lflag,
                                                 ispeed, ospeed, cc ])


    def close (self):
        # reset term settings
        tcsetattr (self.pairs[0][0], TCSADRAIN, self.orig_terminfo)

        [...]

I won't pretend I understand all of that. Checking the file's history, I'm tempted to bet that neither the openssh developers do. I would even bet that it was taken from a telnet or rsh implementation or something. This is the kind of things I meant when I wrote my previous post about implementing these complex pieces of software as a library with a public API and a shallow frontend in the form of a program. At least the guys from openssh say that they're going in that direction. That's wonderful news.

Almost there. The last stone in the way is the terminal emulation. As is, SSHClient.execute_command() tells the other end that we're running in a 80x25 VT100 terminal. Unluckily the API does not allow us to set it by ourselves, but SSHClient.execute_command() is a very simple method that we can rewrite:

channel= c.get_transport ().open_session ()
term= shutil.get_terminal_size ()
channel.get_pty (os.environ['TERM'], term.columns, term.lines)

Reacting to SIGWINCH and changing the terminal's size is left as an exercise for the reader :)


[1] In fact this might seem slightly wasteful, as data has to be read into user space and then pushed down back to the kernel. The problem is that os.sendfile() only works if src is a kernel object that supports mmap(), which sockets don't, and even when splice() is available in a 3dr party module, one of the parameters must be a pipe. There is at least one huge thread spread over 4 or 5 kernel mailing lists discussing widening the applicability of splice(), but to be honest, I hadn't finished reading it.


python ayrton

Juanjo Conti: Goodreads review: Cuentos de los Viudos Negros (Isaac Asimov)

Antes había leído el segundo volumen de esta historia y se nota la diferencia. Los misterios son más inocentes: se repiten las tramas con juegos de palabras o sonidos y las tramas de moribundos que dejan un acertijo al heredero.

Otra cosa que extrañé del segundo volumen es que luego de cada cuento había un epílogo explicando las circunstancias en las que el cuento había sido escrito.

Rating: 2/5

Original: https://www.goodreads.com/review/show/1462897804

Facundo Batista: Cerrando etapas


El tiempo pasa, nos vamos volviendo tecnos.

No me voy a poner a hablar de la inevitabilidad del paso del tiempo, de que la flecha de la entropía sólo apunta para un solo lado, ni de esas cosas.  Quiero hacer foco en que hay que aprovechar el tiempo que tenemos.

Esta genial foto es de una de las últimas clases de natación donde llevé a Malena.

Male y Facu en el agua

Fuimos todo el año, una vez por semana. Empezó tímida, y terminó pasando más tiempo abajo del agua que arriba, totalmente acuática. Para mi fue un placer llevarla y disfrutar esa evolución con ella, lo mismo que hice con Felipe dos años atrás. Y ya está, ese tiempo pasó y nunca más los meteré en el agua con ellos agarrándose a mi. Más allá de la nostalgia anticipada, lo importante es que cuando tuve la oportunidad, la aproveché :)

Sé que van a haber mil otros momentos donde los lleve de la mano a territorios para ellos desconocidos. La clave es estar ahí, darles la mano, acompañarlos, y disfrutar ese proceso.

Otra foto del paso del tiempo: Felipe, en su acto de graduación del Jardín, donde él mismo estaba emocionado mientras otros compañeros pasaban a buscar sus diplomas (todavía no le había llegado su turno), y luego estudiaba la medallita que le habían dado...

Felu graduado