68 lines
2.6 KiB
Python
68 lines
2.6 KiB
Python
"""Database connection management and schema initialisation."""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sqlite3
|
|
from contextlib import asynccontextmanager
|
|
from pathlib import Path
|
|
from typing import AsyncGenerator
|
|
|
|
import aiosqlite
|
|
|
|
DB_PATH: str = os.getenv("DB_PATH", "data/tracker.db")
|
|
|
|
_DDL = """
|
|
CREATE TABLE IF NOT EXISTS runs (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
|
|
scenario TEXT NOT NULL,
|
|
identifier TEXT NOT NULL,
|
|
success INTEGER NOT NULL,
|
|
failure_reason TEXT,
|
|
steps_json TEXT NOT NULL DEFAULT '[]',
|
|
total_ms INTEGER NOT NULL DEFAULT 0
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_runs_scenario ON runs(scenario);
|
|
CREATE INDEX IF NOT EXISTS idx_runs_created_at ON runs(created_at);
|
|
CREATE INDEX IF NOT EXISTS idx_runs_identifier ON runs(identifier);
|
|
|
|
CREATE TABLE IF NOT EXISTS flow_configs (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
|
|
name TEXT NOT NULL,
|
|
is_active INTEGER NOT NULL DEFAULT 0,
|
|
search_texts_json TEXT NOT NULL DEFAULT '[]',
|
|
search_box_selector TEXT NOT NULL DEFAULT '',
|
|
search_box_selector_type TEXT NOT NULL DEFAULT 'css',
|
|
search_submit_selector TEXT NOT NULL DEFAULT '',
|
|
search_submit_selector_type TEXT NOT NULL DEFAULT 'css',
|
|
search_results_selector TEXT NOT NULL DEFAULT '',
|
|
scroll_container_selector TEXT NOT NULL DEFAULT '',
|
|
max_scrolls INTEGER NOT NULL DEFAULT 30,
|
|
scroll_pause_ms INTEGER NOT NULL DEFAULT 1200,
|
|
no_new_content_timeout_ms INTEGER NOT NULL DEFAULT 3000,
|
|
item_selector_template TEXT NOT NULL DEFAULT '',
|
|
item_selector_type TEXT NOT NULL DEFAULT 'css',
|
|
target_item_ids_json TEXT NOT NULL DEFAULT '[]'
|
|
);
|
|
"""
|
|
|
|
|
|
def init_db() -> None:
|
|
"""Create the DB file and apply the schema (sync, called once at startup)."""
|
|
path = Path(DB_PATH)
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
conn = sqlite3.connect(str(path))
|
|
conn.executescript(_DDL)
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
|
|
@asynccontextmanager
|
|
async def get_db() -> AsyncGenerator[aiosqlite.Connection, None]:
|
|
"""Async context manager — yields an open, Row-factory-enabled connection."""
|
|
async with aiosqlite.connect(DB_PATH) as db:
|
|
db.row_factory = aiosqlite.Row
|
|
yield db
|