Ayuda con guión en Bash para procesar salidas de Taskwarrior

Saludos a todos:

Si alguien de aquí se acuerda, hace un tiempo pregunté por el método de
organización Getting Things Done de David Allen [0], en español esta
traducido como Haz las cosas (aunque el libro se llamaba organízate con
eficacia, cosas de los traductores).

El caso es que he venido trastabillando con el asunto utilizando la
suite PIM de Kde, Kontact, pero no me convence porque todos los procesos
son manuales y procesar una tarea de la bandeja de entrada es arduo.

Buscando alternativas, finalmente me he decidido por utilizar
Taskwarrior, la utilidad de línea de comando. En la misma página de
Taskwarrior se referencia una útil guía para adaptar TaskWarrior al uso
con HlC (españolizando GTD), esta:

http://cs-syd.eu/tags/GTD.html

La he adaptado, e incluso he cambiado algunas cosas sobre lo que hace
Tom pero, para procesar la bandeja de entrada, Tom lo hace manualmente,
y eso, a mi, no me convence, porque ya he pasado por ahí con Kontact y,
personalmente, necesito algo más automático.

Así que estoy montando un guión en Bash para procesar la bandeja de
entrada, he ahí la razón por lo que estos días estoy «dando guerra» en
el canal de Telegram con Bash.

Mi idea, es crear un proceso semi-automático que me pregunto que hay que
hacer con la tarea:

a) Hacerla en menos de dos minutos.
b) Archivarla para futuro (o dejarla en ideas-proyectos algún día)
c) Delegarla
d) Borrarla (si ya no hay nada más que hacer con ella)
e) Añadirla como referencia a un proyecto

Eso en un primer paso, después la cosa se va dividiendo si se trata solo
de una tarea o es un proyecto de varias tareas, etc.

(Aquí: http://www.jesusda.com/blog/index.php?id=275 tenéis un enlace con
un gráfico de proceso pintado en bonito.)

El caso es que para que quede medianamente bonito he pensado en utilizar
dialog tras leer (muy, muy rápido) alguna que otra referencia [1] [2] y
ver que no era una cosa muy difícil de manejar.

Antes de seguir voy a poner el guión, o por donde voy ahora:

 #!/bin/bash
## Guión de procesamiento de la bandeja de entrada de HlC 
## implementada en
## TaskWarrior
# Licencia: GPL v3.0

# Funciones

function analiza_tarea() {
   tarea_json=$(task $1 export)

}

function recopila() {

   tareas=$(task bandeja_id |awk '$1~/[0-9]+/&&NF>2{print $1}')

   for i in $tareas; do
      procesar_bandeja $i
   done
}

function procesar_bandeja() {
   id_tarea=$1
   tarea_json=$(task "$id_tarea" export)
   # Intento de procesar con Python
   # echo $tarea_compl | python -c 'import json,sys;
   # obj=json.load(sys.stdin); print obj[0]["description"]'
   Titulo=$(echo "$tarea_json" | jq '.[].description')
   F_entrada=$(echo "$tarea_json" | jq '.[].entry')
   F_modif=$(echo "$tarea_json" | jq '.[].modified')
   Energia=$(echo "$tarea_json" | jq '.[].energia')
   Tiempo=$(echo "$tarea_json" | jq '.[].tiempo')
   # Procesamiento de comentarios
   Total_comentarios=$(echo "$tarea_json" | jq '.[].annotations |length')
   if [$Total_comentarios != "0"]
      then
      for i in $Total_comentarios; do
         orden_jq=".[].annotations[${i}].entry"
         #Comentario_"$i"=$(echo "$tarea_json" | jq
'.[].annotations['$i'].entry')
         Comentario=$(echo "$tarea_json" | jq "$orden_jq")
         echo $Comentario_"$i"
      done
    fi
    echo $Total_comentarios
    echo $(echo $tarea_json) |jq '.[] | length'
}

# Cuerpo principal, llama a la función recopila para procesar la bandeja.
echo -e "\n Procesando la bandeja de entrada,"
echo -e "\n se realiza por orden de entrada de elementos, tal y como
dice HlC."
echo -e "\n Aquí tienes el listado completo de elementos para procesar:"
task bandeja_entrada
if read -t 15 -p "¿Continuas el proceso? [S/N]" continuar
   then
      case $continuar in
         S|s) recopila ;;
         N|n) echo "Saliendo"
              exit ;;
      esac
   else
      echo -e "\n ¡Eres muy lento!"
fi

Como veis, lo primero que hace es lanzar un listado de las cosas
existentes en la bandeja de entrada (task bandeja_entrada), a partir de
ahí lanza la pregunta si sigues o si no (con control de tiempo para
lentorros) y, si sigues, llama a la función «recopila».

La función «recopila» saca las id de cada cosa en la bandeja de entrada
y va llamando para cada una de ellas a la función «procesar_bandeja».
(Aquí hay que dar las gracias a nuestro experto en AWk @Klashxx, esa
parte es suya.)

La función «procesar_bandeja» es la que se lleva el premio gordo.

En teoría lo que he pensado es mostrar un dialogo donde se muestren los
siguientes detalles de la tarea:

  • Título.
  • Fecha de entrada.
  • Fecha de modificación.
  • Energía.
  • Tiempo.
  • Comentario 1, 2, …, n (Si hubiere más de uno)

Como procesar la salida directa de TaskWarrior a la consola puede
suponerme mucho dolor de cabeza, lo que se me ocurrió es exportar la
tarea a formato Json y, tras mucho buscar y probar, encontré la utilidad
jq (instalable mediante apt-get en Debian) que te permite procesar datos
Json.

¿Donde esta ahora el problema? Pues en los comentarios, porque no se
sabe si va a existir uno, ninguno, dos o chorrocientos (a veces anoto
cosas y después voy añadiendo cosas que se me ocurren después).

jq puede acceder a esos comentarios de la siguiente forma:

jq '.[].annotations[0].entry
jq '.[].annotations[0].description
jq '.[].annotations[1].entry
jq '.[].annotations[2].entry

El problema que tengo en el guión es que no soy capaz de expandir la
variable «i» que recorre el bucle for de los comentarios.

Y en esas estoy, seguro que después encuentro otros problemas, pero, por
el momento estoy en este.

Agradezco opiniones para salir de este atolladero; o para reformar
completamente el programa si se tercia, no tengo problema en eso.

Añado la salida en Json de una tarea de la bandeja de entrada (ignorar
lo que ponga en los comentarios, en ese punto de las pruebas era muuuy
tarde):

$ task 7 export
[
{"id":7,"description":"Pues un título
descriptivo.","energia":"M","entry":"20160718T234255Z","modified":"20160720T122709Z","status":"pending","tags":["bandeja_entrada"],"uuid":"ac043334-2922-4aae-ab04-3843b397df00","annotations":[{"entry":"20160718T234255Z","description":"Se
habla de un perro piloto, pero a estas alturas, ¿quien
sabe?"},{"entry":"20160720T122709Z","description":"Pues quiero añadir
otro comentario para probar el procesamiento."}],"urgency":1.71644}
]

Por si alguno quiere ver la salida original de TaskWarrior sobre la
misma tarea:

$ task 7 information

Name          Value

ID            7
Description   Pues un título descriptivo

                2016-07-19 01:42:55 Se habla de un perro piloto, pero a
estas alturas, ¿quien sabe
                2016-07-20 14:27:09 Pues quiero añadir otro comentario
para probar el procesamiento.
Status        Pending
Entered       2016-07-19 01:42:55 (3d)

Last modified 2016-07-20 14:27:09 (2d)
Tags          bandeja_entrada

Virtual tags  ANNOTATED PENDING READY TAGGED UDA UNBLOCKED
UUID          ac043334-2922-4aae-ab04-3843b397df00

Urgency       1.716
Energía       M


    annotations    0.9 *    1 =    0.9
    tags           0.8 *    1 =    0.8
    age          0.008 *    2 =  0.016
                                 1.716

Date                Modification

2016-07-19 01:42:55 Annotation of 'Se habla de un perro piloto, pero a
estas alturas, ¿quien sabe?' added
2016-07-20 14:27:09 Annotation of 'Pues quiero añadir otro comentario
para probar el procesamiento.' added.

Ayer pedí algo de ayuda también en el IRC de TaskWarrior, creo que
podéis encontrar el registro aquí:

https://botbot.me/freenode/taskwarrior/

Aunque tenéis que subir hasta el día 21.

No me enrollo más.

Gracias a todos.

Salud y Revolución.

Lobo.

[0] ¿Alguien usando GTD en el foro?

[1] https://zenon.wordpress.com/2007/11/27/xdialog-ventanas-en-scripts/

[2] http://www.gacetadelinux.com/es/lg/issue101/sunil.html
Libertad es poder elegir en cualquier momento. Ahora yo elijo GNU/Linux,
para no atar mis manos con las cadenas del soft propietario.
Porque la libertad no es tu derecho, es tu responsabilidad.
http://www.mucharuina.com
Desde El Ejido, en Almería, usuario registrado Linux #294013
http://www.counter.li.org

Esta sería la funcion procesar_bandeja().
Los cambios son:

  • He modificado el for para meterle un seq para que haga el bucle bien (espero)
  • He modficado el uso de $Comentario_"$i" a $Comentario"_$i" para que lo coja bien

.

function procesar_bandeja() {
   id_tarea=$1
   tarea_json=$(task "$id_tarea" export)
   # Intento de procesar con Python
   # echo $tarea_compl | python -c 'import json,sys;
   # obj=json.load(sys.stdin); print obj[0]["description"]'
   Titulo=$(echo "$tarea_json" | jq '.[].description')
   F_entrada=$(echo "$tarea_json" | jq '.[].entry')
   F_modif=$(echo "$tarea_json" | jq '.[].modified')
   Energia=$(echo "$tarea_json" | jq '.[].energia')
   Tiempo=$(echo "$tarea_json" | jq '.[].tiempo')
   # Procesamiento de comentarios
   Total_comentarios=$(echo "$tarea_json" | jq '.[].annotations |length')
   if [$Total_comentarios != "0"]
      then
      for i in `seq $Total_comentarios`; do
         orden_jq=".[].annotations[${i}].entry"
         #Comentario_"$i"=$(echo "$tarea_json" | jq '.[].annotations['$i'].entry')
         Comentario=$(echo "$tarea_json" | jq "$orden_jq")
         echo $Comentario"_$i"
      done
    fi
    echo $Total_comentarios
    echo $(echo $tarea_json) |jq '.[] | length'
}

[LatinSuD] LatinSuD http://foro.hacklabalmeria.net/users/latinsud
22 Julio

Esta sería la funcion |procesar_bandeja()|.
Los cambios son:

  • He modificado el |for| para meterle un |seq| para que haga el bucle
    bien (espero)
  • He modficado el uso de |$Comentario_"$i"| a |$Comentario"_$i"| para
    que lo coja bien

Agua:

$ procesar.sh

 Procesando la bandeja de entrada,

 se realiza por orden de entrada de elementos, tal y como dice HlC.

 Aquí tienes el listado completo de elementos para procesar:

ID Descripción

 1 Probando nuevos entornos

 7 Pues un título descriptivo

     2016-07-19 Se habla de un perro piloto, pero a estas alturas,
¿quien sabe
     2016-07-20 Pues quiero añadir otro comentario para probar el
procesamiento.

2 tasks
¿Continuas el proceso? [S/N]s
/home/razlobo/bin/procesar.sh: línea 37: [0: no se encontró la orden
0
10
/home/razlobo/bin/procesar.sh: línea 37: [2: no se encontró la orden
2
10

El problema es la expansión del $i en la orden «jq», no lo expande y…
¡perdón! El problema no esta en que no lo expanda, el problema esta en
que no continua pegando el resto de la orden jq, es decir la orden sería
esta:

|jq '.[].annotations[0].entry

pero en el guión se queda en:

|jq '.[]annotations[0

O se queda solo en [0, no lo se, pero el problema es ese.

Gracias en cualquier caso.

Salud y Revolución.

Lobo.

Según mi experiencia cuando los problemas de parseo se convierten en un verdadero quebradero de cabeza es la hora de plantearse el uso de una herramienta de programación mas potente, para tu caso te sugiero Python, basándome en tu código y sin poder testear (bandeja_id) , esto podría ser una aproximación (en 2.7):

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Taskwarrior
"""

__version__ = '0.1'

TIMEOUT = 15

import sys
import re
import json
import shlex
import select
import subprocess


def recopila():
    """Recopila los ids a procesar para ``bandeja_id``"""

    cmd = 'task bandeja_id'
    for id_tarea in re.findall(r'^\s*(\d+)',
                               subprocess.check_output(shlex.split(cmd)),
                               re.M)[:-1]:
        procesar_bandeja(id_tarea)

    return None


def procesar_bandeja(id_tarea):
    """Procesa la tarea"""

    cmd = 'task {0} export'.format(id_tarea)
    tarea_json = json.loads(subprocess.check_output(shlex.split(cmd)))[0]

    titulo = tarea_json['description']
    fecha_entrada = tarea_json['entry']
    fecha_modificacion = tarea_json['modified']
    try:
        comentarios = tarea_json['annotations']
    except KeyError:
        comentarios = []

    for comentario in comentarios:
        print comentario['entry']

    return None


def main():
    """Toma de input y control de flujo"""

    print 'Procesando la bandeja por orden de entrada'
    print 'Elementos:'
    print subprocess.check_output(shlex.split('task'))
    print '¿Continuas el proceso? [S/N]'

    continuar, _, _ = select.select([sys.stdin], [], [], TIMEOUT)

    if continuar:
        continuar = sys.stdin.readline().strip().upper()
        if continuar == 'S':
            recopila()
        else:
            print "Saliendo"
    else:
        print "¡Eres muy lento!"

    return None


if __name__ == '__main__':
    main()

Te dejo el gist: tasks.py

1 me gusta

Hola @Klashxx:

Gracias por la molestia de crear un código y enviarlo.

El 23/07/16 a las 11:01, Juan Diego escribió:

[klashxx] klashxx http://foro.hacklabalmeria.net/users/klashxx
23 Julio

Según mi experiencia cuando los problemas de /parseo/ se convierten en
un verdadero quebradero de cabeza es la hora de plantearse el uso de una
herramienta de programación mas potente, para tu caso te sugiero
|Python|, basándome en tu código y sin poder /testear/ (|bandeja_id|) ,
esto podría ser una aproximación (en 2.7):

[…]

Ahora solo tengo dos problemas, de los cuales ya he resuelto uno:

a) Una alternativa a Dialog para Python (no renuncio a que la cosa quede
bonita). Y la he encontrado:

http://pythondialog.sourceforge.net/

(Y encima esta disponible en los repos de Debian.)

b) Tengo que aprender Python, se hacer cuatro cosillas pero poco más,
por eso iba por Bash, porque al menos ahí soy capaz de defenderme.

En fin, quizás sea la necesidad que me hacia falta para lanzarme a por
Python.

Gracias de nuevo @Klashxx.

Salud y Revolución.

Lobo.

Actualización: Este bendito foro me ha vuelto a cortar la mitad del correo, ahora que pensaba que habíamos empezado a llevarnos bien. :^m

1 me gusta

Nada hombre, si publicas el código avisa y lo mismo recibes algún pull request. Así aprendemos todos juntos :innocent::grin:

1 me gusta

Bueno, tras muuuucho tiempo (hubo hasta vacaciones por el medio) por fin he conseguido terminar el guión para procesar la bandeja de entrada de HlC con Python.

He tenido que aprender algo de Python por el camino; entre eso y que se programar muy poco el código resultante es bastante malo (en realidad yo le pondría otro adjetivo menos considerado pero bueno).

Como pide @klashxx y dado que todavía no tengo un servidor Git público y trato de no utilizar herramientas propietarias como Github, adjunto el fichero con el guión para que le podáis dar un vistazo.

Se que el margen de mejora es inmenso (al segundo párrafo me remito) pero, por mi parte, estoy bastante contento porque, funcionar, funciona y he conseguido «resolver» todos los problemas «por mi cuenta» (si no fuera por la cantidad de código que hay en la red…). Eso si, el tiempo invertido…

Lo que más me ha costado es comprender los indexamientos en Python, quizás estoy demasiado acostumbrado a la facilidad con la que se hace en Gnu/R, pero me he encasquillado con eso muchas veces.

Como último comentario, se que hay un exceso de uso de print y de if (sobre todo para salir si pasa un tiempo determinado esperando respuesta), pero, por el momento no se hacerlo mejor.

He visto cosas sobre hacer menús con curses (por ejemplo, este) y utilizando clases y métodos, pero, por ahora eso esta fuera de mi alcance; solo llego a programación estructurada.

No me enrollo más, cualquier crítica o mejora es bien recibida.

Si me pitan los oídos sabré que las críticas no son constructivas.

Salud y Revolución.

Lobo.

Pd.: El foro no me deja subir el fichero de marras y me parece muy fuerte «pegar» 928 líneas de código. Aquí tenéis un enlace para bajarlo: Código en Owndrive.

1 me gusta

Buen trabajo !!

Hola:

[jorgesumle] jorgesumle http://foro.hacklabalmeria.net/users/jorgesumle
17 Septiembre

No se por qué, pero no me deja descargar el código fuente. :frowning:
Pone /Conectando…/ en la barra de título del navegador y así lleva 5
minutos.

No es normal, pero es cierto que anoche note algo raro que achaqué a mi
conexión (que es bastante irregular).

[…]

Aquí tienes un Github libre: https://notabug.org/ Es muuuucho mejor que
Github, ya que es 100% software libre (licencia MIT), tiene las
mismas funcionalidades que Github y, además, te permite crear
repositorios privados ilimitados. A mí me va muy bien con él; llevo
varios meses usándolo.

Le di un vistazo el otro día a raíz de algo que publicaste. Lo miraré de
nuevo, en realidad también tengo algo en contra de la centralización de
servicios, pero eso mejor en otro hilo o en otro momento. :^)

[…]

Para empezar con notabug.org podrías publicar el código ahí y nos avisas
para que te podamos ayudar con algún /pull request/, como dice @klashxx
http://foro.hacklabalmeria.net/users/klashxx.

Si me decido, aviso.

EDITO: después de 10 minutos conectando he podido descargar el código
fuente XD. La única pega que le veo al código es que no es compatible
con Python 3. Creo que simplemente con ponerles paréntesis a los print
(en vez de |print ‘texto’|, pon |print(‘texto’)|) eso se arregla.

Creo que también se me ha escapado algún raw_input(), le daré un vistazo
después. Y cambiaré lo de los print también.

Como decía anoche, el guión funciona pero hay muchas cosas que mejorar y
lo iré haciendo poco a poco (espero).

Tengo que aprender Python, se hacer cuatro cosillas pero poco más,
por eso iba por Bash, porque al menos ahí soy capaz de defenderme.

Para saber poco de Python, el código está bastante bien. No puedo verlo
ahora con detenimiento, pero más tarde lo leeré tranquilamente.

Seguramente te referirás a las partes que he adaptado de otros; lo cual
me lleva a una pregunta: he licenciado el guión con GPLv3 pero hay
trozos de código que son de otros, así que no tengo claro como
especificar que esos trozos son de su autoría, quizás @olea nos pueda
sacar de la duda. :^m

Ya uso
Taskwarrior y creo que un programa como este me será útil. :slight_smile:

El guión esta pensado y creado para seguir el método de David Allen, Haz
las cosas (o Getting things done en inglés), no obstante con etiquetar
lo que quieras procesar con «bandeja_entrada» en TaskWarrior ya te debe
funcionar.

En la primera entrada del hilo tienes un enlace a la página de Tom
Sydney donde explica como adaptar TaskWarrior para HlC si te interesa.

Salud y Revolución.

Lobo.