Skip to content

Commit

Permalink
Add plexon 2 support (#918)
Browse files Browse the repository at this point in the history
Co-authored-by: Ben Dichter <[email protected]>
Co-authored-by: Cody Baker <[email protected]>
  • Loading branch information
3 people authored Aug 8, 2024
1 parent 6df8660 commit fc6ae26
Show file tree
Hide file tree
Showing 15 changed files with 167 additions and 11 deletions.
30 changes: 30 additions & 0 deletions .github/actions/install-wine/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Install packages
description: This action installs the package and its dependencies for testing

inputs:
os:
description: 'Operating system to set up'
required: true

runs:
using: "composite"
steps:
- name: Install wine on Linux
if: runner.os == 'Linux'
run: |
sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list
sudo dpkg --add-architecture i386
sudo apt-get update -qq
sudo apt-get install -yqq --allow-downgrades libc6:i386 libgcc-s1:i386 libstdc++6:i386 wine
shell: bash
- name: Install wine on macOS
if: runner.os == 'macOS'
run: |
brew install --cask xquartz
brew install --cask wine-stable
shell: bash

- name: Skip installation on Windows
if: ${{ inputs.os == 'Windows' }}
run: echo "Skipping Wine installation on Windows. Not necessary."
shell: bash
11 changes: 8 additions & 3 deletions .github/workflows/dev-testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ jobs:
git config --global user.email "[email protected]"
git config --global user.name "CI Almighty"
- name: Install Wine (For Plexon2 Tests)
uses: ./.github/actions/install-wine
with:
os: ${{ runner.os }}

- name: Install full requirements
run: pip install --no-cache-dir .[full,test]

Expand Down Expand Up @@ -70,7 +75,7 @@ jobs:
id: cache-ephys-datasets
with:
path: ./ephy_testing_data
key: ephys-datasets-2024-03-27-${{ matrix.os }}-${{ steps.ephys.outputs.HASH_EPHY_DATASET }}
key: ephys-datasets-2024-06-21-ubuntu-latest-${{ steps.ephys.outputs.HASH_EPHY_DATASET }}
- name: Get ophys_testing_data current head hash
id: ophys
run: echo "::set-output name=HASH_OPHYS_DATASET::$(git ls-remote https://gin.g-node.org/CatalystNeuro/ophys_testing_data.git HEAD | cut -f1)"
Expand All @@ -79,7 +84,7 @@ jobs:
id: cache-ophys-datasets
with:
path: ./ophys_testing_data
key: ophys-datasets-2022-08-18-${{ matrix.os }}-${{ steps.ophys.outputs.HASH_OPHYS_DATASET }}
key: ophys-datasets-2022-08-18-ubuntu-latest-${{ steps.ophys.outputs.HASH_OPHYS_DATASET }}
- name: Get behavior_testing_data current head hash
id: behavior
run: echo "::set-output name=HASH_BEHAVIOR_DATASET::$(git ls-remote https://gin.g-node.org/CatalystNeuro/behavior_testing_data.git HEAD | cut -f1)"
Expand All @@ -88,7 +93,7 @@ jobs:
id: cache-behavior-datasets
with:
path: ./behavior_testing_data
key: behavior-datasets-2023-07-26-${{ matrix.os }}-${{ steps.behavior.outputs.HASH_behavior_DATASET }}
key: behavior-datasets-2023-07-26-ubuntu-latest-${{ steps.behavior.outputs.HASH_behavior_DATASET }}



Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/doctests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ jobs:
- name: Install neuroconv with minimal requirements
run: pip install .[full,test]


- name: Install Wine (For Plexon2 Tests)
uses: ./.github/actions/install-wine
with:
os: ${{ runner.os }}

- name: Get ephy_testing_data current head hash
id: ephys
Expand All @@ -39,7 +42,7 @@ jobs:
id: cache-ephys-datasets
with:
path: ./ephy_testing_data
key: ephys-datasets-2024-03-27-${{ matrix.os }}-${{ steps.ephys.outputs.HASH_EPHY_DATASET }}
key: ephys-datasets-2024-08-07-${{ matrix.os }}-${{ steps.ephys.outputs.HASH_EPHY_DATASET }}
- name: Get ophys_testing_data current head hash
id: ophys
run: echo "::set-output name=HASH_OPHYS_DATASET::$(git ls-remote https://gin.g-node.org/CatalystNeuro/ophys_testing_data.git HEAD | cut -f1)"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/formatwise-installation-testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
id: cache-ephys-datasets
with:
path: ./ephy_testing_data
key: ephys-datasets-2024-03-27-${{ matrix.os }}-${{ steps.ephys.outputs.HASH_EPHY_DATASET }}
key: ephys-datasets-2024-08-27-${{ matrix.os }}-${{ steps.ephys.outputs.HASH_EPHY_DATASET }}
- name: Get ophys_testing_data current head hash
id: ophys
run: echo "::set-output name=HASH_OPHYS_DATASET::$(git ls-remote https://gin.g-node.org/CatalystNeuro/ophys_testing_data.git HEAD | cut -f1)"
Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ jobs:
run: pip install .
- name: Test initial import of all non-lazy dependencies
run: python -c "import neuroconv"

- name: Install Wine (For Plexon2 Tests)
uses: ./.github/actions/install-wine
with:
os: ${{ runner.os }}
- name: Install NeuroConv with testing requirements
run: pip install .[test]
- name: Run import tests
Expand Down Expand Up @@ -88,7 +91,7 @@ jobs:
id: cache-ephys-datasets
with:
path: ./ephy_testing_data
key: ephys-datasets-2024-03-27-${{ matrix.os }}-${{ steps.ephys.outputs.HASH_EPHY_DATASET }}
key: ephys-datasets-2024-08-07-${{ matrix.os }}-${{ steps.ephys.outputs.HASH_EPHY_DATASET }}
- name: Get ophys_testing_data current head hash
id: ophys
run: echo "::set-output name=HASH_OPHYS_DATASET::$(git ls-remote https://gin.g-node.org/CatalystNeuro/ophys_testing_data.git HEAD | cut -f1)"
Expand Down
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

### Features
* Support `SortingAnalyzer` in the `SpikeGLXConverterPipe`. [PR #821](https://github.com/catalystneuro/neuroconv/pull/821)
* Add Plexon2 support [PR #918](https://github.com/catalystneuro/neuroconv/pull/918)
* Converter working with multiple VideoInterface instances [PR 914](https://github.com/catalystneuro/neuroconv/pull/914)

### Bug fixes
* Fixed the default naming of multiple electrical series in the `SpikeGLXConverterPipe`. [PR #957](https://github.com/catalystneuro/neuroconv/pull/957)
Expand Down Expand Up @@ -58,7 +60,7 @@

### Improvements
* Propagated `photon_series_type` to `BaseImagingExtractorInterface` init instead of passing it as an argument of `get_metadata()` and `get_metadata_schema()`. [PR #847](https://github.com/catalystneuro/neuroconv/pull/847)
* Converter working with multiple VideoInterface instances [PR 914](https://github.com/catalystneuro/neuroconv/pull/914)




Expand Down
1 change: 1 addition & 0 deletions docs/conversion_examples_gallery/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Recording
NeuroScope <recording/neuroscope>
OpenEphys <recording/openephys>
Plexon <recording/plexon>
Plexon2 <recording/plexon2>
Spike2 <recording/spike2>
Spikegadgets <recording/spikegadgets>
SpikeGLX <recording/spikeglx>
Expand Down
35 changes: 35 additions & 0 deletions docs/conversion_examples_gallery/recording/plexon2.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
Plexon2 Recording Conversion
----------------------------

Install NeuroConv with the additional dependencies necessary for reading Plexon acquisition data.

.. code-block:: bash
pip install neuroconv[plexon]
.. warning::
When running plexon2 conversion on platforms other than Windows, you also need to install `wine <https://www.winehq.org/>`_.

Convert Plexon2 recording data to NWB using :py:class:`~neuroconv.datainterfaces.ecephys.plexon.plexondatainterface.Plexon2RecordingInterface`.

.. code-block:: python
>>> from datetime import datetime
>>> from zoneinfo import ZoneInfo
>>> from pathlib import Path
>>> from neuroconv.datainterfaces import Plexon2RecordingInterface
>>>
>>> file_path = f"{ECEPHY_DATA_PATH}/plexon/4chDemoPL2.pl2"
>>> # Change the file_path to the location in your system
>>> interface = Plexon2RecordingInterface(file_path=file_path, verbose=False)
>>>
>>> # Extract what metadata we can from the source files
>>> metadata = interface.get_metadata()
>>> # For data provenance we add the time zone information to the conversion
>>> tzinfo = ZoneInfo("US/Pacific")
>>> session_start_time = metadata["NWBFile"]["session_start_time"]
>>> metadata["NWBFile"].update(session_start_time=session_start_time.replace(tzinfo=tzinfo))
>>>
>>> # Choose a path for saving the nwb file and run the conversion
>>> nwbfile_path = f"{path_to_save_nwbfile}"
>>> interface.run_conversion(nwbfile_path=nwbfile_path, metadata=metadata)
2 changes: 2 additions & 0 deletions src/neuroconv/datainterfaces/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
from .ecephys.openephys.openephyssortingdatainterface import OpenEphysSortingInterface
from .ecephys.phy.phydatainterface import PhySortingInterface
from .ecephys.plexon.plexondatainterface import (
Plexon2RecordingInterface,
PlexonRecordingInterface,
PlexonSortingInterface,
)
Expand Down Expand Up @@ -124,6 +125,7 @@
EDFRecordingInterface,
TdtRecordingInterface,
PlexonRecordingInterface,
Plexon2RecordingInterface,
PlexonSortingInterface,
BiocamRecordingInterface,
AlphaOmegaRecordingInterface,
Expand Down
53 changes: 53 additions & 0 deletions src/neuroconv/datainterfaces/ecephys/plexon/plexondatainterface.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from pathlib import Path

from ..baserecordingextractorinterface import BaseRecordingExtractorInterface
from ..basesortingextractorinterface import BaseSortingExtractorInterface
from ....utils import DeepDict
Expand Down Expand Up @@ -48,6 +50,57 @@ def get_metadata(self) -> DeepDict:
return metadata


class Plexon2RecordingInterface(BaseRecordingExtractorInterface):
"""
Primary data interface class for converting Plexon2 data.
Uses the :py:class:`~spikeinterface.extractors.Plexon2RecordingExtractor`.
"""

display_name = "Plexon2 Recording"
associated_suffixes = (".pl2",)
info = "Interface for Plexon2 recording data."

@classmethod
def get_source_schema(cls) -> dict:
source_schema = super().get_source_schema()
source_schema["properties"]["file_path"]["description"] = "Path to the .pl2 file."
return source_schema

def __init__(self, file_path: FilePathType, verbose: bool = True, es_key: str = "ElectricalSeries"):
"""
Load and prepare data for Plexon.
Parameters
----------
file_path : str or Path
Path to the .plx file.
verbose : bool, default: True
Allows verbosity.
es_key : str, default: "ElectricalSeries"
"""
stream_id = "3"
assert Path(file_path).is_file(), f"Plexon file not found in: {file_path}"
super().__init__(
file_path=file_path,
verbose=verbose,
es_key=es_key,
stream_id=stream_id,
all_annotations=True,
)

def get_metadata(self) -> DeepDict:
metadata = super().get_metadata()

neo_reader = self.recording_extractor.neo_reader

block_ind = self.recording_extractor.block_index
neo_metadata = neo_reader.raw_annotations["blocks"][block_ind]
metadata["NWBFile"].update(session_start_time=neo_metadata["m_CreatorDateTime"])

return metadata


class PlexonSortingInterface(BaseSortingExtractorInterface):
"""
Primary data interface class for converting Plexon spiking data.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
zugbruecke >= 0.2.1; platform_system != "Windows"
2 changes: 1 addition & 1 deletion src/neuroconv/datainterfaces/ecephys/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
spikeinterface>=0.101.0
neo>=0.13.1
neo>=0.13.2
2 changes: 1 addition & 1 deletion src/neuroconv/datainterfaces/icephys/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
neo>=0.9.0
neo>=0.13.2
7 changes: 7 additions & 0 deletions tests/test_on_data/test_gin_ecephys/test_raw_recordings.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
NeuroScopeRecordingInterface,
OpenEphysBinaryRecordingInterface,
OpenEphysLegacyRecordingInterface,
Plexon2RecordingInterface,
PlexonRecordingInterface,
SpikeGadgetsRecordingInterface,
SpikeGLXRecordingInterface,
Expand Down Expand Up @@ -95,6 +96,12 @@ class TestEcephysRawRecordingsNwbConversions(unittest.TestCase):
),
case_name="plexon_recording",
),
param(
data_interface=Plexon2RecordingInterface,
interface_kwargs=dict(
file_path=str(DATA_PATH / "plexon" / "4chDemoPL2.pl2"),
),
),
param(
data_interface=BiocamRecordingInterface,
interface_kwargs=dict(file_path=str(DATA_PATH / "biocam" / "biocam_hw3.0_fw1.6.brw")),
Expand Down
14 changes: 14 additions & 0 deletions tests/test_on_data/test_metadata/test_plexon_metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import datetime

from neuroconv.datainterfaces import Plexon2RecordingInterface

from ..setup_paths import ECEPHY_DATA_PATH


def test_session_start_time():
file_path = f"{ECEPHY_DATA_PATH}/plexon/4chDemoPL2.pl2"
interface = Plexon2RecordingInterface(file_path=file_path)
metadata = interface.get_metadata()
session_start_time = metadata["NWBFile"]["session_start_time"]

assert session_start_time == datetime.datetime(2013, 11, 20, 15, 59, 39)

0 comments on commit fc6ae26

Please sign in to comment.