Space Panda


#!/usr/bin/env python3
# Usage: [-c] todo.txt
# -c (or --clear, -s, --stop) stops tracking of all tracking tasks
# If -c is not provided, it will provide a rofi/wofi/dmenu menu
# for you to select the task you want to start tracking.
# If there's a task running already, the menu will also provide a
# 'Stop tracking' option.
import subprocess
import pathlib
import datetime
import sys
import shutil
import pytodotxt
import pter.utils
    ('rofi', ['-dmenu']),
    ('wofi', ['-dmenu']),
    ('dmenu', []),
STOP_TRACKING = 'Stop tracking'
TODAY ="%Y-%m-%d")
def safe_t(values):
    if len(values) == 0:
        return '0000-00-00'
    return values[0]
def sort_fnc(task):
    return [task.attributes.get('due', ['9999-99-99'])[0] >= TODAY,
            task.priority or 'ZZZZ',
            task.attributes.get('due', ['9999-99-99'])[0],
filename = sys.argv[1]
do_clear = False
if sys.argv[1] in ['-s', '--stop', '-c', '--clear']:
    filename = sys.argv[2]
    do_clear = True
todotxt = pytodotxt.TodoTxt(pathlib.Path(filename).expanduser())
tasks = [t for t in todotxt.parse()
         if not t.is_completed and safe_t(t.attributes.get('t', [])) <= TODAY]
if do_clear:
    selection = STOP_TRACKING
    for cmd, params in DMENU_ALTERNATIVES:
        dmenu = [shutil.which(cmd)]
        if dmenu[0] is None:
        dmenu += params
    if dmenu[0] is None:
        print("Neither rofi, wofi, nor dmenu found", file=sys.stderr)
    options = []
    if any(['tracking' in t.attributes for t in tasks]):
        options = [STOP_TRACKING]
    options += [task.description or ""
                for task in sorted(tasks, key=sort_fnc)
                if 'tracking' not in task.attributes]
    dmenu += ['-i', '-p',]
    result =, encoding='utf-8', input="\n".join(options) + "\n", capture_output=True)
    if result.returncode == 1:
        selection = result.stdout.strip("\n\r")
    except Exception as exc:
        raise exc
changed = False
# stop tracking of currently tracking tasks - there can be only one!
for task in todotxt.tasks:
    if 'tracking' in task.attributes:
        changed = True
if selection != STOP_TRACKING:
    for task in tasks:
        if task.description == selection:
            changed = True