"""FlowDeck — My Tasks router (v1.9.0).""" from __future__ import annotations import json import logging from fastapi import APIRouter, Request, HTTPException, Query from fastapi.responses import HTMLResponse from app.db import get_conn from app.auth.session import SessionManager logger = logging.getLogger(__name__) router = APIRouter(tags=["my-tasks"], prefix="/my-tasks") def _get_current_user(request: Request) -> dict | None: session = request.cookies.get("flowdeck_session", "") return SessionManager.decode_session(session) @router.get("", response_class=HTMLResponse) async def my_tasks_dashboard(request: Request, view: str = "all", days: int = 7): """My Tasks — aggregates all pages assigned to the current user across all collections.""" user = _get_current_user(request) user_login = user.get("login", "admin") if user else "admin" with get_conn() as conn: collections = conn.execute("SELECT * FROM collections ORDER BY name").fetchall() grouped: dict[str, list[dict]] = {} for col in collections: col_id = col["id"] pages = conn.execute( "SELECT * FROM collection_pages WHERE collection_id=? AND parent_id IS NULL ORDER BY position", (col_id,), ).fetchall() collection_tasks = [] for p in pages: props = json.loads(p["property_values_json"]) # Check if assigned to current user assigned = False for v in props.values(): if isinstance(v, list): for item in v: if isinstance(item, dict) and item.get("login") == user_login: assigned = True break elif isinstance(v, str) and user_login.lower() in v.lower(): assigned = True if assigned: break if not assigned and user_login == "admin": assigned = True # admin sees all if not assigned: continue # Extract status and due date status = None due_date = None for v in props.values(): if isinstance(v, str) and v in ("Todo", "In Progress", "Done", "Complete"): status = v elif isinstance(v, str) and v.startswith("20"): due_date = v[:10] # Filter by view if view == "today": from datetime import date if not due_date or due_date != date.today().isoformat(): continue elif view == "overdue": from datetime import date if not due_date or due_date >= date.today().isoformat(): continue elif view == "upcoming": from datetime import date, timedelta if not due_date: continue today = date.today() limit = today + timedelta(days=days) d = date.fromisoformat(due_date) if d < today or d > limit: continue collection_tasks.append({ "id": p["id"], "title": p["title"], "icon": p.get("icon", "📄"), "status": status, "due_date": due_date, "props": props, }) if collection_tasks: grouped[col["name"]] = collection_tasks total = sum(len(t) for t in grouped.values()) # Render sections = "" for col_name, tasks in grouped.items(): items = "" for t in tasks: due_badge = f"{t['due_date']}" if t.get("due_date") else "" status_badge = f"{t.get('status','—')}" items += f"""
{total} tasks across {len(grouped)} collections
""") @router.get("/api") async def my_tasks_api(request: Request, view: str = "all", days: int = 7): """API: return my tasks as JSON.""" user = _get_current_user(request) user_login = user.get("login", "admin") if user else "admin" with get_conn() as conn: collections = conn.execute("SELECT * FROM collections ORDER BY name").fetchall() result = {} for col in collections: pages = conn.execute( "SELECT * FROM collection_pages WHERE collection_id=? AND parent_id IS NULL ORDER BY position", (col["id"],), ).fetchall() tasks = [] for p in pages: props = json.loads(p["property_values_json"]) tasks.append({ "id": p["id"], "title": p["title"], "icon": p["icon"], "properties": props, }) if tasks: result[col["name"]] = tasks return {"tasks": result, "total": sum(len(t) for t in result.values())}