Runs in Nominal with Python

To use this guide, install the Nominal Python library with pip3 install nominal.

See Quickstart for more details.

Please contact us if you’re not sure whether your organization has access to Nominal.

A Run is Nominal’s primitive for test data that shares a common time domain. This guide details common patterns for working with Runs in Python.

Connect to Nominal

To instantiate a Run, first connect to your Nominal platform tenant.

Concepts
  • Base URL: The URL through which the Nominal API is accessed (typically https://api.gov.nominal.io/api; shown under Settings → API keys).
  • Workspace: A mechanism by which to isolate datasets; each user has one or more workspace, and data in one cannot be seen from another. Note that a token / API key is attached to a user, and may access multiple workspaces.
  • Profile: A combination of base URL, API key, and workspace.

There are two primary ways of authenticating the Nominal Client. The first is to use a profile stored on disk, and the second is to use a token directly.

Run the following in a terminal and follow on-screen prompts to set up a connection profile:

$$ nom config profile add default
>
># Alternatively, if `nom` is missing from the path:
>$ python -m nominal config profile add default

Here, “default” can be any name chosen to represent this profile (reminder: a profile represents a base URL, API key, and workspace).

The profile will be stored in ~/.config/nominal/config.yml, and can then be used to create a client:

1from nominal.core import NominalClient
2
3client = NominalClient.from_profile("default")
4
5# Get details about the currently logged-in user to validate authentication
6# Will display an object like: `User(display_name='your_email@your_company.com', ...)`
7print(client.get_user())

If you have previously used nom to store credentials, prior to the availability of profiles, you will need to migrate your old configuration file (~/.nominal.yml) to the new format (~/.config/nominal/config.yml).

You can do this with the following command:

$nom config migrate
>
># Or, if `nom` is missing from your path:
>python -m nominal config migrate
1from nominal.core import NominalClient
2
3# Get an instance of the client using provided credentials
4client = NominalClient.from_token("<insert api key>")
5
6# Get details about the currently logged-in user to validate authentication
7# Will display an object like: `User(display_name='your_email@your_company.com', ...)`
8print(client.get_user())

NOTE: you should never share your Nominal API key with anyone. We therefore recommend that you not save it in your code and/or scripts.

  • If you trust the computer you are on, use nom to store the credential to disk.
  • Otherwise, use a password manager such as 1password or bitwarden to keep your token safe.
If you’re not sure whether your company has a Nominal tenant, please reach out to us.

Create a Run

It’s possible to create an empty run without any data. Runs must have a start and end time expressed in absolute time. All Datasets added to the Run should overlap with this time domain.

1from nominal.core import NominalClient
2
3client = NominalClient.from_profile("default")
4
5flight_simulator_run = client.create_run(
6 name = 'High precipitation flight',
7 start = '2024-06-08T05:58:42Z',
8 end = '2024-06-08T06:00:06Z'
9)
10
11print("Created run:", flight_simulator_run.rid)

Create a Run off of an Asset

It’s also possible to create a run off of an existing asset:

1asset = client.create_asset('Aircraft', 'My flight simulator aircraft')
2
3flight_simulator_run = client.create_run(
4 name = 'High precipitation flight',
5 start = '2024-09-09T12:35:00Z',
6 end = '2024-09-09T13:18:00Z',
7 asset = asset.rid
8)
9
10print("Created run:", flight_simulator_run.rid)

Add data to a Run

To add a Dataset to a Run, use Run.add_dataset():

1import polars as pl
2
3df = pl.read_csv('hf://datasets/nominal-io/frosty-flight/frosty_flight_1k_rows.csv')
4df.write_csv('frosty_flight_1k_rows.csv')
5
6csv_dataset = client.create_dataset('Frosty Flight')
7csv_dataset.add_tabular_data(
8 'frosty_flight_1k_rows.csv',
9 timestamp_column = 'source_time',
10 timestamp_type = 'iso_8601',
11)
12
13flight_simulator_run.add_dataset(
14 dataset = csv_dataset,
15 ref_name = 'high-precipitation-flight'
16)

Datasets are the file representation of Nominal’s Data Source primitive. Most often, Datasets are tabular files with at least one time dimension. Datasets can also be video files.

Head over to the Datasets page to see your organization’s most recently uploaded Datasets.

Above, we set the start and end times of the Run manually. However, you can also set the Run to span the Dataset:

1csv_dataset.poll_until_ingestion_completed()
2
3# After ingestion, we can query the data bound timestamps
4bounds = csv_dataset.bounds
5
6# And update the run accordingly
7flight_simulator_run.update(
8 start=bounds.start,
9 end=bounds.end
10)

Run data with ref names

To add a Dataset to a Run along with a reference name, set the ref_name parameter in Run.add_dataset().

1flight_simulator_run.add_dataset(
2 dataset = csv_dataset,
3 ref_name = 'high-precipitation-flight'
4)

Ref names (reference names) are a namespace for data sources that share common channels, but do not necessarily belong to the same Run. They allow data sources with similar schema to be referenced as a group. For example, data sources with the same ref name can share Workbook templates and Checklists.

Check if a Run exists

You can check for a Run’s existence with client.search_runs().

1found_runs = client.search_runs(name_substring='High precipitation flight')
2
3if len(found_runs) == 0:
4 flight_simulator_run = nm.create_run(
5 name = 'High precipitation flight',
6 start = '2024-09-09T12:35:00Z',
7 end = '2024-09-09T13:18:00Z',
8 )

Update a Run

Run metadata can be updated with Run.update():

For example, to set a Run’s end time to the present moment:

1import datetime
2flight_simulator_run.update(end = datetime.datetime.now())

To update a Run’s title:

1flight_simulator_run.update(title = 'Low precipitation flight')

To add labels to a Run:

new_labels = ('high altitude', 'simulated')
old_labels = flight_simulator_run.labels
flight_simulator_run.update(labels=old_labels + new_labels)

Please see Run.update() for all updatable metadata.

Run attachments

File attachments such as PDF reports or PowerPoints can be added to Runs:

1filepath = '/path/to/my_report.pdf'
2with open(filepath, 'rb') as f:
3 attachment = client.create_attachment_from_io(
4 f,
5 name='KittyHawk.pdf',
6 )
7my_run.add_attachments(attachments = [attachment])

Retrieve a Run

Like Datasets, Runs can be retrieved by their resource ID (“RID”):

1run = client.get_run('ri.scout.cerulean-staging.run.22697726-5454-4fad-a3ea-fe45e9fa9f09')
2run.update(labels = ['X-PLANE'])

To retrieve a Run’s RID, visit its detail page and click on the clipboard icon next to “ID” in the right-hand drawer:

run-metadata

All Nominal primitives (eg Datasets, Runs, Workbooks, and Checks) have a unique identifier called a “Resource ID”. Resource IDs may be referred to as “RIDs”, or simply “IDs”, throughout the platform. They can be obtained from a primitive’s detail page (or URL) and have a format that looks like ri.catalog.cerulean-staging.dataset.e5ede17b-05f9-404d-aaf5-ba85c99761a2.

Query Runs

Runs can be queried with client.search_runs().

For example, to retrieve all runs with the label “X-PLANE”:

1x_plane_runs = client.search_runs(labels = ['X-PLANE'])

See client.search_runs() for all Run search parameters.

Remove Run Data Sources

The list data_sources can contain Connection, Dataset, Video instances, or rids as string.

1run = client.get_run('ri.scout.cerulean-staging.run.6c41d7fd-a48f-4d60-baa7-f34492264156')
2
3run.remove_data_sources(data_sources = ['ri.catalog.cerulean-staging.dataset.d4f413c4-4787-4259-a142-1286587b50af'])