scripts/log-hours.sh

250 lines
5.2 KiB
Bash

function log-hours {
case $1 in
on)
shift
if [ $# -eq 0 ]
then
LAST_MESSAGE="$(grep 'BEGIN' \
"${HOME}/.log-hours" |
tail -1 |
cut -f 3-)"
echo -e "Reporting beginning of work with" \
"message\n\t${LAST_MESSAGE}"
log-hours_on "${LAST_MESSAGE}"
else
log-hours_on $*
fi
;;
off)
shift
log-hours_off $*
;;
total)
shift
if [ $# -gt 0 ]
then
log-hours_total $*
else
log-hours_total $(date -I)
fi
;;
today)
TODAY="$(date -I)"
log-hours_total "${TODAY}"
;;
yesterday)
YESTERDAY="$(grep -v "$(date -I)" \
"${HOME}/.log-hours" |
tail -1 |
awk '{print $2}' |
cut -d'T' -f1)"
log-hours_total "${YESTERDAY}"
;;
edit)
"${EDITOR}" "${HOME}/.log-hours"
;;
*)
echo '[ERROR] '"'$1'"' is not a valid command.' >&2
return 1
;;
esac
return 0
}
function log-hours_on {
echo -e "BEGIN\t$(date -Imin)\t$*" >> "${HOME}/.log-hours"
}
function log-hours_off {
echo -e "END\t$(date -Imin)\t$*" >> "${HOME}/.log-hours"
}
function log-hours_total {
if DATE=$(date -I -d "$*" 2>/dev/null)
then
printf "Date:\t${DATE}\n"
grep "${DATE}" "${HOME}/.log-hours" |
awk -f <(cat <<-'EOF'
BEGIN {
begin = 0
i = 0
cut_cmd = "cut -f3-"
}
/^\<BEGIN\>/ {
to_epoch = ("date -d \"" $2 "\" +\"%s\"")
if (begin == 0) {
to_epoch | getline begin
print $0 |& cut_cmd
close(cut_cmd, "to")
cut_cmd |& getline task_title
task_titles[i] = task_title
}
close(to_epoch)
close(cut_cmd)
}
/^\<END\>/ {
to_epoch = ("date -d \"" $2 "\" +\"%s\"")
if (begin != 0) {
to_epoch | getline end
total += (end - begin)
task_hours[i] = (end - begin) / 3600.0
sum_rounded += round(task_hours[i], 0.25)
i++;
begin = 0
}
else {
print "ERROR: an END is found at", $2,
"but it is doesn't have an",
"equivalent BEGIN."
begin = 0
}
close(to_epoch)
}
END {
if (FNR == 0) {
print "WARNING: zero lines to process."
}
absolute = total / 3600.0
printf "Hours (absolute):\t%.2f\n", absolute
rounded = round(absolute, 0.25)
printf "Hours (rounded sum):\t%.2f\n", rounded
printf "Hours (sum of rounded):\t%.2f\n", sum_rounded
for (j = 0; j < i; j++) {
printf "\t%.2f\t%.2f\t%s\n", task_hours[j], round(task_hours[j], 0.25), task_titles[j]
#printf "\t%.2f\t%s\n", task_hours[j], task_titles[j]
}
}
function abs(value) {
if (value < 0) {
return (-1 * value);
}
return value;
}
function sgn(x) {
return (x < 0)? (-1) : 1;
}
function ceil(x) {
return (x == int(x)) ? x : int(x + 1)
}
function floor(x) {
return int(x)
}
function round(value, precision, value_to_round) {
# Unified the round (half [1]) away from zero [2] with the
# rounding to a specified multiple [3].
#
# [1]: https://en.wikipedia.org/wiki/Rounding#Round_half_away_from_zero
# [2]: https://en.wikipedia.org/wiki/Rounding#Round_away_from_zero
# [3]: https://en.wikipedia.org/wiki/Rounding#Rounding_to_a_specified_multiple
if (precision == 0) {
precision = 1.0
}
else {
value_to_round = value / (precision * 1.0)
}
# round half away from zero [1]
#return (sgn(value) * floor(abs(value_to_round) + 0.5) * precision)
# round away from zero [2]
return (sgn(value) * ceil(abs(value_to_round)) * precision)
}
EOF
)
else
echo "ERROR: '$*' is not a valid date. Try writing it" \
"according to the ISO 8601 format."
fi
}
function log-hours_complete {
if [ $3 == "on" ]
then
COMPREPLY=$(grep 'BEGIN' ~/.log-hours |
cut -f3- |
sort |
uniq -c |
sed 's/^\s*//' |
sort -t' ' -k1nr -k2 |
cut -d' ' -f2- |
fzy)
fi
}
# Bash completion for `log-hours on`
complete -o nospace -F log-hours_complete log-hours
function weeks-task {
seq $(( "$(date +'%u')" - 1 )) -1 0 |
while read ENTRY
do
egrep $'BEGIN\t'"$(date -I --date="$ENTRY days ago")" ~/.log-hours # filters only this week's entries
done |
cut -f 3- | # take just the logged entries
sed -e 's/^[[:blank:]]*//' -e 's/[[:blank:]]*$//' | # trimming the entries
awk 'entry[$0]++ == 0' # print only the first appearance of the entry
}
function weeks-hours {
MATCH_WORD="absolute"
if [ -n "$1" ]
then
case $1 in
absolute)
MATCH_WORD="absolute"
;;
rounded)
MATCH_WORD="rounded sum"
;;
sum-rounded)
MATCH_WORD="sum of rounded"
;;
*)
echo '[ERROR] '"'$1'"' is not a valid' \
'command.' >&2
return 1
;;
esac
fi
seq $(( "$(date +'%u')" - 1 )) -1 0 |
while read ENTRY; do
log-hours total "$(date -I --date="$ENTRY days ago")"
done |
sed '/'"${MATCH_WORD}"'/{s/^.*\t//;p};/./d' |
tr '\n' '+' |
sed 's/+$/\n/' |
bc
}
function redmine {
COMMAND=${*:-yesterday}
CONTENT=$(log-hours ${COMMAND})
# Print header.
echo "${CONTENT}" |
sed -ne '/^[^[:blank:]]/{p}'
# Unify hours per task.
echo "${CONTENT}" |
egrep '^[[:blank:]]' |
awk -F '\t' '
{
precise[$4] = precise[$4] + $2
rounded[$4] = rounded[$4] + $3
}
END {
for (t in precise) {
printf("\t%0.2f\t%0.2f\t%s\n", precise[t], rounded[t], t)
}
}' |
sort -t $'\t' -k4
}