rss planet warp

August 27, 2010

Acertijo Java (Solución)  Golo

A principios de mes proponía un Acertijo Java.

¿Qué sale por pantalla al ejecutar la clase FooBar?

import static java.lang.System.*;
public class FooBar {
  public FooBar() {
    out.print(" Instance ");
  }
  static {
    out.print(" Static ");
  }
  public static void main (String args) {
    out.print(" Main ");
    new FooBar();
  }
}

Simplificando, lo que ocurre es lo siguiente:

  1. Se carga la clase FooBar en memoria
  2. Se ejecutan los bloques estáticos: static{}
  3. out.print(” Static “); está en un bloque estático
  4. Una vez cargada la clase, ya se pueden ejecutar métodos estáticos como main
  5. out.print(” Main “); está dentro del método main
  6. Se crea una instancia de la clase FooBar en el método main
  7. Para ello se ejecuta el constructor
  8. out.print(” Instance “); está en el constructor

Siguiendo este razonamiento, la opción correcta sería la c: “Static Main Instance”.

La opción c parece correcta, pero no se llega a ejecutar el método main. Recordemos la firma que tiene que tener el método main para ser la entrada de un programa java:

  public static void main (String[] args) {}

Ha de ser esa o equivalente. ¿Ves la diferencia? El parámetro args tiene que ser un array [] de String, ya que representa los argumentos introducidos por línea de comandos. En la clase FooBar el parámetro args es simplemente un String.

Por ello, el código no se ejcutará. Java mostrará un error indicando que en la clase FooBar no existe un método main. Es por ello que la opción correcta era la f: No se ejecutará

En el día a día, un programador no sólo tiene que saber traducir ideas a código. En algunas ocasiones el código no se comporta como queremos y es cuando hay que buscar el detalle.

La agilidad con la que uno detecta pequeños detalles marca la diferencia. Algunos factores que afectan a esta agilidad son, la paciencia, saber escribir código claro, y sobre todo el conocimiento de las herramientas con las que uno trabaja.

Igual que hice en el otro post, animo a cualquiera a ponerse a prueba estudiando la certificación de Programador Java o acudir a uno de los cursos oficiales que imparto, es toda una experiencia.

August 26, 2010

MeeGo Handset Bootchart on Aava Mobile  Hector

Below is a bootchart showing an Aava Mobile’s bootchart using MeeGo Handheld. I used the meego-handset-ia32-aava-mtf-1.0.80.15.20100813.1-sda.bin image.

To generate the bootchart, you just need to mic-chroot and install these two packages (the default kernel has process accounting enabled):

yum install bootchart acct

Then, just copy the image to the boot media, and turn the power on :) Your chart will be stored in /var/log/bootchart-YYYYMMDD-HHMM.svg

(Click to enlarge)

August 16, 2010

MeeGo Handset: from power-on to system init  Hector

Being fully aware of what’s happening on your system, since the very first stages, is one of the things that I really love about open source software.

I used to spend hours and hours dissecting different live cd systems, or setting up network boot scenarios, so it was likely that I would end up doing the same with this lovely Aava Mobile running MeeGo Handset :)

In this post, I will explain just the first stages of its boot sequence, involving the boot loader, the kernel and the initial ramdisk. Details about the system and the graphic session initializations will be presented in next posts.

Boot loader

MeeGo Handheld uses kboot as bootloader, and a two-step boot sequence. Basically, it involves booting the kernel, and then running an intermediate, ramdisk based system, before loading the root filesystem and letting the classic UNIX system init process start.

Kernel

MeeGo Handheld kernel’s default parameters are:

ro pci=noearly console=tty1 console=ttyS0 earlyprintk=mrst loglevel=8 nosplash s0ix_latency=160

An brief explanation for each parameter is included below:

Ramdisk

Once the kernel is loaded, it enters the ramdisk initialization sequence. Its main goal is finding and mounting a suitable root partition, so you don’t need to hardcode it, and the same image can be reused regardless you are storing the root partition on an SD, USB or NAND device.

MeeGo Handheld ramdisk is based on Fedora’s. It includes a basic selection of early user space commands, but -surprisingly for an embedded environment-, it is not Busybox based, sporting a complete bash shell and the full-fledged version of these tools.

Filesystem layout

The ramdisk contents are a bare bones UNIX system, stripped down to contain just the basic stuff:

├── bin
│   ├── bash
│   ├── cat
│   ├── chmod
│   ├── cut
│   ├── dd
│   ├── dmesg
│   ├── echo
│   ├── env
│   ├── grep
│   ├── kill
│   ├── less
│   ├── ln
│   ├── ls
│   ├── mkdir
│   ├── mknod
│   ├── mount
│   ├── rm
│   ├── sed
│   ├── sh -> bash
│   ├── sleep
│   └── umount
├── dev
├── etc
│   ├── fstab
│   └── udev
│       └── rules.d
├── init
├── lib
│   ├── ld-linux.so.2
│   ├── libacl.so.1
│   ├── libattr.so.1
│   ├── libcap.so.2
│   ├── libc.so.6
│   ├── libdl.so.2
│   ├── libpcre.so.0
│   ├── libpthread.so.0
│   ├── librt.so.1
│   ├── libtinfo.so.5
│   ├── terminfo
│   │   ├── a
│   │   │   └── ansi
│   │   ├── d
│   │   │   └── dumb
│   │   ├── l
│   │   │   └── linux
│   │   └── v
│   │       ├── vt100
│   │       ├── vt100-am
│   │       ├── vt100-nav
│   │       ├── vt102
│   │       ├── vt200
│   │       ├── vt220
│   │       └── vt52
│   └── udev
│       └── rules.d
├── proc
├── sbin
│   ├── blkid
│   ├── blockdev
│   ├── chroot
│   ├── dmsetup
│   ├── insmod
│   ├── killall5
│   ├── losetup
│   ├── lsmod
│   ├── modprobe
│   ├── pidof
│   ├── run-init
│   ├── udevadm
│   └── udevd
├── sys
├── sysroot
└── usr
    └── lib
        ├── libblkid.so.1
        ├── libdevmapper.so.1.02
        └── libuuid.so.1

/init script

Once the kernel loads, it starts executing the /init script, that:

  1. Sets up the basic environment
  2. Parses kernel command line options
  3. Sets up root filesystem
  4. Transfers control to the root filesystem’s init

We are going to walk through this small piece of code, skipping some setup and debug sections for clarity’s sake.

1. Set up the basic environment

First, it redirects standard output and error to /dev/console. This is important for debugging, as the console device /dev/console is handled by the kernel, and we can change it using boot params, e.g. to access display-less devices using a serial interface.

Then, the minimal facilities are set up. An initramdisk is a very early, almost "naked" user-space, so we need a way to interact with the operating system: some device files and the Linux kernel’s pseudo filesystems. From this point on, any executable on the ramdisk has a way to perceive and interact with the system. Basically, this stage involves:

2. Parse kernel command line options

It parses the command line options, and then:

The main variables used by the script are detailed below:

Other variables, like live_ram, live_locale, overlay or check, are not used.

3. Set up root filesystem

Most of the /init script code handle setting up the root filesystem.

The detection has two clear execution paths, depending on the "autodetect" variable’s value. This variable is not defined or processed from the kernel’s command line, so it seems it’s just some dead code from another MeeGo initrd.

First, three variables are initialized from the embedded /etc/fstab file, or the related kernel command line options:

For instance, the default /etc/fstab file, stored inside the ramdisk, holds this root filesystem definition:

/dev/sda1 / ext3 defaults,noatime 0 0

Once these variables are set, if autodetect is enabled, it adds different udev rules, depending on how the root filesystem is specified via the root variable, like /dev/disk/by-label, /dev/disk/by-id, CDLABEL, LABEL, UUID, etc. It just does nothing if root is specified by another /dev/* path.

If autodetect is not enabled (or not defined, which seems the case), it just starts udev, waits for it to be settled, and then searches for a filesystem at:

After this process, the thingtomount variable must be set, or an error has occurred.

(At this point, if the "shell" parameter is enabled, the user is dropped on a bash shell)

The final stage of the root filesystem setup involves making sure the device specified by thingtomount holds a suitable root file system, composing mountoptions with rootflags, root_rw and root_ro, and, finally, mounting the root filesystem at /sysroot

4. Transfer control to the root filesystem’s init

Once the new root filesystem is available, the process is almost done. The script just:

#!/bin/bash
cd /sysroot
mount --move . /
exec /sbin/chroot . "$init" "$@"

At this point, everything is ready for the system initialization to start…

August 12, 2010

x86 MeeGo handhelds on their way…  Hector

Since I started to play with Atom based devices, this is the first time I have a real “handheld” in my hands: the Aava Mobile.

It’s still a little bigger than a Nexus One, but anyway, it’s amazing how they managed to miniaturize a complete computer into a SOC:

Since mid 2007, a device like a Samsung Q1 Ultra has reduced four times its size, sporting some new goodies like a bunch of sensors (ambient light, proximity, 3D compass, accelerometer…).

MeeGo for Handhelds is under heavy development, and Intel and Nokia efforts for creating a compeling environment for users, but also for developers, are going to be really, really interesting. Exciting times ahead!

August 02, 2010

Acertijo Java  Golo

Volvemos después de la resaca de año nuevo con un acertijo java.

Suponiendo que el contenido del archivo FooBar.java es el siguiente… ¿Qué saldrá por pantalla al ejecutar la clase FooBar?


import static java.lang.System.*;
public class FooBar {
  public FooBar() {
    out.print(" Instance ");
  }
  static {
    out.print(" Static ");
  }
  public static void main (String args) {
    out.print(" Main ");
    new FooBar();
  }
}

Hagan sus apuestas…

Antes de empezar a preparar la certificación SCJP (Sun Certified Java Programmer) no había caído en la cuenta de cuántas cosas desconocía de programación.

Estés preparando o no la certificación de programador, recomiendo la guía de Kathy Sierra. Además, si quieres que un experto en java (modestia aparte) te asista, puedes invitarme a tomar una cerveza, o apuntarte al curso oficial que impartiré en septiembre: SL-275-SE6 Java Programming Language

¡Date prisa, al tercero que me invite a una cerveza ya no hablaré coherentemente, y el curso tiene las plazas limitadas!

Un Saludo!

Actualizado: Puedes encontrar la solución aquí.

August 01, 2010

Private clouds  Hector

Private clouds for Agile teams was the name of a brief introduction to some existing cloud technologies, held last Thursday in SkillsMatters by Toby Hobson from Raptor Consultants.

The first half of the talk outlined some of the features and practical problems of cloud computing. For instance, the lack of a suitable licensing scheme from some OS/application vendors, the difficulties integrating legacy systems, or the concerns about the security of production environments. This problems, especially the last one, are limiting cloud deployments to development and integration environments, using private cloud technologies.

Then he went on comparing some of the main offerings for the deployment of these sort of clouds:

  • 3Tera: easy to use GUI, most suited to non-techies.
  • Red Hat: good support, but probably you still need to buy professional services. Best option for Red Hat shops doing strategic investment.
  • OpenQRM: not just a private cloud system but a more general datacenter management solution. Suited for environments involving legacy systems. Complex to configure, but offering an excellent and customer focused support.
  • Ubuntu Enterprise Cloud: excellent EC2 support (using Eucalyptus, hi Mårten! :P ), easy to setup, good documentation and excellent support. The best option to deploy quickly a private cloud.
  • Regarding the “for Agile teams” part, unfortunately the talk didn’t delve deeper into development topics, but it mentioned build and test automation, and the need for a good scripting platform/API.

    You can find the podcast here.

    July 23, 2010

    Business Analysts and Agile projects  Hector

    Yesterday, I attended The Business Analyst’s role on Agile projects at Skills Matters. Allan Kelly spoke about “business analysis” and how it fits in an agile project.

    He talked about how naive some of the Extreme Programming and Agile original principles were, regarding analysis activities. Replacing “the customer” in make the customer always available with a business analyst (a customer proxy) is the way he proposed to deal with this problem. E.g. in an SCRUM team, he proposed a Product Owner role encompassing technical leadership, product management and business analysis duties.

    I liked his explanation -with actual data from the MIT Sloan review-, of why, in an organizational improvement context, doing the things right before is better than doing the things right at first. (Slide 18, The Alignment Trap)

    When he talked about Just In Time Requirements, I was confronted with the same thoughts I have most of the times I apply this technique: clients, especially top management, are reluctant to this kind of uncertainty. It takes time, and a tactful approach, to convince them.

    I don’t agree, at least as a general definition, with his standpoint of SCRUM as a “project management method without a project manager”. Besides pure managerial tasks (like some staffing details), I think a separate person is still needed to have a full (not iteration-centric) management view of projects, and most important, to avoid distractions and cope with unplanned or non-deterministic events (e.g. chasing equipment providers or busy external stakeholders).

    The slides and video are available.

    June 09, 2010

    Yo y mis juguetes 3G  Dani

    Es verdad, hay que confesarlo. Tengo un problema con los aparatos… No puedo estar quieto con ellos.

    Hace menos de un mes acabó cayendo en mis manos un lector de tinta electrónica Sony PRS.900. El aparato en sí está bastante bien. Una pantalla de 7′, pantalla táctil (aunque admito todo tipo de críticas acerca de los reflejos que produce) e incluso 3G que permanece el 99,999% del tiempo desactivado porque no sirve para nada excepto para comprar libros en la tienda online de Sony. Para más INRI, el aparato es “Sold in USA”, por lo que el 3G sólo funciona en el país yankee.

    Para hablar acerca de editoriales, libros, precios, gobiernos o de por qué hay bestsellers en Amazon Reino Unido que cuestan un 40% lo que cuestan en la Fnac, ya hay otras páginas. Yo he venido aquí a hablar de mi libro… o sea, de mi problema… o sea, de dejar quieto un aparato sin meterle la zarpa, actualización, firmware, flash, update o una sarta de bits que le provoquen convulsiones y deje de funcionar como debía.

    Desde el principio el tema que más me picó fue el tema del 3G. Tener un aparato que funciona al 99% está bien, pero saber que nunca vas a poder usar ese 1% es terrible. Eso sí, conseguir que funcione ese 1% aunque jamás necesites utilizarlo es la bomba. Por tanto, había que conseguir que funcionase el 3G.

    Como primer paso, busqué lo que había disponible sobre el aparato. Encontré unos firmwares de unos rusos muy listos (o desesperados) que habían traducido el interfaz a la lengua de las muñecas Matroskas. Genial, la parte dificil estaba hecha. Más si habían metido fuentes cirílicas. Para lo que me interesaba: la gente ya sabía “flashear” un dispositivo de estos.

    El Sony PRS-900 contiene un sistema Linux (LinuxLink) de TimeSys. El sistema contiene un sistema de ficheros de tipo cramfs y con algunos pequeños tutoriales, es razonablemente sencillo cambiarlo. El mayor problema es que para flashearlo es necesario un equipo con windows (hay que hacer llamadas a una DLL de windows para entrar en modo recovery).

    Pim Pam Pum, lector flasheado. Si había funcionado la primera vez con un firmware ruso, la siguiente podía funcionar con un firmware Made in Spain. Así, descomprimí la imagen cramfs y me puse a mirarle las tripas al aparato. Por dentro del directorio /opt/sony/ebook/application… se encuentra la mayor parte del software que ve el usuario. Parece una aplicación bastante flexible, que supongo que será la piedra angular de Sony para sus lectores. Muy probablemente, un sólo software podrán utilizarlo para el PRS-300, PRS-500, PRS-505, etc ya que afecta a todo el dispositivo; desde el cambio de hora, hasta las características soportadas por el dispositivo, pasando por la configuración del APN del 3G (uhmmm :-) ).

    Me propuse cambiar ese valor para “esquivar” el APN que tienen en consorcio Sony y AT&T en Estados Unidos, y lo cambié por el de movistar (junto con una SIM). Además, quité la configuración del proxy que también estaba presente, ya que parece que la tienda que muestra el PRS-900 es a través de un navegador web reducido.

    Hechos los cambios, nueva imagen, y a flashear. Fracaso total. El lector no conectaba y lloraba pidiendo que le quitase esa SIM del infierno.

    Los rusos listos también habían encontrado el modo de pruebas de hardware del equipo. Gracias a una pequeña combinación de botones, se puede acceder a todo un menu interno del aparato para probar pantalla, calibrarla, y/o acceder a otra información o configuraciones. Lo mejor de todo es que algunas de las opciones mostraban un volcado de los registros del aparato. Click. Volcado a la SD. A investigar…

    En los logs del sistema (nada parecido a un log Unix), aparecen 3 tipos de archivos. Los que están en formato binario puro (ya si eso les voy pidiendo el manual a Sony…), los que traducen el formato binario a un formato legible, aunque mediante códigos (o llamo a un egiptólogo para que me lo descifre por asociaciones), y por último, los que muestran esos códigos traducidos a cosas más humanas.

    Como ejemplo:

    0, Wed 31 Dec 1969 19:00:08, 883687, CM, Call API, Create Instance, 0, 0, 0
    1, Wed 31 Dec 1969 19:00:09, 36165, log, start, 0, 0, 0, 0
    2, Wed 31 Dec 1969 19:00:09, 43897, pm, stateChangeLockReq, Run, 0, 0, 0
    3, Wed 31 Dec 1969 19:00:09, 69969, pm, doStateChange, Off, Run, 0, 0
    4, Wed 31 Dec 1969 19:00:09, 70066, pm, deviceStateChange, Start, Run, 0, 0
    5, Wed 31 Dec 1969 19:00:09, 71506, pm, deviceStateChange, Finish, Run, 0, 0
    6, Sat 10 Jan 2009 03:30:36, 116727, subCPU, send, system, bootFinish, 0, 0
    7, Sat 10 Jan 2009 03:30:36, 124550, subCPU, send, key, onOff, 822083584, 0
    8, Sat 10 Jan 2009 03:30:36, 134396, subCPU, send, touchpanel, onOff, 822083584, 0
    9, Sat 10 Jan 2009 03:30:36, 144468, subCPU, send, touchpanel, sampling, 50331648, 0
    10, Sat 10 Jan 2009 03:30:36, 154418, subCPU, send, touchpanel, breaktime, 16777216, 0
    11, Sat 10 Jan 2009 03:30:36, 164455, subCPU, send, LED, LED, 0, 0
    12, Sat 10 Jan 2009 03:30:36, 174444, subCPU, send, battery, queryLevel, 0, 0

    Bastante aceptable…

    Ahora era cuestión de investigar qué narices estaba pasando con el módulo 3G


    CM, Received UR, [*EMRDY: 1]
    CM, State, Radio Off, 0, 0, 0
    CM, Sent ATCom, ATE0, OK, 0, 0
    CM, Sent ATCom, AT+CMEE=1, OK, 0, 0
    CM, Sent ATCom, AT+CPIN?, OK, 0, 0
    CM, Info, Sleep Mode, Can Sleep, 0, 0
    WAN, getInfo, 0, 0, 0, 0
    WAN, stateChange, partialOn, 2, 2, 8
    CM, Sent ATCom, AT+CGSN, OK, 0, 0
    CM, Call API, Get Info, 0, 0, 0
    WAN, getInfo, 0, 0, 0, 0
    WAN, lock, 1, 0, 0, 0
    CM, Sent ATCom, AT+CRSM=176,12258,0,0,10, OK, 0, 0
    WAN, unlock, 0, 0, 0, 0
    WAN, lock, 1, 0, 0, 0
    Call API, Stop, 0, 0, 0
    WAN, stop, 0, 0, 0, 0
    WAN, stateChange, waitStop, 2, 2, 7
    CM, Sent ATCom, AT+CFUN=0, OK, 0, 0

    A grosso modo, lo que se ve es que el dispositivo despierta (*EMRDY: 1), oculta el eco local (ATE0), activa los errores numéricos del módulo (AT+CMEE=1) y consulta si necesita PIN (como lo tenía desactivado, todo sin problemas). Después, solicita el número de serie del aparato (normalmente se hace para mostrarlo en algún menú para información del usuario), y llega a un punto en el que lee de forma binaria la SIM, haciendo:

    AT+CRSM=176,12258,0,0,10

    comando que lee el número de identificación de la SIM (ICCID). Tras esta lectura (teoricamente fructuosa), se ve mucho “Stop” y un AT+CFUN=0 que acaba apagando el módulo de telefonía.

    Hasta este punto, es común tanto para la SIM de Movistar como para la de AT&T.

    Si seguimos indagando, parece que con la SIM de AT&T, el módulo ya no sigue progresando. Recibe notificaciones para intentar despertarse de nuevo y seguir conectando, pero no hay más comandos AT, sino sólo estos intentos en vano. Sin embargo, con la SIM de Movistar, se puede ver:


    CM, Received UR, [*EMRDY: 1]
    CM, State, Radio Off, 0, 0, 0
    CM, Sent ATCom, ATE0, OK, 0, 0
    CM, Sent ATCom, AT+CMEE=1, OK, 0, 0
    CM, Sent ATCom, AT+CPIN?, OK, 0, 0
    CM, Sent ATCom, AT+CREG=1, OK, 0, 0
    CM, Sent ATCom, AT+CGREG=1, OK, 0, 0
    CM, Sent ATCom, AT*E2CFUN=1, OK, 0, 0
    CM, Sent ATCom, AT+CFUN=1, OK, 0, 0
    CM, Received UR, [*E2CFUN: 1, 1, 0
    CM, State, No Service, 0, 0, 0
    CM, Sent ATCom, AT*EIAD=0, OK, 0, 0
    CM, Sent ATCom, AT*EIAC=1, OK, 0, 0
    CM, Sent ATCom, AT+CSCS=IRA, OK, 0, 0
    CM, Sent ATCom, AT*EIAPSW=1,1, OK, 0, 0
    CM, Sent ATCom, AT*EIAAUW=1,1, OK, 0, 0
    WAN, stateChange, on, 2, 2, 4
    CM, Received UR, [*ESTKSMENU: mo
    CM, Received UR, [+CREG: 1]
    CM, Received UR, [+CGREG: 1]
    CM, State, Attached, 0, 0, 0
    WAN, notifyState, registered, 0, 0, 0
    CM, Received UR, [*EMWI: 1, 0]
    CM, Received UR, [+PACSP0]
    CM, Sent ATCom, AT*E2OTR=1, OK, 0, 0
    CM, Sent ATCom, AT+CIND?, ERROR, 100, 0
    CM, Sent ATCom, AT+CIND?, ERROR, 100, 0
    CM, Sent ATCom, AT+CIND?, ERROR, 100, 0
    CM, Info, Sleep Mode, Can Sleep, 0, 0
    CM, Sent ATCom, AT*ERINFO=1, OK, 0, 0
    CM, Sent ATCom, AT*ERINFO?, OK, 0, 0
    CM, Sent ATCom, AT*E2NAP=1, OK, 0, 0
    CM, Sent ATCom, AT*ENAP=1,1, OK, 0, 0
    CM, Info, Sleep Mode, Can not Sleep, 0, 0
    WAN, notifyState, error, 0, 0, 0
    CM, Received UR, [*E2NAP: 2]
    CM, Sent ATCom, AT*E2CFUN=1, OK, 0, 0
    WAN, stateChange, waitOff, 7, 2, 6
    CM, Sent ATCom, AT+CFUN=4, OK, 0, 0
    CM, Sent ATCom, AT*E2OTR=0, OK, 0, 0
    WAN, unlock, 0, 0, 0, 0
    CM, Sent ATCom, AT*ERINFO=0, OK, 0, 0
    CM, Received UR, [*E2NAP: 0]
    CM, Error, Connection, 0, 0, 0
    CM, Received UR, [+CREG: 4]
    CM, Received UR, [*E2CFUN: 1, 4, 0
    CM, State, Radio Off, 0, 0, 0
    CM, Sent ATCom, AT+CFUN=0, OK, 0, 0
    CM, Info, Adjust State, Radio Off, 0, 0
    CM, State, Module Off, 0, 0, 0

    Se puede analizar con calma todos los comandos. Aproximadamente el formato es:

    accion,comando,parametros_recibidos
    o
    accion,comando,respuesta,codigo_de_error

    Básicamente, se activan notificaciones, sensores de temperatura, se reinician los contextos de conexión a internet, se crean nuevos (incluso con mis datos cambiados del APN), así todo correcto hasta que se llega al a línea:
    CM, Sent ATCom, AT+CIND?, ERROR, 100, 0
    La cual quiere decir: AT+CIND Indicator control (ver. 5) – Check the current status of indicators and states in the phone, for example, check if a charger is connected, check the current state for a call setup, and so on. Comprueba ciertos estados internos del teléfono y ¿como respuesta?, da un ERROR. Cagada. Como error CME asociado a esta respuesta el número 100… Miramos la tabla de errores, y ¿qué encontramos? Error 100: Unknown. Doble cagada con tirabuzón.

    En resumen, cabe la posibilidad de que el dispositivo esté bloqueado para SIMs de AT&T. Puede que incluso esté bloqueado para que sólo funcione con la SIM que venía con el lector. Puede que se hagan comprobaciones en el software, además de en el módulo de telefonía, para saber si el aparato debe funcionar en España… Al menos hay una cosa a agradecer, el módulo no es CDMA, lo cual sería posible que funcionase en redes GSM europeas (de hecho, eso dice en las hojas de especificación, aunque la página de Sony diga lo contrario). Pero ahora… pueden ser tantas cosas que el siguiente paso es incierto. Una opción podría ser tratar de conseguir un módulo Ericsson F3507G liberado de operadoras, para ver si únicamente está bloqueado. No es una locura, pero tampoco es barato. También se podría intentar colocar otro módulo distinto de otro fabricante, aunque entonces implicaría perder toda la integración del software (y tener que compilar un módulo del kernel como mínimo, o un kernel nuevo)… muy arriesgado.

    Quizá también podría estar quietecito por una vez con estos aparatos, pero… entonces no tendría el problema que me ha hecho escribir el post :)

    Referencias: Información sobre el módulo del lector, una pequeña introducción sobre los comandos soportados, Manual de comandos AT GX64 y la lista de comandos AT para teléfonos Sony Ericsson

    May 16, 2010

    Lego mindstorms, little-big robots  David Pellicer

    For the final project of my computer-science degree at Universidad de Zaragoza, I am developing a complete navigation system for Lego Mindstorm robots. Using all the sensors availible for this kind of robost, my goal is to study how trustly can these robots be.
    I used Lejos OS for the robot brick, and I developed a visual application for PCs that using a bluetooth connection can monitor the robot sensors, the position and the speed of the robot. It is also a remote control to the robot, if you don’t want to trust in its artificial intelligence.
    It navigates using a preload map, and helped with the camera and the ultrasonics sensor it can update the map with the obstacles it detects. When you send a command with the position you want the robot will go, it calculates the best path to go, and if it detects an obstacle while is moving, it updates the map, recalculates the path and send all information to the control application on the PC by bluetooth. It uses a closed-loop control movement, based on its calculated path on a cell based map.

    This is a video of the robot avoiding obstacles and getting into the goal position.

    Using a camera, it is also able to follow color objects, like a red ball. And based in the size of what it sees, it open and close a clamp to catch it, here it is a video of a demostration of it.

    This robots has a diferncial movement, now I am working in a car-like robot, I will upload more information as soon as possible.

    May 12, 2010

    First steps in Caribou keyboard  David Pellicer

    Caribou is a text entry and UI navigation application being developed as an alternative to the Gnome On-screen Keyboard. The overarching goal for Caribou is to create a usable solution for people whose primary way of accessing a computer is a switch device.

    The initial goal is to make an in-place on-screen keyboard suitable for people who can use a mouse but not a hardware keyboard. This on-screen keyboard may also be useful for touch screen or tablet users.

    With the funding from the Consorcio Fernando de los Rios from Junta de Andalucia (Spain), Warp is developing improvements to Caribou keyboard like predictive text, spanish layouts or the posibility of use with a switch device.
    Warp has an early implementation of a DBUS service that provide prediction to the keyboard (or any application) using Presage prediction library, and using a patch developed by Joaquim Rocha we have a different keyboard layout specified with a JSON file.

    This is a little video-demo of what is able to do at the moment.

    I will report on progress of the project in the coming days.

    Hello co!  David Pellicer

    Hello all!!!

    The aim of this blog is to report my advances and my ideas and my thoughts on various subjects that I like.
    I hope to publish many posts but no promises…

    May 11, 2010

    GNOME Marketing Hackfest en Zaragoza  Dani

    Tal y como contábamos, se celebró la semana pasada en Zaragoza el GNOME Marketing Hackfest. Gracias a los sponsors, un grupo de GNOME Marketing se reunió en la Biblioteca del Agua de Zaragoza para preparar el lanzamiento de GNOME 3.

    Primero se definió una campaña de marketing, con muchas actuaciones a atacar. Todo está planificado con fechas concretas y gente a la que perseguir, lo que ayudará a que se vayan cumpliendo todos los puntos.

    También se comenzó el diseño de la página web de presentación de GNOME 3. Ya salieron unos cuantos bocetos, y realmente puede quedar muy limpia y ordenada. Genial! :)

    Otra de las iniciativas interesantes fue la preparación de los vídeos para la web. La idea es preparar unos pequeños spots mostrando las novedades que traerá GNOME 3. Además, se ha preparado un how-to para que cualquiera que quiera ayudar o localizar los vídeos, pueda hacerlo de manera sencilla (aunque requieren bastante material :P )

    Hubo más temas a tratar e ideas geniales, pero alguna sorpresa hay que guardar… Os invito a que entréis a las páginas del wiki para descubrir todo lo que se habló.

    Y aquí está la foto de la gente que estuvo en el Hackfest

    GNOME Marketing Hackfest

    GNOME Marketing Hackfest

    Además de lo que fue trabajo 100% GNOME, también hubo presentaciones de casos de éxito tanto de la Administración local como de la regional, así como de las empresas de aquí. Mención especial a las empresas del sur (Yaco y Emergya) que se vinieron a pasar un ventoso día con nosotros. Éstas, junto con Onirica, Warp y con el Consorcio Fernando de los Ríos son quienes ahora mismo quieren darle un empujón a la Accesibilidad de GNOME. ¡A ver hasta donde se llega!

    May 07, 2010

    Cumpleaños feliz!  Dani

    Esta canción es la que hace unas horas le he cantado a mi parche sobre el código de Android.

    Tras un año completo, sigo esperando una revisión efectiva del parche y que lo acepten (o se quejen) por parte de Google. Entiendo que los ingenieros estén ocupados, pero no sé hasta qué punto a la comunidad que están creando alrededor de Android se le permite colaborar con el proyecto principal.

    Es una sensación rara… en otros proyectos parece que hay más facilidades de cara a las colaboraciones externas… Habrá que meditar con cuidado sobre el tema

    May 04, 2010

    Marketing Hackfest Gnome 3.0 en Zaragoza  Warp Networks

    El equipo de diseño y marketing de la comunidad internacional de Software Libre GNOME, se reúne en Zaragoza entre el 4 y el 7 de mayo con el objetivo de tratar temas estratégicos en el lanzamiento de la versión GNOME 3.0, que será una verdadera revolución de este popular entorno de escritorio.

    El encuentro será sobre todo una reunión de trabajo, dónde se prevé definir y planificar los recursos de diseño y marketing para GNOME 3.0. Además, una parte muy importante de la agenda, liderada por Vincent Untz, incluye acercamientos con los gobiernos locales y con miembros de la comunidad GNOME, con la intención de consolidar los vínculos y conocer de cerca sus proyectos.

    Esta reunión de expertos cuenta con el apoyo del Gobierno de Aragón, Instituto Tecnológico de Aragón, Ayuntamiento de Zaragoza y de las asociaciones Asolif y Cesla. Es importante destacar que el apoyo institucional y su compromiso alrededor de una efectiva implantación del Software Libre han sido fundamentales para que la organización eligiese nuestra ciudad como sede de este encuentro.

    Detalles en GNOME 3.0 Marketing Hackfest

    April 18, 2010

    python-messaging: SMS encoder/decoder for the masses  Pablo

    python-messaging is a pure python SMS encoder decoder. Some of its features:

    • 7bit/8bit/UCS2 encoding: Now you can text your Chinese friend
    • Multipart SMS: Because sometimes 140 chars are not enough
    • SMS status report: Be sure they read it
    • Stuff you take for granted: Alphanumeric addresses, timezone awareness, good test coverage
    • Used in production by several projects: Wader, BCM and MobileManager

    As far as we know, is the most advanced SMS encoder/decoder written in pure Python (otherwise we would be using it ;) If you need a PDU encoder/decoder for your project, python-messaging should fit your needs.

    API walkthrough

    >>> from messaging import PDU
    >>> p = PDU()
    >>> p.encode_pdu('+342323312', 'hello')
    [(18, '001100099143323213F20000AA05E8329BFD06')]
    >>> p.encode_pdu('+342323312', 'hello', csca='+345343234')
    [(18, '069143353432F41101099143323213F20000AA05E8329BFD06')]
    >>> p.encode_pdu('+342323312', 'hello', csca='+345343234', request_status=True)
    [(18, '069143353432F43102099143323213F20000AA05E8329BFD06')]

    The only class you need to deal with is PDU. It is able to encode and decode PDUs with two simple functions: encode_pdu and decode_pdu. encode_pdu receives a number and the text you want to send, plus a number of optional parameters where you can specify smsc, validity, reference number, whether you want a confirmation or not, etc. The function returns a list of tuples with the pdu length and the pdu as a string ready to be sent via the serial port. Why a list of tuples? Well, you might want to send a SMS with more than 140 chars. If so, every fragment of the SMS will be a tuple in the list.

    python-messaging can not send the SMS, is just an encoder! This is what you need to do in order to send a SMS with pyserial and python-messaging:

    import messaging
    import serial
    
    def send_text(number, text, path='/dev/ttyUSB0'):
        # encode the SMS
        p = messaging.PDU()
        # notice how I get the first returned element, this does
        # not deal with concatenated SMS.
        pdu_length, pdu = p.encode_pdu(number, text)[0]
        # open the modem port (assumes Linux)
        ser = serial.Serial(path, timeout=1)
        # write the PDU length and wait 1 second till the
        # prompt appears (a more robust implementation
        # would wait till the prompt appeared)
        ser.write('AT+CMGS=%d\r' % pdu_length)
        print ser.readlines()
        # write the PDU and send a Ctrl+z escape
        ser.write('%s\x1a' % pdu)
        ser.close()
    
    send_text('+3223223', 'hey how are you?')

    Updated: The above snippet had an error, it was trying to send the SMS with GMCS instead of CMGS, thanks Jakob for spotting it.

    It was not too hard was it? This code does not deal with PIN authentication by the way, so be sure your device is already authenticated, or auth is disabled. Oh by the way, sending a UCS2 encoded message is easy, just pass a unicode string and the PDU object will do the right thing.

    Decoding a PDU is a no brainer:

    >>> p.decode_pdu('07911326040000F0040B911346610089F60000208062917314080CC8F71D14969741F977FD07')
    {'cnt': 0, 'seq': 0, 'text': u'How are you?', 'fmt': 0, 'pid': 0, 'csca': '+31624000000', 'number': '+31641600986', 'type': 0, 'date': '02/08/26 19:37:41', 'ref': 0}

    This PDU string was taken from redxanela's online decoder default text, you can compare python-messaging's output with redxanela. Check out the method signatures for what options accepts. I need to write a thorough documentation one of this days, this is an initial effort in that way.

    Hope you liked this walkthrough and that python-messaging fits your project needs. I didn't want to bother you with the project's history, you can check it out in the README. Looking forward to any questions/contributions ;-)

    April 15, 2010

    Cursos certificados Java y MySQL  Warp Networks

    CALENDARIO DE CURSOS MAYO-JULIO 2010 - ZARAGOZA

    Warp Networks como partner oficial de Sun Microsystems, imparte formación certificada MySQL y JAVA. También puede proveer a los interesados de vouchers para certificaciones Sun.

    Los cursos se imparten de forma presencial en Zaragoza.

    Apúntate ahora, las plazas son limitadas!

    FORMACIÓN JAVA:

    FORMACIÓN MYSQL:

    INSCRIPCIONES E INFORMACION: training@warp.es / +34 976 392 644

    April 14, 2010

    Y nos quedamos como estamos…  Dani

    Pues si, así es. Nos quedamos tal y como ya estábamos… Como ya comenté en un post anterior, Google va liberando cada cierto tiempo información sobre cuántos usuarios utilizan qué versión de Android. Afortunada o desgraciadamente, esta semana han actualizado las estadísticas. ¿Y qué podemos ver?

    Pues esto:

    Lo que quiere decir que, aproximadamente (y salvando un poco las distancias), las versiones están repartidas casi a partes iguales. Esto provoca que los desarrolladores de aplicaciones para Android todavía no puedan descartar ninguna versión del sistema de Google (excepto la 1.1), y si quieren cierto éxito sin desechar una gran fuente de usuarios y oportunidades, han de soportar todas ellas.

    ¡Estamos de enhorabuena! Hacer tres veces el trabajo para conseguir lo mismo… Esperemos que a partir de ahora Google se tome las cosas con algo más de calma y no saque cuatro versiones distintas del sistema cada año, ayudando a frenar un poco este caos.

    De todas formas, pensándolo bien, todavía no estamos a la par que los desarrolladores web y la compatibilidad entre navegadores, algo es algo O:-)

    April 06, 2010

    OpenStreetMap en Tecnimap  Dani

    Desde que descubrí OpenStreetMap (OSM) hace ya unos años, el proyecto me ha fascinado. Al principio me parecía en realidad una cosa de locos. Llevar adelante un proyecto como la wikipedia, parece que se puede acometer si se tiene a la suficiente gente disponible, pero… ¿un mapa global? ¿Tener a miles de personas con su GPS tomando muestras para luego enviar los datos a internet? ¿y de manera voluntaria? Lo dicho, parece de locos.

    El resultado es que después de cerca de 6 años en marcha, ahora hay más de 230.000 usuarios en el proyecto. Más de 1.300 millones de puntos, y unos mapas con una calidad asombrosa. De hecho, tan asombroso como puede resultar la wikipedia. Sólo con entrar en http://www.openstreetmap.org, uno queda maravillado.

    Además de toda esta información en forma de mapa, OSM ofrece otras formas de acceder a la información, por medio de una API abierta y muy bien documentada. Sobre OSM, su API, su integración con otros servicios como flickr, y el uso de formatos estándar del W3C estaré hablando en una de las sesiones paralelas de Tecnimap.

    El día 8, a las 9:30 de la mañana os veo en “W3C. El Futuro de la Información Pública en la Web” y aqui podéis encontrar ya disponible la presentación.

    Una pena que en el último momento no haya podido entrar en Tecnimap la otra comunicación que Warp tenía preparada. Al menos está disponible la presentación que preparamos en su día para el evento.

    MovID: llave USB para la firma electrónica  Warp Networks

    Warp Networks, con el patrocinio de la Diputación de Teruel, ha desarrollado MovID, una solución llave USB en mano para la firma electrónica.

    MovID incluye, dentro de una llave USB, todo lo necesario para trabajar de forma segura:

    La solución consiste en un almacén hardware de alta seguridad para la firma digital, protegido mediante contraseña, y contra accesos físicos, pudiendo gestionar y acceder a todos los certificados. Además, proporciona un entorno de trabajo itinerante para aplicaciones y documentos de todo tipo.

    Warp ofrece servicios de personalización de la apariencia, de las aplicaciones disponibles y adaptación del entorno de trabajo, siempre manteniendo la premisa del respeto a los estándares, pudiendo acceder a su contenido y funcionalidades desde cualquier ordenador con USB, independientemente del sistema operativo que se utilice.

    Con motivo del Tecnimap 2010 que se celebra en Zaragoza, Warp presentó una ponencia para divulgar la solución desarrollada, destinada principalmente a Administraciones Públicas. Finalmente no será posible realizar la comunicación en el marco Tecnimap, pero se pueden descargar las transparencias desde esta misma entrada.

    Descargar presentación MovID

    Acerca del proyecto:

    El desarrollo de la solución llave USB en mano, con las características descritas, surge debido al requerimiento y especificaciones de la Diputación Provincial de Teruel, y la cofinanciación del Ministerio de Industria, Turismo y Comercio, a través del Plan Avanza.

    April 03, 2010

    reStructuredText directive for google-code-prettify  Pablo

    reStructuredText is teh light markup format, if you never heard about it go check it out. If you are still living in a Docbook world, you would be amazed of what rst and Sphinx can do for your productivity.

    Anyway, for the last articles I needed a prettify solution to publish several code listings. I use django-mingus as my blog engine, it comes with not one but two solutions for this problem. The first one is Pygments, which is very nice but no matter what I tried could not make it work. The second is prettify.js, a javascript module plus some css, that prettifies in the client side at runtime.

    The only problem of this approach is that the code list will not show up prettified in RSS and such. But you suffered the same problem with Pygments anyway unless you inlined the styles.

    The reStructuredText directive

    The code is deadly simple:

    from cgi import escape
    
    from docutils import nodes
    from docutils.parsers.rst import directives, Directive
    
    
    class CodeDirective(Directive):
        """
        reStructuredText directive to show code listings with google-code-prettify
        """
    
        has_content = True
    
        def run(self):
            self.assert_has_content()
            text = '<pre class="prettyprint">%s</pre>' % self._get_escaped_content()
            return [nodes.raw('', text, format='html')]
    
        def _get_escaped_content(self):
            return '\n'.join(map(escape, self.content))
    
    directives.register_directive("code-list", CodeDirective)
    directives.register_directive("sourcecode", CodeDirective)

    It just wraps the code in <pre class="prettyprint"> and </pre>, and that's it. Oh well, it also wanted the content to be escaped, fortunately Python comes with a handy function for it: cgi.escape. I will probably write an article about rst directives soon, there is not that much documentation about it.

    April 01, 2010

    How to test the Subversion odt checking pre-commit hook  Hector

    As I promised, after explaining the pre-commit hook code, here we go with some tips to give it a try. There are two ways of testing this pre-commit hook, both involving an existing Subversion repository:

    1. Commiting to the repository (and thus triggering a new transaction).
    2. Using an existing transaction, through the --revision command line option (intended for testing).

    In both cases, you must install LanguageTool before.

    To create a brand new repository and test it:

    # Download dependencies
    sudo aptitude install subversion
    # Set up local repo, downloading the script to the appropiate directory
    mkdir -p /tmp/srv/svn/foobar
    svnadmin create /tmp/srv/svn/foobar
    wget http://people.warp.es/~xtor/pre-commit-oo.org -O /tmp/srv/svn/foobar/hooks/pre-commit
    chmod a+x /tmp/srv/svn/foobar/hooks/pre-commit
    # Do a local checkout
    mkdir -p /tmp/devel/svn/foobar
    svn checkout file:///tmp/srv/svn/foobar /tmp/devel/svn/foobar
    cd /tmp/devel/svn/foobar
    

    Now, just copy an odt document to /tmp/devel/svn/foobar, add it to the repo and try to commit… You should see the pre-commit hook fail.

    Or, if you have a repository with OpenDocument files commited, you can just call the pre-commit hook as a regular script, with the --revision parameter:

    # Usage: pre-commit path/to/repo --revision rev_num
    /tmp/srv/svn/foobar/hooks/pre-commit /tmp/srv/svn/foobar/ --revision 1
    

    Enjoy it! :)

    March 31, 2010

    Agile and CMMi in small settings  Hector

    If you use agile methodologies, but are trying to benefit from the huge amount of industry knowledge stored in process frameworks like CMMi, you need to pay attention to some important facts about it. These are lessons that we have learnt at Warp while trying to mix agile methodologies with CMMi level 2 and 3 practices.

    CMMi 2 complements agile methodologies

    Agile methodologies focus on development activities, while the first level of the staged CMMi (which in turn is level 2) focus on management. If your team is great developing software, you can still learn a lot of things from CMMi from a management point of view. For example, Measurement and Analysis will help you to define business goals and KPIs.

    The model must be interpreted

    Theorically, the CMMi model is “methodology-agnostic”, but most of the examples you are going to find trying to explain it are assuming waterfall lifecycles or other heavy-weight process customs. You will need to be creative interpreting the model and the examples.

    The model must be fitted to your software lifecycles

    For instance, CMMi defines two process areas for project management: Project Planning and Project Monitoring and Control. It is a perfect fit for a predictive methodology with two clear Planning and Implementation phases, but when you are using an agile approach you will have planning and monitoring each iteration, and sometimes an additional planning phase prior to start working.

    Your projects should be alike

    For processes to be repeatable, and fulfill a lot of the standarization requirements covered by the generic practices, you need to repeat the same kind of projects. For instance, at Warp we do projects that range from ERP/CRM stuff to embedded systems, so we use different requirements and estimation techniques. CMMi wants you to standarize estimation tools and techniques, document every detail, and make sure everybody is aware and uses them. But when the right choice for each project is an ad-hoc technique, you would end up documenting a whole different process for each project!

    Quality, but not at any cost

    An important fact, especially if you are a small custom software development company with about 20 people, is that you need to find a trade-off between fully implementing some practices, like Process and Product Quality Assurance, and how it translates to your cost structure. For example, you will need to relax some practices to avoid hiring PPQA people. (For those not acquainted with CMMi, PPQA is different from “software testing”).

    Implementing CMMi and passing a CMMi appraisal are different things

    CMMi appraisals require a lot of extra work (or plain waste), that you simply don’t need if you are just interested in enhancing the way you work. For example, each time you review a project’s deviation, if the project is on track you will need to record that “you have reviewed the project”, and that “the project is on track”. So instead of adopting common sense management rules, you will be focused on creating Muda!

    To sum up: avoid judging CMMi beforehand, learn about it and just get the good things which add value and minimize waste.

    Playing with QML and DBus (Part 2)  Pablo

    In part 1 I showed how to modify the recipes.qml example in order to show contacts instead of recipes, and a basic skeleton to provide a ListModel with contact data from QT/C++ space.

    In part 2, I am going to show how to retrieve this contact data from a DBus-backed application, Wader in this case, and the required steps to make it work.

    Wader, ModemManager and Betavine Connection Manager

    Wader is a 3G daemon accessible via DBus, written in Python and released under the GPLv2. Wader runs on Linux and OSX and supports more than fifty heterogeneous devices. Wader also happens to implement the ModemManager API.

    ModemManager is a joint effort between the NetworkManager and Wader projects to produce a modern 3G DBus API that can be reused by other applications of the Unix Desktop. You can have a look at the ModemManager specification to get a glimpse of it.

    Betavine Connection Manager is the third generation of Vodafone's open source mobile connect project, hosted at Betavine's site. BCM 3.0 is built upon Wader core, with a brand new user interface and completely integrated in the Linux desktop.

    ModemManager internals

    ModemManager is a heavy user of DBus interfaces, we are going to interact with just four of the ModemManager specification:

    First we need to get a list with the object paths of the devices registered in the system. To do so we will call EnumerateDevices, this will return an array of object paths. We will use the first element in this array for the rest of the operations.

    Once we have the object path, we need to setup the device with the Enable method. This method accepts a boolean indicating whether to enable or disable the device. This method may be interrupted if PIN authentication is enabled, for simplicity we are not going to handle possible SimPinRequired errors. So make sure that authentication is disabled, or else send the PIN with minicom beforehand.

    With the device enabled, we are just two calls away from finishing! We are going to get the list of contacts and messages, and we will correlate them before populating our classes. We tried to provide a set of common operations in ModemManager, and the Contacts and Sms APIs are the best examples. Both provide a List method that will return the contacts or messages list.

    How to wrap a DBus daemon with QT

    Wrapping a DBus daemon with QT could not be any easier thanks to qdbusxml2cpp. This tool will parse the ModemManager interfaces that we are going to use and will generate the necessary boilerplate to make calls to remote objects. First things first, create the following files in ~/devel/git/contacts:

    org.freedesktop.ModemManager.xml

    <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
            "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
    <node name="/org/freedesktop/ModemManager">
        <interface name="org.freedesktop.ModemManager">
            <method name="EnumerateDevices">
                <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QList<QDBusObjectPath>"/>
                <arg name="devicesList" type="ao" direction="out" />
            </method>
        </interface>
    </node>

    org.freedesktop.ModemManager.Modem.xml

    <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
            "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
    <node name="/">
        <interface name="org.freedesktop.ModemManager.Modem">
            <method name="Enable">
                <arg name="enable" type="b" direction="in" />
            </method>
        </interface>
    </node>

    org.freedesktop.ModemManager.Modem.Gsm.Contacts.xml

    <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
            "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
    <node name="/">
        <interface name="org.freedesktop.ModemManager.Modem.Gsm.Contacts">
            <method name="List">
                <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QList<ContactStruct>"/>
                <arg name="contactList" type="a(uss)" direction="out" />
            </method>
        </interface>
    </node>

    org.freedesktop.ModemManager.Modem.Gsm.Sms.xml

    <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
            "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
    <node name="/">
        <interface name="org.freedesktop.ModemManager.Modem.Gsm.Sms">
            <method name="List">
                <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QList<QVariantMap>"/>
                <arg name="smsList" type="a{sv}" direction="out" />
            </method>
        </interface>
    </node>

    This files follow the DBus introspection format and describe the methods available in each interface (only the ones we are going to use actually) and their arguments. If the type is a generic one, we can just use the standard DBus syntax -i.e. 'b' for bool. If on the other hand the response has a custom type, we need to provide an annotation for it with its type plus some boilerplate code so that the QT type system is able to transform in runtime a DBus response to a QT struct/class. We will revisit this point later on.

    Now we are going to create a script that will generate the necessary code to access the ModemManager service. For each of the four interfaces that we are going to interact with, we need to create a proxy to it. The code bellow generates the static code necessary to access them.

    generate-interfaces.sh

    #!/bin/sh
    
    set -e
    
    qdbusxml2cpp -N -c ModemManagerProxy \
                 -p modemmanager_generic.h:modemmanager_generic.cpp \
                 org.freedesktop.ModemManager.xml \
                 org.freedesktop.ModemManager
    
    qdbusxml2cpp -N -c ModemManagerModemProxy \
                 -p modemmanager_modem.h:modemmanager_modem.cpp \
                 org.freedesktop.ModemManager.Modem.xml \
                 org.freedesktop.ModemManager.Modem
    
    qdbusxml2cpp -N -c ModemManagerContactsProxy \
                 -p modemmanager_contacts.h:modemmanager_contacts.cpp \
                 org.freedesktop.ModemManager.Modem.Gsm.Contacts.xml \
                 org.freedesktop.ModemManager.Modem.Gsm.Contacts
    
    qdbusxml2cpp -N -c ModemManagerSmsProxy \
                 -p modemmanager_sms.h:modemmanager_sms.cpp \
                 org.freedesktop.ModemManager.Modem.Gsm.Sms.xml \
                 org.freedesktop.ModemManager.Modem.Gsm.Sms

    This commands generate a class with the -c switch (first argument), contained in a .cpp and .h files (second argument), by reading the given interface description (third argument) and finally the name of the accessed interface as fourth argument. Note the initial switch -N that produces classes with no namespaces, we had to use it as it would not work otherwise. I think it has to do with the different "depths" of the interfaces used here. Check out qdbusxml2cpp documentation for more details.

    Add this new files to contacts.pro:

    TEMPLATE = app
    TARGET = contacts
    DEPENDPATH += .
    INCLUDEPATH += .
    CONFIG += qdbus
    QT += declarative
    
    # Input
    SOURCES += contact.cpp modemmanager_generic.cpp \
               modemmanager_modem.cpp modemmanager_contacts.cpp \
               modemmanager_sms.cpp main.cpp
    HEADERS += contact.h modemmanager_generic.h modemmanager_modem.h \
               modemmanager_contacts.h modemmanager_sms.h
    RESOURCES += contacts.qrc
    sources.files = $$SOURCES $$HEADERS $$RESOURCES contacts.pro contacts.qml
    sources.path = .
    target.path = .
    
    INSTALLS += sources target

    Now add the modemmanager_* headers to main.cpp, generate the interfaces and make sure it compiles after this last changes:

    chmod +x generate-interfaces.sh
    ./generate-interfaces.sh
    /usr/local/Trolltech/Qt-4.7.0/bin/qmake contacts.pro
    make
    

    We are about to access a DBus daemon written in Python, from a small C++ binary that will in turn show the results in a QML application. Interesting mix isn't it?

    We need to obtain the object path of the device first, to do so add this in your main.cpp (right after the QApplication declaration):

    ModemManagerProxy *proxy = new ModemManagerProxy("org.freedesktop.ModemManager",
                                                     "/org/freedesktop/ModemManager",
                                                     QDBusConnection::systemBus(),
                                                     static_cast<QObject*>(&app));
    
    QDBusObjectPath opath;
    QDBusPendingReply<QList<QDBusObjectPath> > reply = proxy->EnumerateDevices();
    reply.waitForFinished();
    if (reply.isError()) {
        qDebug() << reply.error();
        app.exit(1);
    } else {
        opath = reply.value().first();
        qDebug() << opath.path();
    }
    
    ModemManagerModemProxy *modem_proxy = new ModemManagerModemProxy(
                                                     "org.freedesktop.ModemManager",
                                                     opath.path(),
                                                     QDBusConnection::systemBus(),
                                                     proxy);
    QDBusPendingReply<> modem_reply = modem_proxy->Enable(true);
    modem_reply.waitForFinished();
    if (modem_reply.isError()) {
        qDebug() << "Error enabling device: " << modem_reply.error();
        app.exit(1);
    }

    This code instantiates a new object, proxy which is a pointer to the ModemManagerProxy class we've just created in generate-interfaces.sh. The constructor receives the service and object path of the object as first and second arguments, and the system bus is used. Ignore the fourth argument for now.

    We call EnumerateDevices, check that the reply has no errors, and we store the first value of the response in the opath variable. This object path identifies our device in ModemManager and thus will be used in the rest of the tutorial.

    The next step is to enable the device with Enable, ModemManagerModemProxy is instantiated and receives the object path of the device this time. Enable does not return anything, so we only check for errors in the reply object.

    At this point we have an enabled device ready to receive operations, guess what's next? Yep, retrieving the list of contacts and messages, correlate them and populate the data model with it. Add the following code before the main function:

    QList<QObject*> GetContacts(QDBusObjectPath opath)
    {
        ModemManagerContactsProxy *cts_proxy = new ModemManagerContactsProxy(
                                                     "org.freedesktop.ModemManager",
                                                     opath.path(),
                                                     QDBusConnection::systemBus(),
                                                     0);
        ModemManagerSmsProxy *sms_proxy = new ModemManagerSmsProxy(
                                                     "org.freedesktop.ModemManager",
                                                     opath.path(),
                                                     QDBusConnection::systemBus(),
                                                     0);
    
        QList<QObject*> ret;
    
        QList<ContactStruct> contactList;
        QDBusPendingReply<QList<ContactStruct> > cts_reply = cts_proxy->List();
        cts_reply.waitForFinished();
        if (cts_reply.isError()) {
            qDebug() << cts_reply.error();
            return ret;
        } else {
            contactList = cts_reply.value();
        }
    
        QList<QVariantMap> messagesList;
        QDBusPendingReply<QList<QVariantMap> > sms_reply = sms_proxy->List();
        sms_reply.waitForFinished();
        if (sms_reply.isError()) {
            qDebug() << sms_reply.error();
            return ret;
        } else {
            messagesList = sms_reply.value();
        }
    
        const QString picture = QString("qrc:/content/pics/person.png");
    
        foreach(ContactStruct contact, contactList) {
            QList<QString> smsList;
            foreach(QVariantMap sms, messagesList) {
                if (sms.value("number").toString().endsWith(contact.number)) {
                    smsList.append(sms.value("text").toString());
                }
            }
            QString messages = "<html><ul>";
            if (!smsList.size()) {
                messages.append("<li>No messages!</li>");
            } else {
                foreach(QString sms, smsList) {
                    messages.append(QString("<li>%1</li>").arg(sms));
                }
            }
            messages.append("</ul></html>");
            ret.append(new Contact(contact.name, contact.number, messages, picture));
        }
    
        delete cts_proxy;
        delete sms_proxy;
    
        return ret;
    }

    This method accepts the object path of a device, obtains the list of contacts and messages, correlates them and returns a QList<QObject *> object with the data. If you try to compile it as it is, it will fail by several reasons: First ContactStruct (Gsm.Contacts.List return value) is undefined, second we need to register it with the QT type system, and third we need to provide marshalling functions to serialize/deserialize ContactStruct from DBus.

    Create the following file dbustypes.h and import it in main.cpp before the modemmanager_* ones:

    #include <QtDBus>
    
    struct ContactStruct {
         uint index;
         QString name;
         QString number;
    };
    
    Q_DECLARE_METATYPE(ContactStruct);
    Q_DECLARE_METATYPE(QList<ContactStruct>);
    Q_DECLARE_METATYPE(QList<QVariantMap>);
    
    // Marshall the ContactStruct data into a D-BUS argument
    QDBusArgument &operator<<(QDBusArgument &argument, const ContactStruct &mystruct)
    {
         argument.beginStructure();
         argument << mystruct.index << mystruct.name << mystruct.number;
         argument.endStructure();
         return argument;
    }
    
    // Retrieve the ContactStruct data from the D-BUS argument
    const QDBusArgument &operator>>(const QDBusArgument &argument, ContactStruct &mystruct)
    {
         argument.beginStructure();
         argument >> mystruct.index >> mystruct.name >> mystruct.number;
         argument.endStructure();
         return argument;
    }

    Now, before ModemManagerProxy's instantiation, add this lines in main.cpp:

    qDBusRegisterMetaType<ContactStruct>();
    qDBusRegisterMetaType<QList<ContactStruct> >();
    qDBusRegisterMetaType<QList<QVariantMap> >();

    This will register the compound types that we are going to use in the DBus calls, without this lines the project will not compile.

    At this point we are ready to hook GetContacts in main.cpp. Add after the modem_reply block the following lines:

    QList<QObject*> dataList;
    foreach(QObject* contact, GetContacts(opath)) {
        dataList.append(contact);
    }

    This will populate our data model with the DBus data, and we are ready to go:

    make clean
    rm -rf modemmanager_*
    ./generate-interfaces.sh
    /usr/local/Trolltech/Qt-4.7.0/bin/qmake contacts.pro
    make
    ./contacts
    

    Here you can see the end result of part 2, best viewed with Chrome/Firefox 3.6:

    Your Browser does not support the video tag, upgrade to Firefox 3.5+/Chrome

    This is the current source of the tutorial. Next (and last) part of the tutorial will look at retrieving this data at runtime from QML.

    March 30, 2010

    Playing with QML and DBus (Part 1)  Pablo

    How to integrate your DBus app and a QML UI

    In this tutorials I am going to explain the necessary steps to integrate an existing codebase with DBus IPC and a QML (a.k.a. QtQuick) user interface. In this case, I am going to write a QML interface that will query a ModemManager instance running in the background. It will be possible to see the contacts in the SIM card and each contact will have a detail page where you will be able to see all the SMS sent to that contact.

    Preparation

    You need to download QT 4.7 -at the time of writing still an alpha version- and compile it by yourself. Bear in mind that this process might take several hours. Also I recommend you to start using the wonderful QT Creator 2.0 -an alpha version too at the time of writing- as IDE. It has a decent VIM integration and several goodies, like a visual QML editor. Do not even think about it start downloading them now! Note that the QT 4.7 API might have changed since I wrote this, and some parts might need minor fixes. Also make sure to download the most recent version of both QT and QT Creator instead of the alpha versions I had to use.

    Start the project

    First I created a new "Qt Gui" project. I've used the ~/devel/git/contacts path. This will create a bunch of files, some of them need some editing. But first... design is not one of my strong points, so I started by reusing one of the examples included in the documentation, the recipes example:

    cp -R ~/software/qt-everywhere-opensource-src-4.7.0-tp/examples/declarative/listview/* ~/devel/git/contacts
    

    Now remove the following files, as we won't need them:

    cd ~/devel/git/contacts
    rm dynamic.qml highlight.qml itemlist.qml listview.qml sections.qml
    rm -rf dummydata content/ClickAutoRepeating.qml
    

    Move MediaButton to the top level:

    mv content/MediaButton.qml .
    

    Edit the lines starting with "source:" and add content/ to the path to reflect that we just moved it to the top level.

    Download the default image for our contacts:

    wget http://minimoesfuerzo.org/media/photos/person.png -O content/pics/person.png
    

    Rename recipes.qml to contacts.qml:

    mv recipes.qml contacts.qml
    

    Understand what is going on under the hood

    Before we go on, I think it will be good to provide a minimal overview of how recipes.qml works, and what our goal is. Note that this tutorial assumes certain familiarity with QML/QT and related concepts, if you never heard of this technologies, it is a good moment to check out the docs.

    recipes.qml imports "content" at the beginning of the script, and that folder contains a bunch of images and a MediaButton.qml script. A QML script can import other QML scripts and refer to other files. In addition, "dummydata" is also imported and that is where the hardcoded data for recipes resides, check out dummydata/Recipes.qml and look at its structure and attributes.

    So when you run recipes.qml, it imports in turn a bunch of other scripts and one of them happens to be a ListModel that contains the initial data that you get to see. How is that data referenced in recipes.qml then? Very easy, in recipes.qml line 137 you can see that a ListView is defined and has a model named Recipes. A ListView will always have a delegate for painting and showing the model's data. The delegate for this ListView is recipeDelegate.

    recipeDelegate is a Component declared in line 13, and inside the block if you want to show/paint the model's data, you just use the name of the attribute in the model. For example, if with the current model I want to show the title of the recipe, I need to use 'text: title'. Similarly, if I want to show the details of a recipe, I would use 'text: details'.

    So our goal is to get rid of that hardcoded data model, and use a custom one populated by ModemManager via DBus.

    Initial steps

    Instead save the following lines in ~/devel/git/contacts/contacts.pro:
    TEMPLATE = app
    TARGET = contacts
    DEPENDPATH += .
    INCLUDEPATH += .
    CONFIG += qdbus
    QT += declarative
    
    # Input
    SOURCES += main.cpp contact.cpp
    HEADERS += contact.h
    RESOURCES += contacts.qrc
    sources.files = $$SOURCES $$HEADERS $$RESOURCES contacts.pro contacts.qml
    sources.path = .
    target.path = .
    
    INSTALLS += sources target

    This is a minimal QT project template with all the parts that we, initially, need for the task. The target will be a binary named "contacts", for now the project will use main.cpp and contact.cpp as sources, contact.h as headers and will use the QtDBus and QDeclarative libraries for obvious reasons.

    Initial main.cpp implementation:

    #include <QApplication>
    
    #include <qdeclarativeengine.h>
    #include <qdeclarativecontext.h>
    #include <qdeclarative.h>
    #include <qdeclarativeitem.h>
    #include <qdeclarativeview.h>
    
    #include "contact.h"
    
    int main(int argc, char ** argv)
    {
        QApplication app(argc, argv);
    
        const QString picture = QString("qrc:/content/pics/person.png");
        const QString messages = QString("<html><ul><li>No SMS!</li></ul></html>");
    
        QList<QObject*> dataList;
        dataList.append(new Contact("John", "+34243234232", messages, picture));
    
        QDeclarativeView view;
        QDeclarativeContext *ctxt = view.rootContext();
        ctxt->setContextProperty("myModel", QVariant::fromValue(dataList));
    
        view.setSource(QUrl("qrc:contacts.qml"));
        view.show();
    
        return app.exec();
    }

    Initial contact.h:

    #ifndef CONTACT_H
    #define CONTACT_H
    
    #include <QObject>
    
    class Contact : public QObject
    {
        Q_OBJECT
    
        Q_PROPERTY(QString name READ name WRITE setName)
        Q_PROPERTY(QString number READ number WRITE setNumber)
        Q_PROPERTY(QString picture READ picture WRITE setPicture)
        Q_PROPERTY(QString messages READ messages)
    
    public:
        Contact(QObject *parent=0);
        Contact(const QString &name, const QString &number,
                const QString &messages, const QString &picture,
                QObject *parent=0);
    
        QString name() const;
        void setName(const QString &name);
    
        QString number() const;
        void setNumber(const QString &number);
    
        QString picture() const;
        void setPicture(const QString &picture);
    
        QString messages() const;
    
    private:
        QString m_name;
        QString m_number;
        QString m_messages;
        QString m_picture;
    };
    
    #endif // CONTACT_H

    Initial contact.cpp implementation:

    #include "contact.h"
    
    Contact::Contact(QObject *parent)
        : QObject(parent)
    {
    }
    
    Contact::Contact(const QString &name, const QString &number,
                     const QString &messages, const QString &picture,
                     QObject *parent)
        : QObject(parent), m_name(name), m_number(number),
          m_messages(messages), m_picture(picture)
    {
    }
    
    QString Contact::name() const
    {
        return m_name;
    }
    
    void Contact::setName(const QString &name)
    {
        m_name = name;
    }
    
    QString Contact::number() const
    {
        return m_number;
    }
    
    void Contact::setNumber(const QString &number)
    {
        m_number = number;
    }
    
    QString Contact::picture() const
    {
        return m_picture;
    }
    
    void Contact::setPicture(const QString &picture)
    {
        m_picture = picture;
    }
    
    QString Contact::messages() const
    {
        return m_messages;
    }

    What did we just do? We have declared a QObject class, Person, with four properties (name, number, messages, picture) that will be accessed from QML space transparently.

    Modify contacts.qml

    At this point, contacts.qml still has the contents of recipes.qml, and needs to be changed. We are going to change the references to old attributes, to the new ones that we will use.

    • In line 57: Change 'text: title' to 'text: model.name'
    • In line 59: Change 'text: "Ingredients"' to 'text: "Details"'
    • In line 63: Change 'text: ingredients' to 'text: model.name'
    • In line 78: Change 'text: "Method"' to 'text: "List of Messages"'
    • In line 85: Change 'text: method' to 'text: model.messages'
    • In line 137: Change 'model: Recipes' to 'model: myModel'

    To sum up, we have changed the old attributes in the model (title, picture, ingredients and method) to (name, number, messages and picture). And the model name (from Recipes to myModel as that's the name we've used in main.cpp). A saavy reader will have noticed by now that there's no mention of the number attribute in the current contacts.qml file and that's true, to include it we just need this last modification to contacts.qml, line 44 defines the topLayout Row and should look like this after the changes:

    Row {
        id: topLayout
        x: 10; y: 10; height: recipePic.height; width: parent.width
        spacing: 10
    
        Image {
            id: recipePic
            source: model.picture; width: 48; height: 48
        }
    
        Column {
            height: recipePic.height; width: background.width-recipePic.width-20
            spacing: 5
    
            Column {
                Text { id: title; text: model.name; font.bold: true; font.pointSize: 15 }
                Text { id: phone; text: model.number; font.bold: true; font.pointSize: 9 }
            }
    
            Text {
                text: "Details"; font.pointSize: 12; font.bold: true
                opacity: wrapper.detailsOpacity
            }
            Text {
                text: model.name; wrap: true; width: parent.width
                opacity: wrapper.detailsOpacity
            }
        }
    }

    Now is a good time to remove all the outstanding references to the old attributes in the code, I've done the following substitutions in contacts.qml:

    sed -i 's/recipeDelegate/contactDelegate/g' contacts.qml
    sed -i 's/recipePic/contactPic/g' contacts.qml
    sed -i 's/methodTitle/smsListTitle/g' contacts.qml
    

    The last thing we need to do is provide an initial contacts.qrc:

    <!DOCTYPE RCC><RCC version="1.0">
    <qresource>
        <file>contacts.qml</file>
        <file>MediaButton.qml</file>
        <file>content/pics/button.png</file>
        <file>content/pics/button-pressed.png</file>
        <file>content/pics/person.png</file>
        <file>content/pics/moreDown.png</file>
        <file>content/pics/moreUp.png</file>
    </qresource>
    </RCC>

    This file references every QML component/asset that is going to be used in the project. If they are not referenced here, you will get an error message for each time you try to access from QML an asset not included in contacts.qrc.

    At this point, we are ready to compile our project and run our project:

    qmake contacts.pro
    make
    

    Make sure that the qmake you have used is the 4.7 one, otherwise some of the required libraries, such as QtDeclarative* will not be found. Now launch the contacts binary and rejoy!

    Make sure to read part 2 of this post where I will show how to retrieve the contacts and messages from the SIM via ModemManager . This is the current source for the project contacts-part1.tar.gz.

    Updated: I have added a small video showing the current state of the project, so you can have a quick look of what we are building. Thanks Andrew!

    Your Browser does not support the video tag, upgrade to Firefox 3.5+/Chrome

    March 28, 2010

    Subversion pre-commit hook to check OpenDocument, explained  Hector

    In a previous post, I introduced the context for this pre-commit hook. Now I will explain the small Python script which does the work. If you are not familiar with Subversion hooks, you could take a look at this nice Wordaligned’s article before, but the whole idea is fairly easy to understand:

    1. Our pre-commit hook has a transaction as its input.
    2. A transaction is composed of changes to different files.
    3. For each file changed, we check it against a set of rules.
    4. If we found any error, the transaction fails and the commit is aborted.

    So our pre-commit hook has two main classes:

  • SvnTransaction: offers the main check method, and also handles how to know which files are modified in the transaction, dumps their contents, etc.
  • OdtFileChecker: checks an OpenDocument Text file. We convert to text before, so it’s easier to run the other tools in a server. Our example just implements spelling and grammar checking.
  • There is also a small auxiliary function, create_option_parser which creates the argument parser, feeding SvnTransaction with the required parameters.

    So the hook’s main snippet is:

    def main():
       "Executes the pre commmit hook program."
    
       parser = create_option_parser()
    
       try:
          transaction = SvnTransaction(parser.parse_args())
       except:
          parser.print_help()
          return 1
    
       errors = transaction.check()
       sys.stderr.write(transaction.message())
    
       return errors
    
    if __name__ == '__main__':
       import sys
       sys.exit(main())
    

    transaction.check() extracts the files affected and call the actual file checker, storing messages and errors raised to be used afterwards.

       def check(self):
          """"Checks all the files added or changed in the transaction, returning
          the number or errors found."""
    
          errors = 0
    
          for file_name in self._files_changed():
             tmp_file = self._dump_file_content(file_name)
    
             checker = OdtChecker(tmp_file.name)
             errors += checker.check()
             self._message += checker.message()
    
             tmp_file.close()
    
          return errors
    

    Each call to checker.check() checks the file handled by the checker object, using the next tools:

  • odt2txt: converts the OpenDocument Text file into a plain text file.
  • aspell: checks spelling.
  • LanguageTool: checks grammar.
  • Keep in mind that the current version uses Spanish language checks, and also the standard aspell dictionary. For your own usage, you would probably want to create a custom dictionary including terms like VAT, NDA, SPA…

       def check(self):
          "Checks the OpenDocument file, returning the number or errors found."
    
          self._odt2txt()
          self._check_ortography()
          self._check_grammar()
    
          return self._n_spelling_err + self._n_grammar_err
    
       def _odt2txt(self):
          "Converts the OpenDocument Text file into a plain text file."
    
          odt2txt_cmd = '%s %s --width=-1 --output=%s' \
                        % (ODT2TXT, self._path, self._tmp_file)
          odt2txt_msg = getoutput(odt2txt_cmd)
    
       def _check_ortography(self):
          "Checks the plain text file looking for spelling errors with aspell."
    
          check_cmd = 'cat %s | %s --lang=es list | sort | uniq' \
                      % (self._tmp_file, ASPELL)
          self._spelling_err += "\nChecking ortography using %s\n\n" % check_cmd
          self._spelling_err += getoutput(check_cmd) + "\n"
          self._n_spelling_err += len(self._spelling_err)
    
       def _check_grammar(self):
          "Checks the plain text file looking for grammar errors with LanguageTool."
    
          check_cmd = '%s -jar %s --language es -d WHITESPACE_RULE %s' \
                      % (JAVA, LANGUAGETOOL, self._tmp_file)
          self._grammar_err += "\nChecking grammar using %s\n\n" % check_cmd
          self._grammar_err += getoutput(check_cmd) + "\n"
          # 2 leading lines + 1 at the end, and 5 per error
          self._n_grammar_err += (len(self._grammar_err)-3) / 5
    

    The paths to the different tools can be customized at the beginning of the file:

    ASPELL = '/usr/bin/aspell'
    JAVA = '/usr/bin/java'
    LANGUAGETOOL = '/tmp/Language/LanguageTool.jar'
    ODT2TXT = '/usr/bin/odt2txt'
    
    SVNLOOK = '/usr/bin/svnlook'
    

    In a forthcoming post, I will explain two different ways to test this pre-commit hook, so stay tuned ;)

    See also:

  • A Subversion pre-commit hook for spell and grammar checking
  • Grammar checking for OpenOffice.org
  • March 27, 2010

    A Subversion pre-commit hook for spell and grammar checking  Hector

    When a manager is aware of the power of sofware (beyond a couple of self-written clumsy MSOffice macros that nobody else understands), there is a huge amount of power waiting to be unleased :P As a humble software engineer turned into a manager years ago, I would like to show in this post an example of how development workflows can be used to streamline business processes.

    At Warp, we use Trac to manage all the company’s processes, including the offer writing and reviewing. After a new offer has been written, it must pass a peer-review phase, where adherence to standards or technical feasibility is reviewed.

    But sometimes, small spelling or grammatical mistakes are made, even when you are using the built-in OpenOffice.org’s tools, or external ones like LanguageTool. At the same time, these mistakes can be easily detected with just passing the above mentioned tools. So, why bother if you can write a small script to do the work? :)

    We store the offers in a Subversion repository, so we link the files with the related tickets. But the nice thing about this is that we can use Subversion’s pre-commit hooks to check the files before they are actually commited.

    So here is my spell and grammar checking pre-commit hook for Subversion, which I will explain in a later post…

    March 23, 2010

    Grammar checking for OpenOffice.org  Hector

    Disclaimer: despite the title, this post is not related with my previous flex/bison ones :)

    I found a nice app called Language Tool, which is an open source, Java based, grammar checker supporting a lot of different languages, including Spanish.

    You can download it as an OpenOffice.org extension (.oxt file) from its web page. Unzipping this package with unzip you can use it as a command line checker aswell. To check a plain text file written in Spanish, just write:

    $ java -jar LanguageTool.jar --language es file.txt
    

    March 22, 2010

    Flex with cmake  Hector

    CMake supports flex (and bison), allowing you to include these tools easily in your build chain:

    CMakeLists.txt

    find_package(FLEX)
    
    FLEX_TARGET(Scanner  atiza.l  ${CMAKE_CURRENT_BINARY_DIR}/lex.yy.cc)
    
    add_executable(atiza ${FLEX_Scanner_OUTPUTS})
    

    Running find_package(FLEX) successfully allows us to use FLEX_TARGET to build the Parser target, creating the lex.yy.cc file from atiza.l.

    Then we use this file as the input to create the atiza executable, and g++ is ran transparently to build it.

    March 21, 2010

    Simple flex C++ example  Hector

    Simple, working examples are usually the best way to start customizing an existing software tool to fit your needs.

    Today I was testing flex’s C++ generation and unfortunately the provided sample is not working, and it also lacks a ready-to-go Makefile. Some of the error messages look like missing dependencies:

    `cout' undeclared
    . . .
    undefined reference to `yywrap'
    

    The fixes are trivial, so it was easy to get a working flex foobar.l source and Makefile:

    foobar.l

    %option noyywrap c++
    
    %{
    using namespace std;
    
    int mylineno = 0;
    %}
    
    string  \"[^\n"]+\"
    
    ws      [ \t]+
    
    alpha   [A-Za-z]
    dig     [0-9]
    name    ({alpha}|{dig}|\$)({alpha}|{dig}|[_.\-/$])*
    num1    [-+]?{dig}+\.?([eE][-+]?{dig}+)?
    num2    [-+]?{dig}*\.{dig}+([eE][-+]?{dig}+)?
    number  {num1}|{num2}
    
    %%
    
    {ws}    /* skip blanks and tabs */
    
    "/*"    {
            int c;
    
            while((c = yyinput()) != 0)
                {
                if(c == '\n')
                    ++mylineno;
    
                else if(c == '*')
                    {
                    if((c = yyinput()) == '/')
                        break;
                    else
                        unput(c);
                    }
                }
            }
    
    {number}  cout < < "number " << YYText() << '\n';
    
    \n        mylineno++;
    
    {name}    cout << "name " << YYText() << '\n';
    
    {string}  cout << "string " << YYText() << '\n';
    
    %%
    
    int main( int /* argc */, char** /* argv */ ) {
        FlexLexer* lexer = new yyFlexLexer;
        while(lexer->yylex() != 0);
        return 0;
    }
    

    Makefile

    all: lex.yy.o
    	g++ lex.yy.o -o parser -lfl
    
    lex.yy.o: lex.yy.cc
    	g++ -c -o lex.yy.o lex.yy.cc
    
    lex.yy.cc: foobar.l
    	flex++ foobar.l
    
    clean:
    	rm lex.yy.* parser
    

    So here is a nice, working parser to start with :)

    $ make
    flex++ foobar.l
    g++ -c -o lex.yy.o lex.yy.cc
    g++ lex.yy.o -o parser -lfl
    
    $ ./parser
    2524
    number 2524
    dqedwqde
    name dqedwqde
    -1111
    number -1111
    . . .
    

    March 12, 2010

    Testing ntrack  Pablo

    Background

    Until NetworkManager appeared in scene, network management was painful on Linux. Connecting to a wireless network or a VPN involved several command line scripts, editing files with alien syntax, etc. Seriously, it was a joke when compared to what other operating systems had available. So when NetworkManager was released it filled a nice gap in the Linux Desktop, and everyone rejoiced.

    The problem

    In early 2008, some key apps of the Linux desktop were patched to listen to NetworkManager for connectivity status. Applications such as Firefox or pidgin would not work if NetworkManager considered the system wasn't online.

    As you might know, I have been involved in Linux connection managers since late 2006. The software were I have spent most of my energies, Wader, suffered from this. As the connection was established without NetworkManager, the applications that listened to its signals thought they were offline and thus did not work. Some apps had a workaround (like Firefox) while others didn't (pidgin).

    Say you want to connect via a competing connection manager, such as WICD, Firefox & co will not work. Say you are a token ring user, well you are out of luck, NetworkManager has no plans to support token ring. As you can see the current status is not that good if you are not a NetworkManager developer/user.

    The solution

    Thankfully, Alexander Sack stepped up and started working on ntrack after some talks we had in a UDS.

    ntrack is described on its homepage as:

    "ntrack aims to be a lightweight and easy to use library for application developers that want to get events on network online status changes such as online, offline or route changes."

    ntrack does one thing, and excels at it. It just listens for Netlink messages regarding route changes, and will infer if it is connected from them. As you can see, this approach will work regardless of the connection manager or underlying technology used. In order to be online, you must create a route. Thus the responsibility of determining whether we are connected or not resides on a small daemon completely independent of the connection manager. This is a good thing.

    Today I have tested the last ntrack release (0.6) and a patched empathy that listens to ntrack rather than NetworkManager and it worked wonderfully! I tested it with both PPP and NDIS (HSO) dialup and ntrack detected immediately if I was connected or not. Nice job Alexander :)

    Future steps

    We are going to "lobby" for its inclusion on Debian/Ubuntu, and patch those applications listening to NetworkManager to listen to ntrack instead. ntrack has already been accepted in Debian and it will automatically appear in the next Ubuntu version (Lucid+1). Hopefully the rest of the distros will follow, and in 1-2 years we will have recovered from this error.

    March 08, 2010

    Abierta incripción: MySQL 5.1 para administradores de bases de datos  Warp Networks

    Abierta la inscripción para el curso MySQL 5.1 para administradores de bases de datos. En este curso un instructor autorizado MySQL te enseñará como instalar MySQL, crear y ejecutar estrategias de backup, crear procedimientos almacenados de forma segura, y mucho más. Además, el curso incluye la preparación para obtener el Certified MySQL 5.0 DBA.

    El número de plazas limitado. Inscribete ya, y asegura tu formación!

    (*) Informate sobre como subvencionar la inscripción a través de la Fundación Tripartita.

    Otros cursos MySQL con período de inscripción abierto:

    Contacto para consultas e inscripciones: training@warp.es / +34 976 392 644

    March 06, 2010

    Code Monkey like Fritos  Blaxter

    Until now I've never heard about this song, about a code monkey, funny and nice at the same time. The same songwriter, Jonathan Coulton, has a lot more of similar (geek) songs. Awesome.

    Code Monkey get up get coffee
    Code Monkey go to job
    Code Monkey have boring meeting with boring manager Rob
    Rob say Code Monkey very diligent
    but his output stink
    his code not functional or elegant
    what do Code Monkey think
    Code Monkey think maybe manager want to write goddamn login page himself
    Code Monkey not say it out loud
    Code Monkey not crazy just proud

    Code Monkey like Fritos
    Code Monkey like Tab and Mountain Dew
    Code Monkey very simple man
    with big warm fuzzy secret heart
    Code Monkey like you
    Code Monkey like you

    Code Monkey hang around at front desk
    tell you sweater look nice
    Code Monkey offer buy you soda
    bring you cup bring you ice
    you say no thank you for the soda cause
    soda make you fat
    anyway you busy with the telephone
    no time for chat

    Code Monkey have long walk back to cubicle
    he sit down pretend to work
    Code Monkey not thinking so straight
    Code Monkey not feeling so great

    Code Monkey like Fritos
    Code Monkey like Tab and Mountain Dew
    Code Monkey very simple man
    with big warm fuzzy secret heart
    Code Monkey like you
    Code Monkey like you a lot

    Code Monkey have every reason
    to get out this place
    Code Monkey just keep on working
    to see your soft pretty face
    Much rather wake up eat a coffee cake
    Take bath, take nap
    This job fulfilling in creative way
    such a load of crap
    Code Monkey think someday he have everything even pretty girl like you
    Code Monkey just waiting for now
    Code Monkey say someday, somehow

    Code Monkey like Fritos
    Code Monkey like Tab and Mountain Dew
    Code Monkey very simple man
    with big warm fuzzy secret heart
    Code Monkey like you
    Code Monkey like you

    February 23, 2010

    Curso certificado eBox en el CTA de Zaragoza  Warp Networks

    Warp Networks, como partner oficial de formación de eBox Technologies, informa que en el Centro de Tecnologías Avanzadas (CTA) de Zaragoza, se van a impartir dos cursos de la plataforma certificada eBox (marzo 2010). Los cursos, “Administración de sistemas con Ubuntu - Aplicación eBox”, se dirigen a los empleados, desempleados y profesionales independientes interesados en la gestión de redes basada en Linux. Ambos cursos van a impartirse por el equipo docente de Warp Networks, que cuenta con profesores certificados en la plataforma eBox.

    Los cursos están subvencionados al 100% por el Instituto Aragonés de Empleo y se desarrollarán entre el 8 y el 26 de marzo - uno en horario de mañanas, con una duración total de 60 horas y otro en horario de tardes, con una duración total de 48 horas. Ambos cursos se imparte en castellano e incluyen un examen de certificación. Los alumnos interesados pueden solicitar, hasta el 26 de febrero, su preinscripción.

    Estos cursos están dirigidos a profesionales con conocimientos básicos sobre la gestión de redes informáticas. Por ejemplo, administradores de sistemas Windows que estén interesados en conseguir de manera rápida experiencia en la gestión de redes informáticas con sistemas basados en Linux. Después de recibir estos cursos, los estudiantes serán capaces de entender conceptos sobre gestión de equipos de red y de crear las redes de pequeñas y medianas empresas mediante la plataforma eBox.

    Los cursos incluyen un amplio conjunto de ejercicios prácticos con la plataforma eBox, todas ellas llevadas a cabo en un entorno virtual instalado en el PC de cada estudiante. El programa detallado del curso está disponible en la página web del CTA.

    El número máximo de estudiantes aceptados para cada curso será de 15 y los estudiantes serán seleccionados en base a su currículum vítae y un examen de conocimientos previos, que se realizará el próximo 1 de marzo en las instalaciones del CTA.

    February 14, 2010

    Wader-UI prototyping with Qt and D-BUS  Hector

    Wader, and Betavine Connection Manager since 3.0, your favorite cellnet connection managers, sport a nice GTK+ user interface completely decoupled from their core functionality, which runs as a standalone component, using D-BUS for IPC.

    As one of my regular hacking activities, this weekend I decided to take a look at Qt’s Python bindings, and try to build a proof-of-concept Qt Wader UI interacting with a core using D-BUS.

    I tried the two existing Qt Python bindings, Riverbank’s PyQt4 (GPL) and Nokia’s PySide (LGPL), concluding that, at the moment, the former offers a more mature and reliable alternative.

    The first step was reproducing Wader main window with Qt Designer. Here is my first try :P

    After some minutes tweaking the interface, I was able to remove most of the ugliness

    I played with both QtUiTools’ UiLoader (UI built in runtime) and Python uic (UI built in “compile” time), getting a neat MainWindow class generated with the user interface compiler. I prefer this approach because it allows subclassing the MainWindow, and so it gives me a cleaner code with the autoconnection features (e.g. on_connButton_clicked):

    class MainWindow(QMainWindow, Ui_MainWindow):
    
    	def __init__(self, parent=None):
    		super(MainWindow, self).__init__(parent)
    		self.setupUi(self)
    
    	def on_connButton_clicked(self):
    		print "Connect button clicked"
    		self.dbus.screenOff()
    

    Then I created a simple class to bridge between D-BUS and the UI. I implemented two simple features: a call to a method which turns the screen off, and a handler for the screensaver activation signal. This way I was able to test D-BUS methods and signals with a single action :) The code worked with both Qt and Glib main loops:

    class DBusBridge():
    
    	def __init__(self):
    
    		self.mainloop = dbus.mainloop.qt.DBusQtMainLoop(set_as_default=True)
    
    		self.sessionBus = dbus.SessionBus()
    		self.powerdevil = self.sessionBus.get_object('org.freedesktop.PowerManagement', '/modules/powerdevil')
    		self.screensaver = self.sessionBus.get_object('org.freedesktop.ScreenSaver', '/ScreenSaver')
    
    		dbus.set_default_main_loop(self.mainloop)
    
    		self.screensaver.connect_to_signal('ActiveChanged', self.screensaverActivated, sender_keyword='sender', destination_keyword='dest', interface_keyword='interface', member_keyword='member', path_keyword='path', message_keyword='message')
    
    		print "D-BUS bridge initialized"
    
    	# D-BUS method call
    	def screenOff(self):
    		self.powerdevil.turnOffScreen(dbus_interface='org.kde.PowerDevil')
    
    	# D-BUS signal handler
    	def screensaverActivated(self, *args, **kwargs):
    		print "got signal from %s" % kwargs
    		print kwargs['message'].get_args_list()
    

    So here is the main program code: it just creates the D-BUS bridge and the main window, shows it, and then lets main loop to take control:

    from PyQt4 import *
    from MainWindow import *
    from DBusBridge import *
    
    import sys
    
    if __name__ == "__main__":
    	app = QApplication(sys.argv)
    
    	bridge = DBusBridge()
    
    	mainWindow = MainWindow(bridge)
    	mainWindow.show()
    
    	sys.exit(app.exec_())
    

    To be able to link the connect button with the D-BUS method, we pass bridge as a parameter to MainWindow’s constructor, and then add the call in on_connButton_clicked:

    from PyQt4.QtGui import *
    from Ui_MainWindow import *
    
    class MainWindow(QMainWindow, Ui_MainWindow):
    
    	def __init__(self, bridge, parent=None):
    		super(MainWindow, self).__init__(parent)
    		self.setupUi(self)
    		self.dbus = bridge
    
    	def on_connButton_clicked(self):
    		print "Connect button clicked"
    		self.dbus.screenOff()
    

    To conclude, some interesting tutorials and examples about Python and D-BUS:

  • PyKDE D-BUS Tutorial: powerdevil stuff and method invocation example (Qt and KDE centric)
  • D-BUS Reactor: includes a signal handler example and a nice step by step explanation
  • Python-DBUS Tutorial
  • PySide D-BUS integration example
  • February 12, 2010

    Review of Android OS looking at the Top 5 apps in the Market  Jorge

    As a heavy user of my Android powered mobile phone, I’ve detected some ‘must to have’ improvements, not yet officialy developed by Google.

    A possible solution to these Android lacks, could be installing the Top 5 paid apps in the Market, but it isn’t the final solution whatsoever. Let’s see some of the top apps in the market:

    Google, please, listen to my prayers… or you could paid Warp to do the job ;)

    Como mejorar las reuniones de trabajo  Jorge

    Hace poco leí un artículo que sugería diferentes acciones para mejorar y hacer más productiva una reunión de trabajo. Este artículo me llevo a otro, este a otro, y a otro…

    Lo cierto es que recopilé unos cuantas sugerencias que luego contaré, y que deberíamos aplicar por Warp, pero también me di cuenta de que, por lo visto, hay dos tipos de productividad en las reuniones:

    Yo me quedo con la segunda, ya que la primera, aunque no dediques ni 10 minutos, pueden significar 10 minutos tirados a la basura.

    Las ideas que me han parecido más significativas para sacar provecho de una reunión son las siguientes:

    January 18, 2010

    Spotify is the new Napster  Koke

    I was searching for a solution to a small Spotify bug and found this user praise: Spotify is what napster was for ten years ago.

    Then another user replied:

    how do you mean?

    ten years ago Napster was an illegal channel for sharing and downloading music, built p2p.

    are you ironic and i missed the punchline?

    Fair enough. But still this guy had a point. Spotify is not the same thing as Napster, but another milestone in the Music Industry transformation.

    Napster started a revolution. I’m not talking about piracy, I’m talking about spreading the message that the current formats were obsolete and people had different musical needs. The success of the iTunes Store is a good proof of that.

    Spotify, as I see it, is the beginning of the end of that revolution. Say goodbye to useless CDs and DVDs. Goodbye to paying for an album with a couple of good songs and crap for filling.

    The next logical step would be a Spotify for video.

    The only thing I’ve seen coming from the (music and movie) industry in the last 10 years has been the message that piracy was illegal, and immoral, and that it would kill the music and movies. Maybe they have a point, maybe not.

    Recently, they’ve been trying to ban download sites in Spain. What then? Without P2P:

    I found a quote yesterday that summed it up pretty well:

    “People don’t go out of their way to pirate movies and TV programs; they’re not intrinsically bad people. They do it because often it’s quicker and easier than legitimate means. The quicker the film and TV industries recognise this and make it as easy to buy legal content as it is to download illegitimate content, the more likely they are to stem the flow.”

    But I believe if they shut up for a minute and listen, they’d realize there’s people willing to pay, but they are not selling to them.

    Related posts

    Datos y no especulaciones  Dani

    El día 5 de Enero (como regalo de la noche de Reyes, dirán algunos), Google presentó su propio terminal Android: Nexus One. Este móvil se lanzó con su propia versión de la plataforma Android, la 2.1.

    Durante estos días se ha especulado sobre la cantidad de terminales vendidos (~20.000) en los primeros días, pero no se sabe a ciencia cierta cual es la cuota real de estos terminales o lo que han supuesto sobre el total del mercado.

    Aparte, Google lanzó como recurso de programación el Android Dashboard, una página que muestra cual es la cuota del mercado Android, diferenciado por versiones del SDK. Los cálculos se realizan desde quince días antes de la fecha indicada, y muestra un sencillo gráfico para que los programadores calculen si les interesa o no soportar determinada plataforma.

    Vista actual del Android Dashboard

    Hoy muestra como fecha final de análisis, la del día anterior a la presentación de Nexus One, lo que quiere decir que pronto crecerá el sector de la plataforma 2.1, y veremos cuál es el impacto real de este pequeño aparatito.

    January 14, 2010

    Android no significa “garantía de éxito” (II)  Dani

    Ha pasado algo más de un mes desde el último post. No quería que hubiera pasado tanto, pero ha sido un mes Android bastante interesante. Han salidos nuevos dispositivos, aplicaciones interesantes, juegos divertidísimos, pero como se quedó colgando en el post anterior: “Android no significa garantía de éxito”.

    El por qué tiene un nombre propio: Samsung Galaxy. Cualquiera que vea las prestaciones del aparato puede quedarse bastante impresionado: buena cámara, buenísima pantalla, batería de gran duración… pero, ¿y qué tal se comporta Android? ¿qué tal está integrado? ¿cómo es que no tiene esa serigrafía que dice “with Google”?

    Bien, empecemos con un poco de historia. Este terminal fue puesto a la venta (aproximadamente) en Julio de 2008. Quizá muchos usuarios no tenían muy claro entonces de lo que era aquello de “with Google” pero la explicación es sencilla: ” ‘with Google’ marca aquellos dispositivos desarrollados conjuntamente con Google, y los cuales dispondrán de actualizaciones OTA”. OTA significa ‘over-the-air’ y quiere decir que estos móviles pueden actualizarse descargandose un archivo dentro del móvil y reiniciando. Fin de complicaciones.

    ¿Y si no tienes un móvil ‘with Google’? Pues entonces pasará como con Samsung Galaxy, que has de disponer de un paquete de software provisto por el fabricante que te permitirá actualizar tu terminal. Aquí empieza el calvario de Galaxy. El software provisto por Samsung:

    Aunque la aplicación para actualizar el teléfono, no funcione porque no lo soporta, Samsung ha ido colgando firmwares en su web durante meses. Y la gente los ha usado… pero para ello han tenido que fabricarse una herramienta que pudiera “inyectar” las actualizaciones (más que necesarias) al teléfono.

    Actualizar un buen móvil puede ser una tarea casi innecesaria, puede pensar cualquiera, pero Galaxy habrá tenido entre 7 y 10 firmwares distintos. Con éxitos y fracasos a partes bastante desiguales. En uno de los más sonados firmwares rotos, la fantástica batería que en reposo debería durar 450 horas, duraba 4 aproximadamente. Una centena de veces menos que lo que afirmaba el fabricante! Para asustarse…

    Pero esto fue un suma y sigue:

    ¿Ocurre todo esto en los terminales de HTC? Pues no lo sé ciertamente, pero lo dudo, porque se han desarrollado ‘with Google’ ;-)

    Mirando las tripas que corren dentro del móvil, también se pueden observar procesos extras. Por ejemplo está el que ayuda a la comunicación con la suite de la que antes hablábamos… que lógicamente no funciona, por lo que sólo sirve para gastar batería del terminal. O buscando por las trazas del sistema, puedes encontrar “explosiones” de demonios del sistema operativo, o incluso de la capa de telefonía.

    Si no supiéramos que es Android, y fuera un firmware X desarrollado por el fabricante, este terminal no tendría más de un 2 sobre 10 en su calificación. Y es lo que tienen las cosas a medio hacer, que por muy buen hardware y software base que exista detrás, si no hay una cuidadosa labor de integración entre ambas partes, no sirve de nada.

    Por suerte, las cosas cambiaron hace muy pocas semanas con la aparición de dos nuevos firmwares (abreviados como IK4 e IK5) que resuelven el 90% de los problemas… pero segun los rumores que corren, Galaxy no tendrá actualizaciones posteriores a Android 1.5, cuando la mayoría del resto de terminales si.

    Por tanto, por mucho Android que se pueda disfrutar, queda un sabor bastante descafeinado y muy lejos de ser un terminal “de éxito”. Aunque de todas formas, no tiene por qué ser todo tan malo en el mundo de Android si uno no va de la mano de Google, ¿no?

    January 11, 2010

    Practical Reporting with Ruby and Rails  Blaxter

    practical reporting with ruby and railsPractical Reporting with Ruby and Rails es un libro escrito por David Berube sobre reporting con la ayuda de ruby. Se compone de varios capítulos que van desde generar un gráfico con gruff, generar xml/csv/pdf, importaciones desde feeds, hojas de cálculo o Microsoft Office. Todo esto suena bastante bien, pero es el propio carácter del libro, practical, que hace que pierda bastante puntos por su simpleza y falta de profundidad a la hora de tocar los distintos temas.

    Es un libro realmente bueno si lo lees con las expectativas apropiadas, y puede llegar a ser un auténtico fiasco en otros casos. En mi particular caso fue el último escenario.

    Ya es la segunda vez que me pasa con libros de esta editorial, Apress. Son libros bien escritos, tratando muchos temas, pero con un nivel bastante bajo en cuanto a profundidad de conceptos y nivel requerido. Esto no es malo, por supuesto, simplemente es algo a tener en cuenta para saber si es un libro que te reportará algún beneficio y satisfacción.

    Cada capítulo suele seguir la siguiente estructura: (1) Puesta en contexto del problema a resolver, (2) introducción de herramientas a usar, (3) trozo de código, (4) explicación del código. Donde las dos últimas partes suelen ser prácticamente la totalidad del capítulo. El código suele ser bastante simple y sencillo de entender, pero a pesar de ello tendremos luego un considerable número de páginas para explicar obviedades para cualquiera que haya programado mínimamente en ruby o en reporting en general. Si a esto añadimos que el libro no es muy extenso en páginas (280 aprox.) tenemos que se podría resumir el libro en un listado de referencias a herramientas usadas y 5 o 6 snippets de código interesantes de no más de 200 líneas.

    De todas formas, para alguien que esté interesado en estos temas (reporting) y que no tenga mucha experiencia (en reporting, en ruby o en ambos), es un libro bastante bueno, bien redactado, bien explicado (demasiado) y de rápida lectura. En otros casos no lo recomendaría, y personalmente no me ha gustado mucho que digamos.

    Formación JAVA y MySQL en Zaragoza. Calendario 2010.  Warp Networks

    Os informamos de los cursos públicos JAVA y MySQL, que se van a impartir en el primer semestre de 2010, en Zaragoza.

    Warp Networks como partner oficial de Sun Microsystems, imparte formación certificada MySQL y JAVA.

    También puede proveer a los interesados de vouchers para certificaciones Sun.

    FORMACIÓN JAVA EN ZARAGOZA:

    FORMACIÓN MYSQL EN ZARAGOZA:

    INSCRIPCIONES E INFORMACION: training@warp.es

    January 06, 2010

    My 2009 in pictures  Koke

    Looking back at 2009, it seems I haven’t been taking so many pictures as I did in 2008, but I see more variety. Here are some of those, some I feel proud of, some bring some good memories and some just make me smile

    Most Commented Posts

    December 29, 2009

    minimoesfuerzo.org site details  Pablo

    I got interested in Django around mid 2007 (0.96 timeline), I quickly built a django blog following a known early tutorial in the matter. It was lean and fast, but some basic features were missing.

    Around that time Nat released django-basic-blog (now a part of django-basic-apps), it offered pretty much what you need from a basic blog, but still I wanted to do something on my own.

    I started a couple of half-baked attempts before I lost all interest, it was fun but I couldn't help but thinking that I was reimplementing other people's apps. That's when django-mingus appeared on my radar.

    django-mingus

    Mingus is a blog engine leveraging reusable apps for all its features, its author explained the apps that power django-mingus in great detail.

    I quickly knew that this is what I intended to build in first place, so I just forked it and started customizing it.

    Customizations

    Apart from some CSS tweaks, I was generally pleased with the feature set provided by Mingus. The only grip that I initially had is that Mingus (as Wordpress IIRC) sports both "Categories" and "Tags", I didn't quite like the overlap between them so I removed all the categories uses by tags'. I added restructuredtext support too as that's my favourite lightweight markup. I also removed the quoteme related functionality, as I didn't picture myself using it much.

    Deployment

    This site is deployed with mod_wsgi and it was quite easy, mod_wsgi should be the blessed deployment method rather than mod_python by a mile!

    December 20, 2009

    boost::asio, synchronous read with timeout  Blaxter

    The boost::asio (which means asynchronous input/output) library, is quite powerful library for asynchronous i/o, but it could be a bit difficult at first to figure out how to do a normal synchronous read. So, as a reminder for my future-me, and for you, this snippet it'll be very useful to accomplish that. Probably there will be another ways for doing that, but this is how I managed to do it:

    using namespace boost::asio;
    using namespace boost::system;
    using boost::optional;
     
    ip::tcp::socket _socket; // it could be another kind of socket, not only ip::tcp
     
    /**
     * Dumb function to be used as handler argument and save the error_code
     * into a pointer
     *
     * e.g.: boost::bind( &set_result, some_pointer, _1 )
     */
    void set_result( optional<error_code>* a, error_code b )
    {
      a->reset( b );
    }
     
    #define TIMEOUT 60
    /**
     * it uses _socket
     * if timeout happends throw a system_error exception
     */
    template<typename MutableBufferSequence>
    optional<error_code> read_with_timeout(
        const MutableBufferSequence& buffer
      ) throw( system_error )
    {
      optional<error_code> timer_result;
      optional<error_code> read_result;
     
      deadline_timer timer( _socket.io_service() );
     
      timer.expires_from_now( seconds(TIMEOUT) );
      timer.async_wait( boost::bind(&set_result, &timer_result, _1) );
     
      boost::asio::async_read(
          _socket,
          buffer,
          boost::asio::transfer_at_least( buffer_size_helper(buffer) ),
          boost::bind( &set_result, &read_result, _1 )
        );
     
      _socket.io_service().reset();
     
      while ( _socket.io_service().run_one() )
      {
        if ( read_result )
        {
          timer.cancel();
        }
        else if ( timer_result )
        {
          _socket.cancel();
          throw system_error(
              error_code( errc::timed_out, get_generic_category() )
            );
        }
      }
      return read_result;
    }
     

    I hope it will be useful, have fun.

    December 09, 2009

    Probando la fotografía réflex digital  Jorge

    Ya me he decidido a aprender un poco más en serio sobre fotografía, y para hacerlo… empezaré a usar la cámara que me han regalado hace poco, una Nikon D90.

    Estas son las primeras fotos que hice en modo automático, sin complicaciones vamos:

    Powered by Flickr Gallery

    December 07, 2009

    Android no significa “garantía de éxito” (I)  Dani

    Hablar de Android hoy en día ya no resulta raro para casi nadie. Hace dos años y un mes, Google desveló el proyecto en el que estaban trabajando desde hacía ya tiempo: un sistema operativo para móviles basado en GNU/Linux. Ya entonces, y antes de que apareciera ningún dispositivo móvil liberaron un juego de bibliotecas y una extensa documentación para el desarrollo de aplicaciones. Desde entonces y hasta casi un año después, cuando apareció el HTC Dream (el primer terminal Android), la gente ya había podido comenzar a desarrollar aplicaciones.

    ¿Cuál fue la mayor consecuencia de una acción así? Sencillo, facilitar el desarrollo de aplicaciones significó que los primeros terminales que salieron al mercado ya podían disponer de un gran número de aplicaciones.

    Además del anuncio con el que comenzaba toda esta historia, se le dio la puntilla al proyecto: “La mayor parte de Android será un proyecto liberado bajo la licencia Apache 2.0″. Fantástico. Sencillamente, era díficil pedir más. Y comenzaron a dedicar recursos. Un sitio, un repositorio, listas de correo, etc.

    En las listas de correo se pueden encontrar fácilmente las respuestas de ingenieros de Google. Uno de los que tienen más dedicación (por ser exactamente este su trabajo) es Jean Baptiste Queru, quien aguanta todas las preguntas que tiene la comunidad, y encima trabaja de sol a sol por que todo el mundo pueda construir la versión deseada de Android. Su trabajo es admirable. Otra de sus grandes tareas es mantener una sincronía entre los repositorios de Android; ya que existen dos repositorios: los internos, sobre los que trabaja Google, y los externos, para la comunidad. Tanto los arreglos que la comunidad envía al proyecto, como las nuevas características desarrolladas, son transferidas de un repositorio a otro por él. También existen rumores de la fusión entre ambos repositorios, aunque parece difícil que ocurra, y más cuando (por poner un ejemplo) la publicación del código de Android 2.0 se hizo solo dos semanas más tarde de la presentación oficial.

    Aparte de todo este esfuerzo para la comunidad, Google también (es obvio) tiene sus acuerdos/contratos con los fabricantes para poder desarrollar terminales que funcionen impecablemente con este sistema operativo.

    Resumiendo un poco algunos de los detalles del proyecto, tenemos:

    Pero, si tenemos todo este material en nuestras manos, y todo parece tan perfecto, ¿por qué no es “garantía de éxito”?

    December 02, 2009

    Mostrar iconos en los botones y menús de gnome  Blaxter

    En la última versión de gnome, han decidido poner por defecto que no salgan iconos ni en los botones, ni en los menús. Hasta ahora, tenías iconos en todos lados, esto implicaba que los botones eran siempre más grandes de los normal (por incluir dentro de ellos el icono pertinente) lo que hacia que en general, en gnome, las cosas fuesen como más grandes. Con este cambio los layouts van a ser más compactos, aunque también más rancios y menos usuables, IMHO.

    No me gusta nada este cambio, más vale una imagen que mil palabras, teniendo iconos vas más rápido porque los reconoces y no necesitas leer cosas, aparte que uno de los aspectos que más me gusta de gnome es esa sensación de que todo es grandote :) . Al menos volver a tenerlos es tarea simple, activa /desktop/gnome/interface/buttons_have_icons y /desktop/gnome/interface/menus_have_icons en el editor de configuración de gnome y ya los tendrás.

    $ gconftool-2 --type bool --set /desktop/gnome/interface/buttons_have_icons true
    $ gconftool-2 --type bool --set /desktop/gnome/interface/menus_have_icons true

    December 01, 2009

    How we track outbound links with Google Analytics Events API  Koke

    eBox Platform homepage

    Since the redesign of eBox platform, our bounce rate increased dramatically. After a short investigation, it made sense: our new website was just the homepage and news, and the rest of the content was on different domains (trac, eBox Technologies, …).

    So our bounces were either real bounces, or people visiting our other sites (which I wouldn’t count as bounces).

    My solution: track outgoing links.

    I searched for a solution and found this article, but it wasn’t exactly what I wanted.

    It’s a good first approach, but tracking external links as pageviews makes the analytics reports more confusing. Events Tracking API to the rescue! This API was conceived to track actions that don’t match a page view, like video plays and other application interactions.

    So, with events we could track our exits separately, get the information we need, and get a more accurate Bounce rate.

    The extra code:

    The original article used rel="external" to mark the links to track. There is an easiest way: searching for absolute URLS in the href attribute. Also, I’m using the action parameter to differentiate between internal (our other websites) and external (facebook, twitter,…) links.

    What we are tracking, and will be able to see on the Analytics reports is:

    Warning: your bounce rate will probably drop by tracking events. For us, it reflects our visits more accurately, but that might not be your case.

    This is what google has to say on bounce rate impact:

    In general, a “bounce” is described as a single-page visit to your site. In Analytics, a bounce is calculated specifically as a session that triggers only a single GIF request, such as when a user comes to a single page on your website and then exits without causing any other request to the Analytics server for that session. However, if you implement Event Tracking for your site, you might notice a change in bounce rate metrics for those pages where Event Tracking is present. This is because Event Tracking, like page tracking is classified as an interaction request.

    For example, suppose you have a page with a video player where the bounce rate is historically high, and you have not implemented Event Tracking for the page. If you subsequently set up Event Tracking for the player, you might notice a decrease in the bounce rate for that page, because Analytics will record user interaction with the player and send that interaction to the server as an additional GIF request. Thus, even though the same percentage of visitors to the page might still exit without viewing any other page on your site, their interaction with the video player triggers Event Tracking calls, which disqualifies their visit as a bounce.

    In this way, “bounces” for your event-enabled pages means something slightly different: a single-page visit that includes no user interaction on tracked events.

    Related posts

    November 29, 2009

    Is it… me?  Hector

    I have completed BBC’s The Big Personality Test.

    You just need to create an account, and after answering some questions, you will get your Big Five personality traits, with your scores for Openness, Conscientiousness, Extroversion, Agreeableness and Extroversion.

    These are my results:

    Openness

    You scored 4.5 out of 5 for Openness

    You scored high on Openness.

    This trait is sometimes known as ‘Openness to experience’. People with scores like yours tend to be imaginative and curious about a wide range of things, from appreciating different art forms to exploring new places, cultures and foods.

    Generating lots of imaginative ideas probably comes very easily to you. However, it’s likely that you are prone to daydreaming too.

    Innovators, investigators and creators often score highly on this trait. It has also been suggested that Openness is related to a person’s likelihood to hold unusual beliefs. Do you enjoy the odd conspiracy

    Conscientiousness

    You scored 4.2 out of 5 for Conscientiousness

    You scored high on Conscientiousness.

    Conscientiousness describes how dependable, organised and hard-working a person is likely to be. This may be the reason why, of all the personality traits, Conscientiousness is the most consistent indicator of job success. People with scores like yours tend to be extremely dependable and organised. It’s likely that your life is well-planned and you approach things in a thorough manner.

    At work, you probably approach tasks methodically and thoroughly, with everything in its right place.

    Some studies have shown the more Conscientious an individual is, the more disciplined they are likely to be about exercise and diet.

    Extroversion

    You scored 3.1 out of 5 for Extroversion

    You scored medium on Extroversion.

    Extroversion is characterised by positive emotions and the tendency to seek out pleasure-stimulating or risk-taking activities. People with scores like yours are likely to be comfortable meeting new people and will usually enjoy social occasions.

    You are likely to have a generally positive outlook on life and may display a greater gift for leadership compared to those with low scores on this trait.

    People with high Extroversion are more likely to lead risky lifestyles and take greater risks in pursuit of rewards. Health studies have shown they are more likely to smoke and less likely to get enough sleep than people who score less highly on this trait.

    Agreeableness

    You scored 4 out of 5 for Agreeableness

    You scored high on Agreeableness.

    Agreeableness measures how sympathetic and considerate a person is likely to be. People with scores like yours are likely to find it very easy to get along with other people. You probably find that you are sensitive to the feelings of others and that people find it easy to warm to you.

    As a ‘people person’ you will probably be very comfortable in situations that require teamwork.

    Neuroticism

    You scored 3.9 out of 5 for Neuroticism

    You scored high on Neuroticism.

    In the context of the Big Five personality traits, the term ‘Neuroticism’ relates to a person’s response to threatening or stressful situations. People with scores like yours may find that they often feel tense or anxious, and may well experience changeable moods.

    Some scientists have suggested that Neuroticism was beneficial in evolutionary terms. Early man may have found it advantageous to live in a population where certain individuals had a high sensitivity to threats to the group’s survival.

    There is evidence to suggest that Neuroticism, when combined with high scores in personality traits such as Conscientiousness, can result in a powerful work ethic and a will to succeed.

    Mostrar notificaciones emergentes desde la consola  Blaxter

    A veces es útil mostrar algún tipo de notificación gráfica para informarte, por ejemplo, de cuándo se ha terminado una tarea. Las notificaciones emergentes de Ubuntu, añadidas hace un par de versiones, son una muy buena opción.

    Dos opciones, (1) instalarnos esta librería de perl libnet-dbus-perl, y nos ponemos esta función en nuestro ~/.bashrc

    function notify()
    {
        perl -e "use Net::DBus; my \$sessionBus = Net::DBus->session; my \$notificat
    ionsService = \$sessionBus->get_service('org.freedesktop.Notifications'); my \$n
    otificationsObject = \$notificationsService->get_object('/org/freedesktop/Notifi
    cations', 'org.freedesktop.Notifications'); my \$notificationId; \$notificationI
    d = \$notificationsObject->Notify(shift, 0, '', '$1', '$2', [], {}, -1);"
    }
    $ notify foooooooo baaarrr

    notify

    El primer parámetro es el título y el segundo el contenido.

    Otra forma (2) todavía más fácil es instalarnos el paquete libnotify-bin, el cual contiene el binario notify-send que hace justamente esto. Probablemente la primera solución nos servirá para cualquier distro, mientras que la segunda solo en debian, ubuntu y derivados. Ambas funcionan exactamente igual y sin problemas.

    November 27, 2009

    Redesigning eBox homepage  Koke

    It’s been 3 weeks since we launched the new eBox Platform homepage, and I wanted to share the different steps through the redesign.

    Do you like the new design?

    Do you like any of the previous steps better?

    Do you have any suggestions?

    Related posts

    November 23, 2009

    How to know, in ruby, which methods have been added and by whom?  Blaxter

    If you are not very careful, monkeypatching could be very harmful. One thing to remember is that you should never override a method to add funcionality, for those kind of thinks you must use alias chain method pattern, a safer way of doing that.

    For the rest of the monkeypatching, i.e. add new methods, you could debug them really easy with something like this:

    class Class
       def method_added(method_name)
          puts "#{method_name} added to #{self}, callstack:"
          puts caller.map{|line| "\t#{line}" }.join("\n")
       end
    end

    You can always add more code to filter by class or by method's name. Let's see an example:

    $ more example.rb
    require 'date'
    require 'time'
     
    class Class
       def method_added(method_name)
          return if %w(method_added).include? method_name.to_s
          puts "#{method_name} added to #{self}, callstack:"
          puts caller.map{|line| "\t#{line}" }.join("\n")
       end
    end
     
    class Time
       def to_date
          Date.ordinal self.year, self.yday
       end
    end
     
    class Date
       def to_time
          Time.parse self.to_s
       end
    end
     
    raise "to_date not working" unless
       Time.now.to_date == Date.today
    raise "to time not working" unless
       Time.now.to_date.to_time == Date.today.to_time

    The output will be:

    $ ruby example.rb
    to_date added to Time, callstack:
    	example.rb:13
    to_time added to Date, callstack:
    	example.rb:19

    Nice, isn't it?. Remember to be carefull with your monkeypatching, with great power comes great responsibility, it's just a tool, neither magic nor the panacea.

    November 21, 2009

    Comandos Windows para linuxeros: kill -9, ps, route, servicios  Blaxter

    Si por cuestiones del destino tienes la mala suerte de tener que lidiar con este sistema operativo, he aquí unas pequeñas ayudas para típicas tareas a realizar, traduciendo desde comandos *nix.

    Espero que nunca tengas que usarlos, por el bien de tu salud mental. Si llega a ser el caso, y no hay forma de salir del atolladero sin matar gente, te recomiendo usar alguna que otra ayuda en este hostil, adverso y aciago entorno.

    Hola qué tal  Blaxter

    Cuando no apetece escribir, simplemente no apetece escribir, qué le vamos a hacer. Después de mucho tiempo creo que toca ya volver a dar mal por estos lares. Van ya cerca de 5 años con el blog, y aunque últimamente las redes sociales y el microblogging han quitado muchos usuarios a los blogs (posiblemente todos aquellos que creaban un blog y a los cuatro post lo dejaban abandonado), a la blogosfera la veo mejor que nunca.

    De nuevo, como otras muchas veces, tengo el dilema sobre qué idioma elegir, español o inglés. Mientras que con el primero puedo conseguir, a veces, no cometer faltas ortográficas y crear frases coherentes y comprensibles por otros seres humanos, el segundo, por suerte o desgracia, es el idioma neutral y empleado principalmente en esto del interné, nuevas tecnologías y especialmente en el desarrollo de software. Posiblemente iré decidiéndolo post a post, intercalando, para dar emoción al asunto.

    Y tú, ¿has dejado de escribir en tu blog? Él no lo haría.

    November 18, 2009

    Fixing Snow Leopard ruby readline  Koke

    Building ruby readline

    Since I upgraded to Snow Leopard I’ve been missing readline whe using irb. As I discovered in this article, this is due to apple’s ruby linking to libedit instead of libreadline. I didn’t have that problem before the upgrade since I had compiled ruby myself.

    This time, I was looking for another solution. I could have compiled ruby with readline support, but then probably I’d had to reinstall some gems too. So I present you the quick way to fix your readline

    Step 0: Setup temp dir

    mkdir -p /tmp/rlruby
    cd /tmp/rlruby
    sudo -s

    Step 1: Install readline

    curl -O ftp://ftp.cwru.edu/pub/bash/readline-6.0.tar.gz
    tar xvf readline-6.0.tar.gz
    cd readline-6.0
    ./configure && make && make install
    cd ..

    Step 2: Get ruby source

    To keep the complications to a minimum, I downloaded ruby from apple (check 10.6.2 open source, or other releases). The current patchlevel is ruby-75 so fetch that one:

    curl -O http://www.opensource.apple.com/tarballs/ruby/ruby-75.tar.gz
    tar xvf ruby-75.tar.gz
    cd ruby-75

    Step 4: Build readline extension

    We don’t need to build all ruby, just the readline extension

    cd ruby/ext/readline/
    ruby extconf.rb
    make

    At this point, you’ll probably get the following error:

    readline.c: In function ‘username_completion_proc_call’:
    readline.c:730: error: ‘username_completion_function’ undeclared (first use in this function)
    readline.c:730: error: (Each undeclared identifier is reported only once
    readline.c:730: error: for each function it appears in.)
    readline.c: In function ‘username_completion_proc_call’:
    readline.c:730: error: ‘username_completion_function’ undeclared (first use in this function)
    readline.c:730: error: (Each undeclared identifier is reported only once
    readline.c:730: error: for each function it appears in.)
    lipo: can't open input file: /var/folders/s4/s4qO7oueE3ijABAH7qB6Dk+++TI/-Tmp-//ccW5lOLL.out (No such file or directory)
    make: *** [readline.o] Error 1

    We need to tell gcc that our readline is in /usr/local

    make readline.o CFLAGS='-I/usr/local/include -DHAVE_RL_USERNAME_COMPLETION_FUNCTION'
    cc -arch i386 -arch x86_64 -pipe -bundle -undefined dynamic_lookup -o readline.bundle readline.o -L/usr/local/lib -L/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib -L. -arch i386 -arch x86_64 -lruby -lreadline -lncurses -lpthread -ldl

    To be sure we are using the real readline run otool and make sure libedit doesn’t appear on the results:

    $ otool -L readline.bundle
    readline.bundle:
    /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/libruby.1.dylib (compatibility version 1.8.0, current version 1.8.7)
    /usr/lib/libncurses.5.4.dylib (compatibility version 5.4.0, current version 5.4.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.0.0)

    Step 5: Replace readline.bundle

    cd /System/Library/Frameworks/Ruby.framework/Versions/Current/usr/lib/ruby/1.8/universal-darwin10.0/
    mv readline.bundle readline.bundle.libedit
    cp /tmp/rlruby/ruby-75/ruby/ext/readline/readline.bundle readline.bundle

    Now launch irb and check if all your favorite shortcuts are in place

    Most Commented Posts

    November 10, 2009

    Sick of getting your wordpress hacked? (contest below)  Koke

    Crashed again

    I sure am. After a proper installation/configuration, the most important factor is to always stay updated to the last version. I’m managing at this time 8 or more blogs/websites running different versions of WordPress and it’s hard to keep them up to date.

    Automatic upgrades help, although they still terrify me after the 2.8 crash.

    The problem is, some of these blogs are set up for friends or old projects, and I forgot to frequently check if they are using the latest version. Most of the times, they become crammed with spam, and eventually trigger google’s malware detectors. Most of the times I notice the hack because of firefox malware warning.

    So I started a side project to help me keep track of all those blogs and their versions, and it’s seems is close to see the light. This is how it looks right now:

    beta screenshot

    I will need testing, so if you want to participate in the beta, fill the signup form, and I’ll send some invitations.

    Also, I’m looking for a nice name for the thing. If you have a good idea, put it in the ‘Proposed name’ field on the signup form. The winner(*) will get the first beta invitation and free full access to the product for 1 year after it launches. Make sure a .com domain is available for the name you propose or it won’t have many chances.

    (*) There will be only 1 winner: the first person to propose the chosen product name. Simple rules, but… without rules we are nothing but savages.

    Related posts

    November 08, 2009

    Discovr: a flickr experiment gone wrong  Koke

    I need help with this. I had a dream… Well, not so much as a dream, maybe a “It’d be cool to…”

    I thought it’d be nice to discover new photos on flickr using your favorite photos and the people who also favorited those photos, and the favorite photos of those who also favorited my pictures. Still with me?

    It’s actually a quite simple code (about 500 lines, check it on github: discovr), but it’s terribly slow. Some possible reasons:

    The simplified algorithm goes like this.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
      # method from class User
      def similar_pictures
        similar = {}
     
        favorites.each do |favorite|
          favorite.favorited_by.each do |user|
            user.favorites.each do |v|
              similar[k] ||= {:weight => 0, :picture => v[:picture]}
              similar[k][:weight] += 1
            end
          end
        end
     
        similar.values.sort {|a,b| b[:weight] <=> a[:weight]}.select {|v| v[:weight] > 1}
      end

    So I’ve created a github repository and uploaded the code: discovr at github. Feel free to clone, test and improve

    Related posts

    November 04, 2009

    Cómo me quedé sin teléfono (VIII)  Golo

    Este verano estuve un par de semanas sin teléfono, por culpa de una portabilidad. Partes I, II, III, IV, V, VI, y VII

    Mi Terminal

    Por un fallo del distribuidor en mi última portabilidad, tengo un compromiso de permanencia. Esta experiencia me ha servido para darme cuenta del chollo que suponen los distribuidores para la operadora.

    Si el distribuidor comete un fallo, la operadora alega que yo no he firmado el contrato con ellos, y el distribuidor alega que el operador no le ayuda con el problema. En definitiva, entre unos y otros el cliente se come los mocos.

    Justo después de que este distribuidor intente darme largas, paso por el distribuidor de un amigo, donde sí tienen el terminal que yo quería con mi primera portabilidad.

    Con el calentón no me lo pienso Así que inicio la portabilidad, y sin problemas, en una semana… ¡¡¡Lo tengo!!! ¡¡¡eoeooeoeoeeo!!!

    Y todavía no me lo acabo de creer :) .

    Eso sí, me costará entrar en la lista de morosos por no pagar un compromiso de permanencia que no tengo que pagar. Y reclamaciones, juicios y lo que haga falta para volver a salir… pero… ¡¡¡Lo tengo!!!

    November 03, 2009

    Cómo me quedé sin teléfono (VII)  Golo

    Este verano estuve un par de semanas sin teléfono, por culpa de una portabilidad. Partes I, II, III, IV, V, y VI

    Estoy sin contrato ni compromiso de permanencia, huyo de mi distribuidor.

    Compromiso de Permanencia

    El plan es, vuelvo a mi operador anterior, con el terminal que ya tengo, y como si nada hubiera pasado. Eso sí, vuelvo sin compromiso de permanencia, que ya me canso de que me tengan agarrao.

    Todo es tan bonito… hasta que la comercial se equivoca al hacer los papeles, y me pone permanencia de 18 meses.

    – Oye perdona, habíamos quedado que si no me llevo terminal, no tenía compromiso de permanencia…

    – ¡Ay!, Sí, perdona, vaya, ahora no me deja cambiarlo el sistema, lo cambiamos en el contrato, vuelve la semana que viene y lo cambiamos por teléfono en el sistema.

    La semana siguiente estoy de vacaciones. Me paso cuando puedo, pero la chica está de vacaciones. “Vuelve la semana que viene”. Tengo mudanzas por medio, estoy un poco liado, me paso un par de meses después. “Es que la chica ya no trabaja aquí, te digo donde trabaja y te pasas.”

    Y hasta ahí aguanto. Yo hice el contrato con el distribuidor, no con una comercial. “Veremos a ver si podemos hacer algo desde aquí.”

    Pues más les vale, porque en mi copia del contrato dice que no tengo compromiso de permanencia.

    November 02, 2009

    Cómo me quedé sin teléfono (VI)  Golo

    Este verano estuve un par de semanas sin teléfono, por culpa de una portabilidad. Partes I, II, III, IV, y V

    En una hoja de reclamaciones el distribuidor se compromete a darme el terminal solicitado en cuanto tenga existencias. ¿Será verdad?

    Camino de vuelta

    Desgraciadamente, para contratos de empresa, el procedimiento de reclamación se realiza vía burofax. Mi hoja de reclamaciones no sirve para nada :( .

    El distribuidor se pone en contacto conmigo para indicarme que si no me llevo un teléfono antes de final de mes, darán de baja mi número de teléfono.

    Después de varias quejas en todos los medios de atención al cliente del operador, y llamadas casi diarias al distribuidor (que ya empezaba a cansarse de mí), el distribuidor accede a cancelarme el contrato. Eso sí, manteniéndome el número sin compromiso de permanencia, para que pueda portar a otro operador.

    Así que acudo a un distribuidor de mi viejo operador para realizar otra portabilidad, la segunda en un mes.