This is a web application for managing group fitness classes, including browsing classes, booking, and trainer management.
The application is deployed at: http://159.89.235.55
Swagger docs: http://159.89.235.55/docs
- Recommended: Docker Desktop / Docker Engine 20.10+ with the Compose v2 plugin (no Python or MongoDB install needed — see "Running with Docker" below).
- Or, for the local Python flow: Python 3.10 or higher. A local MongoDB install is not required because
make devuses in-memorymongomockwhenMOCK_DB=true. Only install MongoDB locally if you want to run against a real DB outside Docker.
Refer to MongoDB Installation only if you need a local MongoDB.
- Backend: Flask RESTX for REST APIs, PyMongo for MongoDB communication
- Frontend: Static SPA served by Flask from
/route - JWT for authentication
- Pytest for testing
- Clone this repository and navigate into it.
- Create a
.envfile as per instructions in.samplenv(keepMOCK_DB=truefor this dev flow so no local MongoDB is required). - Run
make dev_envto create a virtual environment and install dependencies. - Run
make devto start the backend in Flask debug mode (hot reload, in-memory mongomock — no real MongoDB needed). Note:make devruns the full pytest suite first as a prerequisite — if any test fails, the server will not start. Runmake testson its own to debug failures, or useflask rundirectly inside the venv to skip the gate. - Backend API will be available at http://127.0.0.1:8000.
For a production-like local stack with real MongoDB, use
make docker(see "Running with Docker" below).
The simplest way to run the full stack (Flask backend + MongoDB) is via Docker Compose. No local Python or MongoDB install required — only Docker Desktop / Docker Engine 20.10+ with the Compose v2 plugin.
- Clone this repository and navigate into it.
- (Optional) Create a
.envfile in the project root to override defaults. The Compose file injects sensible defaults, but at minimum you should setJWT_SECRET_KEYfor any non-trivial use, and theAWS_*/EMAIL_SENDERvalues if you want email reminders to actually send via AWS SES:JWT_SECRET_KEY=your-secret-here DB_NAME=eventsref AWS_ACCESS_KEY=... AWS_SECRET_KEY=... AWS_REGION=us-east-1 EMAIL_SENDER=verified-sender@example.com
- Start the stack:
This is a wrapper around
make docker
docker compose up --build. It tears down any previously running stack first, then rebuilds and starts both containers, so you can re-run it after every code change without thinking about cleanup. First run pullsmongo:7and builds the app image; subsequent runs are fast. - The backend will be available at http://127.0.0.1:8000 and Swagger docs at http://127.0.0.1:8000/docs for the local hosting.
- To stop:
Ctrl+Cfollowed bymake docker_down. To also wipe the MongoDB data volume:docker compose down -v.
make docker Tears down any running stack, then docker compose up --build (foreground) |
- The app container connects to MongoDB at
mongodb://db:27017over the internal Compose network — no need to expose the DB port to the host. - MongoDB data is persisted in the named volume
mongo_dataand survives container restarts. - The container runs as a non-root
appuserand includes a/docshealthcheck. MOCK_DBis forced tofalsein Compose so the app uses real MongoDB; set it totrueonly when running tests outside Docker.- MongoDB runs with
--quietso the per-connection healthcheck logs don't flood your terminal — important warnings still surface.
/classes: Manage fitness classes (GET, POST, detailed view, booking)/classes/bookings: Book or cancel a booking (POST, DELETE)/classes/email: Send class reminders to booked members (POST, trainer only)/classes/recurring: Create recurring classes (POST, trainer only) — Feature 6/auth: User authentication (login, register)/auth/preferences: Configure notification channels (PUT) — Feature 7- API documentation at
/docs
Our Swagger UI supports JWT authentication via the button labeled "Authorize" near the top right.
To make authorized requests (such as creating a class), follow these steps:
- Obtain a JWT token by registering and logging in via the
/auth/registerand/auth/loginendpoints. - Click the green "Authorize" button in the Swagger UI.
- In the popup, enter your JWT token prefixed with
Bearer, for example:Bearer <access_token> - Click "Authorize" and close the dialog.
- Now all your API calls requiring authorization will include the JWT token in the header.
This allows you to test endpoints that require authentication directly from the Swagger UI.
- Access the frontend app at http://127.0.0.1:8000/
- Use the interface to search, book classes, login, and register.
- Trainers can create new classes via the frontend.
make tests— run the full pytest suite with coverage report (no fail-under gate).make coverage_check— runscoverage run --branch -m pytestand fails if total coverage drops below 90%. This is a stricter local gate than CI:python-coverage-testing.yamlcurrently uses line coverage only (no--branch), so anything that passesmake coverage_checkwill also pass CI, but not the other way around.
- POST /auth/register
- Request JSON:
{ "name": "John Doe", "email": "john@example.com", "username": "johndoe", "password": "password123", "role": "member" } - Responses:
- Success (201 Created):
{"message": "User registered successfully"} - Username exists (409 Conflict):
{"message": "Username already exists"} - Invalid Input (400 Bad Request):
{"message": "All fields are required and role must be 'member' or 'trainer'"}
- Success (201 Created):
- POST /auth/login
- Request JSON:
{ "username": "johndoe", "password": "password123" } - Responses:
- Success (200 OK):
{ "message": "Logged in successfully", "access_token": "<jwt_token_here>" } - Invalid Input (400 Bad Request):
{"message": "Username and password are required"} - Incorrect Credentials (401 Unauthorized):
{"message": "Incorrect credentials"}
- Success (200 OK):
- GET /classes
- Optional query parameters:
title,date(YYYY-MM-DD),trainer - Use
cidparameter to get class detail (possibly with class member list if user is trainer) - Optional JWT token in Authorization header
- Response (200 OK):
{ "message": [ { "_id": "699d34ce0abb6394a8de4f21", "cid": "1", "title": "Fitness 101", "description": "A beginner fitness class", "date": "2026-07-01", "start_time": "09:00", "end_time": "10:00", "capacity": 25, "location": "Gym B", "trainer": "John Smith" }, { ... } ] } - Response (200 OK) for class details with valid JWT access token:
{ "message": { "cid": "1", "title": "Fitness 101", "description": "A beginner fitness class", "date": "2026-07-01", "start_time": "09:00", "end_time": "10:00", "capacity": 25, "location": "Gym B", "trainer": "John Smith", "booked_users": [ { "username": "ali513", "name": "Alice", "email": "ali513@nyu.edu" }, { ... } ] } }
- POST /classes
- JWT token with role
trainerrequired in Authorization header - Request JSON:
{ "title": "Fitness 101", "description": "A beginner fitness class", "date": "2026-07-01", "start_time": "09:00", "end_time": "10:00", "capacity": 25, "location": "Gym A", "trainer": "John Smith" } - Responses:
- Success (200 OK):
{"message": "Class created with id: <id>"} - Unauthorized (401):
{"message": "Invalid token"} - Forbidden (403):
{"message": "Only trainers can create classes"} - Validation errors (406 Not Acceptable):
{"message": "Capacity is required and has to be a positive integer"}
- Success (200 OK):
- POST /classes/bookings
- JWT token required
- Request JSON:
{"cid": "<class_id>"} - Responses:
- Success (201 Created):
{"message": "Booking created"} - Bad Request (400):
{"message": "cid is required"} - Not Found (404):
{"message": "Class not found"}
- Success (201 Created):
- DELETE /classes/bookings
- JWT token required
- Request JSON:
{"cid": "<class_id>"} - Responses:
- Success (200 OK):
{"message": "Booking canceled"} - Bad Request (400):
{"message": "cid is required"} - Not Found (404):
{"message": "Booking not found"}
- Success (200 OK):
- POST /classes/recurring
- JWT token with role
trainerrequired in Authorization header - Creates one class per occurrence date generated by the chosen recurrence pattern. Dates that conflict with an existing class or fall in the past are skipped and reported separately.
- Request JSON:
{ "title": "Morning Yoga", "description": "Weekly yoga session", "date": "2026-08-01", "start_time": "08:00", "end_time": "09:00", "capacity": 15, "location": "Studio A", "recurrence_type": "weekly", "recurrence_end_date": "2026-09-30" } recurrence_type:"daily"or"weekly"(more patterns can be added without modifying existing code)recurrence_end_date: last date (inclusive) for generating occurrences (YYYY-MM-DD)- Responses:
- Success (200 OK):
{ "message": "Recurring classes created", "created": ["4", "5", "6", "7", "8", "9"], "skipped": [ {"date": "2026-08-15", "reason": "CONFLICT"} ] } - Unauthorized (401), Forbidden (403), Invalid params (406 Not Acceptable)
- Success (200 OK):
- POST /classes/email
- JWT token required, must be a trainer role
- Request JSON:
{"cid": "<class_id>"} - Description: Sends reminder emails to all users booked in the specified class.
- Responses:
- Success (200 OK):
{ "message": "Emails processed", "success": <number_of_successful_emails>, "failed": <number_of_failed_emails> } - Bad Request (400):
{"message": "cid is required"} - Unauthorized (401):
{"message": "Unauthorized or invalid token"} - Forbidden (403):
{"message": "Only trainers can send reminder emails"} - Not Found (404):
{"message": "Class not found"} - OK (200):
{"message": "No members booked for this class"}
- Success (200 OK):
- PUT /auth/preferences
- JWT token required (any role)
- Sets which channels the authenticated user wants to receive class reminders on. Supported channels:
email,telegram. Additional channels (SMS, WhatsApp, etc.) can be added without modifying existing code. - Request JSON:
{ "channels": ["email", "telegram"], "telegram_chat_id": "123456789" } channels: non-empty list of channel strings ("email"and/or"telegram")telegram_chat_id: required when"telegram"is included. Obtain by messaging your Telegram bot and reading thechat_idfromhttps://api.telegram.org/bot<TOKEN>/getUpdates- Responses:
- Success (200 OK):
{"message": "Preferences updated successfully"} - Bad Request (400) — invalid or missing channels, or
telegram_chat_idmissing when telegram selected:{"message": "telegram_chat_id is required when telegram is selected"} - Unauthorized (401):
{"message": "Unauthorized or invalid token"} - Not Found (404) — user not found:
{"message": "User not found"}
- Success (200 OK):
How reminders work with Feature 7: When
POST /classes/emailis called, each booked member receives a notification on every channel listed in their preferences. A member who selected["email", "telegram"]gets both. Defaults to email-only for existing accounts.
© K-SoftwareEngineers | Jaehyeon Kim, Daewoong Lim, Joonhak Kim