Compare commits

...

5 Commits

1 changed files with 226 additions and 17 deletions

243
clikan.sh
View File

@ -1,6 +1,6 @@
#!/bin/sh
# clikan.sh -- CLI Kanban
ver='2021-01-23/HB9KNS'
ver='2021-09-09/HB9KNS'
conf="${CLIKANCONF:-$HOME/.clikanconf}"
defkanban="$HOME/clikanban.md"
@ -8,7 +8,12 @@ editor="${CLIKANEDIT:-$VISUAL}"
editor="${editor:-$EDITOR}"
editor="${editor:-ed}"
myshell='/bin/sh -c'
filtr='.'
# pattern for target date and estimation: @YYYYMMDD:U.U
# (contains two groups in parentheses)
# if changed, also adjust line after TEPATT comment further down!
tepatt=' @\([0-9][0-9]*\):\([0-9][.0-9]*\)'
buff=`mktemp -t clikanbXXXXXX` || buff=${TMPDIR:-/tmp}/clikanb$$`date +%S%M%d`
self=`mktemp -t clikantXXXXXX` || self=${TMPDIR:-/tmp}/clikant$$`date +%S%M%d`
@ -29,10 +34,22 @@ EOT
# prompt is displayed when waiting for command
prompt |<
# maxshow defines the maximum number of cards/lines to be displayed
maxshow 15
maxshow 23
# sortdir defines order of non-calendar kanban cards
# and can be one of up, down or none (default)
sortdir none
sortdir up
# if newdate is 1, yes or true (actually, begins with'[1tTyY]'),
# then the current date will be appended to new cards/entries
newdate true
# planunit defines the name used for units (purely informative)
planunit h/day
# total units per period of scheduled tasks, e.g hours per day
tunits 24
# planpart defines the percentage of time available for working on
# scheduled/planned tasks (integer value), for example
# '10' means 10% of total time is usable, like 2.4 h/day
# (tunits*planpart/100)
planpart 10
# kanban defines a kanban file, may be given several times
# kanban /some/path/to/jobkanban.txt
# kanban /another/path/to/privatekanban.md
@ -92,6 +109,7 @@ getselect(){
fi
}
# read config, set default values if missing
kbs="`getlines kanban <"$conf"`"
currkan="`echo "$kbs" | head -n 1`"
prompt=`getlines prompt <"$conf" | head -n 1`
@ -99,6 +117,17 @@ maxshow=`getlines maxshow <"$conf" | head -n 1`
maxshow=${maxshow:-22}
sortdir=`getlines sortdir <"$conf" | head -n 1`
sortdir=${sortdir:-none}
nd=`getlines newdate <"$conf" | head -n 1`
case $nd in
1|[yY]*|[tT]*) newdate=true ;;
*) newdate=false ;;
esac
planpart=`getlines planpart <"$conf" | head -n 1`
planpart=${planpart:-10}
planunit=`getlines planunit <"$conf" | head -n 1`
planunit=${planunit:-h/day}
tunits=`getlines tunits <"$conf" | head -n 1`
tunits=${tunits:-24}
showprompt(){
if test "$prompt" != ""
@ -143,17 +172,17 @@ showall(){
fi | showselect "$f " | head -n $ms
}
# replace lines (in any kanban) containing some ed pattern
# arg.1=pattern, remainder=new line contents
# replace lines (in any kanban) containing some string
# arg.1=string, remainder=new line contents
repline(){
local patt newl
patt="$1"
local strg newl
strg="$1"
shift
newl="$*"
for ff in $kbs
do cat "$ff" > $buff
cat $buff | { while read oldl
do if test "`echo \"$oldl\"`" = "$patt"
do if test "`echo \"$oldl\"`" = "$strg"
then echo "$newl"
else echo "$oldl"
fi
@ -192,22 +221,57 @@ command keys:
do N: put card N into doing state
note: done/wait/do accept several arguments separated by white space
f F: filter cards for grep pattern F (empty='.')
p: show planning (time scheduling) for active (doing) cards containing
@YYMMDD/U.U target date (YYMMDD) and expected duration (U.U)
h N U.U [[YY]MMDD]: update expected duration for card N and
optionally set new target date [YY]MMDD
(asks for date if card is missing @YYMMDD)
ec: directly edit config file -- DANGER!
ek: directly edit kanban file -- DANGER!
!: execute arguments with "$myshell"
note: if the command does not match to any of the
internal $0 commands, and starts with a letter,
note: if the command does not match any of the
internal commands, and starts with a letter,
you can even omit the leading exclamation mark!
EOH
;;
esac
}
# simple date number calculation:
# assume each month has 30 days
datnum() {
local dn ds
# normalize to YYYYMMDD
if test "${1%????}" = ""
then ds=`date +%Y`$1
elif test "${1%??????}" = ""
then ds=$century$1
else ds=$1
fi
# 12*YYYY
dn=$(( 12*${ds%????} ))
# remove leading YYYY
ds=${ds#????}
# add 1MM, subtract 100, multiply by 30
dn=$(( 30*($dn+1${ds%??}-100) ))
# add 1DD, subtract 100
dn=$(( $dn+1${ds#??}-100 ))
echo $dn
}
showall doing
showprompt
while read com coa1 coa2 coa3 coar
do case $com in
do
year=`date +%y`
longyear=`date +%Y`
century=${longyear%??}
month=`date +%m`
day=`date +%d`
today=$year-$month-$day
stoday=$century$year$month$day
case $com in
q) break ;;
d) showall doing ;;
w) showall waiting/todo/backlog ;;
@ -216,19 +280,22 @@ do|wait|done) if test "$coa1" = ""
then echo "card number?"
read coa1
fi
coar=`echo "$coa1 $coa2 $coa3 $coar" | sed -e 's/ *$/ /'`
coar=`echo "$coa1 $coa2 $coa3 $coar" | tr -cd ' 0-9' | sed -e 's/ *$/ /'`
if test "$coar" = " "
then coar=''
fi
while test "$coar" != ""
do
coa1=`getlines ${coar%% *} <$self`
stamp=''
case $com in
done) coa2='+'
stamp=`date '+ // %y-%m-%d'`
stamp=" // $today"
;;
wait) coa2='-' ;;
*) coa2='*' ;;
esac
coa3=`echo "$coa1" | sed -e "s/^./$coa2/"`
coa3="$coa2${coa1#?}"
repline "$coa1" "$coa3$stamp"
coar=${coar#* }
done
@ -241,12 +308,16 @@ k) echo "$kbs" | showselect
n) newc=`echo "$coa1 $coa2 $coa3 $coar" | sed -e 's/ *$//'`
if test "$newc" = ""
then
echo enter new card for target $currkan :
echo enter new card for kanban file $currkan :
read newc
else echo adding card for target $currkan
else echo adding card for kanban file $currkan
fi
if test $newdate = true
then stamp=" // $today"
else stamp=''
fi
if test "$newc" != ""
then echo '*' "$newc" >>$currkan
then echo '*' "$newc$stamp" >>$currkan
else echo no content found, nothing added
fi
showall doing
@ -258,6 +329,144 @@ ek) echo calling "$editor $currkan" ...
"$editor" "$currkan" ;;
f) filtr="${coa1:-.}"
echo "filtering for grep pattern '$filtr'" ;;
p) echo "## planning for $planpart% of $tunits $planunit"
# generate list, but hidden
showall doing >/dev/null
# total current capacity in 0.1 units, to calculate limit
# (planpart is in %=0.01 units, divide by 10 to get in 0.1)
cap=$(( $tunits*$planpart/10 ))
now=`datnum $stoday`
echo "$planunit task"
# get all tasks with planning information, sorted
grep "$tepatt" $self | sort -k 2 | { while read nr flag task
do
# set weight for assigned time depending on priority value
case $task in
1*) w8=8 ;;
2*) w8=6 ;;
3*) w8=4 ;;
4*) w8=2 ;;
5*) w8=1 ;;
*) w8=3 ;;
esac
# get target date number of task, and normalize
tdate=`echo "$task" | sed -e "s/.*$tepatt.*/\1/"`
tnum=`datnum $tdate`
# difference in days
td=$(( $tnum-$now ))
# set minimum delta and mark
if test $td -gt 0
then tmark=' '
else
td=1
tmark='*'
fi
# get estimated units for task, append '.0' to force decimal notation,
# remove dot, keep one decimal and truncate additional figures
# (i.e multiply by 10), remove leading 0s
# TODO: following 2 lines are really ugly, should be simplified...
tunits=`echo "$task" | sed -e "s/.*$tepatt.*/\2/;s/$/.0/;s/[.]\([0-9]\).*/\1/;s/^0*//"`
# if empty, set to 0
tunits=${tunits:-0}
# assign 2*weight/8*tunits/days per task, but not more than estimated units
tcap=$(( $w8/4*$tunits/$td ))
if test $tcap -gt $tunits
then tcap=$tunits
# or at least 0.1
elif test $tcap -lt 1
then tcap=1
fi
# result: today's amount / (mark) / task nr / task
tres="$tcap $tmark$nr: $task"
if test $tcap -gt 99
then
# remove trailing figure for values of 10 (100) or higher
echo "$tres" | sed -e 's/. / /'
else
# for smaller, insert decimal and prepend 0 if missing
echo "$tres" | sed -e 's/\(.\) /.\1 /;s/^[.]/0./'
fi
if test $cap -gt 0
then cap=$(( $cap-$tcap ))
if test $cap -le 0
then echo ' (capacity limit reached)'
fi
fi
done
}
;;
h) if test "$coa1" = ""
then echo "card number?"
read coa1
fi
if test "$coa1" = ""
then showprompt
continue
fi
coa1=`echo "$coa1" | tr -cd '0-9'`
oldl=`getlines $coa1 <$self`
newl="$oldl"
echo ": ${oldl#??}"
if test "$coa2" = ""
then echo "new estimated time requirement?"
read coa2
fi
coa2=`echo "$coa2" | tr -cd '.0-9'`
if test "$coa2" = ""
then showprompt
continue
fi
# arg.3 will override existing target date
if test "$coa3" = ""
then
# search for ' @YYYYMMDD/U.U' and get YYYYMMDD
odat=`echo "$oldl" | sed -e "s/.*$tepatt.*/\1/"`
# if nothing found, ask for target date
if test "$odat" = "$oldl"
then
echo "target date [YYYY]MMDD?"
read coa3
# add dummy for later pattern replacement
newl="$newl @1:2"
else
coa3=$odat
fi
else
# remove old pattern and add dummy for later
newl=`echo "$oldl" | sed -e "s/\\(.*\\)$tepatt\\(.*\\)/\\1 @1:2\\4/"`
# force if nothing yet found
if test "$newl" = "$oldl"
then newl="$newl @1:2"
fi
fi
coa3=`echo $coa3 | tr -cd '0-9'`
if test "$coa3" = ""
then coa3=$stoday
echo "(empty/malformed date, using today=$coa3 instead)"
fi
# handle [YY]MMDD dates
# prepend 1 to prevent interpretation of leading 0 as octal
# (use 9999 instead of 1231 to cope with bogus inputs)
if test 1$coa3 -le 19999
then
# compare with today's MMDD
if test 1$coa3 -lt 1$month$day
# before, use next year, else this year
then coa3="$(( 1+$century$year ))$coa3"
else coa3="$century$year$coa3"
fi
elif test 1$coa3 -le 1999999
# prepend century if missing
then coa3=$century$coa3
fi
# TEPATT: adjust replacement string if $tepatt changed!
# (last reference is group 4 because of two groups in tepatt)
coar=`echo "$newl" | sed -e "s/\\(.*\\)$tepatt\\(.*\\)/\\1 @$coa3:$coa2\\4/"`
echo was: "$oldl"
echo now: "$coar"
repline "$oldl" "$coar"
showall doing
;;
!*) echo
$myshell "${com#!} $coa1 $coa2 $coa3 $coar"
echo ;;