Notebook display#
Cards for Nominal resources
In Jupyter, VS Code and Colab, the Nominal Python Client shows assets, runs, datasets, channels and other resources as cards. A card shows the resource’s fields and related resources, links them to the Nominal app, and draws a diagram of how they connect. This notebook shows each card for a simulated helicopter rotor test rig, then how to configure the cards.
Requirements: a default profile
for the Nominal Python Client. Running the notebooks explains how to set it up.
Each execution of the notebook reuses the rig. It archives what earlier executions created on it, so the rig holds one set of data: this execution’s.
Generate sample data#
The rig is a tail rotor gearbox test stand, registered in Nominal as an asset. Its test spins up the rotor and measures the rotor’s angular velocity, the gearbox temperature and vibration.
The test records three minutes of data at 10 Hz: the rotor spins up, holds near 31 rad/s (about 300 rpm), and spins down. Telemetry and vibration go in separate tables:
| time | rotor_speed | gearbox_temp | |
|---|---|---|---|
| 0 | 2026-11-06 16:41:50.000+00:00 | 0.000 | 40.0 |
| 1 | 2026-11-06 16:41:50.100+00:00 | 0.105 | 40.0 |
| 2 | 2026-11-06 16:41:50.200+00:00 | 0.209 | 40.0 |
| 3 | 2026-11-06 16:41:50.300+00:00 | 0.314 | 40.0 |
| 4 | 2026-11-06 16:41:50.400+00:00 | 0.419 | 40.0 |
Each table becomes a dataset on the rig, under its own data scope:
Each phase of the test becomes a run on the rig, and the gearbox overheating late in the hold becomes an event:
Connect to Nominal#
A NominalClient connects to Nominal with a profile. Find the rig by
its properties:
from nominal.core import NominalClient
client = NominalClient.from_profile("default")
rig = client.search_assets(
properties={"rig_id": "RTR-02", "example": "notebook_display"}
)[0]
An asset#
The rig’s Asset card lists its properties, its datasets, by data scope,
and its runs, newest first. Each section starts folded. Click its title to open it.
Each name links to its page in Nominal, and its copy button copies its RID.
Open in Nominal opens the asset’s own page:
rig
Asset(rid='ri.scout.cerulean-staging.asset.c2d499a7-2fcf-5fd4-2e04-b74688da409f', name='Rotor test rig RTR-02', description='Tail rotor gearbox test stand', properties=mappingproxy({'example': 'notebook_display', 'rig_id': 'RTR-02'}), labels=('test-stand',), created_at=1793719747873297000, is_archived=False)Relationship diagrams#
The card above has a Relationships section, after the properties, with a diagram of the rig and the resources around it, drawn like the diagrams in Per-asset organization. Data flows from the bottom up: each dataset feeds the rig under its data scope’s ref name, and the rig’s runs sit above it. The rig’s own node has the thicker border. Each node links to its page in Nominal, and hovering over a node shows its full name.
Run, dataset, workbook, event and data review cards draw diagrams too. A diagram uses only what its card already loads, so it makes no requests of its own, and it needs no JavaScript, so it also displays in untrusted and saved notebooks.
An asset with hundreds of runs would make a diagram too large to read, so a diagram draws at most
graph_max_nodes nodes, 20 by default. Past that, each group shows its first few nodes and a
node counting the rest. That node links to the section of the card that lists them all.
The rig’s six nodes fit, so its diagram draws every dataset and run.
Here collapse_threshold=10 opens the sections with up to 10 items, so the diagram shows open:
from IPython.display import display
from nominal.core import set_display_options
with set_display_options(collapse_threshold=10):
display(rig)
Asset(rid='ri.scout.cerulean-staging.asset.c2d499a7-2fcf-5fd4-2e04-b74688da409f', name='Rotor test rig RTR-02', description='Tail rotor gearbox test stand', properties=mappingproxy({'example': 'notebook_display', 'rig_id': 'RTR-02'}), labels=('test-stand',), created_at=1793719747873297000, is_archived=False)show_graph=False leaves diagrams out of the cards.
Configuring the display#
set_display_options changes how every card looks,
from that cell on, and get_display_options returns the current settings as DisplayOptions:
from nominal.core import get_display_options, set_display_options
get_display_options()
DisplayOptions(style='html', timezone='UTC', time_format='iso', time_precision='s', show_link=True, fetch_details=True, collapse_threshold=0, max_items=50, fetch_timeout=5.0, show_graph=True, graph_max_nodes=20)
Timestamps show to the second by default. Hover over one to see it to the nanosecond. For example, show timestamps in the test site’s time zone, to the millisecond, in a more readable format:
set_display_options(
timezone="America/Denver", time_precision="ms", time_format="readable"
)
rig
Asset(rid='ri.scout.cerulean-staging.asset.c2d499a7-2fcf-5fd4-2e04-b74688da409f', name='Rotor test rig RTR-02', description='Tail rotor gearbox test stand', properties=mappingproxy({'example': 'notebook_display', 'rig_id': 'RTR-02'}), labels=('test-stand',), created_at=1793719747873297000, is_archived=False)Called on its own like this, a change lasts until the kernel restarts.
To use the same options in every notebook, put the call in an
IPython startup file,
such as ~/.ipython/profile_default/startup/nominal.py.
Each card loads its related resources from Nominal, with a few requests, and waits at most
fetch_timeout seconds, 5 by default, for them. A section that hasn’t loaded by then says so.
To display cards faster, or offline, fetch_details=False shows only the fields
the resource already holds.
Used in a with block, the change lasts only until the block ends:
from IPython.display import display
with set_display_options(fetch_details=False):
display(rig)
Asset(rid='ri.scout.cerulean-staging.asset.c2d499a7-2fcf-5fd4-2e04-b74688da409f', name='Rotor test rig RTR-02', description='Tail rotor gearbox test stand', properties=mappingproxy({'example': 'notebook_display', 'rig_id': 'RTR-02'}), labels=('test-stand',), created_at=1793719747873297000, is_archived=False)style="text" shows the plain text form instead of cards:
with set_display_options(style="text"):
display(rig)
Asset(rid='ri.scout.cerulean-staging.asset.c2d499a7-2fcf-5fd4-2e04-b74688da409f', name='Rotor test rig RTR-02', description='Tail rotor gearbox test stand', properties=mappingproxy({'example': 'notebook_display', 'rig_id': 'RTR-02'}), labels=('test-stand',), created_at=1793719747873297000, is_archived=False)
A dataset#
A Dataset card shows the time span of its data and lists its files and channels:
telemetry = rig.get_dataset("telemetry")
telemetry
Dataset(rid='ri.catalog.cerulean-staging.dataset.45d0b732-6fb5-c6b8-510b-abfd79fdff22', name='RTR-02 telemetry', description=None, properties=mappingproxy({'sample_rate_hz': '10'}), labels=('telemetry',), bounds=Bounds(start=1793983310000000000, end=1793983489900000000), is_archived=False)A channel#
A numeric Channel card summarizes its data: the number of samples, the minimum,
maximum and mean, the first and last timestamps, and a chart of its range and mean over time:
telemetry.get_channel("gearbox_temp")
Channel(name='gearbox_temp', data_source='ri.catalog.cerulean-staging.dataset.45d0b732-6fb5-c6b8-510b-abfd79fdff22', data_type=<ChannelDataType.DOUBLE: 'DOUBLE'>, unit='°C', description=None)
A run#
A Run card shows its number, time span and duration, and lists its assets, datasets and links:
hold = next(
run
for run in rig.list_runs()
if run.name == "RTR-02 Hold at 300 rpm" and not run.is_archived
)
hold
Run(rid='ri.scout.cerulean-staging.run.34046f6f-0356-0572-404e-89d3c53686e3', name='RTR-02 Hold at 300 rpm', description='', properties=mappingproxy({'gearbox_sn': 'GB-1187', 'test_bay': 'Bay 3'}), labels=('example',), links=({'url': 'https://dev.nominal.io/platform/data-model/', 'title': 'Nominal data model'},), start=1793983340000000000, end=1793983430000000000, run_number=470, assets=('ri.scout.cerulean-staging.asset.c2d499a7-2fcf-5fd4-2e04-b74688da409f',), created_at=1793983920124227000, is_archived=False)Files and events#
Other resources have cards too, such as a dataset’s files (DatasetFile)
and an asset’s events (Event):
next(iter(telemetry.list_files()))
DatasetFile(id='626f5535-9bc2-0b8d-7f80-830a2f9afdd5', dataset_rid='ri.catalog.cerulean-staging.dataset.45d0b732-6fb5-c6b8-510b-abfd79fdff22', name='RTR-02 telemetry.csv.gz', bounds=Bounds(start=1793983310000000000, end=1793983489900000000), uploaded_at=1793983911750759000, ingested_at=1793983913366529000, deleted_at=None, ingest_status=<IngestStatus.SUCCESS: 'SUCCESS'>, timestamp_channel='time', timestamp_type=Iso8601(), file_tags={}, tag_columns={})rig.search_events()[0]
Event(rid='ri.scout.cerulean-staging.event.68340669-ff75-fb55-9005-40bc0334c959', asset_rids=('ri.scout.cerulean-staging.asset.c2d499a7-2fcf-5fd4-2e04-b74688da409f',), name='Gearbox above 70 °C', description='Gearbox temperature passed the 70 °C limit late in the hold.', start=1793983418000000000, duration=72000000000, properties=mappingproxy({}), labels=['example'], type=<EventType.FLAG: 'FLAG'>, is_archived=False, disposition=None)