diff --git a/CHANGELOG.md b/CHANGELOG.md index 403a1bf8f..27082a57b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Bug fixes * Fixed the default naming of multiple electrical series in the `SpikeGLXConverterPipe`. [PR #957](https://github.com/catalystneuro/neuroconv/pull/957) +* Fixed an issue when passing conversion options to a sub-converter (like the popular `SpikeGLXConverterPipe`) nested inside another `NWBConverter`. [PR #979](https://github.com/catalystneuro/neuroconv/pull/979) ### Improvements * The `OpenEphysBinaryRecordingInterface` now uses `lxml` for extracting the session start time from the settings.xml file and does not depend on `pyopenephys` anymore. [PR #971](https://github.com/catalystneuro/neuroconv/pull/971) diff --git a/src/neuroconv/nwbconverter.py b/src/neuroconv/nwbconverter.py index b654c1a68..47c99f74d 100644 --- a/src/neuroconv/nwbconverter.py +++ b/src/neuroconv/nwbconverter.py @@ -3,6 +3,7 @@ import json import warnings from collections import Counter +from inspect import signature from pathlib import Path from typing import Dict, List, Literal, Optional, Tuple, Union @@ -158,10 +159,24 @@ def create_nwbfile(self, metadata: Optional[dict] = None, conversion_options: Op def add_to_nwbfile(self, nwbfile: NWBFile, metadata, conversion_options: Optional[dict] = None) -> None: conversion_options = conversion_options or dict() - for interface_name, data_interface in self.data_interface_objects.items(): - data_interface.add_to_nwbfile( - nwbfile=nwbfile, metadata=metadata, **conversion_options.get(interface_name, dict()) - ) + for interface_key, data_interface in self.data_interface_objects.items(): + if isinstance(data_interface, NWBConverter): + subconverter_kwargs = dict(nwbfile=nwbfile, metadata=metadata) + + # Certain subconverters fully expose control over their interfaces conversion options + # (such as iterator options, including progress bar details) + subconverter_keyword_arguments = list(signature(data_interface.add_to_nwbfile).parameters.keys()) + if "conversion_options" in subconverter_keyword_arguments: + subconverter_kwargs["conversion_options"] = conversion_options.get(interface_key, None) + # Others do not, and instead expose simplified global keywords similar to a classic interface + else: + subconverter_kwargs.update(conversion_options.get(interface_key, dict())) + + data_interface.add_to_nwbfile(**subconverter_kwargs) + else: + data_interface.add_to_nwbfile( + nwbfile=nwbfile, metadata=metadata, **conversion_options.get(interface_key, dict()) + ) def run_conversion( self, diff --git a/tests/test_on_data/test_format_converters/test_spikeglx_converter.py b/tests/test_on_data/test_format_converters/test_spikeglx_converter.py index 910e238a0..ddc4121f1 100644 --- a/tests/test_on_data/test_format_converters/test_spikeglx_converter.py +++ b/tests/test_on_data/test_format_converters/test_spikeglx_converter.py @@ -96,6 +96,30 @@ class TestConverter(NWBConverter): expected_session_start_time = datetime(2020, 11, 3, 10, 35, 10).astimezone() self.assertNWBFileStructure(nwbfile_path=nwbfile_path, expected_session_start_time=expected_session_start_time) + def test_single_probe_spikeglx_converter_in_converter_preview(self): + class TestConverter(NWBConverter): + data_interface_classes = dict(SpikeGLX=SpikeGLXConverterPipe) + + source_data = dict(SpikeGLX=dict(folder_path=str(SPIKEGLX_PATH / "Noise4Sam_g0"))) + converter = TestConverter(source_data=source_data) + + conversion_options = dict( + SpikeGLX={"imec0.ap": {"stub_test": True}, "imec0.lf": {"stub_test": True}, "nidq": {"stub_test": True}} + ) + + nwbfile_path = self.tmpdir / "test_single_probe_spikeglx_converter_preview.nwb" + converter.run_conversion(nwbfile_path=nwbfile_path, conversion_options=conversion_options) + + expected_session_start_time = datetime(2020, 11, 3, 10, 35, 10).astimezone() + self.assertNWBFileStructure(nwbfile_path=nwbfile_path, expected_session_start_time=expected_session_start_time) + + with NWBHDF5IO(path=nwbfile_path) as io: + nwbfile = io.read() + + assert nwbfile.acquisition["ElectricalSeriesAPImec0"].data.shape == (100, 384) + assert nwbfile.acquisition["ElectricalSeriesLFImec0"].data.shape == (100, 384) + assert nwbfile.acquisition["ElectricalSeriesNIDQ"].data.shape == (100, 9) + class TestMultiProbeSpikeGLXConverter(TestCase): maxDiff = None