All apps · 0 apps
pocketlog-importer
Docker app from Anym001's Repository
Overview
Readme
View on GitHubPocketLog Importer
A small Docker container that turns bank CSV exports (easybank, dadat) into PocketLog transactions. You drop a CSV into a folder, a rules whitelist decides what gets imported (description, category, tags), and the result is pushed to PocketLog via its CSV import API.
Images are published to GHCR and Docker Hub — use whichever you prefer
(image: in your compose file):
ghcr.io/anym001/pocketlog-importer:<tag>
anym001/pocketlog-importer:<tag> # Docker Hub
How it works
bank export ─▶ /data/input ─▶ parse ─▶ rules.yaml (whitelist) ─▶ /data/output ─▶ PocketLog API
│
└─ no match ─▶ <bank>.unmatched.csv (review)
- The container runs an internal scheduler (cron, default hourly).
- Each
*.csvin/data/inputis auto-detected (easybank / dadat), parsed and normalised (amount always positive, direction intype). - Every booking is matched against
rules.yaml(regex, case-insensitive, first match wins). A booking that matches no rule is dropped — only curated bookings reach PocketLog. Dropped bookings are written to a*.unmatched.csvfor review so you can add a rule later. - Matched bookings are written to
/data/output/<bank>-<ts>.csvand imported viaPOST /api/import/csv. PocketLog deduplicates, so re-runs are safe. Transient API failures (network errors, 5xx, 429) are retried with exponential backoff before a file counts as failed. - The processed original is moved to
/data/processed/. Files that fail to parse or import go to/data/failed/.
Quick start
- Create an API key in PocketLog (UI → API keys) with the
importscope. - Prepare the host folders and start the container (see
docker/docker-compose.example.yml):
On first start the container seedsmkdir -p config data/input POCKETLOG_API_KEY=plk_xxx docker compose -f docker/docker-compose.example.yml up -dconfig/config.yamlandconfig/rules.yamlfrom the bundled examples if they are missing (it logs a WARNING). Then editconfig/config.yaml→ setpocketlog.base_url, editconfig/rules.yamlto match your bookings, and restart.To configure before the first start instead, copy the examples yourself:
cp config/config.example.yaml config/config.yamlandcp config/rules.example.yaml config/rules.yaml. - Drop a bank CSV into
data/input/. The scheduler picks it up; or trigger it immediately:docker exec pocketlog-importer pocketlog-import --once
Try it safely first (dry-run)
--dry-run writes the output CSVs but does not import anything:
docker exec pocketlog-importer pocketlog-import --once --dry-run
Triggering
Three equivalent ways to run the pipeline:
| Method | Command |
|---|---|
| Automatic | internal scheduler (schedule.cron in config.yaml) |
| On demand | docker exec pocketlog-importer pocketlog-import --once |
| Test | ... pocketlog-import --once --dry-run |
The --once path is ideal for Unraid User Scripts. A file lock prevents a
manual run from overlapping with a scheduler tick.
Container health
Every run (idle ones included) touches a heartbeat file. The image's
HEALTHCHECK runs pocketlog-import --healthcheck, which reports unhealthy
once the heartbeat is older than ~2 cron intervals — so a wedged scheduler
shows up directly in docker ps / the Unraid dashboard instead of going
unnoticed. The threshold adapts to schedule.cron automatically.
Configuration
config/config.yaml — see config/config.example.yaml.
The PocketLog API key is never stored in YAML; provide it via the
POCKETLOG_API_KEY environment variable.
Rules
config/rules.yaml — see config/rules.example.yaml.
rules:
- match: "STREAMINGCO" # regex, case-insensitive, tested against booking text
description: "Streaming Service" # overrides description (default: raw booking text)
category: "Entertainment" # PocketLog category (auto-created if new)
tags: [subscription] # tags (auto-created if new)
# type: in # optional, overrides the amount-sign direction
# bank: easybank # optional, restrict to one parser
Rules are evaluated top to bottom; the first matching rule wins.
Notifications
Optional push notifications about run outcomes via any Gotify-compatible
endpoint — this includes PushBits
(relays to Matrix) and Gotify itself. Off unless notify.url is set:
notify:
type: gotify # PushBits + Gotify
url: https://pushbits.example.com
events: problems # problems (default) | always
The application token goes into the NOTIFY_TOKEN environment variable —
never into YAML. events: problems notifies only on failed files, unmatched
bookings, or a crashed run (high priority); events: always also reports
clean runs. Idle runs (empty input directory) and dry-runs never notify, and
notifications carry only counters and filenames — no booking data.
Notification delivery is best-effort: a failed push is logged and never
affects the import itself.
Environment variables
| Variable | Default | Purpose |
|---|---|---|
POCKETLOG_API_KEY |
— | Required for real imports (import scope key) |
POCKETLOG_BASE_URL |
— | Optional override of pocketlog.base_url |
NOTIFY_TOKEN |
— | Application token for notify.url (PushBits/Gotify) |
PUID / PGID |
1000 |
Ownership of /config + /data (Unraid: 99 / 100) |
LOG_LEVEL |
INFO |
Log verbosity |
LOG_FORMAT |
text |
Log format: text or json (one JSON object per line) |
LOG_FILE |
— | Optional rotating log file, e.g. /config/logs/importer.log |
LOG_FILE_MAX_BYTES |
1048576 |
Rotation size |
LOG_FILE_BACKUPS |
5 |
Rotated copies kept |
Volumes
| Path | Contents |
|---|---|
/config |
config.yaml, rules.yaml, optional logs/ |
/data/input |
drop bank CSVs here |
/data/output |
generated PocketLog CSVs + *.unmatched.csv |
/data/processed |
successfully processed originals, one subdirectory per run |
/data/failed |
files that failed to parse or import, one subdirectory per run |
Supported banks
| Bank | File | Format |
|---|---|---|
| easybank | EASYBANK_Umsatzliste_*.csv |
no header, 6 cols, DD.MM.YYYY, -13,99 |
| dadat | umsaetzegirokonto_*.csv |
header, 27 cols, YYYY-MM-DD, -200,00 |
Adding a bank = a new parser in pocketlog_importer/parsers/ (implement sniff +
parse) registered in parsers/__init__.py.
Development
python -m venv .venv && . .venv/bin/activate
pip install -r requirements-dev.txt && pip install -e .
Lint and test commands (= CI) and the branching/release flow are in
CONTRIBUTING.md.
Contract tests
tests/integration/ runs the real pipeline against a real PocketLog container
and pins the import API contract (round-trip, dedup idempotency, per-row error
format, auth scopes). Requires Docker; excluded from the default pytest -q
run:
pytest -m integration # released image
POCKETLOG_IMAGE=ghcr.io/anym001/pocketlog:dev pytest -m integration
CI runs them on every PR against the released image, and nightly against
:latest + :dev (contract.yml) to catch contract drift from the PocketLog
side before it is released.
License
Licensed under the GNU Affero General Public License v3.0 or later
(AGPL-3.0-or-later), the same license as the companion
pocketlog project. See
LICENSE for the full text.
Built with Claude Code.
Install pocketlog-importer on Unraid in a few clicks.
Find pocketlog-importer in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.
Download Statistics
Details
ghcr.io/anym001/pocketlog-importer:latestRuntime arguments
- Network
bridge- Shell
sh- Privileged
- false
- Extra Params
--restart=unless-stopped
Template configuration
Persistent app directory. Holds config.yaml, rules.yaml and optional log files (/config/logs). On first start the example config and rules are seeded here if missing.
- Target
- /config
- Default
- /mnt/user/appdata/pocketlog-importer/config
- Value
- /mnt/user/appdata/pocketlog-importer/config
Work directory. Drop bank CSVs into data/input; generated PocketLog CSVs and *.unmatched.csv land in data/output; processed originals in data/processed; files that failed to parse or import in data/failed.
- Target
- /data
- Default
- /mnt/user/appdata/pocketlog-importer/data
- Value
- /mnt/user/appdata/pocketlog-importer/data
Required for real imports: a PocketLog API key with the import scope (created in PocketLog under API keys). Never stored in YAML.
Host user ID that owns the files under /config and /data (Unraid: 99)
- Default
- 99
- Value
- 99
Host group ID for the files under /config and /data (Unraid: 100)
- Default
- 100
- Value
- 100
Container timezone (affects the scheduler cron times)
- Default
- Europe/Berlin
- Value
- Europe/Berlin