#!/bin/bash PROFILE='default' function debug() { echo $DEBUG if [ ${DEBUG:-0} -ne 0 ]; then echo "$(date): $@" fi } function help() { cat << EOF $0 - Captain's log Creates/opens files in which to log daily activity. Files are created with basic prompts. Create a file in "$CLDIR" called "schedule" (or use $0 schedule edit) with lines in the following format YYYY-MM-DD Task to review or = Recurring Task to review ; eg %A=Monday Task for monday OR using "$0 schedule " will achieve the same thing Create a file in "$CLDIR" called "template" with the base text to add to each day If the file is not executable, it is treated as a static template and is copied each day. The string {{DATE}} can be used to insert a date string. {{DATE }} can be used to replace the defualt format. i.e {{DATE %F}} fill produce the text $(date %F). If the file is executable, it will be run in the context of the user, and the output will be used instead. Create a file in "$CLDIR" named "backlog", and it will be opened along with the other daily files, split under "tomorrow" Directories created under $BASEDIR are treated as profiles (if they exist). The default profile is assumed to be 'default'. Specify -p as the first argument to $0 to operate on a different one Run with "review" ($0 review), and you will get a rundown of complete and incomplete tasks for the week. Optionally, supply with date-compatible strings as "$0 review [end] to use a range, end defaults to yesterday A task is considered complete if it matches the regex /^ \*/ and incomplete matching /* \-/ Also considering + to mean "a task that was not planned, but popped up during the course of the day". For the purposes of review it counts as incomplete, unless prefixed with \* I've also implemented /^ ~/, implying a continuing task (as opposed to - which I'm sort of considering "not only incomplete, but barely progressed/attempted"). For the purposes of review, ~ counts as Completed. I'm hoping to capture which tasks are is under-estimated. Events are also printed, assuming ! below Notes are generally ignored, but can be started with "^ #", "^ ;", or "^ !" for future functionality This will evolve with use, but generally (and in order of importance) ! signifies and event worthy of logging ; is a general note possibly worth reviewing # is for a verbose note, probably not of general interest but was worth noting If $CLDIR is a git repo (git rev-parse returns 0), it will be updated and auto committed unless --no-git is passed EOF } function review() { shift DATE_START=${1:-"-7 days"} DATE_END=${2:-"yesterday"} DATE_RANGE=$(( ($(date -d "$DATE_END" "+%s") - $( date -d "$DATE_START" "+%s")) /86400 )) if [ $DATE_RANGE -lt 0 ]; then echo "DATE_END is earlier than DATE_START" exit 1 fi # Make an array of possible dates to get stuff from declare -a DATES # This is stupid. {n..n} notation doesn't work with variables (no substitution) and seq doesn't do reverse ranges # so we use seq to get the range, then reverse it with tac for i in $(seq 0 $DATE_RANGE|tac ); do DATES[${#DATES[@]}]="$(date -d"$DATE_END -$i days" "+%F")" done for j in '\*|~' '-|\+' '!' ; do case $j in \\*\|\~) echo "Completed Tasks:";; \-\|\\+) echo "Incomplete Tasks:";; \!) echo "Events:";; esac for i in ${DATES[@]} ; do if [ -f "$CLDIR/$i" ]; then T="$(grep -P "^\s($j\s)" "$CLDIR/$i")" if [ "$T" != "" ]; then echo -n " ";date "+%A %d %B %Y" -d "$i" echo "$T"|sed "s/^/\t/" fi fi done echo done } function schedule() { shift if [ "$1" == "edit" ]; then if [ -f "$CLDIR/schedule"* ];then vi -p "$CLDIR/schedule"* else vi "$CLDIR/scheduled" fi exit fi if ! [[ $1 =~ = ]] && [ "$(date -d "$1" "+%s")" -lt $(date -d "today" "+%s") ]; then echo -n "$1 Looks like it's in the past, do you still wan to add it? [y/N]: " read answer if ! [[ "$answer" =~ ^[yY] ]]; then exit fi fi echo "$@" >> "$CLDIR/scheduled" } function captains_log() { # Take into account only the work week. # Might make sense in the future to check if there are any notes for the intervening days Sat/Sun # and display them if so if [ "$(date +%a)" == 'Mon' ]; then modY=3;fi if [ "$(date +%a)" == 'Fri' ]; then modT=3;fi YESTERDAY=$(date +%F -d "-${modY:-1} day") TODAY=$(date +%F) TOMORROW=$(date +%F -d "+${modT:-1} day") if [ ${GIT:-1} ] && (git -C $CLDIR rev-parse 2>/dev/null) ; then GIT=1 else GIT=0 fi if ! [ -d "$CLDIR" ]; then mkdir "$CLDIR" fi if [ $GIT -ne 0 ]; then git -C "$CLDIR" pull -q & fi # Create files if they don't exist for i in YESTERDAY TODAY TOMORROW; do if ! [ -f "$CLDIR/${!i}" ]; then # If there's a template and it's executable, execute it and output the result to the file, # If it's not executable, replate {{DATE }} with said date/formate # Otherwise do the boilerplate debug "File $i doesn't exist, creating it" if [ -x "$CLDIR/template" ]; then debug "From executable template" "$CLDIR/template" > $CLDIR/${!i} elif [ -f "$CLDIR/template" ]; then DATE_ARGS="$(grep '{{DATE[^}]*}}' "$CLDIR/template"|tr -d {}|cut -d' ' -f2-)" if [ "$DATE_ARGS" == 'DATE' ]; then DATE_ARGS='%A %d %B %Y'; fi debug "From static template with DATE_ARGS=$DATE_ARGS" sed "s/{{DATE[^}]*}}/$(date "+$DATE_ARGS" -d ${!i})/g" $CLDIR/template > $CLDIR/${!i} else echo -e "$(date "+%A %d %B %Y" -d ${!i})\nWhat do you want to accomplish today?\n\nWhat are your notes for today?\n\nWhat do you need to follow up tomorrow?\n" > "$CLDIR/${!i}" fi fi done # Read from the "scheduled" file, and put lines into the appropriate file if extant if [ -f "$CLDIR/scheduled" ] || [ -f "$CLDIR/schedule" ]; then while read line; do SCHEDULED=0 SCHED=$(echo $line|cut -d' ' -f1) TASK=$(echo $line|cut -d' ' -f2-) for i in YESTERDAY TODAY TOMORROW; do # Check if the schedule is DATE_FORMAT=MATCH_EXPRESSION otherwise assume %F if ( [[ "$SCHED" =~ = ]] && [[ "$( date "+$(echo "$SCHED"|cut -d'=' -f1)" -d "${!i}" )" =~ $(echo $SCHED |cut -d'=' -f2-) ]] ) || ( ! [[ "$SCHED" =~ = ]] && [ "$(date +%F -d $SCHED)" == "${!i}" ] ); then if ! grep -qE "^Scheduled to Review today:" "$CLDIR/${!i}"; then echo "Scheduled to Review today:" >> "$CLDIR/${!i}" fi if ! grep -qE "^ . $TASK" "$CLDIR/${!i}";then echo " - $TASK" >> "$CLDIR/${!i}" fi if ! [[ "$SCHED" =~ = ]]; then SCHEDULED=1 fi fi done if [ $SCHEDULED -eq 0 ]; then echo "$line" >> "$CLDIR/scheduled.tmp" fi done < <(cat "$CLDIR/scheduled" "$CLDIR/schedule" 2>/dev/null) fi # In theory, either everyhing is scheduled OR in the tmp file, so we delete it and do a swapsie rm "$CLDIR/scheduled" "$CLDIR/schedule" 2> /dev/null if [ -f "$CLDIR/scheduled.tmp" ]; then mv "$CLDIR/scheduled.tmp" "$CLDIR/scheduled" fi if [ -f "$CLDIR/backlog" ]; then vi $CLDIR/{$YESTERDAY,$TODAY,$TOMORROW,backlog} -s <( echo -e ":vsplit\n:wincmd w\n:next\n:vsplit\n:wincmd w\n:next\n:split\n:wincmd w\n:next") else vi -O $CLDIR/{$YESTERDAY,$TODAY,$TOMORROW} fi if [ $GIT -ne 0 ]; then echo Updating git... if [ $( git -C "$CLDIR" status -s |wc -l) -ne 0 ]; then git -C "$CLDIR" add -A git -C "$CLDIR" commit -qm "Auto-Commit by $(basename $0)" git -C "$CLDIR" push -q fi fi } BASEDIR="$HOME/.captains_log" # Check if there are any profiles, otherwise just use the basedir if [ "$(find $BASEDIR -type d|wc -l)" -gt 1 ]; then CLDIR="${BASEDIR}/${PROFILE:-default}" else CLDIR="$BASEDIR" fi while [ $1 ]; do case $1 in -p|--profile) CLDIR="$BASEDIR/$2"; shift 2;; --no-git) GIT=0;shift;; --debug) DEBUG=1; shift;; review) review "$@"; exit;; schedule) schedule "$@";exit;; help|--help|-h) help; exit;; *) break;; esac done captains_log