yatri-ai / api /models /database.py
siddharthnavnath7's picture
Add: password toggle, welcome email, feedback form, toast notifications, mobile fixes
92dabc7
Raw
History Blame Contribute Delete
4.65 kB
"""
Async SQLite database layer using aiosqlite.
Tables: users, sessions, favorites, emergency_logs.
"""
import aiosqlite
import logging
from pathlib import Path
from typing import Optional, List, Dict, Any
log = logging.getLogger(__name__)
DB_PATH = Path(__file__).parent.parent.parent / "data" / "yatri.db"
class Database:
def __init__(self, db_path: str = str(DB_PATH)):
self.db_path = db_path
async def init_db(self):
"""Create tables if they don't exist."""
Path(self.db_path).parent.mkdir(parents=True, exist_ok=True)
async with aiosqlite.connect(self.db_path) as conn:
await conn.executescript("""
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
phone TEXT,
password_hash TEXT NOT NULL,
preferred_language TEXT DEFAULT 'hi',
avatar_url TEXT,
created_at TEXT NOT NULL,
last_login TEXT
);
CREATE TABLE IF NOT EXISTS sessions (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
query_text TEXT,
response_text TEXT,
language TEXT,
query_type TEXT CHECK(query_type IN ('voice', 'text', 'emergency')),
conversation_id TEXT,
created_at TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
);
CREATE TABLE IF NOT EXISTS favorites (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
place_id TEXT NOT NULL,
created_at TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
);
CREATE TABLE IF NOT EXISTS emergency_logs (
id TEXT PRIMARY KEY,
user_id TEXT,
scenario TEXT,
language TEXT,
location_lat REAL,
location_lon REAL,
created_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS conversations (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
title TEXT DEFAULT 'New Chat',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
);
CREATE TABLE IF NOT EXISTS feedback (
id TEXT PRIMARY KEY,
name TEXT,
email TEXT,
type TEXT,
message TEXT,
rating INTEGER DEFAULT 0,
created_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_sessions_user ON sessions(user_id);
CREATE INDEX IF NOT EXISTS idx_sessions_conv ON sessions(conversation_id);
CREATE INDEX IF NOT EXISTS idx_conversations_user ON conversations(user_id);
CREATE INDEX IF NOT EXISTS idx_favorites_user ON favorites(user_id);
CREATE INDEX IF NOT EXISTS idx_emergency_user ON emergency_logs(user_id);
""")
await conn.commit()
log.info(f"Database initialized at {self.db_path}")
async def execute(self, sql: str, params: tuple = ()) -> None:
"""Run a write query (INSERT, UPDATE, DELETE)."""
async with aiosqlite.connect(self.db_path) as conn:
await conn.execute(sql, params)
await conn.commit()
async def fetch_one(self, sql: str, params: tuple = ()) -> Optional[Dict[str, Any]]:
"""Fetch a single row as a dict."""
async with aiosqlite.connect(self.db_path) as conn:
conn.row_factory = aiosqlite.Row
cursor = await conn.execute(sql, params)
row = await cursor.fetchone()
if row is None:
return None
return dict(row)
async def fetch_all(self, sql: str, params: tuple = ()) -> List[Dict[str, Any]]:
"""Fetch all rows as a list of dicts."""
async with aiosqlite.connect(self.db_path) as conn:
conn.row_factory = aiosqlite.Row
cursor = await conn.execute(sql, params)
rows = await cursor.fetchall()
return [dict(r) for r in rows]
# Module-level singleton
db = Database()