Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR1119 Review #1

Merged
merged 34 commits into from
May 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
3739be3
[REF] Move DWI specific functions from `filemanip.py` utils to dwi pr…
NicolasGensollen Apr 3, 2024
f0ae75b
Bump pillow from 10.2.0 to 10.3.0 (#1118)
dependabot[bot] Apr 4, 2024
da22c53
[DOC] Remove unused documentation page `ICMClusterInstallation` (#1121)
NicolasGensollen Apr 4, 2024
041fb4a
[DOC] Add FWHM definition to glossary (#1123)
AliceJoubert Apr 5, 2024
93120eb
[DOC] Add SUVR definition in glossary (#1124)
AliceJoubert Apr 5, 2024
5d9710b
[DOC] Add PSF definition to glossary (#1125)
AliceJoubert Apr 8, 2024
4b1c2a4
[DOC] Add PVC definition to glossary (#1126)
AliceJoubert Apr 8, 2024
645fd8c
[ENH] Check that BIDS folders have a `dataset_description.json` file …
NicolasGensollen Apr 9, 2024
e63d4eb
[MAINT] Change to use str.removesuffix (#1131)
AliceJoubert Apr 9, 2024
7d2819a
[MAINT] Change to regex for subject conversation to RID (#1133)
AliceJoubert Apr 9, 2024
bc7e7ff
[REF] Encode names of dataset as an enum (#1134)
NicolasGensollen Apr 10, 2024
d7da4ae
[MAINT] Move `read_participant_tsv` and make it private (#1135)
AliceJoubert Apr 11, 2024
4dc5bbb
Bump idna from 3.6 to 3.7 (#1138)
dependabot[bot] Apr 12, 2024
e565fba
[MAINT] Delete `clinica.utils.mri_registration` module (#1139)
AliceJoubert Apr 12, 2024
81e0586
[ENH] Refactor Freesurfer pipelines architecture (#1120)
NicolasGensollen Apr 12, 2024
eaf6531
[CI] Run CI on maintenance branches (#1145)
NicolasGensollen Apr 23, 2024
e710a95
[FIX] Fix `DXSUM_PDXCONV_ADNIALL` unknown clinical file for adni-to-b…
NicolasGensollen Apr 23, 2024
3856557
[FIX] Fix DWI preprocessing using T1 rename to caps node (#1146)
NicolasGensollen Apr 23, 2024
f4c10f5
[CI] Run CI on maintenance branches (#1149)
NicolasGensollen Apr 24, 2024
2333c12
Release `0.8.1` (#1150)
NicolasGensollen Apr 24, 2024
3fdfea6
Merge branch 'maint-0.8' into dev
NicolasGensollen Apr 25, 2024
e2ce719
[MAINT] Move `merge_nifti_images_in_time_dimension_task` in dwi tasks…
NicolasGensollen Apr 25, 2024
cfa5874
[MAINT] Move bids version constant definition to bids module (#1153)
NicolasGensollen Apr 25, 2024
a61c110
[MAINT] Update OASIS website URL (#1155)
NicolasGensollen Apr 25, 2024
232d335
[ENH] Move `crop_nifti` function (#1156)
AliceJoubert Apr 25, 2024
31543c2
[ENH] Add `crop_nifti` to public API (#1160)
AliceJoubert Apr 26, 2024
a317e11
[CI] Fix MacOS GitHub runner target (#1161)
NicolasGensollen Apr 26, 2024
374794e
[ENH] Add Pydra tasks for BIDS and CAPS I/O (#1027)
ghisvail Apr 26, 2024
914fd77
[FIX] Fix wrong preprocessing sequence in adni-to-bids FDG PET Unifor…
HuguesRoy Apr 26, 2024
06d5c64
ENH: Add tasks for reading BIDS files and dataset (#1141)
ghisvail Apr 26, 2024
cd37d6f
Merge branch 'dev' into fmap040324
AliceJoubert Apr 30, 2024
61a5e97
Review PR1119
AliceJoubert Apr 30, 2024
fcdc015
Remove duplicated function
AliceJoubert Apr 30, 2024
7def9cc
Make code more robust, remove duplication
AliceJoubert May 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Lint

on:
push:
branches: [ dev ]
branches: [ "dev", "maint-*" ]
pull_request:
branches: [ dev ]
branches: [ "dev", "maint-*" ]

permissions:
contents: read
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Unit Tests

on:
push:
branches: [dev]
branches: ["dev", "maint-*"]
pull_request:
branches: [dev]
branches: ["dev", "maint-*"]

permissions:
contents: read
Expand All @@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
os: [ubuntu-latest, macos-12]
python-version: ['3.9', '3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v4
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test_converters.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Non Regression Tests - Converters

on:
push:
branches: [dev]
branches: ["dev", "maint-*"]
pull_request:
branches: [dev]
branches: ["dev", "maint-*"]

permissions:
contents: read
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test_instantiation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Instantiation Tests

on:
push:
branches: [dev]
branches: ["dev", "maint-*"]
pull_request:
branches: [dev]
branches: ["dev", "maint-*"]

permissions:
contents: read
Expand Down
6 changes: 3 additions & 3 deletions LICENSE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ on Multi-Atlas Labeling''
(https://masi.vuse.vanderbilt.edu/workshop2012/index.php/Challenge_Details).
These data were released under the Creative Commons Attribution-NonCommercial
(CC BY-NC) with no end date. Users should credit the MRI scans as originating
from the OASIS project (http://www.oasis-brains.org/) and the labeled data as
from the OASIS project (https://sites.wustl.edu/oasisbrains/) and the labeled data as
"provided by Neuromorphometrics, Inc. (http://Neuromorphometrics.com/) under
academic subscription". These references should be included in all workshop
and final publications:
Expand Down Expand Up @@ -206,15 +206,15 @@ Challenge and Workshop on Multi-Atlas Labeling''
(https://masi.vuse.vanderbilt.edu/workshop2012/index.php/Challenge_Details).
These data were released under the Creative Commons Attribution-NonCommercial
(CC BY-NC) with no end date. Users should credit the MRI scans as originating
from the OASIS project (http://www.oasis-brains.org/) and the labeled data as
from the OASIS project (https://sites.wustl.edu/oasisbrains/) and the labeled data as
"provided by Neuromorphometrics, Inc. (http://Neuromorphometrics.com/) under
academic subscription". These references should be included in all workshop
and final publications.


Websites:
A) https://masi.vuse.vanderbilt.edu/workshop2012/index.php/Challenge_Details
B) http://www.oasis-brains.org/
B) https://sites.wustl.edu/oasisbrains/
C) http://Neuromorphometrics.com/


Expand Down
2 changes: 1 addition & 1 deletion clinica/iotools/bids_dataset_description.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from cattr.gen import make_dict_unstructure_fn, override
from cattr.preconf.json import make_converter

BIDS_VERSION = "1.7.0"
from clinica.utils.bids import BIDS_VERSION


@define
Expand Down
6 changes: 4 additions & 2 deletions clinica/iotools/bids_readme.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

from attrs import define

from .bids_utils import StudyName


@define
class BIDSReadme:
"""Model representing the content for a BIDS Readme."""

name: str
name: StudyName
link: str
description: str

Expand All @@ -18,7 +20,7 @@ def write(self, to: IO[str]):
f"This BIDS directory was generated with Clinica v{version('clinica')}.\n"
f"More information on https://www.clinica.run\n"
f"\n"
f"Study: {self.name}\n"
f"Study: {self.name.value}\n"
f"\n"
f"{self.description}\n\n"
f"Find more about it and about the data user agreement: {self.link}"
Expand Down
100 changes: 51 additions & 49 deletions clinica/iotools/bids_utils.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
"""Methods used by BIDS converters."""

from enum import Enum
from os import PathLike
from pathlib import Path
from typing import BinaryIO, List, Optional, Union

from pandas import DataFrame

SUPPORTED_DATASETS = {
"ADNI",
"CLINAD",
"PREVDEMALS",
"INSIGHT",
"OASIS",
"OASIS3",
"AIBL",
}

class StudyName(str, Enum):
"""Studies supported by the converters of Clinica."""

ADNI = "ADNI"
AIBL = "AIBL"
GENFI = "GENFI"
HABS = "HABS"
NIFD = "NIFD"
OASIS = "OASIS"
OASIS3 = "OASIS3"
UKB = "UKB"


BIDS_VALIDATOR_CONFIG = {
"ignore": [
Expand Down Expand Up @@ -44,7 +49,7 @@
# -- Methods for the clinical data --
# @ToDo:test this function
def create_participants_df(
study_name,
study_name: StudyName,
clinical_spec_path,
clinical_data_dir,
bids_ids,
Expand Down Expand Up @@ -75,11 +80,12 @@ def create_participants_df(
prev_location = ""
index_to_drop = []
subjects_to_drop = []
location_name = study_name + " location"
study_name = StudyName(study_name)
location_name = f"{study_name.value} location"

# Load the data from the clincal specification file
participants_specs = pd.read_csv(clinical_spec_path + "_participant.tsv", sep="\t")
participant_fields_db = participants_specs[study_name]
participant_fields_db = participants_specs[study_name.value]
field_location = participants_specs[location_name]
participant_fields_bids = participants_specs["BIDS CLINICA"]

Expand Down Expand Up @@ -138,12 +144,12 @@ def create_participants_df(
# Add the extracted column to the participant_df
participant_df[participant_fields_bids[i]] = pd.Series(field_col_values)

if study_name == "ADNI" or study_name == "AIBL":
if study_name == StudyName.ADNI or study_name == StudyName.AIBL:
# ADNImerge contains one row for each visits so there are duplicates
participant_df = participant_df.drop_duplicates(
subset=["alternative_id_1"], keep="first"
)
elif study_name == "OASIS":
elif study_name == StudyName.OASIS:
# OASIS provides several MRI for the same session
participant_df = participant_df[
~participant_df.alternative_id_1.str.endswith("_MR2")
Expand All @@ -152,9 +158,9 @@ def create_participants_df(

# Adding participant_id column with BIDS ids
for i in range(0, len(participant_df)):
if study_name == "OASIS":
if study_name == StudyName.OASIS:
value = (participant_df["alternative_id_1"][i].split("_"))[1]
elif study_name == "OASIS3":
elif study_name == StudyName.OASIS3:
value = participant_df["alternative_id_1"][i].replace("OAS3", "")
else:
value = remove_space_and_symbols(participant_df["alternative_id_1"][i])
Expand Down Expand Up @@ -187,7 +193,7 @@ def create_participants_df(
def create_sessions_dict_OASIS(
clinical_data_dir,
bids_dir,
study_name,
study_name: StudyName,
clinical_spec_path,
bids_ids,
name_column_ids,
Expand All @@ -214,9 +220,9 @@ def create_sessions_dict_OASIS(
from clinica.utils.stream import cprint

# Load data
location = study_name + " location"
location = f"{study_name.value} location"
sessions = pd.read_csv(clinical_spec_path + "_sessions.tsv", sep="\t")
sessions_fields = sessions[study_name]
sessions_fields = sessions[study_name.value]
field_location = sessions[location]
sessions_fields_bids = sessions["BIDS CLINICA"]
fields_dataset = []
Expand Down Expand Up @@ -255,9 +261,9 @@ def create_sessions_dict_OASIS(
subj_id = str(subj_id)
# Removes all the - from
subj_id_alpha = remove_space_and_symbols(subj_id)
if study_name == "OASIS":
if study_name == StudyName.OASIS:
subj_id_alpha = str(subj_id[0:3] + "IS" + subj_id[3] + subj_id[5:9])
if study_name == "OASIS3":
if study_name == StudyName.OASIS3:
subj_id_alpha = str(subj_id[0:3] + "IS" + subj_id[3:])

# Extract the corresponding BIDS id and create the output file if doesn't exist
Expand All @@ -279,13 +285,13 @@ def create_sessions_dict_OASIS(
session_names = get_bids_sess_list(subj_dir)
for s in session_names:
s_name = s.replace("ses-", "")
if study_name != "OASIS3":
row = file_to_read.iloc[r]
else:
if study_name == StudyName.OASIS3:
row = file_to_read[
file_to_read["MR ID"].str.startswith(subj_id)
& file_to_read["MR ID"].str.endswith(s_name)
].iloc[0]
else:
row = file_to_read.iloc[r]
if subj_bids not in sessions_dict:
sessions_dict.update({subj_bids: {}})
if s_name not in sessions_dict[subj_bids].keys():
Expand All @@ -294,7 +300,10 @@ def create_sessions_dict_OASIS(
{sessions_fields_bids[i]: row[sessions_fields[i]]}
)
# Calculate the difference in months for OASIS3 only
if study_name == "OASIS3" and sessions_fields_bids[i] == "age":
if (
study_name == StudyName.OASIS3
and sessions_fields_bids[i] == "age"
):
diff_years = (
float(sessions_dict[subj_bids][s_name]["age"])
- participants_df[
Expand All @@ -310,7 +319,7 @@ def create_sessions_dict_OASIS(

def create_scans_dict(
clinical_data_dir,
study_name,
study_name: StudyName,
clinic_specs_path,
bids_ids,
name_column_ids,
Expand Down Expand Up @@ -343,12 +352,6 @@ def create_scans_dict(
prev_file = ""
prev_sheet = ""

if study_name not in SUPPORTED_DATASETS:
raise ValueError(
f"Dataset {study_name} is not supported. "
f"Supported datasets are: {SUPPORTED_DATASETS}."
)

# Init the dictionary with the subject ids
for bids_id in bids_ids:
scans_dict[bids_id] = dict()
Expand All @@ -368,12 +371,12 @@ def create_scans_dict(
fields_mod = []

# Extract the fields available and the corresponding bids name, location and type
for i in range(0, len(scans_specs[study_name])):
field = scans_specs[study_name][i]
for i in range(0, len(scans_specs[study_name.value])):
field = scans_specs[study_name.value][i]
if not pd.isnull(field):
fields_dataset.append(field)
fields_bids.append(scans_specs["BIDS CLINICA"][i])
fields_location.append(scans_specs[study_name + " location"][i])
fields_location.append(scans_specs[f"{study_name.value} location"][i])
fields_mod.append(scans_specs["Modalities related"][i])

# For each field available extract the original name, extract from the file all the values and fill a data structure
Expand Down Expand Up @@ -404,7 +407,7 @@ def create_scans_dict(
# case, we just remove the erroneous content and replace it with -4 which AIBL uses as n/a value.
on_bad_lines = ( # noqa: E731
lambda bad_line: bad_line[:-3] + [-4, bad_line[-1]]
if "flutemeta" in file_path and study_name == "AIBL"
if "flutemeta" in file_path and study_name == StudyName.AIBL
else "error"
)

Expand All @@ -418,7 +421,7 @@ def create_scans_dict(
prev_sheet = sheet

for bids_id in bids_ids:
original_id = bids_id.replace("sub-" + study_name, "")
original_id = bids_id.replace(f"sub-{study_name.value}", "")
for session_name in {"ses-" + key for key in ses_dict[bids_id].keys()}:
# When comparing sessions, remove the "-ses" prefix IF it exists
row_to_extract = file_to_read[
Expand All @@ -439,7 +442,7 @@ def create_scans_dict(
# Fill the dictionary with all the information
value = file_to_read.iloc[row_to_extract][fields_dataset[i]]

if study_name == "AIBL": # Deal with special format in AIBL
if study_name == StudyName.AIBL: # Deal with special format in AIBL
if value == "-4":
value = "n/a"
elif fields_bids[i] == "acq_time":
Expand All @@ -462,7 +465,7 @@ def create_scans_dict(


def _write_bids_dataset_description(
study_name: str,
study_name: StudyName,
bids_dir: Union[str, Path],
bids_version: Optional[str] = None,
) -> None:
Expand All @@ -478,9 +481,9 @@ def _write_bids_dataset_description(


def _write_readme(
study_name: str,
study_name: StudyName,
data_dict: dict,
bids_dir: Union[str, Path],
bids_dir: Path,
) -> None:
"""Write `README` at the root of the BIDS directory."""
from clinica.iotools.bids_readme import BIDSReadme
Expand All @@ -490,31 +493,30 @@ def _write_readme(
link=data_dict["link"],
description=data_dict["desc"],
)

with open(Path(bids_dir) / "README", "w") as f:
with open(bids_dir / "README", "w") as f:
bids_readme.write(to=f)


def _write_bids_validator_config(bids_dir: Union[str, Path]) -> None:
def _write_bids_validator_config(bids_dir: Path) -> None:
"""Write `.bids-validator-config.json` at the root of the BIDS directory."""
import json

with open(Path(bids_dir) / ".bids-validator-config.json", "w") as f:
with open(bids_dir / ".bids-validator-config.json", "w") as f:
json.dump(BIDS_VALIDATOR_CONFIG, f, skipkeys=True, indent=4)


def _write_bidsignore(bids_dir: Union[str, Path]) -> None:
def _write_bidsignore(bids_dir: Path) -> None:
"""Write `.bidsignore` file at the root of the BIDS directory."""
with open(Path(bids_dir) / ".bidsignore", "w") as f:
with open(bids_dir / ".bidsignore", "w") as f:
# pet/ is necessary until PET is added to BIDS standard
f.write("\n".join(["swi/\n"]))
f.write("\n".join(["conversion_info/"]))


def write_modality_agnostic_files(
study_name: str,
study_name: StudyName,
readme_data: dict,
bids_dir: Union[str, Path],
bids_dir: Path,
bids_version: Optional[str] = None,
) -> None:
"""
Expand Down
Loading