-
Notifications
You must be signed in to change notification settings - Fork 53
Description
Is your feature request related to a problem? Please describe.
Spyglass's insert_sessions() function requires a behavior processing module to be present in the NWB file, even when there is no behavioral data to import. This creates an unnecessary dependency that prevents insertion of sessions that contain only non-behavioral data (e.g., electrophysiology data without position tracking).
Describe the solution you'd like
The insert_sessions() function should gracefully handle NWB files that don't contain a behavior processing module. Specifically, the PositionSource.insert_from_nwbfile() method should check whether a behavior module exists before attempting to access it, and skip position-related imports if no behavioral data is present.
Describe alternatives you've considered
Currently, the only workaround is to add an empty or dummy behavior processing module to the NWB file, even when there's no actual behavioral data. This feels like sub-optimal data representation and adds unnecessary boilerplate to NWB file creation.
Additional context
Here is a minimal reproduction of the problem.
from pynwb.testing.mock.file import mock_NWBFile
from pynwb import NWBHDF5IO
from pathlib import Path
def create_minimal_nwb():
nwbfile = mock_NWBFile(
identifier="behavior_module_requirement_bug_demo",
session_description="Mock NWB file demonstrating Spyglass behavior module requirement bug"
)
nwbfile.add_epoch(start_time=0.0, stop_time=100.0, tags=["01"])
return nwbfile
def insert_session(nwbfile_path: Path):
import datajoint as dj
dj_local_conf_path = "/Users/pauladkisson/Documents/CatalystNeuro/Spyglass/spyglass/dj_local_conf.json"
dj.config.load(dj_local_conf_path)
import spyglass.common as sgc
import spyglass.data_import as sgi
from spyglass.utils.nwb_helper_fn import get_nwb_copy_filename
nwb_copy_file_name = get_nwb_copy_filename(nwbfile_path.name)
(sgc.Nwbfile & {"nwb_file_name": nwb_copy_file_name}).delete()
sgi.insert_sessions(str(nwbfile_path), rollback_on_fail=True, raise_err=True)
def main():
nwbfile = create_minimal_nwb()
nwbfile_path = Path("/Volumes/T7/CatalystNeuro/Spyglass/raw/mock_behavior_module_requirement.nwb")
if nwbfile_path.exists():
nwbfile_path.unlink()
nwbfile_path.parent.mkdir(parents=True, exist_ok=True)
with NWBHDF5IO(nwbfile_path, "w") as io:
io.write(nwbfile)
insert_session(nwbfile_path=nwbfile_path)
if __name__ == "__main__":
main()And the resulting error from running the script:
yes "yes" | python behavior_module_requirement_bug.py 1 ↵
/opt/anaconda3/envs/spyglass/lib/python3.10/site-packages/datajoint/plugin.py:4: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.
import pkg_resources
[2025-10-24 12:07:46,948][INFO]: DataJoint is configured from /Users/pauladkisson/Documents/CatalystNeuro/Spyglass/spyglass/dj_local_conf.json
[2025-10-24 12:07:48,018][INFO]: DataJoint 0.14.6 connected to root@localhost:3306
[2025-10-24 12:07:50,057][INFO]: Deleting 1 rows from `common_interval`.`interval_list`
[2025-10-24 12:07:50,071][INFO]: Deleting 1 rows from `common_session`.`_session`
[2025-10-24 12:07:50,084][INFO]: Deleting 1 rows from `common_nwbfile`.`nwbfile`
Commit deletes? [yes, No]: [2025-10-24 12:07:50,088][INFO]: Delete committed.
[12:07:50][INFO] Spyglass: Creating a copy of NWB file mock_behavior_module_requirement.nwb with link to raw ephys data: mock_behavior_module_requirement_.nwb
[12:07:57][INFO] Spyglass: Populating Session...
[12:07:57][INFO] Spyglass: Session: No config found at raw/mock_behavior_module_requirement_.nwb
[12:07:57][INFO] Spyglass: Session populates Institution...
[12:07:57][INFO] Spyglass: No institution metadata found.
[12:07:57][INFO] Spyglass: Session populates Lab...
[12:07:57][INFO] Spyglass: No lab metadata found.
[12:07:57][INFO] Spyglass: Session populates LabMember...
[12:07:57][INFO] Spyglass: No experimenter metadata found.
[12:07:57][INFO] Spyglass: Session populates Subject...
[12:07:57][WARNING] Spyglass: No subject metadata found.
[12:07:57][INFO] Spyglass: Session populates Populate DataAcquisitionDevice...
[12:07:57][WARNING] Spyglass: No conforming data acquisition device metadata found.
[12:07:57][INFO] Spyglass: Session populates Populate CameraDevice...
[12:07:57][WARNING] Spyglass: No conforming camera device metadata found.
[12:07:57][INFO] Spyglass: Session populates Populate Probe...
[12:07:57][WARNING] Spyglass: No conforming probe metadata found.
[12:07:58][INFO] Spyglass: Skipping Apparatus for now...
[12:07:58][INFO] Spyglass: Session populates IntervalList...
[12:07:58][INFO] Spyglass: Populating ElectrodeGroup...
[12:07:58][INFO] Spyglass: Populating Raw...
/Users/pauladkisson/Documents/CatalystNeuro/Spyglass/spyglass/src/spyglass/common/common_ephys.py:302: UserWarning: Unable to get acquisition object in: /Volumes/T7/CatalystNeuro/Spyglass/raw/mock_behavior_module_requirement_.nwb
Skipping entry in `common_ephys`.`_raw`
warnings.warn(
[12:07:58][INFO] Spyglass: Populating SampleCount...
[12:07:58][INFO] Spyglass: Unable to import SampleCount: no data interface named "sample_count" found in mock_behavior_module_requirement_.nwb.
[12:07:58][INFO] Spyglass: Populating DIOEvents...
[12:07:58][WARNING] Spyglass: No conforming behavioral events data interface found in mock_behavior_module_requirement_.nwb
[12:07:58][INFO] Spyglass: Populating TaskEpoch...
[12:07:58][WARNING] Spyglass: No tasks processing module found in root pynwb.file.NWBFile at 0x5057211712
Fields:
epochs: epochs <class 'pynwb.epoch.TimeIntervals'>
file_create_date: [datetime.datetime(2025, 10, 24, 12, 7, 45, 442940, tzinfo=tzoffset(None, -25200))]
identifier: behavior_module_requirement_bug_demo
intervals: {
epochs <class 'pynwb.epoch.TimeIntervals'>
}
session_description: Mock NWB file demonstrating Spyglass behavior module requirement bug
session_start_time: 1970-01-01 00:00:00-08:00
timestamps_reference_time: 1970-01-01 00:00:00-08:00
or config
[12:07:58][INFO] Spyglass: Populating ImportedSpikeSorting...
[12:07:58][WARNING] Spyglass: No units found in NWB file
[12:07:58][INFO] Spyglass: Populating SensorData...
[12:07:58][INFO] Spyglass: No conforming sensor data found in mock_behavior_module_requirement_.nwb
[12:07:58][INFO] Spyglass: Populating Electrode...
[12:07:58][INFO] Spyglass: Populating PositionSource...
[12:07:58][ERROR] Spyglass: Uncaught exception
Traceback (most recent call last):
File "/Users/pauladkisson/Documents/CatalystNeuro/DudchenkoConv/woodcode/behavior_module_requirement_bug.py", line 48, in <module>
main()
File "/Users/pauladkisson/Documents/CatalystNeuro/DudchenkoConv/woodcode/behavior_module_requirement_bug.py", line 44, in main
insert_session(nwbfile_path=nwbfile_path)
File "/Users/pauladkisson/Documents/CatalystNeuro/DudchenkoConv/woodcode/behavior_module_requirement_bug.py", line 29, in insert_session
sgi.insert_sessions(str(nwbfile_path), rollback_on_fail=True, raise_err=True)
File "/Users/pauladkisson/Documents/CatalystNeuro/Spyglass/spyglass/src/spyglass/data_import/insert_sessions.py", line 77, in insert_sessions
return populate_all_common(
File "/Users/pauladkisson/Documents/CatalystNeuro/Spyglass/spyglass/src/spyglass/common/populate_all_common.py", line 175, in populate_all_common
single_transaction_make(
File "/Users/pauladkisson/Documents/CatalystNeuro/Spyglass/spyglass/src/spyglass/common/populate_all_common.py", line 107, in single_transaction_make
raise err
File "/Users/pauladkisson/Documents/CatalystNeuro/Spyglass/spyglass/src/spyglass/common/populate_all_common.py", line 104, in single_transaction_make
table().make(pop_key)
File "/Users/pauladkisson/Documents/CatalystNeuro/Spyglass/spyglass/src/spyglass/common/common_behav.py", line 64, in make
self.insert_from_nwbfile(nwb_file_name, skip_duplicates=True)
File "/Users/pauladkisson/Documents/CatalystNeuro/Spyglass/spyglass/src/spyglass/common/common_behav.py", line 80, in insert_from_nwbfile
all_pos = get_all_spatial_series(nwbf, verbose=True)
File "/Users/pauladkisson/Documents/CatalystNeuro/Spyglass/spyglass/src/spyglass/utils/nwb_helper_fn.py", line 577, in get_all_spatial_series
pos_interface = get_position_obj(nwbf)
File "/Users/pauladkisson/Documents/CatalystNeuro/Spyglass/spyglass/src/spyglass/utils/nwb_helper_fn.py", line 262, in get_position_obj
for obj in nwbfile.processing["behavior"].data_interfaces.values():
File "/opt/anaconda3/envs/spyglass/lib/python3.10/site-packages/hdmf/utils.py", line 1050, in __getitem__
return super().__getitem__(key)
KeyError: 'behavior'