Skip to content

Commit 9b52025

Browse files
authored
Merge pull request #482 from NeurodataWithoutBorders/enh/analysis
final changes before minor release
2 parents e21a7c1 + ccd7a29 commit 9b52025

File tree

10 files changed

+97
-32
lines changed

10 files changed

+97
-32
lines changed

docs/gallery/domain/ophys.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,13 @@
4747

4848

4949
optical_channel = OpticalChannel('my_optchan', 'Ca2+ imaging example',
50-
'pi wavelength', '3.14')
50+
'pi wavelength', 500.)
5151
imaging_plane = nwbfile.create_imaging_plane('my_imgpln',
5252
'Ca2+ imaging example',
5353
optical_channel,
5454
'a very interesting part of the brain',
5555
'imaging_device_1',
56-
'6.28', '2.718', 'GFP', 'my favorite brain location',
56+
600., '2.718', 'GFP', 'my favorite brain location',
5757
(1, 2, 1, 2, 3), 4.0, 'manifold unit', 'A frame to refer to')
5858

5959

docs/gallery/general/linking_data.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,5 +252,5 @@
252252
#
253253
# External links are convenient but to share data we may want to hand a single file with all the
254254
# data to our collaborator rather than having to collect all relevant files. To do this,
255-
# :py:class:`~pynwb.from.backends.hdf5.h5tools.HDF5IO` (and in turn :py:class:`~pynwb.NWBHDF5IO`)
256-
# provide the convenience function :py:func:`~pynwb.from.backends.hdf5.h5tools.HDF5IO.copy_file`
255+
# :py:class:`~pynwb.form.backends.hdf5.h5tools.HDF5IO` (and in turn :py:class:`~pynwb.NWBHDF5IO`)
256+
# provide the convenience function :py:func:`~pynwb.form.backends.hdf5.h5tools.HDF5IO.copy_file`

src/pynwb/data/nwb.file.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ groups:
5959
end users. Such data should be placed in the analysis group. The analysis data
6060
should be documented so that it is sharable with other labs'
6161
name: analysis
62+
groups:
63+
- doc: Custom analysis results
64+
neurodata_type_inc: NWBContainer
65+
quantity: '*'
6266
- doc: "Experimental intervals, whether that be logically distinct sub-experiments\
6367
\ having a particular scientific goal, trials during an experiment, or epochs\
6468
\ deriving from analysis of data. COMMENT: Epochs provide pointers to time\

src/pynwb/data/nwb.ophys.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ groups:
227227
dtype: text
228228
name: device
229229
- doc: Excitation wavelength
230-
dtype: text
230+
dtype: float
231231
name: excitation_lambda
232232
- doc: Rate images are acquired, in Hz.
233233
dtype: text
@@ -283,7 +283,7 @@ groups:
283283
dtype: text
284284
name: description
285285
- doc: Emission lambda for channel
286-
dtype: text
286+
dtype: float
287287
name: emission_lambda
288288
doc: 'One of possibly many groups storing channel-specific data COMMENT: Name
289289
is arbitrary but should be meaningful'

src/pynwb/file.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ class NWBFile(MultiContainerInterface):
8181
'type': NWBDataInterface,
8282
'get': 'get_acquisition'
8383
},
84+
{
85+
'attr': 'analysis',
86+
'add': 'add_analysis',
87+
'type': NWBContainer,
88+
'get': 'get_analysis'
89+
},
8490
{
8591
'attr': 'stimulus',
8692
'add': 'add_stimulus',

src/pynwb/form/container.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ class Container(with_metaclass(abc.ABCMeta, object)):
99
{'name': 'parent', 'type': 'Container', 'doc': 'the Container that holds this Container', 'default': None},
1010
{'name': 'container_source', 'type': str, 'doc': 'the source of this container', 'default': None})
1111
def __init__(self, **kwargs):
12-
self.__name = getargs('name', kwargs)
12+
name = getargs('name', kwargs)
13+
if '/' in name:
14+
raise ValueError("name '" + name + "' cannot contain '/'")
15+
self.__name = name
1316
self.__parent = getargs('parent', kwargs)
1417
self.__container_source = getargs('container_source', kwargs)
1518
self.__children = list()

src/pynwb/ophys.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class OpticalChannel(NWBContainer):
2323
@docval({'name': 'name', 'type': str, 'doc': 'the name of this electrode'},
2424
{'name': 'source', 'type': str, 'doc': 'the source of the data'},
2525
{'name': 'description', 'type': str, 'doc': 'Any notes or comments about the channel.'},
26-
{'name': 'emission_lambda', 'type': str, 'doc': 'Emission lambda for channel.'},
26+
{'name': 'emission_lambda', 'type': float, 'doc': 'Emission lambda for channel.'},
2727
{'name': 'parent', 'type': 'NWBContainer',
2828
'doc': 'The parent NWBContainer for this NWBContainer', 'default': None})
2929
def __init__(self, **kwargs):
@@ -57,11 +57,11 @@ class ImagingPlane(NWBContainer):
5757
'doc': 'One of possibly many groups storing channelspecific data.'},
5858
{'name': 'description', 'type': str, 'doc': 'Description of this ImagingPlane.'},
5959
{'name': 'device', 'type': str, 'doc': 'Name of device in /general/devices'},
60-
{'name': 'excitation_lambda', 'type': str, 'doc': 'Excitation wavelength.'},
60+
{'name': 'excitation_lambda', 'type': float, 'doc': 'Excitation wavelength.'},
6161
{'name': 'imaging_rate', 'type': str, 'doc': 'Rate images are acquired, in Hz.'},
6262
{'name': 'indicator', 'type': str, 'doc': 'Calcium indicator'},
6363
{'name': 'location', 'type': str, 'doc': 'Location of image plane.'},
64-
{'name': 'manifold', 'type': Iterable, 'doc': 'Physical position of each pixel. height, weight, x, y, z.',
64+
{'name': 'manifold', 'type': Iterable, 'doc': 'Physical position of each pixel. height, weight, xyz.',
6565
'default': None},
6666
{'name': 'conversion', 'type': float,
6767
'doc': 'Multiplier to get from stored values to specified unit (e.g., 1e-3 for millimeters)',

tests/integration/ui_write/test_ophys.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ class TestImagingPlaneIO(base.TestMapRoundTrip):
2323

2424
def setUpContainer(self):
2525
self.optical_channel = OpticalChannel('optchan1', 'unit test TestImagingPlaneIO',
26-
'a fake OpticalChannel', '3.14')
26+
'a fake OpticalChannel', 500.)
2727
return ImagingPlane('imgpln1', 'unit test TestImagingPlaneIO', self.optical_channel,
28-
'a fake ImagingPlane', 'imaging_device_1', '6.28', '2.718', 'GFP', 'somewhere in the brain')
28+
'a fake ImagingPlane', 'imaging_device_1', 600., '2.718', 'GFP', 'somewhere in the brain')
2929

3030
def setUpBuilder(self):
3131
optchan_builder = GroupBuilder(
@@ -37,7 +37,7 @@ def setUpBuilder(self):
3737
'source': 'unit test TestImagingPlaneIO'},
3838
datasets={
3939
'description': DatasetBuilder('description', 'a fake OpticalChannel'),
40-
'emission_lambda': DatasetBuilder('emission_lambda', '3.14')},
40+
'emission_lambda': DatasetBuilder('emission_lambda', 500.)},
4141
)
4242
return GroupBuilder(
4343
'imgpln1',
@@ -49,7 +49,7 @@ def setUpBuilder(self):
4949
datasets={
5050
'description': DatasetBuilder('description', 'a fake ImagingPlane'),
5151
'device': DatasetBuilder('device', 'imaging_device_1'),
52-
'excitation_lambda': DatasetBuilder('excitation_lambda', '6.28'),
52+
'excitation_lambda': DatasetBuilder('excitation_lambda', 600.),
5353
'imaging_rate': DatasetBuilder('imaging_rate', '2.718'),
5454
'indicator': DatasetBuilder('indicator', 'GFP'),
5555
'location': DatasetBuilder('location', 'somewhere in the brain')},
@@ -70,10 +70,10 @@ def getContainer(self, nwbfile):
7070
class TestTwoPhotonSeries(base.TestDataInterfaceIO):
7171

7272
def make_imaging_plane(self, source):
73-
self.optical_channel = OpticalChannel('optchan1', source, 'a fake OpticalChannel', '3.14')
73+
self.optical_channel = OpticalChannel('optchan1', source, 'a fake OpticalChannel', 500.)
7474
self.imaging_plane = ImagingPlane('imgpln1', source, self.optical_channel,
7575
'a fake ImagingPlane',
76-
'imaging_device_1', '6.28', '2.718', 'GFP', 'somewhere in the brain')
76+
'imaging_device_1', 600., '2.718', 'GFP', 'somewhere in the brain')
7777

7878
def setUpContainer(self):
7979
self.make_imaging_plane('unit test TestTwoPhotonSeries')
@@ -95,7 +95,7 @@ def setUpBuilder(self):
9595
'source': 'unit test TestTwoPhotonSeries'},
9696
datasets={
9797
'description': DatasetBuilder('description', 'a fake OpticalChannel'),
98-
'emission_lambda': DatasetBuilder('emission_lambda', '3.14')},
98+
'emission_lambda': DatasetBuilder('emission_lambda', 500.)},
9999
)
100100
imgpln_builder = GroupBuilder(
101101
'imgpln1',
@@ -107,7 +107,7 @@ def setUpBuilder(self):
107107
datasets={
108108
'description': DatasetBuilder('description', 'a fake ImagingPlane'),
109109
'device': DatasetBuilder('device', 'imaging_device_1'),
110-
'excitation_lambda': DatasetBuilder('excitation_lambda', '6.28'),
110+
'excitation_lambda': DatasetBuilder('excitation_lambda', 600.),
111111
'imaging_rate': DatasetBuilder('imaging_rate', '2.718'),
112112
'indicator': DatasetBuilder('indicator', 'GFP'),
113113
'location': DatasetBuilder('location', 'somewhere in the brain')},
@@ -167,13 +167,13 @@ def buildPlaneSegmentation(self):
167167
starting_frame=[1, 2, 3], format='tiff', timestamps=list())
168168

169169
self.optical_channel = OpticalChannel('test_optical_channel', 'optical channel source',
170-
'optical channel description', '3.14')
170+
'optical channel description', 500.)
171171
self.imaging_plane = ImagingPlane('test_imaging_plane',
172172
'ophys integration tests',
173173
self.optical_channel,
174174
'imaging plane description',
175175
'imaging_device_1',
176-
'6.28', '2.718', 'GFP', 'somewhere in the brain',
176+
600., '2.718', 'GFP', 'somewhere in the brain',
177177
(1, 2, 1, 2, 3), 4.0, 'manifold unit', 'A frame to refer to')
178178

179179
self.img_mask = deepcopy(img_mask)
@@ -195,7 +195,7 @@ def get_plane_segmentation_builder(self):
195195
'source': 'optical channel source'},
196196
datasets={
197197
'description': DatasetBuilder('description', 'optical channel description'),
198-
'emission_lambda': DatasetBuilder('emission_lambda', '3.14')},
198+
'emission_lambda': DatasetBuilder('emission_lambda', 500.)},
199199
)
200200
self.imgpln_builder = GroupBuilder(
201201
'imgpln1',
@@ -207,7 +207,7 @@ def get_plane_segmentation_builder(self):
207207
datasets={
208208
'description': DatasetBuilder('description', 'imaging plane description'),
209209
'device': DatasetBuilder('device', 'imaging_device_1'),
210-
'excitation_lambda': DatasetBuilder('excitation_lambda', '6.28'),
210+
'excitation_lambda': DatasetBuilder('excitation_lambda', 600.),
211211
'imaging_rate': DatasetBuilder('imaging_rate', '2.718'),
212212
'indicator': DatasetBuilder('indicator', 'GFP'),
213213
'manifold': DatasetBuilder('manifold', (1, 2, 1, 2, 3),
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import unittest2 as unittest
2+
3+
from pynwb.form.container import Container
4+
5+
6+
class MyTestClass(Container):
7+
8+
def __init__(self, src, name, parent=None):
9+
super(MyTestClass, self).__init__(src, name, parent=parent)
10+
11+
def basic_add(self, **kwargs):
12+
return kwargs
13+
14+
def basic_add2(self, **kwargs):
15+
return kwargs
16+
17+
def basic_add2_kw(self, **kwargs):
18+
return kwargs
19+
20+
21+
class MyTestSubclass(MyTestClass):
22+
23+
def basic_add(self, **kwargs):
24+
return kwargs
25+
26+
def basic_add2_kw(self, **kwargs):
27+
return kwargs
28+
29+
30+
class TestNWBContainer(unittest.TestCase):
31+
32+
def test_constructor(self):
33+
"""Test that constructor properly sets parent
34+
"""
35+
parent_obj = MyTestClass('test source', 'obj1')
36+
child_obj = MyTestSubclass('test source', 'obj2', parent=parent_obj)
37+
self.assertIs(child_obj.parent, parent_obj)
38+
39+
def test_set_parent_parent(self):
40+
"""Test that parent setter properly sets parent
41+
"""
42+
parent_obj = MyTestClass('test source', 'obj1')
43+
child_obj = MyTestSubclass('test source', 'obj2')
44+
child_obj.parent = parent_obj
45+
self.assertIs(child_obj.parent, parent_obj)
46+
47+
def test_slash_restriction(self):
48+
self.assertRaises(ValueError, Container, 'bad/name')
49+
50+
51+
if __name__ == '__main__':
52+
unittest.main()

tests/unit/pynwb_tests/test_ophys.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ def CreatePlaneSegmentation():
1717
iSS = ImageSeries(name='test_iS', source='a hypothetical source', data=list(), unit='unit',
1818
external_file=['external_file'], starting_frame=[1, 2, 3], format='tiff', timestamps=list())
1919

20-
oc = OpticalChannel('test_optical_channel', 'test_source', 'description', 'emission_lambda')
21-
ip = ImagingPlane('test_imaging_plane', 'test_source', oc, 'description', 'device', 'excitation_lambda',
20+
oc = OpticalChannel('test_optical_channel', 'test_source', 'description', 500.)
21+
ip = ImagingPlane('test_imaging_plane', 'test_source', oc, 'description', 'device', 600.,
2222
'imaging_rate', 'indicator', 'location', (1, 2, 1, 2, 3), 4.0, 'unit', 'reference_frame')
2323

2424
pS = PlaneSegmentation('test source', 'description', ip, 'test_name', iSS)
@@ -29,19 +29,19 @@ def CreatePlaneSegmentation():
2929

3030
class TwoPhotonSeriesConstructor(unittest.TestCase):
3131
def test_init(self):
32-
oc = OpticalChannel('test_name', 'test_source', 'description', 'emission_lambda')
32+
oc = OpticalChannel('test_name', 'test_source', 'description', 500.)
3333
self.assertEqual(oc.description, 'description')
34-
self.assertEqual(oc.emission_lambda, 'emission_lambda')
34+
self.assertEqual(oc.emission_lambda, 500.)
3535

36-
ip = ImagingPlane('test_imaging_plane', 'test source', oc, 'description', 'device', 'excitation_lambda',
37-
'imaging_rate', 'indicator', 'location', (1, 2, 1, 2, 3), 4.0, 'unit', 'reference_frame')
36+
ip = ImagingPlane('test_imaging_plane', 'test source', oc, 'description', 'device', 600.,
37+
'imaging_rate', 'indicator', 'location', (50, 100, 3), 4.0, 'unit', 'reference_frame')
3838
self.assertEqual(ip.optical_channel[0], oc)
3939
self.assertEqual(ip.device, 'device')
40-
self.assertEqual(ip.excitation_lambda, 'excitation_lambda')
40+
self.assertEqual(ip.excitation_lambda, 600.)
4141
self.assertEqual(ip.imaging_rate, 'imaging_rate')
4242
self.assertEqual(ip.indicator, 'indicator')
4343
self.assertEqual(ip.location, 'location')
44-
self.assertEqual(ip.manifold, (1, 2, 1, 2, 3))
44+
self.assertEqual(ip.manifold, (50, 100, 3))
4545
self.assertEqual(ip.conversion, 4.0)
4646
self.assertEqual(ip.unit, 'unit')
4747
self.assertEqual(ip.reference_frame, 'reference_frame')
@@ -145,8 +145,8 @@ def test_init(self):
145145
iSS = ImageSeries(name='test_iS', source='a hypothetical source', data=list(), unit='unit',
146146
external_file=['external_file'], starting_frame=[1, 2, 3], format='tiff', timestamps=list())
147147

148-
oc = OpticalChannel('test_optical_channel', 'test_source', 'description', 'emission_lambda')
149-
ip = ImagingPlane('test_imaging_plane', 'test_source', oc, 'description', 'device', 'excitation_lambda',
148+
oc = OpticalChannel('test_optical_channel', 'test_source', 'description', 500.)
149+
ip = ImagingPlane('test_imaging_plane', 'test_source', oc, 'description', 'device', 600.,
150150
'imaging_rate', 'indicator', 'location', (1, 2, 1, 2, 3), 4.0, 'unit', 'reference_frame')
151151

152152
pS = PlaneSegmentation('test source', 'description', ip, 'test_name', iSS)

0 commit comments

Comments
 (0)