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.

Download Statistics

1,323
Total Downloads

Details

Repository
ghcr.io/anym001/pocketlog-importer:latest
Last Updated2026-09-01
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.

POCKETLOG_BASE_URLVariable

Optional override of the PocketLog base URL used for the CSV import API (e.g. http://pocketlog:8000). If empty, the value from config.yaml (pocketlog.base_url) is used.

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
LOG_LEVELVariable

Log verbosity: DEBUG, INFO, WARNING or ERROR.

Default
INFO
Value
INFO
LOG_FORMATVariable

Log output format: text or json.

Default
text
Value
text
LOG_FILEVariable

Optional path to a log file inside /config/logs (e.g. /config/logs/pocketlog-importer.log). Leave empty to log to stdout only.