New tiemkeeping implementation, in python this time
This commit is contained in:
parent
7187482f80
commit
6479f767b5
2 changed files with 260 additions and 66 deletions
255
bin/work
255
bin/work
|
@ -1,71 +1,194 @@
|
|||
#!/bin/sh
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
TIMEFILE="$HOME/timekeeping.csv"
|
||||
import argparse
|
||||
from signal import signal, SIGINT
|
||||
import sys
|
||||
from time import sleep
|
||||
from os.path import expanduser, join
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
usage () {
|
||||
>&2 printf "usage:\n\t%s <start|pause|end>" "$(basename "$0")"
|
||||
exit 0
|
||||
}
|
||||
TIME_FILE = join(expanduser("~"), "timekeeping.csv")
|
||||
TODAY_FILE = join(expanduser("~"), ".timekeeping")
|
||||
|
||||
die () {
|
||||
>&2 echo "Error: $*"
|
||||
exit 1
|
||||
}
|
||||
|
||||
from_ts () {
|
||||
if ! date --version >/dev/null 2>&1; then # BSD
|
||||
date -r "$1" '+%H:%M'
|
||||
else # GNU
|
||||
date -d "@$1" '+%H:%M'
|
||||
fi
|
||||
}
|
||||
|
||||
pause_end () {
|
||||
if [ "$ACTION" = "pause" ]; then
|
||||
now="$(date '+%s')"
|
||||
printf "%s," "$((now - START))" >> "$TIMEFILE"
|
||||
>&2 printf "\nYou took a %d minutes pause\n" "$(( (now - START) / 60))"
|
||||
fi
|
||||
}
|
||||
|
||||
trap pause_end INT
|
||||
|
||||
test -z "$1" && usage
|
||||
test -e "$TIMEFILE" || touch "$TIMEFILE"
|
||||
|
||||
TODAY="$(date '+%Y-%m-%d')"
|
||||
START="$(date '+%s')"
|
||||
ACTION="$1"
|
||||
is_on_break = False
|
||||
today_fields = []
|
||||
start_time = datetime.now()
|
||||
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
grep -qE "^$TODAY" "$TIMEFILE" && die "you already started your day"
|
||||
printf "%s,%s," "$TODAY" "$(date '+%s')" >> "$TIMEFILE"
|
||||
>&2 printf "Started work at %s\n" "$(date '+%H:%M')";;
|
||||
pause)
|
||||
grep -qE "^$TODAY" "$TIMEFILE" || die "You haven't even started your day!"
|
||||
awk -F, "/^$TODAY/"'{if (NF > 4) exit 1}' "$TIMEFILE" || die "You're already done for the day"
|
||||
awk -F, "/^$TODAY/"'{if (NF > 2) exit 1}' "$TIMEFILE" || die "You've already expanded your daily break allowance"
|
||||
>&2 echo "Taking a break..."
|
||||
sleep 9999999;;
|
||||
end)
|
||||
test -z "$2" && die "Tell me what you did today"
|
||||
grep -qE "^$TODAY" "$TIMEFILE" || die "You haven't even started your day!"
|
||||
start_hour="$(from_ts "$(tail -n1 "$TIMEFILE" | cut -f 2 -d ,)")"
|
||||
end_hour="$(date '+%H:%M')"
|
||||
shift 1
|
||||
msg="$(printf "%s" "$*" | sed 's/"/""/g')"
|
||||
line="$(awk -F ',' -v now="$START" -v start="$start_hour" -v end="$end_hour" -v msg="$msg" \
|
||||
"/^$TODAY/"'{
|
||||
total=now-$2-$3;
|
||||
h=int(total/3600);
|
||||
m=int(total/60%60);
|
||||
ph=int($3/3600);
|
||||
pm=int($3/60%60);
|
||||
printf "%s,%s,%s,%s:%s,%s:%s,\"%s\"",$1,start,end,ph,pm,h,m,msg;
|
||||
}' \
|
||||
"$TIMEFILE")"
|
||||
printf "\$d\nw\n\q" | ed "$TIMEFILE" > /dev/null 2>&1
|
||||
printf "%s\n" "$line" >> "$TIMEFILE";;
|
||||
esac
|
||||
def die(*args):
|
||||
print("Error: {}".format(*args), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def log(*args):
|
||||
print(*args, file=sys.stderr)
|
||||
|
||||
|
||||
def signal_handler(signal, frame):
|
||||
global today_fields
|
||||
now = datetime.now()
|
||||
hour = now.strftime("%H:%M")
|
||||
if not is_on_break:
|
||||
sys.exit(0)
|
||||
else:
|
||||
break_time = now - start_time
|
||||
log("\nBreak ended at {} and lasted {}".format(
|
||||
hour, td_format(break_time)))
|
||||
today_fields.append(start_time.strftime("%H:%M"))
|
||||
today_fields.append(hour)
|
||||
with open(TODAY_FILE, "w") as f:
|
||||
f.write(",".join(today_fields))
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def td_format(td):
|
||||
hours, remainder = divmod(td.total_seconds(), 3600)
|
||||
minutes, seconds = divmod(remainder, 60)
|
||||
|
||||
return '{:d}:{:02d}'.format(int(hours), int(minutes))
|
||||
|
||||
|
||||
def convert_manual_entry():
|
||||
for line in sys.stdin:
|
||||
fields = line.split(",")
|
||||
if len(fields) < 6:
|
||||
break
|
||||
morning = datetime.strptime(fields[1], "%H:%M")
|
||||
break_start = datetime.strptime(fields[2], "%H:%M")
|
||||
break_end = datetime.strptime(fields[3], "%H:%M")
|
||||
evening = datetime.strptime(fields[4], "%H:%M")
|
||||
break_time = break_end - break_start
|
||||
full_day = evening - morning
|
||||
work_day = full_day - break_time
|
||||
print('{},{},{},{},{},{}'.format(
|
||||
fields[0],
|
||||
morning.strftime("%H:%M"),
|
||||
evening.strftime("%H:%M"),
|
||||
td_format(break_time),
|
||||
td_format(work_day),
|
||||
"".join(fields[5:]).strip()))
|
||||
|
||||
|
||||
def get_today_fields():
|
||||
try:
|
||||
with open(TODAY_FILE, "r") as f:
|
||||
for line in f:
|
||||
if line.startswith(start_time.strftime("%Y-%m-%d")):
|
||||
return line.strip().split(",")
|
||||
except FileNotFoundError:
|
||||
return []
|
||||
return []
|
||||
|
||||
|
||||
def work(args):
|
||||
fields = get_today_fields()
|
||||
if len(fields) < 2:
|
||||
die("you haven't started working yet today")
|
||||
print("stats: {}".format(fields))
|
||||
|
||||
|
||||
def work_start(args):
|
||||
fields = get_today_fields()
|
||||
hour = start_time.strftime("%H:%M")
|
||||
if fields:
|
||||
die("You already started working")
|
||||
with open(TODAY_FILE, "w") as f:
|
||||
f.write("{},{}".format(
|
||||
start_time.strftime("%Y-%m-%d"), hour))
|
||||
log("Started working at {}".format(hour))
|
||||
|
||||
|
||||
def work_pause(args):
|
||||
global is_on_break
|
||||
global today_fields
|
||||
is_on_break = True
|
||||
today_fields = get_today_fields()
|
||||
hour = start_time.strftime("%H:%M")
|
||||
if not today_fields:
|
||||
die("no work to take a break from")
|
||||
log("Taking a break at {}".format(hour))
|
||||
# Wait to be stopped by a Ctrl-C
|
||||
sleep(999999)
|
||||
|
||||
|
||||
def work_end(args):
|
||||
fields = get_today_fields()
|
||||
hour = start_time.strftime("%H:%M")
|
||||
if not fields:
|
||||
die("Why try to leave when you haven't even started")
|
||||
fields.append(hour)
|
||||
# Test for even number of timestamp (but fields[0] is the date)
|
||||
if len(fields) < 3:
|
||||
die("not enough fields in {}".format(TODAY_FILE))
|
||||
elif len(fields) % 2 == 0:
|
||||
die("odd number of timestamps in {}".format(TODAY_FILE))
|
||||
begin_time = None
|
||||
worked_time = timedelta()
|
||||
for field in fields[1:]:
|
||||
try:
|
||||
if begin_time is None:
|
||||
begin_time = datetime.strptime(field, "%H:%M")
|
||||
else:
|
||||
end_time = datetime.strptime(field, "%H:%M")
|
||||
worked_time += end_time - begin_time
|
||||
begin_time = None
|
||||
except ValueError:
|
||||
die("couldn't parse field '{}' in {}".format(
|
||||
field, TODAY_FILE))
|
||||
day_start = datetime.strptime(fields[1], "%H:%M")
|
||||
day_end = datetime.strptime(fields[-1], "%H:%M")
|
||||
total_time = day_end - day_start
|
||||
break_time = total_time - worked_time
|
||||
with open(TIME_FILE, "a") as f:
|
||||
f.write("{},{},{},{},{},{}\n".format(
|
||||
fields[0],
|
||||
day_start.strftime("%H:%M"),
|
||||
start_time.strftime("%H:%M"),
|
||||
td_format(break_time),
|
||||
td_format(worked_time),
|
||||
args.description))
|
||||
# Erase TODAY_FILE
|
||||
with open(TODAY_FILE, "w") as f:
|
||||
f.write("")
|
||||
f.flush()
|
||||
log("Finished working at {} after working {}".format(
|
||||
hour, td_format(worked_time)))
|
||||
|
||||
|
||||
def work_export(args):
|
||||
print("export")
|
||||
|
||||
|
||||
def work_parse(args):
|
||||
print("parse")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Handle Ctrl-C
|
||||
signal(SIGINT, signal_handler)
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.set_defaults(func=work)
|
||||
commands = parser.add_subparsers(dest="command")
|
||||
|
||||
start_parser = commands.add_parser("start")
|
||||
pause_parser = commands.add_parser("pause")
|
||||
end_parser = commands.add_parser("end")
|
||||
export_parser = commands.add_parser("export")
|
||||
parse_parser = commands.add_parser("parse")
|
||||
|
||||
start_parser.set_defaults(func=work_start)
|
||||
pause_parser.set_defaults(func=work_pause)
|
||||
end_parser.add_argument("description")
|
||||
end_parser.set_defaults(func=work_end)
|
||||
export_parser.set_defaults(func=work_export)
|
||||
parse_parser.set_defaults(func=work_parse)
|
||||
|
||||
args = parser.parse_args()
|
||||
args.func(args)
|
||||
|
||||
#now = datetime.now()
|
||||
#with open(TIME_FILE, "r") as f:
|
||||
# for line in f:
|
||||
# print(line.strip())
|
||||
|
|
71
bin/work.old
Executable file
71
bin/work.old
Executable file
|
@ -0,0 +1,71 @@
|
|||
#!/bin/sh
|
||||
|
||||
TIMEFILE="$HOME/timekeeping.csv"
|
||||
|
||||
usage () {
|
||||
>&2 printf "usage:\n\t%s <start|pause|end>" "$(basename "$0")"
|
||||
exit 0
|
||||
}
|
||||
|
||||
die () {
|
||||
>&2 echo "Error: $*"
|
||||
exit 1
|
||||
}
|
||||
|
||||
from_ts () {
|
||||
if ! date --version >/dev/null 2>&1; then # BSD
|
||||
date -r "$1" '+%H:%M'
|
||||
else # GNU
|
||||
date -d "@$1" '+%H:%M'
|
||||
fi
|
||||
}
|
||||
|
||||
pause_end () {
|
||||
if [ "$ACTION" = "pause" ]; then
|
||||
now="$(date '+%s')"
|
||||
printf "%s," "$((now - START))" >> "$TIMEFILE"
|
||||
>&2 printf "\nYou took a %d minutes pause\n" "$(( (now - START) / 60))"
|
||||
fi
|
||||
}
|
||||
|
||||
trap pause_end INT
|
||||
|
||||
test -z "$1" && usage
|
||||
test -e "$TIMEFILE" || touch "$TIMEFILE"
|
||||
|
||||
TODAY="$(date '+%Y-%m-%d')"
|
||||
START="$(date '+%s')"
|
||||
ACTION="$1"
|
||||
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
grep -qE "^$TODAY" "$TIMEFILE" && die "you already started your day"
|
||||
printf "%s,%s," "$TODAY" "$(date '+%s')" >> "$TIMEFILE"
|
||||
>&2 printf "Started work at %s\n" "$(date '+%H:%M')";;
|
||||
pause)
|
||||
grep -qE "^$TODAY" "$TIMEFILE" || die "You haven't even started your day!"
|
||||
awk -F, "/^$TODAY/"'{if (NF > 4) exit 1}' "$TIMEFILE" || die "You're already done for the day"
|
||||
awk -F, "/^$TODAY/"'{if (NF > 2) exit 1}' "$TIMEFILE" || die "You've already expanded your daily break allowance"
|
||||
>&2 echo "Taking a break..."
|
||||
sleep 9999999;;
|
||||
end)
|
||||
test -z "$2" && die "Tell me what you did today"
|
||||
grep -qE "^$TODAY" "$TIMEFILE" || die "You haven't even started your day!"
|
||||
start_hour="$(from_ts "$(tail -n1 "$TIMEFILE" | cut -f 2 -d ,)")"
|
||||
end_hour="$(date '+%H:%M')"
|
||||
shift 1
|
||||
msg="$(printf "%s" "$*" | sed 's/"/""/g')"
|
||||
line="$(awk -F ',' -v now="$START" -v start="$start_hour" -v end="$end_hour" -v msg="$msg" \
|
||||
"/^$TODAY/"'{
|
||||
total=now-$2-$3;
|
||||
h=int(total/3600);
|
||||
m=int(total/60%60);
|
||||
ph=int($3/3600);
|
||||
pm=int($3/60%60);
|
||||
printf "%s,%s,%s,%s:%s,%s:%s,\"%s\"",$1,start,end,ph,pm,h,m,msg;
|
||||
}' \
|
||||
"$TIMEFILE")"
|
||||
printf "\$d\nw\n\q" | ed "$TIMEFILE" > /dev/null 2>&1
|
||||
printf "%s\n" "$line" >> "$TIMEFILE";;
|
||||
esac
|
Loading…
Reference in a new issue