diff --git a/app/README.md b/app/README.md new file mode 100644 index 0000000..94f1bc5 --- /dev/null +++ b/app/README.md @@ -0,0 +1,103 @@ +# Important info for Docker & FastAPI +## Starting & Stopping + +### just like before with the timescaledb, this will also start fastapi +docker compose up -d + +### stop everything +docker compose down + +### restart just fastapi +docker compose restart fastapi + +## Viewing Logs + +docker compose logs -f fastapi + +docker compose logs -f timescaledb + +## Running Python Files + +Any file in the volumes section of `docker-compose.yml` can be accessed. +If you want to access a file in a different folder, add it with the same schema as the other directories. + +### example of running /database/example.py +docker exec lsu-fsae-fastapi python /database/example.py + +### open an interactive Python shell +docker exec -it lsu-fsae-fastapi python + +### open a full bash shell inside the container +docker exec -it lsu-fsae-fastapi bash + +## Adding a New Package + +1. Add it to `app/requirements.txt`: + +2. Recreate the container: + +`docker compose up --force-recreate fastapi` + +If you just want to test a package before adding for everyone: + +`docker exec lsu-fsae-fastapi pip install [package]` + +## API Links + +`http://localhost:8000` - API status +`http://localhost:8000/health` - DB connection +`http://localhost:8000/docs` - Interactive API docs + +## VSCode Example + +**1. Install the Python extension in VSCode** + +**2. Create a virtual environment:** +`cd app` +`python -m venv venv` + +**3. Select the interpreter in VSCode:** +- Open Command Palette `Python: Select Interpreter` +- Pick the one inside `./app/venv` + +**4. Install dependencies in the VSCode terminal:** +`source venv/bin/activate` OR `source venv\Scripts\activate` + +`pip install -r requirements.txt` + +**5. Start the database:** +`docker compose up -d timescaledb` + +**6. Run FastAPI:** +`uvicorn main:app --reload` + +## Adding New API Route example: +``` + simple GET route — no database +@app.get("/hello") +async def hello(): + return {"message": "hello from FSAE"} + + +# GET route with a URL parameter + database query +@app.get("/sensor/{sensor_id}") +async def get_sensor(sensor_id: str, request: Request): + async with request.app.state.pool.connection() as conn: + rows = await (await conn.execute( + "SELECT time, value FROM telemetry WHERE sensor_id = %s ORDER BY time DESC LIMIT 100", + (sensor_id,) + )).fetchall() + return [{"time": str(r[0]), "value": r[1]} for r in rows] + + +# POST route — write data to the database +@app.post("/sensor/{sensor_id}") +async def post_sensor(sensor_id: str, value: float, request: Request): + async with request.app.state.pool.connection() as conn: + await conn.execute( + "INSERT INTO telemetry (time, sensor_id, value) VALUES (NOW(), %s, %s)", + (sensor_id, value) + ) + return {"inserted": True} +``` + diff --git a/app/__pycache__/main.cpython-313.pyc b/app/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000..eb52ebc Binary files /dev/null and b/app/__pycache__/main.cpython-313.pyc differ diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000..889b7ef --- /dev/null +++ b/app/main.py @@ -0,0 +1,26 @@ +import os +from contextlib import asynccontextmanager +from fastapi import FastAPI, Request +from psycopg_pool import AsyncConnectionPool + +DATABASE_URL = os.environ.get("DATABASE_URL", "postgresql://fsae:fsae2026@timescaledb:5432/daq_data") + +@asynccontextmanager +async def lifespan(app: FastAPI): + pool = AsyncConnectionPool(conninfo=DATABASE_URL, min_size=1, max_size=10, open=False) + await pool.open(wait=True, timeout=30) + app.state.pool = pool + yield + await pool.close() + +app = FastAPI(title="LSU FSAE DAQ API", lifespan=lifespan) + +@app.get("/") +async def root(): + return {"status": "running"} + +@app.get("/health") +async def health(request: Request): + async with request.app.state.pool.connection() as conn: + await conn.execute("SELECT 1") + return {"status": "healthy"} diff --git a/app/requirements.txt b/app/requirements.txt new file mode 100644 index 0000000..00f7c7b --- /dev/null +++ b/app/requirements.txt @@ -0,0 +1,4 @@ +fastapi +uvicorn[standard] +psycopg[binary, pool] +pydantic-settings diff --git a/docker-compose.yml b/docker-compose.yml index efe254a..ef31615 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,6 +10,33 @@ services: - "5432:5432" volumes: - timescale_data:/var/lib/postgresql/data + networks: + - fsae-network + + fastapi: + image: python:3.13-slim + container_name: lsu-fsae-fastapi + working_dir: /app + volumes: + - ./app:/app + - ./dashboard:/dashboard + - ./database:/database + - ./parser:/parser + command: > + sh -c "pip install --no-cache-dir -r requirements.txt && + uvicorn main:app --host 0.0.0.0 --port 8000" + ports: + - "8000:8000" + environment: + DATABASE_URL: postgresql://fsae:fsae2026@timescaledb:5432/daq_data + depends_on: + - timescaledb + networks: + - fsae-network + +networks: + fsae-network: + driver: bridge volumes: - timescale_data: \ No newline at end of file + timescale_data: