diff --git a/bids2openminds/converter.py b/bids2openminds/converter.py index b2f63ca..af7d50c 100644 --- a/bids2openminds/converter.py +++ b/bids2openminds/converter.py @@ -2,6 +2,7 @@ from bids import BIDSLayout, BIDSValidator from openminds import Collection import os +import yaml import click from . import main from . import utility @@ -27,10 +28,11 @@ def convert(input_path, save_output=False, output_path=None, multiple_files=Fal subjects_id = bids_layout.get_subjects() - # imprting the dataset description file containing some of the + # importing the dataset description file containing some of the metadata dataset_description_path = utility.table_filter(layout_df, "description") - dataset_description = utility.read_json(dataset_description_path.iat[0, 0]) + citation_path = utility.table_filter(layout_df, "CITATION") + citation = yaml.safe_load(open(citation_path.iat[0, 0], encoding="utf-8")) if not citation_path.empty else None [subjects_dict, subject_state_dict, subjects_list] = main.create_subjects( subjects_id, layout_df, bids_layout, collection) @@ -42,7 +44,7 @@ def convert(input_path, save_output=False, output_path=None, multiple_files=Fal layout_df, input_path, collection) dataset_version = main.create_dataset_version( - bids_layout, dataset_description, layout_df, subjects_list, file_repository, behavioral_protocols, collection) + bids_layout, citation, dataset_description, layout_df, subjects_list, file_repository, behavioral_protocols, collection) dataset = main.create_dataset( dataset_description, dataset_version, collection) diff --git a/bids2openminds/main.py b/bids2openminds/main.py index 92401f4..7a3d9e5 100644 --- a/bids2openminds/main.py +++ b/bids2openminds/main.py @@ -48,8 +48,35 @@ def create_openminds_person(full_name): def create_persons(dataset_description, collection): + # citation.cff case + if "authors" in dataset_description: + person_list = dataset_description["authors"] + openminds_list=[] + for person in person_list: + person_orcid = None + person_affiliation = None + person_contact_information = None + if 'orcid' in person: + person_orcid = [omcore.ORCID(identifier=person['orcid'])] + if 'email' in person: + person_contact_information = omcore.ContactInformation(email=person['email']) + if 'affiliation' in person: + # Handle multiple affiliations separated by semicolon + affiliation_list = [item.strip() for item in person['affiliation'].split(';')] + person_affiliation = [] + for affiliation in affiliation_list: + person_affiliation.append(omcore.Affiliation( + member_of=omcore.Organization(full_name=affiliation))) + + openminds_person = omcore.Person( + affiliations=person_affiliation, digital_identifiers=person_orcid, given_name=person['given-names'], + family_name=person['family-names'], contact_information=person_contact_information) + openminds_list.append(openminds_person) + collection.add(openminds_person) + return openminds_list - if "Authors" in dataset_description: + # dataset_description.json case + elif "Authors" in dataset_description: person_list = dataset_description["Authors"] else: return None @@ -184,21 +211,37 @@ def create_openminds_age(data_subject): return None -def create_dataset_version(bids_layout, dataset_description, layout_df, studied_specimens, file_repository, behavioral_protocols, collection): +def create_dataset_version(bids_layout, citation, dataset_description, layout_df, studied_specimens, file_repository, behavioral_protocols, collection): # Fetch the dataset type from dataset description file # dataset_type=bids2openminds_instance(dataset_description.get("DatasetType",None)) + license = None + digital_identifier = None + name = None + version_identifier = None + if citation: + if 'doi' in citation: + digital_identifier = omcore.DOI(identifier=citation['doi']) + if 'license' in citation: + for lic in omcore.License.instances(): + if citation['license'] == getattr(lic, "short_name", None): + license = lic + break + if 'title' in citation: + name = citation['title'] + if 'version' in citation: + version_identifier = citation['version'] + authors = create_persons(citation, collection) - # Fetch the digitalIdentifier from dataset description file - - if "DatasetDOI" in dataset_description: - digital_identifier = omcore.DOI( - identifier=dataset_description["DatasetDOI"]) else: - digital_identifier = None + name = dataset_description["Name"] + # Fetch the digitalIdentifier from dataset description file + if "DatasetDOI" in dataset_description: + digital_identifier = omcore.DOI( + identifier=dataset_description["DatasetDOI"]) - authors = create_persons(dataset_description, collection) + authors = create_persons(dataset_description, collection) if "Acknowledgements" in dataset_description: other_contribution = dataset_description["Acknowledgements"] @@ -234,19 +277,20 @@ def create_dataset_version(bids_layout, dataset_description, layout_df, studied_ experimental_approaches = create_approaches(layout_df) dataset_version = omcore.DatasetVersion( + authors=authors, + behavioral_protocols=behavioral_protocols, + data_types=dataset_type, digital_identifier=digital_identifier, experimental_approaches=experimental_approaches, - short_name=dataset_description["Name"], - full_name=dataset_description["Name"], - studied_specimens=studied_specimens, - authors=authors, - techniques=techniques, + full_name=name, how_to_cite=how_to_cite, + license=license, repository=file_repository, - behavioral_protocols=behavioral_protocols, - data_types=dataset_type + short_name=name, + studied_specimens=studied_specimens, + techniques=techniques, + version_identifier=version_identifier, # other_contributions=other_contribution # needs to be a Contribution object - # version_identifier ) collection.add(dataset_version) diff --git a/pyproject.toml b/pyproject.toml index 8d5088c..52c5202 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,12 +2,13 @@ name = "bids2openminds" version = "0.1.1" dependencies = [ - "bids-validator == 1.14.6" , + "bids-validator == 1.14.6", "bids", "openminds >= 0.2.3", "click>=8.1", "pandas", - "nameparser >= 1.1.3" + "nameparser >= 1.1.3", + "PyYAML" ] requires-python = ">=3.9" authors = [ diff --git a/test/test_dataset_version.py b/test/test_dataset_version.py new file mode 100644 index 0000000..ec5a1c9 --- /dev/null +++ b/test/test_dataset_version.py @@ -0,0 +1,39 @@ +import pytest + +import pandas as pd +from openminds import Collection +import openminds.v3.core as omcore + +from bids2openminds.main import create_dataset_version + +def test_create_dataset_version_citation(): + # Mock loaded CITATION.cff + citation = { + "title": "My Dataset", + "version": "1.05.9", + "doi": "10.5281/zenodo.123456", + "license": "Apache-2.0" + } + + # Mock BIDS layout DataFrame + layout_df = pd.DataFrame({ + "suffix": ["T1w", "T2w", "bold"], + "path": ["sub-01/anat/sub-01_T1w.nii.gz", "sub-01/anat/sub-01_T2w.nii.gz", "sub-01/func/sub-01_task-rest_bold.nii.gz"], + "datatype": ["anat", "anat", "func"] + }) + + citation_dataset_version = create_dataset_version( + "", citation, {}, layout_df, [], [], [], Collection() + ) + + expected = omcore.DatasetVersion( + digital_identifier=omcore.DOI(identifier="10.5281/zenodo.123456"), + full_name="My Dataset", + short_name="My Dataset", + license=omcore.License.apache_2_0, + version_identifier="1.05.9" + ) + + for field in ["full_name", "short_name", "version_identifier", "license"]: + assert getattr(citation_dataset_version, field) == getattr(expected, field) + assert citation_dataset_version.digital_identifier.identifier == expected.digital_identifier.identifier diff --git a/test/test_person.py b/test/test_person.py index 6dd5371..4b561c7 100644 --- a/test/test_person.py +++ b/test/test_person.py @@ -1,8 +1,11 @@ # test for create_openminds_person function in the main import pytest -from bids2openminds.main import create_openminds_person + +from openminds import Collection import openminds.v3.core as omcore +from bids2openminds.main import create_openminds_person, create_persons + # Test data: (full_name, given_name, family_name) example_names = [("John Ronald Reuel Tolkien", "John Ronald Reuel", "Tolkien"), ("Bilbo Baggins", "Bilbo", "Baggins"), @@ -28,6 +31,60 @@ def test_create_openminds_person(full_name, given_name, family_name): # assert openminds_person_object == bids2openminds_person_object +def test_create_openminds_person_citation_explicit(): + citation = { + "authors": [ + { + "family-names": "Awart", + "given-names": "Peter", + "affiliation": "Place1", + "orcid": "https://orcid.org/1234-5678-9123-4567" + }, + { + "family-names": "Detienne", + "given-names": "Franck", + "affiliation": "Place1; Place2" + } + ] + } + + persons = create_persons(citation, Collection()) + author1 = persons[0] + author2 = persons[1] + + # Expected objects + expected_author1 = omcore.Person( + given_name="Peter", + family_name="Awart", + affiliations=[ + omcore.Affiliation(member_of=omcore.Organization(full_name="Place1")) + ], + digital_identifiers=[omcore.ORCID(identifier="https://orcid.org/1234-5678-9123-4567")] + ) + + expected_author2 = omcore.Person( + given_name="Franck", + family_name="Detienne", + affiliations=[ + omcore.Affiliation(member_of=omcore.Organization(full_name="Place1")), + omcore.Affiliation(member_of=omcore.Organization(full_name="Place2")) + ] + ) + + assert author1.given_name == expected_author1.given_name + assert author1.family_name == expected_author1.family_name + assert author1.digital_identifiers[0].identifier == expected_author1.digital_identifiers[0].identifier + assert author2.given_name == expected_author2.given_name + assert author2.family_name == expected_author2.family_name + + # Single affiliation + assert author1.affiliations[0].member_of.full_name == expected_author1.affiliations[0].member_of.full_name + + # Multiple affiliations + assert author2.affiliations[0].member_of.full_name == expected_author2.affiliations[0].member_of.full_name + assert author2.affiliations[1].member_of.full_name == expected_author2.affiliations[1].member_of.full_name + + @pytest.mark.parametrize("full_not_name", example_not_names) def test_create_openminds_person_not_names(full_not_name): bids2openminds_person_object = create_openminds_person(full_not_name)