diff --git a/app/main.py b/app/main.py index 1aa1bbd..d5d5152 100644 --- a/app/main.py +++ b/app/main.py @@ -1,51 +1,16 @@ -from __future__ import annotations +from fastapi import FastAPI +from app import service, schemas -from fastapi import FastAPI, HTTPException, Query +app = FastAPI() -from .schemas import Task, TaskCreate, TaskStatus -from .service import complete_task, create_task, get_task, list_tasks +@app.get("/tasks") +def read_tasks(status: str | None = None, q: str | None = None): + return service.get_tasks(status=status, q=q) +@app.get("/tasks/{task_id}") +def read_task(task_id: int): + return service.get_task(task_id) -app = FastAPI( - title="Codex Task Tracker API", - description="A small task manager API used for Codex App lab exercises.", - version="0.1.0", -) - - -@app.get("/health") -def health_check() -> dict[str, str]: - return {"status": "ok"} - - -@app.get("/tasks", response_model=list[Task]) -def read_tasks( - status: TaskStatus | None = Query(default=None), - q: str | None = Query( - default=None, - min_length=1, - description="Case-insensitive text search across title and description.", - ), -) -> list[Task]: - return list_tasks(status=status, q=q) - - -@app.get("/tasks/{task_id}", response_model=Task) -def read_task(task_id: int) -> Task: - task = get_task(task_id) - if task is None: - raise HTTPException(status_code=404, detail="Task not found") - return task - - -@app.post("/tasks", response_model=Task, status_code=201) -def add_task(payload: TaskCreate) -> Task: - return create_task(payload.model_dump()) - - -@app.post("/tasks/{task_id}/complete", response_model=Task) -def complete_existing_task(task_id: int) -> Task: - task = complete_task(task_id) - if task is None: - raise HTTPException(status_code=404, detail="Task not found") - return task +@app.post("/tasks/{task_id}/complete") +def complete_task(task_id: int): + return service.complete_task(task_id) diff --git a/app/service.py b/app/service.py index 8022b93..8f08e62 100644 --- a/app/service.py +++ b/app/service.py @@ -1,64 +1,35 @@ -from __future__ import annotations - from datetime import datetime, timezone -from typing import Any - -from .store import load_tasks, next_task_id, save_tasks - - -def list_tasks(status: str | None = None, q: str | None = None) -> list[dict[str, Any]]: - """Return task records, optionally filtered by status and search text.""" - tasks = load_tasks() - filtered: list[dict[str, Any]] = [] - - for task in tasks: - # Instructor note: intentional bug for the lab. - # This uses the literal string "status" instead of the query parameter value. - if status and task["status"] != "status": - continue - - # Instructor note: partial feature for the lab. - # The route already accepts `q`, but search is not implemented yet. - filtered.append(task) +from app import store - return filtered +def get_tasks(status: str | None = None, q: str | None = None): + tasks = store.load_tasks() + if status: + tasks = [t for t in tasks if t.get("status") == status] -def get_task(task_id: int) -> dict[str, Any] | None: - """Find a single task by ID.""" - tasks = load_tasks() - return next((task for task in tasks if task["id"] == task_id), None) + if q: + query = q.lower() + tasks = [ + t for t in tasks + if query in t.get("title", "").lower() + or query in t.get("description", "").lower() + ] + return tasks -def create_task(payload: dict[str, Any]) -> dict[str, Any]: - """Create a new task and persist it.""" - tasks = load_tasks() - task = { - "id": next_task_id(tasks), - "title": payload["title"], - "description": payload["description"], - "status": "open", - "priority": payload["priority"], - "created_at": datetime.now(timezone.utc).isoformat(), - "completed_at": None, - } - tasks.append(task) - save_tasks(tasks) - return task - - -def complete_task(task_id: int) -> dict[str, Any] | None: - """Mark a task as completed.""" - tasks = load_tasks() - +def get_task(task_id: int): + tasks = store.load_tasks() for task in tasks: - if task["id"] == task_id: - updated_task = dict(task) - updated_task["status"] = "done" - updated_task["completed_at"] = datetime.now(timezone.utc).isoformat() - - # Instructor note: intentional bug for the lab. - # The updated task is returned, but the stored list is never updated or saved. - return updated_task + if task.get("id") == task_id: + return task + return None +def complete_task(task_id: int): + tasks = store.load_tasks() + for task in tasks: + if task.get("id") == task_id: + task["status"] = "done" + task["completed_at"] = datetime.now(timezone.utc).isoformat() + store.save_tasks(tasks) + return task return None