From d502e9ef0d568e297b756ad3788aa8b20390e10f Mon Sep 17 00:00:00 2001 From: Mathias Scheider Date: Fri, 11 Sep 2026 10:16:51 +0200 Subject: [PATCH] comdirect ingest: some code structurerefinement --- .gitignore | 1 + pyproject.toml | 1 + settings.template.py | 32 ++++++++++++++++++++++++++++++++ src/models.py | 15 +-------------- src/reader.py | 17 ++++++++--------- src/settings/__init__.py | 0 src/settings/account_ingest.py | 10 ---------- src/settings/constants.py | 18 ++++++++++++++++++ src/settings/models.py | 14 ++++++++++++++ tests/test_reader.py | 13 ++++++------- uv.lock | 2 ++ 11 files changed, 83 insertions(+), 40 deletions(-) create mode 100644 settings.template.py create mode 100644 src/settings/__init__.py delete mode 100644 src/settings/account_ingest.py create mode 100644 src/settings/constants.py create mode 100644 src/settings/models.py diff --git a/.gitignore b/.gitignore index 2a04809..2e81ff7 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ wheels/ # Environment variables .env +settings.py # Database instances *.db diff --git a/pyproject.toml b/pyproject.toml index 9eb8986..e236405 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,5 +32,6 @@ build-backend = "uv_build" dev = [ "black>=26.5.1", "debugpy>=1.8.21", + "faker>=40.38.0", "polyfactory>=3.3.0", ] diff --git a/settings.template.py b/settings.template.py new file mode 100644 index 0000000..10677d4 --- /dev/null +++ b/settings.template.py @@ -0,0 +1,32 @@ +from faker import Faker + +from src.models import ComdirectCreditCard, ComdirectDepot, ComdirectGiro +from src.settings.constants import Currency +from src.settings.models import AccountInfo + +fake = Faker() + +BANK_ACCOUNTS = { + "comdirect": { + "Girokonto": AccountInfo( + iban=fake.iban(), + ingestion_function=lambda x: ComdirectGiro.model_validate(x), + currency=Currency.EUR, + ), + "Visa-Karte (Kreditkarte)": AccountInfo( + iban=fake.iban(), + ingestion_function=lambda x: ComdirectCreditCard.model_validate(x), + currency=Currency.EUR, + ), + "Tagesgeld PLUS-Konto": AccountInfo( + iban=fake.iban(), + ingestion_function=lambda x: ComdirectGiro.model_validate(x), + currency=Currency.EUR, + ), + "Depot": AccountInfo( + iban=fake.iban(), + ingestion_function=lambda x: ComdirectDepot.model_validate(x), + currency=Currency.EUR, + ), + } +} diff --git a/src/models.py b/src/models.py index b2597bf..a500eeb 100644 --- a/src/models.py +++ b/src/models.py @@ -4,20 +4,7 @@ from enum import Enum from pydantic import BaseModel, Field, field_validator -IBAN_PATTERN = r"^[A-Z]{2}\d{2}[A-Z0-9]{11,30}$" - - -class Currency(str, Enum): - EUR = "EUR" - USD = "USD" - GBP = "GBP" - CHF = "CHF" - - def __str__(self): - return self.value - - def __repr__(self): - return f"Currency.{self.name}" +from src.settings.constants import IBAN_PATTERN, Currency class Sign(str, Enum): diff --git a/src/reader.py b/src/reader.py index b675164..db7de09 100644 --- a/src/reader.py +++ b/src/reader.py @@ -3,8 +3,9 @@ import re from enum import Enum, auto from pathlib import Path +from settings import BANK_ACCOUNTS from src.models import B4wExport -from src.settings.account_ingest import ACCOUNT_INGESTION_FUNCTIONS +from src.settings.models import AccountInfo def _reader_raw(path: Path): @@ -29,18 +30,16 @@ class EXPECT_LINE(Enum): DATA = auto() +COMDIRECT_ACCOUNTS = BANK_ACCOUNTS.get("comdirect", {}) +assert isinstance(COMDIRECT_ACCOUNTS, dict) + def reader_comdirect(path: Path): - bank_name = "comdirect" - ingestion_functions = ACCOUNT_INGESTION_FUNCTIONS.get(bank_name) - assert ingestion_functions - result = {} raw = _reader_raw_comdirect(path) for account_name, account_data in raw.items(): - ingestion_function = ingestion_functions.get(account_name) - assert ingestion_function - result[account_name] = list(map(ingestion_function, account_data)) - + account = COMDIRECT_ACCOUNTS.get(account_name) + assert isinstance(account, AccountInfo) + result[account_name] = list(map(account.ingestion_function, account_data)) return result def _reader_raw_comdirect(path: Path): diff --git a/src/settings/__init__.py b/src/settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/settings/account_ingest.py b/src/settings/account_ingest.py deleted file mode 100644 index 4e3f9c8..0000000 --- a/src/settings/account_ingest.py +++ /dev/null @@ -1,10 +0,0 @@ -from src.models import ComdirectCreditCard, ComdirectDepot, ComdirectGiro - -ACCOUNT_INGESTION_FUNCTIONS = { - "comdirect": { - "Girokonto": lambda x: ComdirectGiro.model_validate(x), - "Visa-Karte (Kreditkarte)": lambda x: ComdirectCreditCard.model_validate(x), - "Tagesgeld PLUS-Konto": lambda x: ComdirectGiro.model_validate(x), - "Depot": lambda x: ComdirectDepot.model_validate(x), - } -} diff --git a/src/settings/constants.py b/src/settings/constants.py new file mode 100644 index 0000000..a8cb886 --- /dev/null +++ b/src/settings/constants.py @@ -0,0 +1,18 @@ + +from enum import Enum + +IBAN_PATTERN = r"^[A-Z]{2}\d{2}[A-Z0-9]{11,30}$" + +class Currency(str, Enum): + EUR = "EUR" + USD = "USD" + GBP = "GBP" + CHF = "CHF" + + def __str__(self): + return self.value + + def __repr__(self): + return f"Currency.{self.name}" + + diff --git a/src/settings/models.py b/src/settings/models.py new file mode 100644 index 0000000..d58994a --- /dev/null +++ b/src/settings/models.py @@ -0,0 +1,14 @@ +from collections.abc import Callable + +from pydantic import BaseModel, Field + +from src.settings.constants import IBAN_PATTERN, Currency + + +class AccountInfo(BaseModel): + iban: str = Field(pattern=IBAN_PATTERN) + ingestion_function: Callable + currency: Currency + + + diff --git a/tests/test_reader.py b/tests/test_reader.py index 9bc4911..30aea9c 100644 --- a/tests/test_reader.py +++ b/tests/test_reader.py @@ -1,19 +1,18 @@ from pathlib import Path from src.models import ComdirectCreditCard, ComdirectDepot, ComdirectGiro -from src.reader import reader_comdirect -from src.settings.account_ingest import ACCOUNT_INGESTION_FUNCTIONS +from src.reader import COMDIRECT_ACCOUNTS, reader_comdirect + +TEST_DATA_PATH = ".local/data/comdirect/umsaetze_9773063005_20260906-2043.csv" def test_reader_comdirect(): - bank_name = "comdirect" - assert bank_name in ACCOUNT_INGESTION_FUNCTIONS - test_path = Path(".local/data/comdirect/umsaetze_9773063005_20260906-2043.csv") + test_path = Path(TEST_DATA_PATH) assert test_path.exists() data = reader_comdirect(test_path) assert data - assert len(data) == len(ACCOUNT_INGESTION_FUNCTIONS.get(bank_name)) - assert set(data) == set(ACCOUNT_INGESTION_FUNCTIONS.get(bank_name)) + assert len(data) == len(COMDIRECT_ACCOUNTS) + assert set(data) == set(COMDIRECT_ACCOUNTS) assert all(isinstance(row, ComdirectGiro) for row in data["Girokonto"]) assert all(isinstance(row, ComdirectCreditCard) for row in data["Visa-Karte (Kreditkarte)"]) assert all(isinstance(row, ComdirectGiro) for row in data["Tagesgeld PLUS-Konto"]) diff --git a/uv.lock b/uv.lock index 92a955e..d235a00 100644 --- a/uv.lock +++ b/uv.lock @@ -128,6 +128,7 @@ dependencies = [ dev = [ { name = "black" }, { name = "debugpy" }, + { name = "faker" }, { name = "polyfactory" }, ] @@ -146,6 +147,7 @@ requires-dist = [ dev = [ { name = "black", specifier = ">=26.5.1" }, { name = "debugpy", specifier = ">=1.8.21" }, + { name = "faker", specifier = ">=40.38.0" }, { name = "polyfactory", specifier = ">=3.3.0" }, ]