How to write an app¶
Apps provide customized views of data in the GUI, making it easier for the users to navigate and understand the data related to a specific domain. This typically means that certain domain-specific properties are highlighted, different units may be used for physical properties, and specialized dashboards may be presented. This becomes crucial for NOMAD installations to be able to scale with data that contains a mixture of experiments and simulations, different techniques, and physical properties spanning different time and length scales.
Apps only affect the way data is displayed for the user: if you wish to affect the underlying data structure, you will need to write a Python schema package or a YAML schema package.
This documentation shows you how to write an plugin entry point for an app. You should read the documentation on getting started with plugins to have a basic understanding of how plugins and plugin entry points work in the NOMAD ecosystem.
Getting started¶
You can use our template repository to create an initial structure for a plugin containing an app. The relevant part of the repository layout will look something like this:
nomad-example
├── src
│ ├── nomad_example
│ │ ├── apps
│ │ │ ├── __init__.py
├── LICENSE.txt
├── README.md
└── pyproject.toml
See the documentation on plugin development guidelines for more details on the best development practices for plugins, including linting, testing and documenting.
App entry point¶
The entry point defines basic information about your app and is used to automatically load the app into a NOMAD distribution. It is an instance of an AppEntryPoint
and unlike many other plugin entry points, it does not have a separate resource that needs to be lazy-loaded as the entire app is defined in the configuration as an instance of nomad.config.models.ui.App
. You will learn more about the App
class in the next sections. The entry point should be defined in */apps/__init__.py
like this:
from nomad.config.models.plugins import AppEntryPoint
myapp = MyAppEntryPoint(
name = 'MyApp',
description = 'My custom app.',
app = App(...)
)
Here we have instantiated an object myapp
in which you specify the default parameterization and other details about the app. In the reference you can see all of the available configuration options for an AppEntryPoint
.
The entry point instance should then be added to the [project.entry-points.'nomad.plugin']
table in pyproject.toml
in order for the app to be automatically detected:
App
class¶
The definition fo the actual app is given as an instance of the App
class specified as part of the entry point. A full breakdown of the model is given below in the app reference, but here is a small example:
from nomad.config.models.plugins import AppEntryPoint
from nomad.config.models.ui import App, Column, FilterMenu, FilterMenus, Filters
schema = 'nomad_example.schema_packages.mypackage.MySchema'
myapp = AppEntryPoint(
name='MyApp',
description='App defined using the new plugin mechanism.',
app = App(
# Label of the App
label='My App',
# Path used in the URL, must be unique
path='myapp',
# Used to categorize apps in the explore menu
category='Theory',
# Brief description used in the app menu
description='An app customized for me.',
# Longer description that can also use markdown
readme='Here is a much longer description of this app.',
# Controls the available search filters. If you want to filter by
# quantities in a schema package, you need to load the schema package
# explicitly here. Note that you can use a glob syntax to load the
# entire package, or just a single schema from a package.
filters=Filters(
include=[f'*#{schema}'],
),
# Controls which columns are shown in the results table
columns=[
Column(quantity='entry_id', selected=True),
Column(
quantity=f'data.section.myquantity#{schema}',
selected=True
),
Column(
quantity=f'data.my_repeated_section[*].myquantity#{schema}',
selected=True
)
Column(quantity='upload_create_time')
],
# Dictionary of search filters that are always enabled for queries made
# within this app. This is especially important to narrow down the
# results to the wanted subset. Any available search filter can be
# targeted here. This example makes sure that only entries that use
# MySchema are included.
filters_locked={
"section_defs.definition_qualified_name:all": [schema]
},
# Controls the filter menus shown on the left
filter_menus=FilterMenus(
options={
'material': FilterMenu(label="Material"),
}
),
# Controls the default dashboard shown in the search interface
dashboard={
'widgets': [
{
'type': 'histogram',
'showinput': False,
'autorange': True,
'nbins': 30,
'scale': 'linear',
'quantity': f'data.mysection.myquantity#{schema}',
'layout': {
'lg': {
'minH': 3,
'minW': 3,
'h': 4,
'w': 12,
'y': 0,
'x': 0
}
}
}
]
}
)
)
Tip
If you want to load an app definition from a YAML file, this can be easily done with the pydantic parse_obj
function:
import yaml
from nomad.config.models.plugins import AppEntryPoint
from nomad.config.models.ui import App
yaml_data = """
label: My App
path: myapp
category: Theory
"""
myapp = AppEntryPoint(
name='MyApp',
description='App defined using the new plugin mechanism.',
app=App.parse_obj(
yaml.safe_load(yaml_data)
),
)
Loading custom quantity definitions into an app¶
By default, none of the quantities from custom schemas are available in an app, and they need to be explicitly added. Each app may define additional filters that should be enabled in it. Filters have a special meaning in the app context: filters are pieces of (meta)info that can be queried in the search interface of the app, but also targeted in the rest of the app configuration as explained below in.
Note
Note that not all of the quantities from a custom schema can be exposed as filters. At the moment we only support targeting scalar quantities from custom schemas.
Each schema has a unique name within the NOMAD ecosystem, which is needed to target them in the configuration. The name depends on the resource in which the schema is defined in:
- Python schemas are identified by the python path for the class that inherits
from
Schema
. For example, if you have a python package callednomad_example
, which has a subpackage calledschema_packages
, containing a module calledmypackage.py
, which contains the classMySchema
, then the schema name will benomad_example.schema_packages.mypackage.MySchema
. - YAML schemas are identified by the entry id of the schema file together with
the name of the section defined in the YAML schema. For example
if you have uploaded a schema YAML file containing a section definition called
MySchema
, and it has been assigned anentry_id
, the schema name will beentry_id:<entry_id>.MySchema
.
The quantities from schemas may be included or excluded as filter by using the
filters
field in the app config. This option supports a
wildcard/glob syntax for including/excluding certain filters. For example, to
include all filters from the Python schema defined in the class
nomad_example.schema_packages.mypackage.MySchema
, you could use:
The same thing for a YAML schema could be achieved with:
Once quantities from a schema are included in an app as filters, they can be targeted in the rest of the app. The app configuration often refers to specific filters to configure parts of the user interface. For example, one could configure the results table to show a new column using one of the schema quantities with:
columns=[
Column(quantity='entry_id', selected=True),
Column(
quantity='data.mysection.myquantity#nomad_example.schema_packages.mypackage.MySchema',
selected=True
),
Column(quantity='upload_create_time')
]
The syntax for targeting quantities depends on the resource:
- For python schemas, you need to provide the path and the python schema name separated
by a hashtag (#), for example
data.mysection.myquantity#nomad_example.schema_packages.mypackage.MySchema
. - For YAML schemas, you need to provide the path and the YAML schema name separated
by a hashtag (#), for example
data.mysection.myquantity#entry_id:<entry_id>.MySchema
. - Quantities that are common for all NOMAD entries can be targeted by using only
the path without the need for specifying a schema, e.g.
results.material.symmetry.space_group
.
App reference¶
App¶
Defines the layout and functionality for an App.
name | type | |
---|---|---|
label | str |
Name of the App. |
path | str |
Path used in the browser address bar. |
resource | str |
Targeted resource.default: entries options:- entries - materials |
breadcrumb | str |
Name displayed in the breadcrumb, by default the label will be used. |
category | str |
Category used to organize Apps in the explore menu. |
description | str |
Short description of the App. |
readme | str |
Longer description of the App that can also use markdown. |
pagination | Pagination |
Default result pagination.default: Complex object, default value not displayed. |
columns | List[Column] |
List of columns for the results table. |
rows | Rows |
Controls the display of entry rows in the results table.default: Complex object, default value not displayed. |
filter_menus | FilterMenus |
Filter menus displayed on the left side of the screen. |
filters | Filters |
Controls the filters that are available in this app.default: Complex object, default value not displayed. |
dashboard | Dashboard |
Default dashboard layout. |
filters_locked | dict |
Fixed query object that is applied for this search context. This filter will always be active for this context and will not be displayed to the user by default. |
search_syntaxes | SearchSyntaxes |
Controls which types of search syntax are available. |
Dashboard¶
Dashboard configuration.
name | type | |
---|---|---|
widgets | List[Union[WidgetTerms, WidgetHistogram, WidgetScatterPlot, WidgetPeriodicTable]] |
List of widgets contained in the dashboard. |
WidgetTerms¶
Terms widget configuration.
name | type | |
---|---|---|
title | str |
Custom widget title. If not specified, a widget-specific default title is used. |
type | str |
Set as terms to get this widget type.default: terms |
layout | Dict[str, Layout] |
Defines widget size and grid positioning for different breakpoints. The following breakpoints are supported: sm , md , lg , xl and xxl . |
quantity | str |
Targeted quantity. |
scale | str |
Statistics scaling.options: - linear - log - 1/2 - 1/4 - 1/8 |
showinput | int |
Whether to show text input field.default: True |
Layout¶
Defines widget size and grid positioning for different breakpoints.
name | type | |
---|---|---|
h | int |
Height in grid units |
w | int |
Width in grid units. |
x | int |
Horizontal start location in the grid. |
y | int |
Vertical start location in the grid. |
minH | int |
Minimum height in grid units.default: 3 |
minW | int |
Minimum width in grid units.default: 3 |
WidgetScatterPlot¶
Scatter plot widget configuration.
name | type | |
---|---|---|
title | str |
Custom widget title. If not specified, a widget-specific default title is used. |
type | str |
Set as scatterplot to get this widget type.default: scatterplot |
layout | Dict[str, Layout] |
Defines widget size and grid positioning for different breakpoints. The following breakpoints are supported: sm , md , lg , xl and xxl . |
x | Union[AxisLimitedScale, str] |
Configures the information source and display options for the x-axis. |
y | Union[AxisLimitedScale, str] |
Configures the information source and display options for the y-axis. |
markers | Markers |
Configures the information source and display options for the markers. |
color | str |
Quantity used for coloring points. Note that this field is deprecated and markers should be used instead. |
size | int |
Maximum number of entries to fetch. Notice that the actual number may be more of less, depending on how many entries exist and how many of the requested values each entry contains.default: 1000 |
autorange | int |
Whether to automatically set the range according to the data limits.default: True |
Markers¶
Configuration for plot markers.
name | type | |
---|---|---|
color | Axis |
Configures the information source and display options for the marker colors. |
Axis¶
Configuration for a plot axis with limited scaling options.
name | type | |
---|---|---|
title | str |
Custom title to show for the axis. |
unit | str |
Custom unit used for displaying the values. |
quantity | str |
Path of the targeted quantity. Note that you can most of the features JMESPath syntax here to further specify a selection of values. This becomes especially useful when dealing with repeated sections or statistical values. |
scale | str |
Defines the axis scaling. Defaults to linear scaling.default: ScaleEnum.LINEAR options:- linear - log - 1/2 - 1/4 - 1/8 |
AxisLimitedScale¶
Configuration for a plot axis with limited scaling options.
name | type | |
---|---|---|
title | str |
Custom title to show for the axis. |
unit | str |
Custom unit used for displaying the values. |
quantity | str |
Path of the targeted quantity. Note that you can most of the features JMESPath syntax here to further specify a selection of values. This becomes especially useful when dealing with repeated sections or statistical values. |
scale | str |
Defines the axis scaling. Defaults to linear scaling.default: ScaleEnumPlot.LINEAR options:- linear - log |
WidgetHistogram¶
Histogram widget configuration.
name | type | |
---|---|---|
title | str |
Custom widget title. If not specified, a widget-specific default title is used. |
type | str |
Set as histogram to get this widget type.default: histogram |
layout | Dict[str, Layout] |
Defines widget size and grid positioning for different breakpoints. The following breakpoints are supported: sm , md , lg , xl and xxl . |
quantity | str |
Targeted quantity. Note that this field is deprecated and x should be used instead. |
x | Union[Axis, str] |
Configures the information source and display options for the x-axis. |
y | Union[AxisScale, str] |
Configures the information source and display options for the y-axis. |
scale | str |
Statistics scaling.default: ScaleEnum.LINEAR options:- linear - log - 1/2 - 1/4 - 1/8 |
autorange | int |
Whether to automatically set the range according to the data limits.default: True |
showinput | int |
Whether to show input text fields for minimum and maximum value.default: True |
nbins | int |
Maximum number of histogram bins. Notice that the actual number of bins may be smaller if there are fewer data items available. |
AxisScale¶
Basic configuration for a plot axis.
name | type | |
---|---|---|
scale | str |
Defines the axis scaling. Defaults to linear scaling.default: ScaleEnum.LINEAR options:- linear - log - 1/2 - 1/4 - 1/8 |
WidgetPeriodicTable¶
Periodic table widget configuration.
name | type | |
---|---|---|
title | str |
Custom widget title. If not specified, a widget-specific default title is used. |
type | str |
Set as periodictable to get this widget type.default: periodictable |
layout | Dict[str, Layout] |
Defines widget size and grid positioning for different breakpoints. The following breakpoints are supported: sm , md , lg , xl and xxl . |
quantity | str |
Targeted quantity. |
scale | str |
Statistics scaling.options: - linear - log - 1/2 - 1/4 - 1/8 |
Filters¶
Controls the availability of filters in the app. Filters are pieces of
(meta)info than can be queried in the search interface of the app, but also
targeted in the rest of the app configuration. The include
and exlude
attributes can use glob syntax to target metainfo, e.g. results.*
or
*.#myschema.schema.MySchema
.
name | type | |
---|---|---|
include | List[str] |
List of included options. Supports glob/wildcard syntax. |
exclude | List[str] |
List of excluded options. Supports glob/wildcard syntax. Has higher precedence than include. |
SearchSyntaxes¶
Controls the availability of different search syntaxes. These syntaxes determine how raw user input in e.g. the search bar is parsed into queries supported by the API.
Currently you can only exclude items. By default, the following options are included:
existence
: Used to query for the existence of a specific metainfo field in the data.equality
: Used to query for a specific value with exact match.range_bounded
: Queries values that are between two numerical limits, inclusive or exclusive.range_half_bounded
: Queries values that are above/below a numerical limit, inclusive or exclusive.free_text
: For inexact, free-text queries. Requires that a set of keywords has been filled in the entry.
name | type | |
---|---|---|
exclude | List[str] |
List of excluded options. |
Pagination¶
name | type | |
---|---|---|
order_by | str |
Field used for sorting.default: upload_create_time |
order | str |
Sorting order.default: desc |
page_size | int |
Number of results on each page.default: 20 |
FilterMenus¶
Contains filter menu definitions and controls their availability.
name | type | |
---|---|---|
include | List[str] |
List of included options. If not explicitly defined, all of the options will be included by default. |
exclude | List[str] |
List of excluded options. Has higher precedence than include. |
options | Dict[str, FilterMenu] |
Contains the available filter menu options. |
FilterMenu¶
Defines the layout and functionality for a filter menu.
name | type | |
---|---|---|
label | str |
Menu label to show in the UI. |
level | int |
Indentation level of the menu.default: 0 |
size | str |
Width of the menu.default: FilterMenuSizeEnum.S options:- s - m - l - xl |
actions | FilterMenuActions |
FilterMenuActions¶
Contains filter menu action definitions and controls their availability.
name | type | |
---|---|---|
include | List[str] |
List of included options. If not explicitly defined, all of the options will be included by default. |
exclude | List[str] |
List of excluded options. Has higher precedence than include. |
options | Dict[str, FilterMenuActionCheckbox] |
Contains options for filter menu actions. |
FilterMenuActionCheckbox¶
Contains definition for checkbox action in the filter menu.
name | type | |
---|---|---|
type | str |
Action type.options: - checkbox |
label | str |
Label to show. |
quantity | str |
Targeted quantity |
Rows¶
Controls the visualization of rows in the search results.
name | type | |
---|---|---|
actions | RowActions |
|
details | RowDetails |
|
selection | RowSelection |
RowDetails¶
Controls the visualization of row details that are shown upon pressing the row and contain basic details about the entry.
name | type | |
---|---|---|
enabled | int |
Whether to show row details.default: True |
RowSelection¶
Controls the selection of rows. If enabled, rows can be selected and additional actions performed on them.
name | type | |
---|---|---|
enabled | int |
Whether to show the row selection.default: True |
RowActions¶
Controls the visualization of row actions that are shown at the end of each row.
name | type | |
---|---|---|
include | List[str] |
List of included options. If not explicitly defined, all of the options will be included by default. |
exclude | List[str] |
List of excluded options. Has higher precedence than include. |
options | Dict[str, RowActionURL] |
All available row actions. |
enabled | int |
Whether to enable row actions.default: True |
RowActionURL¶
Action that will open an external link read from the archive.
name | type | |
---|---|---|
description | str |
Description of the action shown to the user. |
type | str |
Set as url to get this widget type.default: url |
path | str |
JMESPath pointing to a path in the archive that contains the URL. |
Column¶
Column show in the search results table. With quantity
you may target a
specific part of the data to be shown. Note that the use of JMESPath is
supported here, and you can e.g. do the following:
- Show first value from a repeating subsection:
repeating_section[0].quantity
- Show slice of values from a repeating subsection:
repeating_section[1:2].quantity
- Show all values from a repeating subsection:
repeating_section[*].quantity
- Show minimum value from a repeating section:
min(repeating_section[*].quantity)
- Show instance that matches a criterion:
repeating_section[?label=='target'].quantity
name | type | |
---|---|---|
quantity | str |
Path of the targeted quantity. Note that you can most of the features JMESPath syntax here to further specify a selection of values. This becomes especially useful when dealing with repeated sections or statistical values. |
selected | int |
Is this column initially selected to be shown.default: False |
title | str |
Label shown in the header. Defaults to the quantity name. |
label | str |
Alias for title. |
align | str |
Alignment in the table.default: AlignEnum.LEFT options:- left - right - center |
unit | str |
Unit to convert to when displaying. If not given will be displayed in using the default unit in the active unit system. |
format | Format |
Controls the formatting of the values. |
Format¶
Value formatting options.
name | type | |
---|---|---|
decimals | int |
Number of decimals to show for numbers.default: 3 |
mode | str |
Display mode for numbers.default: ModeEnum.SCIENTIFIC options:- standard - scientific - separators - date - time |