viewers
tit.server.routes.viewers ¶
/api/view/{kind} and the Viewer's scene-file routes (v1).
The ViewSpec is built entirely in :mod:tit.viewspec (layer rules, LUT
lookups, the six audit-bug fixes, percentile resolution, the Tetravox
ViewSpec v2 scene document); this module only maps the query params
to build_view.
D3 (docs/dev/DECISIONS.md § 2026-09-03 (One Docker image and a real development loop)): the external Freeview/Gmsh
launch routes (POST /api/viewers/freeview, POST /api/viewers/gmsh),
_require_x11 and the viewer job-kind submission they drove are
removed -- there is no X11 in this runtime.
V2 (docs/dev/DECISIONS.md § 2026-09-06 (Native panes, job rows and notebooks)): viewing is the
host-installed Tetravox desktop app, not an embed served from this
origin. POST /api/view/open (bottom of this module) writes the scene
document that app opens; it still launches nothing server-side, because the
app it writes for is not on this side of the container boundary.
Electrode overlay creation (v1 gap, pages/viewer/PARITY.md #1): the
Viewer page's "Create/refresh electrode overlay" action is not a viewer
launch -- it is a tools job that runs
tit.tools.electrode_overlay as a plain module invocation
(tit/jobs/kinds.py's tools branch: simnibs_python -m <module>
*args), the same way any other standalone tit.tools.* script would be
run. The client submits::
POST /api/jobs
{
"kind": "tools",
"config": {
"module": "tit.tools.electrode_overlay",
"args": [
"<sim_dir>/documentation/config.json",
"<m2m_dir>/T1.nii.gz",
"<sim_dir>/<TI|mTI>/montage_imgs/electrode_overlay_subject.nii.gz"
]
},
"subject_ids": ["<id>"]
}
(see tit/tools/electrode_overlay.py::main for the exact positional
config/reference/output argv and the optional --montage/
--eeg-positions-dir flags). Once that job succeeds,
tit.viewspec._electrode_overlay_layer builds the ViewSpec layer for the
file it wrote, so no route is needed for the viewing half of this gap.
viewer_scene_dir ¶
viewer_scene_dir() -> str
<project>/code/ti-toolbox/viewer/ -- sibling of config/ and jobs/.
Source code in tit/server/routes/viewers.py
checked_viewer_path ¶
path with its parent resolved, or 403 if it or its leaf's target leaves the project.
The leaf is kept as named (:func:tit.paths.resolve_leaf_within), so a
delete or replace acts on a stored alias rather than on what it points to.
Source code in tit/server/routes/viewers.py
atomic_viewer_write ¶
Replace a whole document without following a predictable temporary-file symlink.
Source code in tit/server/routes/viewers.py
localise_scene_paths ¶
localise_scene_paths(scene: dict[str, Any], container_root: str, host_root: str | None) -> dict[str, Any]
Every dataset/sidecar URL in scene rewritten to a filesystem path.
Returns a new document; scene is not mutated (the same object is still
the response body of GET /api/view/{kind}, where the URL form is
correct). With no host_root the container path is used, which is right
for a browser-mode download opened on a machine that mounts the project at
the same place, and honest everywhere else -- Tetravox says which file it
could not find.
Source code in tit/server/routes/viewers.py
native_scene ¶
Resolve scene data into host paths, staging bundled resources in the project.
Source code in tit/server/routes/viewers.py
export_scene ¶
Preserve camera/layers and write host-addressed files for the native viewer.
Source code in tit/server/routes/viewers.py
subject_of_path ¶
The subject path belongs to, or None for a file that belongs to nobody.
None is the bundled MNI template and atlases, FreeSurfer's fsaverage and group
outputs, which a scene may always mix in. The same rule, case for case, as the Viewer's
subjectOfPath (desktop/src/renderer/pages/viewer/lib.ts); the shared table is
tests/fixtures/subject_paths.json.
Source code in tit/server/routes/viewers.py
view_open ¶
{kind, subject, ...} -> {name, path, host_path, scene, view, files}.
Launches nothing, and never could: the server has no display (D3). One resolution produces two addressings of the same scene --
view-- every dataset an/api/files/raw/...URL. This is what the Viewer sub-page reads to describe and preview the selection, fetching those bytes back through this origin.scene-- the same document with every path re-rooted onto the host, also written to<project>/code/ti-toolbox/viewer/<kind>.tetravox.jsonso the file can be opened by a desktop Tetravox or kept as a record of what was viewed.
They come from one build_view call on purpose. Two calls could resolve
differently -- a job finishing between them is enough -- and then the list
the page shows, the file on disk and the scene on screen would disagree
about what is in the scene, with nothing to say which was right.
Source code in tit/server/routes/viewers.py
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 | |
viewer_candidates ¶
viewer_candidates(subject: Annotated[SubjectId | None, Query()] = None, simulation: Annotated[EntityName | None, Query()] = None, space: str | None = Query(None)) -> dict[str, Any]
A read: it opens nothing and writes nothing.
Only files a scene can actually use are listed (volumes and meshes), each with its size, because the point of the picker is to choose without guessing -- and one of these files is routinely 64 MB.