Files
guides/backend/database.py
2026-05-27 01:00:33 +02:00

224 lines
6.5 KiB
Python

import aiosqlite
from config import DB_PATH
CREATE_GUIDES = """
CREATE TABLE IF NOT EXISTS guides (
id TEXT PRIMARY KEY,
topic TEXT NOT NULL,
format TEXT NOT NULL,
instructions TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL DEFAULT 'queued',
progress TEXT,
error_msg TEXT,
html_path TEXT,
pdf_path TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
)
"""
CREATE_BAUSTEINE = """
CREATE TABLE IF NOT EXISTS bausteine (
id TEXT PRIMARY KEY,
topic TEXT NOT NULL,
title TEXT NOT NULL,
description TEXT NOT NULL DEFAULT '',
purpose TEXT NOT NULL DEFAULT '',
example TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
)
"""
CREATE_SUGGESTIONS = """
CREATE TABLE IF NOT EXISTS baustein_suggestions (
id TEXT PRIMARY KEY,
topic TEXT NOT NULL,
title TEXT NOT NULL,
description TEXT NOT NULL DEFAULT '',
purpose TEXT NOT NULL DEFAULT '',
example TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL DEFAULT 'pending',
created_at TEXT NOT NULL
)
"""
_db: aiosqlite.Connection | None = None
async def get_db() -> aiosqlite.Connection:
global _db
if _db is None:
_db = await aiosqlite.connect(DB_PATH)
_db.row_factory = None
return _db
async def init_db():
db = await get_db()
await db.execute(CREATE_GUIDES)
await db.execute(CREATE_BAUSTEINE)
await db.execute(CREATE_SUGGESTIONS)
cursor = await db.execute("PRAGMA table_info(guides)")
columns = {row[1] for row in await cursor.fetchall()}
if "instructions" not in columns:
await db.execute("ALTER TABLE guides ADD COLUMN instructions TEXT NOT NULL DEFAULT ''")
await db.execute(
"UPDATE guides SET status = 'error', progress = NULL, error_msg = 'Server-Neustart' "
"WHERE status IN ('queued', 'generating')"
)
await db.commit()
async def close_db():
global _db
if _db is not None:
await _db.close()
_db = None
def _row_to_dict(row, cursor):
columns = [d[0] for d in cursor.description]
return dict(zip(columns, row))
async def create_guide(guide: dict) -> dict:
db = await get_db()
await db.execute(
"""INSERT INTO guides (id, topic, format, instructions, status, progress, html_path, pdf_path, created_at, updated_at)
VALUES (:id, :topic, :format, :instructions, :status, :progress, :html_path, :pdf_path, :created_at, :updated_at)""",
guide,
)
await db.commit()
return guide
async def get_guide(guide_id: str) -> dict | None:
db = await get_db()
cursor = await db.execute("SELECT * FROM guides WHERE id = ?", (guide_id,))
row = await cursor.fetchone()
if row is None:
return None
return _row_to_dict(row, cursor)
async def list_guides() -> list[dict]:
db = await get_db()
cursor = await db.execute("SELECT * FROM guides ORDER BY created_at DESC")
rows = await cursor.fetchall()
return [_row_to_dict(row, cursor) for row in rows]
async def update_guide(guide_id: str, **fields) -> None:
sets = ", ".join(f"{k} = :{k}" for k in fields)
fields["id"] = guide_id
db = await get_db()
await db.execute(f"UPDATE guides SET {sets} WHERE id = :id", fields)
await db.commit()
async def delete_guide(guide_id: str) -> bool:
db = await get_db()
cursor = await db.execute("DELETE FROM guides WHERE id = ?", (guide_id,))
await db.commit()
return cursor.rowcount > 0
# --- Bausteine ---
async def create_baustein(baustein: dict) -> dict:
db = await get_db()
await db.execute(
"""INSERT INTO bausteine (id, topic, title, description, purpose, example, created_at, updated_at)
VALUES (:id, :topic, :title, :description, :purpose, :example, :created_at, :updated_at)""",
baustein,
)
await db.commit()
return baustein
async def list_bausteine(topic: str) -> list[dict]:
db = await get_db()
cursor = await db.execute(
"SELECT * FROM bausteine WHERE topic = ? ORDER BY created_at ASC", (topic,)
)
rows = await cursor.fetchall()
return [_row_to_dict(row, cursor) for row in rows]
async def get_baustein(baustein_id: str) -> dict | None:
db = await get_db()
cursor = await db.execute("SELECT * FROM bausteine WHERE id = ?", (baustein_id,))
row = await cursor.fetchone()
if row is None:
return None
return _row_to_dict(row, cursor)
async def update_baustein(baustein_id: str, **fields) -> None:
sets = ", ".join(f"{k} = :{k}" for k in fields)
fields["id"] = baustein_id
db = await get_db()
await db.execute(f"UPDATE bausteine SET {sets} WHERE id = :id", fields)
await db.commit()
async def delete_baustein(baustein_id: str) -> bool:
db = await get_db()
cursor = await db.execute("DELETE FROM bausteine WHERE id = ?", (baustein_id,))
await db.commit()
return cursor.rowcount > 0
# --- Baustein Suggestions ---
async def create_suggestions(suggestions: list[dict]) -> None:
db = await get_db()
await db.executemany(
"""INSERT INTO baustein_suggestions (id, topic, title, description, purpose, example, status, created_at)
VALUES (:id, :topic, :title, :description, :purpose, :example, :status, :created_at)""",
suggestions,
)
await db.commit()
async def list_suggestions(topic: str) -> list[dict]:
db = await get_db()
cursor = await db.execute(
"SELECT * FROM baustein_suggestions WHERE topic = ? ORDER BY created_at ASC", (topic,)
)
rows = await cursor.fetchall()
return [_row_to_dict(row, cursor) for row in rows]
async def get_suggestion(suggestion_id: str) -> dict | None:
db = await get_db()
cursor = await db.execute("SELECT * FROM baustein_suggestions WHERE id = ?", (suggestion_id,))
row = await cursor.fetchone()
if row is None:
return None
return _row_to_dict(row, cursor)
async def update_suggestion(suggestion_id: str, **fields) -> None:
sets = ", ".join(f"{k} = :{k}" for k in fields)
fields["id"] = suggestion_id
db = await get_db()
await db.execute(f"UPDATE baustein_suggestions SET {sets} WHERE id = :id", fields)
await db.commit()
async def delete_suggestion(suggestion_id: str) -> bool:
db = await get_db()
cursor = await db.execute("DELETE FROM baustein_suggestions WHERE id = ?", (suggestion_id,))
await db.commit()
return cursor.rowcount > 0
async def delete_pending_suggestions(topic: str) -> None:
db = await get_db()
await db.execute(
"DELETE FROM baustein_suggestions WHERE topic = ? AND status = 'pending'", (topic,)
)
await db.commit()