|  | @@ -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 "$@"
 |