Skip to content

feat: display Jupyter notebook in cluster detail page#181

Merged
glaubervila merged 5 commits into
mainfrom
180-display-jupyter-notebook-in-cluster-detail
Jun 30, 2026
Merged

feat: display Jupyter notebook in cluster detail page#181
glaubervila merged 5 commits into
mainfrom
180-display-jupyter-notebook-in-cluster-detail

Conversation

@glaubervila

Copy link
Copy Markdown
Contributor

Adds a pre-rendered Jupyter notebook panel at the bottom of the Cluster Detail page. The notebook is executed server-side with the cluster's data injected as variables and returned as HTML, displayed in an iframe below the Members/Aladin section.

Backend:

  • New endpoint GET /api/metadata/user_tables/{id}/notebook/ that fetches a single cluster row, injects the record, table metadata, and (for cluster catalogs) the related members data into a dedicated # canvas-variables notebook cell, executes it in-process, and returns the rendered HTML via nbconvert
  • query_data helper maps UCD-based virtual fields (meta_id, meta_ra, meta_dec, meta_radius_arcmin) onto each fetched row
  • ucds field added to NestedTableSerializer exposing a UCD→column-name map for notebooks to consume
  • Added dependencies: nbconvert, nbformat, ipykernel, matplotlib

Frontend:

  • getNotebookHtml service calls the new endpoint using catalog.property_id as the filter key and record.meta_id as the value
  • ClusterDetail fetches the notebook HTML with useQuery and renders it in an iframe via srcDoc
  • A Skeleton fills the space while loading; on onLoad the iframe's scrollHeight is measured (with a 500ms re-measure to capture late-painted images/CSS) and the Paper expands to the exact content height
  • Layout fixes: removed height: 100% constraints that capped the page at viewport height, preventing the notebook content from being scrollable in full

Test plan
Open a cluster detail page for a catalog with a notebook template configured
Verify the Skeleton appears while the notebook loads
Verify the notebook renders fully below the Members/Aladin section
Scroll to the bottom and confirm the full notebook content is visible with spacing at the end
Verify the Members tab, Properties tab, and Aladin viewer still work correctly
Open a cluster from a catalog without a notebook — confirm no iframe or skeleton appears

Add GET /api/metadata/user_tables/{id}/notebook/ action to
UserTableViewSet. The endpoint queries a single row from the dynamic
user table using the same filter mechanism as the data/ endpoint,
substitutes the row data into a Jinja-style .ipynb template, and
returns the notebook rendered as HTML via nbconvert.

Also add nbconvert and nbformat to requirements and ship the initial
cluster_detail_template.ipynb with placeholders for meta_id, meta_ra,
meta_dec, and meta_radius_arcmin.
… panel

Add getNotebookHtml service that calls the new notebook/ endpoint using
catalog.property_id as the dynamic filter key and record.meta_id as the
value, avoiding any assumption about fixed column names.

In ClusterDetail, fetch the notebook HTML with useQuery and render it
in an iframe using srcDoc. A Skeleton fills the panel while loading;
once the iframe fires onLoad, its scrollHeight is measured and the Paper
expands to the exact content height, replacing the Skeleton.
Replace simple template string substitution with in-process notebook
execution for the cluster detail view. The notebook now receives the
main record, its table metadata, and (for cluster catalogs) the related
members table data and metadata as Python literals injected into a
dedicated `# canvas-variables` cell.

Key changes:
- Add `_execute_notebook_inprocess` to run notebook code cells in-process,
  capturing stdout/stderr streams and matplotlib figures as outputs
- Add `query_data` helper that fetches rows and maps UCD-based virtual
  fields (meta_id, meta_ra, meta_dec, meta_radius_arcmin) onto each row
- Add `ucds` field to `NestedTableSerializer` exposing a UCD→column-name
  map consumed by the notebook
- For cluster catalogs, also fetch the related members table data and pass
  it to the notebook
- Add `ipykernel` and `matplotlib` dependencies; exclude .ipynb files
  from ruff linting
- Remove height: 100% from page Box so the page can grow beyond the viewport
- Remove height: 100% from ClusterDetail outer Box; Grid items already have
  explicit heights so the constraint was unnecessary and clipped the notebook
- Fix Skeleton height: replace flex:1 (collapsed without a parent height) with
  a fixed height={500}
- Re-measure iframe height 500ms after onLoad to capture late-painted content
  (images, CSS) that affects scrollHeight after initial load
- Add bottom spacer Box below the notebook Paper for page breathing room
@glaubervila
glaubervila requested a review from rcboufleur June 30, 2026 22:42
@glaubervila glaubervila linked an issue Jun 30, 2026 that may be closed by this pull request
@glaubervila
glaubervila merged commit 01aa3fc into main Jun 30, 2026
0 of 2 checks passed
@glaubervila
glaubervila deleted the 180-display-jupyter-notebook-in-cluster-detail branch June 30, 2026 22:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Jupyter notebook rendering in cluster detail

1 participant