#!/usr/bin/env python3 import argparse import datetime import sys, tempfile, os import sqlite3 from subprocess import call EDITOR = os.environ.get('EDITOR', 'nano') DATABASE = os.environ.get('TTRACKER_BD', 'ttracker.db') def db_init(cur): cur.execute("CREATE TABLE IF NOT EXISTS ttracker (start INTEGER, end INTEGER, text TEXT)") def db_get_current_entry(cur): res = cur.execute("SELECT * FROM ttracker ORDER BY start DESC").fetchone() if res is not None and res[1] is None: return res return (None, None, None) def show_last_entries(cur): res = cur.execute("SELECT * FROM ttracker ORDER BY start DESC LIMIT 10").fetchall() for e in res: start = datetime.datetime.fromtimestamp(e[0]) if e[1] is not None: end = datetime.datetime.fromtimestamp(e[1]) print(f'{when_are_we(start.date())}, {start.strftime("%H:%M")}-{end.strftime("%H:%M")}: {e[2]}') else: print(f'{when_are_we(start.date())}, {start.strftime("%H:%M")}-') def new_entry(cur, now): cur.execute(f'INSERT INTO ttracker VALUES (?, null, null)', (int(now.timestamp()),)) def cancel_last_entry(cur, entry): if entry[0] != None: cur.execute("DELETE FROM ttracker WHERE start = ?", (entry[0],)) else: cur.execute("UPDATE ttracker SET end=null, text=null WHERE start = (SELECT start FROM ttracker ORDER BY start DESC LIMIT 1)") def end_entry(cur, entry, now): entry_start = datetime.datetime.fromtimestamp(entry[0]) res = cur.execute("SELECT * FROM ttracker ORDER BY start DESC LIMIT 11").fetchall() initial_message = '\n\n' + \ f'# What did you do since {when_are_we(entry_start.date())} ({entry_start.strftime("%H:%M")})?\n' + \ "#\n" for e in res: if e[1] is not None: start = datetime.datetime.fromtimestamp(e[0]) end = datetime.datetime.fromtimestamp(e[1]) initial_message += f'# {when_are_we(start.date())}, {start.strftime("%H:%M")}-{end.strftime("%H:%M")}: {e[2]}\n' with tempfile.NamedTemporaryFile(suffix=".tmp") as tf: tf.write(initial_message.encode('utf_8')) tf.flush() call([EDITOR, tf.name]) tf.seek(0) edited_message = [l.decode('utf_8') for l in tf.readlines()] stripped_message = [l.strip() for l in edited_message if l.strip() and l != "" and l[0] != "#"] if len(stripped_message) == 0: return cur.execute('UPDATE ttracker SET end = ?, text = ? WHERE start = ?', (int(now.timestamp()), stripped_message[0], entry[0])) print(f'Close entry ending at {now.strftime("%a %d %b")} - {now.strftime("%H:%M")}: {stripped_message[0]}') def when_are_we(date): delta = (datetime.date.today() - date).days if delta == 0: return "Today" elif delta == 1: return "Yesterday" else: return f'{delta} days ago' def main(): parser = argparse.ArgumentParser( prog='ttracker', description='Simple time tracker') subparsers = parser.add_subparsers() parser_show = subparsers.add_parser('show') parser_show.set_defaults(show=True) parser_show = subparsers.add_parser('cancel') parser_show.set_defaults(cancel=True) args = parser.parse_args() now = datetime.datetime.now() con = sqlite3.connect(DATABASE) cur = con.cursor() db_init(cur) if 'show' in args: show_last_entries(cur) return entry = db_get_current_entry(cur) if 'cancel' in args: cancel_last_entry(cur, entry) con.commit() show_last_entries(cur) return if entry[0] is None: new_entry(cur, now) print(f'Add new entry starting at {now.strftime("%a %d %b")} ({when_are_we(now.date())}) - {now.strftime("%H:%M")}') else: end_entry(cur, entry, now) con.commit() if __name__ == "__main__": main()