Skip to content

project

tit.server.routes.project

GET /api/project — the project this server instance is bound to.

POST /api/project/init (v1) submits the project's BIDS/derivative-layout initialization as a project_init job through :mod:tit.jobs.api, the same submit path every other job-producing route uses (owner: B1 for the manager; this module only builds the spec) -- see this lane's final report for the one piece B1 still needs to land (project_init is in the contract's frozen JobKind enum but not yet in tit.jobs.spec.JOB_KINDS nor dispatched by tit.jobs.kinds.command_for, so jobs_api.submit currently raises ValueError for it -- caught below and turned into a 503, exactly like tit.server.routes.viewers's NotImplementedError handling for the same "contract kind, no runner yet" situation).

project

project() -> Project

host_path is LOCAL_PROJECT_DIR when set, else read off this server's own container (bind mount, then the tit.host_project_dir label) -- see :mod:tit.server.host_path for why the environment variable alone left every v3 container answering null.

Source code in tit/server/routes/project.py
@router.get(
    "/api/project",
    response_model=Project,
    summary="The project this server instance is bound to",
)
def project() -> Project:
    """``host_path`` is ``LOCAL_PROJECT_DIR`` when set, else read off this server's own
    container (bind mount, then the ``tit.host_project_dir`` label) -- see
    :mod:`tit.server.host_path` for why the environment variable alone left every v3
    container answering ``null``."""
    pm = get_path_manager()
    container_path = pm.project_dir or ""
    return Project(
        container_path=container_path,
        host_path=host_project_dir(container_path),
        name=pm.project_dir_name or os.path.basename(container_path),
    )

patch_project_status

patch_project_status(body: ProjectStatus) -> dict[str, Any]

Records one-time answers such as example_subject_prompted (the desktop's "Add the example subject?" dialog). Creates the file when it is missing, because an answer that is not persisted is asked again.

Source code in tit/server/routes/project.py
@router.patch(
    "/api/project/status",
    response_model=ProjectStatus,
    summary="Merge fields into project_status.json and return the result",
)
def patch_project_status(body: ProjectStatus) -> dict[str, Any]:
    """Records one-time answers such as ``example_subject_prompted`` (the desktop's
    "Add the example subject?" dialog). Creates the file when it is missing, because
    an answer that is not persisted is asked again."""
    from tit.project_init import load_project_status, update_project_status
    from tit.project_init.initializer import initialize_project_status

    project_dir = Path(_bound_project_dir())
    updates = body.model_dump(exclude_none=True)
    initialize_project_status(project_dir)
    if updates and not update_project_status(project_dir, updates):
        raise HTTPException(status_code=500, detail="Could not write project_status.json.")
    return load_project_status(project_dir)

init_project

init_project(body: dict[str, Any] | None = None) -> dict[str, Any]

{} -> JobStatus for the project_init job.

body is optional and ignored -- unlike most job-submit routes this one has no meaningful subject_ids (project init runs once, before any subject exists). Example data is POST /api/example-data/{sample_id} (:mod:tit.server.routes.example_data) -- a plain function, never a job.

Source code in tit/server/routes/project.py
@router.post(
    "/api/project/init",
    status_code=201,
    summary="Initialize this project's layout",
)
def init_project(body: dict[str, Any] | None = None) -> dict[str, Any]:
    """``{}`` -> ``JobStatus`` for the ``project_init`` job.

    ``body`` is optional and ignored -- unlike most job-submit routes this one
    has no meaningful ``subject_ids`` (project init runs once, before any
    subject exists). Example data is ``POST /api/example-data/{sample_id}``
    (:mod:`tit.server.routes.example_data`) -- a plain function, never a job.
    """
    del body
    try:
        from tit.jobs import api as jobs_api
    except ImportError as exc:  # pragma: no cover - tit.jobs is always importable
        raise HTTPException(
            status_code=503, detail=f"tit.jobs unavailable: {exc}"
        ) from exc
    try:
        return jobs_api.submit(
            {
                "kind": "project_init",
                "config": {},
                "subject_ids": [],
                "created_by": "gui",
            }
        )
    except (NotImplementedError, ValueError) as exc:
        raise HTTPException(status_code=503, detail=str(exc)) from exc