Run OrionBelt Semantic Layer query batches on a schedule and emit reports.
Project description
OrionBelt Runner
Run OrionBelt Semantic Layer query batches and emit reports.
A run is a YAML document combining:
- An OBSL endpoint (base URL, optional auth, optional locale/timezone, optional model to load)
- A list of named queries — any valid OBML query body
- A report config — markdown, HTML, or PDF output with sections bound to queries
Numeric and timestamp cells are pre-rendered server-side using each column's format pattern from the OBML model (the runner sends format_values=true on every query), so reports show e.g. 1.853.429,67 for locale: de without any client-side formatting. See examples/monthly-revenue-2026-04-29.md (markdown), examples/monthly-revenue-2026-04-29.html (HTML), and examples/monthly-revenue-2026-04-29.pdf (PDF) for sample outputs.
Status
Early scaffold (v0.4.0). Markdown, HTML, and PDF reports, with optional per-query TSV exports and an always-on YAML run log sidecar. No scheduler yet — drive it from cron / systemd / GitHub Actions / Cloud Scheduler / etc.
Install
uv sync # core: markdown + HTML reports
uv sync --extra pdf # also enable PDF output (requires Pango / Cairo)
PDF output needs WeasyPrint, which depends on system libraries (Pango,
Cairo, GDK-Pixbuf). On macOS: brew install pango. On Debian / Ubuntu:
apt install libpango-1.0-0 libpangoft2-1.0-0. See the
WeasyPrint install guide
for other platforms. Skip the extra if you only need markdown / HTML.
Apple Silicon note: Homebrew installs libraries to
/opt/homebrew/lib, which Python's loader doesn't search by default. If WeasyPrint can't findlibgobject-2.0-0, prefix the runner with:DYLD_LIBRARY_PATH=/opt/homebrew/lib:/usr/local/lib \ DYLD_FALLBACK_LIBRARY_PATH=/opt/homebrew/lib:/usr/local/lib \ uv run orionbelt-runner run spec.yaml
DYLD_FALLBACK_LIBRARY_PATHis often enough, but setting both variables covers Python/cffi environments that calldlopen()with bare library names such aslibgobject-2.0-0.
Run
uv run orionbelt-runner run examples/monthly-revenue.yaml
Override the OBSL endpoint without editing the spec:
uv run orionbelt-runner run examples/monthly-revenue.yaml \
--base-url http://my-obsl:8080
Or via env (.env or shell):
OBSL_BASE_URL=http://my-obsl:8080 \
OBSL_API_TOKEN=... \
uv run orionbelt-runner run examples/monthly-revenue.yaml
Server expectations
The runner calls /v1/query/execute, so OBSL needs to be configured to execute queries (not just compile them):
QUERY_EXECUTE=true(orFLIGHT_ENABLED=true)- DB driver credentials configured for the dialect(s) you query
Three deployment shapes are supported, in order of preference:
- Single-model mode (
MODEL_FILE=...on the server). Spec leavesobsl.modelandobsl.model_idunset; the runner uses top-level shortcut endpoints. - Multi-model server with a model already loaded. Set
obsl.model_idin the spec; the runner still uses shortcut endpoints and keys into the named model. - Runner-loaded model. Set
obsl.model.yaml_pathin the spec — the runner creates a session, posts the model to/v1/sessions/{id}/models, runs queries against/v1/sessions/{id}/query/execute, and deletes the session in afinallyblock. Useful for ad-hoc reports against a model you keep next to the spec file.
Spec format
See examples/monthly-revenue.yaml for a full spec.
name: Monthly Revenue
obsl:
base_url: http://localhost:8080
locale: de # optional — BCP-47, drives display formatting
# timezone: Europe/Berlin # optional — IANA TZ
# model_id: sales # multi-model server with a pre-loaded model
# model: # OR: load your own model into a fresh session
# yaml_path: ./sales.obml.yaml # path is resolved relative to this spec file
# extends: [./fragments/dim-time.yaml]
queries:
- name: total_revenue
dialect: postgres
query:
select:
measures: [Total Revenue]
report:
format: markdown
output: reports/{name}-{date}.md
title: "Monthly Revenue — {date}"
sections:
- heading: Headline number
query: total_revenue
render: value
Section render modes:
render |
Output |
|---|---|
table |
Table of all rows |
value |
Single bold value (first numeric column of first row by default) |
list |
Bullet list of one column |
Path placeholders are accepted in report.output, report.title, report.intro, and report.footer. The instant comes from OBSL's GET /v1/settings (server clock + effective TZ); falls back to the runner's UTC clock when settings is unreachable.
| Placeholder | Example | Notes |
|---|---|---|
{name} |
Monthly Revenue |
the spec name |
{date} |
2026-04-29 |
YYYY-MM-DD in the resolved TZ |
{datetime} |
2026-04-29T18-02-06Z |
filesystem-safe; trailing Z only when TZ is UTC |
{time} |
18:02:06 |
colons are unsafe on Windows paths — use {time_filename} |
{time_filename} |
18_02_06 |
filesystem-safe |
{tz} |
Europe/Berlin |
IANA name |
{tz_filename} / {timezone} |
Europe, Berlin |
/ replaced with , for path safety |
{runner_version} |
0.4.0 |
the OrionBelt Runner version that produced this report |
report.footer additionally accepts result-derived counters: {number_of_queries}, {number_of_sections}, {number_of_rows} (camelCase aliases — {numberOfQueries} etc. — also work).
Queries from a folder
For larger query libraries, point at a directory instead of (or in addition to) inline queries::
queries_dir: ./queries # recursive *.yaml + *.yml, alpha-sorted
queries: # optional, runs after dir queries in spec order
- name: ad_hoc
query: { ... }
Each file is a full QuerySpec (name, dialect, query, optional description). When name: is omitted the filename stem is used verbatim — total-revenue.yaml → total-revenue. Duplicate names across the dir + inline list raise an error at load time. Paths resolve relative to the spec file.
Outputs
A run produces up to three artefacts in the report directory:
reports/monthly-revenue-2026-04-29.md ← report
reports/monthly-revenue-2026-04-29.run.yaml ← run log (always)
reports/monthly-revenue-2026-04-29_exports/ ← TSV exports (opt-in)
├── total_revenue.tsv
├── revenue_by_nation.tsv
└── top_orders_raw.tsv
Report — markdown, HTML, or PDF
format: markdown (default) writes a .md. format: html writes a self-contained HTML5 document with inline default CSS and a light/dark theme — no external assets, so the file works when emailed, opened from disk, or served from a static host. The output extension follows the template you set in output:.
report:
format: html
output: reports/{name}-{date}.html
title: "Monthly Revenue — {date}"
See examples/monthly-revenue-2026-04-29.html for a rendered HTML sample.
format: pdf reuses the same HTML pipeline and runs the result through WeasyPrint — so PDF layout stays automatically in lockstep with the HTML output. A print-only stylesheet adds page margins and a page n / total footer; section headings (h2) get a page-break-before: auto / page-break-after: avoid hint so tables don't get orphaned.
report:
format: pdf
output: reports/{name}-{date}.pdf
title: "Monthly Revenue — {date}"
pdf_page_size: A4 # "A4" (default) or "A3"
pdf_orientation: portrait # "portrait" (default) or "landscape"
pdf_page_size and pdf_orientation are ignored for markdown / html output. Reach for A3 or landscape when a table has many columns or wide cell values that wrap awkwardly in A4 portrait — the same content, just more horizontal room.
PDF requires the optional pdf extra (uv sync --extra pdf) and WeasyPrint's system libraries — see Install. See examples/monthly-revenue-2026-04-29.pdf for a rendered PDF sample.
Run log (YAML sidecar) — always written
Always emitted next to the report at <report-stem>.run.yaml, even when queries fail (when it's most useful). Captures, for each query: the compiled SQL as a literal block scalar, the OBSL explain plan (planner + reasons + joins + CFL legs), the resolved info (fact tables / dimensions / measures), wall-clock and server-side timing, warnings, and any errors. The file header records the spec name, OBSL version / api_version, session/model IDs, and the report path.
YAML so it's both human-skimmable and trivially machine-parseable in one step. Useful for debugging, audit trails, and downstream tooling that wants the SQL or the plan.
See examples/monthly-revenue-2026-04-29.run.yaml.
Per-query TSV exports — opt-in
Set report.export_results: true to write each query's rows as TSV into a sibling <report-stem>_exports/ directory:
report:
output: reports/{name}-{date}.md
export_results: true
One file per query, named after the query and sanitised to safe path chars. TSV uses \t separator and \n line endings; cells with embedded tabs / newlines / quotes are quoted (compatible with pandas.read_csv(..., sep='\t')). Cells reflect the same format_values=true data the report shows. Exports are only written on a fully successful run.
See examples/monthly-revenue-2026-04-29_exports/.
Architecture
The runner talks to OBSL through a small ObslClient Protocol. One implementation today (HTTP). Tests can drop in a fake; an in-process implementation can be added later without touching the runner, report, or CLI code.
spec.yaml ──▶ load_spec ──▶ Runner ──▶ ObslClient ──▶ OBSL (HTTP)
│
├─▶ render_markdown / render_html / render_pdf ──▶ report.md|html|pdf
├─▶ render_runlog ──▶ report.run.yaml
└─▶ render_tsv (× N) ──▶ report_exports/*.tsv
License
BSL-1.1 (mirrors OrionBelt Semantic Layer).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file orionbelt_runner-0.4.0.tar.gz.
File metadata
- Download URL: orionbelt_runner-0.4.0.tar.gz
- Upload date:
- Size: 61.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
42f73e984ee9c0b5868e2ca0f01186be277cc268c58a77b10609436ca9d92dd0
|
|
| MD5 |
d2ed83fdd562076d9ce538826edaf2b9
|
|
| BLAKE2b-256 |
f5e4cec797a382732175ca050938080aa1aa9ff87b79166e28dff5ecf6d5510b
|
File details
Details for the file orionbelt_runner-0.4.0-py3-none-any.whl.
File metadata
- Download URL: orionbelt_runner-0.4.0-py3-none-any.whl
- Upload date:
- Size: 29.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8156d69cef2083e4f1e7998170f3d473854bc5da6aea665cb480bb8afdd73aaa
|
|
| MD5 |
03caf3ccf699b3cb61933be37a2f6942
|
|
| BLAKE2b-256 |
900619c9bb23f9f15ebedde450b06fa7c2f9ff6ec7871b15efb3d22c00129f74
|