Skip to content

Commit 0a70990

Browse files
stephprincerly
andauthored
add custom html field generation method for TimeSeries (#1831)
* add custom html field generation method for timeseries * update CHANGELOG.md * modify html representation of linked timestamps and data * add test for html representation of linked data * update html representation of linked objects * update html representation test * Use HDMF 3.12.2 --------- Co-authored-by: Ryan Ly <[email protected]>
1 parent f77f33c commit 0a70990

File tree

7 files changed

+55
-4
lines changed

7 files changed

+55
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
- Fix bug where namespaces were loaded in "w-" mode. @h-mayorquin [#1795](https://github.com/NeurodataWithoutBorders/pynwb/pull/1795)
1919
- Fix bug where pynwb version was reported as "unknown" to readthedocs @stephprince [#1810](https://github.com/NeurodataWithoutBorders/pynwb/pull/1810)
2020
- Fixed bug to allow linking of `TimeSeries.data` by setting the `data` constructor argument to another `TimeSeries`. @oruebel [#1766](https://github.com/NeurodataWithoutBorders/pynwb/pull/1766)
21+
- Fix recursion error in html representation generation in jupyter notebooks. @stephprince [#1831](https://github.com/NeurodataWithoutBorders/pynwb/pull/1831)
2122

2223
### Documentation and tutorial enhancements
2324
- Add RemFile to streaming tutorial. @bendichter [#1761](https://github.com/NeurodataWithoutBorders/pynwb/pull/1761)

environment-ros3.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ channels:
66
dependencies:
77
- python==3.11
88
- h5py==3.8.0
9-
- hdmf==3.12.1
9+
- hdmf==3.12.2
1010
- matplotlib==3.7.1
1111
- numpy==1.24.2
1212
- pandas==2.0.0

requirements-min.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# minimum versions of package dependencies for installing PyNWB
22
h5py==2.10 # support for selection of datasets with list of indices added in 2.10
3-
hdmf==3.12.1
3+
hdmf==3.12.2
44
numpy==1.18
55
pandas==1.1.5
66
python-dateutil==2.7.3

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# pinned dependencies to reproduce an entire development environment to use PyNWB
22
h5py==3.10.0
3-
hdmf==3.12.1
3+
hdmf==3.12.2
44
numpy==1.26.1
55
pandas==2.1.2
66
python-dateutil==2.8.2

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
reqs = [
2222
'h5py>=2.10',
23-
'hdmf>=3.12.1',
23+
'hdmf>=3.12.2',
2424
'numpy>=1.16',
2525
'pandas>=1.1.5',
2626
'python-dateutil>=2.7.3',

src/pynwb/base.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,42 @@ def __get_links(self, links):
287287
def __add_link(self, links_key, link):
288288
self.fields.setdefault(links_key, list()).append(link)
289289

290+
def _generate_field_html(self, key, value, level, access_code):
291+
def find_location_in_memory_nwbfile(current_location: str, neurodata_object) -> str:
292+
"""
293+
Method for determining the location of a neurodata object within an in-memory NWBFile object. Adapted from
294+
neuroconv package.
295+
"""
296+
parent = neurodata_object.parent
297+
if parent is None:
298+
return neurodata_object.name + "/" + current_location
299+
elif parent.name == 'root':
300+
# Items in defined top-level places like acquisition, intervals, etc. do not act as 'containers'
301+
# in that they do not set the `.parent` attribute; ask if object is in their in-memory dictionaries
302+
# instead
303+
for parent_field_name, parent_field_value in parent.fields.items():
304+
if isinstance(parent_field_value, dict) and neurodata_object.name in parent_field_value:
305+
return parent_field_name + "/" + neurodata_object.name + "/" + current_location
306+
return neurodata_object.name + "/" + current_location
307+
return find_location_in_memory_nwbfile(
308+
current_location=neurodata_object.name + "/" + current_location, neurodata_object=parent
309+
)
310+
311+
# reassign value if linked timestamp or linked data to avoid recursion error
312+
if key in ['timestamps', 'data'] and isinstance(value, TimeSeries):
313+
path_to_linked_object = find_location_in_memory_nwbfile(key, value)
314+
if key == 'timestamps':
315+
value = value.timestamps
316+
elif key == 'data':
317+
value = value.data
318+
key = f'{key} (link to {path_to_linked_object})'
319+
320+
if key in ['timestamp_link', 'data_link']:
321+
linked_key = 'timestamps' if key == 'timestamp_link' else 'data'
322+
value = [find_location_in_memory_nwbfile(linked_key, v) for v in value]
323+
324+
return super()._generate_field_html(key, value, level, access_code)
325+
290326
@property
291327
def time_unit(self):
292328
return self.__time_unit

tests/unit/test_base.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,20 @@ def test_file_with_starting_time_and_timestamps_in_construct_mode(self):
464464
timestamps=[1, 2, 3, 4, 5]
465465
)
466466

467+
def test_repr_html(self):
468+
""" Test that html representation of linked timestamp data will occur as expected and will not cause a
469+
RecursionError
470+
"""
471+
data1 = [0, 1, 2, 3]
472+
data2 = [4, 5, 6, 7]
473+
timestamps = [0.0, 0.1, 0.2, 0.3]
474+
ts1 = TimeSeries(name="test_ts1", data=data1, unit="grams", timestamps=timestamps)
475+
ts2 = TimeSeries(name="test_ts2", data=data2, unit="grams", timestamps=ts1)
476+
pm = ProcessingModule(name="processing", description="a test processing module")
477+
pm.add(ts1)
478+
pm.add(ts2)
479+
self.assertIn('(link to processing/test_ts1/timestamps)', pm._repr_html_())
480+
467481

468482
class TestImage(TestCase):
469483
def test_init(self):

0 commit comments

Comments
 (0)