diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..628b32e --- /dev/null +++ b/.dockerignore @@ -0,0 +1,15 @@ +# Ignore all files + +* + +# Whitelist necessary build files +!requirements.txt + +# Allow Docker to read app directory +!app/ + +# Ignore everything inside app +app/* + +# Only allow .py files within app and its subdirectories +!app/**/*.py diff --git a/.github/workflows/black-check.yml b/.github/workflows/black-check.yml index 1d11de9..107b541 100644 --- a/.github/workflows/black-check.yml +++ b/.github/workflows/black-check.yml @@ -1,25 +1,11 @@ -name: Black Check +name: Black Formatting Check +# Run on every push and pull request on: [push, pull_request] jobs: - format-check: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.12.2' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install black - - - name: Run Black check - run: black --check . - \ No newline at end of file + call-format-check: + uses: project-carpi/.github/.github/workflows/black-check.yml@main + permissions: + contents: read + secrets: inherit diff --git a/.github/workflows/image-publish.yml b/.github/workflows/image-publish.yml new file mode 100644 index 0000000..3136378 --- /dev/null +++ b/.github/workflows/image-publish.yml @@ -0,0 +1,15 @@ +name: Build and Push Docker Image to GHCR + +# Run this workflow every time code is pushed or merged to the main branch +on: + push: + branches: + - main + +jobs: + call-build-and-push: + uses: project-carpi/.github/.github/workflows/image-publish.yml@main + permissions: + contents: read + packages: write + secrets: inherit diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..61b0fae --- /dev/null +++ b/Dockerfile @@ -0,0 +1,31 @@ +FROM python:3.12-slim AS base + +# Git is needed to install the git-based dependency in requirements.txt +RUN apt-get update && \ + apt-get install -y git && \ + rm -rf /var/lib/apt/lists/* + +# Store all application files in /api +WORKDIR /api + +# Copy the requirements file and install dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy the rest of the application code into the container +COPY . . + +# Indicate that the container should listen on port 8000 +EXPOSE 8000 + +# --- DEVELOPMENT STAGE --- +FROM base as dev + +# Run Uvicorn with reload enabled for development +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"] + +### --- PRODUCTION STAGE --- +FROM base as prod + +# Run Uvicorn for production +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file diff --git a/README.md b/README.md index 9e6d849..94543d9 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,37 @@ -# CARPI-Backend-FastAPI +# CARPI Course Planner - API -FastAPI re-write of the old Spring Boot API. +This is the API service for the CARPI Course Planner application. It is designed to work in tandem with the [frontend](https://github.com/Project-CARPI/site) and [SIS scraper](https://github.com/Project-CARPI/sis-scraper). + +## Running the API + +### Prerequisites + +- **[Docker](https://www.docker.com/products/docker-desktop/)** (Required): Used for the streamlined development environment setup. +- **[Python >= 3.12](https://www.python.org/)** (Optional): Useful for code editor linting or running the API locally without Docker. + +### Setup Instructions + +**1. Configure Environment Variables** + +Create a copy of the `example.env` file in the project root and rename it to `.env`. + +The default values provided in the example file will work out of the box, but they can be customized as needed. + +**2. Start the Development Environment** + +With Docker Engine running, execute the following command in your terminal: + +```bash +docker compose run --rm --service-ports api && docker compose down +``` + +While the container is running: + +- **Live Reloading:** Local files are mounted to the running container, so the API service will automatically respond to live changes in your source code. +- **API Documentation:** A list of endpoint definitions and interactive Swagger documentation can be found at http://localhost:8000/docs. + +### About the Database Environment + +- **Shared Database:** The development environment pulls a shared MySQL database service definition from an external repository, creating a local database volume. +- **Data Persistence:** The test database volume persists between Docker start and stop cycles, ensuring test data remains available across development sessions and for other services that depend on the database. +- **Resetting the Database:** To completely rebuild the database from scratch, you must manually remove the corresponding Docker volume prior to starting the environment. diff --git a/app/dependencies.py b/app/dependencies.py index 2879e86..b9c3def 100644 --- a/app/dependencies.py +++ b/app/dependencies.py @@ -2,12 +2,12 @@ from contextlib import asynccontextmanager from typing import Annotated, AsyncGenerator, Generator +import carpi_data_model.models as models from fastapi import Depends, FastAPI from pydantic_settings import BaseSettings, SettingsConfigDict -from sqlalchemy.engine import Engine from sqlalchemy import create_engine +from sqlalchemy.engine import Engine from sqlalchemy.orm import Session, sessionmaker -import carpi_data_model.models as models class _Settings(BaseSettings): @@ -18,7 +18,8 @@ class _Settings(BaseSettings): db_password: str db_schema: str model_config = SettingsConfigDict( - env_file=os.path.join(os.path.dirname(__file__), ".env") + env_file=os.path.join(os.path.dirname(os.path.dirname(__file__)), ".env"), + extra="ignore", ) diff --git a/app/example.env b/app/example.env deleted file mode 100644 index bbdb23a..0000000 --- a/app/example.env +++ /dev/null @@ -1,8 +0,0 @@ -# Create a file named ".env" in this directory with the following variables - -DB_DIALECT="mysql" -DB_API="mysqlconnector" -DB_HOSTNAME="hostname:port" -DB_USERNAME="username" -DB_PASSWORD="password" -DB_SCHEMA="schema" \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..c9cc47d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,52 @@ +# Docker Compose configuration for the CARPI API environment. +# +# This Compose file includes a shared development environment configuration +# from the Project-CARPI/.github repository, which contains a database service. +# +# To start the development environment, run: +# docker compose run --rm --service-ports api && docker compose down +# +# This will start both the shared development environment and the carpi-api +# container. After the API container is stopped, the "docker compose down" will +# stop the shared development environment. +# +# A warning may appear about the test database volume already existing. This is +# expected and can be safely ignored. +# +# The test database volume persists between Docker start and stop cycles so +# that test data remains available across both development sessions and other +# services that depend on the database. The volume must be manually removed in +# order to build the database from scratch. + +name: carpi-api-dev-environment + +include: + - https://github.com/Project-CARPI/.github.git#main:shared-dev-env/docker-compose.yml + +services: + api: + build: + context: . + target: dev + image: carpi-api:latest + container_name: carpi-api + # Ensure image is built locally and is not pulled from a registry + pull_policy: build + restart: on-failure + ports: + - "8000:8000" + environment: + DB_DIALECT: ${DB_DIALECT} + DB_API: ${DB_API} + # Hardcode the DB_HOSTNAME environment variable + DB_HOSTNAME: db:3306 + DB_USERNAME: ${DB_USERNAME} + DB_PASSWORD: ${DB_PASSWORD} + DB_SCHEMA: ${DB_SCHEMA} + # Mount local code over the code built into the container for automatic + # reloading during development. + volumes: + - .:/api + depends_on: + db: + condition: service_healthy diff --git a/example.env b/example.env new file mode 100644 index 0000000..413468f --- /dev/null +++ b/example.env @@ -0,0 +1,16 @@ +# Create a file named ".env" in this directory with the following variables. +# If using Docker Compose for development, these values can be used as-is. + +# This only needs to be set if the database is different from the one used +# in the Docker Compose environment. +DB_HOSTNAME="hostname:port" + +# This root password is only used for instantiating the MySQL container in +# Docker Compose. +DB_ROOT_PASSWORD="password" + +DB_DIALECT="mysql" +DB_API="mysqlconnector" +DB_USERNAME="username" +DB_PASSWORD="password" +DB_SCHEMA="schema" \ No newline at end of file