SignalWatch / backend /database.py
nehal's picture
Deploy SignalWatch: FastAPI + React dashboard
9704d4f
Raw History Blame Contribute Delete
2.78 kB
"""Database layer: SQLite schema, connection, and CRUD helpers."""
import aiosqlite
import datetime as dt
from pathlib import Path
DB_PATH = Path(__file__).parent / "signalwatch.db"
SCHEMA = """
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
created_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS stocks (
symbol TEXT PRIMARY KEY,
name TEXT NOT NULL,
sector TEXT NOT NULL,
market_cap FLOAT NOT NULL DEFAULT 0
);
-- Price/quote snapshots (time series)
CREATE TABLE IF NOT EXISTS quotes (
symbol TEXT NOT NULL,
ts TEXT NOT NULL, -- ISO UTC
price REAL NOT NULL,
volume INTEGER NOT NULL,
open REAL,
high REAL,
low REAL,
bid REAL,
ask REAL,
PRIMARY KEY (symbol, ts)
);
CREATE TABLE IF NOT EXISTS watchlists (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
name TEXT NOT NULL DEFAULT 'Default',
created_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS watchlist_items (
watchlist_id INTEGER NOT NULL,
symbol TEXT NOT NULL,
added_at TEXT NOT NULL,
PRIMARY KEY (watchlist_id, symbol)
);
-- Alert lifecycle: fired -> seen -> acknowledged -> dismissed
CREATE TABLE IF NOT EXISTS alerts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
symbol TEXT NOT NULL,
alert_type TEXT NOT NULL,
composite_score REAL NOT NULL DEFAULT 0,
headline TEXT NOT NULL,
signals_json TEXT NOT NULL DEFAULT '{}',
status TEXT NOT NULL DEFAULT 'fired',
created_at TEXT NOT NULL,
seen_at TEXT,
acknowledged_at TEXT,
dismissed_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_alerts_user_status ON alerts(user_id, status);
-- Per-user per-stock preferences
CREATE TABLE IF NOT EXISTS stock_prefs (
user_id INTEGER NOT NULL,
symbol TEXT NOT NULL,
interest_level TEXT NOT NULL DEFAULT 'normal', -- low / normal / high
threshold_override REAL,
muted INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY (user_id, symbol)
);
"""
async def init_db():
conn = await aiosqlite.connect(DB_PATH)
await conn.executescript(SCHEMA)
await conn.commit()
await conn.close()
async def get_conn():
conn = await aiosqlite.connect(DB_PATH)
conn.row_factory = aiosqlite.Row
return conn
async def ensure_user() -> int:
conn = await get_conn()
cur = await conn.execute(
"SELECT id FROM users ORDER BY id LIMIT 1"
)
row = await cur.fetchone()
if row is None:
cur = await conn.execute(
"INSERT INTO users (created_at) VALUES (?)",
(dt.datetime.utcnow().isoformat(),),
)
await conn.commit()
user_id = cur.lastrowid
else:
user_id = row["id"]
await conn.close()
return user_id