exlab_wizard.api.routers.browse#
/tree and /run/{path} browse endpoints. Backend Spec §4.6.1.
Two endpoints back the Frontend’s tree view (Frontend §3.6) and run detail panel (Frontend §3.6.2):
GET /tree– equipment / project / run hierarchy.GET /run/{path}– single-run detail (template, operator, sync status, run kind, README content).
The router walks the local filesystem under each configured equipment
root via os.scandir (the same iterator the validator uses for
audit-mode walks; §4.5). creation.json is decoded via
msgspec.json.decode per §4.4.5.
Functions
Construct the |
|
|
Compose the nested hierarchy dict that |
|
Walk the staging root and surface received-equipment nodes. |
|
Synchronous core of the |
Classes
|
An owned-equipment node in the tree (Redesign §3.3). |
|
One row in the new |
|
Immediate contents of one folder. |
|
|
|
A received-equipment node auto-discovered from the staging area. |
|
|
|
One row in the |
|
|
|
|
|
- class exlab_wizard.api.routers.browse.EquipmentNode(**data)[source]#
Bases:
BaseModelAn owned-equipment node in the tree (Redesign §3.3).
sync_mode(“nas” | “stage”) drives the per-equipment badge.relayis False for owned equipment; the received-equipment node type has its ownRelayEquipmentNodeshape below.- Parameters:
data (
Any)
- model_config: ClassVar[ConfigDict] = {'extra': 'forbid'}#
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- projects: list[ProjectNode]#
- class exlab_wizard.api.routers.browse.FolderEntry(**data)[source]#
Bases:
BaseModelOne row in the new
GET /folder/{path}response. Redesign §4.3 / §5.Operator-free per-file NAS sync design (2026-05-21):
sync_statusis one of the five GUI display states –acquiring/syncing/synced/on_nas– sourced from the run’ssync_state.json. Atombstoneentry (is_dir=False,size_bytes=None,modified_iso=None) represents a file present insync_state.jsonbut absent on disk – a cleared run still lists its files as “On NAS”.keep_localcarries the file’s keep-local flag for the badge.- Parameters:
data (
Any)
- model_config: ClassVar[ConfigDict] = {'extra': 'forbid'}#
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class exlab_wizard.api.routers.browse.FolderResponse(**data)[source]#
Bases:
BaseModelImmediate contents of one folder. Redesign §5 (live file feed).
- Parameters:
data (
Any)
- entries: list[FolderEntry]#
- model_config: ClassVar[ConfigDict] = {'extra': 'forbid'}#
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class exlab_wizard.api.routers.browse.ProjectNode(**data)[source]#
Bases:
BaseModel- Parameters:
data (
Any)
- model_config: ClassVar[ConfigDict] = {'extra': 'forbid'}#
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class exlab_wizard.api.routers.browse.RelayEquipmentNode(**data)[source]#
Bases:
BaseModelA received-equipment node auto-discovered from the staging area.
Redesign §3.3: the orchestrator surfaces equipment it has received runs for, even though they are not in its local config registry.
- Parameters:
data (
Any)
- model_config: ClassVar[ConfigDict] = {'extra': 'forbid'}#
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- projects: list[ProjectNode]#
- class exlab_wizard.api.routers.browse.RunDetail(**data)[source]#
Bases:
BaseModel- Parameters:
data (
Any)
- model_config: ClassVar[ConfigDict] = {'extra': 'forbid'}#
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class exlab_wizard.api.routers.browse.RunLogEntry(**data)[source]#
Bases:
BaseModelOne row in the
GET /run/{path}/logresponse.The orchestrator does not write per-run log files. After the operator-free per-file NAS sync redesign (2026-05-21) removed
ingest.json, the per-run “log” is derived from the run’s sync-queue job: each entry carries the queuestateandattimestamp, with queue extras (attempts,last_error…) forwarded as a free-formpayload.- Parameters:
data (
Any)
- model_config: ClassVar[ConfigDict] = {'extra': 'forbid'}#
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class exlab_wizard.api.routers.browse.RunLogResponse(**data)[source]#
Bases:
BaseModelGET /run/{path}/logresponse (Redesign §4.6 View-log surface).- Parameters:
data (
Any)
- history: list[RunLogEntry]#
- model_config: ClassVar[ConfigDict] = {'extra': 'forbid'}#
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class exlab_wizard.api.routers.browse.RunNode(**data)[source]#
Bases:
BaseModel- Parameters:
data (
Any)
- model_config: ClassVar[ConfigDict] = {'extra': 'forbid'}#
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class exlab_wizard.api.routers.browse.TreeResponse(**data)[source]#
Bases:
BaseModel- Parameters:
data (
Any)
- equipment: list[EquipmentNode]#
- model_config: ClassVar[ConfigDict] = {'extra': 'forbid'}#
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- received_equipment: list[RelayEquipmentNode]#
- exlab_wizard.api.routers.browse.build_browse_router()[source]#
Construct the
/tree+/runrouter.- Return type:
- exlab_wizard.api.routers.browse.build_hierarchy_dict(config)[source]#
Compose the nested hierarchy dict that
ui.components.tree.build_treeexpects.The
GET /treeresponse is a flat shape (lists of equipment and received-equipment models);build_treeconsumes a nesteddict[EquipmentNode, dict[ProjectNode, list[RunNode]]]keyed byexlab_wizard.ui.components.tree.EquipmentNode/ProjectNode/RunNode. This helper bridges the two so the NiceGUI mount doesn’t have to re-implement the walk.Returns an empty dict when
configisNone.
- exlab_wizard.api.routers.browse.build_received_equipment_nodes(config)[source]#
Walk the staging root and surface received-equipment nodes.
Redesign §3.3: auto-discovered from the runs the orchestrator has received; the equipment is NOT in this device’s config registry.
- Parameters:
config (
Any)- Return type:
- exlab_wizard.api.routers.browse.scan_folder_sync(folder_path, config)[source]#
Synchronous core of the
GET /folder/{path}endpoint.Extracted so the NiceGUI mount can drive the same scan from a thread via
asyncio.to_thread()(matching the convention inexlab_wizard.cache.equipment). Raises the same FastAPIHTTPExceptioninstances as the endpoint so the HTTP response code is preserved when called from a router; the NiceGUI mount catches them and renders the appropriate UI state.- Parameters:
- Return type: