Skip to content

Repository files navigation

Hospital Shift Optimization & Scheduling System

An automated scheduling and shift optimization platform designed for hospital clinics (such as the Louis Pasteur University Hospital). The system balances staff availability, specialized qualifications, and complex clinical constraints using Mathematical Optimization.


πŸš€ Project Overview

The application streamlines clinical scheduling by dividing the workflow into two major pillars:

  1. Core Administration & Tracking (Phase 1):
  • Employees: Log in to submit their unavailabilities and manage their medical qualifications.
  • Clinic Managers: Oversee specific clinics, manually adjust schedules, and manage staff constraints.
  1. Automated Optimization (Phase 2):
  • Uses a Linear Programming (LP) solver to automatically construct optimal monthly or weekly shift schedules, fully respecting employee unavailabilities and required clinical credentials.

πŸ› οΈ Technical Stack

  • Frontend: React, Vite, TypeScript, Zustand (State Management), Axios
  • Backend: Python (FastAPI), SQLAlchemy (ORM), Alembic (Migrations)
  • Database: PostgreSQL
  • Containerization: Docker & Docker Compose

πŸ“‚ Project Structure

β”œβ”€β”€ .agents/
β”‚   └── rules/              # Custom behavioral guidelines for AI development agents
β”œβ”€β”€ backend/
β”‚   β”œβ”€β”€ app/
β”‚   β”‚   β”œβ”€β”€ api/            # FastAPI REST endpoints
β”‚   β”‚   β”œβ”€β”€ models/         # SQLAlchemy ORM models
β”‚   β”‚   β”œβ”€β”€ schemas/        # Pydantic data validation and serialization
β”‚   β”‚   └── services/       # Core business logic and LP optimization algorithms
β”‚   └── database/           # DB engine sessions and migrations
β”œβ”€β”€ frontend/
β”‚   └── src/
β”‚       β”œβ”€β”€ api/            # API client configurations (Axios)
β”‚       β”œβ”€β”€ components/     # Reusable UI component library
β”‚       β”œβ”€β”€ stores/         # Global application state (Zustand)
β”‚       └── views/          # Page layouts (Manager Board, Calendars)
β”œβ”€β”€ docker-compose.yml      # Orchestrates Python, React, and PostgreSQL containers
└── CONTEXT.md              # Global technical architectural context


⚑ Quick Start (Docker Deployment)

The easiest way to run the entire stack (React frontend, FastAPI backend, and PostgreSQL database) is using Docker Compose.

Prerequisites

Make sure you have Docker and Docker Compose installed on your system.

Running the Application

  1. Clone the repository and navigate to the root directory:
git clone <repository-url>
cd scheduling

Copy the environment template and replace the placeholder password and secret:

Copy-Item .env.example .env
  1. Build and start the containers in detached mode (PowerShell):
.\start.ps1 -Detached

This command automatically:

  • Builds the React frontend container.
  • Builds the Python FastAPI backend container.
  • Retries one transient Docker BuildKit snapshot failure automatically.
  • Applies pending Alembic database migrations before the backend starts.
  • Pulls and initializes the PostgreSQL database.
  • Links them all under a single shared network.
  1. Verify the services are running:
docker compose ps
  1. Access the applications:

Stopping the Services

To stop and remove all containers, networks, and associated volumes:

docker compose down -v

πŸ§‘β€πŸ’» Development Rules & Guidelines

If you are modifying this project or using an AI developer agent (like Antigravity), you must strictly adhere to the contracts defined in .agents/rules/:

  • global.md: Enforces strict English code, docstrings, type hinting, and structural modularity.
  • backend.md: Mandates a strict 1:1:1 domain decoupling (SQLAlchemy Models ↔ Pydantic Schemas ↔ FastAPI Routers).
  • database.md: Outlines transaction safety boundaries and Alembic migration protocols.
  • database-schema.md: Defines the exact relational database layout.

Automatic test/demo data

The backend startup runs the database bootstrap before Uvicorn. Schema creation is always safe to run, while deterministic mock data is updated only when explicitly enabled:

AUTO_SEED=true
SEED_CONFIG=config_1

Each seed profile declares a version. The backend stores applied versions in seed_versions and skips an already-current profile. Increment the profile's version whenever its data changes. Keep AUTO_SEED=false (the default) for production databases; enable it only for CI, preview, test, or controlled demo environments.

What config_1 contains

config_1 is the demo world. Every schedule in it is produced by the same MILP generator the application uses, so a freshly seeded database opens on ten months of 2026 rather than on empty calendars. The departments are named after real ones; only the people on their rosters are invented.

Department Urgent People Roles Where the people come from
I.KAIM no 57 7, of which 3 to 7 are staffed in any given month the department's own roster, reinforced with invented colleagues
II.KAIM no 23 3 invented people, shuffled into the roster at random
KDAIM no 19 2 invented people, shuffled into the roster at random
KUM yes 35 2 borrowed from I.KAIM and from II.KAIM

January to September are seeded as approved schedules and October as a generated draft that is still waiting for its manager, which is the state a planner is actually in partway through a year.

Four properties of the profile are worth knowing before you change it:

  • Demand moves from month to month in I.KAIM. Each month is generated from its own entry in MONTHLY_REQUIREMENTS, so a quiet month staffs three roles and a busy one all seven, and the daily headcount ranges from four to nine. A role a month leaves out is simply not staffed that month. The levels of the last generated month stay in competences afterwards, so the configuration a manager opens is the newest one a schedule was built from.
  • Everybody has opinions about the calendar. Every person gets 8 to 12 blocked days and 2 to 4 requested days per month, drawn from a hash of the person and the month rather than from the clock. Blocked days are hard constraints; requested days only order schedules that are already equally balanced.
  • The urgent department is scheduled last, on purpose. Its roster is already committed elsewhere by then, so it exercises the rule that a duty in one department blocks the same and the neighbouring day in every other one. That is also why all of its people are qualified for both of its roles: the few days each of them has left have to be usable for whichever role is open.
  • The year ends on the settled configuration. September and October run the four roles the clinic has always run, so the staffing levels left behind in competences are the ones a manager would recognise rather than one of the experiments in the middle of the year.

config_2 is the older, smaller profile (ambulancia1..ambulancia4 and four urgent workplaces) and is kept for tests and for comparison.

Rebuilding a database from scratch

Seeding upserts by natural key and only deletes inside the narrow scopes a profile declares, so switching profiles never produces a clean database: rows from an earlier profile stay. To rebuild an environment from nothing, clear it explicitly first:

docker compose exec backend python -m app.db.reset config_1 --yes

This deletes every row of every mapped table and reapplies the profile in a single transaction, so an infeasible profile rolls back instead of leaving the database empty. The table list is read from the ORM metadata, which does not describe Alembic's alembic_version table -- the schema and its migration history therefore survive a reset, and only rows are removed.

A reset of config_1 solves 40 monthly schedules -- ten months in each of the four departments -- and takes well under a minute; the progress of each one is printed as it is generated, together with whether it was seeded approved.

The command refuses to run without --yes and prints the connection target first, because nothing distinguishes a local database from a production one at the point of use. Two consequences are worth planning for:

  • Every session token lives in users, so a reset logs everybody out.
  • A user absent from the profile is recreated on next login with the EMPLOYEE role only. Put the accounts you demo with in the profile, with the roles they need, and seed before logging back in.

About

πŸ₯ Hospital shift scheduling platform. Optimizes clinical rotas with Linear Programming 🧠 | Built via Agentic Workflow πŸ€– with FastAPI ⚑ & React βš›οΈ

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages