Browse Source

Add overduer, TaskWarrior re-scheduling wrapper

Alois Mahdal 6 years ago
parent
commit
5c47c5a944
2 changed files with 273 additions and 0 deletions
  1. 30
    0
      README.md
  2. 243
    0
      bin/overduer

+ 30
- 0
README.md View File

@@ -87,6 +87,36 @@ is passed.
87 87
 Use `-l` to list supported TEMPLATEs
88 88
 
89 89
 
90
+### overduer ###
91
+
92
+    overduer [-d] [--] [FILTER]
93
+    overduer --help
94
+
95
+Wrapper around TaskWarrior that helps you quickly re-schedule tasks that
96
+are over or quickly approaching their due date.
97
+
98
+The main idea behind *overduer* is that if "pushing" tasks becomes
99
+extremely easy and fast, you can develop a habit of constantly re-visiting
100
+your tasks without it being a nuissance.  This will help you avoid the
101
+most common risk asssociated with task planning:
102
+
103
+ *  completely forgetting about them (if you set no due date or too much
104
+    in the future)
105
+
106
+ *  being frustrated by the ever-growing pile,
107
+
108
+ *  or learning to just ignore TaskWarrior altogether.
109
+
110
+*overduer* is heavily inspired by Git's interactive rebase: it uses
111
+*vipe* utility to open simple line-based list that you can edit and save,
112
+while lines that you did not edit or lines that you deleted are ignorred.
113
+
114
+This gives perfect balance between speed and safety: First, it only
115
+takes few seconds to deal with tasks you are immediately sure about and
116
+just ignore the rest.  Also it provides way to bail out from mistake:
117
+just delete the whole buffer, exit editor (save changes!) and start over.
118
+
119
+
90 120
 ### pfile ###
91 121
 
92 122
 Wrapper around *file* utility.  Reads STDIN, stores it in a temporary file

+ 243
- 0
bin/overduer View File

@@ -0,0 +1,243 @@
1
+#!/bin/bash
2
+
3
+OVERDUER_FILTER=${OVERDUER_FILTER:-"( +OVERDUE or +WEEK )"}
4
+
5
+usage() {
6
+    local self
7
+    self=$(basename "$0")
8
+    echo "usage: $self [--] [FILTER]" >&2
9
+    echo "usage: $self --help" >&2
10
+    exit 2
11
+}
12
+
13
+help() {
14
+    local self
15
+    self=$(basename "$0")
16
+    echo "Usage:"
17
+    echo "  $self [-H] [-i] [--] [FILTER...]"
18
+    echo ""
19
+    echo "TaskWarrior wrapper to help manage (approaching!) due dates."
20
+    echo ""
21
+    echo "Options:"
22
+    echo ""
23
+    echo "  --  Stop processing options; treat rest of arguments as"
24
+    echo "      TaskWarrior filter expression."
25
+    echo ""
26
+    echo "  -n  Do not modify any tasks; just show command that would do so."
27
+    echo "      Note: this does not turn off listing commands which, by design"
28
+    echo "      of TaskWarrior, can change numeric task IDs (not UUIDs)."
29
+    echo ""
30
+    echo "  -H  Do not include help messages in the template."
31
+    echo ""
32
+    echo "  -i  Include details of each task (\`task info\`) inside the"
33
+    echo "      template."
34
+    echo ""
35
+    echo "$self will use listing of TaskWarrior tasks past or close to their"
36
+    echo "due date to create a template that will be automatically opened in"
37
+    echo "your favorite editor.  By editing the template and exiting the"
38
+    echo "editor, you can assign new due dates to any of these tasks."
39
+    echo "(Read instructions embedded inside the template for details.)"
40
+    echo ""
41
+    echo "If any tasks are modified, their new state is printed after the"
42
+    echo "fact.  (Hint: use \`task undo\` to revert last change)."
43
+    echo ""
44
+    echo "By default, filter '( +OVERDUE or +WEEK )' is used.  This can be"
45
+    echo "overriden by setting environment variable OVERDUER_FILTER or"
46
+    echo "specifying filter expression directly on command line."
47
+    echo ""
48
+    echo "You need to have vipe (from moreutils) installed and set EDITOR"
49
+    echo "environment variable, see vipe(1) for details."
50
+    exit 0
51
+}
52
+
53
+warn() {
54
+    local msg
55
+    for msg in "$@";
56
+    do
57
+        echo "$msg" >& 2
58
+    done
59
+}
60
+
61
+distill() {
62
+    #
63
+    # Remove comments and everything except due expression and UUID from stdin
64
+    #
65
+    local due
66
+    local rest
67
+    local uuid
68
+    grep -v "^[[:space:]]*#" \
69
+      | while read -r due rest;
70
+        do
71
+            test -n "$due" || continue
72
+            uuid=${rest##* }
73
+            test -n "$uuid" || continue
74
+            echo "$due $uuid"
75
+        done
76
+}
77
+
78
+maybe_do() {
79
+    #
80
+    # Maybe do it, maybe just say it
81
+    #
82
+    case $Dry in
83
+        true)   warn "$*" ;;
84
+        false)  "$@" ;;
85
+    esac
86
+}
87
+
88
+apply() {
89
+    #
90
+    # Apply each pair of DUE UUID on stdin
91
+    #
92
+    local due
93
+    local uuid
94
+    local es=0
95
+    while read -r due uuid;
96
+    do
97
+        task info "$uuid" >/dev/null || {
98
+            warn "ignoring invalid UUID: $due for $uuid"
99
+            es=3
100
+            continue
101
+        }
102
+        task calc "$due" >/dev/null || {
103
+            warn "ignoring invalid due date: $due for $uuid"
104
+            es=3
105
+            continue
106
+        }
107
+        maybe_do task "$uuid" modify "due:$due"
108
+        task "$uuid" info
109
+    done
110
+    return $es
111
+}
112
+
113
+mkhelp1() {
114
+    #
115
+    # Print first part (before listing) of the help text
116
+    #
117
+    $AddHelp || return 0
118
+    echo "#"
119
+    echo "# Edit due dates below to your liking.  The resulting format"
120
+    echo "# must be:"
121
+    echo "#"
122
+    echo "#     TIME [anything] UUID"
123
+    echo "#"
124
+    echo "# where TIME is a valid TaskWarrior time expression (hint: use"
125
+    echo "# \`task calc TIME\`) and UUID, well, a valid task UUID.  The"
126
+    echo "# [anything] part is provided only for your convenience and will"
127
+    echo "# be ignored."
128
+    echo "#"
129
+    echo "# What will also be ignored:"
130
+    echo "#"
131
+    echo "#  *  comments like this,"
132
+    echo "#  *  empty lines,"
133
+    echo "#  *  template lines that were left unchanged."
134
+    echo "#"
135
+    echo
136
+    echo "#"
137
+    echo "# CUR_DUE ID DESCRIPTION                                                            UUID"
138
+    echo
139
+}
140
+
141
+mkhelp2() {
142
+    #
143
+    # Print second part (after the listing) of the help text
144
+    #
145
+    $AddHelp || return 0
146
+    echo
147
+    echo "#"
148
+    echo "# Note that CUR_DUE is ONLY DATE, NOT TIME, while actual due"
149
+    echo "# time may be set more precisely.  If that matters, consult"
150
+    echo "# full task info using syntax such as \`task ID info\` or -i."
151
+    echo "# switch."
152
+    echo "#"
153
+}
154
+
155
+mklisting() {
156
+    #
157
+    # Print task listing in form of "CUR_DUE If DESCRIPTION UUID"
158
+    #
159
+    task rc.detection:off rc.verbose:nothing \
160
+        rc.defaultwidth:120 \
161
+        rc.report.OVERDUER.columns:due,id,description,uuid \
162
+        rc.report.OVERDUER.labels:DUE,ID,DESC,UUID \
163
+        "${TaskFilter[@]}" \
164
+        OVERDUER 2>/dev/null \
165
+      | grep '^[^ ]' \
166
+      | grep -v "^[[:space:]]*-" \
167
+      | grep -v "^[[:space:]]*[A-Z]" \
168
+      | sort
169
+}
170
+
171
+mkinfos() {
172
+    #
173
+    # Print extra info for each listed task; all commented out
174
+    #
175
+    local uuid
176
+    $AddInfos || return 0
177
+    echo ""
178
+    echo ""
179
+    echo "#              #"
180
+    echo "# Task details #"
181
+    echo "#              #"
182
+    echo ""
183
+    mklisting \
184
+      | rev | cut -d' ' -f1 | rev \
185
+      | while read -r uuid;
186
+        do
187
+            task "$uuid" info 2>/dev/null \
188
+              | sed "s/^/# /"
189
+            echo
190
+        done
191
+}
192
+
193
+mktemplate() {
194
+    #
195
+    # Construct template text
196
+    #
197
+    mkhelp1
198
+    mklisting
199
+    mkhelp2
200
+    mkinfos
201
+}
202
+
203
+main() {
204
+    local Dry=false
205
+    local tmp
206
+    local es=0
207
+    local AddHelp=true
208
+    local AddInfos=false
209
+    local TaskFilter=()
210
+    while true; do case $1 in
211
+        -n)     Dry=true;      shift ;;
212
+        -H)     AddHelp=false; shift ;;
213
+        -i)     AddInfos=true; shift ;;
214
+        --)     shift; break ;;
215
+        --help) help  ;;
216
+        -*)     usage ;;
217
+        *)      break ;;
218
+    esac done
219
+    which vipe >/dev/null || {
220
+        warn "fatal: vipe is not installed"
221
+        exit 3
222
+    }
223
+    TaskFilter=("$@")
224
+    test -n "${TaskFilter[*]}" || TaskFilter=("$OVERDUER_FILTER")
225
+    tmp=$(mktemp -d -t overduer.main.XXXXXXXX)
226
+    mktemplate \
227
+      | tee "$tmp/old" \
228
+      | vipe > "$tmp/new"
229
+    <"$tmp/old" distill | sort >"$tmp/old.distilled"
230
+    <"$tmp/new" distill | sort >"$tmp/new.distilled"
231
+    comm -13 "$tmp/old.distilled" "$tmp/new.distilled" > "$tmp/changes"
232
+    if test -s "$tmp/changes";
233
+    then
234
+        <"$tmp/changes" apply; es=$?
235
+    else
236
+        warn "no changes detected; nothing to do"
237
+        es=1
238
+    fi
239
+    rm -r "$tmp"
240
+    return $es
241
+}
242
+
243
+main "$@"