The Problem
Financial analysts and accountants often receive bank statements as scanned PDFs. Extracting line‑item data into a structured CSV is a manual, error‑prone task, especially when statements differ per bank and may be password‑protected.
What This Does
monopoly provides a Python library and a CLI that parse supported bank‑statement PDFs and emit CSV rows. The core logic lives under src/monopoly/, e.g.:
src/monopoly/pdf.py– reads raw PDF text, extracts metadata, and buildsPdfPageobjects.src/monopoly/statements/base.py– implements generic line‑parsing, multiline handling, and transaction boundary detection.- Bank‑specific adapters in
src/monopoly/banks/<bank>/(e.g.,amex.py,bmo.py) inherit fromBankBaseand supply regex patterns for that institution.
The CLI entry point is src/monopoly/cli/cli.py. The public command monopoly invokes process_statement, which builds a Pipeline (see src/monopoly/pipeline.py) that chains PDF extraction, transformation, and CSV writing.
How It Is Wired
Execution starts in src/monopoly/cli/cli.py:
process_statement(line 16) – receives a file path, creates aPdfDocument(src/monopoly/pdf.py) and passes it toPipeline.extract.Pipeline.extract– callsPdfParser(viaPdfDocument) to produce a list ofPdfPageobjects (used in 32 distinct places).- Each
PdfPageis fed to the bank detector (src/monopoly/banks/detector.py). The detector constructs aBankDetectorthat runs a lightweight model inference (the only external inference call). - The selected bank adapter runs its
extractmethod, which heavily usessearch,MetadataIdentifier,NumberExtractor, andDateResolver(the most called internal symbols, 28–14 occurrences). - Parsed
Transactionobjects (src/monopoly/statements/transaction.py) flow intoPipeline.transform, applying generic and bank‑specificPatternhandlers (src/monopoly/generic/patterns.py). - Finally,
Pipeline.convert_dateandsrc/monopoly/write.pyserialize the transaction list to CSV.
The import graph shows 116 internal modules with 57 edges and no circular dependencies, so the code base is acyclic. Hub modules (pdf.py, enums.py, transaction.py) are touched by >15 other files, making them high‑impact change locations.
How To Use It
# Clone the repo
git clone https://github.com/moses-y/monopoly
cd monopoly
# System dependencies for PDF text extraction
sudo apt-get install build-essential libpoppler-cpp-dev pkg-config ocrmypdf # Debian/Ubuntu
# or on macOS
brew install gcc@11 pkg-config poppler ocrmypdf
# Install Python package (uses pyproject.toml)
uv pip install -e . # or: pip install -e .
# Optional OCR support
pip install 'monopoly-core[ocr]'
# Copy env template if you need passwords for protected PDFs
cp .env.template .env
# Edit .env to set PDF_PASSWORDS as a JSON list, e.g. PDF_PASSWORDS=["pwd1","pwd2"]
Run the CLI on a single file or a directory:
monopoly src/monopoly/examples/example_statement.pdf
monopoly ./statements --output ./out --preserve-filename
The CLI flags are defined in src/monopoly/cli/models.py and displayed via monopoly --help.
Real‑World Use
A fintech onboarding pipeline can drop incoming statement PDFs into a shared folder. A nightly cron runs:
#!/usr/bin/env bash
cd /opt/monopoly
monopoly /data/incoming --output /data/processed
The resulting CSVs are then ingested by the downstream accounting system without manual re‑keying.
Code Health & Issues
Static findings (4 categories, 9 instances):
- HIGH – Deep nesting –
src/monopoly/statements/base.py,tests/integration/banks/test_trust_transaction_pattern.py,src/monopoly/cli/cli.py(max indent 20). - HIGH – Duplicated code – identical 6‑line blocks across many bank adapters (
amex.py,trust.py,bmo.py,dbs.py, …). - MEDIUM – File opened without context manager –
tests/unit/test_bank_identifier/test_get_identifier.py. - MEDIUM – High branching density –
src/monopoly/banks/bmo/bmo.py,src/monopoly/banks/cibc/cibc.py,src/monopoly/banks/rbc/rbc.py.
CI/CD health audit (4 findings):
- HIGH – Pin GitHub Actions – actions referenced by tag (
astral-sh/setup-uv@v7, etc.) should use a commit SHA. - MEDIUM – Least‑privilege
GITHUB_TOKEN– workflows lack explicitpermissions:. - MEDIUM – No dependency‑vulnerability scan – add
dependency-review-actionorosv-scanner. - LOW – Missing job timeouts – set
timeout-minutes:in.github/workflows/*.yaml.
No lockfile is present (pyproject.toml only), which makes reproducible builds non‑deterministic.
The Bottom Line
monopoly delivers a functional PDF‑to‑CSV pipeline with extensive bank coverage and a usable CLI. The codebase is well‑structured but suffers from deep nesting, duplicated adapter logic, and modest CI hardening. It is suitable for teams that need quick PDF statement extraction and are comfortable tightening the CI configuration and refactoring the bank adapters for maintainability.