From 779fa2543788469d50fe36932aff6de79e5f11b5 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Wed, 5 Dec 2018 10:41:34 -0800 Subject: [PATCH] Add lab meta data (#751) You can now write zero or more lab extensions and put them in /general/ by making them inherit from LabMetaData and calling nwbfile.add_lab_meta_data tests were also added --- src/pynwb/data/nwb.file.yaml | 4 +++ src/pynwb/file.py | 13 ++++++++ src/pynwb/io/file.py | 1 + tests/unit/pynwb_tests/test_core.py | 1 + tests/unit/pynwb_tests/test_extension.py | 40 +++++++++++++++++++++--- 5 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/pynwb/data/nwb.file.yaml b/src/pynwb/data/nwb.file.yaml index 9b9e79364..ab3371ba6 100644 --- a/src/pynwb/data/nwb.file.yaml +++ b/src/pynwb/data/nwb.file.yaml @@ -223,6 +223,10 @@ groups: date made, injection location, volume, etc. quantity: '?' groups: + - neurodata_type_def: LabMetaData + neurodata_type_inc: NWBContainer + doc: 'place-holder than can be extended so that lab-specific meta-data can be placed in /general' + quantity: '*' - name: devices doc: 'Description of hardware devices used during experiment. COMMENT: Eg, monitors, ADC boards, microscopes, etc' diff --git a/src/pynwb/file.py b/src/pynwb/file.py index 98f8a7b1d..861ed3bb6 100644 --- a/src/pynwb/file.py +++ b/src/pynwb/file.py @@ -28,6 +28,12 @@ class SpecFile(Container): pass +@register_class('LabMetaData', CORE_NAMESPACE) +class LabMetaData(NWBContainer): + def __init__(self, **kwargs): + super(LabMetaData, self).__init__(kwargs['name']) + + @register_class('Subject', CORE_NAMESPACE) class Subject(NWBContainer): @@ -141,6 +147,13 @@ class NWBFile(MultiContainerInterface): 'create': 'create_time_intervals', 'get': 'get_time_intervals' }, + { + 'attr': 'lab_meta_data', + 'add': 'add_lab_meta_data', + 'type': LabMetaData, + 'create': 'create_lab_meta_data', + 'get': 'get_lab_meta_data' + } ] __nwbfields__ = ('timestamps_reference_time', diff --git a/src/pynwb/io/file.py b/src/pynwb/io/file.py index ae98a6f72..9e6acbc12 100644 --- a/src/pynwb/io/file.py +++ b/src/pynwb/io/file.py @@ -47,6 +47,7 @@ def __init__(self, spec): self.map_spec('subject', general_spec.get_group('subject')) self.map_spec('devices', general_spec.get_group('devices').get_neurodata_type('Device')) + self.map_spec('lab_meta_data', general_spec.get_neurodata_type('LabMetaData')) @ObjectMapper.constructor_arg('session_start_time') def dateconversion(self, builder, manager): diff --git a/tests/unit/pynwb_tests/test_core.py b/tests/unit/pynwb_tests/test_core.py index 8c42cb76b..dc6ca36b1 100644 --- a/tests/unit/pynwb_tests/test_core.py +++ b/tests/unit/pynwb_tests/test_core.py @@ -309,6 +309,7 @@ def test_print_file(self): epoch_tags: """ + empty_set_str + """ ic_electrodes: { } imaging_planes: { } + lab_meta_data: { } modules: { } ogen_sites: { } stimulus: { } diff --git a/tests/unit/pynwb_tests/test_extension.py b/tests/unit/pynwb_tests/test_extension.py index 7a1d42636..bfb5c239f 100644 --- a/tests/unit/pynwb_tests/test_extension.py +++ b/tests/unit/pynwb_tests/test_extension.py @@ -1,13 +1,16 @@ -import unittest2 as unittest import os -from tempfile import gettempdir import random import string +from datetime import datetime +from dateutil.tz import tzlocal +from tempfile import gettempdir -from pynwb.spec import NWBNamespaceBuilder, NWBGroupSpec, NWBAttributeSpec, NWBDatasetSpec +import unittest2 as unittest +from pynwb import get_type_map, TimeSeries, NWBFile, register_class, docval, load_namespaces, popargs from pynwb.form.spec.spec import RefSpec -from pynwb import get_type_map, TimeSeries from pynwb.form.utils import get_docval +from pynwb.spec import NWBNamespaceBuilder, NWBGroupSpec, NWBAttributeSpec, NWBDatasetSpec +from pynwb.file import LabMetaData def id_generator(N=10): @@ -80,6 +83,35 @@ def test_load_namespace_with_reftype_attribute_check_autoclass_const(self): self.assertIsNotNone(docval) self.assertEqual(docval['type'], TimeSeries) + def test_lab_meta(self): + ns_builder = NWBNamespaceBuilder('Extension for use in my Lab', self.prefix) + test_meta_ext = NWBGroupSpec( + neurodata_type_def='MyTestMetaData', + neurodata_type_inc='LabMetaData', + doc='my test meta data', + attributes=[ + NWBAttributeSpec(name='test_attr', dtype='float', doc='test_dtype')]) + ns_builder.add_spec(self.ext_source, test_meta_ext) + ns_builder.export(self.ns_path, outdir=self.tempdir) + ns_abs_path = os.path.join(self.tempdir, self.ns_path) + + load_namespaces(ns_abs_path) + + @register_class('MyTestMetaData', self.prefix) + class MyTestMetaData(LabMetaData): + __nwbfields__ = ('test_attr',) + + @docval({'name': 'name', 'type': str, 'doc': 'name'}, + {'name': 'test_attr', 'type': float, 'doc': 'test attribute'}) + def __init__(self, **kwargs): + test_attr = popargs('test_attr', kwargs) + super(MyTestMetaData, self).__init__(**kwargs) + self.test_attr = test_attr + + nwbfile = NWBFile("a file with header data", "NB123A", datetime(2017, 5, 1, 12, 0, 0, tzinfo=tzlocal())) + + nwbfile.add_lab_meta_data(MyTestMetaData(name='test_name', test_attr=5.)) + class TestCatchDupNS(unittest.TestCase):