commit 3d7787a3a4cde88d7d6e98fb1f6e6c6776cd44cc Author: Herbert Püttmann Date: Sun Jul 5 11:00:40 2026 +0200 Projektgeruest: Doku, Abhaengigkeiten, gitignore Co-Authored-By: Claude Fable 5 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b6cf082 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +venv/ +__pycache__/ +*.pyc +.env +data/ +public/ diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..2671748 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,72 @@ +# QR-Tabellen (table.lehrstun.de) + +## Status +**Phase:** MVP fertig — lokal verifiziert, Deployment auf Hetzner in Arbeit +**Stand:** 2026-07-05 + +## Zweck +Kleines selbst gehostetes Tool für den Schuleinsatz: CSV-Tabellen (Stationenlernen, +Materiallisten, Linksammlungen) werden hochgeladen und als hübsche statische +HTML-Seite veröffentlicht; Link-Spalten bekommen automatisch lokal erzeugte +QR-Codes (kein externer API-Call, DSGVO). Dateneingabe bleibt bewusst in +Excel/LibreOffice — Austausch ausschließlich über CSV. + +## Quickstart +```bash +cd D:\Claude-Projekte\qr-tabellen +venv\Scripts\python -m flask --app app.main run # Admin: http://localhost:5000/admin/ +venv\Scripts\python tests\test_engine.py # Engine-Selbsttest +``` + +## Architektur +- **Python 3.13 + Flask + segno** — einzige Abhängigkeiten (Jinja2 kommt mit Flask) +- **Kein JS-Framework, keine DB** — Ablage im Dateisystem + +``` +app/ + engine.py — Kern: CSV parsen/validieren, QR-SVGs (segno), HTML rendern (framework-frei) + storage.py — Dateiablage: Schemas, CSV-Versionen, Veröffentlichung + main.py — Flask-Admin: Schema-Designer, Dropzone, Linkliste, Versionen + templates/ — table.html (öffentliche Seite) + admin/* + static/ — table.css (Design-Tokens, wird in Seiten eingebettet) + admin.css +data/tables//schema.json + versions/.csv (gitignored) +public//index.html (gitignored) +tests/test_engine.py — Selbsttest ohne pytest +``` + +### Datenfluss +1. Schema online anlegen (Spalten: text, text_long, number, date, url, rating_5) +2. CSV-Vorlage herunterladen (Semikolon + UTF-8-BOM → Excel-tauglich) +3. Befüllte CSV in die Dropzone → Zuordnung per Dateiname, sonst Header-Signatur +4. Validierung (URLs, Bewertung 1-5, Zahlen); bei Fehlern wird nichts gespeichert +5. Version abgelegt + in sich geschlossene HTML-Seite nach `public//` gerendert + +### Produktion (Ziel) +- Öffentliche Seiten: Nginx liefert `public/` statisch aus (App-Ausfall egal) +- Admin-Panel: Flask via gunicorn + systemd, hinter Nginx Basic Auth unter `/admin` +- Env: `QT_BASE_URL=https://table.lehrstun.de`, `QT_DATA_DIR`, `QT_PUBLIC_DIR` + +## Design +Tokens in `table.css` (`:root`-Variablen: Farben, Fonts, QR-Größe, Breite). +Systemfont-Stack mit Source Serif 4 / Fira Sans als Wunschfonts — bei Bedarf +lokal hosten (kein Google-CDN, Schulkontext). Print-Media-Query vorhanden. + +## Entscheidungen +| Datum | Entscheidung | Begründung | +|-------|-------------|------------| +| 2026-07-05 | Python/Flask + segno statt Node | Passt zum Hetzner-Deploy-Muster; csv ist Stdlib, segno erzeugt SVG-QR ohne weitere Deps | +| 2026-07-05 | Nur CSV im MVP, kein XLSX | Excel/LO speichern CSV; spart exceljs/openpyxl komplett | +| 2026-07-05 | Öffentliche Seiten statisch, App nur fürs Admin | Schüler-Traffic trifft nie den Python-Prozess; robust bei App-Ausfall | +| 2026-07-05 | QR-Codes als Inline-SVG in der Seite | Eine Datei pro Tabelle, keine Bild-Assets, skaliert per CSS | +| 2026-07-05 | Versions-Erkennung: Dateiname → Header-Signatur → manuell | Kein Ratespiel; Vorlage heißt schon `.csv` | +| 2026-07-05 | Kein Login in der App; Basic Auth via Nginx | Single-Admin, unveröffentlichte Slugs reichen für Schüler | +| 2026-07-05 | CSV-Vorlage mit Semikolon + UTF-8-BOM | Deutsches Excel öffnet sie direkt korrekt | +| 2026-07-05 | Fehlerhafte CSVs werden komplett abgelehnt | Klare Fehlermeldung statt halb veröffentlichter Tabelle | + +## Blocker +- Keine + +## Nächste Schritte +- [ ] Deployment: DNS `table.lehrstun.de`, Gitea-Repo, gunicorn + systemd, Nginx (statisch + /admin Basic Auth), TLS +- [ ] Design-Feintuning (Tokens in table.css), ggf. Fonts lokal hosten +- [ ] Später: XLSX-Direkt-Upload, QR-Label-Styling, XLSX-Re-Export mit QR-Bildern diff --git a/docs/decisions.md b/docs/decisions.md new file mode 100644 index 0000000..09b5faa --- /dev/null +++ b/docs/decisions.md @@ -0,0 +1,26 @@ +# Entscheidungs- und Änderungslog + +## 2026-07-05 — Projektstart (MVP) +- **Stack:** Python/Flask + segno statt Node (Briefing-Vorschlag). Begründung: + Hetzner-Deploy-Muster ist Python + systemd; `csv` ist Stdlib, segno rendert + QR als SVG ohne weitere Abhängigkeiten. Herbs Entscheidung nach Vorlage. +- **Scope-Kürzungen gegenüber Briefing:** kein XLSX-Upload (CSV reicht, Excel/LO + können das), Schema-Designer minimal (Name + Typ), keine Süßholz-Features. +- **Architektur-Trennung:** öffentliche Seiten sind beim Upload fertig gerendertes, + in sich geschlossenes HTML (QR inline als SVG) → Nginx statisch; der + Flask-Prozess existiert nur fürs Admin-Panel (Dropzone, Schema-Designer, + Linkliste, Versionen) hinter Nginx Basic Auth. +- **Herbs Erweiterung zum Briefing:** Admin-Panel mit Dropzone, automatischer + Versions-Zuordnung (Dateiname → Header-Signatur → manuelle Wahl), + CSV-Versionen jederzeit downloadbar, Linkliste mit QR pro Tabelle. + Das beantwortet die offene Briefing-Frage: Upload läuft auf dem Server, + nicht als lokales CLI. +- **Validierung:** fehlerhafte CSVs werden komplett abgelehnt (Zeile/Spalte in + der Fehlermeldung), nichts wird halb veröffentlicht. +- **Encoding-Realität:** Vorlagen mit Semikolon + UTF-8-BOM (deutsches Excel); + Upload-Decoder probiert utf-8-sig, dann cp1252; Delimiter wird gesnifft. +- **Design:** Tokens als CSS-Variablen in `table.css`; Systemfont-Fallbacks für + Source Serif 4 / Fira Sans, bewusst kein Google-CDN (Schulkontext). Vertiefung + später. +- **Global:** Docker-Default aus den Infrastruktur-Regeln gestrichen — Docker + nur noch als Vorschlag für Projekte mit eigenem Ökosystem. diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..11a572e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +flask +segno +gunicorn ; sys_platform != "win32"