Skip to content

About

A fitness class booking system built with Flask and MongoDB.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Fitness Class System

Python Coverage Testing Python Unit Testing

This is a web application for managing group fitness classes, including browsing classes, booking, and trainer management.


Deployment

The application is deployed at: http://159.89.235.55

Swagger docs: http://159.89.235.55/docs


Prerequisites

  • 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 dev uses in-memory mongomock when MOCK_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.


Tech Stack

  • Backend: Flask RESTX for REST APIs, PyMongo for MongoDB communication
  • Frontend: Static SPA served by Flask from / route
  • JWT for authentication
  • Pytest for testing

Backend Setup and Running

  1. Clone this repository and navigate into it.
  2. Create a .env file as per instructions in .samplenv (keep MOCK_DB=true for this dev flow so no local MongoDB is required).
  3. Run make dev_env to create a virtual environment and install dependencies.
  4. Run make dev to start the backend in Flask debug mode (hot reload, in-memory mongomock — no real MongoDB needed). Note: make dev runs the full pytest suite first as a prerequisite — if any test fails, the server will not start. Run make tests on its own to debug failures, or use flask run directly inside the venv to skip the gate.
  5. 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).


Running with Docker

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.

  1. Clone this repository and navigate into it.
  2. (Optional) Create a .env file in the project root to override defaults. The Compose file injects sensible defaults, but at minimum you should set JWT_SECRET_KEY for any non-trivial use, and the AWS_* / EMAIL_SENDER values 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
  3. Start the stack:
    make docker
    This is a wrapper around 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 pulls mongo:7 and builds the app image; subsequent runs are fast.
  4. 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.
  5. To stop: Ctrl+C followed by make docker_down. To also wipe the MongoDB data volume: docker compose down -v.

Other Docker make targets

make docker Tears down any running stack, then docker compose up --build (foreground) |

Notes

  • The app container connects to MongoDB at mongodb://db:27017 over the internal Compose network — no need to expose the DB port to the host.
  • MongoDB data is persisted in the named volume mongo_data and survives container restarts.
  • The container runs as a non-root appuser and includes a /docs healthcheck.
  • MOCK_DB is forced to false in Compose so the app uses real MongoDB; set it to true only when running tests outside Docker.
  • MongoDB runs with --quiet so the per-connection healthcheck logs don't flood your terminal — important warnings still surface.

Backend API Endpoints

  • /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

Using API Documentation (Swagger UI) Authorization

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:

  1. Obtain a JWT token by registering and logging in via the /auth/register and /auth/login endpoints.
  2. Click the green "Authorize" button in the Swagger UI.
  3. In the popup, enter your JWT token prefixed with Bearer , for example:
    Bearer <access_token>
    
  4. Click "Authorize" and close the dialog.
  5. 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.


Frontend Access

  • 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.

Testing

  • make tests — run the full pytest suite with coverage report (no fail-under gate).
  • make coverage_check — runs coverage run --branch -m pytest and fails if total coverage drops below 90%. This is a stricter local gate than CI: python-coverage-testing.yaml currently uses line coverage only (no --branch), so anything that passes make coverage_check will also pass CI, but not the other way around.

API Usage

Authentication

Register User

  • 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'"}

Login User

  • 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"}

Classes API

List or Get Classes

  • GET /classes
  • Optional query parameters: title, date (YYYY-MM-DD), trainer
  • Use cid parameter 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"
              },
              { ... }
          ]
      }
     }

Create New Class

  • POST /classes
  • JWT token with role trainer required 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"}

Bookings API

Book a Class

  • 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"}

Cancel Booking

  • 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"}

Create Recurring Classes (Feature 6)

  • POST /classes/recurring
  • JWT token with role trainer required 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)

Send Reminder Emails

  • 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"}

Configure Notification Preferences (Feature 7)

  • 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 the chat_id from https://api.telegram.org/bot<TOKEN>/getUpdates
  • Responses:
    • Success (200 OK):
      {"message": "Preferences updated successfully"}
    • Bad Request (400) — invalid or missing channels, or telegram_chat_id missing 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"}

How reminders work with Feature 7: When POST /classes/email is 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

About

A fitness class booking system built with Flask and MongoDB.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages