Skip to content

Commit f44b673

Browse files
authored
Use ndx-optogenetics and bump to 0.2.0 (#6)
* Use ndx-optogenetics and bump to 0.2.0 * Update inner cached spec * Remove "experimenter" from FrankLabOptogeneticEpochsTable * Remove serial_number from CameraDevice * Remove resolution_in_pixels from CameraDevice * Fix dims of spatial filter columns * Update upper bounds on pynwb and hdmf * Update changelog * Update docs and tests * Remove extra spec files * Add ndx-optogenetics to pinned testing reqs * Update requirement versions * Use latest pynwb and hdmf * Update CHANGELOG.md * Update CHANGELOG.md * Update Loren's email address
1 parent fe9ed15 commit f44b673

File tree

12 files changed

+541
-39
lines changed

12 files changed

+541
-39
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# ndx-franklab-novela Changelog
22

3+
## 0.2.0 (February 14, 2025)
4+
5+
- Added `FrankLabOptogeneticEpochsTable` to store optogenetic stimulation metadata.
6+
- Added extension dependency on `ndx-optogenetics`.
7+
- Added optional `frame_rate` field to `CameraDevice`.
8+
- Updated versions of dependencies.
9+
310
## 0.1.0 (December 9, 2021)
411

512
- Removed `NdxImageSeries` data type. The core NWB `ImageSeries` type should be used instead.

README.md

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,26 @@ ndx-franklab-novela is a python package containing NWB custom extensions for Lor
55

66
# How to install
77

8-
Add ndx-franklab-novela to your conda environment
8+
Add ndx-franklab-novela to your conda environment:
9+
```
10+
pip install ndx-franklab-novela
11+
```
912

10-
`pip install git+git://github.com/LorenFrankLab/ndx-franklab-novela`
13+
Or install the latest version from the repository:
14+
```
15+
pip install git+git://github.com/LorenFrankLab/ndx-franklab-novela
16+
```
1117

1218
The original published extension maintained by NovelaNeuro can be installed using:
19+
```
20+
conda install -c conda-forge -c novelakrk ndx-franklab-novela
21+
```
1322

14-
`conda install -c conda-forge -c novelakrk ndx-franklab-novela`
15-
16-
17-
# How to install
18-
19-
Add ndx-franklab-novela to your conda environment<br>
20-
```pip install git+git://github.com/LorenFrankLab/ndx-franklab-novela```
21-
22-
The original published extension maintained by NovelaNeuro can be installed using:
23-
```conda install -c conda-forge -c novelakrk ndx-franklab-novela```
23+
# Dependencies
2424

25+
This extension uses the [ndx-optogenetics](https://github.com/rly/ndx-optogenetics) extension.
26+
Installing ndx-franklab-novela will install the latest version of ndx-optogenetics from PyPI.
27+
Loading `ndx-franklab-novela` by importing `ndx_franklab_novela` will also load `ndx_optogenetics`.
2528

2629
# Extensions
2730

@@ -102,6 +105,31 @@ Representation of a camera device in NWB.
102105
- **model** `string`: model of this camera device
103106
- **lens** `string`: info about lens in this camera
104107
- **camera_name** `string`: name of this camera
108+
- **frame_rate** `float`: frame rate of this camera (optional)
109+
110+
## FrankLabOptogeneticEpochsTable
111+
An extension of the `OptogeneticEpochsTable` from [ndx-optogenetics](https://github.com/rly/ndx-optogenetics) with the following columns:
112+
113+
**Columns:**
114+
- **epoch_name** `string`: name of this epoch
115+
- **epoch_number** `int`: 1-indexed number of this epoch
116+
- **convenience_code** `string`: convenience code of this epoch
117+
- **epoch_type** `string`: type of this epoch
118+
- **theta_filter_on** `bool`: whether the theta filter was on (optional)
119+
- **theta_filter_lockout_period_in_samples** `int`: lockout period in samples for theta filter (optional)
120+
- **theta_filter_phase_in_deg** `float`: phase in degrees for theta filter (optional)
121+
- **theta_filter_reference_ntrode** `int`: reference ntrode for theta filter (optional)
122+
- **spatial_filter_on** `bool`: whether the spatial filter was on (optional)
123+
- **spatial_filter_lockout_period_in_samples** `int`: lockout period in samples for spatial filter (optional)
124+
- **spatial_filter_bottom_left_coord_in_pixels** `float`, shape `(2, )`: bottom left coordinate in pixels for spatial filter (optional)
125+
- **spatial_filter_top_right_coord_in_pixels** `float`, shape `(2, )`: top right coordinate in pixels for spatial filter (optional)
126+
- **spatial_filter_cameras_index** `int`: index column for spatial filter cameras (optional)
127+
- **spatial_filter_cameras** : references to `CameraDevice` objects used for spatial filter (optional)
128+
- **spatial_filter_cameras_cm_per_pixel** `float`: cm per pixel for spatial filter cameras (optional)
129+
- **ripple_filter_on** `bool`: whether the ripple filter was on (optional)
130+
- **ripple_filter_lockout_period_in_samples** `int`: lockout period in samples for ripple filter (optional)
131+
- **ripple_filter_threshold_sd** `float`: threshold in standard deviations for ripple filter (optional)
132+
- **ripple_filter_num_above_threshold** `int`: number of tetrodes above threshold for ripple filter (optional)
105133

106134
---
107135
This extension was created using [ndx-template](https://github.com/nwb-extensions/ndx-template).

requirements-dev.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# pinned dependencies to reproduce an entire development environment to run tests and check code style
2-
flake8==4.0.1
3-
pytest==6.2.5
2+
flake8==7.1.1
3+
pytest==8.3.4

requirements.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# pinned dependencies to reproduce a working development environment
2-
hdmf_docutils==0.4.4
3-
hdmf==3.1.1
4-
pynwb==2.0.0
2+
hdmf_docutils==0.4.8
3+
hdmf==4.0.0
4+
pynwb==2.8.3
5+
ndx-optogenetics==0.2.0

setup.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from setuptools import find_packages, setup
66

7-
version = "0.1.0"
7+
version = "0.2.0"
88
print(version)
99

1010
# load README.md/README.rst file
@@ -33,8 +33,9 @@
3333
'url': '',
3434
'license': 'BSD 3-Clause',
3535
'install_requires': [
36-
'hdmf>=3.1.1,<4',
37-
'pynwb>=2.0.0,<3'
36+
'hdmf>=4.0.0,<5',
37+
'pynwb>=2.8.3,<4',
38+
"ndx-optogenetics>=0.2.0",
3839
],
3940
'packages': find_packages('src/pynwb'),
4041
'package_dir': {'': 'src/pynwb'},

spec/ndx-franklab-novela.extensions.yaml

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ groups:
8383
- name: lens
8484
dtype: text
8585
doc: lens info
86+
- name: frame_rate
87+
dtype: float
88+
doc: Frame rate of the camera, in frames per second.
89+
required: false
8690
- neurodata_type_def: HeaderDevice
8791
neurodata_type_inc: Device
8892
doc: metadata from global configuration from header
@@ -179,3 +183,155 @@ groups:
179183
- name: units
180184
dtype: text
181185
doc: 'units of fields, acceptable values: um or mm'
186+
- neurodata_type_def: FrankLabOptogeneticEpochsTable
187+
neurodata_type_inc: OptogeneticEpochsTable
188+
doc: General metadata about the optogenetic stimulation that may change per epoch,
189+
with fields specific to Loren Frank Lab experiments. If the spatial filter is
190+
ON, then the experimenter can stimulate in either open (frequency-based) or closed
191+
loop (theta-based), only when animal is in a particular position. If the spatial
192+
filter is OFF, then ignore the position (this is not common / doesn't happen).
193+
If the spatial filter is ON and the experimeter is stimulating in open loop mode
194+
and the animal enters the spatial filter rectangle, then immediately apply one
195+
and only one stimulation bout. If stimulating in closed loop mode and the animal
196+
enters the rectangle, then every time the particular theta phase is detected,
197+
immediately apply one stimulation bout (accounting for the lockout period).
198+
datasets:
199+
- name: epoch_name
200+
neurodata_type_inc: VectorData
201+
dtype: text
202+
doc: Name of the epoch.
203+
- name: epoch_number
204+
neurodata_type_inc: VectorData
205+
dtype: int
206+
doc: 1-indexed number of the epoch.
207+
- name: convenience_code
208+
neurodata_type_inc: VectorData
209+
dtype: text
210+
doc: Convenience code of the epoch.
211+
- name: epoch_type
212+
neurodata_type_inc: VectorData
213+
dtype: text
214+
doc: Type of the epoch.
215+
- name: theta_filter_on
216+
neurodata_type_inc: VectorData
217+
dtype: bool
218+
doc: Whether the theta filter was on. A theta filter is closed-loop stimulation
219+
- read one tetrode and calculate the phase. Depending on the phase of theta,
220+
apply stimulation immediately. If this column is not present, then the theta
221+
filter was not used.
222+
quantity: '?'
223+
- name: theta_filter_lockout_period_in_samples
224+
neurodata_type_inc: VectorData
225+
dtype: int
226+
doc: If the theta filter was used, lockout period in the number of samples (based
227+
on the clock of the SpikeGadgets hardware) needed between stimulations, start
228+
to start. Use -1 if the theta filter was not used.
229+
quantity: '?'
230+
- name: theta_filter_phase_in_deg
231+
neurodata_type_inc: VectorData
232+
dtype: float
233+
doc: 'If the theta filter was used, phase in degrees during closed-loop theta
234+
phase-specific stimulation experiments. 0 is defined as the trough. 90 is ascending
235+
phase. Options are: 0, 90, 180, 270, 360, NaN. Use NaN if the theta filter was
236+
not used.'
237+
quantity: '?'
238+
- name: theta_filter_reference_ntrode
239+
neurodata_type_inc: VectorData
240+
dtype: int
241+
doc: If the theta filter was used, reference electrode that used used for theta
242+
phase-specific stimulation. ntrode is related to SpikeGadgets. ntrodes are specified
243+
in the electrode groups. (note that ntrodes are 1-indexed.) mapping from ntrode
244+
to electrode ID is in the electrode metadata files. Use -1 if the theta filter
245+
was not used.
246+
quantity: '?'
247+
- name: spatial_filter_on
248+
neurodata_type_inc: VectorData
249+
dtype: bool
250+
doc: Whether the spatial filter was on. Closed-loop stimulation based on whether
251+
the position of the animal is within a specified rectangular region of the video.
252+
If this column is not present, then the spatial filter was not used.
253+
quantity: '?'
254+
- name: spatial_filter_lockout_period_in_samples
255+
neurodata_type_inc: VectorData
256+
dtype: int
257+
doc: If the spatial filter was used, lockout period in the number of samples.
258+
Uses trodes time (samplecount). Use -1 if the spatial filter was not used.
259+
quantity: '?'
260+
- name: spatial_filter_bottom_left_coord_in_pixels
261+
neurodata_type_inc: VectorData
262+
dtype: int
263+
dims:
264+
- n_epochs
265+
- x y
266+
shape:
267+
- null
268+
- 2
269+
doc: If the spatial filter was used, the (x, y) coordinate of the bottom-left
270+
corner pixel of the rectangular region of the video that was used for space-specific
271+
stimulation. (0,0) is the bottom-left corner of the video. Use (-1, -1) if the
272+
spatial filter was not used.
273+
quantity: '?'
274+
- name: spatial_filter_top_right_coord_in_pixels
275+
neurodata_type_inc: VectorData
276+
dtype: int
277+
dims:
278+
- n_epochs
279+
- x y
280+
shape:
281+
- null
282+
- 2
283+
doc: If the spatial filter was used, the (x, y) coordinate of the top-right corner
284+
pixel of the rectangular region of the video that was used for space-specific
285+
stimulation. (0,0) is the bottom-left corner of the video. Use (-1, -1) if the
286+
spatial filter was not used.
287+
quantity: '?'
288+
- name: spatial_filter_cameras_index
289+
neurodata_type_inc: VectorIndex
290+
doc: Index column for `spatial_filter_cameras` so that each epoch can have multiple
291+
cameras.
292+
quantity: '?'
293+
- name: spatial_filter_cameras
294+
neurodata_type_inc: VectorData
295+
dtype:
296+
target_type: CameraDevice
297+
reftype: object
298+
doc: References to camera objects used for the spatial filter.
299+
quantity: '?'
300+
- name: spatial_filter_cameras_cm_per_pixel_index
301+
neurodata_type_inc: VectorIndex
302+
doc: Index column for `spatial_filter_cameras_cm_per_pixel` so that each epoch
303+
can have multiple cameras.
304+
quantity: '?'
305+
- name: spatial_filter_cameras_cm_per_pixel
306+
neurodata_type_inc: VectorData
307+
dtype: float
308+
doc: The cm/pixel values for each spatial filter camera used in this epoch, in
309+
the same order as `spatial_filter_cameras`. Use this if the cm/pixel values
310+
change per epoch. Otherwise, use the `meters_per_pixel` attribute of `CameraDevice`.
311+
quantity: '?'
312+
- name: ripple_filter_on
313+
neurodata_type_inc: VectorData
314+
dtype: bool
315+
doc: Whether the ripple filter was on. Closed-loop stimulation based on whether
316+
a ripple was detected - whether N tetrodes have their signal cross the standard
317+
deviation threshold. If this column is not present, then the ripple filter was
318+
not used.
319+
quantity: '?'
320+
- name: ripple_filter_lockout_period_in_samples
321+
neurodata_type_inc: VectorData
322+
dtype: int
323+
doc: If the ripple filter was used, lockout period in the number of samples. Uses
324+
trodes time (samplecount).
325+
quantity: '?'
326+
- name: ripple_filter_threshold_sd
327+
neurodata_type_inc: VectorData
328+
dtype: float
329+
doc: If the ripple filter was used, the threshold for detecting a ripple, in number
330+
of standard deviations.
331+
quantity: '?'
332+
- name: ripple_filter_num_above_threshold
333+
neurodata_type_inc: VectorData
334+
dtype: int
335+
doc: If the ripple filter was used, the number of tetrodes that have their signal
336+
cross the standard deviation threshold.
337+
quantity: '?'

spec/ndx-franklab-novela.namespace.yaml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ namespaces:
1414
name: ndx-franklab-novela
1515
schema:
1616
- namespace: core
17-
neurodata_types:
18-
- ElectrodeGroup
19-
- Device
20-
- NWBDataInterface
17+
- namespace: ndx-optogenetics
2118
- source: ndx-franklab-novela.extensions.yaml
22-
version: 0.1.0
19+
version: 0.2.0

src/pynwb/ndx_franklab_novela/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import os
22

33
from pynwb import load_namespaces, get_class
4+
import ndx_optogenetics # noqa: F401
5+
# the above import is needed because the definition of FrankLabOptogeneticsEpochsTable
6+
# depends on the definition of OptogeneticEpochsTable in ndx_optogenetics
47

58
# Set path of the namespace.yaml file to the expected install location
69

@@ -26,6 +29,7 @@
2629
AssociatedFiles = get_class('AssociatedFiles', 'ndx-franklab-novela')
2730
CameraDevice = get_class('CameraDevice', 'ndx-franklab-novela')
2831
DataAcqDevice = get_class('DataAcqDevice', 'ndx-franklab-novela')
32+
FrankLabOptogeneticEpochsTable = get_class('FrankLabOptogeneticEpochsTable', 'ndx-franklab-novela')
2933
HeaderDevice = get_class('HeaderDevice', 'ndx-franklab-novela')
3034
NwbElectrodeGroup = get_class('NwbElectrodeGroup', 'ndx-franklab-novela')
3135
Probe = get_class('Probe', 'ndx-franklab-novela')

src/pynwb/tests/test_cameraDevice.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,20 @@ def test_cameraDevice_created_successfully(self):
99

1010
cam_1 = CameraDevice(
1111
name='1',
12+
description="Camera used for tracking running",
1213
meters_per_pixel=0.20,
1314
camera_name='test name',
1415
model='ndx2000',
1516
lens='500dpt',
16-
manufacturer='sony'
17+
manufacturer='sony',
18+
frame_rate=30.0,
1719
)
1820

1921
self.assertEqual(cam_1.name, '1')
22+
self.assertEqual(cam_1.description, 'Camera used for tracking running')
23+
self.assertEqual(cam_1.meters_per_pixel, 0.20)
2024
self.assertEqual(cam_1.camera_name, 'test name')
2125
self.assertEqual(cam_1.model, 'ndx2000')
2226
self.assertEqual(cam_1.lens, '500dpt')
2327
self.assertEqual(cam_1.manufacturer, 'sony')
28+
self.assertEqual(cam_1.frame_rate, 30.0)

src/pynwb/tests/test_nwbElectrodeGroup.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import numpy as np
12
import unittest
23
from unittest.mock import Mock
34

@@ -38,7 +39,10 @@ def test_nwb_electrode_group_successful_created(self):
3839
self.assertEqual(nwb_electrode_group.description, description)
3940
self.assertEqual(nwb_electrode_group.location, location)
4041
self.assertEqual(nwb_electrode_group.device, device)
41-
self.assertEqual(nwb_electrode_group.position, position)
42+
np.testing.assert_array_equal(
43+
nwb_electrode_group.position,
44+
np.array((1., 2., 3.), dtype=[('x', '<f8'), ('y', '<f8'), ('z', '<f8')])
45+
)
4246
self.assertEqual(nwb_electrode_group.targeted_location, targeted_location)
4347
self.assertEqual(nwb_electrode_group.targeted_x, targeted_x)
4448
self.assertEqual(nwb_electrode_group.targeted_y, targeted_y)

0 commit comments

Comments
 (0)