#!/usr/bin/env python3 # -*- coding: utf-8 -*- import argparse from signal import signal, SIGINT import sys from time import sleep from os.path import expanduser, join from datetime import datetime, timedelta TIME_FILE = join(expanduser("~"), "timekeeping.csv") TODAY_FILE = join(expanduser("~"), ".timekeeping") is_on_break = False today_fields = [] start_time = datetime.now() 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 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): new_lines = [] try: with open(args.file, "r") as f: for line in f: 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 new_lines.append('{},{},{},{},{},{}'.format( fields[0], morning.strftime("%H:%M"), evening.strftime("%H:%M"), td_format(break_time), td_format(work_day), "".join(fields[5:]).strip())) except FileNotFoundError: die("file not found: {}".format(args.file)) # TODO do sanity checking, like if a day already exists with open(TIME_FILE, "a") as f: for line in new_lines: f.write("{}\n".format(line)) log("Written {} new entries to {}".format(len(new_lines), TIME_FILE)) 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) parse_parser.add_argument("file") args = parser.parse_args() args.func(args) #now = datetime.now() #with open(TIME_FILE, "r") as f: # for line in f: # print(line.strip())