Skip to content

Commit 81dad61

Browse files
authored
Merge branch 'master' into add_brw_4.x_sparse
2 parents 9566368 + 060d73a commit 81dad61

28 files changed

+539
-157
lines changed

.github/workflows/io-test.yml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,25 @@ jobs:
7676
# restore-key hits should result in `cache-hit` == 'false'
7777
#if: steps.cache-conda-env.outputs.cache-hit != 'true'
7878
run: |
79-
conda install datalad pip numpy=${{ matrix.numpy-version }} -c conda-forge
79+
conda install pip numpy=${{ matrix.numpy-version }} -c conda-forge
8080
# this command is for updating cache. We are resting removal.
8181
# conda env update --name neo-test-env-${{ matrix.python-version }} --file environment_testing.yml --prune
8282

83+
- name: Install git-annex
84+
# this is the trick from the spikeinterface repo for getting git-annex to work with datalad
85+
shell: bash
86+
run: |
87+
pip install datalad-installer
88+
wget https://downloads.kitenet.net/git-annex/linux/current/git-annex-standalone-amd64.tar.gz
89+
mkdir /home/runner/work/installation
90+
mv git-annex-standalone-amd64.tar.gz /home/runner/work/installation/
91+
workdir=$(pwd)
92+
cd /home/runner/work/installation
93+
tar xvzf git-annex-standalone-amd64.tar.gz
94+
echo "$(pwd)/git-annex.linux" >> $GITHUB_PATH
95+
cd $workdir
96+
git config --global filter.annex.process "git-annex filter-process" # recommended for efficiency
97+
8398
- name: Configure git
8499
run: |
85100
git config --global user.email "neo_ci@fake_mail.com"

doc/source/authors.rst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,11 @@ and may not be the current affiliation of a contributor.
9090
* Xin Niu
9191
* Nikhil Chandra [40]
9292
* Luigi Petrucco [42]
93+
* Tommaso Lambresa [43]
94+
* Nina Kudryashova [37]
95+
* Rémi Proville [44]
9396

94-
1. Centre de Recherche en Neuroscience de Lyon, CNRS UMR5292 - INSERM U1028 - Universite Claude Bernard Lyon 1
97+
1. Centre de Recherche en Neuroscience de Lyon, CNRS UMR5292 - INSERM U1028 - Université Claude Bernard Lyon 1
9598
2. Unité de Neuroscience, Information et Complexité, CNRS UPR 3293, Gif-sur-Yvette, France
9699
3. University of California, Berkeley
97100
4. Laboratoire de Neurosciences Intégratives et Adaptatives, CNRS UMR 6149 - Université de Provence, Marseille, France
@@ -133,6 +136,8 @@ and may not be the current affiliation of a contributor.
133136
40. Plexon Inc.
134137
41. Paris Brain Institute
135138
42. Istituto Italiano di Tecnologia (IIT), Italy
139+
43. University of Genoa, Italy
140+
44. AquiNeuro, SAS
136141

137142

138143

neo/io/__init__.py

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@
167167
.. autoclass:: neo.io.MaxwellIO
168168
169169
.. autoattribute:: extensions
170-
170+
171171
.. autoclass:: neo.io.MedIO
172172
173173
.. autoattribute:: extensions
@@ -280,24 +280,17 @@
280280

281281
import pathlib
282282
from collections import Counter
283+
import importlib.util
283284

284-
# try to import the neuroshare library.
285+
# check if neuroshare library exists
285286
# if it is present, use the neuroshareapiio to load neuroshare files
286287
# if it is not present, use the neurosharectypesio to load files
287-
try:
288-
import neuroshare as ns
289-
except ModuleNotFoundError as err:
290-
from neo.io.neurosharectypesio import NeurosharectypesIO as NeuroshareIO
291288

292-
# print("\n neuroshare library not found, loading data with ctypes" )
293-
# print("\n to use the API be sure to install the library found at:")
294-
# print("\n www.http://pythonhosted.org/neuroshare/")
295-
296-
else:
289+
neuroshare_spec = importlib.util.find_spec("neuroshare")
290+
if neuroshare_spec is not None:
297291
from neo.io.neuroshareapiio import NeuroshareapiIO as NeuroshareIO
298-
299-
# print("neuroshare library successfully imported")
300-
# print("\n loading with API...")
292+
else:
293+
from neo.io.neurosharectypesio import NeurosharectypesIO as NeuroshareIO
301294

302295
from neo.io.alphaomegaio import AlphaOmegaIO
303296
from neo.io.asciiimageio import AsciiImageIO

neo/io/baseio.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,7 @@
1313

1414
from __future__ import annotations
1515
from pathlib import Path
16-
17-
try:
18-
from collections.abc import Sequence
19-
except ImportError:
20-
from collections import Sequence
16+
from collections.abc import Sequence
2117
import logging
2218

2319
from neo import logging_handler

neo/io/nixio.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
from uuid import uuid4
2828
import warnings
2929
from packaging.version import Version
30+
import importlib.util
31+
import importlib.metadata
3032

3133
import quantities as pq
3234
import numpy as np
@@ -122,17 +124,15 @@ def dt_from_nix(nixdt, annotype):
122124

123125

124126
def check_nix_version():
125-
try:
126-
import nixio
127-
except ImportError:
128-
raise Exception(
127+
nixio_spec = importlib.util.find_spec("nixio")
128+
if nixio_spec is None:
129+
raise ImportError(
129130
"Failed to import NIX. "
130131
"The NixIO requires the Python package for NIX "
131132
"(nixio on PyPi). Try `pip install nixio`."
132133
)
133134

134-
# nixio version numbers have a 'v' prefix which breaks the comparison
135-
nixverstr = nixio.__version__.lstrip("v")
135+
nixverstr = importlib.metadata.version("nixio")
136136
try:
137137
nixver = Version(nixverstr)
138138
except ValueError:

neo/io/stimfitio.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
08 Feb 2014, C. Schmidt-Hieber, University College London
2727
"""
2828

29+
import importlib.util
30+
2931
import numpy as np
3032
import quantities as pq
3133

@@ -92,7 +94,9 @@ def __init__(self, filename=None):
9294
"""
9395
# We need this module, so try importing now so that it fails on
9496
# instantiation rather than read_block
95-
import stfio # noqa
97+
stfio_spec = importlib.util.find_spec("stfio")
98+
if stfio_spec is None:
99+
raise ImportError("stfio must be installed to use StimfitIO")
96100

97101
BaseIO.__init__(self)
98102

neo/rawio/axonrawio.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
strings section:
2424
[uModifierNameIndex, uCreatorNameIndex, uProtocolPathIndex, lFileComment, lADCCChannelNames, lADCUnitsIndex
2525
lDACChannelNameIndex, lDACUnitIndex, lDACFilePath, nLeakSubtractADC]
26-
['', 'Clampex', '', 'C:/path/protocol.pro', 'some comment', 'IN 0', 'mV', 'IN 1', 'mV', 'Cmd 0', 'pA',
26+
['', 'Clampex', '', 'C:/path/protocol.pro', 'some comment', 'IN 0', 'mV', 'IN 1', 'mV', 'Cmd 0', 'pA',
2727
'Cmd 1', 'pA', 'Cmd 2', 'mV', 'Cmd 3', 'mV']
2828
2929
Information on abf 1 and 2 formats is available here:

neo/rawio/baserawio.py

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,184 @@ def __repr__(self):
239239

240240
return txt
241241

242+
def _repr_html_(self):
243+
"""
244+
HTML representation for the raw recording base.
245+
246+
Returns
247+
-------
248+
html : str
249+
The HTML representation as a string.
250+
"""
251+
html = []
252+
html.append('<div style="font-family: Arial, sans-serif; max-width: 1000px; margin: 0 auto;">')
253+
254+
# Header
255+
html.append(f'<h3 style="color: #2c3e50;">{self.__class__.__name__}: {self.source_name()}</h3>')
256+
257+
if self.is_header_parsed:
258+
# Basic info
259+
nb_block = self.block_count()
260+
html.append(f"<p><strong>nb_block:</strong> {nb_block}</p>")
261+
nb_seg = [self.segment_count(i) for i in range(nb_block)]
262+
html.append(f"<p><strong>nb_segment:</strong> {nb_seg}</p>")
263+
264+
# CSS for tables - using only black, white, and gray colors
265+
html.append(
266+
"""
267+
<style>
268+
#{unique_id} table.neo-table {{
269+
border-collapse: collapse;
270+
width: 100%;
271+
margin-bottom: 20px;
272+
font-size: 14px;
273+
color: inherit;
274+
background-color: transparent;
275+
}}
276+
#{unique_id} table.neo-table th,
277+
#{unique_id} table.neo-table td {{
278+
border: 1px solid #888;
279+
padding: 8px;
280+
text-align: left;
281+
}}
282+
#{unique_id} table.neo-table th {{
283+
background-color: rgba(128,128,128,0.2);
284+
}}
285+
#{unique_id} table.neo-table tr:nth-child(even) {{
286+
background-color: rgba(128,128,128,0.1);
287+
}}
288+
#{unique_id} details {{
289+
margin-bottom: 15px;
290+
border: 1px solid rgba(128,128,128,0.3);
291+
border-radius: 4px;
292+
overflow: hidden;
293+
background-color: transparent;
294+
}}
295+
#{unique_id} summary {{
296+
padding: 10px;
297+
background-color: rgba(128,128,128,0.2);
298+
cursor: pointer;
299+
font-weight: bold;
300+
color: inherit;
301+
}}
302+
#{unique_id} details[open] summary {{
303+
border-bottom: 1px solid rgba(128,128,128,0.3);
304+
}}
305+
#{unique_id} .table-container {{
306+
padding: 10px;
307+
overflow-x: auto;
308+
background-color: transparent;
309+
}}
310+
</style>
311+
"""
312+
)
313+
314+
# Signal Streams
315+
signal_streams = self.header["signal_streams"]
316+
if signal_streams.size > 0:
317+
html.append("<details>")
318+
html.append("<summary>Signal Streams</summary>")
319+
html.append('<div class="table-container">')
320+
html.append('<table class="neo-table">')
321+
html.append("<thead><tr><th>Name</th><th>ID</th><th>Buffer ID</th><th>Channel Count</th></tr></thead>")
322+
html.append("<tbody>")
323+
324+
for i, stream in enumerate(signal_streams):
325+
html.append("<tr>")
326+
html.append(f'<td>{stream["name"]}</td>')
327+
html.append(f'<td>{stream["id"]}</td>')
328+
html.append(f'<td>{stream["buffer_id"]}</td>')
329+
html.append(f"<td>{self.signal_channels_count(i)}</td>")
330+
html.append("</tr>")
331+
332+
html.append("</tbody></table>")
333+
html.append("</div>")
334+
html.append("</details>")
335+
336+
# Signal Channels
337+
signal_channels = self.header["signal_channels"]
338+
if signal_channels.size > 0:
339+
html.append("<details>")
340+
html.append("<summary>Signal Channels</summary>")
341+
html.append('<div class="table-container">')
342+
html.append('<table class="neo-table">')
343+
html.append(
344+
"<thead><tr><th>Name</th><th>ID</th><th>Sampling Rate</th><th>Data Type</th><th>Units</th><th>Gain</th><th>Offset</th><th>Stream ID</th><th>Buffer ID</th></tr></thead>"
345+
)
346+
html.append("<tbody>")
347+
348+
for channel in signal_channels:
349+
html.append("<tr>")
350+
html.append(f'<td>{channel["name"]}</td>')
351+
html.append(f'<td>{channel["id"]}</td>')
352+
html.append(f'<td>{channel["sampling_rate"]}</td>')
353+
html.append(f'<td>{channel["dtype"]}</td>')
354+
html.append(f'<td>{channel["units"]}</td>')
355+
html.append(f'<td>{channel["gain"]}</td>')
356+
html.append(f'<td>{channel["offset"]}</td>')
357+
html.append(f'<td>{channel["stream_id"]}</td>')
358+
html.append(f'<td>{channel["buffer_id"]}</td>')
359+
html.append("</tr>")
360+
361+
html.append("</tbody></table>")
362+
html.append("</div>")
363+
html.append("</details>")
364+
365+
# Spike Channels
366+
spike_channels = self.header["spike_channels"]
367+
if spike_channels.size > 0:
368+
html.append("<details>")
369+
html.append("<summary>Spike Channels</summary>")
370+
html.append('<div class="table-container">')
371+
html.append('<table class="neo-table">')
372+
html.append(
373+
"<thead><tr><th>Name</th><th>ID</th><th>WF Units</th><th>WF Gain</th><th>WF Offset</th><th>WF Left Sweep</th><th>WF Sampling Rate</th></tr></thead>"
374+
)
375+
html.append("<tbody>")
376+
377+
for channel in spike_channels:
378+
html.append("<tr>")
379+
html.append(f'<td>{channel["name"]}</td>')
380+
html.append(f'<td>{channel["id"]}</td>')
381+
html.append(f'<td>{channel["wf_units"]}</td>')
382+
html.append(f'<td>{channel["wf_gain"]}</td>')
383+
html.append(f'<td>{channel["wf_offset"]}</td>')
384+
html.append(f'<td>{channel["wf_left_sweep"]}</td>')
385+
html.append(f'<td>{channel["wf_sampling_rate"]}</td>')
386+
html.append("</tr>")
387+
388+
html.append("</tbody></table>")
389+
html.append("</div>")
390+
html.append("</details>")
391+
392+
# Event Channels
393+
event_channels = self.header["event_channels"]
394+
if event_channels.size > 0:
395+
html.append("<details>")
396+
html.append("<summary>Event Channels</summary>")
397+
html.append('<div class="table-container">')
398+
html.append('<table class="neo-table">')
399+
html.append("<thead><tr><th>Name</th><th>ID</th><th>Type</th></tr></thead>")
400+
html.append("<tbody>")
401+
402+
for channel in event_channels:
403+
html.append("<tr>")
404+
html.append(f'<td>{channel["name"]}</td>')
405+
html.append(f'<td>{channel["id"]}</td>')
406+
html.append(
407+
f'<td>{channel["type"].decode("utf-8") if isinstance(channel["type"], bytes) else channel["type"]}</td>'
408+
)
409+
html.append("</tr>")
410+
411+
html.append("</tbody></table>")
412+
html.append("</div>")
413+
html.append("</details>")
414+
else:
415+
html.append("<p><em>Call <code>parse_header()</code> to load the reader data.</p>")
416+
417+
html.append("</div>")
418+
return "\n".join(html)
419+
242420
def _generate_minimal_annotations(self):
243421
"""
244422
Helper function that generates a nested dict for annotations.

neo/rawio/blackrockrawio.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -979,7 +979,7 @@ def __read_nsx_dataheader_variant_b(
979979
# use of `int` avoids overflow problem
980980
data_size = int(dh["nb_data_points"]) * int(self.__nsx_basic_header[nsx_nb]["channel_count"]) * 2
981981
# define new offset (to possible next data block)
982-
offset = data_header[index]["offset_to_data_block"] + data_size
982+
offset = int(data_header[index]["offset_to_data_block"]) + data_size
983983

984984
index += 1
985985

0 commit comments

Comments
 (0)