from decimal import Decimal from pathlib import Path from typing import Any from faker import Faker from polyfactory.factories.pydantic_factory import ModelFactory from polyfactory.fields import Use from src.models.b4w import B4wExport from src.models.category import Category from src.models.comdirect import ComdirectCreditCard, ComdirectDepot, ComdirectGiro from src.models.transactions import Transaction CATEGORY_PATHS = tuple( line.strip() for line in ( Path(__file__).resolve().parents[1] / "scripts/initial_load/categories/categories.txt" ) .read_text(encoding="utf-8") .splitlines() if line.strip() ) def _category_from_path(path: str) -> Category: category = Category.from_str(path) if category is None: raise ValueError("Category list contains an empty path") return category class CategoryFactory: __faker__ = Faker("de_DE") @classmethod def build(cls) -> Category: return _category_from_path(cls.__faker__.random_element(CATEGORY_PATHS)) @staticmethod def build_all() -> list[Category]: return [_category_from_path(path) for path in CATEGORY_PATHS] def _faker_or_none(faker: Faker, non_default: Any, default: Any = None, p_value=0.5): if faker.pyfloat(min_value=0.0, max_value=1.0) < p_value: return default return non_default def _amount() -> Decimal: return B4wExportFactory.__faker__.pydecimal( left_digits=6, right_digits=2, positive=True, min_value=Decimal("0.01"), ) def _amount_eur() -> str: """Generate German-formatted decimal string (e.g., '32,43').""" val = Faker().pydecimal( right_digits=2, min_value=Decimal("-4000.00"), max_value=Decimal("4000.00") ) # Format as German string with comma decimal separator return f"{val:.2f}".replace(".", ",").strip() def _german_date() -> str: return Faker().date("%d.%m.%Y") def _sentence() -> str: return B4wExportFactory.__faker__.sentence( nb_words=8, variable_nb_words=False, ) def _names() -> str: return B4wExportFactory.__faker__.company() def _iban() -> str: return B4wExportFactory.__faker__.iban() def _iban_or_none() -> str: return _faker_or_none(Faker(), Faker().iban(), default="") class B4wExportFactory(ModelFactory[B4wExport]): __faker__ = Faker("de_DE") __use_examples__ = True Amt = Use(_amount) RmtInf = Use(_sentence) Notes = Use(_sentence) RmtdNm = Use(_names) OwnrAcctIBAN = Use(_iban) RmtdAcctIBAN = Use(_iban_or_none) class ComdirectGiroFactory(ModelFactory[ComdirectGiro]): __faker__ = Faker("de_DE") __use_examples__ = True text = Use(_sentence) amount_eur = Use(_amount_eur) date_val = Use(_german_date) date_book = Use(_german_date) class ComdirectCreditCardFactory(ModelFactory[ComdirectCreditCard]): __faker__ = Faker("de_DE") __use_examples__ = True text = Use(_sentence) amount_eur = Use(_amount_eur) date_val = Use(_german_date) date_book = Use(_german_date) class ComdirectDepotFactory(ModelFactory[ComdirectDepot]): __faker__ = Faker("de_DE") __use_examples__ = True amount_eur = Use(_amount_eur) date_val = Use(_german_date) num = Use(_amount_eur) spot_price = Use(_amount_eur) class TransactionFactory(ModelFactory[Transaction]): __faker__ = Faker("de_DE") __use_examples__ = True account_iban = Use(_iban) text = Use(_sentence) other_name = Use(_names) other_iban = Use(_iban_or_none)