Skip to content

project_init

tit.project_init

Project initialization helpers for TI-Toolbox.

Provides utilities for detecting new projects, scaffolding BIDS-compliant directory structures, and copying bundled example data into a fresh project.

initialize_project_structure

initialize_project_structure(project_dir: Path) -> None

Scaffold a full BIDS-compliant directory structure for a new project.

Parameters

project_dir : Path Root directory of the new project. Directories, metadata files, README, and an initialization marker are created idempotently.

Source code in tit/project_init/initializer.py
def initialize_project_structure(project_dir: Path) -> None:
    """Scaffold a full BIDS-compliant directory structure for a new project.

    Parameters
    ----------
    project_dir : Path
        Root directory of the new project.  Directories, metadata files,
        README, and an initialization marker are created idempotently.
    """
    print("")
    print("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
    print(f"  New project detected: {project_dir.name}")
    print("  Initializing BIDS-compliant structure...")
    print("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
    print("")

    print("Creating directory structure...")
    (project_dir / "code" / "ti-toolbox" / "config").mkdir(parents=True, exist_ok=True)
    (project_dir / "derivatives" / "freesurfer").mkdir(parents=True, exist_ok=True)
    (project_dir / "derivatives" / "SimNIBS").mkdir(parents=True, exist_ok=True)
    (project_dir / "sourcedata").mkdir(parents=True, exist_ok=True)
    print("  ✓ Directories created")

    print("Creating BIDS metadata files...")
    initialize_readme(project_dir)
    print("  ✓ README created")

    initialize_dataset_description(project_dir)
    print("  ✓ Root dataset_description.json created")

    initialize_derivative_dataset_description(project_dir, "ti-toolbox")
    print("  ✓ ti-toolbox dataset_description.json created")

    initialize_derivative_dataset_description(project_dir, "freesurfer")
    print("  ✓ freesurfer dataset_description.json created")

    initialize_derivative_dataset_description(project_dir, "SimNIBS")
    print("  ✓ SimNIBS dataset_description.json created")

    print("Creating project configuration...")
    initialize_project_status(project_dir)
    print("  ✓ Project status file created")

    (project_dir / "code" / "ti-toolbox" / "config" / ".initialized").touch()
    print("  ✓ Initialization marker created")

    print("")
    print("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
    print("  ✓ Project initialization complete!")
    print("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
    print("")

is_new_project

is_new_project(project_dir: Path) -> bool

Return True if project_dir exists and contains no project data.

Parameters

project_dir : Path Root directory of the project.

Returns

bool True when the directory is empty of project data and markers.

Source code in tit/project_init/initializer.py
def is_new_project(project_dir: Path) -> bool:
    """Return ``True`` if *project_dir* exists and contains no project data.

    Parameters
    ----------
    project_dir : Path
        Root directory of the project.

    Returns
    -------
    bool
        ``True`` when the directory is empty of project data and markers.
    """
    return (
        project_dir.exists()
        and project_dir.is_dir()
        and not has_project_data_or_markers(project_dir)
    )

load_project_status

load_project_status(project_dir: Path) -> dict[str, Any]

Read project_status.json and return its contents.

Returns an empty dict when the file is missing or unreadable. This function never writes to disk.

Parameters

project_dir : Path Root directory of the project.

Source code in tit/project_init/initializer.py
def load_project_status(project_dir: Path) -> dict[str, Any]:
    """Read ``project_status.json`` and return its contents.

    Returns an **empty dict** when the file is missing or unreadable.
    This function never writes to disk.

    Parameters
    ----------
    project_dir : Path
        Root directory of the project.
    """
    status_file = _status_file_path(project_dir)
    if not status_file.exists():
        return {}
    try:
        return json.loads(status_file.read_text())
    except Exception as exc:
        logger.warning("Could not read %s: %s", status_file, exc)
        return {}

setup_example_data

setup_example_data(toolbox_root: Path, project_dir: Path) -> bool

Copy bundled example data into project_dir.

Parameters

toolbox_root : Path Root of the TI-Toolbox installation (contains example data). project_dir : Path Target project directory.

Returns

bool True on success, False on failure.

Source code in tit/project_init/initializer.py
def setup_example_data(toolbox_root: Path, project_dir: Path) -> bool:
    """Copy bundled example data into *project_dir*.

    Parameters
    ----------
    toolbox_root : Path
        Root of the TI-Toolbox installation (contains example data).
    project_dir : Path
        Target project directory.

    Returns
    -------
    bool
        ``True`` on success, ``False`` on failure.
    """
    try:
        success, _subjects = example_data_manager.setup_example_data(
            str(toolbox_root), str(project_dir)
        )
        return bool(success)
    except Exception:
        return False

update_project_status

update_project_status(project_dir: Path, updates: dict[str, Any]) -> bool

Merge updates into project_status.json and write back.

Performs a recursive (deep) merge so that nested keys such as user_preferences.show_welcome can be updated without clobbering sibling keys. Automatically sets last_updated.

If the file does not yet exist a warning is logged and the function returns False — the file should have been created by :func:initialize_project_status.

Parameters

project_dir : Path Root directory of the project. updates : dict Fields to merge into the existing status.

Returns

bool True on success.

Source code in tit/project_init/initializer.py
def update_project_status(project_dir: Path, updates: dict[str, Any]) -> bool:
    """Merge *updates* into ``project_status.json`` and write back.

    Performs a recursive (deep) merge so that nested keys such as
    ``user_preferences.show_welcome`` can be updated without clobbering
    sibling keys.  Automatically sets ``last_updated``.

    If the file does not yet exist a warning is logged and the function
    returns ``False`` — the file should have been created by
    :func:`initialize_project_status`.

    Parameters
    ----------
    project_dir : Path
        Root directory of the project.
    updates : dict
        Fields to merge into the existing status.

    Returns
    -------
    bool
        ``True`` on success.
    """
    status_file = _status_file_path(project_dir)
    current = load_project_status(project_dir)
    if not current:
        logger.warning(
            "project_status.json does not exist at %s — skipping update",
            status_file,
        )
        return False

    _deep_merge(current, updates)
    current["last_updated"] = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S")

    try:
        status_file.write_text(json.dumps(current, indent=2))
        return True
    except Exception as exc:
        logger.error("Failed to write %s: %s", status_file, exc)
        return False