import importlib.util
import warnings
import numpy as np
from pydantic import FilePath, validate_call
from pynwb import NWBFile
from ...baseextractorinterface import BaseExtractorInterface
from ...tools.nwb_helpers import make_nwbfile_from_metadata
from ...utils import (
DeepDict,
get_json_schema_from_method_signature,
get_metadata_schema_for_icephys,
get_schema_from_hdmf_class,
)
[docs]
class BaseIcephysInterface(BaseExtractorInterface):
"""Primary class for all intracellular NeoInterfaces."""
keywords = ("intracellular electrophysiology", "patch clamp", "current clamp")
[docs]
@classmethod
def get_source_schema(cls) -> dict:
source_schema = get_json_schema_from_method_signature(method=cls.__init__, exclude=[])
return source_schema
@validate_call
def __init__(self, file_paths: list[FilePath]):
# Check if the ndx_dandi_icephys module is available
dandi_icephys_spec = importlib.util.find_spec("ndx_dandi_icephys")
if dandi_icephys_spec is not None:
from ndx_dandi_icephys import DandiIcephysMetadata
self.DandiIcephysMetadata = DandiIcephysMetadata
self.HAVE_NDX_DANDI_ICEPHYS = True
else:
self.DandiIcephysMetadata = None
self.HAVE_NDX_DANDI_ICEPHYS = False
from ...tools.neo import get_number_of_electrodes, get_number_of_segments
super().__init__(file_paths=file_paths)
self.readers_list = list()
for f in file_paths:
file_source_data = {"filename": f}
self.readers_list.append(self._initialize_extractor(file_source_data))
self.n_segments = get_number_of_segments(neo_reader=self.readers_list[0], block=0)
self.n_channels = get_number_of_electrodes(neo_reader=self.readers_list[0])
self._timestamps = None
[docs]
def get_original_timestamps(self) -> np.ndarray:
raise NotImplementedError("Icephys interfaces do not yet support timestamps.")
[docs]
def get_timestamps(self) -> np.ndarray:
raise NotImplementedError("Icephys interfaces do not yet support timestamps.")
[docs]
def set_aligned_timestamps(self, aligned_timestamps: np.ndarray):
raise NotImplementedError("Icephys interfaces do not yet support timestamps.")
[docs]
def set_aligned_starting_time(self, aligned_starting_time: float):
raise NotImplementedError("This icephys interface has not specified the method for aligning starting time.")
[docs]
def align_by_interpolation(self, unaligned_timestamps: np.ndarray, aligned_timestamps: np.ndarray):
raise NotImplementedError("Icephys interfaces do not yet support timestamps.")
[docs]
def add_to_nwbfile(
self,
nwbfile: NWBFile,
metadata: dict = None,
*args, # TODO: change to * (keyword only) on or after August 2026
icephys_experiment_type: str = "voltage_clamp",
skip_electrodes: tuple[int] = (),
):
"""
Primary function for converting raw (unprocessed) intracellular data to the NWB standard.
Parameters
----------
nwbfile : NWBFile
nwb file to which the recording information is to be added
metadata : dict, optional
metadata info for constructing the nwb file (optional).
icephys_experiment_type : {'voltage_clamp', 'current_clamp', 'izero'}
Type of icephys recording.
skip_electrodes : tuple, optional
Electrode IDs to skip. Defaults to ().
"""
# Handle deprecated positional arguments
if args:
parameter_names = [
"icephys_experiment_type",
"skip_electrodes",
]
num_positional_args_before_args = 2 # nwbfile, metadata
if len(args) > len(parameter_names):
raise TypeError(
f"add_to_nwbfile() takes at most {len(parameter_names) + num_positional_args_before_args} positional arguments but "
f"{len(args) + num_positional_args_before_args} were given. "
"Note: Positional arguments are deprecated and will be removed on or after August 2026. "
"Please use keyword arguments."
)
positional_values = dict(zip(parameter_names, args))
passed_as_positional = list(positional_values.keys())
warnings.warn(
f"Passing arguments positionally to BaseIcephysInterface.add_to_nwbfile() is deprecated "
f"and will be removed on or after August 2026. "
f"The following arguments were passed positionally: {passed_as_positional}. "
"Please use keyword arguments instead.",
FutureWarning,
stacklevel=2,
)
icephys_experiment_type = positional_values.get("icephys_experiment_type", icephys_experiment_type)
skip_electrodes = positional_values.get("skip_electrodes", skip_electrodes)
from ...tools.neo import add_neo_to_nwb
if nwbfile is None:
nwbfile = make_nwbfile_from_metadata(metadata)
if (
self.HAVE_NDX_DANDI_ICEPHYS
and "ndx-dandi-icephys" in metadata
and "DandiIcephysMetadata" not in nwbfile.lab_meta_data
):
nwbfile.add_lab_meta_data(self.DandiIcephysMetadata(**metadata["ndx-dandi-icephys"]))
for i, reader in enumerate(self.readers_list):
add_neo_to_nwb(
neo_reader=reader,
nwbfile=nwbfile,
metadata=metadata,
icephys_experiment_type=metadata["Icephys"]["Sessions"][i]["icephys_experiment_type"],
stimulus_type=metadata["Icephys"]["Sessions"][i]["stimulus_type"],
skip_electrodes=skip_electrodes,
)