# OP-Saalzeiten — OP-Auslastungsmanagement für IKK Kliniken

> Echtzeit-Übersicht über OP-Saalauslastung mit Ampelsystem, Statistiken und Mehstandort-Unterstützung.

## Was ist das?

OP-Saalzeiten ist ein webbasiertes Auslastungsmanagement für OP-Säle in Kliniken. Es bietet eine Echtzeit-Ampelansicht aller Säle (belegt/frei), erfasst OP-Zeiten und liefert Statistiken zu Auslastung, Wechselzeiten und OP-Dauer — standortübergreifend für alle IKK-Kliniken.

Features:
- **Ampel-Dashboard** — Echtzeit-Status aller OP-Säle (grün = frei, rot = belegt) via SSE
- **OP starten/stoppen** — Ein Klick startet oder beendet eine OP, Dauer wird automatisch berechnet
- **Mehstandort-Unterstützung** — Mehrere Klinikstandorte mit eigenen Sälen und Koordinatoren
- **Statistik** — Auslastung (%), Wechselzeiten, OP-Dauer pro Saal und OP-Typ
- **Benutzerverwaltung** — Drei Rollen (Admin, Koordinator, Viewer) mit JWT-Auth
- **Demo-Daten** — 3 Standorte, 9 Säle, ~30 Tage historische OP-Daten

## Architektur

```
┌───────────────────────────────────────────────────┐
│          OP-Saalzeiten (FastAPI)                   │
│              localhost:8900                         │
├──────────┬───────────┬───────────┬────────────────┤
│  Auth    │  OP-Mgmt  │   SSE     │   Static       │
│ /api/    │  /api/    │  /api/    │   /static/     │
│ auth/*   │  saele/*  │  events   │   index.html   │
├──────────┴───────────┴───────────┴────────────────┤
│                                                    │
│  ┌────────────────────────────────────────────┐   │
│  │           SQLite (aiosqlite)               │   │
│  │                                            │   │
│  │  standort ──< saal ──< saalzeit            │   │
│  │  benutzer (JWT + bcrypt)                   │   │
│  └────────────────────────────────────────────┘   │
└───────────────────────────────────────────────────┘
```

## Projektstruktur

```
OP-Saalzeiten/
├── main.py                  # FastAPI-Server (single-file Backend)
├── requirements.txt         # Python-Abhängigkeiten
├── op_saalzeiten.db         # SQLite-Datenbank (wird automatisch erstellt)
├── static/
│   ├── index.html           # Dashboard (Dark Cyan Theme, SSE)
│   └── produkt.html         # Produktseite
├── README.md                # Technische Dokumentation
├── BENUTZERHANDBUCH.md      # Benutzerhandbuch
├── CLAUDE.md                # Claude Code Projektkontext
├── deploy_vps.py            # Deploy auf VPS
└── deploy_downloads.py      # Download-Seite deployen
```

## Schnellstart

```bash
pip install -r requirements.txt
python main.py
```

Der Browser öffnet automatisch `http://localhost:8900`.

**Demo-Zugangsdaten:**

| Benutzer | Passwort | Rolle |
|----------|----------|-------|
| admin | admin | ADMIN |
| p.czaikowski | koord | KOORDINATOR |

## Datenmodell

```sql
standort (id, name, adresse, aktiv, erstellt_am)
    └──< saal (id, standort_id, name, aktiv, erstellt_am)
              └──< saalzeit (id, saal_id, beginn, ende, op_typ, chirurg, bemerkung, erstellt_von, erstellt_am)

benutzer (id, username, password_hash, name, rolle, standort_id, aktiv, erstellt_am)
```

- **standort** — Klinikstandort (z.B. Ilmenau, Arnstadt, Erfurt)
- **saal** — OP-Saal an einem Standort, eindeutig pro Standort
- **saalzeit** — Eine OP-Belegung mit Beginn, Ende, OP-Typ und Chirurg
- **benutzer** — Login mit bcrypt-Hash und JWT-Token (8h Gültigkeit)

## Ampel-Logik

Ein Saal gilt als **belegt** (rot), wenn eine `saalzeit` mit `ende IS NULL` existiert.
Ein Saal gilt als **frei** (grün), wenn keine offene Saalzeit vorhanden ist.

- **OP starten:** Neue Saalzeit mit `beginn = jetzt`, `ende = NULL`
- **OP beenden:** `ende = jetzt` setzen, Dauer automatisch berechnen
- Es kann maximal eine laufende OP pro Saal geben (Constraint im Backend)

## API-Endpunkte

### Authentifizierung

| Methode | Pfad | Beschreibung |
|---------|------|-------------|
| POST | `/api/auth/login` | Login (username + password → JWT-Token) |
| GET | `/api/auth/me` | Aktueller Benutzer (Bearer-Token) |

### Standorte (nur Admin)

| Methode | Pfad | Beschreibung |
|---------|------|-------------|
| GET | `/api/standorte` | Alle aktiven Standorte |
| POST | `/api/standorte` | Standort anlegen |
| PUT | `/api/standorte/{id}` | Standort bearbeiten |
| DELETE | `/api/standorte/{id}` | Standort deaktivieren (Soft-Delete) |

### Säle (nur Admin)

| Methode | Pfad | Beschreibung |
|---------|------|-------------|
| GET | `/api/saele` | Alle Säle (optional `?standort_id=`) |
| POST | `/api/saele` | Saal anlegen |
| PUT | `/api/saele/{id}` | Saal bearbeiten |
| DELETE | `/api/saele/{id}` | Saal deaktivieren (Soft-Delete) |

### Saalzeiten (Koordinator/Admin)

| Methode | Pfad | Beschreibung |
|---------|------|-------------|
| GET | `/api/saalzeiten` | Liste mit Filtern (`saal_id`, `standort_id`, `datum_von`, `datum_bis`, `limit`, `offset`) |
| GET | `/api/saalzeiten/{id}` | Einzelne Saalzeit |
| POST | `/api/saalzeiten` | Saalzeit manuell anlegen |
| PUT | `/api/saalzeiten/{id}` | Saalzeit bearbeiten |
| DELETE | `/api/saalzeiten/{id}` | Saalzeit löschen |

### OP starten/stoppen (Koordinator/Admin)

| Methode | Pfad | Beschreibung |
|---------|------|-------------|
| POST | `/api/op/start` | OP starten (saal_id, optional op_typ, chirurg, bemerkung) |
| POST | `/api/op/stop/{saal_id}` | Laufende OP im Saal beenden |

### Status & SSE

| Methode | Pfad | Beschreibung |
|---------|------|-------------|
| GET | `/api/status` | Ampel-Status aller Säle (belegt/frei, Dauer, OP-Info) |
| GET | `/api/events` | Server-Sent Events (Live-Updates bei Status-Änderungen) |
| GET | `/api/health` | Health-Check (Version, Status) |

### Statistik

| Methode | Pfad | Beschreibung |
|---------|------|-------------|
| GET | `/api/statistik/auslastung` | Auslastung in % (OP-Zeit / 7–18 Uhr Mo–Fr), Filter: `standort_id`, `saal_id`, `monat` |
| GET | `/api/statistik/wechselzeiten` | Durchschnittliche Wechselzeit zwischen OPs pro Saal |
| GET | `/api/statistik/op-dauer` | Durchschnittliche OP-Dauer pro Saal und OP-Typ |

### Benutzerverwaltung (nur Admin)

| Methode | Pfad | Beschreibung |
|---------|------|-------------|
| GET | `/api/benutzer` | Alle Benutzer |
| POST | `/api/benutzer` | Benutzer anlegen |
| PUT | `/api/benutzer/{id}` | Benutzer bearbeiten |
| DELETE | `/api/benutzer/{id}` | Benutzer deaktivieren (Soft-Delete) |

### Dokumentation

| Methode | Pfad | Beschreibung |
|---------|------|-------------|
| GET | `/docs/readme` | README als text/markdown |
| GET | `/docs/handbuch` | Benutzerhandbuch als text/markdown |
| GET | `/produkt` | Produktseite (HTML) |

## Rollen

| Rolle | Rechte |
|-------|--------|
| **ADMIN** | Alles: Standorte, Säle, Benutzer verwalten + OP starten/stoppen + Saalzeiten |
| **KOORDINATOR** | OP starten/stoppen, Saalzeiten verwalten (CRUD), an Standort gebunden |
| **VIEWER** | Nur lesen: Dashboard, Status, Statistiken ansehen |

## Statistik

### Auslastung
Berechnet als `(OP-Minuten / verfügbare Minuten) × 100`. Verfügbare Zeit: Mo–Fr, 7–18 Uhr = 660 Minuten pro Arbeitstag.

### Wechselzeiten
Zeit zwischen Ende einer OP und Beginn der nächsten im selben Saal am selben Tag. Nur Werte unter 5 Stunden werden berücksichtigt.

### OP-Dauer
Durchschnitt, Minimum und Maximum pro Saal und OP-Typ. Gruppiert nach Standort.

## Demo-Daten

Beim ersten Start werden automatisch Demo-Daten angelegt:

- **3 Standorte:** Ilmenau (3 Säle), Arnstadt (2 Säle), Erfurt (4 Säle)
- **2 Benutzer:** admin (ADMIN), p.czaikowski (KOORDINATOR)
- **~30 Tage OP-Daten** mit 10 verschiedenen OP-Typen und 8 Chirurgen

## Deployment

### Produktion
- URL: https://op-saalzeiten.c3po42.de
- Port: 8900
- Deploy: `python deploy_vps.py`
- Downloads: `python deploy_downloads.py`

### Umgebungsvariablen

| Variable | Standard | Beschreibung |
|----------|----------|-------------|
| `DB_PATH` | `./op_saalzeiten.db` | Pfad zur SQLite-Datenbank |
| `JWT_SECRET` | (dev-secret) | JWT-Signatur-Schlüssel |
| `PORT` | `8900` | Server-Port |

## Tech-Stack

- **Backend:** Python 3.10+, FastAPI, uvicorn
- **Datenbank:** SQLite mit aiosqlite (WAL-Modus, Foreign Keys)
- **Auth:** JWT (python-jose) + bcrypt (passlib)
- **Live-Updates:** Server-Sent Events (SSE)
- **Frontend:** Single-file HTML (static/index.html), Dark Cyan Theme
