added lua + micropython + kanban
This commit is contained in:
parent
30d7049838
commit
66d0865df0
|
@ -0,0 +1,353 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# commandline asciii kanban board for minimalist productivity bash hackers (csv-based)
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# kanban init # initialize kanban in current directory
|
||||
# kanban add # add item interactive (adviced)
|
||||
# kanban show [status] .... # show ascii kanban board [with status]
|
||||
# kanban <id> # edit or update item
|
||||
# kanban <id> <status> # update status of todo id (uses $EDITOR as preferred editor)
|
||||
# kanban <status> ..... # list only todo items with this status(es)
|
||||
# kanban list # list all todos (heavy)
|
||||
# kanban tags # list all submitted tags
|
||||
# kanban add <status> <tag> <description> # add item (use quoted strings for args)
|
||||
# kanban stats status [tag]
|
||||
# kanban stats tag
|
||||
# kanban stats history
|
||||
# kanban csv # edit raw csv
|
||||
#
|
||||
# NOTE #1: statuses can be managed in ~/.kanban/.kanban.conf
|
||||
# NOTE #2: the database csv can be found in ~/.kanban/.kanban.csv
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# kanban add TODO projectX "do foo"
|
||||
# kanban TODO DOING HOLD
|
||||
# kanban stats status projectX
|
||||
# kanban stats tag projectX
|
||||
# # notekeeping by entering a filename as description:
|
||||
# echo hello > note.txt && kanban add DOING note.txt
|
||||
# # store in github repo
|
||||
# git clone https://../foo.git && cd foo.git && kanban init && git add .kanban
|
||||
#
|
||||
# Environment:
|
||||
#
|
||||
# X=120 kanban .... # set max line-width to 120
|
||||
# NOCOLOR=1 kanban .... # disable colors
|
||||
# PLAIN=1 kanban ... # plaintext, disable utf8 chars
|
||||
#
|
||||
# Tips:
|
||||
#
|
||||
# * <current_dir>/.kanban or ~/.kanban dir is checked for configuration / data
|
||||
# * put .kanban directories in project-dirs
|
||||
# * entering an existing text-filename as description enables note-keeping
|
||||
#
|
||||
# Copyright (C) 2015, Leon van Kammen / Coder of Salvation
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
deps="tput column pr gawk"
|
||||
for dep in $deps; do which $dep &>/dev/null || { echo "$dep-cmd not installed..aborting (maybe install coreutils util-linux pkgs?)"; exit 1; }; done
|
||||
BOX=(┌ ─ ┐ └ ┘ │ ┤ ┴ ┬ ├ ┼)
|
||||
BAR=(▁ ▂ ▃ ▄ ▅ ▆ ▆ ▇ ▇ '█' '█')
|
||||
COL0="\033[0m"
|
||||
COL1="\033[37;1m"
|
||||
COL2="\033[91;1m"
|
||||
COL3="\033[91;5m"
|
||||
TMP=~/.kanban.tmp
|
||||
[[ ! -n $TERM ]] && TERM=vt100
|
||||
locale | grep -q "UTF-8" && UTF8=1
|
||||
[[ -n $PLAIN ]] && unset UTF8
|
||||
[[ ! -n $X ]] && X=$(tput cols) # get size of terminal window
|
||||
[[ ! -n $Y ]] && Y=$(tput lines) #
|
||||
SMALLSCREEN=('SCHEDULED' 'HOLD' 'DOING') # uncomment to get simplified kanban board
|
||||
[[ ! -n $XSMALL ]] && XSMALL=119
|
||||
FILE_CONF=".kanban/.kanban.conf"
|
||||
FILE_CSV=".kanban/.kanban.csv"
|
||||
[[ ! -n $EDITOR ]] && {
|
||||
which nano &>/dev/null && EDITOR=nano
|
||||
which pico &>/dev/null && EDITOR=pico
|
||||
}
|
||||
|
||||
# migration: move config files of old kanban versions to .kanban
|
||||
moveconfig(){
|
||||
mkdir $1/.kanban
|
||||
echo "[!] old config files detected..moving to .kanban-folder"
|
||||
mv $1/.kanban.* $1/.kanban/.
|
||||
sleep 3s
|
||||
clear
|
||||
}
|
||||
[[ -f ~/.kanban.csv ]] && moveconfig ~
|
||||
[[ -f .kanban.csv ]] && moveconfig "$(pwd)"
|
||||
|
||||
|
||||
config_example="# kanban config file
|
||||
statuses=('TODO' 'HOLD' 'DOING' 'DONE' 'NOTES' 'BACKLOG')
|
||||
|
||||
XSMALL=119 # show small kanban for terminalwidth < 119 chars
|
||||
SMALLSCREEN=('DOING' 'TODO' 'HOLD') # define simplified kanban board statuses
|
||||
|
||||
# maximum amount of todos within status (triggers warning when exceeds)
|
||||
declare -A maximum_todo
|
||||
maximum_todo[HOLD]=10
|
||||
maximum_todo[DOING]=5
|
||||
"
|
||||
|
||||
# usage: fb put <x> <y> <string>
|
||||
put() { printf "\x1B["$2";"$1"f$3" "$4"; }
|
||||
|
||||
strtoline(){ for((i=0;i<${#1};i++)); do printf "$2"; done; }
|
||||
|
||||
draw_line(){
|
||||
for((i=0;i<$1;i++)); do printf "-"; done
|
||||
}
|
||||
|
||||
# usage: fb box <x> <y> <width> <height>
|
||||
draw_topline(){
|
||||
w=$((X-2));
|
||||
printf ${BOX[0]}; draw_line $w; printf "${BOX[2]}\n"
|
||||
}
|
||||
|
||||
createconfig(){
|
||||
dir="$1"
|
||||
[[ ! -n $1 ]] && {
|
||||
dir=~
|
||||
[[ -f ~/$FILE_CONF ]] && {
|
||||
read -p "overwrite current config? (y/n)" overwrite;
|
||||
[[ ! "$overwrite" == "y" ]] && echo "aborted" && exit 1;
|
||||
}
|
||||
}
|
||||
[[ ! -d $dir/.kanban ]] && mkdir $dir/.kanban
|
||||
echo "$config_example" > $dir/$FILE_CONF
|
||||
touch $dir/$FILE_CSV
|
||||
}
|
||||
|
||||
init(){ createconfig $(pwd); }
|
||||
|
||||
tags(){
|
||||
cat $KANBANFILE | awk -F',' '{ print $2 }' | sed 's/,.*//g;s/"//g' | tail -n+2 | sort | uniq | tr '\n' ' '
|
||||
}
|
||||
|
||||
get_statuses(){
|
||||
echo ${statuses[@]}
|
||||
}
|
||||
|
||||
add_interactive(){
|
||||
echo "enter description:"
|
||||
read -p "> " description
|
||||
echo "enter one of statuses: ${statuses[@]}"
|
||||
read -p "> " status
|
||||
echo "enter one of tags: $(tags)"
|
||||
read -p "> " tag
|
||||
add "$status" "$tag" "$description"
|
||||
}
|
||||
|
||||
add(){
|
||||
[[ ! -n $1 ]] && { add_interactive "$@"; return 0; }
|
||||
[[ ! "${statuses[*]}" =~ "$1" ]] && echo "invalid status $1 (possible: ${statuses[*]})" && exit 1
|
||||
status="$1"
|
||||
csvline='"'$1'","'$2'"'; shift;shift;
|
||||
csvline="$csvline,\"$*\",\"${status:0:1}\",\"$(get_current_date)\"\""
|
||||
echo "${csvline:0:$((${#csvline}-1))}" >> $KANBANFILE
|
||||
}
|
||||
|
||||
evaluate(){
|
||||
IFS=''; cat - | sed 's/\\/\\\\\\\\/g' | while read -r line; do
|
||||
[[ "$line" =~ '$' ]] && line="$(eval "echo \"$( echo "$line" | sed 's/"/\\"/g')\"")";
|
||||
echo "$line"
|
||||
done
|
||||
}
|
||||
|
||||
stats(){
|
||||
[[ ! -n $1 ]] && exit 1
|
||||
create_index
|
||||
field=$1; shift; tags="$*"
|
||||
greppattern="(${tags// /\|})"
|
||||
[[ "$field" == "status" ]] && field=2
|
||||
[[ "$field" == "tag" ]] && field=3
|
||||
[[ "$field" == "history" ]] && field=5
|
||||
[[ -n $2 ]] && WIDTH=$2 || WIDTH=20;
|
||||
[[ -n $3 ]] && PADDING=$3 || PADDING=20;
|
||||
{
|
||||
if [[ -n $PADDING ]]; then
|
||||
cat $TMP.index | grep -E "$greppattern" | gawk -vFS='^"|","|"$|",|,"|,' '{h[$'"$field"']++}END{for(i in h){print h[i],i|"sort -rn|head -20"}}' |gawk '!max{max=$1;}{r="";i=s='$WIDTH'*$1/max;while(i-->0)r=r"'"${BAR[5]}"'";printf "%'$PADDING's %5d %s %s",$2,$1,r,"\n";}'
|
||||
else
|
||||
cat $TMP.index | grep -E "$greppattern" | gawk -vFS='^"|","|"$|",|,"|,' '{h[$'"$field"']++}END{for(i in h){print h[i],i|"sort -rn|head -20"}}' |gawk '!max{max=$1;}{r="";i=s='$WIDTH'*$1/max;while(i-->0)r=r"'"${BAR[5]}"'";printf "%s %s: %5d\n",r,$2,$1;}' | tr -s " "
|
||||
fi
|
||||
} | grep -v 'tag\|status\|history\|-[ ]\+1' | grep -v '^[ ]\+1' # remove header rows
|
||||
}
|
||||
|
||||
_init(){
|
||||
trap "[[ ! -n \$NOCOLOR ]] && tput cnorm -- normal; " 0 1 5 # reset terminal colors to normal
|
||||
(( $X > $XSMALL )) && unset SMALLSCREEN
|
||||
[[ -n $NOCOLOR ]] && { COL1="";COL0="";COL2=""; COL3=""; }
|
||||
}
|
||||
|
||||
list(){
|
||||
tags="$*"
|
||||
greppattern="(${tags// /\|})"
|
||||
create_index
|
||||
echo -e "$COL1"
|
||||
cat $TMP.index | grep -E "$greppattern" | sort -k2 -t, | HEADER="id,status,tag,description,history,start,touched\n-,-,-,-,-\n" printcsv 6 | cut -c 1-$X | colorize 3
|
||||
rm $TMP.*
|
||||
}
|
||||
|
||||
create_index(){
|
||||
rm $TMP.index &>/dev/null
|
||||
cat -n $KANBANFILE | sed 's/^[ ]\+//g;s/\t/,/g;s/"\/.*\//"/g' | evaluate >> $TMP.index
|
||||
}
|
||||
|
||||
columnize(){
|
||||
awkformat='{ tag=substr($2,1,4); gsub($2,tag); printf("%-5s",$1); print " #"$2" "$3" "$4" "$5 }'
|
||||
[[ -n $SMALLSCREEN ]] && awkformat='{ print $1" "$3" "$4" "$5 }'
|
||||
i=1; lines="$(cat)"; header="$( echo "$lines" | head -n0 )"; output="";
|
||||
rm $TMP.col.* &>/dev/null
|
||||
for status in "${statuses[@]}"; do
|
||||
[[ -n $SMALLSCREEN ]] && ! [[ "${SMALLSCREEN[@]}" =~ $status ]] && continue
|
||||
label=$status
|
||||
nlines=$(cat $TMP.index | grep "$status" | wc -l)
|
||||
maxlines=0
|
||||
[[ -v "maximum_todo[$status]" ]] && maxlines=${maximum_todo[$status]}
|
||||
[[ ${maximum_todo[$status]} > 0 ]] && [[ $nlines > ${maximum_todo[$status]} ]] && label="*$status*"
|
||||
echo -e ".$(strtoline "$label" "~")~~.\n| $label |_______\n|" > $TMP.col.$i
|
||||
cat $TMP.index | grep "$status" | sed 's/["]\?'$status'["]\?//g' | \
|
||||
printcsv 5 | awk "$awkformat" | sed 's/^/| /g;s/ / /g' | unexpand >> $TMP.col.$i
|
||||
i=$((i+1))
|
||||
done
|
||||
pr -m -t -w$((X-5)) -S" " $TMP.col.* | lines
|
||||
rm $TMP.col.* # print and cleanup
|
||||
}
|
||||
|
||||
lines(){
|
||||
echo -e "$COL1"
|
||||
if [[ -n $UTF8 ]]; then
|
||||
cat | sed 's/| /│ /g;s/\.~/┌/g;s/~\./──┐/g;s///g;s/~/─/g;s/|\./└/g;s/|/│/g' # nice utf9 lines
|
||||
else
|
||||
cat | sed 's/~/-/g;s/|+/|/g;';
|
||||
fi | colorize 3
|
||||
}
|
||||
|
||||
colorize(){
|
||||
cat | awk '{
|
||||
a = gensub(/#([a-zA-Z0-9_\.-]+) /,"'$COL1'&'$COL0'","g")
|
||||
a = gensub(/\*([a-zA-Z0-9_\.-]+)\*/,"'$COL3'&'$COL0'","g",a)
|
||||
if( NR == '$1' ){ printf "'$COL0'"; }
|
||||
print a
|
||||
}'
|
||||
}
|
||||
|
||||
align(){
|
||||
cat | awk '{ for(i=3;i<=NF;i++){ $2=$2" "$i } printf "%-5s %s\n", $1,$2 ; }'
|
||||
}
|
||||
|
||||
show(){
|
||||
[[ -n $1 ]] && { cd $1; echo "[kanban] showing nested kanban '$1'"; $0 show; exit 0; }
|
||||
[[ ! -f "$KANBANFILE" ]] && touch "$KANBANFILE"
|
||||
create_index
|
||||
if [[ -n $1 ]]; then
|
||||
statuses=(); for status in $*; do statuses+=($status); done
|
||||
fi
|
||||
{
|
||||
echo "$1"
|
||||
if [[ -n $1 ]]; then cat $TMP.index | grep "$1"; else cat $TMP.index; fi
|
||||
} | columnize
|
||||
[[ -n $SMALLSCREEN ]] && echo -e " ${COL1}(${COL0} small terminal detected, hiding certain columns ${COL1})"
|
||||
echo ""
|
||||
}
|
||||
|
||||
get_current_date(){ date "+%Y-%m-%d@%H:%M"; }
|
||||
|
||||
update_item_status(){
|
||||
item="$( cat $KANBANFILE | awk "{ if (NR==$1) print \$0 }" )"
|
||||
[[ ${#item} == 0 ]] && echo "item $1 not found" && exit 1
|
||||
if [[ -n "$2" ]]; then # status change
|
||||
status="$(echo "$item" | awk -F',' '{ print $1 }' | sed 's/"//g' )"
|
||||
flags="$(echo "$item" | awk -F',' '{ print $4 }' | sed 's/"//g' )"
|
||||
dates="$(echo "$item" | awk -F',' '{ print $5 }' | sed 's/"//g' )"
|
||||
newflags="$flags${2:0:1}"
|
||||
newdates="$dates $(get_current_date)"
|
||||
#[[ "$status" =~ "\$(" ]] && { update_item $1; return 0; }
|
||||
[[ "$2" =~ "DONE" ]] && date="$(get_current_date)"
|
||||
newitem="$item"
|
||||
newitem="${newitem/$status/$2}"
|
||||
newitem="${newitem/$flags/$newflags}"
|
||||
newitem="${newitem/$dates/$newdates}"
|
||||
sed -i "s|$item|$newitem|g" $KANBANFILE
|
||||
echo "$status -> $2"
|
||||
fi
|
||||
}
|
||||
|
||||
update_item(){
|
||||
item="$( cat $KANBANFILE | awk "{ if (NR==$1) print \$0 }" )"
|
||||
[[ ${#item} == 0 ]] && echo "item $1 not found" && exit 1
|
||||
status="$(echo "$item" | awk -F',' '{ print $1 }')"
|
||||
file="$(echo "$item" | awk -F',' '{ print $3 }')"
|
||||
file="${file//\"/}"
|
||||
[[ -d $file/.kanban ]] && { show $file; return; }
|
||||
[[ -f $file ]] && ${EDITOR} "$file"
|
||||
echo '#
|
||||
# STATUSES ARE: '${statuses[*]}'
|
||||
#
|
||||
'"$item" > $TMP.update
|
||||
${EDITOR} $TMP.update
|
||||
sed -i "s|$item|$(cat $TMP.update | tail -n1)|g" $KANBANFILE
|
||||
echo "updated item $1"
|
||||
}
|
||||
|
||||
printcsv(){
|
||||
csv="$HEADER$(cat)"
|
||||
[[ ! -n $1 ]] && max=999999 || max=$1
|
||||
[[ ! -n $2 ]] && min=1 || min=$1
|
||||
echo -e "$csv" | sed 's/,"",/," ",/g' | gawk -vFS='^"|","|"$|",|,"|,' \
|
||||
'{out=""; for(i='$min';i<NF+1&&i<max;i++) out=out"\t"$i; print out }' \
|
||||
max=$max | sed 's/""/"/g' | column -t -s $'\t'
|
||||
}
|
||||
|
||||
csv(){
|
||||
${EDITOR} $KANBANFILE
|
||||
}
|
||||
|
||||
# source config
|
||||
[[ ! -f ~/$FILE_CONF ]] && { createconfig; }
|
||||
[[ -f $FILE_CONF ]] && source $FILE_CONF
|
||||
[[ ! -f $FILE_CONF ]] && source ~/$FILE_CONF
|
||||
|
||||
if [[ ! -n $KANBANFILE ]]; then
|
||||
[[ -f "$FILE_CSV" ]] && KANBANFILE="$(pwd)/$FILE_CSV"
|
||||
[[ ! -f $KANBANFILE ]] && KANBANFILE=~/"$FILE_CSV"
|
||||
fi
|
||||
[[ ! -f $KANBANFILE ]] && { echo "$KANBANFILE does not exist"; exit; }
|
||||
|
||||
|
||||
# execute main
|
||||
_init
|
||||
|
||||
if [[ -n "$1" ]]; then
|
||||
|
||||
[[ "${statuses[*]}" =~ "$1" ]] && { list "$@" ; exit 0; }
|
||||
case "$1" in
|
||||
|
||||
[0-9]*) [[ -n $2 ]] && {
|
||||
update_item_status "$@"
|
||||
[[ ! "${statuses[*]}" =~ "$2" ]] && echo -e "[!] burying in csv (visible statuses are: ${statuses[*]})"
|
||||
exit 0
|
||||
}
|
||||
update_item "$@"
|
||||
;;
|
||||
|
||||
*) "$@"
|
||||
;;
|
||||
esac
|
||||
else grep -A40 "^# Usage:" "$0" | sed 's/^# //g' | more ; fi
|
|
@ -17,6 +17,8 @@ BR2_TARGET_SYSLINUX=y
|
|||
|
||||
BR2_PACKAGE_UNZIP=y
|
||||
BR2_PACKAGE_ZIP=y
|
||||
BR2_PACKAGE_MICROPYTHON=y
|
||||
BR2_PACKAGE_LUA=y
|
||||
|
||||
# If you want to add the nled editor, uncomment NLED + NCURSES
|
||||
#BR2_PACKAGE_NCURSES=y
|
||||
|
|
Loading…
Reference in New Issue