Ingest comdirect #5

Merged
mathias2784 merged 31 commits from ingest-comdirect into master 2026-09-13 20:27:16 +00:00
3 changed files with 100 additions and 38 deletions
Showing only changes of commit afe2478cc7 - Show all commits
+59 -33
View File
@@ -1,7 +1,7 @@
from datetime import date
from decimal import Decimal
from pydantic import Field, computed_field, field_validator
from pydantic import ConfigDict, Field, computed_field, field_validator
from sqlalchemy import Column, Date, String
from src.models._common import (
@@ -11,10 +11,12 @@ from src.models._common import (
_parse_date_german,
_parse_decimal_german,
date2str,
decimal2str,
)
class ComdirectBase(ExportBaseModel):
model_config = ConfigDict(populate_by_name=True)
date_val: date | None = Field(
alias="Wertstellung (Valuta)", examples=["07.09.2026"]
)
@@ -22,7 +24,9 @@ class ComdirectBase(ExportBaseModel):
@field_validator("date_val", mode="before")
@classmethod
def parse_date_val(cls, date_str: str) -> date | None:
def parse_date_val(cls, date_str: str | None) -> date | None:
if date_str is None:
return
return _parse_date_german(date_str)
@field_validator("amount_eur", mode="before")
@@ -30,13 +34,28 @@ class ComdirectBase(ExportBaseModel):
def parse_amount(cls, decimal_str: str) -> Decimal:
return _parse_decimal_german(decimal_str)
def parameters_to_dict(self) -> dict[str, str]: ...
@classmethod
def _date2str(cls, date_value: date | None) -> str:
return date2str(date_value, default="--", date_format="%d.%m.%Y")
def parameters_to_str(self, sep=", ", str_qualifier=False): ...
def parameters_to_dict(self, by_name=True) -> dict[str, str]:
return {
"date_val" if by_name else "Wertstellung (Valuta)": self._date2str(
self.date_val
),
"amount_eur" if by_name else "Umsatz in EUR": decimal2str(self.amount_eur),
}
def __repr__(self): ...
def get_orig_headers(self, sep: str = ";", bracket: str = '"'):
return sep.join(
f"{bracket}{txt}{bracket}" for txt in self.parameters_to_dict(by_name=False)
)
def __str__(self): ...
def get_orig_row(self, sep: str = ";", bracket: str = '"'):
return sep.join(
f"{bracket}{txt}{bracket}"
for txt in self.parameters_to_dict(by_name=False).values()
)
class ComdirectGiro(ComdirectBase):
@@ -59,21 +78,18 @@ class ComdirectGiro(ComdirectBase):
def parse_date_book(cls, date_str: str) -> date | None:
return _parse_date_german(date_str)
def parameters_to_dict(self) -> dict[str, str]:
return {
"Buchungstag": date2str(
self.date_book, default="--", date_format="%d.%m.%Y"
),
"Vorgang": self.type,
"Text": self.text,
}
def parameters_to_str(self, sep=", ", str_qualifier=False):
...
def __repr__(self): ...
def __str__(self): ...
def parameters_to_dict(self, by_name=True) -> dict[str, str]:
parameters = dict(**super().parameters_to_dict(by_name=by_name))
parameters.update(
{
"date_book" if by_name else "Buchungstag": self._date2str(
self.date_book
),
"type" if by_name else "Vorgang": self.type,
"text" if by_name else "Buchungstext": self.text,
}
)
return parameters
class ComdirectCreditCard(ComdirectGiro):
@@ -96,13 +112,17 @@ class ComdirectCreditCard(ComdirectGiro):
alias="Umsatz in EUR", examples=["32,43"], default=None
)
def parameters_to_dict(self) -> dict[str, str]: ...
def parameters_to_str(self, sep=", ", str_qualifier=False): ...
def __repr__(self): ...
def __str__(self): ...
def parameters_to_dict(self) -> dict[str, str]:
parameters = dict(**super().parameters_to_dict())
parameters.update(
{
"Umsatztag": self._date2str(self.date_val),
"Referenz": self.reference,
}
)
return parameters
class ComdirectDepot(ComdirectBase):
@@ -121,13 +141,19 @@ class ComdirectDepot(ComdirectBase):
def parse_decimals(cls, decimal_in: str) -> Decimal:
return _parse_decimal_german(decimal_in)
def parameters_to_dict(self) -> dict[str, str]: ...
def parameters_to_str(self, sep=", ", str_qualifier=False): ...
def __repr__(self): ...
def __str__(self): ...
def parameters_to_dict(self) -> dict[str, str]:
parameters = dict(**super().parameters_to_dict())
parameters.update(
{
"Geschäftstag": self._date2str(self.date_val),
"Stück / Nom.": decimal2str(self.num),
"Bezeichnung": self.name,
"WKN": self.wkn,
"Währung": self.currency,
"Ausführungskurs": decimal2str(self.spot_price),
}
)
return parameters
class ComdirectBaseDb(IngestdbBase):
+17 -5
View File
@@ -1,4 +1,3 @@
from collections.abc import Callable
from decimal import Decimal
from typing import Any
@@ -27,6 +26,18 @@ def _amount() -> Decimal:
)
def _amount_eur() -> str:
"""Generate German-formatted decimal string (e.g., '32,43')."""
val = B4wExportFactory.__faker__.pydecimal(
left_digits=6,
right_digits=2,
positive=True,
min_value=Decimal("0.01"),
)
# Format as German string with comma decimal separator
return f"{val:.2f}".replace(".", ",").strip()
def _sentence() -> str:
return B4wExportFactory.__faker__.sentence(
nb_words=8,
@@ -61,16 +72,17 @@ class B4wExportFactory(ModelFactory[B4wExport]):
class ComdirectGiroFactory(ModelFactory[ComdirectGiro]):
__faker__ = Faker("de_DE")
__use_example__ = True
__use_examples__ = True
Buchungstext = Use(_sentence)
text = Use(_sentence)
amount_eur = Use(_amount_eur)
class ComdirectCreditCardFactory(ModelFactory[ComdirectCreditCard]):
__faker__ = Faker("de_DE")
__use_example__ = True
__use_examples__ = True
class ComdirectDepotFactory(ModelFactory[ComdirectDepot]):
__faker__ = Faker("de_DE")
__use_example__ = True
__use_examples__ = True
+24
View File
@@ -0,0 +1,24 @@
from src.models.comdirect import ComdirectGiro
from tests.factories import ComdirectGiroFactory
def test_comdirect_giro_basic_and_stringification():
data = ComdirectGiroFactory.build()
assert data
assert isinstance(data, ComdirectGiro)
orig_headers = data.get_orig_headers()
assert orig_headers
assert isinstance(orig_headers, str)
header_list = orig_headers.split(";")
assert len(header_list) > 0
assert set(header_list) == {
'"Wertstellung (Valuta)"',
'"Umsatz in EUR"',
'"Buchungstag"',
'"Vorgang"',
'"Buchungstext"',
}
orig_row = data.get_orig_row()
assert orig_row
assert isinstance(orig_row, str)
assert len(orig_row.split(";")) > 0