pocketlog-importer

pocketlog-importer

Docker app from Anym001's Repository

Overview

PocketLog Importer is a companion container for PocketLog (https://github.com/anym001/pocketlog) that turns bank CSV exports into PocketLog transactions automatically. It has no web interface - it runs as a background service. HOW IT WORKS: Drop a bank CSV into /data/input. An internal scheduler (cron, default hourly) auto-detects the bank format (easybank, dadat), parses and normalises each booking, then matches it against a regex rules whitelist (config/rules.yaml). A booking that matches a rule is enriched (description, category, tags) and imported into PocketLog via its CSV import API. A booking that matches no rule is dropped and written to a *.unmatched.csv for review - only curated bookings reach PocketLog. PocketLog deduplicates server-side, so re-runs are safe. Processed originals move to /data/processed; files that fail to parse or import move to /data/failed. You can also trigger a run on demand with the User Scripts plugin: docker exec pocketlog-importer pocketlog-import --once (add --dry-run to write output CSVs without importing). SETUP: In PocketLog create an API key with the "import" scope and provide it via POCKETLOG_API_KEY. On first start the container seeds config/config.yaml and config/rules.yaml from bundled examples (it logs a WARNING) - then edit config/config.yaml to set pocketlog.base_url (or use POCKETLOG_BASE_URL), edit config/rules.yaml to match your bookings, and restart. STORAGE: Map /config (config.yaml, rules.yaml, optional logs) and /data (input, output, processed, failed). Set PUID=99 / PGID=100 so the files are owned by nobody:users on Unraid. NOTIFICATIONS (optional): Run-outcome push notifications to any Gotify-compatible endpoint (PushBits, Gotify). Set notify.url in config.yaml and provide the application token via NOTIFY_TOKEN. Project: https://github.com/anym001/pocketlog-importer

PocketLog Importer

Tests License: AGPL v3 Release GHCR Docker Hub

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)
  1. The container runs an internal scheduler (cron, default hourly).
  2. Each *.csv in /data/input is auto-detected (easybank / dadat), parsed and normalised (amount always positive, direction in type).
  3. 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.csv for review so you can add a rule later.
  4. Matched bookings are written to /data/output/<bank>-<ts>.csv and imported via POST /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.
  5. The processed original is moved to /data/processed/. Files that fail to parse or import go to /data/failed/.

Quick start

  1. Create an API key in PocketLog (UI → API keys) with the import scope.
  2. Prepare the host folders and start the container (see docker/docker-compose.example.yml):
    mkdir -p config data/input
    POCKETLOG_API_KEY=plk_xxx docker compose -f docker/docker-compose.example.yml up -d
    
    On first start the container seeds config/config.yaml and config/rules.yaml from the bundled examples if they are missing (it logs a WARNING). Then edit config/config.yaml → set pocketlog.base_url, edit config/rules.yaml to match your bookings, and restart.

    To configure before the first start instead, copy the examples yourself: cp config/config.example.yaml config/config.yaml and cp config/rules.example.yaml config/rules.yaml.

  3. 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.

Open the Apps tab on your Unraid server Search Community Apps for pocketlog-importer Review the template variables and paths Click Install

Download Statistics

68
Total Downloads

Details

Repository
ghcr.io/anym001/pocketlog-importer:latest
Last Updated2026-06-15
First Seen2026-06-13

Runtime arguments

Network
bridge
Shell
sh
Privileged
false
Extra Params
--restart=unless-stopped

Template configuration

App Config DirPathrw

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
Data DirPathrw

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
POCKETLOG_API_KEYVariable

Required for real imports: a PocketLog API key with the import scope (created in PocketLog under API keys). Never stored in YAML.

PUIDVariable

Host user ID that owns the files under /config and /data (Unraid: 99)

Default
99
Value
99
PGIDVariable

Host group ID for the files under /config and /data (Unraid: 100)

Default
100
Value
100
TZVariable

Container timezone (affects the scheduler cron times)

Default
Europe/Berlin
Value
Europe/Berlin