This commit is contained in:
2026-06-15 19:56:18 +03:30
parent 7d61fd8322
commit a5d09be46e
7 changed files with 283 additions and 210 deletions
+67
View File
@@ -0,0 +1,67 @@
"""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