interox
Interactive outcome explanation for process-mining case logs.
interox helps you find out why some cases of a process end the way they do.
You hand it a case log and a boolean outcome; it suggests DNF rules (via a fork
of RIPPER rule induction) that tell the outcome cases apart from the rest, and
gives you notebook widgets to inspect, store, edit and build rules until the
outcome is explained.
uv add interox # or: pip install interox
The widgets ship pre-built inside the package, so using interox needs no Node or JavaScript toolchain.
Use
from interox import InterOX
iox = InterOX(outcome, case_log) # outcome: bool Series, case_log: DataFrame, same index
iox.get_exp_status() # how much of the outcome the stored rules explain
iox.get_suggested_ruleset() # induced suggestions, as a table widget
iox.get_stored_ruleset() # the rules you kept
iox.get_rule_builder() # build a rule from scratch
Rendering needs a notebook that speaks anywidget: marimo, Jupyter, VS Code. The API reference is generated from the docstrings:
uv run pdoc interox
A complete walkthrough on a public event log lives in interox-nodebook-sepsis.
Requirements
Python 3.13+ and pandas 3. Case-log enrichment comes from
eclear and rule induction from
rulelearn-ripper-interactive
(imported as rulelearn_ri); both install as dependencies.
Development
Everything Python goes through uv:
uv sync # creates .venv with interox (editable) + the dev group
./code-check.sh # pylint + mypy + pytest
The widgets are React 18 + TypeScript in js/, bundled with esbuild into
interox/static/*.js:
cd js
npm ci # install React, esbuild, TypeScript tooling
npm run build # minified production bundles → ../interox/static/
npm run dev # watch mode, unminified, for working on a widget
npm run typecheck # esbuild does not typecheck; run this separately
See DEVELOPMENT.md for the architecture, DECISIONS.md for why it is shaped this way, and ubiquitous-language.md for the agreed vocabulary.
How the compiled widgets get into the package
Each widget class points _esm at a file next to it, e.g.
pathlib.Path(__file__).parent / "static" / "rule_builder.js". anywidget reads
that file when the widget is created, so the bundle has to be inside the
installed package. Two things make sure it is:
interox/static/*.jsis committed on purpose (the same choice as eclear). Whatever is in git is what a build from a clean checkout ships, and a notebook user never runs npm.[tool.hatch.build.targets.wheel] packages = ["interox"]puts the wholeinterox/directory,static/included, into the wheel.js/andtests/stay out of the wheel; the sdist carries them so it can be rebuilt.
So the rule is: whenever anything under js/src changes, run
npm run build and commit the regenerated interox/static/*.js together with
the source change. A stale bundle is the one packaging mistake that goes
unnoticed until someone opens a notebook.
Releasing to PyPI
Run from the repo root. Nothing here is automated: an upload is irreversible, so each step is yours to run.
1. Rebuild the widgets and re-verify against exactly what you will ship:
(cd js && npm ci && npm run typecheck && npm run build)
git status --short interox/static # changed bundles? commit them first
./code-check.sh
2. Bump the version in pyproject.toml (version = "0.1.0"). A version
number can never be reused on PyPI, even after deleting the release.
3. Build the distributions:
rm -rf dist/
uv build
uvx twine check dist/*
unzip -l dist/interox-*.whl
The wheel listing must contain the four bundles
interox/static/{rule_detail,ruleset_table,explanation_status,rule_builder}.js
and must not contain js/, node_modules or tests/.
4. Smoke-test the built wheel from a directory that is not this repo,
otherwise Python imports the local interox/ folder and you have tested
nothing:
cd /tmp
uv run --isolated --no-project --with ~/Developer/interox/dist/interox-0.1.0-py3-none-any.whl \
python -c "import interox, interox.rule_builder_widget as w; print(interox.__file__); print(len(str(w.RuleBuilderWidget._esm)))"
The path must be inside a site-packages, and the length must be well above
zero: that is the bundle anywidget will send to the notebook.
5. Dry run on TestPyPI (separate site, separate account and token: https://test.pypi.org/manage/account/token/):
uv publish --publish-url https://test.pypi.org/legacy/
cd /tmp
uv run --isolated --no-project \
--index https://test.pypi.org/simple/ --index-strategy unsafe-best-match \
--with interox python -c "import interox; print(interox.__file__)"
The extra index flags are needed because the dependencies come from real PyPI.
6. Publish for real. Create a token at https://pypi.org/manage/account/token/. For a project that does not exist yet it must be account-scoped; after the first upload, replace it with a project-scoped one.
read -rs UV_PUBLISH_TOKEN && export UV_PUBLISH_TOKEN && uv publish
7. Tag the release:
git tag v0.1.0
git push origin main --tags
Later, PyPI trusted publishing (a GitHub Actions workflow that uploads on a pushed tag, no stored token) removes the token step: https://pypi.org/manage/project/interox/settings/publishing/.
Metadata
Release files for interox 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| interox-0.1.0.tar.gz | 322.0 kB | Without provenance | Details for interox-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| interox-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for interox-0.1.0-py3-none-any.whl |
Total release size: 575.7 kB
Release files / interox-0.1.0.tar.gz
| Download URL | interox-0.1.0.tar.gz |
|---|---|
| Size | 322.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
09b56144697d2db7363f48f0f232ec3b56c0410dcb6ce12d7d3c3a4ddca3090f
|
|
BLAKE2b-256 checksum How to use checksums |
1853bb2de0b44accf775185e3559071719e14955420fd211fc8f1085c1feff13
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.12 {"installer":{"name":"uv","version":"0.11.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"44","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / interox-0.1.0-py3-none-any.whl
| Download URL | interox-0.1.0-py3-none-any.whl |
|---|---|
| Size | 253.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2449fda7114aae011b9e430a4fe1ad3545cae06a48876e98575430faee3c8385
|
|
BLAKE2b-256 checksum How to use checksums |
4de6d9de64cdba776bc058c4a72c9421f37745ce8b004eb22e472cd117df0e08
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.12 {"installer":{"name":"uv","version":"0.11.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"44","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for interox 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.