No description
- Python 76.1%
- HTML 19.1%
- CSS 4.6%
- Dockerfile 0.1%
| static | ||
| templates | ||
| tests | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| .secret_key | ||
| agent.py | ||
| app.py | ||
| camt_parser.py | ||
| docker-compose.yml | ||
| Dockerfile | ||
| entrypoint.sh | ||
| helpers.py | ||
| parsers.py | ||
| pytest.ini | ||
| README.md | ||
| requirements-dev.txt | ||
| requirements.txt | ||
| storage.py | ||
Ausgaben-Tracker
KI-gestützter Ausgaben-Tracker für Schweizer Bankauszüge im CAMT-Format (.053/.054). Transaktionen werden per lokalem LLM (Ollama) automatisch kategorisiert.
Features
- Multi-Format-Upload – CAMT-XML (.053/.054), CSV oder XLSX. Spalten werden automatisch erkannt (deutsche + englische Header). Falls nicht möglich, Mapping-UI zum manuellen Zuordnen
- KI-Kategorisierung – Lokales LLM via Ollama klassifiziert jede Transaktion (Essen, ÖV, Miete, …)
- Counterparty-Cache – Wiederkehrende Gegenparteien (z.B. SBB, Coop) werden beim zweiten Mal aus dem Cache kategorisiert, ohne LLM-Call
- Vorschläge verwalten – Neue Kategorien vom LLM können akzeptiert, verworfen oder in eine bestehende Kategorie zusammengeführt werden. Bulk-Merge pro Ziel-Kategorie für mehrere Vorschläge gleichzeitig
- Manuelles Editieren – Kategorien jederzeit per Dropdown korrigierbar (Cache wird mit-aktualisiert)
- Dashboard – Ausgaben pro Kategorie und Monat als Balkendiagramm (Chart.js)
- CSV-Export – Alle Transaktionen als CSV herunterladen
- Konfigurierbar – Kategorien, Ollama-Server & Modell über Web-UI editierbar
- Ollama-Verbindungstest – Button in der Config prüft ob der Server erreichbar und das Modell installiert ist
- Overlap-Warnung – Beim Upload wird gewarnt wenn der Zeitraum bereits Transaktionen enthält
- Multi-Währungs-Warnung – Banner wenn Daten verschiedene Währungen enthalten
- Docker – Fertiges Docker-Setup mit
docker compose - Tests – 220 pytest-Tests (Parser, Storage-Concurrency, Agent, Routes, Merge, Suche, Bulk-Edit, Farben, Löschen, DuckDuckGo-Anreicherung)
- KI-Internet-Anreicherung – DuckDuckGo (Instant Answer API) liefert Wikipedia-Beschreibungen zu jeder Gegenpartei (z.B. "Migros" → "Schweizer Detailhandelskette"). Beschreibungen landen im LLM-Prompt für bessere Kategorisierung. Cache in SQLite, kein Ablauf, Rate-Limit 1.2s/Call. Privacy-Hinweis in der Config
Voraussetzungen
- Ollama läuft auf einem Server (separater LXC oder gleicher Host)
- Docker für den App-Container
- Ein Ollama-Modell (z.B.
llama3.2,qwen2.5,mistral)
Quickstart
# 1. Projekt klonen
git clone <repo-url> Ausgaben
cd Ausgaben
# 2. .env erstellen & anpassen
cp .env.example .env
# Bearbeite Ollama-URL und Modell:
# OLLAMA_URL=http://<deine-ollama-ip>:11434
# OLLAMA_MODEL=llama3.2
# 3. Starten
docker compose up -d --build
# 4. Öffnen
# http://<server-ip>:5000
Konfiguration
.env (Docker)
| Variable | Default | Beschreibung |
|---|---|---|
PORT |
5000 |
Port, auf dem die App läuft |
OLLAMA_URL |
http://localhost:11434 |
Ollama-Server-Adresse |
OLLAMA_MODEL |
llama3.2 |
Zu nutzendes Ollama-Modell |
BATCH_SIZE |
5 |
Transaktionen pro LLM-Aufruf (Cache-Hits werden separat gezählt) |
Datenbank
Alle Daten (Transaktionen, Kategorien, Vorschläge, Config, Counterparty-Cache) liegen in einer SQLite-DB unter DATA_DIR/ausgaben.db. Im Docker-Container ist das /data/ausgaben.db.
Schema:
categories(id, name UNIQUE)transactions(id, date, amount, currency, counterparty, counterparty_iban, reference, type, category_id, category_confidence, category_is_new, updated_at)suggestions(name, count, examples_json)config(key, value)counterparty_cache(counterparty, category_id, confidence, updated_at)
WAL-Modus aktiv → konfliktfreie parallele Reads, atomare Writes. Bei docker compose down bleiben die Daten erhalten. Zurücksetzen: docker compose down -v (löscht das Volume).
Tests
pip install -r requirements-dev.txt
pytest
Workflow
- Upload – CAMT-XML, CSV oder XLSX per Drag & Drop auf die Startseite hochladen
- Parsen – Die App extrahiert automatisch alle Transaktionen
- Bei CSV/XLSX mit unbekannten Headern → Mapping-UI zur manuellen Spalten-Zuordnung
- Warnung – Falls der Zeitraum bereits Transaktionen enthält, wird gewarnt
- Kategorisieren – Button KI kategorisieren klicken – das LLM klassifiziert (oder Cache-Hit). Neue Kategorien markiert das LLM als
is_new - Prüfen – Ergebnisse kontrollieren, Kategorien bei Bedarf manuell korrigieren
- Speichern – Transaktionen speichern – sie landen in der SQLite-DB.
is_new-Kategorien werden als "pending" gespeichert, nicht auto-erstellt - Auswerten – Unter
/transactionsund/statsDashboard und Details einsehen- Einnahmen-Filter: Default werden nur Ausgaben angezeigt (positiver Betrag = ausgeblendet). Toggle "Einnahmen einblenden" in der Filter-Bar
- Kategorie-Farben: Jede Kategorie hat eine deterministische Farbe (SHA256-Hash → HSL). Visueller Punkt vor jeder Kategorie, einheitliche Farben in Charts
- Suche: Volltext-Suche über Gegenpartei, IBAN und Buchungstext (z.B. "Volg" findet alle Volg-Buchungen). Kombinierbar mit Kategorie- und Monats-Filter
- Bulk-Edit: Mehrere Transaktionen auswählen + gemeinsam zu einer Kategorie zuordnen (Sticky Action-Bar unten)
- Bulk-Delete: Mehrere Transaktionen auswählen + gemeinsam löschen (mit Bestätigungs-Modal, unwiderruflich)
- Einzel-Delete: Pro Zeile ×-Button → confirm-Dialog → entfer
- Neue Kategorie inline: Im Dropdown jeder Zeile "+ Neue Kategorie…" wählen — Name eingeben, fertig
- Vorschläge verwalten – Unter
/suggestionsfür jede LLM-vorgeschlagene Kategorie:- Akzeptieren: neue Kategorie offiziell bestätigen
- Zusammenführen: in eine bestehende Kategorie überführen (z.B. "Streaming" → "Abos / Streaming"). Alle betroffenen Transaktionen + Counterparty-Cache werden umgezogen
- Bulk-Merge: pro Ziel-Kategorie mehrere Vorschläge gleichzeitig per Checkbox auswählen und überführen
- Ablehnen: Vorschlag entfernen (Transaktionen verlieren die Kategorie-Zuordnung)
Architektur
Ausgaben/
├── app.py # Flask-Web-App (Routes & UI)
├── camt_parser.py # CAMT.053/.054 XML-Parser (defusedxml)
├── parsers.py # Multi-Format-Dispatcher (CAMT, CSV, XLSX + Header-Heuristik)
├── agent.py # LLM-Agent (Ollama /api/chat, Cache, Retry)
├── storage.py # SQLite-Storage (CRUD, Cache, Concurrency)
├── ausgaben.db # SQLite-Datenbank (im DATA_DIR)
├── Dockerfile
├── docker-compose.yml
├── .env.example
├── entrypoint.sh
├── requirements.txt
├── requirements-dev.txt
├── pytest.ini
├── tests/ # pytest-Suite (92 Tests)
│ ├── conftest.py
│ ├── test_camt_parser.py
│ ├── test_parsers.py
│ ├── test_storage.py
│ ├── test_agent.py
│ ├── test_app.py
│ └── fixtures/
├── templates/ # Jinja2-Vorlagen
│ ├── base.html
│ ├── index.html
│ ├── categorize.html
│ ├── mapping.html
│ ├── transactions.html
│ ├── suggestions.html
│ ├── stats.html
│ └── config.html
└── static/
└── style.css
Ohne Docker (Entwicklung)
python3 -m venv venv
source venv/bin/activate
pip install -r requirements-dev.txt
export DATA_DIR=$PWD # DB im Projektordner
python app.py --port 5000
Ollama-Modell installieren (falls nicht vorhanden)
ollama pull llama3.2 # ~2 GB, schnell
ollama pull qwen2.5:7b # ~4.5 GB, sehr guter JSON-Output
ollama pull mistral:7b # ~4.1 GB, solide
Lizenz
MIT