diff --git a/bids2openminds/main.py b/bids2openminds/main.py index f710850..7c5f7f5 100644 --- a/bids2openminds/main.py +++ b/bids2openminds/main.py @@ -11,7 +11,7 @@ from openminds import IRI from .utility import table_filter, pd_table_value, file_hash, file_storage_size, detect_nifti_version -from .mapping import bids2openminds_instance +from . import mapping def create_openminds_person(full_name): @@ -94,32 +94,66 @@ def create_behavioral_protocol(layout, collection): return behavioral_protocols, behavioral_protocols_dict +def techniques_openminds(suffix): + possible_types = ["Technique", "AnalysisTechnique", "StimulationApproach", + "StimulationTechnique"] + + if suffix in mapping.MAP_2_TECHNIQUES: + items_openminds = mapping.MAP_2_TECHNIQUES[suffix] + else: + return [] + + if items_openminds is None: + return [] + + openminds_techniques_list = [] + for item_openminds in items_openminds: + for possible_type in possible_types: + openminds_type = getattr(controlled_terms, possible_type) + try: + openminds_obj = openminds_type.by_name(item_openminds) + break + except KeyError: + pass + openminds_techniques_list.append(openminds_obj) + return openminds_techniques_list + + def create_techniques(layout_df): suffixs = layout_df["suffix"].unique().tolist() techniques = [] not_techniques_index = ["description", "participants", "events"] for suffix in suffixs: # excluding the None and non thechnique indexes - if not (pd.isna(suffix) or (suffix in not_techniques_index)): - openminds_techniques_cache = bids2openminds_instance( - suffix, "MAP_2_TECHNIQUES") - # Excluding the suffixs that are not in the library or flagged as non technique suffixes - if not pd.isna(openminds_techniques_cache): - techniques.extend(openminds_techniques_cache) - else: - warn( - f"The {suffix} suffix is currently considered an auxiliary file for already existing techniques or a non technique file.") + if not (pd.isnull(suffix) or (suffix in not_techniques_index)): + openminds_techniques_cache = techniques_openminds(suffix) + techniques.extend(openminds_techniques_cache) - return techniques or None + techniques_set = set(techniques) + techniques_list = list(techniques_set) + return techniques_list or None + + +def approaches_openminds(datatype): + + if datatype in mapping.MAP_2_EXPERIMENTAL_APPROACHES: + items_openminds = mapping.MAP_2_EXPERIMENTAL_APPROACHES[datatype] + + approches_list = [] + + for item in items_openminds: + approches_list.append( + controlled_terms.ExperimentalApproach.by_name(item)) + + return approches_list def create_approaches(layout_df): datatypes = layout_df["datatype"].unique().tolist() approaches = set([]) for datatype in datatypes: - if not (pd.isna(datatype)): - approaches.update(bids2openminds_instance( - datatype, "MAP_2_EXPERIMENTAL_APPROACHES")) + if not (pd.isnull(datatype)): + approaches.update(approaches_openminds(datatype)) return list(approaches) or None @@ -233,6 +267,52 @@ def create_dataset(dataset_description, dataset_version, collection): return dataset +def spices_openminds(data_subject: pd.DataFrame): + bids_species = pd_table_value(data_subject, "species") + if bids_species is None: + # In BIDS the default species is homo sapiens. + return controlled_terms.Species.homo_sapiens + if bids_species in mapping.MAP_2_SPECIES: + openminds_species = mapping.MAP_2_SPECIES[bids_species] + return controlled_terms.Species.by_name(openminds_species[0]) + else: + try: + openminds_species = controlled_terms.Species.by_name(bids_species) + warn( + f"You have specified {bids_species} as species, we have autodetected {openminds_species.name}, please verify it.") + return openminds_species + except KeyError: + warn( + f"You have specified {bids_species} we currently don't support this species.") + return None + + +def handedness_openminds(data_subject: pd.DataFrame): + bids_handedness = pd_table_value(data_subject, "handedness") + if bids_handedness is None: + return None + if bids_handedness in mapping.MAP_2_HANDEDNESS: + openminds_handedness = mapping.MAP_2_HANDEDNESS[bids_handedness] + return controlled_terms.Handedness.by_name(openminds_handedness[0]) + else: + warn( + f"You have specified {bids_handedness} which is not a allowed value for handedness defined by BIDS standard.") + return None + + +def sex_openminds(data_subject: pd.DataFrame): + bids_sex = pd_table_value(data_subject, "sex") + if bids_sex is None: + return None + if bids_sex in mapping.MAP_2_BIOLOGICALSEX: + bids_sex = mapping.MAP_2_BIOLOGICALSEX[bids_sex] + return controlled_terms.BiologicalSex.by_name(bids_sex[0]) + else: + warn( + f"You have specified {bids_sex} which is not a allowed value for handedness defined by BIDS standard.") + return None + + def create_subjects(subject_id, layout_df, layout, collection): sessions = layout.get_sessions() @@ -286,8 +366,6 @@ def create_subjects(subject_id, layout_df, layout, collection): # Select the tsv file of the table participants_path_tsv = pd_table_value(table_filter( participants_paths, ".tsv", "extension"), "path") - participants_path_json = pd_table_value(table_filter( - participants_paths, ".json", "extension"), "path") participants_table = pd.read_csv(participants_path_tsv, sep="\t", header=0) for subject in subject_id: @@ -299,8 +377,7 @@ def create_subjects(subject_id, layout_df, layout, collection): if not sessions: state = omcore.SubjectState( age=create_openminds_age(data_subject), - handedness=bids2openminds_instance(pd_table_value( - data_subject, "handedness"), "MAP_2_HANDEDNESS", is_list=False), + handedness=handedness_openminds(data_subject), internal_identifier=f"Studied state {subject_name}".strip(), lookup_label=f"Studied state {subject_name}".strip() ) @@ -312,8 +389,7 @@ def create_subjects(subject_id, layout_df, layout, collection): if not (table_filter(table_filter(layout_df, session, "session"), subject, "subject").empty): state = omcore.SubjectState( age=create_openminds_age(data_subject), - handedness=bids2openminds_instance(pd_table_value( - data_subject, "handedness"), "MAP_2_HANDEDNESS", is_list=False), + handedness=handedness_openminds(data_subject), internal_identifier=f"Studied state {subject_name} {session}".strip( ), lookup_label=f"Studied state {subject_name} {session}".strip( @@ -324,13 +400,11 @@ def create_subjects(subject_id, layout_df, layout, collection): state_cache.append(state) subject_state_dict[f"{subject}"] = state_cache_dict subject_cache = omcore.Subject( - biological_sex=bids2openminds_instance(pd_table_value( - data_subject, "sex"), "MAP_2_SEX", is_list=False), + biological_sex=sex_openminds(data_subject), lookup_label=f"{subject_name}", internal_identifier=f"{subject_name}", # TODO species should default to homo sapiens - species=bids2openminds_instance(pd_table_value( - data_subject, "species"), "MAP_2_SPECIES", is_list=False), + species=spices_openminds(data_subject), studied_states=state_cache ) subjects_dict[f"{subject}"] = subject_cache diff --git a/bids2openminds/mapping.py b/bids2openminds/mapping.py index 4782e45..f630abf 100644 --- a/bids2openminds/mapping.py +++ b/bids2openminds/mapping.py @@ -1,173 +1,180 @@ -from .utility import openminds_instance - - MAP_2_EXPERIMENTAL_APPROACHES = { - "func": ["@id: https://openminds.ebrains.eu/instances/experimentalApproach/neuroimaging"], + "func": ["neuroimaging"], "dwi": [ - "@id: https://openminds.ebrains.eu/instances/experimentalApproach/neuroimaging", - "@id: https://openminds.ebrains.eu/instances/experimentalApproach/neuralConnectivity", - "@id: https://openminds.ebrains.eu/instances/experimentalApproach/anatomy", + "neuroimaging", + "neural connectivity", + "anatomy" ], - "fmap": ["@id: https://openminds.ebrains.eu/instances/experimentalApproach/neuroimaging"], + "fmap": ["neuroimaging"], "anat": [ - "@id: https://openminds.ebrains.eu/instances/experimentalApproach/neuroimaging", - "@id: https://openminds.ebrains.eu/instances/experimentalApproach/anatomy", + "neuroimaging", + "anatomy" ], "perf": [ - "@id: https://openminds.ebrains.eu/instances/experimentalApproach/neuroimaging", - "@id: https://openminds.ebrains.eu/instances/experimentalApproach/anatomy", + "neuroimaging", + "anatomy" ], - "meg": ["@id: https://openminds.ebrains.eu/instances/experimentalApproach/neuroimaging"], - "eeg": ["@id: https://openminds.ebrains.eu/instances/experimentalApproach/electrophysiology"], - "ieeg": ["@id: https://openminds.ebrains.eu/instances/experimentalApproach/electrophysiology"], - "beh": ["@id: https://openminds.ebrains.eu/instances/experimentalApproach/behavior"], + "meg": ["neuroimaging"], + "eeg": ["electrophysiology"], + "ieeg": ["electrophysiology"], + "beh": ["behavior"], "pet": [ - "@id: https://openminds.ebrains.eu/instances/experimentalApproach/radiology", - "@id: https://openminds.ebrains.eu/instances/experimentalApproach/neuroimaging", + "neuroimaging", + "radiology" ], "micr": [ - "@id: https://openminds.ebrains.eu/instances/experimentalApproach/microscopy", - "@id: https://openminds.ebrains.eu/instances/experimentalApproach/anatomy", - "@id: https://openminds.ebrains.eu/instances/experimentalApproach/histology", + "microscopy", + "anatomy", + "histology" ], - "nirs": ["@id: https://openminds.ebrains.eu/instances/experimentalApproach/neuroimaging"], + "nirs": ["neuroimaging"] } MAP_2_TECHNIQUES = { - "angio": ["Angiogram"], - "M0map": ["Equilibrium magnetization (M0) map"], - "FLASH": ["Fast-Low-Angle-Shot image"], - "FLAIR": ["Fluid attenuated inversion recovery image"], - "UNIT1": ["Homogeneous (flat) T1-weighted MP2RAGE image"], - "inplaneT1": ["Inplane T1"], - "inplaneT2": ["Inplane T2"], - "R1map": ["Longitudinal relaxation rate image"], - "T1map": ["Longitudinal relaxation time image"], - "MTVmap": ["Macromolecular tissue volume (MTV) image"], - "MTRmap": ["Magnetization transfer ratio image"], - "MTsat": ["Magnetization transfer saturation image"], - "MWFmap": ["Myelin water fraction image"], - "S0map": ["Observed signal amplitude (S0) image"], - "R2starmap": ["Observed transverse relaxation rate image"], - "T2starmap": ["Observed transverse relaxation time image"], - "PDT2": ["PD and T2 weighted image"], - "PDw": ["Proton density (PD) weighted image"], - "PD": ["Proton density image"], - "PDmap": ["Proton density image"], - "Chimap": ["Quantitative susceptibility map (QSM)"], - "RB1map": ["RF receive sensitivity map"], - "TB1map": ["RF transmit field image"], - "T1rho": ["T1 in rotating frame (T1 rho) image"], - "T1w": ["T1-weighted image"], - "T2w": ["T2-weighted image"], - "T2star": ["T2* image"], - "T2starw": ["T2star weighted image"], - "R2map": ["True transverse relaxation rate image"], - "T2map": ["True transverse relaxation time image"], - "bold": ["Blood-Oxygen-Level Dependent image"], - "cbv": ["Cerebral blood volume image"], - "phase": ["Phase image"], - "defacemask": ["Defacing masks"], - "epi": ["EPI"], - "fieldmap": ["Fieldmap"], - "magnitude": ["Magnitude"], - "magnitude1": ["Magnitude"], - "magnitude2": ["Magnitude"], - "phase1": ["Phase"], - "phase2": ["Phase"], - "phasediff": ["Phase-difference"], - "dwi": ["@id: https://openminds.ebrains.eu/instances/technique/diffusionWeightedImaging"], - "sbref": ["Single-band reference image"], - "asl": ["Arterial Spin Labeling"], - "m0scan": ["M0"], - "eeg": ["@id:https://openminds.ebrains.eu/instances/technique/electroencephalography"], - "ieeg": ["Intracranial Electroencephalography"], - "physio": ["Physiological continuous recordings"], - "stim": ["stimulation continuous recordings"], - "beh": ["Behavioral experiments"], - "pet": ["@id:https://openminds.ebrains.eu/instances/technique/positronEmissionTomography"], - "2PE": ["2-photon excitation microscopy"], - "BF": ["Bright-field microscopy"], - "CARS": ["Coherent anti-Stokes Raman spectroscopy"], - "CONF": ["Confocal microscopy"], - "DIC": ["Differential interference contrast microscopy"], - "DF": ["Dark-field microscopy"], - "FLUO": ["Fluorescence microscopy"], - "MPE": ["Multi-photon excitation microscopy"], - "NLO": ["Nonlinear optical microscopy"], - "OCT": ["Optical coherence tomography"], - "PC": ["Phase-contrast microscopy"], - "PLI": ["Polarized-light microscopy"], - "SEM": ["Scanning electron microscopy"], - "SPIM": ["Selective plane illumination microscopy"], - "SR": ["Super-resolution microscopy"], - "TEM": ["Transmission electron microscopy"], - "uCT": ["Micro-CT"], - "nirs": ["Near-Infrared Spectroscopy"], - "motion": ["Motion"], + "angio": ["angiography"], + "M0map": ["equilibrium magnetization mapping"], + "FLASH": ["fast-low-angle-shot pulse sequence"], #TODO instance TBD + "FLAIR": ["fluid attenuated inversion recovery pulse sequence"], #TODO instance TBD + "UNIT1": None, #TODO instance TBD + "inplaneT1": [ + "T1 pulse sequence", + "structural magnetic resonance imaging" + ], #TODO sMRI + "inplaneT2": [ + "T2 pulse sequence", + "structural magnetic resonance imaging" + ], #TODO sMRI + "R1map": None, #TODO instance TBD + "T1map": None, #TODO instance TBD + "MTVmap": [ + "quantitative magnetic resonance imaging", + "macromolecular tissue volume image processing" + ], #TODO other? + "MTRmap": [ + "magnetization transfer imaging", + "magnetization transfer ratio image processing", + "magnetization transfer pulse sequence" + ], #TODO instances + "MTsat": [ + "magnetization transfer imaging", + "magnetization transfer saturation image processing", + "magnetization transfer pulse sequence" + ], #TODO instances + "MWFmap": [ + "myelin water imaging", + "T2 pulse sequence", + "myelin water fraction image processing" + ], #TODO instances + "S0map": None, #TODO instances + "R2starmap": None, #TODO instance TBD + "T2starmap": None, #TODO instance TBD + "PDT2": None, #TODO instance TBD + "PDw": None, #TODO instance TBD + "PD": None, #TODO instance TBD + "PDmap": None, #TODO instance TBD + "Chimap": None, #TODO instance TBD + "RB1map": None, #TODO instance TBD + "TB1map": None, #TODO instance TBD + "T1rho": None, #TODO instance TBD + "T1w": None, #TODO instance TBD + "T2w": None, #TODO instance TBD + "T2star": None, #TODO instance TBD + "T2starw": None, #TODO instance TBD + "R2map": None, #TODO instance TBD + "T2map": None, #TODO instance TBD + "bold": None, #TODO instance TBD + "cbv": None, #TODO instance TBD + "phase": None, #TODO instance TBD + "defacemask": None, #TODO instance TBD + "epi": None, #TODO instance TBD + "fieldmap": None, #TODO instance TBD + "magnitude": None, #TODO instance TBD + "magnitude1": None, #TODO instance TBD + "magnitude2": None, #TODO instance TBD + "phase1": None, #TODO instance TBD + "phase2": None, #TODO instance TBD + "phasediff": None, #TODO instance TBD + "dwi": ["diffusion-weighted imaging"], + "sbref": None, #TODO instance TBD + "asl": None, #TODO instance TBD + "m0scan": None, #TODO instance TBD + "eeg": ["electroencephalography"], + "ieeg": ["intracranial electroencephalography"], + "physio": None, #TODO instance TBD + "stim": None, #TODO instance TBD + "beh": None, #TODO instance TBD + "pet": ["positron emission tomography"], + "2PE": ["two-photon fluorescence microscopy"], + "BF": None, #TODO instance TBD + "CARS": None, #TODO instance TBD + "CONF": ["confocal microscopy"], + "DIC": None, #TODO instance TBD + "DF": None, #TODO instance TBD + "FLUO": None, #TODO instance TBD + "MPE": None, #TODO instance TBD + "NLO": None, #TODO instance TBD + "OCT": None, #TODO instance TBD + "PC": None, #TODO instance TBD + "PLI": ["polarized light microscopy"], + "SEM": None, #TODO instance TBD + "SPIM": None, #TODO instance TBD + "SR": None, #TODO instance TBD + "TEM": ["transmission electron microscopy"], + "uCT": None, #TODO instance TBD + "nirs": None, #TODO instance TBD + "motion": None, #TODO instance TBD } -MAP_2_UNITS = {"year": ["@id:https://openminds.ebrains.eu/instances/unitOfMeasurement/year"]} +MAP_2_UNITS = { + "year": ["year"] +} -MAP_2_SEX = { - "male": ["@id: https://openminds.ebrains.eu/instances/biologicalSex/male"], - "m": ["@id: https://openminds.ebrains.eu/instances/biologicalSex/male"], - "M": ["@id: https://openminds.ebrains.eu/instances/biologicalSex/male"], - "MALE": ["@id: https://openminds.ebrains.eu/instances/biologicalSex/male"], - "Male": ["@id: https://openminds.ebrains.eu/instances/biologicalSex/male"], - "female": ["@id: https://openminds.ebrains.eu/instances/biologicalSex/female"], - "f": ["@id: https://openminds.ebrains.eu/instances/biologicalSex/female"], - "F": ["@id: https://openminds.ebrains.eu/instances/biologicalSex/female"], - "FEMALE": ["@id: https://openminds.ebrains.eu/instances/biologicalSex/female"], - "Female": ["@id: https://openminds.ebrains.eu/instances/biologicalSex/female"], +MAP_2_BIOLOGICALSEX = { + "male": ["male"], + "m": ["male"], + "M": ["male"], + "MALE": ["male"], + "Male": ["male"], + "female": ["female"], + "f": ["female"], + "F": ["female"], + "FEMALE": ["female"], + "Female": ["female"] } MAP_2_HANDEDNESS = { - "left": ["@id:https://openminds.ebrains.eu/instances/handedness/leftHandedness"], - "l": ["@id:https://openminds.ebrains.eu/instances/handedness/leftHandedness"], - "L": ["@id:https://openminds.ebrains.eu/instances/handedness/leftHandedness"], - "LEFT": ["@id:https://openminds.ebrains.eu/instances/handedness/leftHandedness"], - "Left": ["@id:https://openminds.ebrains.eu/instances/handedness/leftHandedness"], - "right": ["@id:https://openminds.ebrains.eu/instances/handedness/rightHandedness"], - "r": ["@id:https://openminds.ebrains.eu/instances/handedness/rightHandedness"], - "R": ["@id:https://openminds.ebrains.eu/instances/handedness/rightHandedness"], - "RIGHT": ["@id:https://openminds.ebrains.eu/instances/handedness/rightHandedness"], - "Right": ["@id:https://openminds.ebrains.eu/instances/handedness/rightHandedness"], - "ambidextrous": ["@id:https://openminds.ebrains.eu/instances/handedness/ambidextrousHandedness"], - "a": ["@id:https://openminds.ebrains.eu/instances/handedness/ambidextrousHandedness"], - "A": ["@id:https://openminds.ebrains.eu/instances/handedness/ambidextrousHandedness"], - "AMBIDEXTROUS": ["@id:https://openminds.ebrains.eu/instances/handedness/ambidextrousHandedness"], - "Ambidextrous": ["@id:https://openminds.ebrains.eu/instances/handedness/ambidextrousHandedness"], + "left": ["left handedness"], + "l": ["left handedness"], + "L": ["left handedness"], + "LEFT": ["left handedness"], + "Left": ["left handedness"], + "right": ["right handedness"], + "r": ["right handedness"], + "R": ["right handedness"], + "RIGHT": ["right handedness"], + "Right": ["right handedness"], + "ambidextrous": ["ambidextrous handedness"], + "a": ["ambidextrous handedness"], + "A": ["ambidextrous handedness"], + "AMBIDEXTROUS": ["ambidextrous handedness"], + "Ambidextrous": ["ambidextrous handedness"] } MAP_2_SPECIES = { - "homo sapiens": ["@id: https://openminds.ebrains.eu/instances/species/homoSapiens"], - "mus musculus": ["@id: https://openminds.ebrains.eu/instances/species/musMusculus"], - "rattus norvegicus": ["@id: https://openminds.ebrains.eu/instances/species/rattusNorvegicus"], + "homo sapiens": ["Homo sapiens"], + "mus musculus": ["Mus musculus"], + "rattus norvegicus": ["Rattus norvegicus"] } -sample_types = { - "cell line", - "in vitro differentiated cells", - "primary cell", - "cell-free sample", - "cloning host", - "tissue", - "whole organisms", - "organoid", - "technical sample", -} - - -def bids2openminds_instance(bids_instance, dictionary, is_list: bool = True): - if bids_instance is None: - return None - - try: - instance = openminds_instance(eval(dictionary)[bids_instance], is_list=is_list) - # TODO if warning warn(f"{item}is not a proper openMINDS instance") should say that this instance is not yet in openminds - return instance - except KeyError: - # Handle the case where the specified bids instance is not present in the dictionary - KeyError(f"Error: the bids instance '{bids_instance}' is not present in dictionary.") +#sample_types = { +# "cell line": None, #TODO instance TBD +# "in vitro differentiated cells": None, #TODO instance TBD +# "primary cell": None, #TODO instance TBD +# "cell-free sample": None, #TODO instance TBD +# "cloning host": None, #TODO instance TBD +# "tissue": None, #TODO instance TBD +# "whole organisms": None, #TODO instance TBD +# "organoid": None, #TODO instance TBD +# "technical sample": None #TODO instance TBD +#} diff --git a/bids2openminds/utility.py b/bids2openminds/utility.py index 0dbf9ac..ba557f3 100644 --- a/bids2openminds/utility.py +++ b/bids2openminds/utility.py @@ -12,11 +12,6 @@ from openminds.latest.controlled_terms import UnitOfMeasurement -def camel_to_snake(name): - name = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name) - return re.sub("([a-z0-9])([A-Z])", r"\1_\2", name).lower() - - def read_json(file_path: str) -> dict: """ Reads the content of a JSON file and returns it as a Python dictionary. @@ -69,30 +64,6 @@ def table_filter(dataframe: pd.DataFrame, filter_str: str, column: str = "suffix KeyError(f"Error: Column '{column}' not found in the DataFrame.") -def openminds_instance(list: list, Terminologie: str = None, is_list: bool = True): - openminds_list = [] - - for item in list: - if item.replace(" ", "")[0:32] == "@id:https://openminds.ebrains.eu": - slash_location = item.rfind("/") - item_name = item[slash_location + 1:] - item_name_snake = camel_to_snake(item_name) - if not (Terminologie): - Terminologie = item[item[:slash_location].rfind( - "/") + 1: slash_location] - Terminologie = Terminologie[0].upper() + Terminologie[1:] - controlled_class = getattr(controlled_terms, Terminologie) - openminds_item = getattr(controlled_class, item_name_snake) - openminds_list.append(openminds_item) - else: - warn(f"{item}is not a proper openMINDS instance") - - if is_list: - return openminds_list - else: - return openminds_item - - def pd_table_value(data_frame, column_name, not_list: bool = True): try: if column_name in data_frame.columns: diff --git a/test/test_example_datasets_click.py b/test/test_example_datasets_click.py index 75a7372..3c32416 100644 --- a/test/test_example_datasets_click.py +++ b/test/test_example_datasets_click.py @@ -4,7 +4,7 @@ from bids2openminds.converter import convert_click from click.testing import CliRunner -(test_data_set, number_of_openminds_files) = ("ds003", 141) +(test_data_set, number_of_openminds_files) = ("ds003", 143) def test_example_datasets_click():