from __future__ import annotations import sqlite3 from dataclasses import dataclass from pathlib import Path from typing import Iterable @dataclass(frozen=True) class DatabaseConfig: db_path: Path class DatabaseBootstrapper: """Creates the SQLite schema and demo data on demand.""" def __init__(self, config: DatabaseConfig) -> None: self._config = config @property def path(self) -> Path: return self._config.db_path def initialize(self) -> None: self._ensure_parent_dir() with sqlite3.connect(self.path) as conn: conn.execute("PRAGMA foreign_keys = ON") self._create_tables(conn) self._seed_demo_data(conn) conn.commit() def _ensure_parent_dir(self) -> None: self.path.parent.mkdir(parents=True, exist_ok=True) def _create_tables(self, conn: sqlite3.Connection) -> None: conn.executescript( """ CREATE TABLE IF NOT EXISTS modules ( id TEXT PRIMARY KEY, title TEXT NOT NULL, credit_points INTEGER NOT NULL, status TEXT NOT NULL, progress_percent INTEGER NOT NULL ); CREATE TABLE IF NOT EXISTS exams ( id TEXT PRIMARY KEY, module_id TEXT NOT NULL REFERENCES modules(id) ON DELETE CASCADE, title TEXT NOT NULL, exam_date TEXT NOT NULL, status TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS appointments ( id TEXT PRIMARY KEY, title TEXT NOT NULL, start_date TEXT NOT NULL, description TEXT NOT NULL ); """ ) def _seed_demo_data(self, conn: sqlite3.Connection) -> None: modules = [ ( "MAT101", "Analysis I", 5, "In Progress", 40, ), ( "CS201", "Algorithms", 6, "Planned", 0, ), ( "ENG150", "Academic Writing", 3, "Completed", 100, ), ] exams = [ ( "EXAM-MAT101", "MAT101", "Klausur Analysis I", "2025-12-15", "Scheduled", ), ( "EXAM-CS201", "CS201", "Algorithmik Projekt", "2026-01-20", "Planned", ), ] appointments = [ ( "APPT-MENTOR", "Mentoring", "2025-12-08", "Mentoring call via Teams", ), ( "APPT-STUDY", "Lerngruppe", "2025-12-10", "Gruppenlernen Bibliothek", ), ] module_sql = "INSERT OR IGNORE INTO modules " "VALUES (?, ?, ?, ?, ?)" exam_sql = "INSERT OR IGNORE INTO exams " "VALUES (?, ?, ?, ?, ?)" appointment_sql = "INSERT OR IGNORE INTO appointments " "VALUES (?, ?, ?, ?)" self._bulk_insert(conn, module_sql, modules) self._bulk_insert(conn, exam_sql, exams) self._bulk_insert(conn, appointment_sql, appointments) def _bulk_insert( self, conn: sqlite3.Connection, sql: str, rows: Iterable[tuple], ) -> None: conn.executemany(sql, rows)