Pasando de bbdb a org-contacts
Llevo años utilizando bbdb
como gestor de contactos y acumulo en esa
base de datos cientos de registros con las direcciones y teléfonos de
la gente con la que me cruzo en esta vida. Había una excepción y era
que los datos de mis clientes iban a otro lugar gestionándose para
interactuar con ellos desde org-mode
y tener juntos los datos
personales y las historias clínicas con las visitas, pruebas,
avances en el tratamiento y anotaciones necesarias para gestionar
correctamente mi trabajo.
Así, pues, tenía duplicados algunos datos en el sistema y dos modos de
gestionar datos personales. Los tenía duplicados porque bbdb
permite
exportar e importar datos desde ficheros externos estándar, como
vcard
, mientras que org-contacts
sólo permite la exportación.
Además, la captura de datos la tengo automatizada para realizarla a
org-contacts
porque si la hacía para bbdb
siempre se me olvidaba
desactivar ivy-mode
y cuando estaba activo la captura de datos para
bbdb
entraba en bucle y tenía que reiniciar el proceso. Capturando
para el modo org
no había esos problemas, pero luego tenía que pasar
los datos a bbdb
. Si alguien tiene curiosidad por el código que
utilizo para la captura de datos, aunque creo que ya lo puse alguna
vez por el blog, es éste:
[...] ;;; Templates de captura para cosas que no son para la agenda ("n" "Capturas no para agenda") ("nc" "Anotar (c) contacto" entry (file+headline "~/agenda/especiales/personal.org.gpg" "Sin ordenar") "** %^{Nombre} %^{Apellidos}%? :PROPERTIES: :Nombre: %\\1 :Apellidos: %\\2 :Alias: %^{Alias} :Grupo: %^{Grupo} :F-nacim: %^{F-nacim}u :Móvil: %^{Móvil} :Teléfono: :Email: %^{Email} :Web: :Dirección: %^{Dirección} :Ciudad: %^{Ciudad} :Provincia: %^{Provincia} :Cód.Pos: %^{Código Postal} :Compañía: :Notas: :END:" :empty-lines 1) [...]
Como se puede apreciar pasan al fichero personal.org.gpg
y se
almacenan directamente cifrados en él, en un apartado que se llama
Sin ordenar y que después serán movidos esos datos a la posición que
corresponda dentro del grupo de datos correspondiente, porque lo tengo
dividido en apartados como Familia, Amigos, Trabajo,
Asociación, etc. que viene también especificado por el Grupo
que
se puede observar en la plantilla.
Pasar los datos de una base de datos a otra
Para pasar todos los datos que tenía en bbdb
, primero hay que
configurar y preparar el fichero de contactos para que se comporte de
una manera más amigable. En principio, org-contacts
lo que hace es
organizar un árbol de entradas donde los datos se guardan como
propiedades. Si abres el documento con todos los datos desplegados,
cada registro consta de tantas líneas como campos, más la cabecera,
más los delimitadores de las propiedades. Esto hace que navegar por
los datos sea un poco tedioso. Personalmente, para la visualización
prefiero el modo columnas, que presenta cada registro en una línea
con los campos más habituales a la vista.
Configurar los contactos
El caso es que la duplicidad de datos y el tener que gestionar las dos
formas de almacenarlos, poco a poco, me han ido cansando y decidí
centralizarlo todo en org-contacts
. Dicho paquete no se instala
independientemente, sino que pertenece a un metapaquete que se llama
org-plus-contrib
donde podemos encontrar muchos paquetes
interesantes y útiles.
Para activar los datos de los contactos, hace falta algo de código en
nuestro init.el
. Como podéis ver en la plantilla de captura, todos
los campos los nombro en español, así que tengo que decirle a
org-contacts
a qué campo corresponde cada uno:
(setq org-contacts-address-property "Dirección") (setq org-contacts-birthday-format "Cumpleaños: %04d-%02d-%02d") (setq org-contacts-birthday-property "F-nacim") (setq org-contacts-email-property "Email") (setq org-contacts-files (quote ("~/agenda/especiales/personal.org.gpg"))) (setq org-contacts-matcher "Email<>\"\"|Alias<>\"\"|Móvil<>\"\"|F-nacim<>\"\"") (setq org-contacts-note-property "Notas") (setq org-contacts-tel-property "Móvil")
Además, en el fichero que guarda la base de datos, personal.org.gpg
,
tengo establecido en su cabecera
#+COLUMNS: %25Nombre %25Apellidos %Alias %Móvil %Email
Así, cuando consulto la base de datos puedo establecer el modo
columnas pulsando la combinación C-c C-x C-c
para visualizar los
datos en una tabla.
Importar los datos desde bbdb
El problema que me encontré es que no existe una manera de importar
grandes cantidades de registros a org-contacts
. Sí los puedes
exportar a formato vcard
, sin embargo, no importarlos. La opción de
meterlos uno a uno en el archivo aprovechando la plantilla de captura
es inviable cuando tienes cientos de registros.
La solución pasa por un poco de investigación y algo de código. Lo
primero que hice para no cargarme la base de datos de bbdb
fue hacer
una copia y trabajar sobre ella. Al abrirla como si fuera un fichero
de texto normal, se puede apreciar que los datos se guardan ordenados
en una lista de arrays, una fila para cada registro o array.
Puesto que era una copia, lo encapsulé todo en una lista que llamé
contactos
y renombré a convertir.el
el fichero copia de los datos:
(setq contactos (list ;;; Aquí todos los registros con los datos ))
Podría haber escrito el código en otro fichero e importar los datos desde fuera, pero ya que los tenía en formato texto me pareció más rápido encapsular los datos en una lista y no romperme la cabeza con importaciones externas.
Posteriormente me dediqué a observar la estructura de los registros y
cómo extraer los datos. Lo más normal era utilizar la función insert
para abrir el fichero donde importarlos y que el código insertara los
datos de manera directa. Voy a poner todo el código fuente y ahora me
entretengo un poco en explicar cómo funciona:
(setq contactos (list ;;; Aquí todos los registros con los datos )) (defun varios-telefonos (cada-telefono) (aref cada-telefono 1)) (defun insertar-datos (elem) (insert (concat "** " (aref elem 0) " " (aref elem 1) "\n")) (org-set-property "Nombre" (if (eq (aref elem 0) nil) (aref elem 1) (aref elem 0))) (org-set-property "Apellidos" (if (eq (aref elem 0) nil) "" (aref elem 1))) (org-set-property "Alias" "") (org-set-property "Grupo" (if (eq (aref elem 4) nil) "" (car (aref elem 4)))) (org-set-property "F-nacim" (if (or (eq (aref elem 8) nil) (eq (cdr (assoc 'birthday (aref elem 8))) nil)) "" (cdr (assoc 'birthday (aref elem 8))))) (org-set-property "Móvil" (if (eq (aref elem 5) nil) "" (aref (car (aref elem 5)) 1))) (org-set-property "Teléfono" (if (> (length (aref elem 5)) 1) (mapconcat 'varios-telefonos (aref elem 5) ", ") "")) (org-set-property "Email" (mapconcat 'append (aref elem 7) ", ")) (org-set-property "Dirección" (if (eq (aref elem 6) nil) "" (car (aref (car (aref elem 6)) 1)))) (org-set-property "Ciudad" (if (eq (aref elem 6) nil) "" (aref (car (aref elem 6)) 2))) (org-set-property "Provincia" (if (eq (aref elem 6) nil) "" (aref (car (aref elem 6)) 3))) (org-set-property "Cód.Pos" (if (eq (aref elem 6) nil) "" (aref (car (aref elem 6)) 4))) (org-set-property "Compañía" "") (org-set-property "Notas" "") (insert "\n")) (defun convertir-datos () (interactive) (mapc 'insertar-datos contactos))
La función que llamamos desde M-x
será convertir-datos
. No
necesita parámetros, porque toda la información la guarda en el mismo
script. Lo único que hace es una iteración (mapc
) llamando a
insertar-datos
para cada registro de contactos
.
La función insertar-datos
recibe un elemento en elem
e inserta
(insert
) una cadena de texto extrayendo el nombre (aref elem 0
)
y los apellidos (aref elem 1
) del registro. La función aref
extrae de un array el elemento designado por orden.
A continuación, comienza a insertar propiedades con el nombre y la
estructura que tiene la plantilla de captura. Siempre comprobando el
contenido para dejarlo en blanco (""
) o extraer las cadenas
necesarias. Algunos campos además contienen listas, como por ejemplo
el email y por eso se extrajeron todos separándolos con ,
mediante
la función mapconcat
.
El caso del campo «teléfono» era más complejo, porque está compuesto
por una lista de arrays y necesité un paso intermedio para extraer
las cadenas del número de cada array. Para eso utilicé la función
auxiliar varios-telefonos
, que básicamente devuelve el número, dado
un registro de la lista de teléfonos.
Teniendo ya todos los datos cargados en el fichero toca ir
ordenándolos y acomodándolos. También ir abandonando bbdb
hasta que
quede sólo el fichero de org-contacts
.
Conclusión
El abandono de bbdb
no se debe a un mal funcionamiento del sistema
de base de datos, sino a la comodidad de tenerlo todo junto y a la
posibilidad de realizar la toma de datos, incluso sin tener activado
org-contacts
ni la plantilla de captura, porque al final los datos
se encuentran en formato de texto plano.
La capacidad de leer los datos desde otros buffers, gracias al
sistema de propiedades que org-mode
utiliza, me facilita el trabajo
en mis tareas automatizadas mediante emacs. Esto ha hecho que me
decantara finalmente por este sistema, que a pesar de todo me parece
más lento que bbdb
y también que ocupa bastante más espacio (algo
que para mí no es vital pero sí importante, pues es un sistema menos
optimizado de gestión de datos).