gtg-user team mailing list archive
-
gtg-user team
-
Mailing list archive
-
Message #00298
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')