Source code for nwb2bids.bids_models._participant

import pathlib
import re

import pydantic
import pynwb
import typing_extensions

from ._model_globals import _VALID_ARCHIVES_SEXES, _VALID_BIDS_SEXES, _VALID_ID_REGEX, _VALID_SPECIES_REGEX
from ..bids_models._base_metadata_model import BaseMetadataModel
from ..notifications import Notification


[docs] class Participant(BaseMetadataModel): participant_id: str | None = pydantic.Field( description="A unique identifier for this participant.", default=None, ) species: str | None = pydantic.Field( description=( "The species should be the proper Latin binomial species name from the NCBI Taxonomy " "(for example, Mus musculus)." ), default=None, ) sex: str | None = pydantic.Field( description=( 'String value indicating phenotypical sex, one of "male", "female", "other".\n' '\tFor "male", use one of these values: male, m, M, MALE, Male.\n' '\tFor "female", use one of these values: female, f, F, FEMALE, Female.\n' '\tFor "other", use one of these values: other, o, O, OTHER, Other.' ), default=None, ) strain: str | None = pydantic.Field( description=( "For species other than Homo sapiens, the string value indicating the strain of the species " "(for example, C57BL/6J)." ), default=None, ) # TODO: age (current BIDS is numeric in years, not required) def _check_fields(self, file_paths: list[pathlib.Path] | list[pydantic.HttpUrl]) -> None: # Check if values are specified if self.participant_id is None: notification = Notification.from_definition(identifier="MissingParticipantID", source_file_paths=file_paths) self.notifications.append(notification) if self.species is None: notification = Notification.from_definition(identifier="MissingSpecies", source_file_paths=file_paths) self.notifications.append(notification) if self.sex is None: notification = Notification.from_definition( identifier="MissingParticipantSex", source_file_paths=file_paths ) self.notifications.append(notification) # Check if specified values are valid if ( self.participant_id is not None and re.match(pattern=f"{_VALID_ID_REGEX}$", string=self.participant_id) is None ): notification = Notification.from_definition(identifier="InvalidParticipantID", source_file_paths=file_paths) self.notifications.append(notification) if self.species is not None and re.match(pattern=_VALID_SPECIES_REGEX, string=self.species) is None: notification = Notification.from_definition(identifier="InvalidSpecies", source_file_paths=file_paths) self.notifications.append(notification) if self.sex is not None and _VALID_BIDS_SEXES.get(self.sex, None) is None: notification = Notification.from_definition( identifier="InvalidParticipantSexBIDS", source_file_paths=file_paths ) self.notifications.append(notification) if self.sex is not None and _VALID_ARCHIVES_SEXES.get(self.sex, None) is None: notification = Notification.from_definition( identifier="InvalidParticipantSexArchives", source_file_paths=file_paths ) self.notifications.append(notification)
[docs] @classmethod @pydantic.validate_call def from_nwbfiles(cls, nwbfiles: list[pydantic.InstanceOf[pynwb.NWBFile]]) -> typing_extensions.Self: """ Extracts participant metadata from the in-memory NWBFile objects. """ file_paths = [nwbfile.container_source for nwbfile in nwbfiles] notifications = [] if len(nwbfiles) > 1: notification = Notification.from_definition( identifier="MultipleNWB:Participant", source_file_paths=file_paths ) notifications.append(notification) nwbfile = nwbfiles[0] if nwbfile.subject is None: notification = Notification.from_definition(identifier="MissingParticipant", source_file_paths=file_paths) notifications.append(notification) participant = cls( notifications=notifications, participant_id="0", # Similar to the missing session ID; let placeholder default to "0" ) return participant participant = cls( participant_id=nwbfile.subject.subject_id, species=nwbfile.subject.species, sex=nwbfile.subject.sex, strain=nwbfile.subject.strain, notifications=notifications, # TODO: add more # birthday=nwbfile.participant.date_of_birth, # age=nwbfile.participant.age ) participant._check_fields(file_paths=file_paths) return participant