No description
  • HTML 44.1%
  • Python 31.3%
  • TypeScript 21.1%
  • Shell 1.3%
  • Just 1%
  • Other 1.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Markus Walther 658362f7ea
All checks were successful
ci / ci (push) Successful in 3m55s
test: traceability lists the data import tests for I3 and I10
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 11:43:46 +01:00
.claude/skills/project-handbook chore: bootstrap repository with spec, standing instructions and ADRs 2026-10-20 18:15:48 +02:00
.forgejo/workflows ci: buildx for BuildKit; browser tests only via just test-browser 2026-10-27 08:16:33 +01:00
backend fix(data): keep a channel's enabled flag when its secret is not in the export 2026-10-30 11:41:48 +01:00
browser feat(ingest): browser fetch kind via a Chromium sidecar; esyoil; M4 complete 2026-10-21 01:00:05 +02:00
docs docs: guide for moving data to a new installation (de, en) 2026-10-30 11:43:42 +01:00
fixtures Merge branch 'worktree-agent-a7b6913da1e032f92': tanke günstig and FastEnergy 2026-10-27 09:19:32 +01:00
frontend feat(web): 'Daten übertragen' section on the sources page (ADR-0015) 2026-10-30 11:40:15 +01:00
installer feat(install): build on the host network where image builds get no DNS 2026-10-30 11:26:24 +01:00
prompts/extract-rule feat(llm): rule finder on JSON responses, prompt v2 (ADR-0014) 2026-10-27 08:56:33 +01:00
proxy fix: invariant gaps in ingest and installer review findings 2026-10-21 02:10:40 +02:00
schemas feat(llm): extraction-rule finder with five backends (ADR-0009) 2026-10-21 01:43:34 +02:00
scripts ci: glibc job container, no bind mounts of the checkout 2026-10-27 08:06:55 +01:00
seeds Merge branch 'worktree-agent-a7b6913da1e032f92': tanke günstig and FastEnergy 2026-10-27 09:19:32 +01:00
spikes Merge branch 'worktree-agent-a7b6913da1e032f92': tanke günstig and FastEnergy 2026-10-27 09:19:32 +01:00
tests test: traceability lists the data import tests for I3 and I10 2026-10-30 11:43:46 +01:00
.dockerignore feat(web): React dashboard, sources, averages with the kss design system 2026-10-21 00:14:08 +02:00
.env.example feat(install): build on the host network where image builds get no DNS 2026-10-30 11:26:24 +01:00
.gitattributes chore: bootstrap repository with spec, standing instructions and ADRs 2026-10-20 18:15:48 +02:00
.gitignore chore: ignore agent worktrees 2026-10-27 08:42:48 +01:00
.gitleaks.toml build: compose stack, images, justfile and quality gates 2026-10-20 19:08:06 +02:00
.yamllint.yaml build: compose stack, images, justfile and quality gates 2026-10-20 19:08:06 +02:00
CHANGELOG.md docs: guide for moving data to a new installation (de, en) 2026-10-30 11:43:42 +01:00
CLAUDE.md chore: release 0.2.0 2026-10-27 09:26:12 +01:00
compose.yaml feat(install): build on the host network where image builds get no DNS 2026-10-30 11:26:24 +01:00
DESIGN_SPEC.md docs(adr): ADR-0015 data export and import as one JSON document 2026-10-30 11:38:33 +01:00
install.sh feat(install): build on the host network where image builds get no DNS 2026-10-30 11:26:24 +01:00
justfile feat(data): export and import configuration plus samples as JSON (ADR-0015) 2026-10-30 11:38:33 +01:00
LICENSE chore: bootstrap repository with spec, standing instructions and ADRs 2026-10-20 18:15:48 +02:00
openapi.json feat(data): export and import configuration plus samples as JSON (ADR-0015) 2026-10-30 11:38:33 +01:00
README.md docs: admin, concepts and reference pages; generated API reference 2026-10-21 01:47:44 +02:00
ruff.toml docs: handbook (de, en) for M1, runbook, README, changelog, CI 2026-10-20 19:19:23 +02:00
RUN.md feat(install): build on the host network where image builds get no DNS 2026-10-30 11:26:24 +01:00
SPEC_SPEC.md chore: bootstrap repository with spec, standing instructions and ADRs 2026-10-20 18:15:48 +02:00

Ölkurs

Ölkurs is a self-hosted heating-oil price tracker for one household. Several times a day it fetches the price of Heizöl EL from the vendor pages you configure, for the order amounts and postal codes you care about, keeps every observation as a time series, shows the development with moving averages, and pushes prices or charts to your phone through ntfy or Telegram — so you can decide when to fill the tank.

What it does

  • Fetches vendor price calculators: plain pages, forms and JSON endpoints, or JavaScript-only pages through a headless browser; per order amount, postal code and product.
  • Normalises every price to EUR per 100 l including VAT and keeps the raw text for auditing.
  • Refuses doubtful values: no price, an implausible price or an unconfirmed jump of more than 25 % produces no sample; samples are never changed afterwards.
  • Shows the price history with dashed moving averages over days or samples, and the lowest, highest and latest price with the time each was observed.
  • Notifies on a schedule, on a price change, on a new low or high, or when the price crosses an average — as numbers or as a chart — via ntfy or Telegram, never twice for the same event.
  • Keeps snapshots of every response, so extraction rules are fixed against stored pages, not by asking the vendor again, and is polite to vendors (identifying User-Agent, interval floor, one request per series).

The interface and the handbook are German; the handbook is also available in English.

Requirements

A Linux host with Docker and the compose plugin. The installer offers to install just, the command menu. Everything else runs in containers.

Installation

git clone ssh://git@fj16.bastler.space:223/markusw/oelkurs.git
cd oelkurs
./install.sh

The installer asks for the public URL, the time zone, whether to enable the headless browser for JavaScript-only vendors, and the reverse proxy: the bundled Caddy (automatic HTTPS for a public hostname) or your own Caddy, Traefik, nginx or Apache, in Docker or on the host. It writes .env, compose.override.yaml and, for your own proxy, a ready-to-include configuration in deploy/proxy/, then builds and starts everything, creates the first login, imports the example sources and checks that the site answers.

Re-running ./install.sh is safe: the current settings are the defaults and secrets are kept. For a non-interactive install:

cp installer/answers.example.toml answers.toml   # edit it
./install.sh --answers answers.toml

Details for every question and proxy: handbook, Administration → Installation and Reverse proxy.

Running

Open the public URL (default http://localhost:8094) and sign in. Under Quellen you see the example sources (LEU Energie, esyoil); Jetzt abrufen fetches prices right away, the Übersicht shows them. Under Kanäle & Regeln you connect ntfy or Telegram.

Command What it does
just ps / just logs worker state of the services / what the worker does
just fetch-now leu fetch a source now and print the result
just extract-test leu 3000l re-run the extraction rule on the last stored page
just backup / just restore <file> database dump and snapshot archive / restore a dump
just upgrade back up, rebuild, migrate, restart, verify
just install-proxy [--undo] copy the generated snippet into your proxy and reload it (asks first)

just --list shows every command; RUN.md explains them.

Documentation

Development

just test, just check and just ci run the tests and quality gates; E2E_PASSWORD=… just e2e runs the Playwright suite in the official Playwright container. Stack: Python 3.12 (Django 5.2, DRF, APScheduler), PostgreSQL 16, React 19 with TypeScript and Vite, Caddy, Astro Starlight.

Licence

Copyright 2026 Markus Walther. Licensed under the GNU Affero General Public License v3.0. If you run a modified version as a network service, you must offer its source code to its users.