commit bb7123241deace366b20730d9625216cacae04ff Author: chapeau Date: Fri Jan 10 18:10:05 2025 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bb71efc --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +ttracker.db diff --git a/README.md b/README.md new file mode 100644 index 0000000..7330566 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +`ttracker` is a simple time tracker in python. + +When launched, it checks if a time period is currently started. If not, it starts one and exits. If yes, it opens an editor, wait for a description of the past time period, stops it, and exits. + +When it opens the editor, `ttracker` show the 10 last entries. + +All time periods are stored in a sqlite database. + +### Usage + +`EDITOR=nano DATABASE=ttracker.db python3 ttracker.py` diff --git a/ttracker.py b/ttracker.py new file mode 100755 index 0000000..92e0058 --- /dev/null +++ b/ttracker.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 + +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 new_entry(cur, now): + cur.execute(f'INSERT INTO ttracker VALUES (?, null, null)', (int(now.timestamp()),)) + +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(): + now = datetime.datetime.now() + con = sqlite3.connect(DATABASE) + cur = con.cursor() + db_init(cur) + entry = db_get_current_entry(cur) + 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()