- Complete tasks now displayed in scroll view alongside unfinished tasks - Priority order: completed tasks first (by finished_time desc), then unfinished (by order asc) - Time picker-style scroll: wheel scroll snaps per task, center item gets visual focus - Landscape mode (>=1024px): scroll view + edit panel side by side, panel always visible - Portrait mode: edit panel slides in from right on tap - Fixed flex layout so scroll view and edit panel align perfectly in height
138 lines
3.4 KiB
Python
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 finished + unfinished
|