From 55b0fb08d6efb61c72cf35fcb8a416576fd276f9 Mon Sep 17 00:00:00 2001
From: Markus Scheidgen <markus.scheidgen@gmail.com>
Date: Thu, 30 Mar 2023 15:43:32 +0200
Subject: [PATCH] Added documentation for schema and parser plugins.

Changelog: Added

Fixed typos and some language in the plugin docs.

Updated docs and scripts.
---
 .dockerignore                |    2 +
 .gitmodules                  |    6 +
 docs/develop/setup.md        |  163 ++--
 docs/plugins.md              |  111 +++
 examples/plugins/parser      |    1 +
 examples/plugins/schema      |    1 +
 gui/.env.development         |    1 +
 gui/public/env.js            | 1706 ----------------------------------
 mkdocs.yml                   |    1 +
 nomad/app/v1/routers/info.py |    4 +-
 nomad/cli/dev.py             |    6 +
 nomad/mkdocs.py              |    5 +
 scripts/setup_dev_env.sh     |    4 +-
 13 files changed, 234 insertions(+), 1777 deletions(-)
 create mode 100644 docs/plugins.md
 create mode 160000 examples/plugins/parser
 create mode 160000 examples/plugins/schema
 create mode 100644 gui/.env.development
 delete mode 100644 gui/public/env.js

diff --git a/.dockerignore b/.dockerignore
index c67c38adcc..75ef39777a 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -36,6 +36,8 @@ gui/public/env.js
 !gui/tests/nomad.yaml
 !ops/docker-compose/nomad-oasis/configs/nomad.yaml
 !ops/docker-compose/nomad-oasis-with-keycloak/configs/nomad.yaml
+!examples/plugins/schema/nomad.yaml
+!examples/plugins/parser/nomad.yaml
 
 # Ignore built gui and docs artufacts
 nomad/app/static/
diff --git a/.gitmodules b/.gitmodules
index 326af5de69..5a5e164f41 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -33,3 +33,9 @@
 [submodule "dependencies/matid"]
 	path = dependencies/matid
 	url = https://github.com/SINGROUP/matid.git
+[submodule "examples/plugins/schema"]
+	path = examples/plugins/schema
+	url = https://github.com/nomad-coe/nomad-schema-plugin-example.git
+[submodule "examples/plugins/parser"]
+	path = examples/plugins/parser
+	url = https://github.com/nomad-coe/nomad-parser-plugin-example.git
diff --git a/docs/develop/setup.md b/docs/develop/setup.md
index 1feaceb1f6..8a41105178 100644
--- a/docs/develop/setup.md
+++ b/docs/develop/setup.md
@@ -110,59 +110,63 @@ for Intel, and install and use an Intel based Python. The second answer in this
 describes how to use both the Apple and Intel homebrew simultaneously.
 
 ### Install nomad
-The following command can be used to install all dependencies of all submodules and nomad
-itself. If successful you can skip the rest of this *Install nomad* section.
+The following command can be used to install all dependencies of all submodules
+and nomad itself.
 ```
 ./scripts/setup_dev_env.sh
 ```
 
-Install all the requirements needed for development (including submodul requirements):
+??? note "Installation details"
 
-```sh
-pip install --prefer-binary -r requirements-dev.txt
-```
+    Here is more detailed rundown of the installation steps.
 
-Finally, you can add nomad to the environment itself (including all extras).
-The `-e` option will install the NOMAD with symbolic links allowing you
-to change the code without having to reinstall after each change.
-```sh
-pip install -e .[parsing,infrastructure,dev]
-```
+    First we ensure that all submodules are up-to-date:
 
-If pip tries to use and compile sources and this creates errors, it can be told to prefer binary version:
+    ```sh
+    git submodule update --init --recursive
+    ```
 
-```sh
-pip install -e .[parsing,infrastructure,dev] --prefer-binary
-```
+    Previous build is cleaned:
 
+    ```sh
+    rm -rf nomad/app/static/docs
+    rm -rf nomad/app/static/gui
+    rm -rf site
+    ```
 
-### Generate GUI artifacts
+    All the requirements needed for development (including submodule requirements) are installed:
 
-The NOMAD GUI requires two static javascript files (*gui artifacts*) that need to be generated
-from the NOMAD Python code:
-```sh
-python -m nomad.cli dev gui-artifacts > gui/public/artifacts.js
-python -m nomad.cli dev gui-config > gui/public/env.js
-```
+    ```sh
+    pip install --prefer-binary -r requirements-dev.txt
+    ```
 
-Or simply run
-```sh
-./scripts/generate_gui_artifacts.sh
-```
+    Next we install the `nomad` package itself (including all extras). The `-e`
+    option will install the NOMAD with symbolic links allowing you to change
+    the code without having to reinstall after each change.
+
+    ```sh
+    pip install -e .[parsing,infrastructure,dev]
+    ```
 
-If there are changes to the configuration (e.g. `nomad.yaml`), units, metainfo, new parsers,
-new toolkit notebooks it might be necessary to regenerate these gui artifacts.
+    If pip tries to use and compile sources and this creates errors, it can be told to prefer binary version:
 
-These generated files are not stored in GIT (ignore). A deployed NOMAD GUI will get
-these artifacts from the NOMAD backend, but the development GUI requires you to generate
-those files manually.
+    ```sh
+    pip install -e .[parsing,infrastructure,dev] --prefer-binary
+    ```
 
-A specifically configured smaller test version of these files is stored in GIT at `gui/test`
-to perform the automated GUI tests. The `generate_gui_artifacts.sh` script is
-updating both the GIT ignored ones and the test ones.
+    The NOMAD GUI requires a static .env file that can be generated with:
 
-In addition, you have to do some more steps to prepare your working copy to run all
-the tests. See below.
+    ```sh
+    python -m nomad.cli dev gui-env > gui/.env.development
+    ```
+
+    This file includes some of the server details that are needed so that the
+    GUI can make the initial connection properly. If you e.g. change the server
+    address in your NOMAD configuration file, it will be necessary to regenerate
+    this .env file. In production this file will be overridden.
+
+In addition, you have to do some more steps to prepare your working copy to run
+all the tests. See below.
 
 ## Run the infrastructure
 
@@ -220,15 +224,28 @@ via your preferred tools. Just make sure to use the right ports.
 
 ## Run NOMAD
 
+### nomad.yaml
+
 Before you run NOMAD for development purposes, you should configure it to use the `test`
 realm of our user management system. By default, NOMAD will use the `fairdi_nomad_prod` realm.
 Create a `nomad.yaml` file in the root folder:
 
-```
+```yaml
 keycloak:
   realm_name: fairdi_nomad_test
 ```
 
+You might also want to exclude some of the default plugins, or only include the plugins
+you'll need. Especially plugins that slower start-up and import times due to instantiation
+of large schemas (e.g. nexus create couple thousand definitions for 70+ applications) can
+often be excluded.
+
+```yaml
+plugins:
+  exclude:
+    - parsers/nexus
+```
+
 ### App and Worker
 NOMAD consist of the NOMAD app/api, a worker, and the GUI. You can run the app and the worker with
 the NOMAD cli. These commands will run the services and display their log output. You should open
@@ -369,6 +386,8 @@ of testing.
     to using an actual browser (does not support e.g. WebGL), but in practice
     is realistic enough for the majority of the test.
 
+#### Test structure
+
 We have adopted a `pytest`-like structure for organizing the test utilities:
 each source code folder may contain a `conftest.js` file that contains
 utilities that are relevant for testing the code in that particular folder.
@@ -449,44 +468,54 @@ def search():
     )
     data.save()
 ```
-When running in the `test-integration` or `test-record` mode (see below), this
-function will be executed in order to prepare the application backend. The
-`closeAPI` function will handle cleaning the test state between successive
-`startAPI` calls: it will completely wipe out MongoDB, ElasticSearch and the
-upload files.
 
-!!! note
+When running in the online mode (see below), this function will be executed in
+order to prepare the application backend. The `closeAPI` function will handle
+cleaning the test state between successive `startAPI` calls: it will completely
+wipe out MongoDB, ElasticSearch and the upload files.
 
-    The tests are using the configuration specified in `gui/tests/nomad.yaml`, that
-    specifies a separate database/filesystem config in order to prevent interacting with
-    any other instances of NOMAD.
+#### Running tests
+The tests can be run in two different modes. _Offline testing_ uses
+pre-recorded files to mock the API traffic during testing. This allows one to
+run tests more quickly without a server. During _online testing_, the tests
+perform calls to a running server where a test state has been prepared. This
+mode can be used to perform integration tests, but also to record the snapshot
+files needed by the offline testing.
 
-In order to control how the API traffic is handled, there are three main ways
-for running the test suite, as configured in `package.json`:
+##### Offline testing
+This is the way our CI pipeline runs the tests and should be used locally
+whenever you wish to e.g. reproduce pipeline errors or when your tests do not
+involve any API traffic.
 
- - `yarn test [<filename>]`: Runs the tests parallelly in an 'offline'
-   mode: `startAPI` will use pre-recorded API snapshot files that are found in
-   gui/tests.
- - `yarn test-integration filename>]`: Runs the tests serially and `startAPI`
-   will forward any API traffic to a live API that is running locally.
- - `yarn test-record [<filename>]`: Runs the tests serially and `startAPI` will
-   forward traffic to a live API that is running locally, additionally
-   recording the traffic to the specified snapshot file.
+1. Ensure that the gui artifacts are up-to-date:
+    ```sh
+    ./scripts/generate_gui_test_artifacts.sh
+    ```
+    As snapshot tests do not connect to the server, the artifacts cannot be
+    fetched dynamically from the server and static files need to be used
+    instead.
 
-!!! note
+2. Run `yarn test` to run the whole suite or `yarn test [<filename>]` to run a
+   specific test.
+
+##### Online testing
+When you wish to record API traffic for offline testing, or to perform
+integration tests, you will need to have a server running with the correct
+configuration. To do this, follow these steps:
+
+1. Have the docker infrastructure running: `docker compose up`
 
-    Before running against a live API (`yarn test-integration` and `yarn
-    test-record`), you need to boot up the infrastructure and ensure that the
-    nomad package is available with the correct test configuration:
+2. Have the `nomad appworker` running with the config found in
+    `gui/tests/nomad.yaml`. This can be achieved e.g. with the command: `export
+    NOMAD_CONFIG=gui/tests/nomad.yaml; nomad admin run appworker`
 
-    1. Have the docker infrastructure running: `docker compose up`
+3. Activate the correct python virtual environment before running the tests
+    with yarn (yarn will run the python functions that prepare the state).
 
-    2. Have the `nomad appworker` running with the config found in
-       `gui/tests/nomad.yaml`. This can be achieved e.g. with the command: `export
-       NOMAD_CONFIG=gui/tests/nomad.yaml; nomad admin run appworker`
+4. Run the tests with `yarn test-record [<filename>]` if you wish to record a
+   snapshot file or `yarn test-integration [<filename>]` if you want the
+   perform the test without any recording.
 
-    3. Activate the correct python virtual environment before running the tests
-       with yarn (yarn will run the python functions that prepare the state).
 
 ## Build the docker image
 
diff --git a/docs/plugins.md b/docs/plugins.md
new file mode 100644
index 0000000000..a0a6629730
--- /dev/null
+++ b/docs/plugins.md
@@ -0,0 +1,111 @@
+Plugins allow you to add Python-based functionality to NOMAD without a custom build
+NOMAD image or package. We support different kinds of plugins:
+
+- Python **schema**, read also [Python schema documentation](schema/python.md).
+- **parser**, read also [parser development documentation](develop/parser.md).
+- **normalizer** (coming soon...)
+- additional custom **APIs** (coming soon...)
+
+## Develop a plugin
+
+We provide template projects on GitHub. You can fork these projects and follow the
+instructions in their `README.md`. These instructions will give you everything you
+need to run your plugin as a plugin developer:
+
+- [schema plugin](https://github.com/nomad-coe/nomad-schema-plugin-example)
+- [parser plugin](https://github.com/nomad-coe/nomad-parser-plugin-example)
+
+### Plugin anatomy
+
+A plugin usually consist of the *plugin code* (a Python package) and
+*plugin metadata*. The installation independent *plugin metadata* (e.g. name, description, python package, etc.)
+can be defined in a `nomad_plugin.yaml` that is part of the *plugin code*.
+The installation dependent *plugin metadata* (e.g. plugin key, order and priority, parser matching rules, etc.)
+is added to the `nomad.yaml` of the NOMAD installation.
+
+Here is the project layout of the schema example:
+
+```
+my-nomad-schema
+├── nomadschemaexample
+│   ├── __init__.py
+│   ├── nomad_plugin.yaml
+│   └── schema.py
+├── tests
+│   ├── data
+│   │   └── test.archive.yaml
+│   └── test_schema.py
+├── LICENSE
+├── README.md
+├── nomad.yaml
+└── requirements.txt
+```
+
+### Plugin code
+
+The directory `nomadschemaexample` is our Python package *plugin code*. In this case,
+it contains a simple `schema.py`. Read the [Python schema documentation](schema/python.md)
+for more details:
+
+```python
+{{ file_contents('examples/plugins/schema/nomadschemaexample/schema.py') }}
+```
+
+### Plugin metadata
+
+The file `nomad_plugin.yaml` contains the installation independent plugin metadata:
+
+```yaml
+{{ file_contents('examples/plugins/schema/nomadschemaexample/nomad_plugin.yaml') }}
+```
+
+The metadata contains the `plugin_type` (e.g. `schema` or `parser`). The rest of the
+yaml will depend on the type and the underlying metadata model. For schemas there are only
+descriptive metadata like `name` or `description` as schemas do not contain any technical
+metadata that is necessary to use them. See below for a reference of the *plugin metadata*
+models.
+
+The file `nomad.yaml` shows how to add the plugin to a nomad installation. As a plugin
+developer you have [installed our Python package](./pythonlib.md) and can run the `nomad parse`
+command as your "installation" to try your schema:
+
+```yaml
+{{ file_contents('examples/plugins/schema/nomad.yaml') }}
+```
+
+Plugins are defined under the `plugins` key. This consists of `include` (or `exclude`) to
+select a subset of all plugins defined under `options`. The `options` given in the
+`nomad.yaml` will be merged with all the default "plugins" that come with NOMAD (mostly the NOMAD parsers at the moment).
+
+In this example, we disable all default plugins by just including our `schemas/example`.
+The `options` field can be used to add define the *code* and installation independent *metadata*
+via the `python_package` key. But you can overwrite or add more *metadata* keys here as well:
+each `options` entry with `python_package` will be merged with the data in the package's
+`nomad_plugin.yaml`.
+
+Please note that `python_package` is the name of a Python package and not a path to the
+code. This also means that the package has to be in your `PYTHONPATH` (see below).
+
+Now follow the instructions for one of our examples and try for yourself:
+
+- [schema plugin](https://github.com/nomad-coe/nomad-schema-plugin-example)
+- [parser plugin](https://github.com/nomad-coe/nomad-parser-plugin-example)
+
+## Add a plugin to your NOMAD (Oasis)
+
+To add a plugin, you need to add the *plugin metadata* to `nomad.yaml` and you need
+to add the *plugin code* to the `PYTHONPATH` of your NOMAD. The `nomad.yaml` needs to be
+edited manually in the usual way. There are several ways to
+add *plugin code* to a NOMAD installation:
+
+- adding it to the `PYTHONPATH`, if you run nomad directly, e.g. as a developer
+- mounting plugin sources into NOMAD (Oasis) containers, e.g. as an Oasis admin (coming soon...)
+- via python packages (coming soon...)
+- via github projects (coming soon...)
+
+## Publish a plugin
+
+coming soon...
+
+We plan to provide a plugin registry that allows you to publish your plugin's *metadata*.
+This can then be used to simplify plugin management within a NOMAD installation.
\ No newline at end of file
diff --git a/examples/plugins/parser b/examples/plugins/parser
new file mode 160000
index 0000000000..5059c52882
--- /dev/null
+++ b/examples/plugins/parser
@@ -0,0 +1 @@
+Subproject commit 5059c528828875b94210f7130eed0670372ae46f
diff --git a/examples/plugins/schema b/examples/plugins/schema
new file mode 160000
index 0000000000..b6642e43b7
--- /dev/null
+++ b/examples/plugins/schema
@@ -0,0 +1 @@
+Subproject commit b6642e43b768a01aa5ded08af56980e144d204c1
diff --git a/gui/.env.development b/gui/.env.development
new file mode 100644
index 0000000000..796f4a6dd7
--- /dev/null
+++ b/gui/.env.development
@@ -0,0 +1 @@
+REACT_APP_BACKEND_URL=http://localhost:8000/fairdi/nomad/latest
diff --git a/gui/public/env.js b/gui/public/env.js
deleted file mode 100644
index 5b7b471502..0000000000
--- a/gui/public/env.js
+++ /dev/null
@@ -1,1706 +0,0 @@
-window.nomadEnv = {
-  "appBase": "http://localhost:8000/fairdi/nomad/latest",
-  "northBase": "http://localhost:9000/fairdi/nomad/latest/north",
-  "keycloakBase": "https://nomad-lab.eu/fairdi/keycloak/auth/",
-  "keycloakRealm": "fairdi_nomad_test",
-  "keycloakClientId": "nomad_public",
-  "debug": false,
-  "encyclopediaBase": "https://nomad-lab.eu/prod/rae/encyclopedia/#",
-  "aitoolkitEnabled": false,
-  "oasis": false,
-  "version": {},
-  "globalLoginRequired": false,
-  "servicesUploadLimit": 10,
-  "ui": {
-    "theme": {
-      "title": "NOMAD"
-    },
-    "unit_systems": {
-      "selected": "Custom"
-    },
-    "entry": {
-      "cards": {
-        "exclude": [
-          "relatedResources"
-        ],
-        "options": {
-          "sections": {
-            "error": "Could not render section card."
-          },
-          "definitions": {
-            "error": "Could not render definitions card."
-          },
-          "nexus": {
-            "error": "Could not render NeXus card."
-          },
-          "material": {
-            "error": "Could not render material card."
-          },
-          "solarcell": {
-            "error": "Could not render solar cell properties."
-          },
-          "electronic": {
-            "error": "Could not render electronic properties."
-          },
-          "vibrational": {
-            "error": "Could not render vibrational properties."
-          },
-          "mechanical": {
-            "error": "Could not render mechanical properties."
-          },
-          "thermodynamic": {
-            "error": "Could not render thermodynamic properties."
-          },
-          "structural": {
-            "error": "Could not render structural properties."
-          },
-          "dynamical": {
-            "error": "Could not render dynamical properties."
-          },
-          "geometry_optimization": {
-            "error": "Could not render geometry optimization."
-          },
-          "eels": {
-            "error": "Could not render EELS properties."
-          },
-          "workflow": {
-            "error": "Could not render workflow card."
-          },
-          "references": {
-            "error": "Could not render references card."
-          },
-          "relatedResources": {
-            "error": "Could not render related resources card."
-          }
-        }
-      }
-    },
-    "apps": {
-      "options": {
-        "entries": {
-          "label": "Entries",
-          "path": "entries",
-          "resource": "entries",
-          "breadcrumb": "Entries",
-          "category": "All",
-          "description": "Search entries across all domains",
-          "help": {
-            "title": "Entries search",
-            "content": "This page allows you to search **entries** within NOMAD.\nEntries represent any individual data items that have\nbeen uploaded to NOMAD, no matter whether they come from\ntheoretical calculations, experiments, lab notebooks or\nany other source of data. This allows you to perform\ncross-domain queries, but if you are interested in a\nspecific subfield, you should see if a specific\napplication exists for it in the explore menu to get\nmore details."
-          },
-          "pagination": {
-            "order_by": "upload_create_time",
-            "order": "desc",
-            "page_size": 20
-          },
-          "columns": {
-            "options": {
-              "entry_name": {
-                "label": "Name",
-                "align": "left"
-              },
-              "results.material.chemical_formula_hill": {
-                "label": "Formula",
-                "align": "left"
-              },
-              "entry_type": {
-                "label": "Entry type",
-                "align": "left"
-              },
-              "upload_name": {
-                "label": "Upload name",
-                "align": "left"
-              },
-              "upload_id": {
-                "label": "Upload id",
-                "align": "left"
-              },
-              "upload_create_time": {
-                "label": "Upload time",
-                "align": "left"
-              },
-              "authors": {
-                "label": "Authors",
-                "align": "left"
-              },
-              "results.method.method_name": {
-                "label": "Method name",
-                "align": "left"
-              },
-              "results.method.simulation.program_name": {
-                "label": "Program name",
-                "align": "left"
-              },
-              "results.method.simulation.dft.basis_set_name": {
-                "label": "Basis set name",
-                "align": "left"
-              },
-              "results.method.simulation.dft.xc_functional_type": {
-                "label": "XC Functional Type",
-                "align": "left"
-              },
-              "results.material.structural_type": {
-                "label": "Dimensionality",
-                "align": "left"
-              },
-              "results.material.symmetry.crystal_system": {
-                "label": "Crystal system",
-                "align": "left"
-              },
-              "results.material.symmetry.space_group_symbol": {
-                "label": "Space group symbol",
-                "align": "left"
-              },
-              "results.material.symmetry.space_group_number": {
-                "label": "Space group number",
-                "align": "left"
-              },
-              "results.eln.lab_ids": {
-                "label": "Lab IDs",
-                "align": "left"
-              },
-              "results.eln.sections": {
-                "label": "Sections",
-                "align": "left"
-              },
-              "results.eln.methods": {
-                "label": "Methods",
-                "align": "left"
-              },
-              "results.eln.tags": {
-                "label": "Tags",
-                "align": "left"
-              },
-              "results.eln.instruments": {
-                "label": "Instruments",
-                "align": "left"
-              },
-              "mainfile": {
-                "label": "Mainfile",
-                "align": "left"
-              },
-              "comment": {
-                "label": "Comment",
-                "align": "left"
-              },
-              "references": {
-                "label": "References",
-                "align": "left"
-              },
-              "datasets": {
-                "label": "Datasets",
-                "align": "left"
-              },
-              "published": {
-                "label": "Access",
-                "align": "left"
-              }
-            },
-            "selected": [
-              "entry_name",
-              "results.material.chemical_formula_hill",
-              "entry_type",
-              "upload_create_time",
-              "authors"
-            ]
-          },
-          "rows": {
-            "actions": {
-              "enabled": true
-            },
-            "details": {
-              "enabled": true
-            },
-            "selection": {
-              "enabled": true
-            }
-          },
-          "filter_menus": {
-            "options": {
-              "material": {
-                "label": "Material",
-                "level": 0,
-                "size": "s"
-              },
-              "elements": {
-                "label": "Elements / Formula",
-                "level": 1,
-                "size": "xl"
-              },
-              "structure": {
-                "label": "Structure",
-                "level": 1,
-                "size": "s"
-              },
-              "method": {
-                "label": "Method",
-                "level": 0,
-                "size": "s"
-              },
-              "dft": {
-                "label": "DFT",
-                "level": 1,
-                "size": "s"
-              },
-              "gw": {
-                "label": "GW",
-                "level": 1,
-                "size": "s"
-              },
-              "projection": {
-                "label": "Projection",
-                "level": 1,
-                "size": "s"
-              },
-              "dmft": {
-                "label": "DMFT",
-                "level": 1,
-                "size": "s"
-              },
-              "eels": {
-                "label": "EELS",
-                "level": 1,
-                "size": "s"
-              },
-              "workflow": {
-                "label": "Workflow",
-                "level": 0,
-                "size": "s"
-              },
-              "molecular_dynamics": {
-                "label": "Molecular dynamics",
-                "level": 1,
-                "size": "s"
-              },
-              "geometry_optimization": {
-                "label": "Geometry Optimization",
-                "level": 1,
-                "size": "s"
-              },
-              "properties": {
-                "label": "Properties",
-                "level": 0,
-                "size": "s"
-              },
-              "electronic": {
-                "label": "Electronic",
-                "level": 1,
-                "size": "s"
-              },
-              "vibrational": {
-                "label": "Vibrational",
-                "level": 1,
-                "size": "s"
-              },
-              "mechanical": {
-                "label": "Mechanical",
-                "level": 1,
-                "size": "s"
-              },
-              "usecases": {
-                "label": "Use Cases",
-                "level": 0,
-                "size": "s"
-              },
-              "solarcell": {
-                "label": "Solar Cells",
-                "level": 1,
-                "size": "s"
-              },
-              "author": {
-                "label": "Author / Origin / Dataset",
-                "level": 0,
-                "size": "m"
-              },
-              "metadata": {
-                "label": "Visibility / IDs / Schema",
-                "level": 0,
-                "size": "s"
-              },
-              "optimade": {
-                "label": "Optimade",
-                "level": 0,
-                "size": "m"
-              }
-            }
-          },
-          "filters": {
-            "exclude": [
-              "mainfile",
-              "entry_name",
-              "combine"
-            ]
-          }
-        },
-        "calculations": {
-          "label": "Calculations",
-          "path": "calculations",
-          "resource": "entries",
-          "breadcrumb": "Calculations",
-          "category": "Theory",
-          "description": "Search calculations",
-          "help": {
-            "title": "Calculations",
-            "content": "This page allows you to search **calculations** within\nNOMAD.  Calculations typically come from a specific\nsimulation software that uses an approximate model to\ninvestigate and report different physical properties."
-          },
-          "pagination": {
-            "order_by": "upload_create_time",
-            "order": "desc",
-            "page_size": 20
-          },
-          "columns": {
-            "options": {
-              "results.material.chemical_formula_hill": {
-                "label": "Formula",
-                "align": "left"
-              },
-              "results.method.simulation.program_name": {
-                "label": "Program name",
-                "align": "left"
-              },
-              "results.method.method_name": {
-                "label": "Method name",
-                "align": "left"
-              },
-              "upload_create_time": {
-                "label": "Upload time",
-                "align": "left"
-              },
-              "authors": {
-                "label": "Authors",
-                "align": "left"
-              },
-              "results.method.simulation.dft.basis_set_name": {
-                "label": "Basis set name",
-                "align": "left"
-              },
-              "results.method.simulation.dft.xc_functional_type": {
-                "label": "XC Functional Type",
-                "align": "left"
-              },
-              "results.material.structural_type": {
-                "label": "Dimensionality",
-                "align": "left"
-              },
-              "results.material.symmetry.crystal_system": {
-                "label": "Crystal system",
-                "align": "left"
-              },
-              "results.material.symmetry.space_group_symbol": {
-                "label": "Space group symbol",
-                "align": "left"
-              },
-              "results.material.symmetry.space_group_number": {
-                "label": "Space group number",
-                "align": "left"
-              },
-              "results.eln.lab_ids": {
-                "label": "Lab IDs",
-                "align": "left"
-              },
-              "results.eln.sections": {
-                "label": "Sections",
-                "align": "left"
-              },
-              "results.eln.methods": {
-                "label": "Methods",
-                "align": "left"
-              },
-              "results.eln.tags": {
-                "label": "Tags",
-                "align": "left"
-              },
-              "results.eln.instruments": {
-                "label": "Instruments",
-                "align": "left"
-              },
-              "entry_name": {
-                "label": "Name",
-                "align": "left"
-              },
-              "entry_type": {
-                "label": "Entry type",
-                "align": "left"
-              },
-              "mainfile": {
-                "label": "Mainfile",
-                "align": "left"
-              },
-              "comment": {
-                "label": "Comment",
-                "align": "left"
-              },
-              "references": {
-                "label": "References",
-                "align": "left"
-              },
-              "datasets": {
-                "label": "Datasets",
-                "align": "left"
-              },
-              "published": {
-                "label": "Access",
-                "align": "left"
-              }
-            },
-            "selected": [
-              "results.material.chemical_formula_hill",
-              "results.method.simulation.program_name",
-              "results.method.method_name",
-              "upload_create_time",
-              "authors"
-            ]
-          },
-          "rows": {
-            "actions": {
-              "enabled": true
-            },
-            "details": {
-              "enabled": true
-            },
-            "selection": {
-              "enabled": true
-            }
-          },
-          "filter_menus": {
-            "options": {
-              "material": {
-                "label": "Material",
-                "level": 0,
-                "size": "s"
-              },
-              "elements": {
-                "label": "Elements / Formula",
-                "level": 1,
-                "size": "xl"
-              },
-              "structure": {
-                "label": "Structure",
-                "level": 1,
-                "size": "s"
-              },
-              "method": {
-                "label": "Method",
-                "level": 0,
-                "size": "s"
-              },
-              "dft": {
-                "label": "DFT",
-                "level": 1,
-                "size": "s"
-              },
-              "gw": {
-                "label": "GW",
-                "level": 1,
-                "size": "s"
-              },
-              "projection": {
-                "label": "Projection",
-                "level": 1,
-                "size": "s"
-              },
-              "dmft": {
-                "label": "DMFT",
-                "level": 1,
-                "size": "s"
-              },
-              "workflow": {
-                "label": "Workflow",
-                "level": 0,
-                "size": "s"
-              },
-              "molecular_dynamics": {
-                "label": "Molecular dynamics",
-                "level": 1,
-                "size": "s"
-              },
-              "geometry_optimization": {
-                "label": "Geometry Optimization",
-                "level": 1,
-                "size": "s"
-              },
-              "properties": {
-                "label": "Properties",
-                "level": 0,
-                "size": "s"
-              },
-              "electronic": {
-                "label": "Electronic",
-                "level": 1,
-                "size": "s"
-              },
-              "vibrational": {
-                "label": "Vibrational",
-                "level": 1,
-                "size": "s"
-              },
-              "mechanical": {
-                "label": "Mechanical",
-                "level": 1,
-                "size": "s"
-              },
-              "author": {
-                "label": "Author / Origin / Dataset",
-                "level": 0,
-                "size": "m"
-              },
-              "metadata": {
-                "label": "Visibility / IDs / Schema",
-                "level": 0,
-                "size": "s"
-              },
-              "optimade": {
-                "label": "Optimade",
-                "level": 0,
-                "size": "m"
-              }
-            }
-          },
-          "filters": {
-            "exclude": [
-              "mainfile",
-              "entry_name",
-              "combine"
-            ]
-          },
-          "filters_locked": {
-            "quantities": "results.method.simulation.program_name"
-          }
-        },
-        "materials": {
-          "label": "Materials",
-          "path": "materials",
-          "resource": "materials",
-          "breadcrumb": "Materials",
-          "category": "Theory",
-          "description": "Search materials that are identified from calculations",
-          "help": {
-            "title": "Materials",
-            "content": "This page allows you to search **materials** within\nNOMAD. NOMAD can often automatically detect the material\nfrom individual calculations that contain the full\natomistic structure and can then group the data by using\nthese detected materials. This allows you to search\nindividual materials which have properties that are\naggregated from several entries. Following the link for\na specific material will take you to the corresponding\n[NOMAD Encyclopedia](https://nomad-lab.eu/prod/rae/encyclopedia/#/search)\npage for that material. NOMAD Encyclopedia is a service\nthat is specifically oriented towards materials property\nexploration.\n\nNotice that by default the properties that you search\ncan be combined from several different entries. If\ninstead you wish to search for a material with an\nindividual entry fullfilling your search criteria,\nuncheck the **combine results from several\nentries**-checkbox."
-          },
-          "pagination": {
-            "order_by": "chemical_formula_hill",
-            "order": "asc",
-            "page_size": 20
-          },
-          "columns": {
-            "options": {
-              "chemical_formula_hill": {
-                "label": "Formula",
-                "align": "left"
-              },
-              "structural_type": {
-                "label": "Dimensionality",
-                "align": "left"
-              },
-              "symmetry.structure_name": {
-                "label": "Structure name",
-                "align": "left"
-              },
-              "symmetry.space_group_number": {
-                "label": "Space group number",
-                "align": "left"
-              },
-              "symmetry.crystal_system": {
-                "label": "Crystal system",
-                "align": "left"
-              },
-              "symmetry.space_group_symbol": {
-                "label": "Space group symbol",
-                "align": "left"
-              },
-              "material_id": {
-                "label": "Material ID",
-                "align": "left"
-              }
-            },
-            "selected": [
-              "chemical_formula_hill",
-              "structural_type",
-              "symmetry.structure_name",
-              "symmetry.space_group_number",
-              "symmetry.crystal_system"
-            ]
-          },
-          "rows": {
-            "actions": {
-              "enabled": true
-            },
-            "details": {
-              "enabled": false
-            },
-            "selection": {
-              "enabled": false
-            }
-          },
-          "filter_menus": {
-            "options": {
-              "material": {
-                "label": "Material",
-                "level": 0,
-                "size": "s"
-              },
-              "elements": {
-                "label": "Elements / Formula",
-                "level": 1,
-                "size": "xl"
-              },
-              "structure": {
-                "label": "Structure",
-                "level": 1,
-                "size": "s"
-              },
-              "method": {
-                "label": "Method",
-                "level": 0,
-                "size": "s"
-              },
-              "dft": {
-                "label": "DFT",
-                "level": 1,
-                "size": "s"
-              },
-              "gw": {
-                "label": "GW",
-                "level": 1,
-                "size": "s"
-              },
-              "projection": {
-                "label": "Projection",
-                "level": 1,
-                "size": "s"
-              },
-              "dmft": {
-                "label": "DMFT",
-                "level": 1,
-                "size": "s"
-              },
-              "workflow": {
-                "label": "Workflow",
-                "level": 0,
-                "size": "s"
-              },
-              "molecular_dynamics": {
-                "label": "Molecular dynamics",
-                "level": 1,
-                "size": "s"
-              },
-              "geometry_optimization": {
-                "label": "Geometry Optimization",
-                "level": 1,
-                "size": "s"
-              },
-              "properties": {
-                "label": "Properties",
-                "level": 0,
-                "size": "s"
-              },
-              "electronic": {
-                "label": "Electronic",
-                "level": 1,
-                "size": "s"
-              },
-              "vibrational": {
-                "label": "Vibrational",
-                "level": 1,
-                "size": "s"
-              },
-              "mechanical": {
-                "label": "Mechanical",
-                "level": 1,
-                "size": "s"
-              },
-              "author": {
-                "label": "Author / Origin / Dataset",
-                "level": 0,
-                "size": "m"
-              },
-              "metadata": {
-                "label": "Visibility / IDs / Schema",
-                "level": 0,
-                "size": "s"
-              },
-              "optimade": {
-                "label": "Optimade",
-                "level": 0,
-                "size": "m"
-              },
-              "combine": {
-                "level": 0,
-                "size": "s",
-                "actions": {
-                  "options": {
-                    "combine": {
-                      "type": "checkbox",
-                      "label": "Combine results from several entries",
-                      "quantity": "combine"
-                    }
-                  }
-                }
-              }
-            }
-          },
-          "filters": {
-            "exclude": [
-              "mainfile",
-              "entry_name"
-            ]
-          }
-        },
-        "eln": {
-          "label": "ELN",
-          "path": "eln",
-          "resource": "entries",
-          "breadcrumb": "ELN",
-          "category": "Experiment",
-          "description": "Search electronic lab notebooks",
-          "help": {
-            "title": "ELN search",
-            "content": "This page allows you to specifically seach **electronic\nlab notebooks (ELNs)** within NOMAD.  It is very similar\nto the entries search, but with a reduced filter set and\nspecialized arrangement of default columns."
-          },
-          "pagination": {
-            "order_by": "upload_create_time",
-            "order": "desc",
-            "page_size": 20
-          },
-          "columns": {
-            "options": {
-              "entry_name": {
-                "label": "Name",
-                "align": "left"
-              },
-              "entry_type": {
-                "label": "Entry type",
-                "align": "left"
-              },
-              "upload_create_time": {
-                "label": "Upload time",
-                "align": "left"
-              },
-              "authors": {
-                "label": "Authors",
-                "align": "left"
-              },
-              "results.material.chemical_formula_hill": {
-                "label": "Formula",
-                "align": "left"
-              },
-              "results.method.method_name": {
-                "label": "Method name",
-                "align": "left"
-              },
-              "results.eln.lab_ids": {
-                "label": "Lab IDs",
-                "align": "left"
-              },
-              "results.eln.sections": {
-                "label": "Sections",
-                "align": "left"
-              },
-              "results.eln.methods": {
-                "label": "Methods",
-                "align": "left"
-              },
-              "results.eln.tags": {
-                "label": "Tags",
-                "align": "left"
-              },
-              "results.eln.instruments": {
-                "label": "Instruments",
-                "align": "left"
-              },
-              "mainfile": {
-                "label": "Mainfile",
-                "align": "left"
-              },
-              "comment": {
-                "label": "Comment",
-                "align": "left"
-              },
-              "references": {
-                "label": "References",
-                "align": "left"
-              },
-              "datasets": {
-                "label": "Datasets",
-                "align": "left"
-              },
-              "published": {
-                "label": "Access",
-                "align": "left"
-              }
-            },
-            "selected": [
-              "entry_name",
-              "entry_type",
-              "upload_create_time",
-              "authors"
-            ]
-          },
-          "rows": {
-            "actions": {
-              "enabled": true
-            },
-            "details": {
-              "enabled": true
-            },
-            "selection": {
-              "enabled": true
-            }
-          },
-          "filter_menus": {
-            "options": {
-              "material": {
-                "label": "Material",
-                "level": 0,
-                "size": "s"
-              },
-              "elements": {
-                "label": "Elements / Formula",
-                "level": 1,
-                "size": "xl"
-              },
-              "eln": {
-                "label": "Electronic Lab Notebook",
-                "level": 0,
-                "size": "s"
-              },
-              "custom_quantities": {
-                "label": "User Defined Quantities",
-                "level": 0,
-                "size": "l"
-              },
-              "author": {
-                "label": "Author / Origin / Dataset",
-                "level": 0,
-                "size": "m"
-              },
-              "metadata": {
-                "label": "Visibility / IDs / Schema",
-                "level": 0,
-                "size": "s"
-              },
-              "optimade": {
-                "label": "Optimade",
-                "level": 0,
-                "size": "m"
-              }
-            }
-          },
-          "filters": {
-            "exclude": [
-              "mainfile",
-              "entry_name",
-              "combine"
-            ]
-          },
-          "filters_locked": {
-            "quantities": "data"
-          }
-        },
-        "eels": {
-          "label": "EELS",
-          "path": "eels",
-          "resource": "entries",
-          "breadcrumb": "EELS",
-          "category": "Experiment",
-          "description": "Search electron energy loss spectroscopy experiments",
-          "help": {
-            "title": "EELS",
-            "content": "This page allows you to spefically search **Electron\nEnergy Loss Spectroscopy (EELS) experiments** within\nNOMAD. It is similar to the entries search, but with a\nreduced filter set and specialized arrangement of\ndefault columns."
-          },
-          "pagination": {
-            "order_by": "upload_create_time",
-            "order": "desc",
-            "page_size": 20
-          },
-          "columns": {
-            "options": {
-              "results.material.chemical_formula_hill": {
-                "label": "Formula",
-                "align": "left"
-              },
-              "results.properties.spectroscopy.eels.detector_type": {
-                "label": "Detector type",
-                "align": "left"
-              },
-              "results.properties.spectroscopy.eels.resolution": {
-                "label": "Resolution",
-                "align": "left"
-              },
-              "upload_create_time": {
-                "label": "Upload time",
-                "align": "left"
-              },
-              "authors": {
-                "label": "Authors",
-                "align": "left"
-              },
-              "results.properties.spectroscopy.eels.min_energy": {
-                "align": "left"
-              },
-              "results.properties.spectroscopy.eels.max_energy": {
-                "align": "left"
-              },
-              "entry_name": {
-                "label": "Name",
-                "align": "left"
-              },
-              "entry_type": {
-                "label": "Entry type",
-                "align": "left"
-              },
-              "mainfile": {
-                "label": "Mainfile",
-                "align": "left"
-              },
-              "comment": {
-                "label": "Comment",
-                "align": "left"
-              },
-              "references": {
-                "label": "References",
-                "align": "left"
-              },
-              "datasets": {
-                "label": "Datasets",
-                "align": "left"
-              },
-              "published": {
-                "label": "Access",
-                "align": "left"
-              }
-            },
-            "selected": [
-              "results.material.chemical_formula_hill",
-              "results.properties.spectroscopy.eels.detector_type",
-              "results.properties.spectroscopy.eels.resolution",
-              "upload_create_time",
-              "authors"
-            ]
-          },
-          "rows": {
-            "actions": {
-              "enabled": true
-            },
-            "details": {
-              "enabled": true
-            },
-            "selection": {
-              "enabled": true
-            }
-          },
-          "filter_menus": {
-            "options": {
-              "material": {
-                "label": "Material",
-                "level": 0,
-                "size": "s"
-              },
-              "elements": {
-                "label": "Elements / Formula",
-                "level": 1,
-                "size": "xl"
-              },
-              "method": {
-                "label": "Method",
-                "level": 0,
-                "size": "s"
-              },
-              "eels": {
-                "label": "EELS",
-                "level": 1,
-                "size": "s"
-              },
-              "author": {
-                "label": "Author / Origin / Dataset",
-                "level": 0,
-                "size": "m"
-              },
-              "metadata": {
-                "label": "Visibility / IDs / Schema",
-                "level": 0,
-                "size": "s"
-              },
-              "optimade": {
-                "label": "Optimade",
-                "level": 0,
-                "size": "m"
-              }
-            }
-          },
-          "filters": {
-            "exclude": [
-              "mainfile",
-              "entry_name",
-              "combine"
-            ]
-          },
-          "filters_locked": {
-            "results.method.method_name": "EELS"
-          }
-        },
-        "solarcells": {
-          "label": "Solar Cells",
-          "path": "solarcells",
-          "resource": "entries",
-          "breadcrumb": "Solar Cells",
-          "category": "Use Cases",
-          "description": "Search solar cells",
-          "help": {
-            "title": "Solar cells",
-            "content": "This page allows you to search **solar cell data**\nwithin NOMAD. The filter menu on the left and the shown\ndefault columns are specifically designed for solar cell\nexploration. The dashboard directly shows useful\ninteractive statistics about the data."
-          },
-          "pagination": {
-            "order_by": "results.properties.optoelectronic.solar_cell.efficiency",
-            "order": "desc",
-            "page_size": 20
-          },
-          "columns": {
-            "options": {
-              "results.material.chemical_formula_descriptive": {
-                "label": "Descriptive Formula",
-                "align": "left"
-              },
-              "results.properties.optoelectronic.solar_cell.efficiency": {
-                "label": "Efficiency (%)",
-                "align": "left",
-                "format": {
-                  "decimals": 2,
-                  "mode": "standard"
-                }
-              },
-              "results.properties.optoelectronic.solar_cell.open_circuit_voltage": {
-                "label": "Open circuit voltage",
-                "align": "left",
-                "unit": "V",
-                "format": {
-                  "decimals": 3,
-                  "mode": "standard"
-                }
-              },
-              "results.properties.optoelectronic.solar_cell.short_circuit_current_density": {
-                "label": "Short circuit current density",
-                "align": "left",
-                "unit": "A/m**2",
-                "format": {
-                  "decimals": 3,
-                  "mode": "standard"
-                }
-              },
-              "results.properties.optoelectronic.solar_cell.fill_factor": {
-                "label": "Fill factor",
-                "align": "left",
-                "format": {
-                  "decimals": 3,
-                  "mode": "standard"
-                }
-              },
-              "references": {
-                "label": "References",
-                "align": "left"
-              },
-              "results.material.chemical_formula_hill": {
-                "label": "Formula",
-                "align": "left"
-              },
-              "results.material.structural_type": {
-                "label": "Dimensionality",
-                "align": "left"
-              },
-              "results.properties.optoelectronic.solar_cell.illumination_intensity": {
-                "label": "Illum. intensity",
-                "align": "left",
-                "unit": "W/m**2",
-                "format": {
-                  "decimals": 3,
-                  "mode": "standard"
-                }
-              },
-              "results.eln.lab_ids": {
-                "label": "Lab IDs",
-                "align": "left"
-              },
-              "results.eln.sections": {
-                "label": "Sections",
-                "align": "left"
-              },
-              "results.eln.methods": {
-                "label": "Methods",
-                "align": "left"
-              },
-              "results.eln.tags": {
-                "label": "Tags",
-                "align": "left"
-              },
-              "results.eln.instruments": {
-                "label": "Instruments",
-                "align": "left"
-              },
-              "entry_name": {
-                "label": "Name",
-                "align": "left"
-              },
-              "entry_type": {
-                "label": "Entry type",
-                "align": "left"
-              },
-              "mainfile": {
-                "label": "Mainfile",
-                "align": "left"
-              },
-              "upload_create_time": {
-                "label": "Upload time",
-                "align": "left"
-              },
-              "authors": {
-                "label": "Authors",
-                "align": "left"
-              },
-              "comment": {
-                "label": "Comment",
-                "align": "left"
-              },
-              "datasets": {
-                "label": "Datasets",
-                "align": "left"
-              },
-              "published": {
-                "label": "Access",
-                "align": "left"
-              }
-            },
-            "selected": [
-              "results.material.chemical_formula_descriptive",
-              "results.properties.optoelectronic.solar_cell.efficiency",
-              "results.properties.optoelectronic.solar_cell.open_circuit_voltage",
-              "results.properties.optoelectronic.solar_cell.short_circuit_current_density",
-              "results.properties.optoelectronic.solar_cell.fill_factor",
-              "references"
-            ]
-          },
-          "rows": {
-            "actions": {
-              "enabled": true
-            },
-            "details": {
-              "enabled": true
-            },
-            "selection": {
-              "enabled": true
-            }
-          },
-          "filter_menus": {
-            "options": {
-              "material": {
-                "label": "Absorber Material",
-                "level": 0,
-                "size": "s"
-              },
-              "elements": {
-                "label": "Elements / Formula",
-                "level": 1,
-                "size": "xl"
-              },
-              "structure": {
-                "label": "Structure",
-                "level": 1,
-                "size": "s"
-              },
-              "electronic": {
-                "label": "Electronic Properties",
-                "level": 0,
-                "size": "s"
-              },
-              "solarcell": {
-                "label": "Solar Cell Properties",
-                "level": 0,
-                "size": "s"
-              },
-              "eln": {
-                "label": "Electronic Lab Notebook",
-                "level": 0,
-                "size": "s"
-              },
-              "custom_quantities": {
-                "label": "User Defined Quantities",
-                "level": 0,
-                "size": "l"
-              },
-              "author": {
-                "label": "Author / Origin / Dataset",
-                "level": 0,
-                "size": "m"
-              },
-              "metadata": {
-                "label": "Visibility / IDs / Schema",
-                "level": 0,
-                "size": "s"
-              },
-              "optimade": {
-                "label": "Optimade",
-                "level": 0,
-                "size": "m"
-              }
-            }
-          },
-          "filters": {
-            "exclude": [
-              "mainfile",
-              "entry_name",
-              "combine"
-            ]
-          },
-          "dashboard": {
-            "widgets": [
-              {
-                "type": "periodictable",
-                "layout": {
-                  "xxl": {
-                    "minH": 8,
-                    "minW": 12,
-                    "h": 8,
-                    "w": 13,
-                    "x": 0,
-                    "y": 0
-                  },
-                  "xl": {
-                    "minH": 8,
-                    "minW": 12,
-                    "h": 8,
-                    "w": 12,
-                    "x": 0,
-                    "y": 0
-                  },
-                  "lg": {
-                    "minH": 8,
-                    "minW": 12,
-                    "h": 8,
-                    "w": 12,
-                    "x": 0,
-                    "y": 0
-                  },
-                  "md": {
-                    "minH": 8,
-                    "minW": 12,
-                    "h": 8,
-                    "w": 12,
-                    "x": 0,
-                    "y": 0
-                  },
-                  "sm": {
-                    "minH": 8,
-                    "minW": 12,
-                    "h": 8,
-                    "w": 12,
-                    "x": 0,
-                    "y": 16
-                  }
-                },
-                "quantity": "results.material.elements",
-                "scale": "linear"
-              },
-              {
-                "type": "scatterplot",
-                "layout": {
-                  "xxl": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 8,
-                    "w": 12,
-                    "x": 24,
-                    "y": 0
-                  },
-                  "xl": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 8,
-                    "w": 9,
-                    "x": 12,
-                    "y": 0
-                  },
-                  "lg": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 6,
-                    "w": 12,
-                    "x": 0,
-                    "y": 8
-                  },
-                  "md": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 6,
-                    "w": 9,
-                    "x": 0,
-                    "y": 8
-                  },
-                  "sm": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 5,
-                    "w": 6,
-                    "x": 0,
-                    "y": 0
-                  }
-                },
-                "x": "results.properties.optoelectronic.solar_cell.open_circuit_voltage",
-                "y": "results.properties.optoelectronic.solar_cell.efficiency",
-                "color": "results.properties.optoelectronic.solar_cell.short_circuit_current_density",
-                "size": 1000,
-                "autorange": true
-              },
-              {
-                "type": "scatterplot",
-                "layout": {
-                  "xxl": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 8,
-                    "w": 11,
-                    "x": 13,
-                    "y": 0
-                  },
-                  "xl": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 8,
-                    "w": 9,
-                    "x": 21,
-                    "y": 0
-                  },
-                  "lg": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 6,
-                    "w": 12,
-                    "x": 0,
-                    "y": 14
-                  },
-                  "md": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 6,
-                    "w": 9,
-                    "x": 9,
-                    "y": 8
-                  },
-                  "sm": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 5,
-                    "w": 6,
-                    "x": 6,
-                    "y": 0
-                  }
-                },
-                "x": "results.properties.optoelectronic.solar_cell.open_circuit_voltage",
-                "y": "results.properties.optoelectronic.solar_cell.efficiency",
-                "color": "results.properties.optoelectronic.solar_cell.device_architecture",
-                "size": 1000,
-                "autorange": true
-              },
-              {
-                "type": "terms",
-                "layout": {
-                  "xxl": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 6,
-                    "w": 6,
-                    "x": 14,
-                    "y": 8
-                  },
-                  "xl": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 6,
-                    "w": 6,
-                    "x": 14,
-                    "y": 8
-                  },
-                  "lg": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 6,
-                    "w": 6,
-                    "x": 12,
-                    "y": 0
-                  },
-                  "md": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 4,
-                    "w": 6,
-                    "x": 12,
-                    "y": 4
-                  },
-                  "sm": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 6,
-                    "w": 4,
-                    "x": 0,
-                    "y": 10
-                  }
-                },
-                "quantity": "results.properties.optoelectronic.solar_cell.device_stack",
-                "scale": "linear",
-                "showinput": true
-              },
-              {
-                "type": "histogram",
-                "layout": {
-                  "xxl": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 3,
-                    "w": 8,
-                    "x": 0,
-                    "y": 8
-                  },
-                  "xl": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 3,
-                    "w": 8,
-                    "x": 0,
-                    "y": 11
-                  },
-                  "lg": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 4,
-                    "w": 12,
-                    "x": 12,
-                    "y": 12
-                  },
-                  "md": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 3,
-                    "w": 8,
-                    "x": 10,
-                    "y": 17
-                  },
-                  "sm": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 3,
-                    "w": 8,
-                    "x": 4,
-                    "y": 13
-                  }
-                },
-                "quantity": "results.properties.optoelectronic.solar_cell.illumination_intensity",
-                "scale": "1/4",
-                "autorange": true,
-                "showinput": true,
-                "nbins": 30
-              },
-              {
-                "type": "terms",
-                "layout": {
-                  "xxl": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 6,
-                    "w": 6,
-                    "x": 8,
-                    "y": 8
-                  },
-                  "xl": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 6,
-                    "w": 6,
-                    "x": 8,
-                    "y": 8
-                  },
-                  "lg": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 6,
-                    "w": 6,
-                    "x": 18,
-                    "y": 0
-                  },
-                  "md": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 4,
-                    "w": 6,
-                    "x": 12,
-                    "y": 0
-                  },
-                  "sm": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 5,
-                    "w": 4,
-                    "x": 0,
-                    "y": 5
-                  }
-                },
-                "quantity": "results.properties.optoelectronic.solar_cell.absorber_fabrication",
-                "scale": "linear",
-                "showinput": true
-              },
-              {
-                "type": "histogram",
-                "layout": {
-                  "xxl": {
-                    "minH": 3,
-                    "minW": 8,
-                    "h": 3,
-                    "w": 8,
-                    "x": 0,
-                    "y": 11
-                  },
-                  "xl": {
-                    "minH": 3,
-                    "minW": 8,
-                    "h": 3,
-                    "w": 8,
-                    "x": 0,
-                    "y": 8
-                  },
-                  "lg": {
-                    "minH": 3,
-                    "minW": 8,
-                    "h": 4,
-                    "w": 12,
-                    "x": 12,
-                    "y": 16
-                  },
-                  "md": {
-                    "minH": 3,
-                    "minW": 8,
-                    "h": 3,
-                    "w": 8,
-                    "x": 10,
-                    "y": 14
-                  },
-                  "sm": {
-                    "minH": 3,
-                    "minW": 8,
-                    "h": 3,
-                    "w": 8,
-                    "x": 4,
-                    "y": 10
-                  }
-                },
-                "quantity": "results.properties.electronic.band_structure_electronic.band_gap.value",
-                "scale": "1/4",
-                "autorange": false,
-                "showinput": false,
-                "nbins": 30
-              },
-              {
-                "type": "terms",
-                "layout": {
-                  "xxl": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 6,
-                    "w": 6,
-                    "x": 20,
-                    "y": 8
-                  },
-                  "xl": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 6,
-                    "w": 5,
-                    "x": 25,
-                    "y": 8
-                  },
-                  "lg": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 6,
-                    "w": 6,
-                    "x": 18,
-                    "y": 6
-                  },
-                  "md": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 6,
-                    "w": 5,
-                    "x": 0,
-                    "y": 14
-                  },
-                  "sm": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 5,
-                    "w": 4,
-                    "x": 4,
-                    "y": 5
-                  }
-                },
-                "quantity": "results.properties.optoelectronic.solar_cell.electron_transport_layer",
-                "scale": "linear",
-                "showinput": true
-              },
-              {
-                "type": "terms",
-                "layout": {
-                  "xxl": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 6,
-                    "w": 6,
-                    "x": 26,
-                    "y": 8
-                  },
-                  "xl": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 6,
-                    "w": 5,
-                    "x": 20,
-                    "y": 8
-                  },
-                  "lg": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 6,
-                    "w": 6,
-                    "x": 12,
-                    "y": 6
-                  },
-                  "md": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 6,
-                    "w": 5,
-                    "x": 5,
-                    "y": 14
-                  },
-                  "sm": {
-                    "minH": 3,
-                    "minW": 3,
-                    "h": 5,
-                    "w": 4,
-                    "x": 8,
-                    "y": 5
-                  }
-                },
-                "quantity": "results.properties.optoelectronic.solar_cell.hole_transport_layer",
-                "scale": "linear",
-                "showinput": true
-              }
-            ]
-          },
-          "filters_locked": {
-            "sections": "nomad.datamodel.results.SolarCell"
-          }
-        }
-      }
-    },
-    "north": {},
-    "example_uploads": {}
-  }
-}
diff --git a/mkdocs.yml b/mkdocs.yml
index 5343ea2ef7..075a27fb5b 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -17,6 +17,7 @@ nav:
     - schema/elns.md
     - schema/workflows.md
     - schema/hdf5.md
+  - Plugins: plugins.md
   # - Using the AI Toolkit and other remote tools: aitoolkit.md
   - Developing NOMAD:
     - develop/setup.md
diff --git a/nomad/app/v1/routers/info.py b/nomad/app/v1/routers/info.py
index 12d4c1b563..7c5fb7e0ee 100644
--- a/nomad/app/v1/routers/info.py
+++ b/nomad/app/v1/routers/info.py
@@ -112,8 +112,8 @@ async def get_info():
             key[key.index('/') + 1:]
             for key in parsers.parser_dict.keys()]),
         'codes': [
-            {'code_name': x['codeLabel'], 'code_homepage': x.get('codeUrl')}
-            for x in sorted(code_metadata.values(), key=lambda info: info['codeLabel'].lower())
+            {'code_name': x.get('codeLabel', 'unknown code'), 'code_homepage': x.get('codeUrl')}
+            for x in sorted(code_metadata.values(), key=lambda info: info.get('codeLabel', 'unknown code').lower())
         ],
         'normalizers': [normalizer.__name__ for normalizer in normalizing.normalizers],
         'statistics': statistics(),
diff --git a/nomad/cli/dev.py b/nomad/cli/dev.py
index 6a03f278d3..e92e58d696 100644
--- a/nomad/cli/dev.py
+++ b/nomad/cli/dev.py
@@ -235,6 +235,12 @@ def get_gui_config() -> str:
     return f'window.nomadEnv = {json.dumps(data, indent=2)}'
 
 
+@dev.command(help='Generates the GUI development .env file based on NOMAD config.')
+def gui_env():
+    from nomad import config
+    print(f'REACT_APP_BACKEND_URL={config.ui.app_base}')
+
+
 @dev.command(help='Generates the GUI development config JS file based on NOMAD config.')
 def gui_config():
     print(get_gui_config())
diff --git a/nomad/mkdocs.py b/nomad/mkdocs.py
index 0864e5ac59..d31128e9a7 100644
--- a/nomad/mkdocs.py
+++ b/nomad/mkdocs.py
@@ -220,6 +220,11 @@ def define_env(env):
             or the archive of each entry (e.g. [a VASP example](../gui/search/entries/entry/id/d5OYC0SJTDevHMPk7YHd4A/-7j8ojKkna2NLXdytv_OjV4zsBXw/archive))
             in the web-interface.''')
 
+    @env.macro
+    def file_contents(path):  # pylint: disable=unused-variable
+        with open(path, 'r') as f:
+            return f.read()
+
     @env.macro
     def yaml_snippet(path, indent, filter=None):  # pylint: disable=unused-variable
         '''
diff --git a/scripts/setup_dev_env.sh b/scripts/setup_dev_env.sh
index 8cf67d842f..34564fe510 100755
--- a/scripts/setup_dev_env.sh
+++ b/scripts/setup_dev_env.sh
@@ -24,5 +24,5 @@ mkdocs build
 mkdir -p nomad/app/static/docs
 cp -r site/* nomad/app/static/docs
 
-# Generate a config file for the GUI
-python -m nomad.cli dev gui-config >gui/public/env.js
+# Generate .env file for the GUI
+python -m nomad.cli dev gui-env > gui/.env.development
-- 
GitLab