from uuid import uuid4 from fastapi import APIRouter, HTTPException from app.db import fetch_project_or_404, get_conn, utcnow from app.models import Project, ProjectCreate, ProjectUpdate router = APIRouter(prefix="/projects", tags=["projects"]) @router.post("", response_model=Project, status_code=201) def create_project(payload: ProjectCreate) -> Project: if not payload.name.strip(): raise HTTPException(status_code=400, detail="name is required") now = utcnow() record = { "id": str(uuid4()), "name": payload.name.strip(), "external_ref": payload.external_ref, "created_at": now, "updated_at": now, } with get_conn() as conn: conn.execute( "INSERT INTO projects (id, name, external_ref, created_at, updated_at) VALUES (%s, %s, %s, %s, %s)", (record["id"], record["name"], record["external_ref"], record["created_at"], record["updated_at"]), ) conn.commit() return Project.model_validate(record) @router.get("", response_model=list[Project]) def list_projects() -> list[Project]: with get_conn() as conn: rows = conn.execute( "SELECT id, name, external_ref, created_at, updated_at FROM projects ORDER BY created_at ASC" ).fetchall() return [Project.model_validate(row) for row in rows] @router.get("/{project_id}", response_model=Project) def get_project(project_id: str) -> Project: with get_conn() as conn: return Project.model_validate(fetch_project_or_404(conn, project_id)) @router.patch("/{project_id}", response_model=Project) def update_project(project_id: str, payload: ProjectUpdate) -> Project: with get_conn() as conn: current = fetch_project_or_404(conn, project_id) if payload.name is not None: if not payload.name.strip(): raise HTTPException(status_code=400, detail="name cannot be empty") current["name"] = payload.name.strip() if payload.external_ref is not None: current["external_ref"] = payload.external_ref current["updated_at"] = utcnow() conn.execute( "UPDATE projects SET name = %s, external_ref = %s, updated_at = %s WHERE id = %s", (current["name"], current["external_ref"], current["updated_at"], project_id), ) conn.commit() return Project.model_validate(current)