3.7 KiB
3.7 KiB
finanz-cockpit Agent Instructions
Setup Commands
# Install dependencies
uv sync
# Run migrations
alembic upgrade head
# Run all tests
pytest
# Run a single test file
pytest tests/test_model_b4wexport.py -v
Project Structure
src/: Application codemain.py: CLI entrypoint for CSV ingestiondb.py: Database connection management (uses global_enginepattern)models/: Pydantic models + SQLAlchemy tables for b4w and comdirect importsingest/: Data ingestion logic per source
tests/: Tests with fixtures inconftest.pyalembic/versions/: Database migrations
Key Patterns & Gotchas
Database Configuration
from src.db import get_engine, get_session, set_engine
# Development: use sqlite://
os.environ["DATABASE_URL"] = "sqlite://"
# Production/default: sqlite:///finanz.db
engine = get_engine() # reads DATABASE_URL or uses default
with get_session() as session:
session.query(...).all()
Critical: Tests override engine via conftest.py. Never assume global state - always use get_session() context manager.
CSV Format Expectations
- b4w: German bank export (CSV) from
/mnt/anbau/Software/Python/finanz-cockpit/scripts/dummy_data/b4w_export_dummy_01.csv - comdirect: German bank export (CSV) with fields like
Buchungstag,Umsatz in EUR,Vorgang
Testing Workflow
# Tests automatically run migrations via conftest fixture
pytest tests/ -v --tb=short
# To test without migrations (in-memory SQLite), use DATABASE_URL="sqlite://"
export DATABASE_URL="sqlite://" pytest tests/test_db.py
Environment Variables
DATABASE_URL: Database connection string (default:sqlite:///finanz.db)
Data Models
Two sources with shared patterns:
- b4w:
B4wExport,Category, transactional import toimport_b4wtable +categorytable - comdirect:
ComdirectGiro,ComdirectCreditCard,ComdirectDepot, import tocomdirect_girotable - unified:
Transaction,TransactionDb– both ingest pipelines project their staging rows (import_b4w,comdirect_giro) into onetransactionstable viasrc/ingest/transactions.py(map_*mappers +consolidate_transactions). Dedup uses a business-keyhash_id(source + IBAN + value date + amount + text + counterparty) with a UNIQUE constraint and SQLiteINSERT OR IGNORE, so re-imports are idempotent. Amounts are canonical signed (negative = money out). Depot/credit-card/tagesgeld are not projected yet.
All models inherit from common base in src/models/_common.py:
_hash(): SHA256-based deduplication key_parse_date_german(): Handles "07.09.2026" format and values like "offen", "--"_parse_decimal_german(): Converts "1,23" to Decimal
Factory Pattern for Testing
Import from tests/factories.py:
from tests.factories import B4wExportFactory, ComdirectGiroFactory
from src.constants import Currency
B4wExportFactory.__faker__ = Faker("de_DE")
ComdirectGiroFactory.__faker__ = Faker("de_DE")
# Use in tests:
item = B4wExportFactory.build( # build() for partial, create() for full
Amt="50.00",
RmtInf="Supermarket",
)
Common Mistakes to Avoid
- Assuming global engine state: Always use
get_session()context manager - Ignoring German date formats: Use
_parse_date_german()for CSV parsing - Forgetting migration setup in tests: Tests auto-migrate, but integration tests with real db need explicit upgrade
- Wrong Faker locale: Use
Faker("de_DE")for realistic names/IBANs
Testing Tips
- Run single test:
pytest tests/test_model_b4wexport.py -v - View migrations:
ls alembic/versions/ - Test data location:
/scripts/dummy_data/