Note
Go to the end to download the full example code.
20: The command line¶
Run the whole pipeline from a shell, with no Python, and read the JSON summary it prints.
Introduction¶
Everything the tutorials did in Python is also a shell command. The evoparam console
script has thirteen sub-commands, for a pipeline you can put in a batch script, a cluster
job or a Makefile without writing any Python:
simulatewrites a synthetic BIDS dataset with a known truth;detect-eventsfinds the pulses in a continuous file;cleanremoves the artifact;testruns the existence test;fitdoes the whole chain (read, clean, reference, fit, test) and writes BIDS derivatives;reportmakes an HTML, PDF or Markdown report;validate-bids,reproduce,migrate,info,presetsandcite.
Every command takes --json (one JSON summary on stdout), --random-state and
--config (a JSON or TOML file of options). The exit code says what happened:
0 success, 2 usage error, 3 partial (some channel failed), 4 a missing optional package,
5 a bad input, 6 a reproduction mismatch. We drive it here with subprocess, exactly
as a shell would.
import json
import subprocess
import sys
import tempfile
from pathlib import Path
work = Path(tempfile.mkdtemp(prefix="evoparam-cli-"))
def evoparam(*args):
"""Run one ``evoparam`` command with ``--json`` and return its parsed summary."""
proc = subprocess.run(
[sys.executable, "-m", "evoparam", *map(str, args), "--json"],
capture_output=True, text=True, check=False,
) # fmt: skip
summary = json.loads(proc.stdout.splitlines()[-1])
cmd = " ".join(map(str, args))
print(f"$ evoparam {cmd} -> {summary['status']} (exit {proc.returncode})")
return summary
1. Make a dataset¶
simulate writes a small BIDS dataset with ground truth, so the rest of the tutorial
needs no data of your own. In real use you point fit at your own BIDS root instead.
evoparam("simulate", "--modality", "ccep", "--n-trials", "10", "--n-channels", "4",
"--seed", "0", "--out", work / "bids") # fmt: skip
$ evoparam simulate --modality ccep --n-trials 10 --n-channels 4 --seed 0 --out /tmp/evoparam-cli-8hsp1487/bids -> ok (exit 0)
{'command': 'simulate', 'version': '0.1.0.dev0', 'status': 'ok', 'exit_code': 0, 'outputs': ['/tmp/evoparam-cli-8hsp1487/bids/sub-01/ieeg/sub-01_task-ccep_ieeg.vhdr'], 'n_units': {'ok': 0, 'no_response': 0, 'failed': 0, 'skipped': 0}, 'warnings': [], 'duration_s': 1.026036268000098}
2. Check the stimulation metadata, then fit¶
validate-bids reports, run by run, which stimulation fields are present, inferred,
missing or inconsistent. Then fit runs the whole chain and writes the derivatives.
summary = evoparam("validate-bids", work / "bids")
print(" metadata:", summary["result"]["counts"])
summary = evoparam("fit", work / "bids", "--sub", "01", "--task", "ccep", "--preset", "ccep",
"--random-state", "0",
"--out", work / "bids/derivatives/evoparam") # fmt: skip
print(" units:", summary["n_units"])
print(" wrote:", len(summary["outputs"]), "files")
$ evoparam validate-bids /tmp/evoparam-cli-8hsp1487/bids -> ok (exit 0)
metadata: {'present': 6, 'missing': 3, 'inferred': 1}
$ evoparam fit /tmp/evoparam-cli-8hsp1487/bids --sub 01 --task ccep --preset ccep --random-state 0 --out /tmp/evoparam-cli-8hsp1487/bids/derivatives/evoparam -> ok (exit 0)
units: {'ok': 3, 'no_response': 1, 'failed': 0, 'skipped': 0}
wrote: 15 files
3. Report, and the JSON summary¶
report turns the derivatives into a Markdown (or HTML, or PDF) report. The --json
object is the same on every command: status, exit_code, outputs, n_units
(ok, no_response, failed, skipped), warnings and duration_s. A script reads that
object to decide what to do next, and the exit code to stop on an error.
summary = evoparam("report", work / "bids/derivatives/evoparam", "--format", "md",
"--out", work / "report") # fmt: skip
print("\nfull JSON summary of the last command:")
keys = ("command", "status", "exit_code", "n_units")
print(json.dumps({key: summary[key] for key in keys}, indent=2))
$ evoparam report /tmp/evoparam-cli-8hsp1487/bids/derivatives/evoparam --format md --out /tmp/evoparam-cli-8hsp1487/report -> ok (exit 0)
full JSON summary of the last command:
{
"command": "report",
"status": "ok",
"exit_code": 0,
"n_units": {
"ok": 0,
"no_response": 0,
"failed": 0,
"skipped": 0
}
}
The same four lines, as a shell script, are:
evoparam simulate --modality ccep --n-trials 10 --n-channels 4 --seed 0 --out bids/
evoparam validate-bids bids/
evoparam fit bids/ --sub 01 --task ccep --preset ccep --out bids/derivatives/evoparam
evoparam report bids/derivatives/evoparam --format html
evoparam fit bids/ ... writes exactly the same files as the Python
write_bids_derivatives(fit(read_bids(...))), so you can prototype in Python and run in
a shell, or the other way round.
Conclusion¶
The command line is the whole package without Python: simulate for test data,
fit for the pipeline, report for the write-up, and the smaller commands for
events, cleaning, the existence test, validation, reproduction and citation. --json
plus the exit codes make it safe to script over many subjects on a cluster.
Total running time of the script: (0 minutes 5.545 seconds)