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>
3.5 KiB
3.5 KiB
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 gueltighard_score < 0→ mindestens eine harte Regel ist verletzt (Solver meldet das alsinfeasible)soft_score→ wird in den UI angezeigt; je naeher an 0, desto besser
Erweitern um einen 16. Constraint-Typ
- Neue Tabelle in
school-service/internal/database/timetable_constraints_migrations.go - Model + DTO in
models/timetable_constraints.go - Service + Handler im gleichen Paket-Pattern wie die existierenden 15
- Route in
cmd/server/main.go - Rule-Dataclass in
timetable-solver-service/app/rules.py - ProblemFactCollection in
domain.py - ConstraintProvider-Funktion in
constraints.py(Hard + Soft Variante) - Frontend: Editor-Komponente in
_components/regeln/, dann inRegelnHub.tsxregistrieren