Download db.py from mnoorchenar/focus-tracker: direct link, hf CLI and curl.
- Browser
- Download file 5.58 kB
-
https://huggingface.co/mnoorchenar/focus-tracker/resolve/main/db.py
- Command line
-
hf download hf://mnoorchenar/focus-tracker/db.py
-
curl -L -o db.py https://huggingface.co/mnoorchenar/focus-tracker/resolve/main/db.py
5.58 kB
| """SQLite storage: one row per focus session (epoch seconds, local time is derived on display).""" | |
| import os | |
| import sqlite3 | |
| from pathlib import Path | |
| DATA_DIR = Path(os.environ.get("APPDATA", Path.home())) / "FocusTracker" | |
| DB_PATH = DATA_DIR / "focus.db" | |
| MIN_SESSION_S = 10 | |
| SCHEMA = """ | |
| CREATE TABLE IF NOT EXISTS sessions ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| project TEXT NOT NULL, | |
| start_ts INTEGER NOT NULL, | |
| end_ts INTEGER, | |
| last_ts INTEGER, | |
| plan_min INTEGER | |
| ); | |
| CREATE INDEX IF NOT EXISTS idx_sessions_start ON sessions(start_ts); | |
| CREATE TABLE IF NOT EXISTS projects (name TEXT PRIMARY KEY); | |
| CREATE TABLE IF NOT EXISTS settings (key TEXT PRIMARY KEY, value TEXT); | |
| """ | |
| class DB: | |
| def __init__(self, path=DB_PATH): | |
| path = Path(path) | |
| path.parent.mkdir(parents=True, exist_ok=True) | |
| self.con = sqlite3.connect(path) | |
| self.con.executescript(SCHEMA) | |
| cols = [r[1] for r in self.con.execute("PRAGMA table_info(sessions)")] | |
| if "plan_min" not in cols: # databases created before plans existed | |
| self.con.execute("ALTER TABLE sessions ADD COLUMN plan_min INTEGER") | |
| self._recover() | |
| def _recover(self): | |
| # A crash or power loss leaves an open row; close it at the last heartbeat. | |
| self.con.execute( | |
| "UPDATE sessions SET end_ts = COALESCE(last_ts, start_ts) WHERE end_ts IS NULL") | |
| self.con.execute("DELETE FROM sessions WHERE end_ts - start_ts < ?", (MIN_SESSION_S,)) | |
| self.con.commit() | |
| def get(self, key, default=None): | |
| row = self.con.execute("SELECT value FROM settings WHERE key=?", (key,)).fetchone() | |
| return row[0] if row else default | |
| def set(self, key, value): | |
| self.con.execute("INSERT OR REPLACE INTO settings VALUES (?, ?)", (key, str(value))) | |
| self.con.commit() | |
| # ---- sessions ---- | |
| def start(self, project, ts, plan_min): | |
| self.add_project(project) | |
| cur = self.con.execute( | |
| "INSERT INTO sessions (project, start_ts, last_ts, plan_min) VALUES (?, ?, ?, ?)", | |
| (project, ts, ts, plan_min)) | |
| self.con.commit() | |
| return cur.lastrowid | |
| def heartbeat(self, sid, ts): | |
| self.con.execute("UPDATE sessions SET last_ts=? WHERE id=?", (ts, sid)) | |
| self.con.commit() | |
| def set_plan(self, sid, plan_min): | |
| self.con.execute("UPDATE sessions SET plan_min=? WHERE id=?", (plan_min, sid)) | |
| self.con.commit() | |
| def stop(self, sid, ts): | |
| start = self.con.execute("SELECT start_ts FROM sessions WHERE id=?", (sid,)).fetchone()[0] | |
| if ts - start < MIN_SESSION_S: | |
| self.con.execute("DELETE FROM sessions WHERE id=?", (sid,)) | |
| else: | |
| self.con.execute("UPDATE sessions SET end_ts=?, last_ts=? WHERE id=?", (ts, ts, sid)) | |
| self.con.commit() | |
| def add_session(self, project, start, end, plan_min): | |
| self.add_project(project) | |
| cur = self.con.execute( | |
| "INSERT INTO sessions (project, start_ts, end_ts, last_ts, plan_min) VALUES (?, ?, ?, ?, ?)", | |
| (project, start, end, end, plan_min)) | |
| self.con.commit() | |
| return cur.lastrowid | |
| def update_session(self, sid, project, start, end, plan_min): | |
| self.add_project(project) | |
| self.con.execute( | |
| "UPDATE sessions SET project=?, start_ts=?, end_ts=?, last_ts=?, plan_min=? WHERE id=?", | |
| (project, start, end, end, plan_min, sid)) | |
| self.con.commit() | |
| def has_session(self, project, start, end): | |
| return self.con.execute( | |
| "SELECT 1 FROM sessions WHERE project=? AND start_ts=? AND end_ts=?", | |
| (project, start, end)).fetchone() is not None | |
| def delete_session(self, sid): | |
| self.con.execute("DELETE FROM sessions WHERE id=?", (sid,)) | |
| self.con.commit() | |
| def sessions(self, now): | |
| """All sessions as (id, project, start, end, plan_min); a running session ends at now.""" | |
| return self.con.execute( | |
| "SELECT id, project, start_ts, COALESCE(end_ts, ?), plan_min FROM sessions " | |
| "ORDER BY start_ts", (now,)).fetchall() | |
| def seconds_since(self, since, now): | |
| row = self.con.execute( | |
| "SELECT SUM(MIN(COALESCE(end_ts, ?), ?) - MAX(start_ts, ?)) FROM sessions " | |
| "WHERE COALESCE(end_ts, ?) > ?", (now, now, since, now, since)).fetchone() | |
| return row[0] or 0 | |
| # ---- projects ---- | |
| def add_project(self, name): | |
| self.con.execute("INSERT OR IGNORE INTO projects VALUES (?)", (name,)) | |
| self.con.commit() | |
| def projects(self): | |
| """Project names, most recently used first, then projects never used.""" | |
| used = [r[0] for r in self.con.execute( | |
| "SELECT project FROM sessions GROUP BY project ORDER BY MAX(start_ts) DESC")] | |
| extra = [r[0] for r in self.con.execute("SELECT name FROM projects ORDER BY name") | |
| if r[0] not in used] | |
| return used + extra | |
| def rename_project(self, old, new): | |
| self.add_project(new) | |
| self.con.execute("UPDATE sessions SET project=? WHERE project=?", (new, old)) | |
| self.con.execute("DELETE FROM projects WHERE name=?", (old,)) | |
| self.con.commit() | |
| def delete_project(self, name): | |
| self.con.execute("DELETE FROM sessions WHERE project=?", (name,)) | |
| self.con.execute("DELETE FROM projects WHERE name=?", (name,)) | |
| self.con.commit() | |