Goals table has selected_task_id referencing tasks(id) without ON DELETE SET NULL. When deleting a task, first clear any selected_task_id references in goals to prevent FK violation. Also update schema for future databases.
100 lines
2.6 KiB
Python
100 lines
2.6 KiB
Python
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()
|