import argparse
import collections
+import csv
import itertools
import re
from collections import namedtuple
'Gehalt/Steuern': re.compile('Gehalt/Rente|RED HAT|Finanzamt|FK Guenzburg', re.I),
'Wohnung': re.compile('Telekom|hands4home|Goetzfried|Green Planet Energy|Beitragsservice.*Rundfunk', re.I),
'Medizin': re.compile(r'Apotheke|MVZ|\bmed\b|ZAB Abrechnung|Caika|HNOeins|PVS|Dr\..*Sellier|'
- r'BFS Health|Streifeneder|Labor|Physio|(Drescher.*Lung)|'
- '(Dr.*Borchers)|(Debeka.*Überweisung)|(DKV.*Überweisung)|Beihilfe', re.I),
- 'Versicherung': re.compile('(debeka|DKV|Hallesche|Versicherung|Alte Leipziger|ConceptIF|'
- 'Baloise).*Lastschrift', re.I),
- 'Transport': re.compile('DB Vertrieb|Deutsche Bahn|Nextbike|Carsharing', re.I),
- 'Lebensmittel': re.compile('BIOS|Wolf|Ruta|Rewe', re.I),
- 'Eigentumswohnungen': re.compile('Rechnung Darl.-Leistung|Semmelweis', re.I),
- 'Hobby/Sport': re.compile('Holstein|Mrs. Sporty|Kieser|DJK', re.I),
- 'Sparen': re.compile('Bausparkasse Mainz AG|FIL Fondsbank|MIG (Fonds|GmbH)|Netfonds AG', re.I),
+ r'BFS Health|Streifeneder|Labor|Physio|(Drescher.*Lung)|Osteopath|'
+ '(Dr.*Borchers)|(Debeka.*Überweisung)|(DKV.*Überweisung)|Beihilfe|Klinik|'
+ 'Niklas Hermann', re.I),
+ 'Versicherung': re.compile('((debeka|DKV|Hallesche|Versicherung|Alte Leipziger|ConceptIF|'
+ 'Baloise).*Lastschrift)|Hallesche.*Bonu', re.I),
+ 'Transport': re.compile('DB Vertrieb|DB Fernverkehr|Deutsche Bahn|Nextbike|Carsharing|Radstation', re.I),
+ 'Lebensmittel': re.compile('BIOS|Bäcker|Baecker|Ruta|Rewe', re.I),
+ 'Eigentumswohnungen': re.compile('Rechnung Darl.-Leistung|Semmelweis|BHP Buerkner', re.I),
+ 'Hobby/Sport': re.compile('Holstein|Mrs. Sporty|Kieser|DJK|Dimaso', re.I),
+ 'Sparen': re.compile('Bausparkasse Mainz AG|FIL Fondsbank|MIG (Fonds|GmbH)|Netfonds AG|Festgeld', re.I),
'Spenden': re.compile('Spende|Signal Foundation|nebenan.de|(paypal.*Sabine.Hossenf)|'
'Patreon|Umwelthilfe|Foerderer|Malteser|(Aktion Tier.*Mensch)|'
'campact|Amnesty', re.I),
return 'Sonstiges'
-def parse_entry(line: str) -> Entry:
- fields = [f.strip() for f in line.strip().split(';')]
+def parse_entry(raw_fields: Iterable[str]) -> Entry:
+ fields = [f.strip() for f in raw_fields]
+ # format change in May 2024, adds a 9th field "Währung"; ignore
+ fields = fields[:8]
# last field is the value, parse as float
value = float(fields.pop().replace('.', '').replace(',', '.'))
# match on who, IBAN, type, or desc
return filter_re.search(entry.date)
with path.open() as f:
+ reader = csv.reader(f, delimiter=';')
+ next(reader) # skip header
# first line is the column headers, chop it off
- entries = map(parse_entry, f.readlines()[1:])
- return filter(filter_entry, entries)
+ entries = map(parse_entry, reader)
+ # do the actual iteration here, as the files get closed afterwards
+ return list(filter(filter_entry, entries))
def main():