from datetime import date from quart import Blueprint, jsonify, request from fabledassistant.models.task import TaskPriority, TaskStatus from fabledassistant.services.tasks import ( create_task, delete_task, get_task, list_tasks, update_task, ) from fabledassistant.utils.tags import extract_tags tasks_bp = Blueprint("tasks", __name__, url_prefix="/api/tasks") @tasks_bp.route("", methods=["GET"]) async def list_tasks_route(): q = request.args.get("q") tag = request.args.getlist("tag") status = request.args.get("status") priority = request.args.get("priority") note_id = request.args.get("note_id", type=int) sort = request.args.get("sort", "updated_at") order = request.args.get("order", "desc") limit = request.args.get("limit", 50, type=int) offset = request.args.get("offset", 0, type=int) tasks, total = await list_tasks( q=q, tags=tag or None, status=status, priority=priority, note_id=note_id, sort=sort, order=order, limit=limit, offset=offset, ) return jsonify({"tasks": [t.to_dict() for t in tasks], "total": total}) @tasks_bp.route("", methods=["POST"]) async def create_task_route(): data = await request.get_json() description = data.get("description", "") tags = extract_tags(description) due_date = None if data.get("due_date"): due_date = date.fromisoformat(data["due_date"]) status = TaskStatus(data["status"]) if "status" in data else TaskStatus.todo priority = ( TaskPriority(data["priority"]) if "priority" in data else TaskPriority.none ) task = await create_task( title=data.get("title", ""), description=description, status=status, priority=priority, due_date=due_date, tags=tags, ) return jsonify(task.to_dict()), 201 @tasks_bp.route("/", methods=["GET"]) async def get_task_route(task_id: int): task = await get_task(task_id) if task is None: return jsonify({"error": "Task not found"}), 404 return jsonify(task.to_dict()) @tasks_bp.route("/", methods=["PUT"]) async def update_task_route(task_id: int): data = await request.get_json() fields = {} for key in ("title", "description", "status", "priority"): if key in data: fields[key] = data[key] if "due_date" in data: fields["due_date"] = ( date.fromisoformat(data["due_date"]) if data["due_date"] else None ) if "description" in fields: fields["tags"] = extract_tags(fields["description"]) task = await update_task(task_id, **fields) if task is None: return jsonify({"error": "Task not found"}), 404 return jsonify(task.to_dict()) @tasks_bp.route("//status", methods=["PATCH"]) async def patch_task_status(task_id: int): data = await request.get_json() status_val = data.get("status") if not status_val: return jsonify({"error": "status is required"}), 400 try: TaskStatus(status_val) except ValueError: return jsonify({"error": f"Invalid status: {status_val}"}), 400 task = await update_task(task_id, status=status_val) if task is None: return jsonify({"error": "Task not found"}), 404 return jsonify(task.to_dict()) @tasks_bp.route("/", methods=["DELETE"]) async def delete_task_route(task_id: int): deleted = await delete_task(task_id) if not deleted: return jsonify({"error": "Task not found"}), 404 return "", 204