← Back to team overview

gtg-user team mailing list archive

A user script to process your inbox

 

Hi all,

I mentioned in an earlier email that I use tasks without tags as my inbox. A difficulty was that I had no easy way to process my inbox. When I process my inbox I want to be able to easily "move" the inbox task such that it is a subtask of an appropriate task (project) or just remove the item.

The solution to this problem I am currently using is a small stand-alone python command-line script with a very simple interactive curses interface. See attachment. It computes the first (only first) inbox item and let's you select a task from your task-tree. Run it several times until you have processed your entire inbox.

It is a quick ugly hack and sometimes makes gtg crash, so maybe it is not suitable to be a part of the main gtg code. I still think it could be useful for people like me who try to implement GTD by the book.

I am using GTG 0.2.4.

Best regards,

Elias



#!/usr/bin/python
# coding: latin-1

# A simple ncurses interface to process your inbox
#


# imports for the menu system
import curses
import curses.wrapper
import curses.textpad
import re

# imports for the dbus and GTG
import sys
import os
sys.path.append('/usr/share/gtg'); # Hardcoded!
import dbus
import cgi
import getopt
import datetime

from GTG import _

### Menu code 
def print_menu(stdscr,entries,active):
    k=0
    offset=2
    for s in entries:
        if (k==active):
            stdscr.addstr(offset+k,0,s,curses.A_STANDOUT)
        else:
            stdscr.addstr(offset+k,0,s)
        k=k+1
    stdscr.refresh()

def mymenu(stdscr,list,default_text):
    # Start up the menu and return the edited text and selected entry 
    
    stdscr.refresh()
    finished=False

    textbox=curses.textpad.Textbox(stdscr)
    textbox.stripspaces=False;

    stdscr.addstr(0,0,default_text)
    
    [y,x]=stdscr.getmaxyx()
    if (len(list)>=y):
        return ["", -1]

    k=0
    
    while (not(finished)):
        yx=stdscr.getyx()
        print_menu(stdscr,list,k)
        stdscr.addstr(yx[0],yx[1],'')

        ch=stdscr.getch()
        if (ch==curses.KEY_UP):
            k=k-1
        elif (ch==curses.KEY_DOWN):
            k=k+1
        elif (ch==10):
            finished=True
        else:
           textbox.do_command(ch)
        if (k<0):
            k=0
        if (k>=len(list)):
            k=len(list)-1
    alltext=textbox.gather()
    m=re.search('^(.*?)\n',alltext)
    txt=m.group(0)
    m=re.search('(.*?)\s*$',txt)
    txt=m.group(0)
    p=re.compile('\s*$')
    v=p.sub('',txt)
    return [v,k]


###  Task and gtg-communication code 
def update_task(timi,tid,td):
    dtask=timi.get_task(tid)
    for fn in td.keys():
        dtask[fn]=td[fn]
    timi.modify_task(tid,dtask)


def get_task_data(timi):
# make hash tables from the task data
    alltasks=timi.get_tasks()
    
    id_to_task={}
    children={}
    parents={}
    id_list=[]
    for t in alltasks:
        myid=str(t.get('id'))
        id_to_task[myid]=t
        mychildren=[]


        
        for ts in t.get('subtask'):
            mychildren.append(str(ts))
            parentlist=[]
            if (ts in parents):
                parentlist=parents[ts]
            parentlist.append(myid)
            parents[ts]=parentlist
            
        children[myid]=mychildren

        

    return [id_to_task,children,parents]
     

def get_unprocessed_tasks(task_data):
    # Fetch tasks with no parents and no tag 
    unprocessed_tasks=[]
    id_to_task=task_data[0]; 
    id_to_children=task_data[1];
    id_to_parent=task_data[2];
    
    for myid in id_to_task.keys():

        if ((myid not in id_to_parent) and (len(id_to_children[myid])==0) and (len(id_to_task[myid].get('tags'))==0)):
            unprocessed_tasks.append(myid)
    return unprocessed_tasks




def gen_tree(roots,task_data,remove_ids,level):
#  Generate a array lists containing the task tree data
    id_to_task=task_data[0]; 
    id_to_children=task_data[1];
    id_to_parent=task_data[2];

    mylist=[]
    idlist=[]
    for myid in roots:
        if ((myid in id_to_task) & (myid in id_to_children) ):
            if (len(id_to_children[myid])>0):
                mylist.append(str(id_to_task[myid].get('title')))
                idlist.append(str(myid))
                if (level>0):
                    if (myid in id_to_children):
                        [mysublist,subidlist]=gen_tree(id_to_children[myid],task_data,remove_ids,level-1)
                        for s in mysublist:
                            mylist.append(' '+s) 
                        for i in subidlist:
                            idlist.append(i)
                       
    return [mylist,idlist]


### Main code

bus = dbus.SessionBus()
liste = bus.list_names()
busname = "org.GTG"
remote_object = bus.get_object(busname,"/org/GTG")
timi = dbus.Interface(remote_object,dbus_interface="org.GTG")

# Fetching the task tree data
task_data=get_task_data(timi)

id_to_task=task_data[0]; 
id_to_children=task_data[1];
id_to_parent=task_data[2];



# Computing inbox
unprocessed_tasks=get_unprocessed_tasks(task_data)
for task_id in unprocessed_tasks:
    print('inbox:'+task_id+str(id_to_task[task_id].get('title')))


remove_ids=unprocessed_tasks
no_parent_list=[]
for myid in id_to_task.keys():
    if ((myid not in id_to_parent) & (remove_ids.count(myid)==0) ):
        no_parent_list.append(myid)
        
[mylist,idlist]=gen_tree(no_parent_list,task_data,remove_ids,4)

mylist.append('Remove')
#for i in mylist:
#    print(i)

if (len(unprocessed_tasks)==0):
    print('Inbox empty => Well done!')
    exit(0)


print('Starting menu')

processing_task_id=unprocessed_tasks[0]
processing_task=id_to_task[processing_task_id]

# Start the menu system
default_text=str(processing_task.get('title'))
[txt,parent_count]=curses.wrapper(mymenu,mylist,default_text)

if (parent_count<0):
   print('Ncurses error: make your terminal window larger')
   exit(0)



if (parent_count<len(idlist)):
    # get parent data
    parent_id=str(idlist[parent_count])
    parent_task=id_to_task[parent_id]
        
        
    # Modify the title of the task
    mytask={}
    mytask['title']=txt
    update_task(timi,processing_task_id,mytask)
    
    # Add as a subtask 
    mytask={} 
    mytask['subtask']=parent_task.get('subtask')
    mytask['subtask'].append(dbus.String(processing_task_id))
    update_task(timi,parent_id,mytask)
    
    # Open in a new window
    timi.open_task_editor(processing_task_id)

else:
    timi.delete_task(str(processing_task.get('id')))
    print('Removed')