Logbuch#homeos#selfhosting#mealie#essensplanung
Ein Wochenplan aus 5447 Rezepten — und Nährwerte, die ehrlich „unbekannt“ sagen dürfen
Von Niclas Hennrich · veröffentlicht · geschehen am
Selbst gebaut und in Betrieb genommen. Gehört zum Vorhaben HomeOS — Control Center mit drei Säulen.
- Rezepte im Index
- 5447
- Erster Listenlauf
- 15.3s
- 7-Tage-Plan
- 0.27s
Kurz zur Einordnung: HomeOS ist mein selbst gebautes Control Center fürs Homelab, aus dem Infrastruktur-NOC herausgewachsen, inzwischen mit drei Säulen — Infrastruktur, Haus (Smart Home) und Family (Haushalt). Dieser Eintrag spielt in der dritten.
Was ich wollte: Essensplanung, die mehr kann als „Rezept in Kalender ziehen". Regionale Supermarkt-Angebote, die Rezepte aus Mealie (unser selbst gehosteter Rezeptmanager), die Vorräte, Ernährungsziele und die Vorlieben des Haushalts sollen zusammen erklärbare Empfehlungen ergeben — und daraus einen Wochenplan mit Einkaufsliste. Mealie bleibt dabei die Rezeptquelle, HomeOS ist nur die Schicht, die orchestriert und optimiert.
Was ich gebaut habe: an einem Tag elf neue Tabellen in einer Migration — Lebensmittel mit Aliasen, Händler, Angebote samt Import-Läufen, ein Rezept-Index, Vorräte, Präferenzen, Feedback und Plan-Kandidaten. Dazu eine Provider-Abstraktion für Angebotsquellen (eine inoffizielle Marktguru-Anbindung, per Umgebungsvariable zuschaltbar, plus manuelle Erfassung als Fallback, der immer geht), eine Normalisierung, die Produktnamen über einen Alias-Katalog auf 79 kanonische Lebensmittel abbildet und unsichere Treffer in eine Review-Queue legt, ein Deal-Score, ein Planner (Greedy plus zwei Local-Search-Pässe mit Strafen für Einseitigkeit, Budget und zu viele Läden) und fünf neue Seiten im Frontend.
Das klingt nach viel Oberfläche. Die eigentlichen Entscheidungen lagen aber im Datenmodell.
Der Rezept-Index neben der Arbeitstabelle. Es gab schon eine schlanke Rezepttabelle im Haushalt — dort landet, was wirklich verplant wird. Für Empfehlungen brauche ich aber alle 5447 Mealie-Rezepte, nicht nur die bekannten. Beides in eine Tabelle zu stopfen hätte den Haushalt mit Tausenden nie gekochter Rezepte zugemüllt. Also ein eigenes Read-Model: Der Index kennt den ganzen Bestand, die Arbeitstabelle bleibt klein, und derselbe Upsert-Schlüssel verbindet beide. Nimmt man einen Plan an, entsteht der Haushaltseintrag aus einer Kopie des Index — ohne einen einzigen HTTP-Aufruf gegen Mealie. Der Wochenplan darf nicht daran hängen, ob Mealie gerade antwortet.
Der Index synchronisiert inkrementell, ohne Cursor-Buchhaltung: Mealie sortiert nach Änderungsdatum absteigend, und sobald eine Seite nur noch Bekanntes enthält, bricht der Lauf ab. Details holt ein Loop alle zehn Minuten in 200er-Häppchen nach.
Keine erfundenen Werte — als Architekturentscheidung, nicht als
Vorsatz. Jeder Nährwert trägt seine Herkunft (mealie, derived,
unknown), jeder Preis ein retrieved_at, jede Kostenschätzung eine
Quote, wie viel davon auf bekannten Preisen beruht. Beim ersten Lauf hatten
2183 von 2200 übernommenen Rezepten Nährwerte aus Mealie, 17 stehen als
unknown da. Das Datenmodell zwingt zur Ehrlichkeit, weil es die Lücke
speichern kann statt sie mit einem Schätzwert zuzukleistern.
Daraus folgt eine Regel, die ich erst beim Bauen verstanden habe: Empfehlungen ohne Daten müssen neutral sein, nicht schlecht. Fehlen Nährwerte oder Vorlieben, gibt es 0.5 und keinen Grund — sonst bestraft der Score Unwissen, und die Rangfolge lügt. Und weil jede Empfehlung ihre Gründe mitliefert, sieht man auch, warum etwas oben steht.
Was dabei schiefging: Die lokale Entwicklungsdatenbank behauptete einen Migrationsstand, dem zwei Spalten fehlten — ich hatte die Migrationsdatei nach dem Anwenden noch erweitert. Repariert habe ich gezielt die Datenbank, nicht die Migrationskette; die Autogenerierung hätte die Drift sonst gleich mit eingebaut. Und ein Test nahm an, „Speisequark Magerstufe" bliebe unzugeordnet — der Alias-Katalog traf aber korrekt. Test angepasst, nicht der Code.
Stand nach dem Deploy auf optimus: 5447 Rezepte in 15,3 s erkannt, Empfehlungen über 2200 indizierte Rezepte in etwa 0,3 s, ein 7-Tage-Plan mit sieben verschiedenen Rezepten in 0,27 s. Der Angebotsteil meldet ehrlich „PLZ nicht gesetzt" — er wartet auf zwei Handgriffe in den Einstellungen. Ob Angebote die Planung wirklich besser machen, weiß ich also noch nicht. Das ist der nächste Schritt.