Stapel-Import und automatischer Titel
Beim Erstbestand sind viele Teile auf einmal zu erfassen. Der Aufwand
steckt nicht im Fotografieren, sondern im Rundlauf pro Stück. Darum:
- Stapel-Import: erst alles mit der Kamera-App fotografieren, dann alle
Fotos auf einmal hochladen. Pro Foto entsteht ein Entwurf, die Details
kommen später. Bewusst ohne capture="environment" - das erzwingt ein
Foto pro Vorgang und schliesst multiple aus.
- Titel wird optional und sonst aus Kategorie und Grösse gebildet
("Hose 98/104"). Erzeugt beim Anzeigen, nicht beim Speichern, damit er
einer späteren Korrektur der Grösse folgt.
Daraus folgt ein neuer Status "draft" im Datenmodell; title und size
müssen NULL erlauben. Entwürfe erscheinen nicht in der Galerie.
Ausserdem HEIC in Phase 2 aufgenommen: iPhones nehmen so auf, und beim
Stapel-Import fiele ein nicht lesbares Bild sonst still hinten runter.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GR4bNaj9GtRu57J4Niii8o
This commit is contained in:
@@ -76,7 +76,7 @@ getarntes Skript ebenfalls.
|
|||||||
|
|
||||||
## Datenmodell – Abweichungen von `projekt.md`
|
## Datenmodell – Abweichungen von `projekt.md`
|
||||||
|
|
||||||
Das Schema wird grundsätzlich übernommen. Vier Ergänzungen:
|
Das Schema wird grundsätzlich übernommen. Fünf Ergänzungen:
|
||||||
|
|
||||||
1. **Feste Wertelisten statt freier VARCHAR.** `gender`, `season`, `condition`
|
1. **Feste Wertelisten statt freier VARCHAR.** `gender`, `season`, `condition`
|
||||||
und `status` bekommen ein Python-Enum plus `CHECK`-Bedingung in der
|
und `status` bekommen ein Python-Enum plus `CHECK`-Bedingung in der
|
||||||
@@ -97,12 +97,65 @@ Das Schema wird grundsätzlich übernommen. Vier Ergänzungen:
|
|||||||
4. **`updated_at`** wird in der Anwendung gesetzt, nicht per DB-Trigger –
|
4. **`updated_at`** wird in der Anwendung gesetzt, nicht per DB-Trigger –
|
||||||
bleibt so unabhängig von SQLite-Eigenheiten.
|
bleibt so unabhängig von SQLite-Eigenheiten.
|
||||||
|
|
||||||
|
5. **Status `draft` (neu).** `projekt.md` kennt nur `available`, `reserved`
|
||||||
|
und `given_away`. Der Stapel-Import (siehe unten) legt Einträge an, die
|
||||||
|
noch keinen Titel und keine Grösse haben – die dürfen nicht in der
|
||||||
|
öffentlichen Galerie erscheinen. `title` und `size` müssen dafür
|
||||||
|
`NULL` erlauben; gefüllt sein müssen sie erst beim Wechsel auf
|
||||||
|
`available`.
|
||||||
|
|
||||||
Ausserdem: beim Löschen eines Eintrags müssen die **Bilddateien mitgelöscht**
|
Ausserdem: beim Löschen eines Eintrags müssen die **Bilddateien mitgelöscht**
|
||||||
werden, sonst wächst das Upload-Verzeichnis unbegrenzt mit verwaisten Dateien.
|
werden, sonst wächst das Upload-Verzeichnis unbegrenzt mit verwaisten Dateien.
|
||||||
`ON DELETE CASCADE` räumt nur die Datenbankzeilen weg, nicht die Dateien.
|
`ON DELETE CASCADE` räumt nur die Datenbankzeilen weg, nicht die Dateien.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Schnelles Erfassen
|
||||||
|
|
||||||
|
Beim Erstbestand sind auf einen Schlag viele Teile zu erfassen. Der Aufwand
|
||||||
|
steckt dabei nicht im Fotografieren, sondern im Rundlauf pro Stück: Formular
|
||||||
|
öffnen, fotografieren, Felder ausfüllen, speichern, von vorn. Zwei Massnahmen
|
||||||
|
dagegen.
|
||||||
|
|
||||||
|
### Stapel-Import
|
||||||
|
|
||||||
|
Zuerst wird alles mit der gewohnten Kamera-App fotografiert – die ist fürs
|
||||||
|
schnelle Knipsen gebaut, ohne Umweg über den Browser. Danach **einmal** ins
|
||||||
|
Formular, alle Fotos auf einmal auswählen:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<input type="file" name="fotos" accept="image/*" multiple>
|
||||||
|
```
|
||||||
|
|
||||||
|
Pro Foto entsteht ein Eintrag mit Status `draft`. Die Details werden später
|
||||||
|
nachgetragen – in Ruhe, auch am Rechner mit richtiger Tastatur. Damit ist
|
||||||
|
Fotografieren vom Erfassen entkoppelt.
|
||||||
|
|
||||||
|
Bewusst **ohne** `capture="environment"`: das springt zwar direkt in die
|
||||||
|
Rückkamera, erzwingt aber ein Foto pro Vorgang und schliesst `multiple` aus.
|
||||||
|
Für einzelne Nachträge im Alltag ist es passend – dann als zweiter, kleiner
|
||||||
|
Knopf „Direkt fotografieren" neben dem regulären Feld.
|
||||||
|
|
||||||
|
Nötig dafür:
|
||||||
|
- Ansicht „Unfertige Einträge (12)", nur für angemeldete Benutzer
|
||||||
|
- Entwürfe erscheinen nicht in der Galerie und nicht in `GET /api/v1/items`,
|
||||||
|
solange dort nicht ausdrücklich `status=draft` angefragt wird
|
||||||
|
- Ein Entwurf wird erst zu `available`, wenn Grösse und Kategorie gesetzt sind
|
||||||
|
|
||||||
|
### Titel ist freiwillig
|
||||||
|
|
||||||
|
`title` wird optional. Bleibt das Feld leer, setzt die Anwendung ihn aus
|
||||||
|
Kategorie und Grösse zusammen – „Hose 98/104", „Winterjacke 116". Das ist
|
||||||
|
genau die Angabe, nach der man in einer Galerie ohnehin sucht, und spart beim
|
||||||
|
Erfassen das mit Abstand lästigste Feld, weil es als einziges freien Text
|
||||||
|
verlangt.
|
||||||
|
|
||||||
|
Erzeugt wird der Titel **beim Anzeigen**, nicht beim Speichern: sonst bliebe
|
||||||
|
ein automatisch gesetzter Titel stehen, wenn später die Grösse korrigiert
|
||||||
|
wird. In der Datenbank bleibt `title` dann schlicht `NULL`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Absicherung
|
## Absicherung
|
||||||
|
|
||||||
Die App ist aus dem Internet erreichbar, also gelten hier andere Massstäbe als
|
Die App ist aus dem Internet erreichbar, also gelten hier andere Massstäbe als
|
||||||
@@ -181,9 +234,15 @@ Die Phasen aus `projekt.md`, ergänzt um Tests und die Absicherung.
|
|||||||
**Phase 2 – Bilder**
|
**Phase 2 – Bilder**
|
||||||
- Upload per `multipart/form-data`, mehrere Bilder pro Eintrag
|
- Upload per `multipart/form-data`, mehrere Bilder pro Eintrag
|
||||||
- Prüfung und WebP-Konvertierung (max. 1200 px Breite) nach Abschnitt 1 oben
|
- Prüfung und WebP-Konvertierung (max. 1200 px Breite) nach Abschnitt 1 oben
|
||||||
|
- **HEIC unterstützen** (`pillow-heif`): iPhones nehmen in diesem Format auf.
|
||||||
|
Safari wandelt beim Hochladen über ein Datei-Feld meist selbst nach JPEG
|
||||||
|
um – aber nicht zuverlässig, und beim Stapel-Import fällt ein einzelnes
|
||||||
|
nicht lesbares Bild sonst still hinten runter. Pillow allein kann HEIC
|
||||||
|
nicht öffnen.
|
||||||
- Ausliefer-Route, `is_primary`-Logik, Aufräumen beim Löschen
|
- Ausliefer-Route, `is_primary`-Logik, Aufräumen beim Löschen
|
||||||
- Prüfpunkt: bewusst fehlerhafte Uploads (zu gross, falscher Typ, riesige
|
- Prüfpunkt: bewusst fehlerhafte Uploads (zu gross, falscher Typ, riesige
|
||||||
Pixelmasse, `.php` als `.jpg` getarnt) werden alle abgewiesen
|
Pixelmasse, `.php` als `.jpg` getarnt) werden alle abgewiesen; ein
|
||||||
|
HEIC-Bild wird angenommen
|
||||||
|
|
||||||
**Phase 3 – Absicherung**
|
**Phase 3 – Absicherung**
|
||||||
- Anmeldung, Sitzung, CSRF, Rate-Limits, Sicherheits-Header
|
- Anmeldung, Sitzung, CSRF, Rate-Limits, Sicherheits-Header
|
||||||
@@ -192,10 +251,12 @@ Die Phasen aus `projekt.md`, ergänzt um Tests und die Absicherung.
|
|||||||
|
|
||||||
**Phase 4 – Oberfläche**
|
**Phase 4 – Oberfläche**
|
||||||
- Mobile-First-Layout, Galerie mit Filterleiste (HTMX)
|
- Mobile-First-Layout, Galerie mit Filterleiste (HTMX)
|
||||||
- Detailseite, Erfassungsformular mit Kamera-Aufnahme
|
- Detailseite und Erfassungsformular
|
||||||
(`<input type="file" accept="image/*" capture="environment">`)
|
- **Stapel-Import** mit Entwurfsliste zum Nachtragen (siehe „Schnelles
|
||||||
|
Erfassen"), dazu der automatische Titel aus Kategorie und Grösse
|
||||||
- Reservierungs-Ablauf inklusive Selbst-Freigabe
|
- Reservierungs-Ablauf inklusive Selbst-Freigabe
|
||||||
- Prüfpunkt: der ganze Weg auf einem echten Smartphone
|
- Prüfpunkt: der ganze Weg auf einem echten Smartphone – 20 Fotos in einem
|
||||||
|
Rutsch hochladen, danach die Entwürfe abarbeiten
|
||||||
|
|
||||||
**Phase 5 – Betrieb**
|
**Phase 5 – Betrieb**
|
||||||
- `Dockerfile` (Tailwind-Build inbegriffen), `docker-compose.yml`, Volumes
|
- `Dockerfile` (Tailwind-Build inbegriffen), `docker-compose.yml`, Volumes
|
||||||
|
|||||||
Reference in New Issue
Block a user