"""SQLite-Zugriff: Schema, Verbindung, Hilfsfunktionen. Die Datenbank wird vom Poller-Dienst geschrieben und vom Web-Dienst gelesen. WAL-Modus erlaubt gleichzeitiges Lesen/Schreiben ohne Sperrkonflikte. """ import sqlite3 from contextlib import contextmanager from . import config SCHEMA = """ CREATE TABLE IF NOT EXISTS tickets ( id INTEGER PRIMARY KEY, number TEXT, title TEXT, state TEXT, state_id INTEGER, priority TEXT, priority_id INTEGER, owner TEXT, owner_id INTEGER, "group" TEXT, customer TEXT, article_count INTEGER, zammad_created_at TEXT, zammad_updated_at TEXT, last_contact_at TEXT, last_contact_agent_at TEXT, last_contact_customer_at TEXT, close_at TEXT, last_close_at TEXT, raw_json TEXT, first_seen_at TEXT, last_synced_at TEXT ); CREATE TABLE IF NOT EXISTS changes ( id INTEGER PRIMARY KEY AUTOINCREMENT, ticket_id INTEGER NOT NULL, ticket_number TEXT, ticket_title TEXT, field TEXT NOT NULL, label TEXT, old_value TEXT, new_value TEXT, detail TEXT, detected_at TEXT NOT NULL, seen INTEGER NOT NULL DEFAULT 0, notified INTEGER NOT NULL DEFAULT 0 ); CREATE INDEX IF NOT EXISTS idx_changes_detected ON changes(detected_at DESC); CREATE INDEX IF NOT EXISTS idx_changes_ticket ON changes(ticket_id); CREATE INDEX IF NOT EXISTS idx_changes_seen ON changes(seen); CREATE TABLE IF NOT EXISTS sync_runs ( id INTEGER PRIMARY KEY AUTOINCREMENT, started_at TEXT, finished_at TEXT, ticket_count INTEGER, change_count INTEGER, status TEXT, error TEXT ); """ def connect() -> sqlite3.Connection: conn = sqlite3.connect(config.DB_PATH, timeout=30) conn.row_factory = sqlite3.Row conn.execute("PRAGMA journal_mode=WAL;") conn.execute("PRAGMA busy_timeout=10000;") conn.execute("PRAGMA foreign_keys=ON;") return conn @contextmanager def get_conn(): conn = connect() try: yield conn conn.commit() finally: conn.close() def init_db() -> None: with get_conn() as conn: conn.executescript(SCHEMA)