386 lines
12 KiB
Markdown
386 lines
12 KiB
Markdown
# Putzliga
|
|
|
|
Putzliga ist eine moderne, leichte Haushaltsaufgaben-Web-App mit spielerischem Charakter. Version 1 setzt auf Flask, SQLite, Jinja-Templates, responsives CSS, minimales JavaScript und eine saubere PWA-/Web-Push-Basis. Die App ist für mehrere Nutzer ausgelegt, läuft lokal sehr unkompliziert und ist so vorbereitet, dass sie auf Cloudron direkt als Container-App betrieben werden kann.
|
|
|
|
## Features
|
|
|
|
- Mehrere Nutzer mit Login, Admin-Nutzermanagement und Profil-/Avatar-Einstellungen
|
|
- Trennung zwischen `TaskTemplate` und `TaskInstance`
|
|
- Aufgaben anlegen, bearbeiten, zuweisen und erledigen
|
|
- globale Schnellaufgabe per Plus-Button mit Titel + Aufwand und automatisch passender Punktezahl
|
|
- Wiederholungen für einmalig, alle X Tage, alle X Wochen und alle X Monate
|
|
- Saubere Erledigungslogik für fremd zugewiesene Aufgaben mit Auswahl, wer wirklich erledigt hat
|
|
- Statuslogik für offen, bald fällig, überfällig und erledigt
|
|
- `Meine Aufgaben`, `Alle Aufgaben`, `Aufgabe erstellen`, `Kalender/Liste`, `Highscoreboard`, `Optionen`
|
|
- Monats-Highscore mit Badge-Boni und Balkendarstellung
|
|
- Monatsarchiv über `MonthlyScoreSnapshot`
|
|
- PWA mit `manifest.json`, Service Worker, App-Icons und iOS-freundlicher Installationsbasis
|
|
- Echte Web-Push-Architektur mit gespeicherten `PushSubscription`s
|
|
- CLI-Kommandos für Archivierung und serverseitig triggerbare Benachrichtigungen
|
|
- Cloudron-/Container-tauglicher Start mit `start.sh`, `Dockerfile` und `CloudronManifest.json`
|
|
- Keine freie Registrierung nach dem ersten Nutzer; weitere Nutzer lassen sich kontrolliert per CLI anlegen
|
|
- Dauerhaft gespeicherte Badges pro Nutzer mit eigener Admin-Badge-Seite
|
|
|
|
## Projektstruktur
|
|
|
|
```text
|
|
app/
|
|
routes/
|
|
services/
|
|
static/
|
|
css/
|
|
fonts/
|
|
icons/
|
|
images/
|
|
js/
|
|
manifest.json
|
|
service-worker.js
|
|
templates/
|
|
auth/
|
|
partials/
|
|
scoreboard/
|
|
settings/
|
|
tasks/
|
|
app.py
|
|
config.py
|
|
seed.py
|
|
start.sh
|
|
Dockerfile
|
|
CloudronManifest.json
|
|
requirements.txt
|
|
.env.example
|
|
scripts/
|
|
data/
|
|
```
|
|
|
|
## Lokale Daten vs. Cloudron-Inhalte
|
|
|
|
Die App ist jetzt so vorbereitet, dass lokale Entwicklungsdaten nicht versehentlich mit nach Cloudron wandern:
|
|
|
|
- `data/` ist in `.gitignore` ausgeschlossen und wird nicht committed
|
|
- `data/` ist zusätzlich in `.dockerignore` ausgeschlossen und landet nicht im Docker-Build-Kontext
|
|
- Uploads liegen standardmäßig ebenfalls unter `data/uploads` und bleiben damit lokal bzw. im persistenten Cloudron-Storage
|
|
- Beim ersten Cloudron-Start wird keine lokale Entwicklungsdatenbank ins Image kopiert
|
|
|
|
Damit kannst du lokal mit Seed-Daten entwickeln und online unabhängig davon echte Inhalte pflegen.
|
|
|
|
## Lokales Setup
|
|
|
|
### 1. Abhängigkeiten installieren
|
|
|
|
```bash
|
|
python3 -m venv .venv
|
|
. .venv/bin/activate
|
|
pip install -r requirements.txt
|
|
```
|
|
|
|
### 2. Umgebungsvariablen setzen
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
```
|
|
|
|
Wichtige Variablen:
|
|
|
|
- `SECRET_KEY`: Flask Secret Key
|
|
- `DATA_DIR`: Persistentes Datenverzeichnis
|
|
- `DATABASE_PATH`: SQLite-Datei
|
|
- `UPLOAD_FOLDER`: Upload-Verzeichnis für Avatare
|
|
- `APP_BASE_URL`: Vollständige Basis-URL der App, wichtig für Push-Links
|
|
- `APP_TIMEZONE`: Standardmäßig `Europe/Berlin`
|
|
- `VAPID_PUBLIC_KEY` / `VAPID_PRIVATE_KEY`: Web-Push-Schlüssel
|
|
- `VAPID_CLAIMS_SUBJECT`: Kontaktadresse für VAPID
|
|
|
|
### 3. App-Icons erzeugen
|
|
|
|
Die Raster-Icons liegen als generierte Dateien im Projekt. Falls du sie neu erzeugen willst:
|
|
|
|
```bash
|
|
python scripts/generate_assets.py
|
|
```
|
|
|
|
### 4. Datenbank und Seed-Daten anlegen
|
|
|
|
```bash
|
|
flask --app app.py init-db
|
|
python seed.py
|
|
```
|
|
|
|
Demo-Logins:
|
|
|
|
- `mail@hnz.io` / `putzliga123` (Admin)
|
|
- `anna@putzliga.local` / `putzliga123`
|
|
- `ben@putzliga.local` / `putzliga123`
|
|
|
|
## Nutzer anlegen
|
|
|
|
Freie Registrierung ist deaktiviert, sobald mindestens ein Nutzer existiert.
|
|
|
|
- Wenn die Datenbank noch leer ist, darf genau der erste Nutzer über `/register` angelegt werden.
|
|
- Weitere Nutzer legst du kontrolliert per CLI an:
|
|
|
|
```bash
|
|
flask --app app.py create-user
|
|
```
|
|
|
|
Admins können Nutzer zusätzlich direkt in der App unter `Optionen -> Profil & Team` verwalten.
|
|
|
|
## Badges
|
|
|
|
Badges werden dauerhaft pro Nutzer gespeichert und automatisch freigeschaltet. Die Badge-Regeln werden für Admins auf einer eigenen Seite unter `Optionen -> Badges` gepflegt.
|
|
|
|
## Schnellaufgabe
|
|
|
|
Über den global sichtbaren Plus-Button rechts unten kannst du auf jeder eingeloggten Seite eine Schnellaufgabe anlegen.
|
|
|
|
- nur `Titel` und `Aufwand`
|
|
- die Aufgabe wird automatisch dem gerade eingeloggten Nutzer zugewiesen
|
|
- Fälligkeit ist direkt `heute`
|
|
- die Punkte hängen vom Aufwand ab
|
|
|
|
Die Aufwand-Stufen sind:
|
|
|
|
- Schnell
|
|
- Normal
|
|
- Dauert etwas
|
|
- Aufwendig
|
|
|
|
Admins können die Punkte je Aufwand unter `Optionen -> Profil & Team` anpassen.
|
|
|
|
### 5. Entwicklungsserver starten
|
|
|
|
```bash
|
|
flask --app app.py run --debug
|
|
```
|
|
|
|
oder produktionsnah:
|
|
|
|
```bash
|
|
./start.sh
|
|
```
|
|
|
|
## Seed-Daten
|
|
|
|
Die Seed-Datei erzeugt:
|
|
|
|
- 2 Beispielnutzer
|
|
- wiederkehrende und einmalige Vorlagen
|
|
- offene, bald fällige, überfällige und erledigte Aufgaben
|
|
- Punkte im aktuellen Monat
|
|
- erledigte Aufgaben aus dem Vormonat, damit das Archiv direkt sichtbar ist
|
|
- Standard-Badges für Frühstarter, Serien und Monatsmenge
|
|
|
|
## Datenmodell
|
|
|
|
Umgesetzt sind die Kernmodelle:
|
|
|
|
- `User`
|
|
- `TaskTemplate`
|
|
- `TaskInstance`
|
|
- `MonthlyScoreSnapshot`
|
|
- `PushSubscription`
|
|
- `NotificationLog`
|
|
|
|
Zusätzlich für Version 1:
|
|
|
|
- `BadgeDefinition` für pflegbare Badge-Regeln in den Optionen
|
|
|
|
Wichtig: `TaskTemplate` beschreibt die wiederverwendbare Vorlage, `TaskInstance` die konkrete Aufgabe mit Fälligkeit, Status und tatsächlicher Erledigung.
|
|
|
|
## Monatsarchivierung
|
|
|
|
Putzliga speichert keine monatlichen Punktetotale als Live-Zähler. Stattdessen wird der Monatsstand aus erledigten `TaskInstance`s berechnet. Dadurch startet jeder neue Monat automatisch bei 0, weil nur Aufgaben des aktuellen Monats zählen.
|
|
|
|
Die Archivierung funktioniert so:
|
|
|
|
- Vor Requests wird geprüft, ob bis zum Vormonat Archive fehlen
|
|
- Fehlende Monate werden als `MonthlyScoreSnapshot` erzeugt
|
|
- Archivwerte enthalten erledigte Aufgaben und Badge-Boni des jeweiligen Monats
|
|
- Frühere Monate bleiben dauerhaft sichtbar
|
|
|
|
Zusätzlicher CLI-Trigger:
|
|
|
|
```bash
|
|
flask --app app.py archive-months
|
|
```
|
|
|
|
## Push-Benachrichtigungen
|
|
|
|
Putzliga nutzt echte Web-Push-Benachrichtigungen mit Service Worker und VAPID.
|
|
|
|
### Architektur
|
|
|
|
- Browser registriert Service Worker
|
|
- Subscription wird pro Nutzer in `PushSubscription` gespeichert
|
|
- Server versendet über `pywebpush`
|
|
- Versand ist getrennt in Prüf- und Ausführungslogik
|
|
- Logs werden in `NotificationLog` dedupliziert
|
|
|
|
### Verfügbare Trigger
|
|
|
|
```bash
|
|
flask --app app.py notify-due
|
|
flask --app app.py notify-monthly-winner
|
|
```
|
|
|
|
`notify-due`:
|
|
|
|
- prüft offene Aufgaben, die heute oder morgen fällig sind
|
|
- berücksichtigt die Nutzeroption `notification_task_due_enabled`
|
|
|
|
`notify-monthly-winner`:
|
|
|
|
- sendet am 1. des Monats ab 09:00 Uhr
|
|
- verweist auf das Scoreboard/Archiv des letzten Monats
|
|
- berücksichtigt `notification_monthly_winner_enabled`
|
|
|
|
Für iPhone/iPad muss zusätzlich sichergestellt sein, dass der Server ausgehend Apple Web Push erreichen kann. Laut WebKit sollten dafür Verbindungen zu `*.push.apple.com` möglich sein.
|
|
|
|
### Produktiver Betrieb
|
|
|
|
Auf Cloudron oder einem anderen Server solltest du diese Kommandos regelmäßig per Cronjob oder Task ausführen, zum Beispiel:
|
|
|
|
```bash
|
|
flask --app /app/app.py notify-due
|
|
flask --app /app/app.py notify-monthly-winner
|
|
```
|
|
|
|
### iPhone-/iOS-Hinweis
|
|
|
|
Web-Push auf iPhone/iPad funktioniert nur in neueren iOS-/iPadOS-Versionen, wenn die Web-App über Safari zum Home-Bildschirm hinzugefügt wurde. Innerhalb eines normalen Safari-Tabs stehen Push-Berechtigungen nicht zuverlässig zur Verfügung.
|
|
|
|
## PWA
|
|
|
|
Enthalten sind:
|
|
|
|
- `app/static/manifest.json`
|
|
- `app/static/service-worker.js`
|
|
- `app/static/images/pwa-icon-192.png`
|
|
- `app/static/images/pwa-icon-512.png`
|
|
- `app/static/images/apple-touch-icon.png`
|
|
- `app/static/images/pwa-badge.png`
|
|
|
|
Der Service Worker cached die App-Shell und Assets pragmatisch für eine stabile Basis. Für Version 1 ist das bewusst schlank gehalten.
|
|
|
|
## Branding und Assets aus `heinz.marketing`
|
|
|
|
Aus `../heinz.marketing` wurden bewusst nur verwertbare Grundlagen übernommen:
|
|
|
|
- `Inter` und `Space Grotesk` aus `css/fonts/`
|
|
- ausgewählte lokale SVG-Icons aus `css/fontawesome-pro-plus-7.0.0-web/svgs-full/chisel-regular/`
|
|
|
|
Diese Assets wurden nicht unverändert als fertiges Branding übernommen. Putzliga nutzt darauf aufbauend:
|
|
|
|
- eine eigene helle, iOS-nahe Farbwelt
|
|
- ein neues App-Logo (`app/static/images/logo.svg`)
|
|
- ein eigenes Favicon (`app/static/images/favicon.svg`)
|
|
- eigene generierte PWA-Raster-Icons (`scripts/generate_assets.py`)
|
|
|
|
## Cloudron
|
|
|
|
Cloudron-Dateien im Projekt:
|
|
|
|
- `Dockerfile`
|
|
- `start.sh`
|
|
- `CloudronManifest.json`
|
|
|
|
Die Manifest- und Docker-Struktur orientiert sich an der aktuellen Cloudron-Dokumentation für Docker-/Container-Apps:
|
|
https://docs.cloudron.io/docker/
|
|
|
|
### Wichtige Punkte für Cloudron
|
|
|
|
- App hört auf `PORT` und standardmäßig auf `8000`
|
|
- `DATA_DIR` und `UPLOAD_FOLDER` sollten im persistenten Storage liegen
|
|
- SQLite-Datei liegt standardmäßig unter `/app/data/putzliga.db`
|
|
- `start.sh` initialisiert die DB und startet Gunicorn
|
|
- `APP_BASE_URL` kann auf Cloudron über `CLOUDRON_APP_ORIGIN` gesetzt oder daraus abgeleitet werden
|
|
- Lokale Testdaten aus `data/` werden weder committed noch in das Docker-Image gepackt
|
|
|
|
### Cloudron-ENV für Push
|
|
|
|
Für Cloudron brauchst du mindestens diese Variablen:
|
|
|
|
```dotenv
|
|
SECRET_KEY=hier-einen-langen-zufallswert-eintragen
|
|
APP_TIMEZONE=Europe/Berlin
|
|
VAPID_PUBLIC_KEY=dein_public_key
|
|
VAPID_PRIVATE_KEY=dein_private_key
|
|
VAPID_CLAIMS_SUBJECT=mailto:mail@deine-domain.tld
|
|
```
|
|
|
|
Hinweise:
|
|
|
|
- `APP_BASE_URL` musst du auf Cloudron normalerweise nicht setzen, weil `config.py` automatisch `CLOUDRON_APP_ORIGIN` verwendet
|
|
- `DATA_DIR`, `DATABASE_PATH` und `UPLOAD_FOLDER` können auf den Standardwerten bleiben, solange du die App normal auf Cloudron betreibst
|
|
- `VAPID_PRIVATE_KEY` darf als einzeiliger Wert mit escaped `\\n` gespeichert werden; Putzliga wandelt das beim Start automatisch zurück
|
|
|
|
Wenn du die Werte lokal in einer `.env` testen willst, kann das zum Beispiel so aussehen:
|
|
|
|
```dotenv
|
|
SECRET_KEY=lokal-irgendein-langer-zufallswert
|
|
APP_BASE_URL=http://127.0.0.1:5000
|
|
APP_TIMEZONE=Europe/Berlin
|
|
VAPID_PUBLIC_KEY=dein_public_key
|
|
VAPID_PRIVATE_KEY=dein_private_key
|
|
VAPID_CLAIMS_SUBJECT=mailto:mail@deine-domain.tld
|
|
```
|
|
|
|
### Beispielstart in Cloudron-/Container-Umgebungen
|
|
|
|
```bash
|
|
./start.sh
|
|
```
|
|
|
|
## Hilfsskripte
|
|
|
|
VAPID-Schlüssel generieren:
|
|
|
|
```bash
|
|
python scripts/generate_vapid_keys.py
|
|
```
|
|
|
|
Der ausgegebene `VAPID_PRIVATE_KEY` ist bereits `.env`-freundlich mit escaped Newlines formatiert. `config.py` wandelt `\\n` beim Start automatisch in echte Zeilenumbrüche zurück.
|
|
|
|
## Release Notes
|
|
|
|
### 0.6.0
|
|
|
|
- Persönlicher read-only ICS-Feed pro Nutzer für externe Kalender ergänzt
|
|
- Export enthält nur eigene zugewiesene Aufgaben mit Titel, Beschreibung und Fälligkeitsdatum
|
|
- Kalender-Link in den Optionen sichtbar gemacht und Token-Neugenerierung ergänzt
|
|
- Cloudron-Version auf `0.6.0` angehoben
|
|
|
|
### 0.5.1
|
|
|
|
- Footer mit automatischer Versionsanzeige und Links zu Releases und hnz.io
|
|
- Login-Seite bereinigt und Demo-Login-Hinweis entfernt
|
|
- Mobile Abstände rund um die feste Bottom-Navigation weiter nachgeschärft
|
|
- Cloudron-Version auf `0.5.1` angehoben
|
|
|
|
### 0.5.0
|
|
|
|
- Mehrnutzer-Haushalts-App mit Login, Admin-Nutzermanagement und deaktivierter offener Registrierung
|
|
- Aufgaben mit sauberer Trennung aus `TaskTemplate` und `TaskInstance`
|
|
- Wiederkehrende Aufgaben für Tage, Wochen und Monate
|
|
- Monats-Highscore mit Badge-Boni, Balkenanzeige und Monatsarchiv
|
|
- Persistentes Badge-System mit eigener Admin-Seite
|
|
- Schnellaufgabe per globalem Plus-Button mit admin-konfigurierbarem Aufwand
|
|
- Kalender- und Listenansicht, inklusive mobiler Agenda-Ansicht
|
|
- Systemabhängiges Light-/Dark-Design
|
|
- PWA-Grundlage mit Manifest, Service Worker und App-Icons
|
|
- Echte Web-Push-Architektur mit VAPID und Cloudron-tauglicher ENV-Konfiguration
|
|
- Cloudron-kompatibler Start mit persistentem Storage, ohne lokale Entwicklungsdaten ins Deployment zu übernehmen
|
|
|
|
Icons neu generieren:
|
|
|
|
```bash
|
|
python scripts/generate_assets.py
|
|
```
|
|
|
|
## Hinweise für spätere Erweiterungen
|
|
|
|
- Maluslogik für verspätete Erledigungen kann an `compute_monthly_scores()` und `TaskInstance` ergänzt werden
|
|
- echte Admin-/Rollenrechte können ergänzt werden, aktuell dürfen bewusst alle Nutzer Aufgaben pflegen
|
|
- Scheduler kann auf Cloudron später als separater Task sauber ausgelagert werden
|
|
- Badge-Awards könnten in einer eigenen Tabelle historisiert werden, falls spätere Regeln rückwirkungsfrei versioniert werden sollen
|