goalsbreakdown/database.py
Yuyao Huang f3bffa40cd Initial commit: GoalsBreakDown web app
- Flask backend with TinyDB database
- Multi-user auth with bcrypt password hashing
- Goal CRUD with activation/deactivation and per-user limits
- Task CRUD with status tracking (todo/doing/pending/done)
- Focus rule: one doing task per goal
- Time picker-style scroll view with drag-and-drop reordering
- Admin panel for user management
- uv environment management
2026-05-08 12:41:19 +08:00

138 lines
3.4 KiB
Python

import os
from tinydb import TinyDB, Query
from tinydb.operations import increment
import config
os.makedirs(os.path.dirname(config.DB_PATH), exist_ok=True)
db = TinyDB(config.DB_PATH)
users_table = db.table("users")
goals_table = db.table("goals")
tasks_table = db.table("tasks")
User = Query()
Goal = Query()
Task = Query()
def init_db():
if users_table.count(User.role == "admin") == 0:
import bcrypt
password_hash = bcrypt.hashpw(
config.DEFAULT_ADMIN_PASSWORD.encode("utf-8"),
bcrypt.gensalt()
).decode("utf-8")
users_table.insert({
"username": config.DEFAULT_ADMIN_USERNAME,
"password_hash": password_hash,
"role": "admin",
"max_goals": 100
})
def get_user_by_username(username):
result = users_table.search(User.username == username)
return result[0] if result else None
def get_user_by_id(user_id):
return users_table.get(doc_id=user_id)
def create_user(username, password_hash, role="user", max_goals=None):
if max_goals is None:
max_goals = config.DEFAULT_MAX_GOALS
doc_id = users_table.insert({
"username": username,
"password_hash": password_hash,
"role": role,
"max_goals": max_goals
})
return users_table.get(doc_id=doc_id)
def update_user(user_id, **kwargs):
users_table.update(kwargs, doc_ids=[user_id])
def get_all_users():
return users_table.all()
def get_goals_by_user(user_id):
return goals_table.search(Goal.user_id == user_id)
def get_goal_by_id(goal_id):
return goals_table.get(doc_id=goal_id)
def create_goal(user_id, title):
doc_id = goals_table.insert({
"user_id": user_id,
"title": title,
"activated": True
})
return goals_table.get(doc_id=doc_id)
def update_goal(goal_id, **kwargs):
goals_table.update(kwargs, doc_ids=[goal_id])
def delete_goal(goal_id):
tasks = tasks_table.search(Task.goal_id == goal_id)
for task in tasks:
tasks_table.remove(doc_ids=[task.doc_id])
goals_table.remove(doc_ids=[goal_id])
def count_goals_by_user(user_id):
return len(goals_table.search(Goal.user_id == user_id))
def get_tasks_by_goal(goal_id):
return tasks_table.search(Task.goal_id == goal_id)
def get_task_by_id(task_id):
return tasks_table.get(doc_id=task_id)
def create_task(goal_id, title, desc="", status="todo", order=None):
if order is None:
existing_tasks = tasks_table.search(Task.goal_id == goal_id)
unfinished = [t for t in existing_tasks if t.get("status") != "done"]
order = max([t.get("order", 0) for t in unfinished], default=0) + 1.0
doc_id = tasks_table.insert({
"goal_id": goal_id,
"title": title,
"desc": desc,
"status": status,
"start_time": None,
"finished_time": None,
"order": order
})
return tasks_table.get(doc_id=doc_id)
def update_task(task_id, **kwargs):
tasks_table.update(kwargs, doc_ids=[task_id])
def delete_task(task_id):
tasks_table.remove(doc_ids=[task_id])
def get_tasks_sorted(goal_id):
all_tasks = tasks_table.search(Task.goal_id == goal_id)
unfinished = [t for t in all_tasks if t.get("status") != "done"]
finished = [t for t in all_tasks if t.get("status") == "done"]
unfinished.sort(key=lambda t: t.get("order", 0))
finished.sort(key=lambda t: t.get("finished_time", ""), reverse=True)
return unfinished + finished