Skip to content

example_data_manager

tit.project_init.example_data_manager

Example Data Manager for TI-Toolbox

This module handles the automatic copying of example data to BIDS-compliant locations when a new project is initialized. It copies anatomical NIfTI files for ernie and MNI152 subjects to their respective anat folders for immediate use in TI-Toolbox workflows.

Author: TI-Toolbox Development Team

ExampleDataManager

ExampleDataManager(toolbox_root: str, project_dir: str)

Manages copying of example data to BIDS-compliant project structure.

Initialize the Example Data Manager.

Parameters:

Name Type Description Default
toolbox_root str

Path to the TI-toolbox root directory

required
project_dir str

Path to the target project directory

required
Source code in tit/project_init/example_data_manager.py
def __init__(self, toolbox_root: str, project_dir: str):
    """
    Initialize the Example Data Manager.

    Args:
        toolbox_root: Path to the TI-toolbox root directory
        project_dir: Path to the target project directory
    """
    self.toolbox_root = Path(toolbox_root)
    self.project_dir = Path(project_dir)
    self.example_data_dir = self.toolbox_root / "resources" / "example_data"

    # Define mapping of example data to BIDS structure
    self.data_mapping = self._create_data_mapping()

check_example_data_exists

check_example_data_exists() -> bool

Check if example data directory exists and contains expected data.

Returns:

Type Description
bool

True if example data is available, False otherwise

Source code in tit/project_init/example_data_manager.py
def check_example_data_exists(self) -> bool:
    """
    Check if example data directory exists and contains expected data.

    Returns:
        True if example data is available, False otherwise
    """
    if not self.example_data_dir.exists():
        logger.warning(f"Example data directory not found: {self.example_data_dir}")
        return False

    # Check for key example subjects
    key_subjects = ["ernie", "MNI152"]
    for subject in key_subjects:
        subject_dir = self.example_data_dir / subject
        if not subject_dir.exists():
            logger.warning(f"Key example subject not found: {subject}")
            return False

    return True

is_new_project

is_new_project() -> bool

Determine if this is a new project that should receive example data.

A project is considered new if: 1. No subject directories exist yet 2. Example data hasn't been copied before 3. No actual user data is present

Returns:

Type Description
bool

True if this is a new project, False otherwise

Source code in tit/project_init/example_data_manager.py
def is_new_project(self) -> bool:
    """
    Determine if this is a new project that should receive example data.

    A project is considered new if:
    1. No subject directories exist yet
    2. Example data hasn't been copied before
    3. No actual user data is present

    Returns:
        True if this is a new project, False otherwise
    """
    # Check for existing subject directories
    subject_dirs = list(self.project_dir.glob("sub-*"))
    if subject_dirs:
        logger.info(
            "Found existing subject directories, skipping example data copy"
        )
        return False

    # Check project status file if it exists to see if example data was already copied
    pm = get_path_manager(str(self.project_dir))
    status_file = Path(pm.project_status())
    if status_file.exists():
        try:
            import json

            with open(status_file, "r") as f:
                status_data = json.load(f)

            # Check if example data was already copied
            if status_data.get("example_data_copied", False):
                logger.info(
                    "Example data already copied according to project status"
                )
                return False

        except Exception as e:
            logger.warning(f"Could not read project status file: {e}")

    # Check for any user-created NIfTI files in the project root
    user_nifti_files = list(self.project_dir.glob("*.nii.gz"))
    if user_nifti_files:
        logger.info("Found existing user NIfTI files, skipping example data copy")
        return False

    # Check for sourcedata directory with actual content (indicates user data)
    sourcedata_dir = self.project_dir / "sourcedata"
    if sourcedata_dir.exists():
        sourcedata_content = list(sourcedata_dir.rglob("*.dcm")) + list(
            sourcedata_dir.rglob("*.nii.gz")
        )
        if sourcedata_content:
            logger.info(
                "Found existing user data in sourcedata, skipping example data copy"
            )
            return False

    return True

copy_file_or_directory

copy_file_or_directory(src_path: Path, dst_path: Path) -> bool

Copy a file or directory from source to destination.

Parameters:

Name Type Description Default
src_path Path

Source path

required
dst_path Path

Destination path

required

Returns:

Type Description
bool

True if copy was successful, False otherwise

Source code in tit/project_init/example_data_manager.py
def copy_file_or_directory(self, src_path: Path, dst_path: Path) -> bool:
    """
    Copy a file or directory from source to destination.

    Args:
        src_path: Source path
        dst_path: Destination path

    Returns:
        True if copy was successful, False otherwise
    """
    try:
        # Create parent directories if they don't exist
        dst_path.parent.mkdir(parents=True, exist_ok=True)

        if src_path.is_file():
            shutil.copy2(src_path, dst_path)
            logger.info(f"Copied file: {src_path.name} -> {dst_path}")
        elif src_path.is_dir():
            if dst_path.exists():
                shutil.rmtree(dst_path)
            shutil.copytree(src_path, dst_path)
            logger.info(f"Copied directory: {src_path.name} -> {dst_path}")
        else:
            logger.warning(f"Source path does not exist: {src_path}")
            return False

        return True

    except Exception as e:
        logger.error(f"Failed to copy {src_path} to {dst_path}: {e}")
        return False

copy_example_data

copy_example_data() -> tuple[bool, list[str]]

Copy example data to the project directory using BIDS structure. Ensures all necessary BIDS directories are created.

Returns:

Type Description
tuple[bool, list[str]]

Tuple of (success: bool, copied_subjects: list[str])

Source code in tit/project_init/example_data_manager.py
def copy_example_data(self) -> tuple[bool, list[str]]:
    """
    Copy example data to the project directory using BIDS structure.
    Ensures all necessary BIDS directories are created.

    Returns:
        Tuple of (success: bool, copied_subjects: list[str])
    """
    if not self.check_example_data_exists():
        return False, []

    if not self.is_new_project():
        logger.info("Project is not new, skipping example data copy")
        return False, []

    logger.info(f"Copying example data to new project: {self.project_dir}")

    # Initialize path manager and ensure core BIDS directories exist
    pm = get_path_manager(str(self.project_dir))

    core_dirs = [
        pm.ensure(pm.sourcedata()),
        pm.ensure(pm.ti_toolbox()),
        pm.ensure(pm.simnibs()),
        pm.ensure(pm.freesurfer()),
        pm.ensure(pm.config_dir()),
    ]

    for dir_path in core_dirs:
        Path(dir_path).mkdir(parents=True, exist_ok=True)

    copied_subjects = []
    total_operations = 0
    successful_operations = 0

    # Process each subject's data according to the mapping
    for subject_key, file_mapping in self.data_mapping.items():
        subject_src_dir = self.example_data_dir / subject_key

        if not subject_src_dir.exists():
            logger.warning(f"Subject directory not found: {subject_src_dir}")
            continue

        logger.info(f"Processing subject: {subject_key}")
        subject_copied = False

        # Copy each file/directory for this subject
        for src_name, dst_relative_path in file_mapping.items():
            src_path = subject_src_dir / src_name
            dst_path = self.project_dir / dst_relative_path

            total_operations += 1
            if self.copy_file_or_directory(src_path, dst_path):
                successful_operations += 1
                subject_copied = True

        if subject_copied:
            # Extract subject ID from the mapping for tracking
            subject_id = list(file_mapping.values())[0].split("/")[
                0
            ]  # Get subject ID from first mapping
            copied_subjects.append(subject_id)
            logger.info(f"Successfully copied example data for {subject_id}")

    # Copy the readme file to the project root for reference if it exists
    readme_src = self.example_data_dir / "readme.txt"
    if readme_src.exists():
        readme_dst = self.project_dir / "EXAMPLE_DATA_README.txt"
        total_operations += 1
        if self.copy_file_or_directory(readme_src, readme_dst):
            successful_operations += 1

    # Update project status to indicate example data was copied
    self._update_project_status(copied_subjects)

    success = successful_operations == total_operations
    if success:
        logger.info(
            f"Successfully copied example data for {len(copied_subjects)} subjects"
        )
    else:
        logger.warning(
            f"Copied {successful_operations}/{total_operations} items successfully"
        )

    return success, copied_subjects

create_bids_dataset_description

create_bids_dataset_description() -> None

Create BIDS dataset_description.json file for the project. Only creates if it doesn't already exist (loader.sh may have created it).

Source code in tit/project_init/example_data_manager.py
def create_bids_dataset_description(self) -> None:
    """
    Create BIDS dataset_description.json file for the project.
    Only creates if it doesn't already exist (loader.sh may have created it).
    """
    try:
        desc_file = self.project_dir / "dataset_description.json"

        # Skip if already exists
        if desc_file.exists():
            logger.info("BIDS dataset_description.json already exists, skipping")
            return

        dataset_desc = {
            "Name": f"TI-Toolbox Project: {self.project_dir.name}",
            "BIDSVersion": "1.10.0",
            "DatasetType": "raw",
            "Authors": ["TI-Toolbox User"],
            "Description": "TI-Toolbox project with example data (ernie and MNI152) for temporal interference analysis",
            "License": "CC BY-NC 4.0",
            "Acknowledgements": "Example data provided by SimNIBS team. Ernie subject includes T1/T2 anatomical NIfTI files. MNI152 template provided for reference.",
            "ReferencesAndLinks": [
                "https://simnibs.github.io/",
                "https://github.com/idossha/TI-Toolbox",
            ],
        }

        with open(desc_file, "w") as f:
            import json

            json.dump(dataset_desc, f, indent=2)

        logger.info("Created BIDS dataset_description.json")

    except Exception as e:
        logger.error(f"Failed to create dataset description: {e}")

create_bids_readme

create_bids_readme() -> None

Create BIDS README file for the project. Only creates if it doesn't already exist (loader.sh may have created it).

Source code in tit/project_init/example_data_manager.py
    def create_bids_readme(self) -> None:
        """
        Create BIDS README file for the project.
        Only creates if it doesn't already exist (loader.sh may have created it).
        """
        try:
            readme_file = self.project_dir / "README"

            # Skip if already exists
            if readme_file.exists():
                logger.info("BIDS README already exists, skipping")
                return

            project_name = self.project_dir.name
            readme_content = f"""# {project_name}

This is a BIDS-compliant neuroimaging dataset generated by TI-Toolbox for temporal interference (TI) stimulation modeling and analysis.

## Overview

This project contains structural MRI data and derivatives for simulating and analyzing temporal interference electric field patterns in the brain.

## Example Data

This project includes example data for testing and demonstration:
- **sub-ernie**: Example subject with T1w and T2w anatomical scans (provided by SimNIBS)
- **sub-MNI152**: Standard MNI152 template for reference

## Dataset Structure

- `sourcedata/` - Raw DICOM source files
- `sub-*/` - Subject-level BIDS-formatted neuroimaging data (NIfTI files)
- `derivatives/` - Processed data and analysis results
  - `freesurfer/` - FreeSurfer anatomical segmentation and surface reconstructions
  - `SimNIBS/` - SimNIBS head models and electric field simulations
  - `ti-toolbox/` - TI-Toolbox simulation results and analyses
- `code/ti-toolbox/` - Configuration files for the toolbox

## Software

Data processing and simulations were performed using:
- **TI-Toolbox** - Temporal interference modeling pipeline
- **FreeSurfer** - Cortical reconstruction and volumetric segmentation
- **SimNIBS** - Finite element modeling for electric field simulations

## More Information

For more information about TI-Toolbox, visit:
- GitHub: https://github.com/idossha/TI-Toolbox
- Documentation: https://idossha.github.io/TI-toolbox/

## BIDS Compliance

This dataset follows the Brain Imaging Data Structure (BIDS) specification for organizing and describing neuroimaging data. For more information about BIDS, visit: https://bids.neuroimaging.io/
"""

            with open(readme_file, "w") as f:
                f.write(readme_content)

            logger.info("Created BIDS README file")

        except Exception as e:
            logger.error(f"Failed to create README: {e}")

setup_example_data

setup_example_data(toolbox_root: str, project_dir: str) -> tuple[bool, list[str]]

Main function to set up example data in a new project.

Parameters:

Name Type Description Default
toolbox_root str

Path to the TI-toolbox root directory

required
project_dir str

Path to the target project directory

required

Returns:

Type Description
tuple[bool, list[str]]

Tuple of (success: bool, copied_subjects: list[str])

Source code in tit/project_init/example_data_manager.py
def setup_example_data(toolbox_root: str, project_dir: str) -> tuple[bool, list[str]]:
    """
    Main function to set up example data in a new project.

    Args:
        toolbox_root: Path to the TI-toolbox root directory
        project_dir: Path to the target project directory

    Returns:
        Tuple of (success: bool, copied_subjects: list[str])
    """
    try:
        manager = ExampleDataManager(toolbox_root, project_dir)
        success, subjects = manager.copy_example_data()

        if success and subjects:
            # Create BIDS dataset description and README
            manager.create_bids_dataset_description()
            manager.create_bids_readme()

        return success, subjects

    except Exception as e:
        logger.error(f"Error setting up example data: {e}")
        return False, []