# SpaFu02 – Entwicklungsvorgaben tags: [sparfuchs, spafu02, entwicklung, planung] erstellt: 2026-06-13 aktualisiert: 2026-06-15 status: in Entwicklung --- ## Überblick SpaFu02 ist die Nachfolgeversion von Sparfuchs Koblenz (SpaFu01). Ziel ist eine quellenunabhängige, öffentlich betriebene Preisvergleichs-Anwendung auf Basis von Discounter-Prospekten. --- ## Motivation Die bisherige Datenquelle (marktguru API) ist nur für den privaten Gebrauch lizenziert und für einen öffentlichen Betrieb nicht geeignet. SpaFu02 ersetzt die API durch automatisierte Verarbeitung von Discounter-Newslettern/Prospekten. --- ## Systemarchitektur ``` ┌─────────────────────────────┐ │ System "Gen" (lokal) │ │ - IMAP Newsletter lesen │ │ - PDF herunterladen │ │ - PyMuPDF + Claude API │ │ - SQLite schreiben │ └────────────┬────────────────┘ │ Sync bei Bedarf (Strategie offen) ┌────────────▼────────────────┐ │ System "Web" (Hetzner VPS) │ │ ⚠️ ausgeschaltet bis │ │ Anwendung fehlerfrei │ └─────────────────────────────┘ ``` ### System "Gen" (lokal) – LXC 106 - IP: 192.168.2.181, Port 8765 - Proxmox Host: [email protected], SSH-Key: C:\Users\Joachim2026\.ssh\id_ed25519_proxmox - Pfad: /opt/preisdb/ - DB: /opt/preisdb/data/preisdb.sqlite (Schema v6) - PyMuPDF nur im venv → immer /opt/preisdb/venv/bin/python3 ### System "Web" (Hetzner VPS) – ⚠️ ausgeschaltet - Host: [email protected], SSH-Key: ~/.ssh/id_ed25519 - Domain: sparfuchs-koblenz.com (Cloudflare Proxy, Full strict) - Stack: Ubuntu 26.04, Python 3.14, Caddy v2.11.4, FastAPI Port 8000 --- ## Deploy-Workflow (lokal → VPS) – zurückgestellt ```powershell cat "C:\MoniBase\Anwendungen\Schnäppchen Jäger\main.py" | ssh -i ~/.ssh/id_ed25519 [email protected] "cat > /opt/preisdb/app/main.py" ssh -i ~/.ssh/id_ed25519 [email protected] "sudo systemctl restart preisdb-api" ``` --- ## Dateiversionen (Stand 2026-06-14) | Datei | Version | Status | |-------|---------|--------| | `main.py` | v2.4 – PDF-Download-Endpoint | LXC ✅, VPS ⏳, commit eea4867 | | `models.py` | NEU – Pydantic-Klassen ausgelagert | LXC ✅, VPS ⏳ | | `index.html` | v2.8 – Download-Button Original-PDF | LXC ✅, VPS ⏳, commit eea4867 | | `quality_check.py` | v1.0 – Schritt 6 QS | LXC ✅, commit eea4867 | | `offer_extractor.py` | v1.4 – Claude Vision + API-Log | LXC ✅, commit 3a07b0e | | `page_processor.py` | v1.2 – DB-Write + Prospekt-Status | LXC ✅, commit c6bf442 | | `pdf_extractor.py` | v2.0 – newsletter/link/mail_render | LXC ✅, committed | | `mail_classifier.py` | v1.0 – Claude Haiku | LXC ✅, committed | | `imap_reader.py` | v1.0 | LXC ✅, committed | | `importer.py` | v1.4 | ✅ | | `run_import.py` | v1.5 | ✅ | --- ## Datensynchronisation Gen → Web - **Erster Schritt:** komplette DB-Übertragung - **Später:** nur geänderte Datensätze (Entscheidung offen) - **Transfermethode:** offen (scp/rsync, Script, REST-API) --- ## Datenquelle: Discounter-Newsletter - Postfach: `[email protected]` - IMAP: `imap.secureserver.net:993` (SSL/TLS) ### Ziel-Händler (Koblenz) - Aldi Nord / Aldi Süd - Lidl - Penny - Netto --- ## Verarbeitungslogik (Pipeline) ### Schritt 1: IMAP-Abruf (`imap_reader.py`) ✅ - Alle Mails abrufen (ALL), nach Download löschen - `.eml`-Datei speichern unter `/opt/preisdb/data/mails/` ### Schritt 2: Händler-Erkennung (`mail_classifier.py`) ✅ - Claude Haiku (claude-haiku-4-5-20251001) analysiert `.eml` - `sicher=false` → Warteschlange für manuelle Zuordnung im Admin ### Schritt 3: PDF-Extraktion (`pdf_extractor.py`) ✅ - Priorität: PDF-Anhang → Link-Download → HTML-Rendering (wkhtmltopdf) - Speichern: `/opt/preisdb/data/prospekte/haendler_plz_datum.pdf` - PDFs werden als **eine Datei pro Prospekt** gespeichert – keine Seiten-Dateien - Prospekt-Kopfsatz in DB anlegen ### Schritt 4: Seitenverarbeitung (`page_processor.py` v1.2) ✅ - PyMuPDF im venv: `/opt/preisdb/venv/bin/python3` - Seiten werden direkt aus Gesamt-PDF im RAM gerendert (`doc[seite_nr - 1].get_pixmap()`) - Testlauf ALDI SÜD Prospekt 4: 7 Seiten verarbeitet ✅ ### Schritt 5: Angebots-Extraktion (`offer_extractor.py` v1.4) ✅ - Claude Haiku Vision: PDF-Seite als PNG → JSON-Angebote - Ergebnis Prospekt 4: 9 Angebote aus 5 von 7 Seiten (`qs_status='neu'`) - API-Log: `/opt/preisdb/logs/claude_api.log` ### Schritt 6: QS-Verfahren (`quality_check.py` v1.0) ✅ - Höchste `cc_guete` gewinnt → `qs_status='freigegeben'` - Aufruf: `python3 quality_check.py [prospekt_id]` --- ## Reset-Script (`reset_testdaten.py`) – geplant Setzt die Testumgebung vollständig zurück für wiederholbare Tests. **Dateisystem:** - `/opt/preisdb/data/mails/*.eml` – alle löschen - `/opt/preisdb/data/prospekte/*.pdf` – alle löschen **Datenbank (Reihenfolge wegen Foreign Keys):** ```sql DELETE FROM Angebot; DELETE FROM Preishistorie; DELETE FROM SeitenErgebnis; DELETE FROM ProspektSeite; DELETE FROM Prospekt; DELETE FROM Produkt; DELETE FROM Kategorie; DELETE FROM Marke; DELETE FROM Haendler; ``` **Nicht löschen:** ErkennungsSystem, SparfuchsGruppe, SparfuchsKategorie, Lebensmittelgruppe, Suchbegriff **Aufruf:** `/opt/preisdb/venv/bin/python3 /opt/preisdb/app/reset_testdaten.py` --- ## QS-Oberfläche (Admin-Tab "QS / Prospekte") ✅ **FastAPI-Endpunkte (JWT-geschützt):** - `GET /admin/prospekte` – Liste aller Prospekte - `GET /admin/prospekte/{id}` – Kopfdaten + Seiten - `GET /admin/prospekte/{id}/seiten/{seite_nr}` – Erkennungsergebnisse + Angebote - `GET /admin/prospekte/{id}/seiten/{seite_nr}/bild` – PDF-Seite als PNG - `GET /admin/prospekte/{id}/pdf` – Original-PDF-Download --- ## PDF-Verarbeitung: Erkennungssysteme (nach Priorität) | Priorität | System | Typ | Beschreibung | |-----------|--------|-----|--------------| | 1 | `pymupdf` | lokal | PyMuPDF – Standard, 96% Qualität ✅ in DB | | 2 | `pdfplumber` | lokal | Tabellenerkennung – Fallback ✅ in DB | | 3 | `claude_api` | Cloud | Vision API – Bild-PDFs ✅ in DB | | 4 | `mistral_ocr` | Cloud | Mistral OCR ✅ in DB | | 5 | `google_dai` | Cloud | Google Cloud Document AI ✅ in DB | | 6 | `azure_di` | Cloud | Azure AI Document Intelligence ✅ in DB | | 7 | `nutrient_ocr` | Cloud | Nutrient OCR (ehem. PSPDFKit) ✅ in DB | | 8 | `docupipe` | Cloud | DocuPipe ✅ in DB | --- ## Datenbankschema: schema_v6.sql ``` Prospekt (Kopfsatz, PLZ) └── ProspektSeite (je Seite) └── SeitenErgebnis ←── ErkennungsSystem ↓ Angebot (qs_status) ←── Produkt, Haendler ``` --- ## Gesamtplan ### Sofort (Claude Code) - [ ] `reset_testdaten.py` erstellen und testen - [ ] `quality_check.py` auf Prospekt 4 ausführen (9 Angebote → freigeben) - [ ] Prospekte 1 + 3 klären (status='fehler', 0 Seiten) ### Testrunde - [ ] Reset → Newsletter einspielen → komplette Pipeline testen - [ ] QS-Oberfläche im Admin prüfen ### Wenn Pipeline fehlerfrei - [ ] Weitere Händler-Newsletter abonnieren (Lidl, Penny, Netto) - [ ] VPS einschalten + Deploy (erst fragen!) - [ ] Sync Gen→Web einrichten ### Features (parallel) - [ ] Admin: manueller PDF-Upload - [ ] Button-Text "Verwaltung" → "Gruppen anpassen" (`sfBtn-verwaltung` in index.html:427) - [ ] Einkaufsliste nach LG sortieren beim Drucken - [ ] index.html aufteilen: styles.css + app.js - [ ] Standorte via Overpass/OSM - [ ] sendmail auf VPS einrichten --- ## Dateien - Schema: `C:\MoniBase\Anwendungen\Sparfuchs-Koblenz\schema_v6.sql` - Migration: `C:\MoniBase\Anwendungen\Sparfuchs-Koblenz\migrate_v5_to_v6.sql` - Lokale App-Dateien: `C:\MoniBase\Anwendungen\Schnäppchen Jäger\` - OCR-Bewertung: `C:\MoniBase\Anwendungen\Sparfuchs-Koblenz\SpaFu02-Bewertung-OCR.md` - Design PDF-Extraktion: `C:\MoniBase\Anwendungen\Sparfuchs-Koblenz\Design – PDF-Extraktion.md` - LXC-Setup: `C:\MoniBase\Anwendungen\Sparfuchs-Koblenz\SpaFu02-LXC-Setup.md` - VPS-Setup: `C:\MoniBase\Anwendungen\Sparfuchs-Koblenz\04 Sparfuchs-VPS-Setup bei Hetchner.md` - Claude Code Memory: `C:\Users\Joachim2026\.claude\projects\C--Users-Joachim2026\memory\project_preisdb_koblenz.md`