from fastapi import FastAPI, HTTPException, status from pydantic import BaseModel from typing import List, Optional from pathlib import Path import uuid import sqlite3 # Create the FastAPI app app = FastAPI( title="Simple Todo App", description="A very simple todo application using FastAPI and SQLite", version="0.1.0", ) # Ensure database directory exists DB_DIR = Path("/app") / "storage" / "db" DB_DIR.mkdir(parents=True, exist_ok=True) # Database setup SQLALCHEMY_DATABASE_URL = f"sqlite:///{DB_DIR}/db.sqlite" DB_PATH = f"{DB_DIR}/db.sqlite" def init_db(): """Initialize the SQLite database with the todos table if it doesn't exist.""" conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() cursor.execute(''' CREATE TABLE IF NOT EXISTS todos ( id TEXT PRIMARY KEY, title TEXT NOT NULL, description TEXT, completed BOOLEAN NOT NULL DEFAULT 0 ) ''') conn.commit() conn.close() # Initialize database on startup init_db() # Pydantic models class TodoBase(BaseModel): title: str description: Optional[str] = None completed: bool = False class TodoCreate(TodoBase): pass class Todo(TodoBase): id: str class Config: from_attributes = True # Health check endpoint @app.get("/health", status_code=status.HTTP_200_OK, tags=["health"]) async def health_check(): """Return health status of the application.""" return {"status": "healthy"} # CRUD Operations @app.post("/todos/", response_model=Todo, status_code=status.HTTP_201_CREATED, tags=["todos"]) async def create_todo(todo: TodoCreate): """Create a new todo item.""" todo_id = str(uuid.uuid4()) conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() cursor.execute( "INSERT INTO todos (id, title, description, completed) VALUES (?, ?, ?, ?)", (todo_id, todo.title, todo.description, todo.completed) ) conn.commit() conn.close() return {**todo.model_dump(), "id": todo_id} @app.get("/todos/", response_model=List[Todo], tags=["todos"]) async def read_todos(): """Get all todo items.""" conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row cursor = conn.cursor() cursor.execute("SELECT * FROM todos") rows = cursor.fetchall() conn.close() return [dict(row) for row in rows] @app.get("/todos/{todo_id}", response_model=Todo, tags=["todos"]) async def read_todo(todo_id: str): """Get a specific todo item by ID.""" conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row cursor = conn.cursor() cursor.execute("SELECT * FROM todos WHERE id = ?", (todo_id,)) row = cursor.fetchone() conn.close() if row is None: raise HTTPException(status_code=404, detail="Todo not found") return dict(row) @app.put("/todos/{todo_id}", response_model=Todo, tags=["todos"]) async def update_todo(todo_id: str, todo: TodoCreate): """Update a todo item.""" conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() cursor.execute( "UPDATE todos SET title = ?, description = ?, completed = ? WHERE id = ?", (todo.title, todo.description, todo.completed, todo_id) ) if cursor.rowcount == 0: conn.close() raise HTTPException(status_code=404, detail="Todo not found") conn.commit() conn.close() return {**todo.model_dump(), "id": todo_id} @app.delete("/todos/{todo_id}", status_code=status.HTTP_204_NO_CONTENT, response_model=None, tags=["todos"]) async def delete_todo(todo_id: str): """Delete a todo item.""" conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() cursor.execute("DELETE FROM todos WHERE id = ?", (todo_id,)) if cursor.rowcount == 0: conn.close() raise HTTPException(status_code=404, detail="Todo not found") conn.commit() conn.close() return None if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000)