Initial WhatToDo app
Self-hosted personal task list: FastAPI + SQLite in Docker, mobile-friendly PWA frontend, deadlines, priorities, categories, tags, notes, checklists, recurring tasks, single-password login, and JSON export/import. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
import os
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
|
||||
DB_PATH = os.environ.get("DB_PATH", "/data/whattodo.db")
|
||||
SCHEMA_VERSION = 1
|
||||
|
||||
DEFAULT_CATEGORIES = [
|
||||
("Work", "#2563eb"),
|
||||
("Life", "#16a34a"),
|
||||
("Personal", "#9333ea"),
|
||||
("YouTube", "#dc2626"),
|
||||
]
|
||||
|
||||
SCHEMA = """
|
||||
CREATE TABLE IF NOT EXISTS meta (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS categories (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE COLLATE NOCASE,
|
||||
color TEXT NOT NULL DEFAULT '#6b7280',
|
||||
position INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS tasks (
|
||||
id INTEGER PRIMARY KEY,
|
||||
title TEXT NOT NULL,
|
||||
notes TEXT NOT NULL DEFAULT '',
|
||||
category_id INTEGER REFERENCES categories(id) ON DELETE SET NULL,
|
||||
priority INTEGER NOT NULL DEFAULT 0,
|
||||
due_date TEXT,
|
||||
tags TEXT NOT NULL DEFAULT '[]',
|
||||
recurrence TEXT NOT NULL DEFAULT 'none',
|
||||
recur_interval INTEGER NOT NULL DEFAULT 1,
|
||||
done INTEGER NOT NULL DEFAULT 0,
|
||||
done_at TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS subtasks (
|
||||
id INTEGER PRIMARY KEY,
|
||||
task_id INTEGER NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
||||
title TEXT NOT NULL,
|
||||
done INTEGER NOT NULL DEFAULT 0,
|
||||
position INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_subtasks_task ON subtasks(task_id);
|
||||
"""
|
||||
|
||||
|
||||
def connect() -> sqlite3.Connection:
|
||||
# FastAPI may open a request's connection and run its endpoint on different threads;
|
||||
# each connection is still used by only one request at a time.
|
||||
conn = sqlite3.connect(DB_PATH, check_same_thread=False)
|
||||
conn.row_factory = sqlite3.Row
|
||||
conn.execute("PRAGMA foreign_keys = ON")
|
||||
return conn
|
||||
|
||||
|
||||
def init() -> None:
|
||||
Path(DB_PATH).parent.mkdir(parents=True, exist_ok=True)
|
||||
conn = connect()
|
||||
try:
|
||||
conn.execute("PRAGMA journal_mode = WAL")
|
||||
conn.executescript(SCHEMA)
|
||||
row = conn.execute("SELECT value FROM meta WHERE key = 'schema_version'").fetchone()
|
||||
if row is None:
|
||||
# Fresh database: seed default categories.
|
||||
with conn:
|
||||
for i, (name, color) in enumerate(DEFAULT_CATEGORIES):
|
||||
conn.execute(
|
||||
"INSERT INTO categories (name, color, position) VALUES (?, ?, ?)",
|
||||
(name, color, i),
|
||||
)
|
||||
conn.execute(
|
||||
"INSERT INTO meta (key, value) VALUES ('schema_version', ?)",
|
||||
(str(SCHEMA_VERSION),),
|
||||
)
|
||||
finally:
|
||||
conn.close()
|
||||
Reference in New Issue
Block a user