Skip to content

catalog_v1

tit.server.routes.catalog_v1

/api/catalog/* v1 endpoints beyond the v0 subjects/simulations list.

Thin HTTP wrappers over :mod:tit.catalog only -- every discovery/CRUD rule (which files make a montage, how an ROI is named, which run dirs count as complete) lives there on top of :class:tit.paths.PathManager, per docs/dev/DECISIONS.md ยง 2026-08-27 (Electron and the job server) "Design rules" R1. Response bodies are plain dicts rather than a response_model for endpoints where the v1 contract's hand-authored schema does not exactly match what the underlying files contain today (see FreehandConfig below and this lane's final report); FastAPI still serialises them correctly, just without an extra validation pass that would reject real data.

nifti_labels

nifti_labels(subject: Annotated[SubjectId, Query()], path: str | None = Query(None)) -> list[dict]

Browse the labels of a segmentation volume (the sub-cortical exporter's picker).

One 404 covers unknown subject, a path outside the project jail, and a file that is not there -- see :func:tit.catalog.nifti_labels for why they are not distinguished.

Source code in tit/server/routes/catalog_v1.py
@router.get("/api/catalog/nifti/labels", summary="Integer labels of a NIfTI volume")
def nifti_labels(
    subject: Annotated[SubjectId, Query()],
    path: str | None = Query(None),
) -> list[dict]:
    """Browse the labels of a segmentation volume (the sub-cortical exporter's picker).

    One 404 covers unknown subject, a path outside the project jail, and a file that
    is not there -- see :func:`tit.catalog.nifti_labels` for why they are not
    distinguished.
    """
    return _or_404(
        catalog.nifti_labels(_pm(), subject, path),
        f"No readable label volume for {subject}",
    )

flex_run_mapping

flex_run_mapping(run: EntityName, subject: Annotated[SubjectId, Query()], eeg_net: Annotated[EntityName, Query()]) -> dict

Return the run's electrodes as eeg_net's labels, mapping if needed.

A flex run only carries electrode_mapping_<net>.json for the nets it was already mapped onto, so the Simulator's "Map to net" choice would otherwise be limited to those. :func:resolve_flex_montage maps the optimiser's XYZ onto any net of the subject (Hungarian assignment) and caches the result beside the run, so the first request for a new net computes the mapping and every later one -- including flex_runs' mappings list -- reads the file it wrote.

Source code in tit/server/routes/catalog_v1.py
@router.get(
    "/api/catalog/flex-runs/{run}/mapping",
    summary="Map one flex-search run's optimised positions onto an EEG net",
)
def flex_run_mapping(
    run: EntityName,
    subject: Annotated[SubjectId, Query()],
    eeg_net: Annotated[EntityName, Query()],
) -> dict:
    """Return the run's electrodes as *eeg_net*'s labels, mapping if needed.

    A flex run only carries ``electrode_mapping_<net>.json`` for the nets it
    was already mapped onto, so the Simulator's "Map to net" choice would
    otherwise be limited to those. :func:`resolve_flex_montage` maps the
    optimiser's XYZ onto *any* net of the subject (Hungarian assignment) and
    caches the result beside the run, so the first request for a new net
    computes the mapping and every later one -- including ``flex_runs``'
    ``mappings`` list -- reads the file it wrote.
    """
    from tit.sim import montage_sources

    try:
        montage = montage_sources.resolve_flex_montage(
            _pm(), subject, run, "mapped", eeg_net=eeg_net
        )
    except ValueError as exc:
        raise HTTPException(status_code=404, detail=str(exc)) from exc
    return {
        "eeg_net": eeg_net,
        "pairs": [list(pair) for pair in montage.electrode_pairs],
    }

group_stats

group_stats(name: EntityName, type: Annotated[EntityName, Query()]) -> dict

Detail for one derivatives/ti-toolbox/stats/<type>/<name>/ run.

group() above lists these by name and path only, which is all the Results tree needs; the preview pane needs the run's inputs (groups, test, permutations, threshold), its cluster table and its files, and -- for a run that failed before it wrote anything -- the reason, so the pane can say why instead of showing one .log.

Source code in tit/server/routes/catalog_v1.py
@router.get(
    "/api/catalog/group/stats/{name}",
    summary="One group-statistics run: its inputs, its outcome and its files",
)
def group_stats(name: EntityName, type: Annotated[EntityName, Query()]) -> dict:
    """Detail for one ``derivatives/ti-toolbox/stats/<type>/<name>/`` run.

    ``group()`` above lists these by name and path only, which is all the Results tree
    needs; the preview pane needs the run's inputs (groups, test, permutations,
    threshold), its cluster table and its files, and -- for a run that failed before it
    wrote anything -- the reason, so the pane can say why instead of showing one ``.log``.
    """
    return _or_404(
        catalog.group_stats_detail(_pm(), type, name),
        f"Unknown group-statistics run: {type}/{name}",
    )