import sqlite3 import os import config CREATE_USERS = """ CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL UNIQUE, password_hash TEXT NOT NULL, role TEXT NOT NULL DEFAULT 'user', max_goals INTEGER NOT NULL DEFAULT 5, selected_goal_id INTEGER ) """ CREATE_GOALS = """ CREATE TABLE IF NOT EXISTS goals ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, title TEXT NOT NULL, activated INTEGER NOT NULL DEFAULT 1, selected_task_id INTEGER, FOREIGN KEY (user_id) REFERENCES users(id), FOREIGN KEY (selected_task_id) REFERENCES tasks(id) ON DELETE SET NULL ) """ CREATE_TASKS = """ CREATE TABLE IF NOT EXISTS tasks ( id INTEGER PRIMARY KEY AUTOINCREMENT, goal_id INTEGER NOT NULL, title TEXT NOT NULL, desc TEXT NOT NULL DEFAULT '', status TEXT NOT NULL DEFAULT 'todo', start_time TEXT, finished_time TEXT, "order" REAL NOT NULL DEFAULT 0.0, FOREIGN KEY (goal_id) REFERENCES goals(id) ) """ CREATE_NOTES = """ CREATE TABLE IF NOT EXISTS notes ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, goal_id INTEGER, task_id INTEGER, title TEXT NOT NULL, content TEXT NOT NULL DEFAULT '', created_at TEXT NOT NULL, updated_at TEXT NOT NULL, FOREIGN KEY (user_id) REFERENCES users(id), FOREIGN KEY (goal_id) REFERENCES goals(id) ON DELETE CASCADE, FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE ) """ def get_connection(): os.makedirs(os.path.dirname(config.DB_PATH), exist_ok=True) conn = sqlite3.connect(config.DB_PATH) conn.row_factory = sqlite3.Row conn.execute("PRAGMA foreign_keys = ON") return conn def init_db(): conn = get_connection() try: conn.execute(CREATE_USERS) conn.execute(CREATE_GOALS) conn.execute(CREATE_TASKS) conn.execute(CREATE_NOTES) try: conn.execute("ALTER TABLE goals ADD COLUMN selected_task_id INTEGER") except sqlite3.OperationalError: pass try: conn.execute("ALTER TABLE users ADD COLUMN selected_goal_id INTEGER") except sqlite3.OperationalError: pass conn.commit() import bcrypt password_hash = bcrypt.hashpw( config.DEFAULT_ADMIN_PASSWORD.encode("utf-8"), bcrypt.gensalt() ).decode("utf-8") conn.execute( "INSERT OR IGNORE INTO users (username, password_hash, role, max_goals) VALUES (?, ?, ?, ?)", (config.DEFAULT_ADMIN_USERNAME, password_hash, "admin", 100) ) conn.commit() finally: conn.close()