Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add custom html field generation method for TimeSeries #1831

Merged
merged 9 commits into from
Feb 9, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
### Bug fixes
- Fix bug where namespaces were loaded in "w-" mode. @h-mayorquin [#1795](https://github.com/NeurodataWithoutBorders/pynwb/pull/1795)
- Fix bug where pynwb version was reported as "unknown" to readthedocs @stephprince [#1810](https://github.com/NeurodataWithoutBorders/pynwb/pull/1810)
- Fix recursion error in html representation generation in jupyter notebooks. @stephprince [#1831](https://github.com/NeurodataWithoutBorders/pynwb/pull/1831)

### Documentation and tutorial enhancements
- Add RemFile to streaming tutorial. @bendichter [#1761](https://github.com/NeurodataWithoutBorders/pynwb/pull/1761)
Expand Down
36 changes: 36 additions & 0 deletions src/pynwb/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,42 @@ def __get_links(self, links):
def __add_link(self, links_key, link):
self.fields.setdefault(links_key, list()).append(link)

def _generate_field_html(self, key, value, level, access_code):
def find_location_in_memory_nwbfile(current_location: str, neurodata_object) -> str:
"""
Method for determining the location of a neurodata object within an in-memory NWBFile object. Adapted from
neuroconv package.
"""
parent = neurodata_object.parent
if parent is None:
return neurodata_object.name + "/" + current_location
elif parent.name == 'root':
# Items in defined top-level places like acquisition, intervals, etc. do not act as 'containers'
# in that they do not set the `.parent` attribute; ask if object is in their in-memory dictionaries
# instead
for parent_field_name, parent_field_value in parent.fields.items():
if isinstance(parent_field_value, dict) and neurodata_object.name in parent_field_value:
return parent_field_name + "/" + neurodata_object.name + "/" + current_location
return neurodata_object.name + "/" + current_location
return find_location_in_memory_nwbfile(
current_location=neurodata_object.name + "/" + current_location, neurodata_object=parent
)

# reassign value if linked timestamp or linked data to avoid recursion error
if key in ['timestamps', 'data'] and isinstance(value, TimeSeries):
path_to_linked_object = find_location_in_memory_nwbfile(key, value)
if key == 'timestamps':
value = value.timestamps
elif key == 'data':
value = value.data
key = f'{key} (link to {path_to_linked_object})'

if key in ['timestamp_link', 'data_link']:
linked_key = 'timestamps' if key == 'timestamp_link' else 'data'
value = [find_location_in_memory_nwbfile(linked_key, v) for v in value]

return super()._generate_field_html(key, value, level, access_code)

@property
def time_unit(self):
return self.__time_unit
Expand Down
11 changes: 11 additions & 0 deletions tests/unit/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,17 @@ def test_file_with_starting_time_and_timestamps_in_construct_mode(self):
timestamps=[1, 2, 3, 4, 5]
)

def test_repr_html(self):
""" Test that html representation of linked timestamp data will occur as expected and will not cause a
RecursionError
"""
data1 = [0, 1, 2, 3]
data2 = [4, 5, 6, 7]
timestamps = [0.0, 0.1, 0.2, 0.3]
ts1 = TimeSeries(name="test_ts1", data=data1, unit="grams", timestamps=timestamps)
ts2 = TimeSeries(name="test_ts2", data=data2, unit="grams", timestamps=ts1)
self.assertIn('(link to test_ts1/timestamps)', ts2._repr_html_())


class TestImage(TestCase):
def test_init(self):
Expand Down
Loading