Skip to content

Commit 060d73a

Browse files
h-mayorquinzm711
andauthored
Add html repr to raw io readers (#1651)
* first iteration * add html repr * background * use correct boolean --------- Co-authored-by: Zach McKenzie <[email protected]>
1 parent 48b855f commit 060d73a

File tree

1 file changed

+178
-0
lines changed

1 file changed

+178
-0
lines changed

neo/rawio/baserawio.py

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,184 @@ def __repr__(self):
239239

240240
return txt
241241

242+
def _repr_html_(self):
243+
"""
244+
HTML representation for the raw recording base.
245+
246+
Returns
247+
-------
248+
html : str
249+
The HTML representation as a string.
250+
"""
251+
html = []
252+
html.append('<div style="font-family: Arial, sans-serif; max-width: 1000px; margin: 0 auto;">')
253+
254+
# Header
255+
html.append(f'<h3 style="color: #2c3e50;">{self.__class__.__name__}: {self.source_name()}</h3>')
256+
257+
if self.is_header_parsed:
258+
# Basic info
259+
nb_block = self.block_count()
260+
html.append(f"<p><strong>nb_block:</strong> {nb_block}</p>")
261+
nb_seg = [self.segment_count(i) for i in range(nb_block)]
262+
html.append(f"<p><strong>nb_segment:</strong> {nb_seg}</p>")
263+
264+
# CSS for tables - using only black, white, and gray colors
265+
html.append(
266+
"""
267+
<style>
268+
#{unique_id} table.neo-table {{
269+
border-collapse: collapse;
270+
width: 100%;
271+
margin-bottom: 20px;
272+
font-size: 14px;
273+
color: inherit;
274+
background-color: transparent;
275+
}}
276+
#{unique_id} table.neo-table th,
277+
#{unique_id} table.neo-table td {{
278+
border: 1px solid #888;
279+
padding: 8px;
280+
text-align: left;
281+
}}
282+
#{unique_id} table.neo-table th {{
283+
background-color: rgba(128,128,128,0.2);
284+
}}
285+
#{unique_id} table.neo-table tr:nth-child(even) {{
286+
background-color: rgba(128,128,128,0.1);
287+
}}
288+
#{unique_id} details {{
289+
margin-bottom: 15px;
290+
border: 1px solid rgba(128,128,128,0.3);
291+
border-radius: 4px;
292+
overflow: hidden;
293+
background-color: transparent;
294+
}}
295+
#{unique_id} summary {{
296+
padding: 10px;
297+
background-color: rgba(128,128,128,0.2);
298+
cursor: pointer;
299+
font-weight: bold;
300+
color: inherit;
301+
}}
302+
#{unique_id} details[open] summary {{
303+
border-bottom: 1px solid rgba(128,128,128,0.3);
304+
}}
305+
#{unique_id} .table-container {{
306+
padding: 10px;
307+
overflow-x: auto;
308+
background-color: transparent;
309+
}}
310+
</style>
311+
"""
312+
)
313+
314+
# Signal Streams
315+
signal_streams = self.header["signal_streams"]
316+
if signal_streams.size > 0:
317+
html.append("<details>")
318+
html.append("<summary>Signal Streams</summary>")
319+
html.append('<div class="table-container">')
320+
html.append('<table class="neo-table">')
321+
html.append("<thead><tr><th>Name</th><th>ID</th><th>Buffer ID</th><th>Channel Count</th></tr></thead>")
322+
html.append("<tbody>")
323+
324+
for i, stream in enumerate(signal_streams):
325+
html.append("<tr>")
326+
html.append(f'<td>{stream["name"]}</td>')
327+
html.append(f'<td>{stream["id"]}</td>')
328+
html.append(f'<td>{stream["buffer_id"]}</td>')
329+
html.append(f"<td>{self.signal_channels_count(i)}</td>")
330+
html.append("</tr>")
331+
332+
html.append("</tbody></table>")
333+
html.append("</div>")
334+
html.append("</details>")
335+
336+
# Signal Channels
337+
signal_channels = self.header["signal_channels"]
338+
if signal_channels.size > 0:
339+
html.append("<details>")
340+
html.append("<summary>Signal Channels</summary>")
341+
html.append('<div class="table-container">')
342+
html.append('<table class="neo-table">')
343+
html.append(
344+
"<thead><tr><th>Name</th><th>ID</th><th>Sampling Rate</th><th>Data Type</th><th>Units</th><th>Gain</th><th>Offset</th><th>Stream ID</th><th>Buffer ID</th></tr></thead>"
345+
)
346+
html.append("<tbody>")
347+
348+
for channel in signal_channels:
349+
html.append("<tr>")
350+
html.append(f'<td>{channel["name"]}</td>')
351+
html.append(f'<td>{channel["id"]}</td>')
352+
html.append(f'<td>{channel["sampling_rate"]}</td>')
353+
html.append(f'<td>{channel["dtype"]}</td>')
354+
html.append(f'<td>{channel["units"]}</td>')
355+
html.append(f'<td>{channel["gain"]}</td>')
356+
html.append(f'<td>{channel["offset"]}</td>')
357+
html.append(f'<td>{channel["stream_id"]}</td>')
358+
html.append(f'<td>{channel["buffer_id"]}</td>')
359+
html.append("</tr>")
360+
361+
html.append("</tbody></table>")
362+
html.append("</div>")
363+
html.append("</details>")
364+
365+
# Spike Channels
366+
spike_channels = self.header["spike_channels"]
367+
if spike_channels.size > 0:
368+
html.append("<details>")
369+
html.append("<summary>Spike Channels</summary>")
370+
html.append('<div class="table-container">')
371+
html.append('<table class="neo-table">')
372+
html.append(
373+
"<thead><tr><th>Name</th><th>ID</th><th>WF Units</th><th>WF Gain</th><th>WF Offset</th><th>WF Left Sweep</th><th>WF Sampling Rate</th></tr></thead>"
374+
)
375+
html.append("<tbody>")
376+
377+
for channel in spike_channels:
378+
html.append("<tr>")
379+
html.append(f'<td>{channel["name"]}</td>')
380+
html.append(f'<td>{channel["id"]}</td>')
381+
html.append(f'<td>{channel["wf_units"]}</td>')
382+
html.append(f'<td>{channel["wf_gain"]}</td>')
383+
html.append(f'<td>{channel["wf_offset"]}</td>')
384+
html.append(f'<td>{channel["wf_left_sweep"]}</td>')
385+
html.append(f'<td>{channel["wf_sampling_rate"]}</td>')
386+
html.append("</tr>")
387+
388+
html.append("</tbody></table>")
389+
html.append("</div>")
390+
html.append("</details>")
391+
392+
# Event Channels
393+
event_channels = self.header["event_channels"]
394+
if event_channels.size > 0:
395+
html.append("<details>")
396+
html.append("<summary>Event Channels</summary>")
397+
html.append('<div class="table-container">')
398+
html.append('<table class="neo-table">')
399+
html.append("<thead><tr><th>Name</th><th>ID</th><th>Type</th></tr></thead>")
400+
html.append("<tbody>")
401+
402+
for channel in event_channels:
403+
html.append("<tr>")
404+
html.append(f'<td>{channel["name"]}</td>')
405+
html.append(f'<td>{channel["id"]}</td>')
406+
html.append(
407+
f'<td>{channel["type"].decode("utf-8") if isinstance(channel["type"], bytes) else channel["type"]}</td>'
408+
)
409+
html.append("</tr>")
410+
411+
html.append("</tbody></table>")
412+
html.append("</div>")
413+
html.append("</details>")
414+
else:
415+
html.append("<p><em>Call <code>parse_header()</code> to load the reader data.</p>")
416+
417+
html.append("</div>")
418+
return "\n".join(html)
419+
242420
def _generate_minimal_annotations(self):
243421
"""
244422
Helper function that generates a nested dict for annotations.

0 commit comments

Comments
 (0)