Skip to content

Commit c13ff2c

Browse files
authored
Merge branch 'nwb-schema-2.9.0' into add-ch-idx-to-event-detection
2 parents 8723d3f + 87dcadc commit c13ff2c

File tree

11 files changed

+160
-38
lines changed

11 files changed

+160
-38
lines changed

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
- When an external file is detected when initializing an ImageSeries and no format is provided, automatically set format to "external" instead of raising an error. @stephprince [#2060](https://github.com/NeurodataWithoutBorders/pynwb/pull/2060)
2020
- Added mask_type option to `mock_PlaneSegmentation`. @pauladkisson [#2067](https://github.com/NeurodataWithoutBorders/pynwb/pull/2067)
2121
- Improved the documentation of the `spike_times` in the Units table methods @h-mayorquin [#2085](https://github.com/NeurodataWithoutBorders/pynwb/pull/2085)
22-
- Updated minimum HDMF version to 4.1.0. @mavaylon1 @rly [#2063](https://github.com/NeurodataWithoutBorders/pynwb/pull/2063)
22+
- Removed core namespace warning unless cached version is newer. @stephprince [#2077](https://github.com/NeurodataWithoutBorders/pynwb/pull/2077)
23+
- Bumped minimum HDMF version to 4.1.0. @stephprince [#2077](https://github.com/NeurodataWithoutBorders/pynwb/pull/2077)
2324

2425
### Bug fixes
2526
- Fixed `add_data_interface` functionality that was mistakenly removed in PyNWB 3.0. @stephprince [#2052](https://github.com/NeurodataWithoutBorders/pynwb/pull/2052)
@@ -29,6 +30,10 @@
2930
- Fixed missing `IndexSeries.indexed_images`. @rly [#2074](https://github.com/NeurodataWithoutBorders/pynwb/pull/2074)
3031
- Fixed missing `__nwbfields__` and `_fieldsname` for `NWBData` and its subclasses. @rly [#2082](https://github.com/NeurodataWithoutBorders/pynwb/pull/2082)
3132
- Fixed caching of the type map when using HDMF 4.1.0. @rly [#2087](https://github.com/NeurodataWithoutBorders/pynwb/pull/2087)
33+
- Removed use of complex numbers in scratch tutorial because of incompatibilities with HDMF 4.1.0. @stephprince [#2090](https://github.com/NeurodataWithoutBorders/pynwb/pull/2090/)
34+
35+
### Documentation and tutorial enhancements
36+
- Added NWB AI assistant to the home page of the documentation. @magland [#2076](https://github.com/NeurodataWithoutBorders/pynwb/pull/2076)
3237

3338
## PyNWB 3.0.0 (February 26, 2025)
3439

docs/gallery/general/scratch.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,14 +83,12 @@
8383
# Now that we have a copy, lets process some data, and add the results as a :py:class:`~pynwb.base.ProcessingModule`
8484
# to our copy of the file. [#]_
8585

86-
import scipy.signal as sps
87-
8886
mod = nwb_proc.create_processing_module(
8987
"filtering_module", "a module to store filtering results"
9088
)
9189

9290
ts1 = nwb_in.acquisition["raw_timeseries"]
93-
filt_data = sps.correlate(ts1.data, np.ones(128), mode="same") / 128
91+
filt_data = np.convolve(ts1.data, np.ones(128), mode="same") / 128
9492
ts2 = TimeSeries(name="filtered_timeseries", data=filt_data, unit="m", timestamps=ts1)
9593

9694
mod.add_container(ts2)
@@ -147,12 +145,13 @@
147145

148146
filt_ts = nwb_scratch.processing["filtering_module"]["filtered_timeseries"]
149147

150-
fft = np.fft.fft(filt_ts.data)
148+
# simple power spectrum without normalization
149+
ps = np.abs(np.fft.fft(filt_ts.data))**2
151150

152151
nwb_scratch.add_scratch(
153-
fft,
154-
name="dft_filtered",
155-
description="discrete Fourier transform from filtered data",
152+
ps,
153+
name="power_spectrum",
154+
description="power spectrum from filtered data",
156155
)
157156

158157

@@ -171,9 +170,9 @@
171170
scratch_io = NWBHDF5IO("scratch_analysis.nwb", "r")
172171
nwb_scratch_in = scratch_io.read()
173172

174-
fft_in = nwb_scratch_in.scratch["dft_filtered"]
173+
fft_in = nwb_scratch_in.scratch["power_spectrum"]
175174

176-
fft_in = nwb_scratch_in.get_scratch("dft_filtered")
175+
fft_in = nwb_scratch_in.get_scratch("power_spectrum")
177176

178177
####################
179178
#
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
.assistant-toggle {
2+
display: none;
3+
position: fixed;
4+
right: 20px;
5+
top: 20px;
6+
z-index: 1001;
7+
padding: 8px 16px;
8+
background-color: #2980b9;
9+
color: white;
10+
border: none;
11+
border-radius: 4px;
12+
cursor: pointer;
13+
font-size: 14px;
14+
}
15+
16+
/* Hide button when assistant is open */
17+
.assistant-container.show ~ .assistant-toggle {
18+
display: none !important;
19+
}
20+
21+
.assistant-toggle:hover {
22+
background-color: #3498db;
23+
}
24+
25+
.assistant-container {
26+
display: none;
27+
}
28+
29+
@media (min-width: 1300px) {
30+
.assistant-toggle {
31+
display: block;
32+
}
33+
34+
.assistant-container {
35+
display: block;
36+
visibility: hidden;
37+
position: fixed;
38+
right: 0;
39+
top: 0;
40+
width: calc(100vw - 1140px);
41+
height: 100vh;
42+
z-index: 1000;
43+
transform: translateX(100%);
44+
transition: transform 0.3s ease-in-out, visibility 0.3s ease-in-out;
45+
}
46+
47+
.assistant-container.show {
48+
visibility: visible;
49+
transform: translateX(0);
50+
}
51+
52+
.assistant-iframe {
53+
width: 100%;
54+
height: 100%;
55+
border: none;
56+
background: white;
57+
}
58+
59+
.wy-nav-content {
60+
transition: margin-right 0.3s ease-in-out;
61+
}
62+
63+
.assistant-container.show ~ .wy-nav-content {
64+
margin-right: calc(100vw - 1140px);
65+
}
66+
}

docs/source/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ def __call__(self, filename):
263263
# or fully qualified paths (eg. https://...)
264264
html_css_files = [
265265
'css/custom.css',
266+
'css/nwb_assistant.css'
266267
]
267268

268269
# The name for this set of Sphinx documents. If None, it defaults to

docs/source/index.rst

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,30 @@ The NWB team consists of neuroscientists and software developers
2020
who recognize that adoption of a unified data format is an important step toward
2121
breaking down the barriers to data sharing in neuroscience.
2222

23+
.. raw:: html
24+
25+
<div class="assistant-container">
26+
<iframe class="assistant-iframe"></iframe>
27+
</div>
28+
<button class="assistant-toggle">Open Assistant</button>
29+
<script>
30+
document.addEventListener('DOMContentLoaded', function() {
31+
const toggle = document.querySelector('.assistant-toggle');
32+
const container = document.querySelector('.assistant-container');
33+
const iframe = document.querySelector('.assistant-iframe');
34+
let iframeLoaded = false;
35+
36+
toggle.addEventListener('click', function() {
37+
const isShowing = container.classList.toggle('show');
38+
39+
// Load iframe content only when first opened
40+
if (isShowing && !iframeLoaded) {
41+
iframe.src = 'https://magland.github.io/nwb-assistant/chat';
42+
iframeLoaded = true;
43+
}
44+
});
45+
});
46+
</script>
2347

2448
.. toctree::
2549
:maxdepth: 2

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ classifiers = [
3434
]
3535
dependencies = [
3636
"h5py>=3.2.0",
37-
"hdmf>=4.1.0,<6",
37+
"hdmf>=4.1.0,<5",
3838
"numpy>=1.24.0",
3939
"pandas>=1.2.0",
4040
"python-dateutil>=2.8.2",

src/pynwb/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def __get_resources() -> dict:
8888
# a global type map
8989
global __TYPE_MAP
9090

91-
__ns_catalog = NamespaceCatalog(NWBGroupSpec, NWBDatasetSpec, NWBNamespace)
91+
__ns_catalog = NamespaceCatalog(NWBGroupSpec, NWBDatasetSpec, NWBNamespace, core_namespaces=[CORE_NAMESPACE])
9292

9393
hdmf_typemap = hdmf.common.get_type_map()
9494
__TYPE_MAP = TypeMap(__ns_catalog)

tests/back_compat/test_read.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,7 @@ class TestReadOldVersions(TestCase):
4141

4242
def get_io(self, path):
4343
"""Get an NWBHDF5IO object for the given path."""
44-
with warnings.catch_warnings():
45-
warnings.filterwarnings(
46-
"ignore",
47-
message=r"Ignoring cached namespace .*",
48-
category=UserWarning,
49-
)
50-
return NWBHDF5IO(str(path), 'r')
44+
return NWBHDF5IO(str(path), 'r')
5145

5246
def test_read(self):
5347
"""Test reading and validating all NWB files in the same folder as this file.

tests/integration/ros3/test_ros3.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,13 @@ def setUp(self):
3030
def test_read(self):
3131
s3_path = 'https://dandiarchive.s3.amazonaws.com/ros3test.nwb'
3232
with warnings.catch_warnings():
33-
warnings.filterwarnings(
34-
"ignore",
35-
message=r"Ignoring cached namespace .*",
36-
category=UserWarning,
37-
)
3833
with NWBHDF5IO(s3_path, mode='r', driver='ros3') as io:
3934
nwbfile = io.read()
4035
test_data = nwbfile.acquisition['ts_name'].data[:]
4136
self.assertEqual(len(test_data), 3)
4237

4338
def test_dandi_read(self):
4439
with warnings.catch_warnings():
45-
warnings.filterwarnings(
46-
"ignore",
47-
message=r"Ignoring cached namespace .*",
48-
category=UserWarning,
49-
)
5040
with NWBHDF5IO(path=self.s3_test_path, mode='r', driver='ros3') as io:
5141
nwbfile = io.read()
5242
test_data = nwbfile.acquisition['TestData'].data[:]

tests/unit/test_extension.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import os
22
import random
33
import string
4+
import warnings
45
from datetime import datetime
56
from dateutil.tz import tzlocal
67
from tempfile import gettempdir
78

8-
from hdmf.spec import RefSpec
9+
from hdmf.spec import RefSpec, NamespaceCatalog
910
from hdmf.utils import get_docval, docval, popargs
1011
from pynwb import get_type_map, TimeSeries, NWBFile, register_class, load_namespaces, get_class
1112
from pynwb.spec import NWBNamespaceBuilder, NWBGroupSpec, NWBAttributeSpec, NWBDatasetSpec
@@ -143,6 +144,10 @@ def setUp(self):
143144
self.ext_source2 = '%s_extension2.yaml' % self.prefix
144145
self.ns_path2 = '%s_namespace2.yaml' % self.prefix
145146

147+
self.ns_catalog = get_type_map().namespace_catalog
148+
self.core_ns = 'core'
149+
self.core_ns_version = self.ns_catalog.get_namespace(self.core_ns)['version']
150+
146151
def tearDown(self):
147152
files = (self.ext_source1,
148153
self.ns_path1,
@@ -169,3 +174,48 @@ def test_catch_dup_name(self):
169174
ns_builder2.export(self.ns_path2, outdir=self.tempdir)
170175
type_map = get_type_map(extensions=os.path.join(self.tempdir, self.ns_path1))
171176
type_map.load_namespaces(os.path.join(self.tempdir, self.ns_path2))
177+
178+
def test_catch_dup_name_core_newer(self):
179+
new_ns_version = '100.0.0'
180+
ns_builder1 = NWBNamespaceBuilder('Extension for us in my Lab', self.core_ns, version=new_ns_version)
181+
ext1 = NWBGroupSpec('A custom ElectricalSeries for my lab',
182+
attributes=[NWBAttributeSpec(name='trode_id', doc='the tetrode id', dtype='int')],
183+
neurodata_type_inc='ElectricalSeries',
184+
neurodata_type_def='TetrodeSeries')
185+
ns_builder1.add_spec(self.ext_source1, ext1)
186+
ns_builder1.export(self.ns_path1, outdir=self.tempdir)
187+
188+
# create new catalog and merge the loaded core namespace catalog
189+
ns_catalog = NamespaceCatalog()
190+
ns_catalog.merge(self.ns_catalog)
191+
192+
# test loading newer namespace than one already loaded will warn
193+
msg = (f'Ignoring the following cached namespace(s) because another version is already loaded:\n'
194+
f'{self.core_ns} - cached version: {new_ns_version}, loaded version: {self.core_ns_version}\n'
195+
f'Please update to the latest package versions.')
196+
with self.assertWarnsWith(UserWarning, msg):
197+
ns_catalog.load_namespaces(os.path.join(self.tempdir, self.ns_path1))
198+
199+
def test_catch_dup_name_core_older(self):
200+
new_ns_version = '0.0.0'
201+
ns_builder1 = NWBNamespaceBuilder('Extension for us in my Lab', self.core_ns, version=new_ns_version)
202+
ext1 = NWBGroupSpec('A custom ElectricalSeries for my lab',
203+
attributes=[NWBAttributeSpec(name='trode_id', doc='the tetrode id', dtype='int')],
204+
neurodata_type_inc='ElectricalSeries',
205+
neurodata_type_def='TetrodeSeries')
206+
ns_builder1.add_spec(self.ext_source1, ext1)
207+
ns_builder1.export(self.ns_path1, outdir=self.tempdir)
208+
209+
# create new catalog and merge the loaded core namespace catalog
210+
ns_catalog = NamespaceCatalog()
211+
ns_catalog.merge(self.ns_catalog)
212+
213+
# test no warning if loading older namespace than one already loaded
214+
msg = (f'Ignoring the following cached namespace(s) because another version is already loaded:\n'
215+
f'{self.core_ns} - cached version: {new_ns_version}, loaded version: {self.core_ns_version}\n'
216+
f'Please update to the latest package versions.')
217+
with warnings.catch_warnings(record=True) as ws:
218+
ns_catalog.load_namespaces(os.path.join(self.tempdir, self.ns_path1))
219+
for w in ws:
220+
self.assertTrue(str(w.message) != msg)
221+
warnings.warn(str(w.message), w.category)

0 commit comments

Comments
 (0)