Worklog: Difference between revisions
Jump to navigation
Jump to search
No edit summary |
(No difference)
|
Latest revision as of 02:02, 30 August 2011
Worklog
#!/bin/sh
# worklog
#
# (C)2010 Marcel Post. All Rights Reserved
# Last modified: 22 December 2010 (optimised for Bashv4, possibly Dash too)
#
# This script tracks changes made to files in a target directory.
# The way it works is that it will calculate the time between when
# a file was first edited (poll every 5 minutes) and when the last
# change was made (assuming that no edits were made for 60 minutes).
#
# For permissions, the cron executer must own the 'worklog' file
#
# Example cron entry:
#
# */5 * * * * /usr/local/bin/worklog /var/www/attendance [path-to-worklog-file]
#
#
# BUGS
#
# once had that a file could not be 'stat' by worklog, as if it was locked by
# vi when I was writing back the changes or so..
#
#
# TODO
# - add a --help switch with version number etc
#
# User Variables:
WL=worklog # name and location of the worklog file
TMPDIR=/tmp # a temp directory that we can safely write to
IDLEMINS=20 # when we should consider an edit to be finished
DEBUG=0 # turn on for more verbose output
# --- script follows ---
if test $# -lt 1
then
# no arguments provided
echo "Syntax: worklog [path-to-worklog-file]"
exit 1
fi
if test $# -lt 2
then
# one argument provided. Can either be:
# -show to display the worklog file, or
# to find changes in the 'path' files and directories and
# write the updates to the worklog file located in 'path'
if test $1 = "-show"
then
# use current directory to find the workdir file
DEBUG=2
# set projectdir
PROJECTDIR=`pwd`
# set worklog file
WORKLOG=$PROJECTDIR/$WL
else
# using project directory as specified with argument 1
# also use the path directory to find the worklog file
if test -d $1
then
# project dir exists ok
PROJECTDIR=$1
# set worklog file
WORKLOG=$PROJECTDIR/$WL
else
echo "Project directory does not exist"
exit 1
fi
fi
else
# at least two arguments provided. Can either be:
# -show to display the worklog file in the
# specified 'path'
# update worklog in a possibly different
# location than the 'path' specification
if test $1 = "-show"
then
# use second argument to find the worklog file
DEBUG=2
# set location of worklog file
if test -f $2
then
# worklog file exists ok
WORKLOG=$2
else
echo "Could not access worklog file at $2"
echo "Please create a worklog file by running an update first"
echo "Syntax: worklog [path-to-worklog-file]"
exit 1
fi
else
# test if arguments link to an existing directory and a worklogfile
if test -d $1
then
# project dir exists ok
PROJECTDIR=$1
else
echo "Project directory does not exist"
exit 1
fi
# just set the name and location of the worklog file to the second argument
# testing if it exists happens further in the script
WORKLOG=$2
fi
fi
# uncomment for debugging purposes
#if test $# -eq 2
#then
# if test $2 = "-d"
# then
# DEBUG=1
# echo
# fi
# if test $2 = "-show"
# then
# # show real dates instead of timestamps
# DEBUG=2
# echo
# fi
#fi
# test if project directory exists
if ! test -d $PROJECTDIR
then
echo "Project directory not found"
exit 1
fi
# test if logfile exists
if test -f $WORKLOG
then
# -- check existing worklog file --
# check if worklog has a filesize greater than zero
if test -s $WORKLOG
then
# check if we can recognise the header of the file
WORKLOG_HEADER=`head -n 1 $WORKLOG`
if test `echo $WORKLOG_HEADER | awk -F : '{print $1}'` = "LAST-CHANGED" > /dev/null 2>&1
then
NEWFILE=0
else
echo "The worklog file seems corrupt. Please check."
echo "if possible delete $WORKLOG"
exit 1
fi
else
NEWFILE=1
fi
# check permissions
if ! test -O $WORKLOG
then
if test $DEBUG != 2
then
echo "File $WORKLOG is not owned by the user running this script! Exiting now."
exit 1
fi
fi
# check if we have write permissions to the worklog file
# who is running this script
RUNUID=`set | grep ^UID | awk -F = '{print $2}'`
#get stat from logfile
LOGFSTAT=`/usr/bin/stat $WORKLOG | grep Uid`
#check if Uid is same as the user running this script
LOGF_PERMS=`echo $LOGFSTAT | awk -F Uid '{print $1}' | awk -F / '{print $2}' | cut -c 3`
if test "$LOGF_PERMS" != "w"
then
echo "No write permissions to worklog file! Exiting now."
exit 1
fi
# Excellent!
# if we're still in the running here, we've got:
#
# - an existing worklog file (maybe zero length)
# - owned by the runner of this script
# - with write permissions
else
# -- create new worklog file --
if test $DEBUG = 2
then
echo "Please create a worklog file by running an update first"
echo "Syntax: worklog [path-to-worklog-file]"
exit 1
else
echo "Worklog at $WORKLOG not found"
echo -n "Attempting to create a new worklog file... "
if touch $WORKLOG > /dev/null 2>&1
then
echo "ok"
if test $DEBUG = 1
then
echo "new worklog created"
fi
NEWFILE=1
else
echo "Permission denied!"
exit 1
fi
fi
fi
# -- part 1 b --
# show human readable dates and summarise time spent
# if we were called with the -show argument now is the
# time to display the worklog file using real date (ymd)
# instead of timestamps.
if test $DEBUG = 2
then
echo "worklog for $1"
TOTALTIME=0
TOTALTIME_W=0
TOTALTIME_C=0
MAXLINES=`grep -c minutes $WORKLOG`
MAXLINES=$((MAXLINES + 2))
LINENR=3
PREVWEEK=0
DO_WEEKSUMMARY=0
while test $LINENR -le $MAXLINES
do
READLN=`cat $WORKLOG | head -n $LINENR | tail -n 1`
# only process completed entries..
if echo $READLN | grep minutes > /dev/null 2>&1
then
DO_PRINT=1
BEGINTIME=`echo $READLN | awk '{print $1}'`
WEEKNR=`date -d @$BEGINTIME +%U`
if test $PREVWEEK -eq 0
then
PREVWEEK=$WEEKNR
else
if test $WEEKNR != $PREVWEEK
then
DO_WEEKSUMMARY=1
fi
fi
else
# incomplete line detected. Don't print anything
DO_PRINT=0
fi
# show week summary line
if test $DO_WEEKSUMMARY -eq 1
then
if test $TOTALTIME -gt 0
then
# new week detected
TOTAL_HOURS_W=$((TOTALTIME_W / 60))
TOTAL_HXM_W=$((TOTAL_HOURS_W * 60))
TOTAL_MINUTES_W=$((TOTALTIME_W - TOTAL_HXM_W))
echo -n "Summary for week $PREVWEEK: $TOTALTIME_W minutes $TOTAL_HOURS_W h $TOTAL_MINUTES_W m"
# cumulative week totals
TOTALTIME_C=$((TOTALTIME_C + TOTALTIME_W))
TOTAL_HOURS_C=$((TOTALTIME_C / 60))
TOTAL_HXM_C=$((TOTAL_HOURS_C * 60))
TOTAL_MINUTES_C=$((TOTALTIME_C - TOTAL_HXM_C))
echo " ($TOTAL_HOURS_C h $TOTAL_MINUTES_C m)"
echo "--"
PREVWEEK=$WEEKNR
fi
DO_WEEKSUMMARY=0
TOTALTIME_W=0
fi
BEGINTIME=`date -d @$BEGINTIME`
ENDTIME=`echo $READLN | awk '{print $2}'`
ENDTIME=`date -d @$ENDTIME`
DIFFERENCE=`echo $READLN | awk '{print $3}'`
TOTALTIME_W=$((TOTALTIME_W + DIFFERENCE))
TOTALTIME=$((TOTALTIME + DIFFERENCE))
# print entry on screen
echo "$BEGINTIME to $ENDTIME = $DIFFERENCE minutes"
# show week summary if we've reached the last line
if test $LINENR -eq $MAXLINES
then
TOTAL_HOURS_W=$((TOTALTIME_W / 60))
TOTAL_HXM_W=$((TOTAL_HOURS_W * 60))
TOTAL_MINUTES_W=$((TOTALTIME_W - TOTAL_HXM_W))
echo -n "Summary for week $PREVWEEK: $TOTALTIME_W minutes $TOTAL_HOURS_W h $TOTAL_MINUTES_W m"
# cumulative week totals
TOTALTIME_C=$((TOTALTIME_C + TOTALTIME_W))
TOTAL_HOURS_C=$((TOTALTIME_C / 60))
TOTAL_HXM_C=$((TOTAL_HOURS_C * 60))
TOTAL_MINUTES_C=$((TOTALTIME_C - TOTAL_HXM_C))
if test $TOTALTIME_C -eq $TOTALTIME_W
then
echo
else
echo " ($TOTAL_HOURS_C h $TOTAL_MINUTES_C m)"
fi
echo "--"
fi
LINENR=$((LINENR + 1))
done
echo "---------------------------"
echo -n " $TOTALTIME minutes"
TOTAL_HOURS=$((TOTALTIME / 60))
TOTAL_HXM=$((TOTAL_HOURS * 60))
TOTAL_MINUTES=$((TOTALTIME - TOTAL_HXM))
echo " $TOTAL_HOURS h $TOTAL_MINUTES m"
exit 1
fi
# -- part 2 --
# track changes
# find out which file was lastly changed
# the first line of the worklog file will be used to track changes
#
# Example:
# LAST-CHANGED : 1273572131 left.php
# get just the file name of a regular file that was changed last
LC_FILENAME=`/bin/ls -tr1 $PROJECTDIR | grep -v $WL | tail -n 1`
# get the timestamp for this file
LC_TIMESTAMP=`/usr/bin/stat --format=%Y $PROJECTDIR/$LC_FILENAME`
# compare this with what's in the worklog
# -- create worklog header in case of new file --
if test $NEWFILE = 1
then
WORKLOG_HEADER="LAST-CHANGED : $LC_TIMESTAMP $LC_FILENAME"
echo $WORKLOG_HEADER > $WORKLOG
echo "--" >> $WORKLOG
if test $DEBUG = 1
then
echo "created new worklog header for file: $LC_FILENAME"
fi
fi
# update the header if a change has been detected
if echo $WORKLOG_HEADER | grep -e "$LC_TIMESTAMP $LC_FILENAME" > /dev/null 2>&1
then
# no changes detected
CHANGES=0
if test $DEBUG = 1
then
echo "no changes detected"
fi
else
CHANGES=1
# a recent change has been made
if test $DEBUG = 1
then
echo "changes detected in file: $LC_FILENAME"
echo "updating header"
fi
# update the worklog header
# get the total number of lines in the worklog (minus one)
WL_MAXLINES=`wc -l $WORKLOG | awk '{print $1}'`
WL_MAXLINES=$((WL_MAXLINES - 1))
#set the new header
WORKLOG_HEADER="LAST-CHANGED : $LC_TIMESTAMP $LC_FILENAME"
#rewrite the worklog file
cat $WORKLOG | tail -n $WL_MAXLINES > $TMPDIR/tmpfil_worklog.001
echo $WORKLOG_HEADER > $WORKLOG
cat $TMPDIR/tmpfil_worklog.001 >> $WORKLOG
rm $TMPDIR/tmpfil_worklog.001
fi
# -- lastline updating --
# get the last line of the worklog
WL_LASTLINE=`tail -n 1 $WORKLOG`
WL_LL_LENGTH=${#WL_LASTLINE}
# the length of the line is an indication as to what's written in it
# length up to 18 chars = unknown
# length is greater than 30 chars = minutes have been calculated
if test $CHANGES = 1
then
if test $WL_LL_LENGTH = 2 || test $WL_LL_LENGTH -gt 18
then
# open new lastline
WL_LASTLINE="$LC_TIMESTAMP unknown"
echo $WL_LASTLINE >> $WORKLOG
if test $DEBUG = 1
then
echo "opening new tracker line"
fi
fi
fi
# -- timestamp updating --
# are we checking after the idle time?
# get timestamp for right now
TIME_NOW=`date +%s`
# create timestamp for LC_TIMESTAMP + ( N x 60 )
IDLESECS=$((IDLEMINS * 60))
TIMESTAMP_IDLE=$((LC_TIMESTAMP + IDLESECS))
# test if our present time is the IDLE time past the Last Changed time
if test $TIME_NOW -gt $TIMESTAMP_IDLE
then
# we're past our idle time
# update 'unknown' with the last known time from the header
# and calculate the difference
#
# just exit if unkown is not the last keyword
if test $WL_LL_LENGTH = 18
then
# line has not been closed yet, do so now
if test $DEBUG = 1
then
echo "past idle time, closing tracker line"
fi
# get the last known change time from the header
WL_TS=`head -n 1 $WORKLOG | awk '{print $3}'`
#rewrite the last line of the logfile
# get the total number of lines in the worklog (minus one)
WL_MAXLINES=`wc -l $WORKLOG | awk '{print $1}'`
WL_MAXLINES=$((WL_MAXLINES - 1))
#set the lastline with an updated timestamp
WL_LASTLINE_PRE=`echo "$WL_LASTLINE" | awk '{print $1}'`
#calculate the time between start and finish
WL_STARTTIME=`tail -n 1 $WORKLOG | awk '{print $1}'`
TIMESPAN=$((WL_TS - WL_STARTTIME))
TIMESPAN=$((TIMESPAN / 60))
WL_LASTLINE="$WL_LASTLINE_PRE $WL_TS $TIMESPAN minutes"
#rewrite the worklog file
cat $WORKLOG | head -n $WL_MAXLINES > $TMPDIR/tmpfil_worklog.001
cat $TMPDIR/tmpfil_worklog.001 > $WORKLOG
if [ $TIMESPAN -le 0 ] || [ $TIMESPAN -gt 720 ]
then
# ignore updating the last line, it was probably faulty anyway
echo > /dev/null
if test $DEBUG = 1
then
echo "timespan zero, negative or too big, not writing tracker line"
echo "details:"
echo "original starttime: $WL_STARTTIME"
echo "last changed time: $WL_TS"
fi
else
echo $WL_LASTLINE >> $WORKLOG
if test $DEBUG = 1
then
echo "updating tracker line with timespan"
fi
fi
rm $TMPDIR/tmpfil_worklog.001
fi
fi
if test $DEBUG = 1
then
echo "Timestamp now: `date +%s` `date`"
fi
# That's all folks