from __future__ import annotations import sqlite3 import threading from whatchanged.models import Entry, DOMAINS, EVENTS _COLS = ["date"] + DOMAINS + EVENTS + ["note"] _WRITE_LOCK = threading.Lock() def init_db(path: str = "data/whatchanged.db") -> sqlite3.Connection: import os parent = os.path.dirname(path) if parent: os.makedirs(parent, exist_ok=True) conn = sqlite3.connect(path, check_same_thread=False) conn.row_factory = sqlite3.Row cols_sql = ",\n".join( ["date TEXT PRIMARY KEY"] + [f"{d} INTEGER" for d in DOMAINS] + [f"{e} INTEGER NOT NULL DEFAULT 0" for e in EVENTS] + ["note TEXT NOT NULL DEFAULT ''"] ) conn.execute(f"CREATE TABLE IF NOT EXISTS entries (\n{cols_sql}\n)") conn.commit() return conn def upsert_entry(conn: sqlite3.Connection, entry: Entry) -> None: d = entry.to_dict() placeholders = ", ".join("?" for _ in _COLS) updates = ", ".join(f"{c}=excluded.{c}" for c in _COLS if c != "date") with _WRITE_LOCK: conn.execute( f"INSERT INTO entries ({', '.join(_COLS)}) VALUES ({placeholders}) " f"ON CONFLICT(date) DO UPDATE SET {updates}", [d[c] for c in _COLS], ) conn.commit() def get_entry(conn: sqlite3.Connection, date: str) -> Entry | None: row = conn.execute("SELECT * FROM entries WHERE date=?", (date,)).fetchone() return Entry.from_dict(dict(row)) if row else None def get_range(conn: sqlite3.Connection, start: str, end: str) -> list[Entry]: rows = conn.execute( "SELECT * FROM entries WHERE date>=? AND date<=? ORDER BY date ASC", (start, end), ).fetchall() return [Entry.from_dict(dict(r)) for r in rows]