commit 0046fa3020ef8b716e381c901c55e7621141ba0c Author: neingeist Date: Tue Sep 2 21:36:25 2014 +0200 initial commit diff --git a/task-recurring-delete b/task-recurring-delete new file mode 100755 index 0000000..444afcc --- /dev/null +++ b/task-recurring-delete @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# For taskwarrior users: +# Delete all overdue & duplicate tasks and keep only the first of a kind. +# +# Needs the taskw Python bindings to run. + +__requires__ = ["taskw >= 0.8.3"] +import pkg_resources + +from subprocess import Popen, PIPE, STDOUT +from uuid import UUID + +import collections +import datetime +import os +import pytz +import taskw + + +def task_delete(uuid): + """Delete the given task.""" + + assert isinstance(uuid, UUID) + + # This is somewhat a hack, but as TaskWwarrior 2.4.0 does NOT disable + # the 'Do you want to delete all pending recurrences ...' confirmation + # on rc.confirmation=off, so this is necessary. + DEVNULL = open(os.devnull, 'wb') + p = Popen(['task', 'rc.confirmation=off', str(uuid), 'delete'], + stdin=PIPE, stdout=DEVNULL, stderr=STDOUT) + p.communicate(input=bytes('no', 'UTF-8')) + + +w = taskw.TaskWarrior(marshal=True) +tasks = w.load_tasks() + + +# Only (over-)due and recurring tasks are considered for deletion: +due_before = datetime.datetime.utcnow() +due_before = due_before.replace(tzinfo=pytz.utc) +recurring_tasks_due = [task for task in tasks['pending'] + if 'recur' in task + and 'parent' in task + and 'due' in task and task['due'] < due_before] + +# Delete all but the first of all (over-)due and duplicate tasks: +parents = collections.Counter([task['parent'] + for task in recurring_tasks_due]) +for parent in parents: + count = parents[parent] + if count > 1: + dupe_tasks = [task for task in recurring_tasks_due + if task['parent'] == parent] + dupe_tasks = sorted(dupe_tasks, key=lambda t: t['due']) + + dupe_tasks_to_keep = dupe_tasks[0:1] + dupe_tasks_to_trash = dupe_tasks[1:] + + print('Deleting {} duplicate due tasks: "{}"'.format( + len(dupe_tasks_to_trash), dupe_tasks_to_trash[0]['description'])) + for task in dupe_tasks_to_trash: + task_delete(task['uuid'])