Novedades: actualmente estoy impulsando el proyecto Argentina en Python, desde aquí (ver mapa)

Crear mosaico de fotos

En estos días he estado preparando la charla de Argentina en Python que voy a dar en @PyCaribbean en Febrero de 2016. En uno de los slides quería agredecer el apoyo que hemos recibido de toda la comunidad y para eso se me ocurrió poner una foto de un mosaico de todas las fotitos de perfiles de los seguidores de la cuenta @argenpython de Twitter.

Entonces, me puse a trabajar en eso.

Primero, busqué un script que había venido utilizando cuando estaba buscando herramientas para crear YourReminder (un programador de tuits automáticos) para obtener todos los seguidores de una cuenta en particular. Usé la biblioteca twitter:

twitter-follow --oauth argenpython > "argenpython.followers.txt"

Eso me dejó un archivo de la pinta:

b'thehandro'
b'raviol'
b'AlvaroAnguix'
b'rddebona'
b'kragen'
...

Luego, quería buscar todas las fotos de perfil de los usuarios que aparecen en ese txt. Para eso me hice este pequeño script en Python que accede a todas las urls de esos usuarios e imprime el link a la foto en la pantalla:

import re
import sys
import requests  # fades
from bs4 import BeautifulSoup  # fades

for user in open(sys.argv[1], 'r').readlines():
    url = 'http://twitter.com/{}'.format(user[2:-1])
    data = requests.get(url).content
    soup = BeautifulSoup(data)
    img = soup.find('img', {'class': 'ProfileAvatar-image'})
    print(img.get('src'))

Ejecutamos (obvio, con fades!):

$ fades get_twitter_profile_pics.py argenpython.followers.txt

https://pbs.twimg.com/profile_images/673314332340539393/lSKlxZ7r_400x400.jpg
https://pbs.twimg.com/profile_images/378800000304367652/2fb693020239400ff0062b15034890e3_400x400.jpeg
https://pbs.twimg.com/profile_images/676336395141672960/mB8x63HP_400x400.png
...

Ahora, teniendo todas las urls de las fotos de los perfiles, solo me quedaba bajarlas y hacer el mosaico. Mientras hacía este script de Python ya había preguntado por Twitter como hacer el mosaico y había obtenido una respuesta muy acertada a la cual finalmente modifiqué un poquito y fue lo que terminé usando.

Para bajar las fotos tenía dos caminos. Escribir algo en Python y lidiar programando, o pasarle toda esta basura a quien más sabe del tema: wget

fades get_twitter_profile_pics.py argenpython.followers.txt | xargs wget -c

Mientras esperaba que se bajen todas las fotos, me puse a leer las respuestas de Twitter y leer la documentación de convert y montage para finalmente terminar utilizando montage de esta forma:

montage ./*.{jpg,JPG,jpeg,png,gif} -tile 35x -geometry 96x96+0+0 -quality 90 ahlapelotita.jpg

Eso, finalmente, me llevó al resultado que necesitaba:

ahlapelotita.thumbnail.jpg

Mosaico de imágenes de perfiles de los seguidores de @argenpython

Y eso, ¡gracias a todos ustedes por el aguante!


Administrar mapas de Maps.me y OsmAnd

El primer problema que tuve con los mapas de OsmAnd es que, tarde o temprano, se te acaban las descargas disponibles de mapas que trae la versión gratuita. Ya sea por actualizarlos o bien porque vas necesitando mapas de otros países a medida que te vas moviendo ;)

Luego, vinieron los problemas con Maps.Me que si bien uno puede descargar la cantidad de mapas que desee de forma ilimitada, se ve que tienen algún problema en sus servidores ya que es muy probable que la descarga demore mucho o que finalmente termine fallando. A tal punto que ellos mismos tienen una FAQ en su sitio sobre este inconveniente.

Todo esto ya me venía diciendo que necesitaba una mejor forma de administrar los mapas de OpenStreetMap en mi dispositivo Android. Además, sumado a eso, a veces necesitaba compartir alguno de los mapas (más actualizados) que yo tenía con mi compañera de viaje. Por lo tanto, debería tener "a mano" una forma de enviarle unos 40~100 Mb de datos de una forma sencilla y que estos programas lo puedan leer sin problemas.

Y para cerrar las necesidades, también quería poder exponer estos mapas "ya descargados de internet" como servicio de nuestra Red Libre para que cuando no haya conexión todos podamos descargarlos sin problemas.

Pero claro, para eso, necesitaba una buena forma de administrar todos estos mapas y que sea confiable.

OsmAnd

OsmAnd tiene un sitio web de descarga directa de mapas de donde podemos descargar todos los mapas que deseemos para luego copiar a nuestro dispositivo. Los trucos que debemos saber aquí son:

  • Es necesario descomprimir el archivo .zip que bajamos
  • Debemos copiar el archivo .obf dentro de la carpeta osmand (en mi Nexus 5 con Android 6.0.1 está en el directorio raíz)
  • Hay que renombrar el archivo .obf quitando el _2 de su nombre

Resumir descargas con wget

Si bajás los archivos de mapas con wget, siempre está la opción -c para resumir las descargas. Sin embargo, una vez descargado por completo el archivo, si volvemos a ejecutar ese mismo comando despues de un tiempo y ha habido una nueva actualización, el comando descargará solo la parte que le falta. Sin embargo, cuando lo copiemos a al dispositivo Android este mapa no será leído.

wget -c \
  "http://download.osmand.net/download.php?standard=yes&file=Peru_southamerica_2.obf.zip" \
  -O Peru_southamerica_2.obf.zip

Maps.Me

Esta aplicación también tiene su propio sitio de descarga de mapas de una forma directa (sin hacerlo desde su propia app).

En Maps.Me necesitamos descargar 2 archivos si es que queremos tener la característica de routing. En cualquier caso, siempre debemos descargar el .mwm, no es que si queremos routing solo descargamos el .routing.

  • Descargamos los archivos .mwm y .routing del país que deseamos
  • Abrimos el archivo settings.ini que se encuentra en la carpeta MapsWithMe de nuestro dispositivo (en mi Nexus 5 está en el directorio raíz). Ahí buscamos el valor de DataVersion (una fecha), abrimos esa carpeta (o al creamos si no existe) y copiamos los archivos .mwm y .routing dentro de ella.

Mapas desactualizados

El tip del DataVersion me solucionó la notificación de Maps.Me diciéndome que mis mapas estaban desactualizados. Si por algún motivo te sigue indicando que está outdated deberías revisar que el nombre de la carpeta coincida con el nombre del DataVersion.


Lo que nos deja el fracaso

Aunque crean que mi vida es un éxito por la cantidad de proyectos, empresas, fundaciones y organizaciones que he fundado y las diferentes cuentas de banco que no dejan de aumentar diariamente sus activos (?); tengo que decirles algo:

Soy un fracasado

Estuve 4 días (de reloj -diría mi abuela) trabajando en configurar correctamente PAC, WPAD, Polipo/Squid dentro de la Red Libre y Portal Cautivo de PyFi Spot y, no lo logré.

Probé varias configuraciones diferentes y aquí dejo algunos comentarios para en un futuro no caer de nuevo en las mismas ideas:

  1. Polipo no soporta funcionar como proxy transparente

  2. Las únicas funciones soportadas dentro del archivo wpad.dat son las que figuran en su documentación. No se puede utilizar otras funciones que sí están implementadas en los Browsers cuando uno abre la consola de desarrollo. Por ejemplo, yo traté de utilizar XMLHttpRequest y no está disponible.

  3. Una buena forma de debuggear nuestro archivo PAC es utilizar estas herramientas:

    • pacparser
    • En Google Chrome, ingresando a chrome://net-internals/ y luego a la sección de Proxy y Events. Dentro de Events uno puede ver qué archivo wpad.dat está bajando y si da algún error al ejecutarlo.
  4. Para que WPAD funcione a la perfección por DNS debemos configurar las siguientes cosas; teniendo en cuenta que nuestro dominio es "redlibre":

    • Nuestro DNS debe resolver wpad.redlibre a la PC que tiene ese archivo.
    • Nuestro Web Server debe entregar ese archivo.
    • Mediante http://wpad.redlibre/wpad.dat debo poder descargar el archivo wpad.dat. Esto es lo que busca Firefox (lo dice la práctica).
    • Mediante http://wpad/wpad.dat también debo poder descargar el archivo. Esto es lo que hace Google Chrome (lo dice la práctica)
  5. Tinyproxy no tiene función de caché.

  6. La única forma que encontré de hacer un proxy transparente es con Squid y esta regla de iptables. Además, me sigue redirigiendo al Portal para loguearme:

    $IPTABLES -i $IFACE_HOSTAPD -t nat -A PREROUTING -m mark ! \
        --mark 99 -p tcp --dport 3128 -j REDIRECT --to-ports 80
    

    Esto quiere decir, básicamente, que todo lo que entra por $IFACE_HOSTAPD y no está marcado como 99 y es TCP y va al puerto 3128 -> lo redirigimos al puerto 80 de la misma máquina.

  7. Nadie sabe en qué momento los navegadores hacen un GET del archivo wpad.dat. Sin embargo, por lo general lo hacen al abrirse o al forzar su recarga desde algún lugar oscuro de la interfaz. Aunque, por otro lado, Google Chrome lo pide periódicamente cada un cierto tiempo desconocido.

  8. Cualquier cabecera HTTP que tenga que ver con Caché no es respetada por Firefox ni Chrome. Implementé una versión de wpad.dat que era generada con Flask + Jinja2 dependiendo del IP que la solicitaba y no logré hacer que no cachee su contenido utilizando Pragma y Cache-Control de varias formas y combinaciones.

  9. Google Chrome en Linux no "auto descubre" el proxy. Viene con esa configuración deshabilitada (al menos en Xubuntu 14.04). Entonces, para que auto descubra el proxy hay que llamarlo así:

    google-chrome --proxy-auto-detect
    

    Por lo que, tener una "auto configuración" de proxy en una red que muchos van a tener Linux y usar Google Chrome, es una estupidez. Firefox, por default, viene bien configurado.

  10. La función myIpAddress() dentro del archivo PAC devuelve algo muy diferente en Firefox (mi verdadero IP dentro de la red en la que estoy) y Google Chrome (127.0.1.1). Por lo tanto, no es confiable y no debería usarse.

  11. Parece que si instalás un paquete en Linux, Google Chrome comienza a obtener correctamente el IP utilizando la función anterior:

    sudo apt-get install libnss-myhostname
    
  12. Hay una función myIpAddressEx() (notar el Ex en el nombre) que no alcancé a probar porque ya estaba muy hinchado los huevos; pero que supuestamente funciona.

  13. Ah! agregale otro tema al myIPAddress(): los MS Windows 8 te devuelve la dirección de IPV6!!!

    —César Ballardini

  14. El Content-Type del archivo PAC debe ser application/x-ns-proxy-autoconfig porque sino puede ser que algunos browsers no lo utilicen.

  15. alert() y console.log() dentro del archivo PAC no tienen ningún efecto. "No insista".

César me estuvo ayudando muchísimo, sobre todo con las ideas de cómo implementarlo y qué probar para poder llegar a buen puerto. Sin embargo, creo que ambos nos cansamos de dedicarle tiempo y esfuerzo a algo que está demasiado mal implementado y se rompe por todos lados. Sinceramente, creo que no hay un standard bien creado.

PAC y WPAD anda si lo ponés en una maceta y no dejás más que una plantita a la cual cuidas con mucho esmero, y cuando crece la arrancás para que no sea diferente.

—César Ballardini

Y yo me despedí de él así:

Te lo digo así: PAC, WPAD, Google Chrome, Firefox e Internet Explorer se pueden ir bien a la mierda. No puedo creer que esta gente haya inventado una tecnología tan pero tan mala para auto-configurar un proxy de mierda. Que no respete estándares y que sea tan difícil de debuggear.

Acordate bien de esto: ¡estos tipos son unos hijos de puta!

—Manuel Kaufmann

Entonces, borré todo lo que hice, desinstalé Polipo, Tinyproxy y Squid. Además, borré el archivo wpad.dat y mandé todo al carajo. No tiene sentido implementar algo que es tan inestable. Para lo único que simple todo esto es para tener algo demasiado-muy sencillo, esa plantita a la que hace referencia César.

Espero que la siguiente persona que quiera configurar PAC y WPAD llegue a este artículo antes de aventurarse en esto y terminar siendo un fracasado más.


#1 Report to PSF

Para darle un poco más de transparencia al proyecto Argentina en Python y también para dar a conocer cuáles fueron los eventos que hemos ido organizando a lo largo de que empezó la financiación por parte de la Python Software Foundation, vamos a ir dando a conocer los reportes que hemos ido enviando

Hoy publicamos el #1 reporte enviando: Tue, 10 Feb 2015 22:30:51 -0300

Hello PSF,

How are you? I'm writing my 1st report to comment you about how my
project "Argentina in Python" is going.

During the last month I've been working on some Python's events:

== Past Events ==

- 17 Jan - Sprint at Resistencia, Chaco, Argentina
  (https://elblogdehumitos.com/posts/primer-sprint-de-python-en-resistencia-chaco/)

- ~20 attendees (4 girls). Most of them without knowledge of
  Python, so we worked in different groups. The idea of a possible
  PyDay at Formosa merged from this meeting.

- 30 Jan - MeetUp at Resistencia, Chaco, Argentina
  (http://www.meetup.com/Python-NEA/events/219942458/)

- ~15 attendees (3 girls). A very informal meeting to keep all the
  energy from the past Sprint together and to start creating a
  local community. We talk about the future of Python, brython,
  javascript, django, php, openstreetmap and much more.

== Future Events ==

- 14 Feb - PyDay at Formosa, Formosa, Argentina

- 21 Mar - PyDay at Asunción, Gran Asunción, Paraguay

- 20-22 May - Educational Track for secondary students at SciPy LA
  2015, Posadas, Misiones, Argentina (http://scipyla.org/conf/2015/)

- Possible PyDay around May at Encarnación, Itapúa, Paraguay

- Possible PyDay around May at Apóstoles, Misiones, Argentina


* Is this the kind of information you want from me to report?
* Is this useful for you?

Please, let me know if you need another kind of information.

Thank you guys.