mitmario.dev

Wie es weitergeht

Python Sandbox 4 Min Lesezeit 3 BeispieleLektion 7 von 8

Jetzt steht ein Werkzeug da, das einen Kontoauszug einliest und dir zeigt, wofür das Geld draufgegangen ist. Fünf Dateien und sechs Tests. Das war am Anfang des Kurses versprochen, und es ist kein Spielzeug. Es liest Exporte in zwei Kodierungen, lässt sich ohne Code an neue Läden anpassen und sagt beim Aufruf, was es braucht. Was noch fehlt, ist dein eigener Rechner mit deinem eigenen Export.

Das ganze Werkzeug

Das ganze Werkzeug
import argparse
import csv

from bericht import excel_zahl, uebersicht
from einlesen import lies_kontoauszug
from kategorien import kategorisiere, lies_regeln


def schreibe_bericht(buchungen, monat):
    """Schreibt die Buchungen in bericht-JJJJ-MM.csv und gibt den Namen zurück."""
    name = f"bericht-{monat}.csv"
    with open(name, "w", encoding="utf-8-sig", newline="") as datei:
        schreiber = csv.writer(datei, delimiter=";")
        schreiber.writerow(["Datum", "Empfänger", "Verwendungszweck", "Kategorie", "Betrag"])
        for buchung in buchungen:
            schreiber.writerow([
                buchung.datum.strftime("%d.%m.%Y"),
                buchung.empfaenger,
                buchung.zweck,
                buchung.kategorie,
                excel_zahl(buchung.betrag),
            ])
    return name


def main():
    parser = argparse.ArgumentParser(description="Wertet den CSV-Export eines Girokontos aus.")
    parser.add_argument("datei", help="der Export der Bank, etwa kontoauszug.csv")
    parser.add_argument("--monat", help="der Monat als JJJJ-MM, ohne Angabe der neueste")
    args = parser.parse_args()

    buchungen, kodierung = lies_kontoauszug(args.datei)
    regeln = lies_regeln("regeln.csv")
    for buchung in buchungen:
        buchung.kategorie = kategorisiere(buchung, regeln)

    monat = args.monat
    if monat is None:
        monat = max([buchung.datum for buchung in buchungen]).strftime("%Y-%m")
    im_monat = [buchung for buchung in buchungen if buchung.datum.strftime("%Y-%m") == monat]
    if not im_monat:
        parser.error(f"im Monat {monat} gibt es keine Buchungen")

    print(f"{args.datei}: {len(buchungen)} Buchungen, {kodierung}")
    print(uebersicht(buchungen, monat))
    print("Bericht:", schreibe_bericht(im_monat, monat))


if __name__ == "__main__":
    main()

Hier sind alle Teile auf einmal zu sehen, einlesen.py, kategorien.py, bericht.py und main.py, dazu die Regeln in regeln.csv und die Tests. Gestartet wird es so, wie es auf jedem Rechner gestartet würde, mit der Datei und dem Monat dahinter. In der Befehlsleiste stehen außerdem der Aufruf ohne Monat, die Hilfe und die Tests.

Auf deinen Rechner

Wie Python auf den eigenen Rechner kommt, steht in Lektion 11.1, und wie eine eigene Umgebung entsteht, in Abschnitt 13. Für das Werkzeug heißt das: ein Ordner mit den fünf Dateien und regeln.csv, darin eine Umgebung mit python -m venv .venv, und eine requirements.txt mit zwei Zeilen, tabulate und pytest, jeweils mit dem Bereich ihrer Hauptversion. Nach pip install -r requirements.txt läuft python main.py kontoauszug.csv dort genauso wie hier.

Dann kommt der eigene Export dazu. Um die Kodierung kümmert sich lies_kontoauszug, um die Zeilen über den Spaltennamen auch. Was sich von Bank zu Bank wirklich unterscheidet, sind die Namen der Spalten. Und manchmal setzt eine Bank jedes Feld in Anführungszeichen.

Eine andere Bank
import csv

from einlesen import Buchung, datum_aus_text, zahl_aus_text

# Welche Spalte dieser Bank welches Feld der Buchung füllt. Für die nächste
# Bank ändert sich nur dieses Dictionary.
SPALTEN = {
    "datum": "Buchungsdatum",
    "empfaenger": "Name",
    "zweck": "Verwendungszweck",
    "betrag": "Betrag (EUR)",
}

with open("andere-bank.csv", encoding="utf-8-sig", newline="") as datei:
    for zeile in csv.DictReader(datei, delimiter=";"):
        buchung = Buchung(
            datum_aus_text(zeile[SPALTEN["datum"]]),
            zeile[SPALTEN["empfaenger"]],
            zeile[SPALTEN["zweck"]],
            zahl_aus_text(zeile[SPALTEN["betrag"]]),
        )
        print(buchung)

Hier heißt das Datum Buchungsdatum, der Empfänger Name und der Betrag Betrag (EUR), und um jedes Feld stehen Anführungszeichen. Die Anführungszeichen nimmt csv von selbst weg. Für die Namen reicht ein Dictionary, in dem steht, welche Spalte welches Feld füllt.

Wenn so etwas oben in einlesen.py steht, ist der Wechsel zu einer anderen Bank eine Änderung an einer einzigen Stelle. Ob danach alles andere noch stimmt, siehst du an den Tests.

Dazu kommt die Zeile in lies_kontoauszug, an der die Zeile mit den Spaltennamen erkannt wird. Bei dieser Bank fängt sie nicht mit Buchungstag an, sondern mit "Buchungsdatum", samt Anführungszeichen. Beim Suchen dieser Zeile liest nämlich noch kein csv mit.

Und dann die Regeln. Beim ersten Lauf mit dem eigenen Export landet viel unter Sonstiges, und genau da stehen die Regeln, die fehlen. Nach zwei, drei Monaten sind es nur noch die Ausreißer, und eine neue Regel ist eine neue Zeile in regeln.csv.

Drei Richtungen

Daten

Wenn dir die Monatsübersicht Spaß gemacht hat, ist pandas das nächste Paket. Damit lassen sich Tabellen wie diese mit ein paar Zeilen gruppieren, verbinden und als Diagramm zeichnen. Dazu passt Jupyter, eine Umgebung, in der Code, Tabelle und Diagramm untereinander auf einer Seite stehen. Beides baut auf dem auf, was hier vorkam: Listen, Dictionaries, CSV, Datum und Geld.

Das Web

In Abschnitt 18 ging es darum, wie aus Python eine Seite im Browser wird. Mit Flask kommt man weit, auch mit Anmeldung und Datenbank. Django bringt für größere Anwendungen das meiste schon mit, von der Verwaltungsoberfläche bis zur Anmeldung, und FastAPI ist für Schnittstellen wie die des Haushaltsbuchs gemacht. Wer Seiten baut, braucht dazu HTML und CSS, und für beides gibt es auf dieser Lernplattform einen eigenen Kurs.

Automatisierung

Viele der nützlichsten Skripte sieht man nie. Sie laufen jeden Morgen von selbst, holen etwas ab, räumen etwas auf und legen einen Bericht hin. Wie man ein Skript zu festen Zeiten startet, mit der Aufgabenplanung von Windows, mit cron oder launchd, steht in Lektion 11.1. Das Werkzeug hier braucht dafür nur noch den Export. Den holt bisher ein Mensch.

Was du dir als Nächstes bauen könntest

Ein Monatsvergleich
from tabulate import tabulate

from bericht import euro
from einlesen import lies_kontoauszug
from kategorien import kategorisiere, lies_regeln

buchungen, kodierung = lies_kontoauszug("kontoauszug.csv")
regeln = lies_regeln("regeln.csv")

# Die Ausgaben nach Kategorie und Monat, wie in Lektion 19.4.
summen = {}
for buchung in buchungen:
    if buchung.betrag < 0:
        schluessel = (kategorisiere(buchung, regeln), buchung.datum.strftime("%Y-%m"))
        summen[schluessel] = summen.get(schluessel, 0) - buchung.betrag

kategorien = set()
monate = set()
for kategorie, monat in summen:
    kategorien.add(kategorie)
    monate.add(monat)
monate = sorted(monate)

zeilen = []
for kategorie in sorted(kategorien):
    zeile = [kategorie]
    for monat in monate:
        zeile.append(euro(summen.get((kategorie, monat), 0)))
    zeilen.append(zeile)
print(tabulate(zeilen, headers=["Kategorie"] + monate, tablefmt="rounded_outline", colalign=["left"] + ["right"] * len(monate)))

Am meisten lernt man an einem Werkzeug für ein Problem, das man selbst hat. Ein paar Ideen, alle mit dem Stoff aus diesem Kurs machbar:

  • Ein Monatsvergleich, wie im Beispiel: jede Kategorie über alle Monate nebeneinander, damit man sieht, wo es teurer wird.
  • Daueraufträge finden, also Buchungen, die jeden Monat mit demselben Empfänger und demselben Betrag kommen und die man vielleicht gar nicht mehr braucht.
  • Der Bericht als Excel-Mappe mit openpyxl aus Abschnitt 13, mit fetten Spaltennamen und Beträgen, mit denen Excel rechnen kann.
  • Eine Warnung bei einer Ausgabe, die größer ist als alles, was in den drei Monaten davor in derselben Kategorie vorkam.
  • Fotos nach dem Datum im Dateinamen in Monatsordner sortieren, oder die Rechnungen aus dem Download-Ordner jeden Freitag an ihren Platz räumen.
  • Das Haushaltsbuch im Browser, aus Abschnitt 18, mit der Datenbank aus Abschnitt 16 darunter.

Ein Werkzeug muss dabei nicht fertig sein, um zu helfen. Die erste Fassung des Kontoauszugs aus Abschnitt 12 hatte keine Tests, keine Regeldatei und keinen Aufruf. Gerechnet hat sie trotzdem schon.

Zum Mitnehmen

Das Werkzeug läuft auf jedem Rechner mit Python, in einer eigenen Umgebung mit tabulate und pytest. Für den eigenen Export ändern sich vor allem die Spaltennamen, damit auch die Zeile, an der lies_kontoauszug die Tabelle erkennt, und dazu die Regeln. Weiter geht es danach in drei Richtungen, zu Daten mit pandas, ins Web mit Flask und mehr oder zu Skripten, die zu festen Zeiten von selbst laufen.

Jetzt du

Basis Konto, kostenlos

Im Editor änderst du die Beispiele dieser Lektion und lässt sie gleich laufen. So merkst du am schnellsten, ob es sitzt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.

Was in dieser Lektion steckt

  • Artikel mit 3 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.