From 26b9f961d75788a3a8cd8da6325e420e31737005 Mon Sep 17 00:00:00 2001 From: Michelle Wang Date: Thu, 5 Nov 2026 00:59:55 -0500 Subject: [PATCH 1/3] prepend sub- prefix if it's not there --- src/brainflex/core/get_data.py | 6 ++++++ tests/core/test_get_data.py | 15 +++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/brainflex/core/get_data.py b/src/brainflex/core/get_data.py index 5f44f45..66b923f 100644 --- a/src/brainflex/core/get_data.py +++ b/src/brainflex/core/get_data.py @@ -181,6 +181,8 @@ def _get_freesurfer( def _write_pheno(layout: DatasetLayout, session_ids: dict[str, str]) -> None: """Copy participants.tsv to the tabular directory, with a session ID column. + Participant IDs without the ``sub-`` prefix have it prepended. + Participants without BIDS imaging data get an empty session ID. Warns if participant IDs and BIDS subjects do not fully match, and raises ValueError if none match. @@ -196,6 +198,10 @@ def _write_pheno(layout: DatasetLayout, session_ids: dict[str, str]) -> None: fieldnames = reader.fieldnames rows = list(reader) + for row in rows: + if not row[COL_PARTICIPANT_ID].startswith(BIDS_SUBJECT_PREFIX): + row[COL_PARTICIPANT_ID] = BIDS_SUBJECT_PREFIX + row[COL_PARTICIPANT_ID] + fieldnames = [COL_PARTICIPANT_ID, COL_SESSION_ID] + [ col for col in fieldnames if col not in (COL_PARTICIPANT_ID, COL_SESSION_ID) ] diff --git a/tests/core/test_get_data.py b/tests/core/test_get_data.py index 341b8ea..b39b9fd 100644 --- a/tests/core/test_get_data.py +++ b/tests/core/test_get_data.py @@ -188,6 +188,21 @@ def test_no_matching_participants( with pytest.raises(ValueError, match="No participant IDs"): get_data(root, DATASET_ID) + def test_participants_without_prefix( + self, mock_s3: dict[tuple[str, str], bytes], root: Path + ): + mock_s3[(OPENNEURO_BUCKET, f"{DATASET_ID}/participants.tsv")] = ( + b"participant_id\tage\n01\t20\n02\t30\nsub-03\t40\n" + ) + dpath_study = get_data(root, DATASET_ID) + pheno = (dpath_study / "tabular" / "pheno.tsv").read_text() + assert pheno == ( + "participant_id\tsession_id\tage\n" + "sub-01\tses-unnamed\t20\n" + "sub-02\tses-unnamed\t30\n" + "sub-03\t\t40\n" + ) + def test_no_participants_json( self, mock_s3: dict[tuple[str, str], bytes], root: Path ): From 45fc62a7a51b6cb84ba8602da349e44cefebd0af Mon Sep 17 00:00:00 2001 From: Michelle Wang Date: Thu, 5 Nov 2026 01:04:45 -0500 Subject: [PATCH 2/3] emit warning if sub- needed to be added --- src/brainflex/core/get_data.py | 10 ++++++++-- tests/core/test_get_data.py | 11 +++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/brainflex/core/get_data.py b/src/brainflex/core/get_data.py index 66b923f..42cf27e 100644 --- a/src/brainflex/core/get_data.py +++ b/src/brainflex/core/get_data.py @@ -199,8 +199,14 @@ def _write_pheno(layout: DatasetLayout, session_ids: dict[str, str]) -> None: rows = list(reader) for row in rows: - if not row[COL_PARTICIPANT_ID].startswith(BIDS_SUBJECT_PREFIX): - row[COL_PARTICIPANT_ID] = BIDS_SUBJECT_PREFIX + row[COL_PARTICIPANT_ID] + participant_id = row[COL_PARTICIPANT_ID] + if not participant_id.startswith(BIDS_SUBJECT_PREFIX): + row[COL_PARTICIPANT_ID] = BIDS_SUBJECT_PREFIX + participant_id + warnings.warn( + f"Prepended {BIDS_SUBJECT_PREFIX!r} to participant ID" + f" {participant_id!r} in {PARTICIPANTS_FILENAME}", + stacklevel=3, + ) fieldnames = [COL_PARTICIPANT_ID, COL_SESSION_ID] + [ col for col in fieldnames if col not in (COL_PARTICIPANT_ID, COL_SESSION_ID) diff --git a/tests/core/test_get_data.py b/tests/core/test_get_data.py index b39b9fd..07c1b38 100644 --- a/tests/core/test_get_data.py +++ b/tests/core/test_get_data.py @@ -183,7 +183,7 @@ def test_no_matching_participants( self, mock_s3: dict[tuple[str, str], bytes], root: Path ): mock_s3[(OPENNEURO_BUCKET, f"{DATASET_ID}/participants.tsv")] = ( - b"participant_id\tage\nSZ_0113\t25\n" + b"participant_id\tage\nsub-SZ0113\t25\n" ) with pytest.raises(ValueError, match="No participant IDs"): get_data(root, DATASET_ID) @@ -194,7 +194,14 @@ def test_participants_without_prefix( mock_s3[(OPENNEURO_BUCKET, f"{DATASET_ID}/participants.tsv")] = ( b"participant_id\tage\n01\t20\n02\t30\nsub-03\t40\n" ) - dpath_study = get_data(root, DATASET_ID) + with pytest.warns(UserWarning) as record_warnings: + dpath_study = get_data(root, DATASET_ID) + assert [ + str(w.message) for w in record_warnings if "Prepended" in str(w.message) + ] == [ + "Prepended 'sub-' to participant ID '01' in participants.tsv", + "Prepended 'sub-' to participant ID '02' in participants.tsv", + ] pheno = (dpath_study / "tabular" / "pheno.tsv").read_text() assert pheno == ( "participant_id\tsession_id\tage\n" From 08b2448b78e75c46c4add16f84dd490fa298c019 Mon Sep 17 00:00:00 2001 From: Michelle Wang Date: Thu, 5 Nov 2026 09:56:31 -0500 Subject: [PATCH 3/3] fix failing test --- tests/core/test_get_data.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/core/test_get_data.py b/tests/core/test_get_data.py index 181714b..f367d41 100644 --- a/tests/core/test_get_data.py +++ b/tests/core/test_get_data.py @@ -228,7 +228,6 @@ def test_participants_without_prefix( "participant_id\tsession_id\tage\n" "sub-01\tses-unnamed\t20\n" "sub-02\tses-unnamed\t30\n" - "sub-03\t\t40\n" ) def test_no_participants_json(