306886a42b
Auth (Test-Mode):
- middleware.AuthMiddleware now takes a devMode flag. In dev,
requests without Authorization fall back to a deterministic dev
UUID (00000000-...-001) and role=teacher. ENVIRONMENT=production
re-enables the strict 401 path.
- main.go wires devMode = cfg.Environment != "production".
- page.tsx replaces the red 'Anmeldung noch nicht integriert' banner
with a softer Testumgebung notice; the manual-token form moves
behind a nested details block.
Export endpoints (school-service):
- LoadExportLessons joins tt_lesson with tt_period for wall-clock
times; one query feeds both CSV and ICS.
- WriteCSV streams 10 columns including pinned flag.
- WriteICS emits one VEVENT per lesson anchored to a Monday — caller
overridable via ?start=YYYY-MM-DD. RFC 5545 escapes for ',', ';',
'\n' in icsEscape().
- NextMonday helper for the default anchor.
- GET /timetable/solutions/:id/export.{csv,ics} handlers attach
Content-Disposition: attachment so browsers download instead of
rendering.
Frontend:
- lib/stundenplan/api.ts downloadSolutionExport() fetches as blob,
triggers a synthetic <a download> click, and forwards the JWT when
present.
- PlanView gains CSV / ICS / Drucken buttons next to the perspective
selector. The toolbar carries class 'no-print' so window.print()
yields only the grid.
- globals.css @media print rule hides chrome, forces white
background, gives the table proper borders for A4.
Docs:
- docs-src/services/stundenplan/{index,architecture,constraints,
solver-tuning,export}.md with nav entry in mkdocs.yml under
Services → Stundenplaner.
- sbom/stundenplan/README.md lists manually-verified key dependencies
and the policy reference. scripts/stundenplan-sbom.sh generates
full machine-readable inventories via go-licenses + pip-licenses
+ license-checker when those tools are available.
Tests:
- internal/services/timetable_exports_test.go: 4 unit tests covering
CSV column layout + quoting, ICS structure + DTSTART formatting,
icsEscape special chars, NextMonday weekday math.
- studio-v2/e2e/stundenplan-export.spec.ts split out of the main spec
file (LOC budget) — 3 tests for button render, CSV download,
ICS download.
- mockSchoolApi extended with export.csv + export.ics routes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
72 lines
3.5 KiB
Markdown
72 lines
3.5 KiB
Markdown
# Constraint-Referenz
|
||
|
||
Jeder Constraint-Eintrag im UI legt eine Row in der korrespondierenden
|
||
`tt_constraint_*` Tabelle an. Der Solver liest sie als Problem-Facts und
|
||
joined sie gegen die Lessons.
|
||
|
||
## Gemeinsame Felder
|
||
|
||
| Feld | Typ | Bedeutung |
|
||
|------|-----|-----------|
|
||
| `is_hard` | bool | true = Solver muss einhalten (HardScore -1 pro Verstoss). false = Soft-Penalty (SoftScore -weight pro Verstoss) |
|
||
| `weight` | int 0-100 | Multiplikator fuer Soft-Penalties; bei Hard ignoriert |
|
||
| `active` | bool | inaktive Rows werden vom Solver ignoriert |
|
||
| `note` | TEXT | Freier Begruendungstext fuer den Rektor |
|
||
|
||
## Constraint-Typen
|
||
|
||
### Universal (immer aktiv, nicht abschaltbar)
|
||
|
||
| Constraint | Bedeutung |
|
||
|------------|-----------|
|
||
| `class_conflict` | Eine Klasse hat nur eine Lesson pro Timeslot |
|
||
| `teacher_conflict` | Ein Lehrer haelt nur eine Lesson pro Timeslot |
|
||
| `room_conflict` | Ein Raum hostet nur eine Lesson pro Timeslot |
|
||
|
||
### DB-Driven (vom Rektor konfigurierbar)
|
||
|
||
Jeder Typ existiert als `_hard` und `_soft` Constraint im Provider:
|
||
|
||
| Typ | Tabelle | Beispiel |
|
||
|-----|---------|----------|
|
||
| Lehrer Tag nicht verfuegbar | `tt_constraint_teacher_unavailable_day` | „Anna nie Montags" |
|
||
| Lehrer Zeitfenster nicht verfuegbar | `tt_constraint_teacher_unavailable_window` | „Bob Dienstag 13–17 Uhr nicht" |
|
||
| Lehrer Max h/Tag | `tt_constraint_teacher_max_hours_day` | Anti-Burnout |
|
||
| Lehrer Max h/Woche | `tt_constraint_teacher_max_hours_week` | Teilzeit-Cap |
|
||
| Lehrer Fach ausgeschlossen | `tt_constraint_teacher_excluded_subject` | Qualifikationsluecke |
|
||
| Lehrer Raum ausgeschlossen | `tt_constraint_teacher_excluded_room` | Rollstuhl, kein Fahrstuhl |
|
||
| Fach Mindest-Tagesabstand | `tt_constraint_subject_min_day_gap` | Mathe nicht 2 Tage hintereinander |
|
||
| Fach Max Stunden am Stueck | `tt_constraint_subject_max_consecutive` | Keine Dreifachstunde |
|
||
| Fach Mehrfach=zusammen | `tt_constraint_subject_contiguous_when_repeated` | Wenn 2× am Tag, dann benachbart |
|
||
| Fach Bevorzugte Stunden | `tt_constraint_subject_preferred_period` | Hauptfaecher morgens |
|
||
| Fach Doppelstunde bevorzugt | `tt_constraint_subject_double_lesson` | Sport als 90-min-Block |
|
||
| Klasse Max h/Tag | `tt_constraint_class_max_hours_day` | Jugendgerecht |
|
||
| Klasse Keine Freistunden | `tt_constraint_class_no_gaps` | Soft, minimiert Loecher |
|
||
| Raumtyp erforderlich | `tt_constraint_room_requires_type` | Sport → Sporthalle |
|
||
| Raum nicht verfuegbar | `tt_constraint_room_unavailable` | Wartung, Renovierung |
|
||
|
||
## Hard vs. Soft — Faustregel
|
||
|
||
- **Hard** wenn die Schule den Plan rechtlich oder physisch nicht
|
||
ausfuehren kann (Lehrervertrag, Behinderung, Raum existiert nicht).
|
||
- **Soft** wenn es nur eine Praeferenz ist („Mathe lieber morgens",
|
||
„keine Freistunden").
|
||
|
||
Score-Bewertung im UI:
|
||
- `hard_score = 0` → Plan ist gueltig
|
||
- `hard_score < 0` → mindestens eine harte Regel ist verletzt (Solver
|
||
meldet das als `infeasible`)
|
||
- `soft_score` → wird in den UI angezeigt; je naeher an 0, desto besser
|
||
|
||
## Erweitern um einen 16. Constraint-Typ
|
||
|
||
1. Neue Tabelle in `school-service/internal/database/timetable_constraints_migrations.go`
|
||
2. Model + DTO in `models/timetable_constraints.go`
|
||
3. Service + Handler im gleichen Paket-Pattern wie die existierenden 15
|
||
4. Route in `cmd/server/main.go`
|
||
5. Rule-Dataclass in `timetable-solver-service/app/rules.py`
|
||
6. ProblemFactCollection in `domain.py`
|
||
7. ConstraintProvider-Funktion in `constraints.py` (Hard + Soft Variante)
|
||
8. Frontend: Editor-Komponente in `_components/regeln/`, dann in
|
||
`RegelnHub.tsx` registrieren
|