diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 61f1ef1fde7f504ac40538eda2b13cddfe1965c4..87421e09b76e25b0ec68ebcd813a269f60d575f2 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -80,7 +80,7 @@ python linting:
     - cd /app
   script:
     - pycodestyle --config=pycodestyle.ini nomad tests
-    - pylint --load-plugins=pylint_mongoengine,nomad.metainfo.pylint_plugin nomad tests
+    - pylint --rcfile=.pylintrc nomad tests
     - mypy --ignore-missing-imports --follow-imports=silent --no-strict-optional nomad tests
   rules:
     - if: $CI_COMMIT_TAG
diff --git a/.pylintrc b/.pylintrc
index a1dbdc628d2ba69358f38755aa7b1885345f40d1..6915175a4d7573590d49ba48a71ef95f7b8cc9bc 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -5,6 +5,10 @@
 # run arbitrary code.
 extension-pkg-whitelist=pydantic
 
+# List of plugins (as comma separated values of python module names) to load,
+# usually to register additional checkers.
+load-plugins=pylint_mongoengine,nomad.metainfo.pylint_plugin
+
 
 [MESSAGES CONTROL]
 
@@ -18,6 +22,7 @@ extension-pkg-whitelist=pydantic
 # no Warning level messages displayed, use "--disable=all --enable=classes
 # --disable=W".
 disable=blacklisted-name,
+        unspecified-encoding,
         invalid-name,
         missing-docstring,
         empty-docstring,
@@ -27,6 +32,7 @@ disable=blacklisted-name,
         unidiomatic-typecheck,
         consider-using-enumerate,
         consider-iterating-dictionary,
+        consider-using-f-string,
         bad-classmethod-argument,
         bad-mcs-method-argument,
         bad-mcs-classmethod-argument,
@@ -50,6 +56,7 @@ disable=blacklisted-name,
         ungrouped-imports,
         wrong-import-position,
         useless-import-alias,
+        import-outside-toplevel,
         old-style-class,
         len-as-condition,
         raw-checker-failed,
@@ -234,7 +241,22 @@ disable=blacklisted-name,
         deprecated-sys-function,
         exception-escape,
         comprehension-escape,
-        mongoengine-placeholder
+        mongoengine-placeholder,
+        # TODO: These were temporarily added when upgrading to newer pylint version that
+        # catches these new linting issues. Should be addressed and removed.
+        self-assigning-variable,
+        use-dict-literal,
+        unnecessary-comprehension,
+        use-sequence-for-iteration,
+        f-string-without-interpolation,
+        super-with-arguments,
+        used-before-assignment,
+        consider-using-dict-items,
+        no-else-continue,
+        arguments-renamed,
+        consider-using-with,
+        raise-missing-from,
+        no-else-break
 
 # Enable the message, report, category or checker with the given id(s). You can
 # either give multiple identifier separated by comma (,) or put this option
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 38e73a1e992d5426dd4e355283073365492e3aa0..8c02f2530995fd426b322398950aec11657d0395 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -11,7 +11,7 @@
     },
     "files.trimTrailingWhitespace": true,
     "python.linting.pylintArgs": [
-        "--load-plugins=pylint_mongoengine",
+        "--rcfile=${workspaceFolder}/.pylintrc"
     ],
     "python.linting.pycodestylePath": "pycodestyle",
     "python.linting.pycodestyleEnabled": true,
diff --git a/docs/config.md b/docs/config.md
index db9421e416bd565842343ce22c249d3117c32043..233b2d9588e543f6ce2080535e8b6e7f9b660338 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -37,6 +37,56 @@ Here is an example `nomad.yaml` file:
 --8<-- "ops/docker-compose/nomad-oasis/configs/nomad.yaml"
 ```
 
+When overwriting an *object* in the configuration, the new value will be merged with the default value. The new merged object will have all of the attributes of the new object in addition to any old attributes that were not overwritten. This allows you to simply change an individual setting without having to provide the entire structure again, which simplifies customization that happens deep in the configuration hierarchy. When overwriting anything else (numbers, strings, lists etc.) the new value completely replaces the old one.
+
+#### User interface customization
+
+Many of the UI options use a data model that contains the following three fields: `include`, `exclude` and `options`. This structure allows you to easily disable, enable, reorder and modify the UI layout with minimal config rewrite. Here are examples of common customization tasks using the search columns as an example:
+
+Disable item:
+```yaml
+ui:
+  apps:
+    options:
+      entries:
+        columns:
+          exclude: ['upload_create_time']
+```
+
+Explicitly select the shown items and their order
+```yaml
+ui:
+  apps:
+    options:
+      entries:
+        columns:
+          include: ['entry_id', 'upload_create_time']
+```
+
+Modify existing option
+```yaml
+ui:
+  apps:
+    options:
+      entries:
+        columns:
+          options:
+            upload_create_time:
+              label: "Uploaded"
+```
+
+Add a new item that does not yet exist in options. Note that by default all options are shown in the order they have been declared unless the order is explicitly given in `include`.
+```yaml
+ui:
+  apps:
+    options:
+      entries:
+        columns:
+          options:
+            upload_id:
+              label: "Upload ID"
+```
+
 The following is a reference of all configuration sections and attributes.
 
 ## Services
diff --git a/gui/public/env.js b/gui/public/env.js
index 15e071b223d44f641e2bb73e9071ed289f9d86aa..92b074ae6146829533c0b233ec02a0b4be45dcf2 100644
--- a/gui/public/env.js
+++ b/gui/public/env.js
@@ -12,8 +12,14 @@ window.nomadEnv = {
   "globalLoginRequired": false,
   "servicesUploadLimit": 10,
   "ui": {
-    "entry_context": {
-      "overview": {
+    "theme": {
+      "title": "NOMAD"
+    },
+    "unit_systems": {
+      "selected": "Custom"
+    },
+    "entry": {
+      "cards": {
         "exclude": [
           "relatedResources"
         ],
@@ -69,7 +75,7 @@ window.nomadEnv = {
         }
       }
     },
-    "search_contexts": {
+    "apps": {
       "options": {
         "entries": {
           "label": "Entries",
@@ -88,13 +94,6 @@ window.nomadEnv = {
             "page_size": 20
           },
           "columns": {
-            "enable": [
-              "entry_name",
-              "results.material.chemical_formula_hill",
-              "entry_type",
-              "upload_create_time",
-              "authors"
-            ],
             "options": {
               "entry_name": {
                 "label": "Name",
@@ -117,43 +116,56 @@ window.nomadEnv = {
                 "align": "left"
               },
               "results.method.method_name": {
-                "label": "Method name"
+                "label": "Method name",
+                "align": "left"
               },
               "results.method.simulation.program_name": {
-                "label": "Program name"
+                "label": "Program name",
+                "align": "left"
               },
               "results.method.simulation.dft.basis_set_name": {
-                "label": "Basis set name"
+                "label": "Basis set name",
+                "align": "left"
               },
               "results.method.simulation.dft.xc_functional_type": {
-                "label": "XC Functional Type"
+                "label": "XC Functional Type",
+                "align": "left"
               },
               "results.material.structural_type": {
-                "label": "Dimensionality"
+                "label": "Dimensionality",
+                "align": "left"
               },
               "results.material.symmetry.crystal_system": {
-                "label": "Crystal system"
+                "label": "Crystal system",
+                "align": "left"
               },
               "results.material.symmetry.space_group_symbol": {
-                "label": "Space group symbol"
+                "label": "Space group symbol",
+                "align": "left"
               },
               "results.material.symmetry.space_group_number": {
-                "label": "Space group number"
+                "label": "Space group number",
+                "align": "left"
               },
               "results.eln.lab_ids": {
-                "label": "Lab IDs"
+                "label": "Lab IDs",
+                "align": "left"
               },
               "results.eln.sections": {
-                "label": "Sections"
+                "label": "Sections",
+                "align": "left"
               },
               "results.eln.methods": {
-                "label": "Methods"
+                "label": "Methods",
+                "align": "left"
               },
               "results.eln.tags": {
-                "label": "Tags"
+                "label": "Tags",
+                "align": "left"
               },
               "results.eln.instruments": {
-                "label": "Instruments"
+                "label": "Instruments",
+                "align": "left"
               },
               "mainfile": {
                 "label": "Mainfile",
@@ -172,111 +184,110 @@ window.nomadEnv = {
                 "align": "left"
               },
               "published": {
-                "label": "Access"
+                "label": "Access",
+                "align": "left"
               }
-            }
+            },
+            "selected": [
+              "entry_name",
+              "results.material.chemical_formula_hill",
+              "entry_type",
+              "upload_create_time",
+              "authors"
+            ]
           },
           "rows": {
             "actions": {
-              "enable": true
+              "enabled": true
             },
             "details": {
-              "enable": true
+              "enabled": true
             },
             "selection": {
-              "enable": true
+              "enabled": true
             }
           },
           "filter_menus": {
             "options": {
               "material": {
                 "label": "Material",
-                "level": 0
+                "level": 0,
+                "size": "small"
               },
               "elements": {
                 "label": "Elements / Formula",
                 "level": 1,
-                "size": "large",
-                "menu_items": {}
+                "size": "large"
               },
               "structure": {
                 "label": "Structure",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "method": {
                 "label": "Method",
                 "level": 0,
-                "menu_items": {}
+                "size": "small"
               },
               "dft": {
                 "label": "DFT",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "gw": {
                 "label": "GW",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "projection": {
                 "label": "Projection",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "dmft": {
                 "label": "DMFT",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "eels": {
                 "label": "EELS",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "workflow": {
                 "label": "Workflow",
-                "level": 0
+                "level": 0,
+                "size": "small"
               },
               "molecular_dynamics": {
                 "label": "Molecular dynamics",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "geometry_optimization": {
                 "label": "Geometry Optimization",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "properties": {
                 "label": "Properties",
-                "level": 0
+                "level": 0,
+                "size": "small"
               },
               "electronic": {
                 "label": "Electronic",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "vibrational": {
                 "label": "Vibrational",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "mechanical": {
                 "label": "Mechanical",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "usecases": {
                 "label": "Use Cases",
@@ -286,26 +297,22 @@ window.nomadEnv = {
               "solarcell": {
                 "label": "Solar Cells",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "author": {
                 "label": "Author / Origin / Dataset",
                 "level": 0,
-                "size": "medium",
-                "menu_items": {}
+                "size": "medium"
               },
               "metadata": {
                 "label": "Visibility / IDs / Schema",
                 "level": 0,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "optimade": {
                 "label": "Optimade",
                 "level": 0,
-                "size": "medium",
-                "menu_items": {}
+                "size": "medium"
               }
             }
           },
@@ -334,23 +341,18 @@ window.nomadEnv = {
             "page_size": 20
           },
           "columns": {
-            "enable": [
-              "results.material.chemical_formula_hill",
-              "results.method.simulation.program_name",
-              "results.method.method_name",
-              "upload_create_time",
-              "authors"
-            ],
             "options": {
               "results.material.chemical_formula_hill": {
                 "label": "Formula",
                 "align": "left"
               },
               "results.method.simulation.program_name": {
-                "label": "Program name"
+                "label": "Program name",
+                "align": "left"
               },
               "results.method.method_name": {
-                "label": "Method name"
+                "label": "Method name",
+                "align": "left"
               },
               "upload_create_time": {
                 "label": "Upload time",
@@ -361,37 +363,48 @@ window.nomadEnv = {
                 "align": "left"
               },
               "results.method.simulation.dft.basis_set_name": {
-                "label": "Basis set name"
+                "label": "Basis set name",
+                "align": "left"
               },
               "results.method.simulation.dft.xc_functional_type": {
-                "label": "XC Functional Type"
+                "label": "XC Functional Type",
+                "align": "left"
               },
               "results.material.structural_type": {
-                "label": "Dimensionality"
+                "label": "Dimensionality",
+                "align": "left"
               },
               "results.material.symmetry.crystal_system": {
-                "label": "Crystal system"
+                "label": "Crystal system",
+                "align": "left"
               },
               "results.material.symmetry.space_group_symbol": {
-                "label": "Space group symbol"
+                "label": "Space group symbol",
+                "align": "left"
               },
               "results.material.symmetry.space_group_number": {
-                "label": "Space group number"
+                "label": "Space group number",
+                "align": "left"
               },
               "results.eln.lab_ids": {
-                "label": "Lab IDs"
+                "label": "Lab IDs",
+                "align": "left"
               },
               "results.eln.sections": {
-                "label": "Sections"
+                "label": "Sections",
+                "align": "left"
               },
               "results.eln.methods": {
-                "label": "Methods"
+                "label": "Methods",
+                "align": "left"
               },
               "results.eln.tags": {
-                "label": "Tags"
+                "label": "Tags",
+                "align": "left"
               },
               "results.eln.instruments": {
-                "label": "Instruments"
+                "label": "Instruments",
+                "align": "left"
               },
               "entry_name": {
                 "label": "Name",
@@ -418,123 +431,120 @@ window.nomadEnv = {
                 "align": "left"
               },
               "published": {
-                "label": "Access"
+                "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": {
-              "enable": true
+              "enabled": true
             },
             "details": {
-              "enable": true
+              "enabled": true
             },
             "selection": {
-              "enable": true
+              "enabled": true
             }
           },
           "filter_menus": {
             "options": {
               "material": {
                 "label": "Material",
-                "level": 0
+                "level": 0,
+                "size": "small"
               },
               "elements": {
                 "label": "Elements / Formula",
                 "level": 1,
-                "size": "large",
-                "menu_items": {}
+                "size": "large"
               },
               "structure": {
                 "label": "Structure",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "method": {
                 "label": "Method",
                 "level": 0,
-                "menu_items": {}
+                "size": "small"
               },
               "dft": {
                 "label": "DFT",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "gw": {
                 "label": "GW",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "projection": {
                 "label": "Projection",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "dmft": {
                 "label": "DMFT",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "workflow": {
                 "label": "Workflow",
-                "level": 0
+                "level": 0,
+                "size": "small"
               },
               "molecular_dynamics": {
                 "label": "Molecular dynamics",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "geometry_optimization": {
                 "label": "Geometry Optimization",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "properties": {
                 "label": "Properties",
-                "level": 0
+                "level": 0,
+                "size": "small"
               },
               "electronic": {
                 "label": "Electronic",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "vibrational": {
                 "label": "Vibrational",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "mechanical": {
                 "label": "Mechanical",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "author": {
                 "label": "Author / Origin / Dataset",
                 "level": 0,
-                "size": "medium",
-                "menu_items": {}
+                "size": "medium"
               },
               "metadata": {
                 "label": "Visibility / IDs / Schema",
                 "level": 0,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "optimade": {
                 "label": "Optimade",
                 "level": 0,
-                "size": "medium",
-                "menu_items": {}
+                "size": "medium"
               }
             }
           },
@@ -562,114 +572,115 @@ window.nomadEnv = {
           },
           "pagination": {
             "order_by": "chemical_formula_hill",
-            "order": "asc"
+            "order": "asc",
+            "page_size": 20
           },
           "columns": {
-            "enable": [
-              "chemical_formula_hill",
-              "structural_type",
-              "symmetry.structure_name",
-              "symmetry.space_group_number",
-              "symmetry.crystal_system"
-            ],
             "options": {
               "chemical_formula_hill": {
                 "label": "Formula",
                 "align": "left"
               },
               "structural_type": {
-                "label": "Dimensionality"
+                "label": "Dimensionality",
+                "align": "left"
               },
               "symmetry.structure_name": {
-                "label": "Structure name"
+                "label": "Structure name",
+                "align": "left"
               },
               "symmetry.space_group_number": {
-                "label": "Space group number"
+                "label": "Space group number",
+                "align": "left"
               },
               "symmetry.crystal_system": {
-                "label": "Crystal system"
+                "label": "Crystal system",
+                "align": "left"
               },
               "symmetry.space_group_symbol": {
-                "label": "Space group symbol"
+                "label": "Space group symbol",
+                "align": "left"
               },
               "material_id": {
-                "label": "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": {
-              "enable": true
+              "enabled": true
             },
             "details": {
-              "enable": false
+              "enabled": false
             },
             "selection": {
-              "enable": false
+              "enabled": false
             }
           },
           "filter_menus": {
             "options": {
               "material": {
                 "label": "Material",
-                "level": 0
+                "level": 0,
+                "size": "small"
               },
               "elements": {
                 "label": "Elements / Formula",
                 "level": 1,
-                "size": "large",
-                "menu_items": {}
+                "size": "large"
               },
               "structure": {
                 "label": "Structure",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "method": {
                 "label": "Method",
                 "level": 0,
-                "menu_items": {}
+                "size": "small"
               },
               "dft": {
                 "label": "DFT",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "gw": {
                 "label": "GW",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "projection": {
                 "label": "Projection",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "dmft": {
                 "label": "DMFT",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "workflow": {
                 "label": "Workflow",
-                "level": 0
+                "level": 0,
+                "size": "small"
               },
               "molecular_dynamics": {
                 "label": "Molecular dynamics",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "geometry_optimization": {
                 "label": "Geometry Optimization",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "properties": {
                 "label": "Properties",
@@ -679,40 +690,36 @@ window.nomadEnv = {
               "electronic": {
                 "label": "Electronic",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "vibrational": {
                 "label": "Vibrational",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "mechanical": {
                 "label": "Mechanical",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "author": {
                 "label": "Author / Origin / Dataset",
                 "level": 0,
-                "size": "medium",
-                "menu_items": {}
+                "size": "medium"
               },
               "metadata": {
                 "label": "Visibility / IDs / Schema",
                 "level": 0,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "optimade": {
                 "label": "Optimade",
                 "level": 0,
-                "size": "medium",
-                "menu_items": {}
+                "size": "medium"
               },
               "combine": {
+                "level": 0,
+                "size": "small",
                 "actions": {
                   "options": {
                     "combine": {
@@ -749,12 +756,6 @@ window.nomadEnv = {
             "page_size": 20
           },
           "columns": {
-            "enable": [
-              "entry_name",
-              "entry_type",
-              "upload_create_time",
-              "authors"
-            ],
             "options": {
               "entry_name": {
                 "label": "Name",
@@ -777,22 +778,28 @@ window.nomadEnv = {
                 "align": "left"
               },
               "results.method.method_name": {
-                "label": "Method name"
+                "label": "Method name",
+                "align": "left"
               },
               "results.eln.lab_ids": {
-                "label": "Lab IDs"
+                "label": "Lab IDs",
+                "align": "left"
               },
               "results.eln.sections": {
-                "label": "Sections"
+                "label": "Sections",
+                "align": "left"
               },
               "results.eln.methods": {
-                "label": "Methods"
+                "label": "Methods",
+                "align": "left"
               },
               "results.eln.tags": {
-                "label": "Tags"
+                "label": "Tags",
+                "align": "left"
               },
               "results.eln.instruments": {
-                "label": "Instruments"
+                "label": "Instruments",
+                "align": "left"
               },
               "mainfile": {
                 "label": "Mainfile",
@@ -811,62 +818,64 @@ window.nomadEnv = {
                 "align": "left"
               },
               "published": {
-                "label": "Access"
+                "label": "Access",
+                "align": "left"
               }
-            }
+            },
+            "selected": [
+              "entry_name",
+              "entry_type",
+              "upload_create_time",
+              "authors"
+            ]
           },
           "rows": {
             "actions": {
-              "enable": true
+              "enabled": true
             },
             "details": {
-              "enable": true
+              "enabled": true
             },
             "selection": {
-              "enable": true
+              "enabled": true
             }
           },
           "filter_menus": {
             "options": {
               "material": {
                 "label": "Material",
-                "level": 0
+                "level": 0,
+                "size": "small"
               },
               "elements": {
                 "label": "Elements / Formula",
                 "level": 1,
-                "size": "large",
-                "menu_items": {}
+                "size": "large"
               },
               "eln": {
                 "label": "Electronic Lab Notebook",
                 "level": 0,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "custom_quantities": {
                 "label": "User Defined Quantities",
                 "level": 0,
-                "size": "large",
-                "menu_items": {}
+                "size": "large"
               },
               "author": {
                 "label": "Author / Origin / Dataset",
                 "level": 0,
-                "size": "medium",
-                "menu_items": {}
+                "size": "medium"
               },
               "metadata": {
                 "label": "Visibility / IDs / Schema",
                 "level": 0,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "optimade": {
                 "label": "Optimade",
                 "level": 0,
-                "size": "medium",
-                "menu_items": {}
+                "size": "medium"
               }
             }
           },
@@ -898,23 +907,18 @@ window.nomadEnv = {
             "page_size": 20
           },
           "columns": {
-            "enable": [
-              "results.material.chemical_formula_hill",
-              "results.properties.spectroscopy.eels.detector_type",
-              "results.properties.spectroscopy.eels.resolution",
-              "upload_create_time",
-              "authors"
-            ],
             "options": {
               "results.material.chemical_formula_hill": {
                 "label": "Formula",
                 "align": "left"
               },
               "results.properties.spectroscopy.eels.detector_type": {
-                "label": "Detector type"
+                "label": "Detector type",
+                "align": "left"
               },
               "results.properties.spectroscopy.eels.resolution": {
-                "label": "Resolution"
+                "label": "Resolution",
+                "align": "left"
               },
               "upload_create_time": {
                 "label": "Upload time",
@@ -924,8 +928,12 @@ window.nomadEnv = {
                 "label": "Authors",
                 "align": "left"
               },
-              "results.properties.spectroscopy.eels.min_energy": {},
-              "results.properties.spectroscopy.eels.max_energy": {},
+              "results.properties.spectroscopy.eels.min_energy": {
+                "align": "left"
+              },
+              "results.properties.spectroscopy.eels.max_energy": {
+                "align": "left"
+              },
               "entry_name": {
                 "label": "Name",
                 "align": "left"
@@ -951,32 +959,40 @@ window.nomadEnv = {
                 "align": "left"
               },
               "published": {
-                "label": "Access"
+                "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": {
-              "enable": true
+              "enabled": true
             },
             "details": {
-              "enable": true
+              "enabled": true
             },
             "selection": {
-              "enable": true
+              "enabled": true
             }
           },
           "filter_menus": {
             "options": {
               "material": {
                 "label": "Material",
-                "level": 0
+                "level": 0,
+                "size": "small"
               },
               "elements": {
                 "label": "Elements / Formula",
                 "level": 1,
-                "size": "large",
-                "menu_items": {}
+                "size": "large"
               },
               "method": {
                 "label": "Method",
@@ -986,26 +1002,22 @@ window.nomadEnv = {
               "eels": {
                 "label": "EELS",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "author": {
                 "label": "Author / Origin / Dataset",
                 "level": 0,
-                "size": "medium",
-                "menu_items": {}
+                "size": "medium"
               },
               "metadata": {
                 "label": "Visibility / IDs / Schema",
                 "level": 0,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "optimade": {
                 "label": "Optimade",
                 "level": 0,
-                "size": "medium",
-                "menu_items": {}
+                "size": "medium"
               }
             }
           },
@@ -1036,637 +1048,642 @@ window.nomadEnv = {
             "order": "desc",
             "page_size": 20
           },
-          "dashboard": {
-            "widgets": [
-              {
-                "type": "periodictable",
-                "scale": "linear",
-                "quantity": "results.material.elements",
-                "layout": {
-                  "xxl": {
-                    "minH": 8,
-                    "minW": 12,
-                    "h": 8,
-                    "w": 13,
-                    "y": 0,
-                    "x": 0
-                  },
-                  "xl": {
-                    "minH": 8,
-                    "minW": 12,
-                    "h": 8,
-                    "w": 12,
-                    "y": 0,
-                    "x": 0
-                  },
-                  "lg": {
-                    "minH": 8,
-                    "minW": 12,
-                    "h": 8,
-                    "w": 12,
-                    "y": 0,
-                    "x": 0
-                  },
-                  "md": {
-                    "minH": 8,
-                    "minW": 12,
-                    "h": 8,
-                    "w": 12,
-                    "y": 0,
-                    "x": 0
-                  },
-                  "sm": {
-                    "minH": 8,
-                    "minW": 12,
-                    "h": 8,
-                    "w": 12,
-                    "y": 16,
-                    "x": 0
-                  }
+          "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": "small"
+              },
+              "elements": {
+                "label": "Elements / Formula",
+                "level": 1,
+                "size": "large"
+              },
+              "structure": {
+                "label": "Structure",
+                "level": 1,
+                "size": "small"
+              },
+              "electronic": {
+                "label": "Electronic Properties",
+                "level": 0,
+                "size": "small"
+              },
+              "solarcell": {
+                "label": "Solar Cell Properties",
+                "level": 0,
+                "size": "small"
+              },
+              "eln": {
+                "label": "Electronic Lab Notebook",
+                "level": 0,
+                "size": "small"
+              },
+              "custom_quantities": {
+                "label": "User Defined Quantities",
+                "level": 0,
+                "size": "large"
+              },
+              "author": {
+                "label": "Author / Origin / Dataset",
+                "level": 0,
+                "size": "medium"
+              },
+              "metadata": {
+                "label": "Visibility / IDs / Schema",
+                "level": 0,
+                "size": "small"
+              },
+              "optimade": {
+                "label": "Optimade",
+                "level": 0,
+                "size": "medium"
+              }
+            }
+          },
+          "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",
-                "autorange": true,
-                "size": 1000,
-                "color": "results.properties.optoelectronic.solar_cell.short_circuit_current_density",
-                "y": "results.properties.optoelectronic.solar_cell.efficiency",
-                "x": "results.properties.optoelectronic.solar_cell.open_circuit_voltage",
                 "layout": {
                   "xxl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 8,
                     "w": 12,
-                    "y": 0,
-                    "x": 24
+                    "x": 24,
+                    "y": 0
                   },
                   "xl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 8,
                     "w": 9,
-                    "y": 0,
-                    "x": 12
+                    "x": 12,
+                    "y": 0
                   },
                   "lg": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 12,
-                    "y": 8,
-                    "x": 0
+                    "x": 0,
+                    "y": 8
                   },
                   "md": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 9,
-                    "y": 8,
-                    "x": 0
+                    "x": 0,
+                    "y": 8
                   },
                   "sm": {
                     "minH": 3,
                     "minW": 3,
                     "h": 5,
                     "w": 6,
-                    "y": 0,
-                    "x": 0
+                    "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",
-                "autorange": true,
-                "size": 1000,
-                "color": "results.properties.optoelectronic.solar_cell.device_architecture",
-                "y": "results.properties.optoelectronic.solar_cell.efficiency",
-                "x": "results.properties.optoelectronic.solar_cell.open_circuit_voltage",
                 "layout": {
                   "xxl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 8,
                     "w": 11,
-                    "y": 0,
-                    "x": 13
+                    "x": 13,
+                    "y": 0
                   },
                   "xl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 8,
                     "w": 9,
-                    "y": 0,
-                    "x": 21
+                    "x": 21,
+                    "y": 0
                   },
                   "lg": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 12,
-                    "y": 14,
-                    "x": 0
+                    "x": 0,
+                    "y": 14
                   },
                   "md": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 9,
-                    "y": 8,
-                    "x": 9
+                    "x": 9,
+                    "y": 8
                   },
                   "sm": {
                     "minH": 3,
                     "minW": 3,
                     "h": 5,
                     "w": 6,
-                    "y": 0,
-                    "x": 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",
-                "inputfields": true,
-                "scale": "linear",
-                "quantity": "results.properties.optoelectronic.solar_cell.device_stack",
                 "layout": {
                   "xxl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 6,
-                    "y": 8,
-                    "x": 14
+                    "x": 14,
+                    "y": 8
                   },
                   "xl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 6,
-                    "y": 8,
-                    "x": 14
+                    "x": 14,
+                    "y": 8
                   },
                   "lg": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 6,
-                    "y": 0,
-                    "x": 12
+                    "x": 12,
+                    "y": 0
                   },
                   "md": {
                     "minH": 3,
                     "minW": 3,
                     "h": 4,
                     "w": 6,
-                    "y": 4,
-                    "x": 12
+                    "x": 12,
+                    "y": 4
                   },
                   "sm": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 4,
-                    "y": 10,
-                    "x": 0
+                    "x": 0,
+                    "y": 10
                   }
-                }
+                },
+                "quantity": "results.properties.optoelectronic.solar_cell.device_stack",
+                "scale": "linear",
+                "showinput": true
               },
               {
                 "type": "histogram",
-                "autorange": true,
-                "nbins": 30,
-                "scale": "1/4",
-                "quantity": "results.properties.optoelectronic.solar_cell.illumination_intensity",
                 "layout": {
                   "xxl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 3,
                     "w": 8,
-                    "y": 8,
-                    "x": 0
+                    "x": 0,
+                    "y": 8
                   },
                   "xl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 3,
                     "w": 8,
-                    "y": 11,
-                    "x": 0
+                    "x": 0,
+                    "y": 11
                   },
                   "lg": {
                     "minH": 3,
                     "minW": 3,
                     "h": 4,
                     "w": 12,
-                    "y": 12,
-                    "x": 12
+                    "x": 12,
+                    "y": 12
                   },
                   "md": {
                     "minH": 3,
                     "minW": 3,
                     "h": 3,
                     "w": 8,
-                    "y": 17,
-                    "x": 10
+                    "x": 10,
+                    "y": 17
                   },
                   "sm": {
                     "minH": 3,
                     "minW": 3,
                     "h": 3,
                     "w": 8,
-                    "y": 13,
-                    "x": 4
+                    "x": 4,
+                    "y": 13
                   }
-                }
+                },
+                "quantity": "results.properties.optoelectronic.solar_cell.illumination_intensity",
+                "scale": "1/4",
+                "autorange": true,
+                "showinput": true,
+                "nbins": 30
               },
               {
                 "type": "terms",
-                "inputfields": true,
-                "scale": "linear",
-                "quantity": "results.properties.optoelectronic.solar_cell.absorber_fabrication",
                 "layout": {
                   "xxl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 6,
-                    "y": 8,
-                    "x": 8
+                    "x": 8,
+                    "y": 8
                   },
                   "xl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 6,
-                    "y": 8,
-                    "x": 8
+                    "x": 8,
+                    "y": 8
                   },
                   "lg": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 6,
-                    "y": 0,
-                    "x": 18
+                    "x": 18,
+                    "y": 0
                   },
                   "md": {
                     "minH": 3,
                     "minW": 3,
                     "h": 4,
                     "w": 6,
-                    "y": 0,
-                    "x": 12
+                    "x": 12,
+                    "y": 0
                   },
                   "sm": {
                     "minH": 3,
                     "minW": 3,
                     "h": 5,
                     "w": 4,
-                    "y": 5,
-                    "x": 0
+                    "x": 0,
+                    "y": 5
                   }
-                }
+                },
+                "quantity": "results.properties.optoelectronic.solar_cell.absorber_fabrication",
+                "scale": "linear",
+                "showinput": true
               },
               {
                 "type": "histogram",
-                "inputfields": false,
-                "autorange": false,
-                "nbins": 30,
-                "scale": "1/4",
-                "quantity": "results.properties.electronic.band_structure_electronic.band_gap.value",
                 "layout": {
                   "xxl": {
                     "minH": 3,
                     "minW": 8,
                     "h": 3,
                     "w": 8,
-                    "y": 11,
-                    "x": 0
+                    "x": 0,
+                    "y": 11
                   },
                   "xl": {
                     "minH": 3,
                     "minW": 8,
                     "h": 3,
                     "w": 8,
-                    "y": 8,
-                    "x": 0
+                    "x": 0,
+                    "y": 8
                   },
                   "lg": {
                     "minH": 3,
                     "minW": 8,
                     "h": 4,
                     "w": 12,
-                    "y": 16,
-                    "x": 12
+                    "x": 12,
+                    "y": 16
                   },
                   "md": {
                     "minH": 3,
                     "minW": 8,
                     "h": 3,
                     "w": 8,
-                    "y": 14,
-                    "x": 10
+                    "x": 10,
+                    "y": 14
                   },
                   "sm": {
                     "minH": 3,
                     "minW": 8,
                     "h": 3,
                     "w": 8,
-                    "y": 10,
-                    "x": 4
+                    "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",
-                "inputfields": true,
-                "scale": "linear",
-                "quantity": "results.properties.optoelectronic.solar_cell.electron_transport_layer",
                 "layout": {
                   "xxl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 6,
-                    "y": 8,
-                    "x": 20
+                    "x": 20,
+                    "y": 8
                   },
                   "xl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 5,
-                    "y": 8,
-                    "x": 25
+                    "x": 25,
+                    "y": 8
                   },
                   "lg": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 6,
-                    "y": 6,
-                    "x": 18
+                    "x": 18,
+                    "y": 6
                   },
                   "md": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 5,
-                    "y": 14,
-                    "x": 0
+                    "x": 0,
+                    "y": 14
                   },
                   "sm": {
                     "minH": 3,
                     "minW": 3,
                     "h": 5,
                     "w": 4,
-                    "y": 5,
-                    "x": 4
+                    "x": 4,
+                    "y": 5
                   }
-                }
+                },
+                "quantity": "results.properties.optoelectronic.solar_cell.electron_transport_layer",
+                "scale": "linear",
+                "showinput": true
               },
               {
                 "type": "terms",
-                "inputfields": true,
-                "scale": "linear",
-                "quantity": "results.properties.optoelectronic.solar_cell.hole_transport_layer",
                 "layout": {
                   "xxl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 6,
-                    "y": 8,
-                    "x": 26
+                    "x": 26,
+                    "y": 8
                   },
                   "xl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 5,
-                    "y": 8,
-                    "x": 20
+                    "x": 20,
+                    "y": 8
                   },
                   "lg": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 6,
-                    "y": 6,
-                    "x": 12
+                    "x": 12,
+                    "y": 6
                   },
                   "md": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 5,
-                    "y": 14,
-                    "x": 5
+                    "x": 5,
+                    "y": 14
                   },
                   "sm": {
                     "minH": 3,
                     "minW": 3,
                     "h": 5,
                     "w": 4,
-                    "y": 5,
-                    "x": 8
+                    "x": 8,
+                    "y": 5
                   }
-                }
-              }
-            ]
-          },
-          "columns": {
-            "enable": [
-              "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"
-            ],
-            "options": {
-              "results.material.chemical_formula_descriptive": {
-                "label": "Descriptive Formula",
-                "align": "left"
-              },
-              "results.properties.optoelectronic.solar_cell.efficiency": {
-                "label": "Efficiency (%)",
-                "format": {
-                  "decimals": 2,
-                  "mode": "standard"
-                }
-              },
-              "results.properties.optoelectronic.solar_cell.open_circuit_voltage": {
-                "label": "Open circuit voltage",
-                "unit": "V",
-                "format": {
-                  "decimals": 3,
-                  "mode": "standard"
-                }
-              },
-              "results.properties.optoelectronic.solar_cell.short_circuit_current_density": {
-                "label": "Short circuit current density",
-                "unit": "A/m**2",
-                "format": {
-                  "decimals": 3,
-                  "mode": "standard"
-                }
-              },
-              "results.properties.optoelectronic.solar_cell.fill_factor": {
-                "label": "Fill factor",
-                "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"
-              },
-              "results.properties.optoelectronic.solar_cell.illumination_intensity": {
-                "label": "Illum. intensity",
-                "unit": "W/m**2",
-                "format": {
-                  "decimals": 3,
-                  "mode": "standard"
-                }
-              },
-              "results.eln.lab_ids": {
-                "label": "Lab IDs"
-              },
-              "results.eln.sections": {
-                "label": "Sections"
-              },
-              "results.eln.methods": {
-                "label": "Methods"
-              },
-              "results.eln.tags": {
-                "label": "Tags"
-              },
-              "results.eln.instruments": {
-                "label": "Instruments"
-              },
-              "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"
-              }
-            }
-          },
-          "rows": {
-            "actions": {
-              "enable": true
-            },
-            "details": {
-              "enable": true
-            },
-            "selection": {
-              "enable": true
-            }
-          },
-          "filter_menus": {
-            "options": {
-              "material": {
-                "label": "Absorber Material",
-                "level": 0
-              },
-              "elements": {
-                "label": "Elements / Formula",
-                "level": 1,
-                "size": "large",
-                "menu_items": {}
-              },
-              "structure": {
-                "label": "Structure",
-                "level": 1,
-                "size": "small",
-                "menu_items": {}
-              },
-              "electronic": {
-                "label": "Electronic Properties",
-                "level": 0,
-                "size": "small",
-                "menu_items": {}
-              },
-              "solarcell": {
-                "label": "Solar Cell Properties",
-                "level": 0,
-                "size": "small",
-                "menu_items": {}
-              },
-              "eln": {
-                "label": "Electronic Lab Notebook",
-                "level": 0,
-                "size": "small",
-                "menu_items": {}
-              },
-              "custom_quantities": {
-                "label": "User Defined Quantities",
-                "level": 0,
-                "size": "large",
-                "menu_items": {}
-              },
-              "author": {
-                "label": "Author / Origin / Dataset",
-                "level": 0,
-                "size": "medium",
-                "menu_items": {}
-              },
-              "metadata": {
-                "label": "Visibility / IDs / Schema",
-                "level": 0,
-                "size": "small",
-                "menu_items": {}
-              },
-              "optimade": {
-                "label": "Optimade",
-                "level": 0,
-                "size": "medium",
-                "menu_items": {}
+                },
+                "quantity": "results.properties.optoelectronic.solar_cell.hole_transport_layer",
+                "scale": "linear",
+                "showinput": true
               }
-            }
-          },
-          "filters": {
-            "exclude": [
-              "mainfile",
-              "entry_name",
-              "combine"
             ]
           },
           "filters_locked": {
@@ -1675,14 +1692,7 @@ window.nomadEnv = {
         }
       }
     },
-    "example_uploads": {
-      "include": null,
-      "exclude": null
-    },
-    "default_unit_system": "Custom",
-    "north_enabled": false,
-    "theme": {
-      "title": "NOMAD"
-    }
+    "north": {},
+    "example_uploads": {}
   }
 }
diff --git a/gui/src/components/UserdataPage.js b/gui/src/components/UserdataPage.js
index 96dc315d932750bff3fd2ff2569901f0d7df18c0..250acce78a8d1c774a42e964d24b3ca27065f49d 100644
--- a/gui/src/components/UserdataPage.js
+++ b/gui/src/components/UserdataPage.js
@@ -71,7 +71,7 @@ DOI, they will be redirected to a NOMAD view that shows the dataset and allows i
 Once you assigned a DOI to a dataset, no entries can be removed or added to the dataset.
 `
 
-const context = ui?.search_contexts?.options?.entries
+const context = ui?.apps?.options?.entries
 const initialFiltersLocked = {
   'visibility': 'user'
 }
diff --git a/gui/src/components/UserdataPage.spec.js b/gui/src/components/UserdataPage.spec.js
index fabe12118863ebeefdbe96e49ef655a0d0b8aaf0..01e1e6866ee61ec4fefdec57dae2c30a20935f97 100644
--- a/gui/src/components/UserdataPage.spec.js
+++ b/gui/src/components/UserdataPage.spec.js
@@ -24,7 +24,7 @@ import UserDatapage from './UserdataPage'
 import { minutes } from '../setupTests'
 
 test('renders user data search page correctly', async () => {
-  const context = ui.search_contexts.options.entries
+  const context = ui.apps.options.entries
   await startAPI('tests.states.search.search', 'tests/data/search/userdatapage', 'test', 'password')
   render(<UserDatapage />)
 
diff --git a/gui/src/components/dataset/DatasetPage.js b/gui/src/components/dataset/DatasetPage.js
index 19dea1ab06094d27a2874e5e940313a23116eb3e..2b1ed040cfa5900dadfb15de8b7effff88029532 100644
--- a/gui/src/components/dataset/DatasetPage.js
+++ b/gui/src/components/dataset/DatasetPage.js
@@ -30,7 +30,7 @@ This page allows you to **inspect** and **download** NOMAD datasets. It also all
 to explore a dataset with similar controls that the search page offers.
 `
 
-const context = ui?.search_contexts?.options?.entries
+const context = ui?.apps?.options?.entries
 const useStyles = makeStyles(theme => ({
   header: {
     display: 'flex',
diff --git a/gui/src/components/entry/EntryContext.js b/gui/src/components/entry/EntryContext.js
index 0232a75326d6101b4c74a714a2c38447d8fe6da0..12b3d9d8050189a3c1752c9723f2c6ee8ae6d59f 100644
--- a/gui/src/components/entry/EntryContext.js
+++ b/gui/src/components/entry/EntryContext.js
@@ -18,7 +18,6 @@
 
 import React, { useState, useMemo, useEffect, useContext } from 'react'
 import PropTypes from 'prop-types'
-import { cloneDeep } from 'lodash'
 import { useApi } from '../api'
 import { useDataStore, useEntryStoreObj } from '../DataStore'
 import { ui, apiBase} from '../../config'
@@ -90,7 +89,7 @@ export const useEntryContext = () => {
  * by different components.
  */
 const entryContext = React.createContext()
-export const EntryContext = React.memo(({entryId, overview, children}) => {
+export const EntryContext = React.memo(({entryId, cards, children}) => {
   const dataStore = useDataStore()
   dataStore.resetIfNeeded(entryId)
 
@@ -101,23 +100,18 @@ export const EntryContext = React.memo(({entryId, overview, children}) => {
   }, [dataStore, entryId])
 
   // Get the overview config
-  const finalOverview = useMemo(() => {
-    const tmpOverview = overview || ui?.entry_context?.overview
-    if (!tmpOverview) return {}
-    const finalOverview = cloneDeep(tmpOverview)
-    const options = (finalOverview.include || Object.keys(finalOverview.options))
-      .filter(key => !finalOverview?.exclude?.includes(key))
-      .map(key => ({key, ...finalOverview.options[key]}))
-    return {options}
-  }, [overview])
+  const finalCards = useMemo(() => {
+    const finalCards = cards || ui?.entry?.cards
+    return finalCards || undefined
+  }, [cards])
 
-  const value = useMemo(() => ({entryId, overview: finalOverview}), [entryId, finalOverview])
+  const value = useMemo(() => ({entryId, cards: finalCards}), [entryId, finalCards])
   return <entryContext.Provider value={value}>
     {children}
   </entryContext.Provider>
 })
 EntryContext.propTypes = {
   entryId: PropTypes.string,
-  overview: PropTypes.object,
+  cards: PropTypes.object,
   children: PropTypes.node
 }
diff --git a/gui/src/components/entry/OverviewView.js b/gui/src/components/entry/OverviewView.js
index 54dc15c6312d3d164792f1365ac18a18723e0e3e..3109f38870fb3ee9fab81bde0539ae47db738b7a 100644
--- a/gui/src/components/entry/OverviewView.js
+++ b/gui/src/components/entry/OverviewView.js
@@ -119,7 +119,7 @@ const required = {
 }
 
 const OverviewView = React.memo(() => {
-  const { overview } = useEntryContext()
+  const { cards } = useEntryContext()
   const { data: index, response: indexApiData } = useIndex()
   const { url, exists, editable, archive: archiveTmp, archiveApiData } = useEntryStore(required)
 
@@ -162,7 +162,7 @@ const OverviewView = React.memo(() => {
   }, [archive, dataMetainfoDef, setSections, raiseError])
 
   // Determine the cards to show
-  const cards = useMemo(() => {
+  const cardComps = useMemo(() => {
     if (!exists || !index) return []
     const cardMap = {
       definitions: DefinitionsCard,
@@ -184,13 +184,13 @@ const OverviewView = React.memo(() => {
       relatedResources: RelatedResourcesCard
     }
 
-    if (isEmpty(overview?.options)) {
+    if (isEmpty(cards?.options)) {
       return <Alert severity="warning">
         No overview cards defined in the entry context. Ensure that all GUI artifacts are created.
       </Alert>
     }
 
-    return overview.options.map((option) => {
+    return Object.values(cards.options).map((option) => {
       let comp = null
       const msg = option.error || "Could not render card."
       const key = option.key
@@ -217,7 +217,7 @@ const OverviewView = React.memo(() => {
       }
       return comp
     })
-  }, [exists, index, overview, sections, editable, archive, properties])
+  }, [exists, index, cards, sections, editable, archive, properties])
 
   if (!exists) {
     return <Page>
@@ -280,18 +280,12 @@ const OverviewView = React.memo(() => {
             <ArchiveDeleteButton />
           </Box>
         )}
-        {cards}
+        {cardComps}
       </Grid>
     </Grid>
   </Page>
 })
 
-OverviewView.propTypes = {
-  url: PropTypes.string,
-  editable: PropTypes.bool,
-  exists: PropTypes.bool
-}
-
 OverviewView.whyDidYouRender = true
 
 export default OverviewView
diff --git a/gui/src/components/entry/OverviewView.spec.js b/gui/src/components/entry/OverviewView.spec.js
index a4f3c6cc45915bdbf075dbaad5a1c64d7d3d3a22..0e370de2019777730bb11f94f83d587ec5318bce 100644
--- a/gui/src/components/entry/OverviewView.spec.js
+++ b/gui/src/components/entry/OverviewView.spec.js
@@ -32,7 +32,6 @@ import { ui } from '../../config'
 import { EntryContext } from './EntryContext'
 import userEvent from '@testing-library/user-event'
 import { act } from 'react-dom/test-utils'
-import { cloneDeep } from 'lodash'
 
 test.each([
   ['material', 'material', 'Material'],
@@ -43,17 +42,16 @@ test.each([
   ['structural', 'rdf', 'Structural properties']
 ])('correctly renders %s card when card is enabled/disabled in config', async (card, state, label) => {
   await startAPI(`tests.states.entry.${state}`, `tests/data/entry/${card}_card`)
-  const overview = cloneDeep(ui.entry_context.overview)
   for (const enabled of [true, false]) {
-    overview.exclude = enabled ? [] : [card]
+    const cards = enabled ? {options: {card: ui.entry.cards.options[card]}} : {options: {}}
     render(
-      <EntryContext entryId={'dft_bulk'} overview={overview}>
+      <EntryContext entryId={'dft_bulk'} cards={cards}>
         <OverviewView/>
       </EntryContext>
     )
 
     // Wait until initial render is done.
-    const firstLabel = 'Entry References'
+    const firstLabel = 'Metadata'
     expect(await screen.findByText(firstLabel))
 
     // Check that the correct sections are shown
diff --git a/gui/src/components/nav/Routes.js b/gui/src/components/nav/Routes.js
index f414c92adbd6c16e3a557a3115776327f5384ed5..24f7007eb27fb6bf8d6d79a3b611f9912eb1889b 100644
--- a/gui/src/components/nav/Routes.js
+++ b/gui/src/components/nav/Routes.js
@@ -181,46 +181,41 @@ const toolkitRoute = (!oasis && aitoolkitEnabled)
     tooltip: 'Visit the NOMAD Artificial Intelligence Analytics Toolkit'
   }
 
-const include = ui.search_contexts.include || Object.keys(ui.search_contexts.options)
-const searchRoutes = include
-  ? include
-    .filter(key => !ui?.search_contexts?.exclude?.includes(key))
-    .map(key => {
-      const context = ui.search_contexts.options[key]
-      const routeMap = {
-        entries: entryRoutes
-      }
-      return {
-        path: context.path,
-        exact: true,
-        cache: 'always',
-        menu: context.label,
-        tooltip: context.description,
-        breadcrumb: context.breadcrumb,
-        category: context.category,
-        render: (props) => (
-          <SearchContext
-            {...props}
-            resource={context.resource}
-            initialPagination={context.pagination}
-            initialColumns={context.columns}
-            initialRows={context.rows}
-            initialFilterMenus={context.filter_menus}
-            initialFilters={context?.filters}
-            initialFiltersLocked={context.filters_locked}
-            initialDashboard={context?.dashboard}
-          >
-            <SearchPage/>
-          </SearchContext>
-        ),
-        help: {
-          title: context.help?.title,
-          content: context.help?.content
-        },
-        routes: routeMap[context.resource]
-      }
-    })
-  : []
+const searchRoutes = Object.values(ui?.apps?.options || {})
+  .map((context) => {
+    const routeMap = {
+      entries: entryRoutes
+    }
+    return {
+      path: context.path,
+      exact: true,
+      cache: 'always',
+      menu: context.label,
+      tooltip: context.description,
+      breadcrumb: context.breadcrumb,
+      category: context.category,
+      render: (props) => (
+        <SearchContext
+          {...props}
+          resource={context.resource}
+          initialPagination={context.pagination}
+          initialColumns={context.columns}
+          initialRows={context.rows}
+          initialFilterMenus={context.filter_menus}
+          initialFilters={context?.filters}
+          initialFiltersLocked={context.filters_locked}
+          initialDashboard={context?.dashboard}
+        >
+          <SearchPage/>
+        </SearchContext>
+      ),
+      help: {
+        title: context.help?.title,
+        content: context.help?.content
+      },
+      routes: routeMap[context.resource]
+    }
+  })
 
 /**
  * The list with all routes. This is used to determine the routes for routing, the breadcrumbs,
@@ -315,7 +310,7 @@ export const routes = [
         title: 'Artificial Intelligence Toolkit',
         component: ReproducePage
       },
-      ...(ui.north_enabled ? [
+      ...(ui?.north?.enabled ? [
         {
           path: 'north',
           menu: 'NOMAD Remote Tools Hub',
diff --git a/gui/src/components/north/NorthPage.js b/gui/src/components/north/NorthPage.js
index 8a8649900ad4e5a13a2dd9ab85d3026c2d1486e6..3707c8063074e8cd5a93c9699f19e41066f911c3 100644
--- a/gui/src/components/north/NorthPage.js
+++ b/gui/src/components/north/NorthPage.js
@@ -205,7 +205,7 @@ export default withLoginRequired(NorthPage)
  * Hook for loading the list of available tools from the NORTH API.
 */
 export function useTools() {
-  if (!ui.north_enabled) {
+  if (!ui?.north?.enabled) {
     return []
   }
   return _tools.default
diff --git a/gui/src/components/search/FilterRegistry.js b/gui/src/components/search/FilterRegistry.js
index 6320a16b7cb8f5d7405bd65bfec55d10119ddb3a..f14b62153af8983b7c5d2a4a238b7f7b8a30e552 100644
--- a/gui/src/components/search/FilterRegistry.js
+++ b/gui/src/components/search/FilterRegistry.js
@@ -229,7 +229,7 @@ function registerFilterOptions(name, group, target, label, description, options)
 const histogramWidgetConfig = {
   type: 'histogram',
   scale: 'linear',
-  inputfields: false,
+  showinput: false,
   autorange: false,
   nbins: 30,
   layout: {
@@ -244,7 +244,7 @@ const histogramWidgetConfig = {
 const termsWidgetConfig = {
   type: 'terms',
   scale: 'linear',
-  inputfields: false,
+  showinput: false,
   layout: {
     sm: {w: 6, h: 9, minW: 6, minH: 9},
     md: {w: 6, h: 9, minW: 6, minH: 9},
diff --git a/gui/src/components/search/SearchContext.js b/gui/src/components/search/SearchContext.js
index 0ec5b57f5e591dec64bfb2b7e1803cd90530bfa7..d8d66b7213fa97aaf3f607a1c15ae9a8c0fdc528 100644
--- a/gui/src/components/search/SearchContext.js
+++ b/gui/src/components/search/SearchContext.js
@@ -46,7 +46,16 @@ import { v4 as uuidv4 } from 'uuid'
 import PropTypes from 'prop-types'
 import { useHistory } from 'react-router-dom'
 import { useApi } from '../api'
-import { setToArray, authorList, entryName, entryType, formatTimestamp, getDeep, formatNumber, getDatatype } from '../../utils'
+import {
+  setToArray,
+  authorList,
+  entryName,
+  entryType,
+  formatTimestamp,
+  getDeep,
+  formatNumber,
+  getDatatype
+} from '../../utils'
 import { Quantity, Unit } from '../../units'
 import { useErrors } from '../errors'
 import { combinePagination, addColumnDefaults } from '../datatable/Datatable'
@@ -124,19 +133,14 @@ export const SearchContext = React.memo(({
   const indexFilters = useRef(0)
   const indexLocked = useRef(0)
 
-  // The final filtered set of columns
+  // The final set of columns
   const columns = useMemo(() => {
     if (!initialColumns) return undefined
-    const columns = cloneDeep(initialColumns)
-    const include = columns.include || (columns.options && Object.keys(columns.options))
-    let options = include
-      ? include
-        .filter(key => !columns?.exclude?.includes(key))
-        .map(key => ({key, ...columns.options[key]}))
-      : []
 
     // Add unit information if one is defined. This unit is currently fixed and
     // not affected by global unit system.
+    const config = cloneDeep(initialColumns)
+    let options = config?.options ? Object.values(config.options) : []
     options.forEach(option => {
       const unit = option.unit
       if (unit) {
@@ -145,7 +149,7 @@ export const SearchContext = React.memo(({
       }
     })
 
-    // Determine the final render function
+    // Automatically determine the render function based on metainfo.
     options.forEach(option => {
       option.render = (data) => {
         const value = getDeep(data, option.key)
@@ -171,7 +175,8 @@ export const SearchContext = React.memo(({
       }
     })
 
-    // Custom render is used for a subset of columns.
+    // Custom render and other setting overrides ared used for a subset of
+    // columns.
     const overrides = {
       entry_name: {
         render: entryName
@@ -219,56 +224,38 @@ export const SearchContext = React.memo(({
       }
     }
 
-    addColumnDefaults(options)
-    options = options.map(
-      option => ({...option, ...(overrides[option.key] || {})})
-    )
-
-    if (columns.enable) {
-      // sort the options following the enabled array
+    // Sort options by putting initially selected ones on top
+    if (config.selected) {
       options = [
-        ...columns.enable.map(key => options.find(opt => opt.key === key)).filter(opt => !!opt),
-        ...options.filter(opt => !columns.enable.find(key => key === opt.key))
+        ...config.selected.map(key => options.find(opt => opt.key === key)).filter(opt => !!opt),
+        ...options.filter(opt => !config.selected.find(key => key === opt.key))
       ]
     }
 
-    return {
-      options,
-      enable: columns.enable
-    }
+    // Add defaults and custom overrides to the options
+    addColumnDefaults(options)
+    config.options = Object.fromEntries(options.map(option => {
+      return [option.key, {...option, ...(overrides[option.key] || {})}]
+    }))
+
+    return config
   }, [initialColumns])
 
-  // The final row setup
+  // The final row configuration
   const rows = useMemo(() => {
     return initialRows || undefined
   }, [initialRows])
 
-  // The final filtered set of menus
+  // The final menu configuration
   const filterMenus = useMemo(() => {
-    const filterMenus = cloneDeep(initialFilterMenus)
-    const include = filterMenus?.include || (filterMenus?.options && Object.keys(filterMenus.options))
-    return include
-      ? include
-        .filter(key => !filterMenus?.exclude?.includes(key))
-        .map(key => {
-          const data = filterMenus.options[key]
-          const actions = data?.actions
-          if (actions) {
-            const include = actions.include || (actions?.options && Object.keys(actions.options))
-            data.actions = include
-              ? include
-                .filter(action => !actions?.exclude?.includes(action))
-                .map(action => ({key, ...actions.options[action]}))
-              : undefined
-          }
-          return {key, ...data}
-        })
-      : undefined
+    return initialFilterMenus || undefined
   }, [initialFilterMenus])
 
-  // The initial dashboard configuration
+  // The final dashboard configuration
   const dashboard = useMemo(() => {
-    return {initialDashboard, widgets: getWidgetsObject(initialDashboard?.widgets || [])}
+    return initialDashboard
+      ? {...initialDashboard, widgets: getWidgetsObject(initialDashboard?.widgets || [])}
+      : undefined
   }, [initialDashboard])
 
   // Initialize the set of available filters. This may depend on the resource.
@@ -456,7 +443,7 @@ export const SearchContext = React.memo(({
 
     const widgetIds = atom({
       key: `widgetIds_${contextID}`,
-      default: [...Object.keys(dashboard?.widgets)]
+      default: [...Object.keys(dashboard?.widgets || {})]
     })
 
     // Used to get/set the widgets configuration
diff --git a/gui/src/components/search/SearchPage.spec.js b/gui/src/components/search/SearchPage.spec.js
index 0df3ef98a7e4fd28ea5020c75c7a7783bd77a59a..48cac7cfcb0c66b1d6f4dc6f6b996849f123665d 100644
--- a/gui/src/components/search/SearchPage.spec.js
+++ b/gui/src/components/search/SearchPage.spec.js
@@ -31,7 +31,7 @@ describe('', () => {
   afterAll(() => closeAPI())
 
   test.each(
-    Object.entries(ui.search_contexts.options)
+    Object.entries(ui.apps.options)
   )('renders search page correctly, context: %s', async (key, context) => {
     render(
       <SearchContext
diff --git a/gui/src/components/search/SearchResults.js b/gui/src/components/search/SearchResults.js
index 9397da772b8f7372aee1c7c5c656f5187817af8f..60a7991220b72f18f9290a23f41accf7a9e3e13a 100644
--- a/gui/src/components/search/SearchResults.js
+++ b/gui/src/components/search/SearchResults.js
@@ -30,6 +30,7 @@ import EntryDownloadButton from '../entry/EntryDownloadButton'
 import EntryDetails, { EntryRowActions } from '../entry/EntryDetails'
 import { MaterialRowActions } from '../material/MaterialDetails'
 import { pluralize, formatInteger } from '../../utils'
+import { isEmpty } from 'lodash'
 import { useSearchContext } from './SearchContext'
 
 /**
@@ -55,7 +56,7 @@ const SearchResults = React.memo(function SearchResults(props) {
     return {entry_id: [...selected]}
   }, [selected, searchQuery])
 
-  if (!columns) {
+  if (isEmpty(columns)) {
     return <Alert severity="warning">
       No search columns defined within this search context. Ensure that all GUI artifacts are created.
     </Alert>
@@ -86,23 +87,23 @@ const SearchResults = React.memo(function SearchResults(props) {
       data={data}
       pagination={pagination}
       onPaginationChanged={setPagination}
-      columns={columns?.options}
-      shownColumns={columns?.enable}
-      selected={rows?.selection?.enable ? selected : undefined}
+      columns={columns?.options && Object.values(columns.options)}
+      shownColumns={columns?.selected}
+      selected={rows?.selection?.enabled ? selected : undefined}
       getId={option => option.entry_id}
-      onSelectedChanged={rows?.selection?.enable ? setSelected : undefined}
+      onSelectedChanged={rows?.selection?.enabled ? setSelected : undefined}
       {...otherProps}
     >
       <DatatableToolbar title={`${formatInteger(data.length)}/${pluralize('result', pagination.total, true, true, 'search')}`}>
-        {rows?.selection?.enable &&
+        {rows?.selection?.enabled &&
           <DatatableToolbarActions selection>
             {buttons}
           </DatatableToolbarActions>
         }
       </DatatableToolbar>
       <DatatableTable
-        actions={rows?.actions?.enable ? actions : undefined}
-        details={rows?.details?.enable ? details : undefined}
+        actions={rows?.actions?.enabled ? actions : undefined}
+        details={rows?.details?.enabled ? details : undefined}
         defaultUncollapsedRow={defaultUncollapsedEntryID && data.find(row => row.entry_id === defaultUncollapsedEntryID)}
       >
         <DatatableLoadMorePagination color="primary">load more</DatatableLoadMorePagination>
diff --git a/gui/src/components/search/conftest.spec.js b/gui/src/components/search/conftest.spec.js
index 0efb39c35c0918e5449acacc59fa62556fcfc0f5..686b11d1b5adc7db8b65b9a18c86f5831a94aee1 100644
--- a/gui/src/components/search/conftest.spec.js
+++ b/gui/src/components/search/conftest.spec.js
@@ -29,6 +29,7 @@ import { filterData } from './FilterRegistry'
 import { format } from 'date-fns'
 import { DType } from '../../utils'
 import { Unit, unitSystems } from '../../units'
+import { menuMap } from './menus/FilterMainMenu'
 
 /*****************************************************************************/
 // Renders
@@ -284,7 +285,7 @@ export async function expectFilterMainMenu(context, root = screen) {
 
     // Check that clicking the menu items with a submenu opens up the menu
     for (const menuItem of menuItems) {
-      if (menuItem.menu_items) {
+      if (menuMap[menuItem.key]) {
         const labelMenu = screen.getByTestId(`menu-item-label-${menuItem.key}`)
         const labelSubMenu = await screen.findByTestId(`filter-menu-header-${menuItem.key}`)
         expect(labelSubMenu).not.toBeVisible()
@@ -310,7 +311,7 @@ export async function expectSearchResults(context, root = screen) {
 
     // Check that correct columns are displayed
     const columnConfig = context.columns
-    const columnLabels = columnConfig.enable.map(key => {
+    const columnLabels = columnConfig.selected.map(key => {
       const config = columnConfig.options[key]
       const unit = config.unit
       const label = config.label
diff --git a/gui/src/components/search/input/InputRange.js b/gui/src/components/search/input/InputRange.js
index 301e934d752e12ff500e56a7a47a88e9a041281d..af3ea3bd09a821606039c1b234830a9f3a1c8bec 100644
--- a/gui/src/components/search/input/InputRange.js
+++ b/gui/src/components/search/input/InputRange.js
@@ -112,7 +112,7 @@ export const Range = React.memo(({
   nBins,
   disableHistogram,
   autorange,
-  inputfields,
+  showinput,
   aggId,
   className,
   classes,
@@ -637,7 +637,7 @@ export const Range = React.memo(({
             data-testid={`${testID}-histogram`}
           />
         }
-        {inputfields && <div className={styles.row}>
+        {showinput && <div className={styles.row}>
           {inputMinField}
           {disableHistogram && !isTime
             ? <div className={styles.spacer}>
@@ -679,7 +679,7 @@ Range.propTypes = {
   /* Set the range automatically according to data. */
   autorange: PropTypes.bool,
   /* Show the input fields for min and max value */
-  inputfields: PropTypes.bool,
+  showinput: PropTypes.bool,
   aggId: PropTypes.string,
   className: PropTypes.string,
   classes: PropTypes.object,
@@ -757,7 +757,7 @@ const InputRange = React.memo(({
       nBins={nBins}
       disableHistogram={disableHistogram}
       autorange={autorange}
-      inputfields
+      showinput
       aggId={aggId}
       classes={{histogram: styles.histogram}}
       data-testid={testID}
diff --git a/gui/src/components/search/menus/FilterMainMenu.js b/gui/src/components/search/menus/FilterMainMenu.js
index c34459c443ad132b1c87db5fc3ee17cdcba36af2..9a82ad9c03c5747bfd98f400634ec43ffd65e923 100644
--- a/gui/src/components/search/menus/FilterMainMenu.js
+++ b/gui/src/components/search/menus/FilterMainMenu.js
@@ -49,7 +49,7 @@ import FilterSubMenuGeometryOptimization from './FilterSubMenuGeometryOptimizati
 import InputCheckbox from '../input/InputCheckbox'
 import FilterSubMenuCustomQuantities from './FilterSubMenuCustomQuantities'
 
-const menuMap = {
+export const menuMap = {
   elements: FilterSubMenuElements,
   structure: FilterSubMenuStructure,
   method: FilterSubMenuMethod,
@@ -99,15 +99,15 @@ const FilterMainMenu = React.memo(({
 
   // The shown menu items
   const menuItems = useMemo(() => {
-    return filterMenus
-     ? filterMenus.map(option => {
+    return filterMenus?.options
+     ? Object.values(filterMenus.options).map(option => {
         return <FilterMenuItem
           key={option.key}
           id={option.key}
           label={option.label}
           level={option.level}
           disableButton={!has(menuMap, option.key)}
-          actions={option?.actions && option.actions
+          actions={option?.actions?.options && Object.values(option.actions.options)
             .map((action) => {
               const content = action.type === 'checkbox'
                 ? <InputCheckbox
@@ -129,9 +129,9 @@ const FilterMainMenu = React.memo(({
 
   // The shown submenus
   const subMenus = useMemo(() => {
-    return filterMenus
-      ? filterMenus
-        .filter(option => option.menu_items)
+    return filterMenus?.options
+      ? Object.values(filterMenus.options)
+        .filter(option => menuMap[option.key])
         .map(option => {
           const Comp = menuMap[option.key]
           return <Comp
diff --git a/gui/src/components/search/menus/FilterMenu.js b/gui/src/components/search/menus/FilterMenu.js
index 1396449df76166eff134b1b3e6f5026aa362fe91..9b87f275bf6b51f99e0b894de507f1b5423170dc 100644
--- a/gui/src/components/search/menus/FilterMenu.js
+++ b/gui/src/components/search/menus/FilterMenu.js
@@ -313,10 +313,7 @@ export const FilterMenuItems = React.memo(({
   return <div className={clsx(className, styles.root)}>
     <div className={clsx(styles.menu, open && styles.menuBorder, collapsed && styles.hidden)}>
       <div className={styles.container}>
-        <FilterMenuTopHeader
-          // title={`${resource} search`}
-          // actions={!collapsed && }
-        />
+        <FilterMenuTopHeader/>
         <FilterMenuHeader
           title="Filters"
           actions={<>
diff --git a/gui/src/components/search/widgets/WidgetHistogram.js b/gui/src/components/search/widgets/WidgetHistogram.js
index 02680923e6c7c8d8d2da13942307d7e572d34c66..ff6d1b8cd9e05141d764d0caf9ee1fbce6ece595 100644
--- a/gui/src/components/search/widgets/WidgetHistogram.js
+++ b/gui/src/components/search/widgets/WidgetHistogram.js
@@ -46,7 +46,7 @@ export const WidgetHistogram = React.memo((
   nbins,
   scale,
   autorange,
-  inputfields,
+  showinput,
   className
 }) => {
   const { useSetWidget } = useSearchContext()
@@ -89,7 +89,7 @@ export const WidgetHistogram = React.memo((
       scale={scale}
       anchored={true}
       autorange={autorange}
-      inputfields={inputfields}
+      showinput={showinput}
       disableHistogram={false}
       aggId={id}
     />
@@ -104,7 +104,7 @@ WidgetHistogram.propTypes = {
   nbins: PropTypes.number,
   scale: PropTypes.string,
   autorange: PropTypes.bool,
-  inputfields: PropTypes.bool,
+  showinput: PropTypes.bool,
   className: PropTypes.string
 }
 
@@ -214,7 +214,7 @@ export const WidgetHistogramEdit = React.memo((props) => {
         </WidgetEditOption>
         <WidgetEditOption>
           <FormControlLabel
-            control={<Checkbox checked={settings.inputfields} onChange={(event, value) => handleChange('inputfields', value)}/>}
+            control={<Checkbox checked={settings.showinput} onChange={(event, value) => handleChange('showinput', value)}/>}
             label='Show input fields'
           />
         </WidgetEditOption>
@@ -230,7 +230,7 @@ WidgetHistogramEdit.propTypes = {
   scale: PropTypes.string,
   nbins: PropTypes.number,
   autorange: PropTypes.bool,
-  inputfields: PropTypes.bool,
+  showinput: PropTypes.bool,
   onClose: PropTypes.func
 }
 
@@ -239,5 +239,5 @@ export const schemaWidgetHistogram = schemaWidget.shape({
   scale: string().required('Scale is required.'),
   nbins: number().integer().required(),
   autorange: bool(),
-  inputfields: bool()
+  showinput: bool()
 })
diff --git a/gui/src/components/search/widgets/WidgetTerms.js b/gui/src/components/search/widgets/WidgetTerms.js
index 21eb2da5166f9685028b7562d61918357582013a..0b32620bfa58e5d4e89cf8a90e28bbde154d6bc8 100644
--- a/gui/src/components/search/widgets/WidgetTerms.js
+++ b/gui/src/components/search/widgets/WidgetTerms.js
@@ -99,7 +99,7 @@ export const WidgetTerms = React.memo((
   description,
   quantity,
   scale,
-  inputfields,
+  showinput,
   className,
   'data-testid': testID
 }) => {
@@ -192,7 +192,7 @@ export const WidgetTerms = React.memo((
     <InputTooltip>
       <div className={clsx(styles.outerContainer)}>
         <div className={clsx(styles.innerContainer)}>
-          {inputfields
+          {showinput
             ? <InputTextQuantity
                 className={styles.textField}
                 quantity={quantity}
@@ -229,7 +229,7 @@ WidgetTerms.propTypes = {
   nbins: PropTypes.number,
   scale: PropTypes.string,
   autorange: PropTypes.bool,
-  inputfields: PropTypes.bool,
+  showinput: PropTypes.bool,
   className: PropTypes.string,
   'data-testid': PropTypes.string
 }
@@ -322,7 +322,7 @@ export const WidgetTermsEdit = React.memo((props) => {
       <WidgetEditGroup title="general">
         <WidgetEditOption>
           <FormControlLabel
-            control={<Checkbox checked={settings.inputfields} onChange={(event, value) => handleChange('inputfields', value)}/>}
+            control={<Checkbox checked={settings.showinput} onChange={(event, value) => handleChange('showinput', value)}/>}
             label='Show input field'
           />
         </WidgetEditOption>
@@ -338,12 +338,12 @@ WidgetTermsEdit.propTypes = {
   scale: PropTypes.string,
   nbins: PropTypes.number,
   autorange: PropTypes.bool,
-  inputfields: PropTypes.bool,
+  showinput: PropTypes.bool,
   onClose: PropTypes.func
 }
 
 export const schemaWidgetTerms = schemaWidget.shape({
   quantity: string().required('Quantity is required.'),
   scale: string().required('Scale is required.'),
-  inputfields: bool()
+  showinput: bool()
 })
diff --git a/gui/src/components/uploads/SectionSelectDialog.js b/gui/src/components/uploads/SectionSelectDialog.js
index 19c4316ad93042b6e0d4a1b05b637846e382043b..6ef85b20cf2825d8ce8b4c51e036448ece5a2037 100644
--- a/gui/src/components/uploads/SectionSelectDialog.js
+++ b/gui/src/components/uploads/SectionSelectDialog.js
@@ -40,9 +40,9 @@ import { cloneDeep } from 'lodash'
 import {getItemLabelKey} from '../archive/ArchiveBrowser'
 
 const searchDialogContext = React.createContext()
-const context = cloneDeep(ui?.search_contexts?.options?.entries)
+const context = cloneDeep(ui?.apps?.options?.entries)
 
-const allFilters = new Set(filterGroups && (context?.filter_menus?.include || Object.keys(context?.filter_menus?.options))
+const allFilters = new Set(filterGroups && (Object.keys(context?.filter_menus?.options))
   .map(filter => {
     const group = filterGroups?.[filter]
     return group ? Array.from(group) : []
@@ -319,13 +319,11 @@ function SectionSelectDialog(props) {
   const {open, onSelectedChanged, selected, onCancel, filtersLocked} = props
   const columns = context?.columns
   const rows = context?.rows
-  columns['enable'] = shownColumns
-  rows['details'] = {enable: true, render: Details}
-  rows['actions'] = {enable: false}
+  columns.selected = shownColumns
+  rows.details = {enabled: true, render: Details}
+  rows.actions = {enabled: false}
 
-  if (!open) {
-    return null
-  }
+  if (!open) return null
 
   return <SearchContext
     resource={context?.resource}
diff --git a/gui/src/config.js b/gui/src/config.js
index 90d46f34139a2895b11e809f55b6c1183dd72fb9..b4ed0d6aa023ece572089daa42a69267ea766467 100644
--- a/gui/src/config.js
+++ b/gui/src/config.js
@@ -16,6 +16,7 @@
  * limitations under the License.
  */
 import { createTheme } from '@material-ui/core'
+import { isPlainObject, isNil } from 'lodash'
 
 /**
  * Used to normalized the given URL into an absolute form which starts with
@@ -40,13 +41,53 @@ import { createTheme } from '@material-ui/core'
   return absUrl
 }
 
+/**
+ * Returns a normalized version of an UI config model. The following changes are applied
+ * in the normalized version:
+ *
+ *  - The 'include' and 'exclude' attributes have been used to filter and order the
+ *    options object and the key is automatically included as an option property.
+ *
+ * @param {object} config The original UI config.
+ * @return Normalized version of the UI config model.
+ */
+export function normalizeConfig(config) {
+    if (isNil(config)) return config
+
+    function normalize(obj) {
+      for (const [key, value] of Object.entries(obj)) {
+        if (isPlainObject(value)) {
+          normalize(value)
+
+          // Normalize the options
+          if (!value.options) continue
+          const include = value.include || (value.options && Object.keys(value.options))
+          const options = include
+            ? include
+              .filter(key => !value?.exclude?.includes(key))
+              .map(key => ({key, ...value.options[key]}))
+            : []
+
+          const config = {options: Object.fromEntries(options.map(option => [option.key, option]))}
+          if (value.selected) config.selected = value.selected
+          obj[key] = config
+        }
+      }
+    }
+
+    // Recursively normalize the config
+    normalize(config)
+
+    return config
+}
+
 window.nomadEnv = window.nomadEnv || {}
 export const version = window.nomadEnv.version
 export const appBase = urlAbs(window.nomadEnv.appBase.replace(/\/$/, ''))
 export const apiBase = `${appBase}/api`
 export const northBase = urlAbs(window.nomadEnv.northBase)
 export const guiBase = process.env.PUBLIC_URL
-export const ui = window.nomadEnv.ui
+export const ui = normalizeConfig(window.nomadEnv.ui)
 export const servicesUploadLimit = window.nomadEnv.servicesUploadLimit
 export const keycloakBase = window.nomadEnv.keycloakBase
 export const keycloakRealm = window.nomadEnv.keycloakRealm
diff --git a/gui/src/units.js b/gui/src/units.js
index 15fec802dadd48f371e08a57d1e1bbd68e22d7b3..d694b4b22c43c3a09a2df31d7d4c830654a419ed 100644
--- a/gui/src/units.js
+++ b/gui/src/units.js
@@ -194,7 +194,7 @@ for (const [systemName, system] of Object.entries(unitSystems)) {
 // A state containing the currently configured unit system.
 export const unitsState = atom({
   key: 'units',
-  default: unitSystems[ui.default_unit_system || 'Custom'] || unitSystems.Custom
+  default: unitSystems[ui?.unitsystems?.selected || 'Custom'] || unitSystems.Custom
 })
 
 /**
diff --git a/gui/tests/env.js b/gui/tests/env.js
index 423976188d36f294eeeb9a37e9d2b5ae24dcfa81..92b074ae6146829533c0b233ec02a0b4be45dcf2 100644
--- a/gui/tests/env.js
+++ b/gui/tests/env.js
@@ -12,8 +12,14 @@ window.nomadEnv = {
   "globalLoginRequired": false,
   "servicesUploadLimit": 10,
   "ui": {
-    "entry_context": {
-      "overview": {
+    "theme": {
+      "title": "NOMAD"
+    },
+    "unit_systems": {
+      "selected": "Custom"
+    },
+    "entry": {
+      "cards": {
         "exclude": [
           "relatedResources"
         ],
@@ -69,7 +75,7 @@ window.nomadEnv = {
         }
       }
     },
-    "search_contexts": {
+    "apps": {
       "options": {
         "entries": {
           "label": "Entries",
@@ -88,13 +94,6 @@ window.nomadEnv = {
             "page_size": 20
           },
           "columns": {
-            "enable": [
-              "entry_name",
-              "results.material.chemical_formula_hill",
-              "entry_type",
-              "upload_create_time",
-              "authors"
-            ],
             "options": {
               "entry_name": {
                 "label": "Name",
@@ -117,43 +116,56 @@ window.nomadEnv = {
                 "align": "left"
               },
               "results.method.method_name": {
-                "label": "Method name"
+                "label": "Method name",
+                "align": "left"
               },
               "results.method.simulation.program_name": {
-                "label": "Program name"
+                "label": "Program name",
+                "align": "left"
               },
               "results.method.simulation.dft.basis_set_name": {
-                "label": "Basis set name"
+                "label": "Basis set name",
+                "align": "left"
               },
               "results.method.simulation.dft.xc_functional_type": {
-                "label": "XC Functional Type"
+                "label": "XC Functional Type",
+                "align": "left"
               },
               "results.material.structural_type": {
-                "label": "Dimensionality"
+                "label": "Dimensionality",
+                "align": "left"
               },
               "results.material.symmetry.crystal_system": {
-                "label": "Crystal system"
+                "label": "Crystal system",
+                "align": "left"
               },
               "results.material.symmetry.space_group_symbol": {
-                "label": "Space group symbol"
+                "label": "Space group symbol",
+                "align": "left"
               },
               "results.material.symmetry.space_group_number": {
-                "label": "Space group number"
+                "label": "Space group number",
+                "align": "left"
               },
               "results.eln.lab_ids": {
-                "label": "Lab IDs"
+                "label": "Lab IDs",
+                "align": "left"
               },
               "results.eln.sections": {
-                "label": "Sections"
+                "label": "Sections",
+                "align": "left"
               },
               "results.eln.methods": {
-                "label": "Methods"
+                "label": "Methods",
+                "align": "left"
               },
               "results.eln.tags": {
-                "label": "Tags"
+                "label": "Tags",
+                "align": "left"
               },
               "results.eln.instruments": {
-                "label": "Instruments"
+                "label": "Instruments",
+                "align": "left"
               },
               "mainfile": {
                 "label": "Mainfile",
@@ -172,111 +184,110 @@ window.nomadEnv = {
                 "align": "left"
               },
               "published": {
-                "label": "Access"
+                "label": "Access",
+                "align": "left"
               }
-            }
+            },
+            "selected": [
+              "entry_name",
+              "results.material.chemical_formula_hill",
+              "entry_type",
+              "upload_create_time",
+              "authors"
+            ]
           },
           "rows": {
             "actions": {
-              "enable": true
+              "enabled": true
             },
             "details": {
-              "enable": true
+              "enabled": true
             },
             "selection": {
-              "enable": true
+              "enabled": true
             }
           },
           "filter_menus": {
             "options": {
               "material": {
                 "label": "Material",
-                "level": 0
+                "level": 0,
+                "size": "small"
               },
               "elements": {
                 "label": "Elements / Formula",
                 "level": 1,
-                "size": "large",
-                "menu_items": {}
+                "size": "large"
               },
               "structure": {
                 "label": "Structure",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "method": {
                 "label": "Method",
                 "level": 0,
-                "menu_items": {}
+                "size": "small"
               },
               "dft": {
                 "label": "DFT",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "gw": {
                 "label": "GW",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "projection": {
                 "label": "Projection",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "dmft": {
                 "label": "DMFT",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "eels": {
                 "label": "EELS",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "workflow": {
                 "label": "Workflow",
-                "level": 0
+                "level": 0,
+                "size": "small"
               },
               "molecular_dynamics": {
                 "label": "Molecular dynamics",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "geometry_optimization": {
                 "label": "Geometry Optimization",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "properties": {
                 "label": "Properties",
-                "level": 0
+                "level": 0,
+                "size": "small"
               },
               "electronic": {
                 "label": "Electronic",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "vibrational": {
                 "label": "Vibrational",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "mechanical": {
                 "label": "Mechanical",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "usecases": {
                 "label": "Use Cases",
@@ -286,26 +297,22 @@ window.nomadEnv = {
               "solarcell": {
                 "label": "Solar Cells",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "author": {
                 "label": "Author / Origin / Dataset",
                 "level": 0,
-                "size": "medium",
-                "menu_items": {}
+                "size": "medium"
               },
               "metadata": {
                 "label": "Visibility / IDs / Schema",
                 "level": 0,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "optimade": {
                 "label": "Optimade",
                 "level": 0,
-                "size": "medium",
-                "menu_items": {}
+                "size": "medium"
               }
             }
           },
@@ -334,23 +341,18 @@ window.nomadEnv = {
             "page_size": 20
           },
           "columns": {
-            "enable": [
-              "results.material.chemical_formula_hill",
-              "results.method.simulation.program_name",
-              "results.method.method_name",
-              "upload_create_time",
-              "authors"
-            ],
             "options": {
               "results.material.chemical_formula_hill": {
                 "label": "Formula",
                 "align": "left"
               },
               "results.method.simulation.program_name": {
-                "label": "Program name"
+                "label": "Program name",
+                "align": "left"
               },
               "results.method.method_name": {
-                "label": "Method name"
+                "label": "Method name",
+                "align": "left"
               },
               "upload_create_time": {
                 "label": "Upload time",
@@ -361,37 +363,48 @@ window.nomadEnv = {
                 "align": "left"
               },
               "results.method.simulation.dft.basis_set_name": {
-                "label": "Basis set name"
+                "label": "Basis set name",
+                "align": "left"
               },
               "results.method.simulation.dft.xc_functional_type": {
-                "label": "XC Functional Type"
+                "label": "XC Functional Type",
+                "align": "left"
               },
               "results.material.structural_type": {
-                "label": "Dimensionality"
+                "label": "Dimensionality",
+                "align": "left"
               },
               "results.material.symmetry.crystal_system": {
-                "label": "Crystal system"
+                "label": "Crystal system",
+                "align": "left"
               },
               "results.material.symmetry.space_group_symbol": {
-                "label": "Space group symbol"
+                "label": "Space group symbol",
+                "align": "left"
               },
               "results.material.symmetry.space_group_number": {
-                "label": "Space group number"
+                "label": "Space group number",
+                "align": "left"
               },
               "results.eln.lab_ids": {
-                "label": "Lab IDs"
+                "label": "Lab IDs",
+                "align": "left"
               },
               "results.eln.sections": {
-                "label": "Sections"
+                "label": "Sections",
+                "align": "left"
               },
               "results.eln.methods": {
-                "label": "Methods"
+                "label": "Methods",
+                "align": "left"
               },
               "results.eln.tags": {
-                "label": "Tags"
+                "label": "Tags",
+                "align": "left"
               },
               "results.eln.instruments": {
-                "label": "Instruments"
+                "label": "Instruments",
+                "align": "left"
               },
               "entry_name": {
                 "label": "Name",
@@ -418,123 +431,120 @@ window.nomadEnv = {
                 "align": "left"
               },
               "published": {
-                "label": "Access"
+                "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": {
-              "enable": true
+              "enabled": true
             },
             "details": {
-              "enable": true
+              "enabled": true
             },
             "selection": {
-              "enable": true
+              "enabled": true
             }
           },
           "filter_menus": {
             "options": {
               "material": {
                 "label": "Material",
-                "level": 0
+                "level": 0,
+                "size": "small"
               },
               "elements": {
                 "label": "Elements / Formula",
                 "level": 1,
-                "size": "large",
-                "menu_items": {}
+                "size": "large"
               },
               "structure": {
                 "label": "Structure",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "method": {
                 "label": "Method",
                 "level": 0,
-                "menu_items": {}
+                "size": "small"
               },
               "dft": {
                 "label": "DFT",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "gw": {
                 "label": "GW",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "projection": {
                 "label": "Projection",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "dmft": {
                 "label": "DMFT",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "workflow": {
                 "label": "Workflow",
-                "level": 0
+                "level": 0,
+                "size": "small"
               },
               "molecular_dynamics": {
                 "label": "Molecular dynamics",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "geometry_optimization": {
                 "label": "Geometry Optimization",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "properties": {
                 "label": "Properties",
-                "level": 0
+                "level": 0,
+                "size": "small"
               },
               "electronic": {
                 "label": "Electronic",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "vibrational": {
                 "label": "Vibrational",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "mechanical": {
                 "label": "Mechanical",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "author": {
                 "label": "Author / Origin / Dataset",
                 "level": 0,
-                "size": "medium",
-                "menu_items": {}
+                "size": "medium"
               },
               "metadata": {
                 "label": "Visibility / IDs / Schema",
                 "level": 0,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "optimade": {
                 "label": "Optimade",
                 "level": 0,
-                "size": "medium",
-                "menu_items": {}
+                "size": "medium"
               }
             }
           },
@@ -562,114 +572,115 @@ window.nomadEnv = {
           },
           "pagination": {
             "order_by": "chemical_formula_hill",
-            "order": "asc"
+            "order": "asc",
+            "page_size": 20
           },
           "columns": {
-            "enable": [
-              "chemical_formula_hill",
-              "structural_type",
-              "symmetry.structure_name",
-              "symmetry.space_group_number",
-              "symmetry.crystal_system"
-            ],
             "options": {
               "chemical_formula_hill": {
                 "label": "Formula",
                 "align": "left"
               },
               "structural_type": {
-                "label": "Dimensionality"
+                "label": "Dimensionality",
+                "align": "left"
               },
               "symmetry.structure_name": {
-                "label": "Structure name"
+                "label": "Structure name",
+                "align": "left"
               },
               "symmetry.space_group_number": {
-                "label": "Space group number"
+                "label": "Space group number",
+                "align": "left"
               },
               "symmetry.crystal_system": {
-                "label": "Crystal system"
+                "label": "Crystal system",
+                "align": "left"
               },
               "symmetry.space_group_symbol": {
-                "label": "Space group symbol"
+                "label": "Space group symbol",
+                "align": "left"
               },
               "material_id": {
-                "label": "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": {
-              "enable": true
+              "enabled": true
             },
             "details": {
-              "enable": false
+              "enabled": false
             },
             "selection": {
-              "enable": false
+              "enabled": false
             }
           },
           "filter_menus": {
             "options": {
               "material": {
                 "label": "Material",
-                "level": 0
+                "level": 0,
+                "size": "small"
               },
               "elements": {
                 "label": "Elements / Formula",
                 "level": 1,
-                "size": "large",
-                "menu_items": {}
+                "size": "large"
               },
               "structure": {
                 "label": "Structure",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "method": {
                 "label": "Method",
                 "level": 0,
-                "menu_items": {}
+                "size": "small"
               },
               "dft": {
                 "label": "DFT",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "gw": {
                 "label": "GW",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "projection": {
                 "label": "Projection",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "dmft": {
                 "label": "DMFT",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "workflow": {
                 "label": "Workflow",
-                "level": 0
+                "level": 0,
+                "size": "small"
               },
               "molecular_dynamics": {
                 "label": "Molecular dynamics",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "geometry_optimization": {
                 "label": "Geometry Optimization",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "properties": {
                 "label": "Properties",
@@ -679,40 +690,36 @@ window.nomadEnv = {
               "electronic": {
                 "label": "Electronic",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "vibrational": {
                 "label": "Vibrational",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "mechanical": {
                 "label": "Mechanical",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "author": {
                 "label": "Author / Origin / Dataset",
                 "level": 0,
-                "size": "medium",
-                "menu_items": {}
+                "size": "medium"
               },
               "metadata": {
                 "label": "Visibility / IDs / Schema",
                 "level": 0,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "optimade": {
                 "label": "Optimade",
                 "level": 0,
-                "size": "medium",
-                "menu_items": {}
+                "size": "medium"
               },
               "combine": {
+                "level": 0,
+                "size": "small",
                 "actions": {
                   "options": {
                     "combine": {
@@ -749,12 +756,6 @@ window.nomadEnv = {
             "page_size": 20
           },
           "columns": {
-            "enable": [
-              "entry_name",
-              "entry_type",
-              "upload_create_time",
-              "authors"
-            ],
             "options": {
               "entry_name": {
                 "label": "Name",
@@ -777,22 +778,28 @@ window.nomadEnv = {
                 "align": "left"
               },
               "results.method.method_name": {
-                "label": "Method name"
+                "label": "Method name",
+                "align": "left"
               },
               "results.eln.lab_ids": {
-                "label": "Lab IDs"
+                "label": "Lab IDs",
+                "align": "left"
               },
               "results.eln.sections": {
-                "label": "Sections"
+                "label": "Sections",
+                "align": "left"
               },
               "results.eln.methods": {
-                "label": "Methods"
+                "label": "Methods",
+                "align": "left"
               },
               "results.eln.tags": {
-                "label": "Tags"
+                "label": "Tags",
+                "align": "left"
               },
               "results.eln.instruments": {
-                "label": "Instruments"
+                "label": "Instruments",
+                "align": "left"
               },
               "mainfile": {
                 "label": "Mainfile",
@@ -811,62 +818,64 @@ window.nomadEnv = {
                 "align": "left"
               },
               "published": {
-                "label": "Access"
+                "label": "Access",
+                "align": "left"
               }
-            }
+            },
+            "selected": [
+              "entry_name",
+              "entry_type",
+              "upload_create_time",
+              "authors"
+            ]
           },
           "rows": {
             "actions": {
-              "enable": true
+              "enabled": true
             },
             "details": {
-              "enable": true
+              "enabled": true
             },
             "selection": {
-              "enable": true
+              "enabled": true
             }
           },
           "filter_menus": {
             "options": {
               "material": {
                 "label": "Material",
-                "level": 0
+                "level": 0,
+                "size": "small"
               },
               "elements": {
                 "label": "Elements / Formula",
                 "level": 1,
-                "size": "large",
-                "menu_items": {}
+                "size": "large"
               },
               "eln": {
                 "label": "Electronic Lab Notebook",
                 "level": 0,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "custom_quantities": {
                 "label": "User Defined Quantities",
                 "level": 0,
-                "size": "large",
-                "menu_items": {}
+                "size": "large"
               },
               "author": {
                 "label": "Author / Origin / Dataset",
                 "level": 0,
-                "size": "medium",
-                "menu_items": {}
+                "size": "medium"
               },
               "metadata": {
                 "label": "Visibility / IDs / Schema",
                 "level": 0,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "optimade": {
                 "label": "Optimade",
                 "level": 0,
-                "size": "medium",
-                "menu_items": {}
+                "size": "medium"
               }
             }
           },
@@ -898,23 +907,18 @@ window.nomadEnv = {
             "page_size": 20
           },
           "columns": {
-            "enable": [
-              "results.material.chemical_formula_hill",
-              "results.properties.spectroscopy.eels.detector_type",
-              "results.properties.spectroscopy.eels.resolution",
-              "upload_create_time",
-              "authors"
-            ],
             "options": {
               "results.material.chemical_formula_hill": {
                 "label": "Formula",
                 "align": "left"
               },
               "results.properties.spectroscopy.eels.detector_type": {
-                "label": "Detector type"
+                "label": "Detector type",
+                "align": "left"
               },
               "results.properties.spectroscopy.eels.resolution": {
-                "label": "Resolution"
+                "label": "Resolution",
+                "align": "left"
               },
               "upload_create_time": {
                 "label": "Upload time",
@@ -924,8 +928,12 @@ window.nomadEnv = {
                 "label": "Authors",
                 "align": "left"
               },
-              "results.properties.spectroscopy.eels.min_energy": {},
-              "results.properties.spectroscopy.eels.max_energy": {},
+              "results.properties.spectroscopy.eels.min_energy": {
+                "align": "left"
+              },
+              "results.properties.spectroscopy.eels.max_energy": {
+                "align": "left"
+              },
               "entry_name": {
                 "label": "Name",
                 "align": "left"
@@ -951,32 +959,40 @@ window.nomadEnv = {
                 "align": "left"
               },
               "published": {
-                "label": "Access"
+                "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": {
-              "enable": true
+              "enabled": true
             },
             "details": {
-              "enable": true
+              "enabled": true
             },
             "selection": {
-              "enable": true
+              "enabled": true
             }
           },
           "filter_menus": {
             "options": {
               "material": {
                 "label": "Material",
-                "level": 0
+                "level": 0,
+                "size": "small"
               },
               "elements": {
                 "label": "Elements / Formula",
                 "level": 1,
-                "size": "large",
-                "menu_items": {}
+                "size": "large"
               },
               "method": {
                 "label": "Method",
@@ -986,26 +1002,22 @@ window.nomadEnv = {
               "eels": {
                 "label": "EELS",
                 "level": 1,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "author": {
                 "label": "Author / Origin / Dataset",
                 "level": 0,
-                "size": "medium",
-                "menu_items": {}
+                "size": "medium"
               },
               "metadata": {
                 "label": "Visibility / IDs / Schema",
                 "level": 0,
-                "size": "small",
-                "menu_items": {}
+                "size": "small"
               },
               "optimade": {
                 "label": "Optimade",
                 "level": 0,
-                "size": "medium",
-                "menu_items": {}
+                "size": "medium"
               }
             }
           },
@@ -1036,637 +1048,642 @@ window.nomadEnv = {
             "order": "desc",
             "page_size": 20
           },
-          "dashboard": {
-            "widgets": [
-              {
-                "type": "periodictable",
-                "scale": "linear",
-                "quantity": "results.material.elements",
-                "layout": {
-                  "xxl": {
-                    "minH": 8,
-                    "minW": 12,
-                    "h": 8,
-                    "w": 13,
-                    "y": 0,
-                    "x": 0
-                  },
-                  "xl": {
-                    "minH": 8,
-                    "minW": 12,
-                    "h": 8,
-                    "w": 12,
-                    "y": 0,
-                    "x": 0
-                  },
-                  "lg": {
-                    "minH": 8,
-                    "minW": 12,
-                    "h": 8,
-                    "w": 12,
-                    "y": 0,
-                    "x": 0
-                  },
-                  "md": {
-                    "minH": 8,
-                    "minW": 12,
-                    "h": 8,
-                    "w": 12,
-                    "y": 0,
-                    "x": 0
-                  },
-                  "sm": {
-                    "minH": 8,
-                    "minW": 12,
-                    "h": 8,
-                    "w": 12,
-                    "y": 16,
-                    "x": 0
-                  }
+          "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": "small"
+              },
+              "elements": {
+                "label": "Elements / Formula",
+                "level": 1,
+                "size": "large"
+              },
+              "structure": {
+                "label": "Structure",
+                "level": 1,
+                "size": "small"
+              },
+              "electronic": {
+                "label": "Electronic Properties",
+                "level": 0,
+                "size": "small"
+              },
+              "solarcell": {
+                "label": "Solar Cell Properties",
+                "level": 0,
+                "size": "small"
+              },
+              "eln": {
+                "label": "Electronic Lab Notebook",
+                "level": 0,
+                "size": "small"
+              },
+              "custom_quantities": {
+                "label": "User Defined Quantities",
+                "level": 0,
+                "size": "large"
+              },
+              "author": {
+                "label": "Author / Origin / Dataset",
+                "level": 0,
+                "size": "medium"
+              },
+              "metadata": {
+                "label": "Visibility / IDs / Schema",
+                "level": 0,
+                "size": "small"
+              },
+              "optimade": {
+                "label": "Optimade",
+                "level": 0,
+                "size": "medium"
+              }
+            }
+          },
+          "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",
-                "autorange": true,
-                "size": 1000,
-                "color": "results.properties.optoelectronic.solar_cell.short_circuit_current_density",
-                "y": "results.properties.optoelectronic.solar_cell.efficiency",
-                "x": "results.properties.optoelectronic.solar_cell.open_circuit_voltage",
                 "layout": {
                   "xxl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 8,
                     "w": 12,
-                    "y": 0,
-                    "x": 24
+                    "x": 24,
+                    "y": 0
                   },
                   "xl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 8,
                     "w": 9,
-                    "y": 0,
-                    "x": 12
+                    "x": 12,
+                    "y": 0
                   },
                   "lg": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 12,
-                    "y": 8,
-                    "x": 0
+                    "x": 0,
+                    "y": 8
                   },
                   "md": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 9,
-                    "y": 8,
-                    "x": 0
+                    "x": 0,
+                    "y": 8
                   },
                   "sm": {
                     "minH": 3,
                     "minW": 3,
                     "h": 5,
                     "w": 6,
-                    "y": 0,
-                    "x": 0
+                    "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",
-                "autorange": true,
-                "size": 1000,
-                "color": "results.properties.optoelectronic.solar_cell.device_architecture",
-                "y": "results.properties.optoelectronic.solar_cell.efficiency",
-                "x": "results.properties.optoelectronic.solar_cell.open_circuit_voltage",
                 "layout": {
                   "xxl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 8,
                     "w": 11,
-                    "y": 0,
-                    "x": 13
+                    "x": 13,
+                    "y": 0
                   },
                   "xl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 8,
                     "w": 9,
-                    "y": 0,
-                    "x": 21
+                    "x": 21,
+                    "y": 0
                   },
                   "lg": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 12,
-                    "y": 14,
-                    "x": 0
+                    "x": 0,
+                    "y": 14
                   },
                   "md": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 9,
-                    "y": 8,
-                    "x": 9
+                    "x": 9,
+                    "y": 8
                   },
                   "sm": {
                     "minH": 3,
                     "minW": 3,
                     "h": 5,
                     "w": 6,
-                    "y": 0,
-                    "x": 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",
-                "inputfields": true,
-                "scale": "linear",
-                "quantity": "results.properties.optoelectronic.solar_cell.device_stack",
                 "layout": {
                   "xxl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 6,
-                    "y": 8,
-                    "x": 14
+                    "x": 14,
+                    "y": 8
                   },
                   "xl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 6,
-                    "y": 8,
-                    "x": 14
+                    "x": 14,
+                    "y": 8
                   },
                   "lg": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 6,
-                    "y": 0,
-                    "x": 12
+                    "x": 12,
+                    "y": 0
                   },
                   "md": {
                     "minH": 3,
                     "minW": 3,
                     "h": 4,
                     "w": 6,
-                    "y": 4,
-                    "x": 12
+                    "x": 12,
+                    "y": 4
                   },
                   "sm": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 4,
-                    "y": 10,
-                    "x": 0
+                    "x": 0,
+                    "y": 10
                   }
-                }
+                },
+                "quantity": "results.properties.optoelectronic.solar_cell.device_stack",
+                "scale": "linear",
+                "showinput": true
               },
               {
                 "type": "histogram",
-                "autorange": true,
-                "nbins": 30,
-                "scale": "1/4",
-                "quantity": "results.properties.optoelectronic.solar_cell.illumination_intensity",
                 "layout": {
                   "xxl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 3,
                     "w": 8,
-                    "y": 8,
-                    "x": 0
+                    "x": 0,
+                    "y": 8
                   },
                   "xl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 3,
                     "w": 8,
-                    "y": 11,
-                    "x": 0
+                    "x": 0,
+                    "y": 11
                   },
                   "lg": {
                     "minH": 3,
                     "minW": 3,
                     "h": 4,
                     "w": 12,
-                    "y": 12,
-                    "x": 12
+                    "x": 12,
+                    "y": 12
                   },
                   "md": {
                     "minH": 3,
                     "minW": 3,
                     "h": 3,
                     "w": 8,
-                    "y": 17,
-                    "x": 10
+                    "x": 10,
+                    "y": 17
                   },
                   "sm": {
                     "minH": 3,
                     "minW": 3,
                     "h": 3,
                     "w": 8,
-                    "y": 13,
-                    "x": 4
+                    "x": 4,
+                    "y": 13
                   }
-                }
+                },
+                "quantity": "results.properties.optoelectronic.solar_cell.illumination_intensity",
+                "scale": "1/4",
+                "autorange": true,
+                "showinput": true,
+                "nbins": 30
               },
               {
                 "type": "terms",
-                "inputfields": true,
-                "scale": "linear",
-                "quantity": "results.properties.optoelectronic.solar_cell.absorber_fabrication",
                 "layout": {
                   "xxl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 6,
-                    "y": 8,
-                    "x": 8
+                    "x": 8,
+                    "y": 8
                   },
                   "xl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 6,
-                    "y": 8,
-                    "x": 8
+                    "x": 8,
+                    "y": 8
                   },
                   "lg": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 6,
-                    "y": 0,
-                    "x": 18
+                    "x": 18,
+                    "y": 0
                   },
                   "md": {
                     "minH": 3,
                     "minW": 3,
                     "h": 4,
                     "w": 6,
-                    "y": 0,
-                    "x": 12
+                    "x": 12,
+                    "y": 0
                   },
                   "sm": {
                     "minH": 3,
                     "minW": 3,
                     "h": 5,
                     "w": 4,
-                    "y": 5,
-                    "x": 0
+                    "x": 0,
+                    "y": 5
                   }
-                }
+                },
+                "quantity": "results.properties.optoelectronic.solar_cell.absorber_fabrication",
+                "scale": "linear",
+                "showinput": true
               },
               {
                 "type": "histogram",
-                "inputfields": false,
-                "autorange": false,
-                "nbins": 30,
-                "scale": "1/4",
-                "quantity": "results.properties.electronic.band_structure_electronic.band_gap.value",
                 "layout": {
                   "xxl": {
                     "minH": 3,
                     "minW": 8,
                     "h": 3,
                     "w": 8,
-                    "y": 11,
-                    "x": 0
+                    "x": 0,
+                    "y": 11
                   },
                   "xl": {
                     "minH": 3,
                     "minW": 8,
                     "h": 3,
                     "w": 8,
-                    "y": 8,
-                    "x": 0
+                    "x": 0,
+                    "y": 8
                   },
                   "lg": {
                     "minH": 3,
                     "minW": 8,
                     "h": 4,
                     "w": 12,
-                    "y": 16,
-                    "x": 12
+                    "x": 12,
+                    "y": 16
                   },
                   "md": {
                     "minH": 3,
                     "minW": 8,
                     "h": 3,
                     "w": 8,
-                    "y": 14,
-                    "x": 10
+                    "x": 10,
+                    "y": 14
                   },
                   "sm": {
                     "minH": 3,
                     "minW": 8,
                     "h": 3,
                     "w": 8,
-                    "y": 10,
-                    "x": 4
+                    "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",
-                "inputfields": true,
-                "scale": "linear",
-                "quantity": "results.properties.optoelectronic.solar_cell.electron_transport_layer",
                 "layout": {
                   "xxl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 6,
-                    "y": 8,
-                    "x": 20
+                    "x": 20,
+                    "y": 8
                   },
                   "xl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 5,
-                    "y": 8,
-                    "x": 25
+                    "x": 25,
+                    "y": 8
                   },
                   "lg": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 6,
-                    "y": 6,
-                    "x": 18
+                    "x": 18,
+                    "y": 6
                   },
                   "md": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 5,
-                    "y": 14,
-                    "x": 0
+                    "x": 0,
+                    "y": 14
                   },
                   "sm": {
                     "minH": 3,
                     "minW": 3,
                     "h": 5,
                     "w": 4,
-                    "y": 5,
-                    "x": 4
+                    "x": 4,
+                    "y": 5
                   }
-                }
+                },
+                "quantity": "results.properties.optoelectronic.solar_cell.electron_transport_layer",
+                "scale": "linear",
+                "showinput": true
               },
               {
                 "type": "terms",
-                "inputfields": true,
-                "scale": "linear",
-                "quantity": "results.properties.optoelectronic.solar_cell.hole_transport_layer",
                 "layout": {
                   "xxl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 6,
-                    "y": 8,
-                    "x": 26
+                    "x": 26,
+                    "y": 8
                   },
                   "xl": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 5,
-                    "y": 8,
-                    "x": 20
+                    "x": 20,
+                    "y": 8
                   },
                   "lg": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 6,
-                    "y": 6,
-                    "x": 12
+                    "x": 12,
+                    "y": 6
                   },
                   "md": {
                     "minH": 3,
                     "minW": 3,
                     "h": 6,
                     "w": 5,
-                    "y": 14,
-                    "x": 5
+                    "x": 5,
+                    "y": 14
                   },
                   "sm": {
                     "minH": 3,
                     "minW": 3,
                     "h": 5,
                     "w": 4,
-                    "y": 5,
-                    "x": 8
+                    "x": 8,
+                    "y": 5
                   }
-                }
-              }
-            ]
-          },
-          "columns": {
-            "enable": [
-              "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"
-            ],
-            "options": {
-              "results.material.chemical_formula_descriptive": {
-                "label": "Descriptive Formula",
-                "align": "left"
-              },
-              "results.properties.optoelectronic.solar_cell.efficiency": {
-                "label": "Efficiency (%)",
-                "format": {
-                  "decimals": 2,
-                  "mode": "standard"
-                }
-              },
-              "results.properties.optoelectronic.solar_cell.open_circuit_voltage": {
-                "label": "Open circuit voltage",
-                "unit": "V",
-                "format": {
-                  "decimals": 3,
-                  "mode": "standard"
-                }
-              },
-              "results.properties.optoelectronic.solar_cell.short_circuit_current_density": {
-                "label": "Short circuit current density",
-                "unit": "A/m**2",
-                "format": {
-                  "decimals": 3,
-                  "mode": "standard"
-                }
-              },
-              "results.properties.optoelectronic.solar_cell.fill_factor": {
-                "label": "Fill factor",
-                "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"
-              },
-              "results.properties.optoelectronic.solar_cell.illumination_intensity": {
-                "label": "Illum. intensity",
-                "unit": "W/m**2",
-                "format": {
-                  "decimals": 3,
-                  "mode": "standard"
-                }
-              },
-              "results.eln.lab_ids": {
-                "label": "Lab IDs"
-              },
-              "results.eln.sections": {
-                "label": "Sections"
-              },
-              "results.eln.methods": {
-                "label": "Methods"
-              },
-              "results.eln.tags": {
-                "label": "Tags"
-              },
-              "results.eln.instruments": {
-                "label": "Instruments"
-              },
-              "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"
-              }
-            }
-          },
-          "rows": {
-            "actions": {
-              "enable": true
-            },
-            "details": {
-              "enable": true
-            },
-            "selection": {
-              "enable": true
-            }
-          },
-          "filter_menus": {
-            "options": {
-              "material": {
-                "label": "Absorber Material",
-                "level": 0
-              },
-              "elements": {
-                "label": "Elements / Formula",
-                "level": 1,
-                "size": "large",
-                "menu_items": {}
-              },
-              "structure": {
-                "label": "Structure",
-                "level": 1,
-                "size": "small",
-                "menu_items": {}
-              },
-              "electronic": {
-                "label": "Electronic Properties",
-                "level": 0,
-                "size": "small",
-                "menu_items": {}
-              },
-              "solarcell": {
-                "label": "Solar Cell Properties",
-                "level": 0,
-                "size": "small",
-                "menu_items": {}
-              },
-              "eln": {
-                "label": "Electronic Lab Notebook",
-                "level": 0,
-                "size": "small",
-                "menu_items": {}
-              },
-              "custom_quantities": {
-                "label": "User Defined Quantities",
-                "level": 0,
-                "size": "large",
-                "menu_items": {}
-              },
-              "author": {
-                "label": "Author / Origin / Dataset",
-                "level": 0,
-                "size": "medium",
-                "menu_items": {}
-              },
-              "metadata": {
-                "label": "Visibility / IDs / Schema",
-                "level": 0,
-                "size": "small",
-                "menu_items": {}
-              },
-              "optimade": {
-                "label": "Optimade",
-                "level": 0,
-                "size": "medium",
-                "menu_items": {}
+                },
+                "quantity": "results.properties.optoelectronic.solar_cell.hole_transport_layer",
+                "scale": "linear",
+                "showinput": true
               }
-            }
-          },
-          "filters": {
-            "exclude": [
-              "mainfile",
-              "entry_name",
-              "combine"
             ]
           },
           "filters_locked": {
@@ -1675,14 +1692,7 @@ window.nomadEnv = {
         }
       }
     },
-    "example_uploads": {
-      "include": null,
-      "exclude": null
-    },
-    "default_unit_system": "Custom",
-    "north_enabled": true,
-    "theme": {
-      "title": "NOMAD"
-    }
+    "north": {},
+    "example_uploads": {}
   }
 }
diff --git a/nomad/app/resources/main.py b/nomad/app/resources/main.py
index 960b6076249c154b3f29d9a5bb5c0d1185a7f362..99b74ec9ff3a8c18a298c7ac0a12d2ba73def130 100644
--- a/nomad/app/resources/main.py
+++ b/nomad/app/resources/main.py
@@ -89,10 +89,7 @@ def setup_mongo():
 
 
 def remove_mongo():
-    global mongo_client_resources
-
     setup_mongo()
-
     mongo_client_resources.drop_database(config.resources.db_name)
 
 
diff --git a/nomad/app/v1/models.py b/nomad/app/v1/models.py
index 87100e2a9e97ec10558f225157f8cdd81aaeb0e0..1faa5c083c3c6a2e18d2ddfa68fe3d81986f9ae8 100644
--- a/nomad/app/v1/models.py
+++ b/nomad/app/v1/models.py
@@ -392,7 +392,7 @@ class QueryParameters:
                     'loc': ['query', 'q'],
                     'msg': 'wrong format, use <quantity>[__<op>]__<value>'}])
             name_op, value = '__'.join(fragments[:-1]), fragments[-1]
-            quantity_name = name_op.split('__')[0]
+            quantity_name = name_op.split('__', maxsplit=1)[0]
 
             doc_type = self.doc_type
             if quantity_name.startswith('entries.'):
diff --git a/nomad/app/v1/routers/entries.py b/nomad/app/v1/routers/entries.py
index 44ca81f0c79b55965093078119d492d2d129a402..0e5f5d1338e1d16b286518ce1e8783d471abcaaa 100644
--- a/nomad/app/v1/routers/entries.py
+++ b/nomad/app/v1/routers/entries.py
@@ -1411,7 +1411,7 @@ async def post_entries_edit(
     try:
         verified_json = proc.MetadataEditRequestHandler.edit_metadata(edit_request_json, None, user)
         return verified_json
-    except RequestValidationError as e:
+    except RequestValidationError:
         raise  # A problem which we have handled explicitly. Fastapi does json conversion.
     except Exception as e:
         # The upload is processing or some kind of unexpected error has occured
diff --git a/nomad/app/v1/routers/north.py b/nomad/app/v1/routers/north.py
index 80217e0254b5957854c2b3d2cabe6b58b6cb3d37..a952ee61edb3eea0541c72b1241f8f3d1a3d0f34 100644
--- a/nomad/app/v1/routers/north.py
+++ b/nomad/app/v1/routers/north.py
@@ -26,6 +26,7 @@ from fastapi import APIRouter, Depends, status, HTTPException
 from mongoengine.queryset.visitor import Q
 
 from nomad import config
+from nomad.config.models import NORTHTool
 from nomad.utils import strip, get_logger, slugify
 from nomad.processing import Upload
 from .auth import create_user_dependency, oauth2_scheme
@@ -47,7 +48,7 @@ class ToolStateEnum(str, Enum):
     stopped = 'stopped'
 
 
-class ToolModel(config.NorthTool):
+class ToolModel(NORTHTool):
     name: str
     state: Optional[ToolStateEnum]
 
@@ -72,7 +73,7 @@ def _get_status(tool: ToolModel, user: User) -> ToolModel:
     if not user:
         return tool
 
-    url = f'{config.north.hub_url()}/api/users/{user.username}/servers/{tool.name}/progress'
+    url = f'{config.hub_url()}/api/users/{user.username}/servers/{tool.name}/progress'
     response = requests.get(url, headers=hub_api_headers)
 
     if response.status_code == 404:
@@ -103,7 +104,7 @@ async def get_tools(user: User = Depends(create_user_dependency())):
     return ToolsResponseModel(
         data=[
             _get_status(ToolModel(name=name, **tool.dict()), user)
-            for name, tool in cast(Dict[str, config.NorthTool], config.north.tools).items()
+            for name, tool in cast(Dict[str, NORTHTool], config.north.tools).items()
         ]
     )
 
@@ -114,7 +115,7 @@ async def tool(name: str) -> ToolModel:
             status_code=status.HTTP_404_NOT_FOUND,
             detail='The tools does not exist.')
 
-    tool = cast(Dict[str, config.NorthTool], config.north.tools)[name]
+    tool = cast(Dict[str, NORTHTool], config.north.tools)[name]
     return ToolModel(name=name, **tool.dict())
 
 
@@ -151,7 +152,7 @@ async def start_tool(
     tool.state = ToolStateEnum.stopped
 
     # Make sure the user exists
-    url = f'{config.north.hub_url()}/api/users/{user.username}'
+    url = f'{config.hub_url()}/api/users/{user.username}'
     response = requests.get(url, headers=hub_api_headers)
     if response.status_code == 404:
         response = requests.post(url, headers=hub_api_headers)
@@ -203,7 +204,7 @@ async def start_tool(
             username=user.username,
             data=_get_status(tool, user))
 
-    url = f'{config.north.hub_url()}/api/users/{user.username}/servers/{tool.name}'
+    url = f'{config.hub_url()}/api/users/{user.username}/servers/{tool.name}'
     body = {
         'tool': {
             'image': tool.image,
@@ -254,7 +255,7 @@ async def stop_tool(
     tool: ToolModel = Depends(tool),
     user: User = Depends(create_user_dependency(required=True))
 ):
-    url = f'{config.north.hub_url()}/api/users/{user.username}/servers/{tool.name}'
+    url = f'{config.hub_url()}/api/users/{user.username}/servers/{tool.name}'
     response = requests.delete(url, json={'remove': True}, headers=hub_api_headers)
 
     if response.status_code == 404:
diff --git a/nomad/app/v1/routers/uploads.py b/nomad/app/v1/routers/uploads.py
index 8b6feb84824432e37de16f9905720fb8f6e3f710..e6d9a20ae76bb3377d447b990858bc9014c81d3d 100644
--- a/nomad/app/v1/routers/uploads.py
+++ b/nomad/app/v1/routers/uploads.py
@@ -19,7 +19,7 @@ import os
 import io
 import shutil
 from datetime import datetime
-from typing import Tuple, List, Set, Dict, Any, Optional
+from typing import Tuple, List, Set, Dict, Any, Optional, Union
 from pydantic import BaseModel, Field, validator
 from mongoengine.queryset.visitor import Q
 from urllib.parse import unquote
@@ -553,7 +553,7 @@ async def get_upload_entries(
     # load entries's metadata from search
     metadata_entries_query = WithQuery(
         query={
-            'entry_id:any': list([entry.entry_id for entry in entries])
+            'entry_id:any': list(entry.entry_id for entry in entries)
         }).query
     metadata_entries = search(
         pagination=MetadataPagination(page_size=len(entries)),
@@ -1320,7 +1320,7 @@ async def post_upload(
         elif len(upload_paths) == 1:
             upload_name = os.path.basename(upload_paths[0])
 
-    upload = Upload.create(
+    upload: Upload = Upload.create(
         upload_id=upload_id,
         main_author=user,
         upload_name=upload_name,
@@ -1385,7 +1385,7 @@ async def post_upload_edit(
     try:
         MetadataEditRequestHandler.edit_metadata(edit_request_json, upload_id, user)
         return UploadProcDataResponse(upload_id=upload_id, data=_upload_to_pydantic(Upload.get(upload_id)))
-    except RequestValidationError as e:
+    except RequestValidationError:
         raise  # A problem which we have handled explicitly. Fastapi does json conversion.
     except Exception as e:
         # The upload is processing or some kind of unexpected error has occured
@@ -1762,6 +1762,7 @@ async def post_upload_bundle(
 
     bundle_importer: BundleImporter = None
     bundle_path: str = None
+    method = None
 
     try:
         bundle_importer = BundleImporter(user, import_settings)
@@ -1803,7 +1804,7 @@ async def post_upload_bundle(
 
 async def _get_files_if_provided(
         tmp_dir_prefix: str, request: Request, file: List[UploadFile], local_path: str, file_name: str,
-        user: User) -> Tuple[List[str], int]:
+        user: User) -> Tuple[List[str], Union[None, int]]:
     '''
     If the user provides one or more files with the api call, load and save them to a temporary
     folder (or, if method 0 is used, just "forward" the file path). The method thus needs to identify
diff --git a/nomad/bundles.py b/nomad/bundles.py
index 08b84a12b22267b39dbd68b9ca4bb3708f1ed7e4..da5b2448edbcdc0c410f6c1d519f8c747a2117d1 100644
--- a/nomad/bundles.py
+++ b/nomad/bundles.py
@@ -14,6 +14,7 @@ import json
 from datetime import datetime, timedelta
 
 from nomad import config, utils, datamodel, search
+from nomad.config.models import BundleImportSettings, BundleExportSettings
 from nomad.files import (
     zipfile, PathObject, UploadFiles, PublicUploadFiles, StagingUploadFiles,
     FileSource, BrowsableFileSource, CombinedFileSource, StreamedFileSource, DiskFileSource, ZipFileSource,
@@ -26,7 +27,7 @@ from fastapi import HTTPException, status
 class BundleExporter:
     def __init__(
             self, upload: Upload, export_as_stream: bool, export_path: str, zipped: bool, overwrite: bool,
-            export_settings: config.BundleExportSettings):
+            export_settings: BundleExportSettings):
         '''
         Class for exporting an upload as a *bundle*. Bundles are used to export and import
         uploads between different NOMAD installations. After instantiating a BundleExporter,
@@ -61,7 +62,7 @@ class BundleExporter:
         self.export_settings = export_settings
 
     @classmethod
-    def check_export_settings(cls, export_settings: config.BundleExportSettings):
+    def check_export_settings(cls, export_settings: BundleExportSettings):
         assert export_settings.include_archive_files or export_settings.include_raw_files, (
             'Export must include the archive files or the raw files, or both')
 
@@ -128,7 +129,7 @@ class BundleExporter:
 
 
 class BundleImporter:
-    def __init__(self, user: datamodel.User, import_settings: config.BundleImportSettings, embargo_length: int = None):
+    def __init__(self, user: datamodel.User, import_settings: BundleImportSettings, embargo_length: int = None):
         '''
         Class for importing an upload from a *bundle*.
 
@@ -138,7 +139,7 @@ class BundleImporter:
                 permission checks are done.
             import_settings:
                 Settings for controlling the bundle content. See the
-                `config.BundleImportSettings` for applicable options.
+                `BundleImportSettings` for applicable options.
                 NOTE: the dictionary must specify a complete set of options.
             embargo_length:
                 Used to set the embargo length. If set to None, the value will be imported
diff --git a/nomad/cli/admin/admin.py b/nomad/cli/admin/admin.py
index a0df64c6b294de97f8fc862bfa64148fd41e0dd7..2eb527aab6103dd5eeb77b06826da1b99b64f7c5 100644
--- a/nomad/cli/admin/admin.py
+++ b/nomad/cli/admin/admin.py
@@ -339,7 +339,7 @@ def migrate_mongo(
 
     from pymongo.database import Database
     from nomad import infrastructure
-    import nomad.cli.admin.migrate as migrate
+    from nomad.cli.admin import migrate
 
     config.mongo.host = host
     config.mongo.port = port
diff --git a/nomad/cli/aflow.py b/nomad/cli/aflow.py
index b5102152b9f874c5b15a649debd793e66e59f2ab..2c54fe04a39d547145fdd00d0a38506c87a92f55 100644
--- a/nomad/cli/aflow.py
+++ b/nomad/cli/aflow.py
@@ -37,7 +37,7 @@ import json
 import numpy as np
 import ase
 import bs4
-import matid
+import matid  # pylint: disable=import-error
 
 from nomad import atomutils, config, client, processing as proc
 from nomad.client import api, upload_file
diff --git a/nomad/cli/dev.py b/nomad/cli/dev.py
index 85d5cebadc300e8470028c9fe322bd7b50102602..24634e7c1e13f1eb446f2c5285801cc7a10c3c5f 100644
--- a/nomad/cli/dev.py
+++ b/nomad/cli/dev.py
@@ -44,7 +44,7 @@ def qa(skip_tests: bool, exitfirst: bool):
     click.echo('Run code style checks ...')
     ret_code += os.system('python -m pycodestyle --config=pycodestyle.ini nomad tests')
     click.echo('Run linter ...')
-    ret_code += os.system('python -m pylint --load-plugins=pylint_mongoengine,nomad.metainfo.pylint_plugin nomad tests')
+    ret_code += os.system('python -m pylint --rcfile=.pylintrc nomad tests')
     click.echo('Run static type checks ...')
     ret_code += os.system('python -m mypy --ignore-missing-imports --follow-imports=silent --no-strict-optional nomad tests')
 
@@ -158,7 +158,7 @@ def _generate_search_quantities():
             metadict['name'] = instanceMeta['name']
             metadict['repeats'] = repeats or instanceMeta.get('repeats')
             es_annotations = search_quantity.m_get_annotations(Elasticsearch, as_list=True)
-            nested = any([x.nested for x in es_annotations])
+            nested = any(x.nested for x in es_annotations)
             metadict['nested'] = nested
         else:
             keys = ['name', 'description', 'type', 'unit', 'shape', 'aliases']
@@ -240,7 +240,7 @@ def get_gui_config(proxy: bool = False) -> str:
         'version': config.meta.beta if config.meta.beta else {},
         'globalLoginRequired': config.oasis.allowed_users is not None,
         'servicesUploadLimit': config.services.upload_limit,
-        'ui': config.ui.dict() if config.ui else {}
+        'ui': config.ui.dict(exclude_none=True) if config.ui else {}
     }
 
     return f'window.nomadEnv = {json.dumps(data, indent=2)}'
diff --git a/nomad/config.py b/nomad/config.py
deleted file mode 100644
index 3127532d9d1cb75fca1cbf011a6e120fab80bdf4..0000000000000000000000000000000000000000
--- a/nomad/config.py
+++ /dev/null
@@ -1,1620 +0,0 @@
-#
-# Copyright The NOMAD Authors.
-#
-# This file is part of NOMAD. See https://nomad-lab.eu for further info.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-'''
-This module describes all configurable parameters for the nomad python code. The
-configuration is used for all executed python code including API, worker, CLI, and other
-scripts. To use the configuration in your own scripts or new modules, simply import
-this module.
-
-All parameters are structured into objects for two reasons. First, to have
-categories. Second, to allow runtime manipulation that is not effected
-by python import logic. The categories are choosen along infrastructure components:
-``mongo``, ``elastic``, etc.
-
-This module also provides utilities to read the configuration from environment variables
-and .yaml files. This is done automatically on import. The precedence is env over .yaml
-over defaults.
-'''
-
-import logging
-import os
-import inspect
-import os.path
-import yaml
-import warnings
-import json
-from typing import TypeVar, List, Dict, Any, Union, cast
-from pkg_resources import get_distribution, DistributionNotFound
-from pydantic import BaseModel, Field, validator
-
-try:
-    __version__ = get_distribution("nomad-lab").version
-except DistributionNotFound:
-    # package is not installed
-    pass
-
-
-warnings.filterwarnings('ignore', message='numpy.dtype size changed')
-warnings.filterwarnings('ignore', message='numpy.ufunc size changed')
-warnings.filterwarnings('ignore', category=DeprecationWarning)
-
-
-NomadSettingsBound = TypeVar('NomadSettingsBound', bound='NomadSettings')
-
-
-class NomadSettings(BaseModel):
-    def customize(
-            self: NomadSettingsBound,
-            custom_settings: Union[NomadSettingsBound, Dict[str, Any]]) -> NomadSettingsBound:
-        '''
-        Returns a new config object, created by taking a copy of the current config and
-        updating it with the settings defined in `custom_settings`. The `custom_settings` can
-        be a NomadSettings or a dictionary (in the latter case it must not contain any new keys
-        (keys not defined in this NomadSettings). If it does, an exception will be raised.
-        '''
-
-        rv = self.copy(deep=True)
-
-        if custom_settings:
-            if isinstance(custom_settings, BaseModel):
-                for field_name in custom_settings.__fields__.keys():
-                    try:
-                        setattr(rv, field_name, getattr(custom_settings, field_name))
-                    except Exception:
-                        raise AssertionError(f'Invalid setting: {field_name}')
-            elif isinstance(custom_settings, dict):
-                for key, value in custom_settings.items():
-                    if value is None:
-                        continue
-                    try:
-                        setattr(rv, key, value)
-                    except Exception:
-                        raise AssertionError(f'Invalid setting: {field_name}')
-
-        return cast(NomadSettingsBound, rv)
-
-
-class Services(NomadSettings):
-    '''
-    Contains basic configuration of the NOMAD services (app, worker, north).
-    '''
-    api_host = Field('localhost', description='''
-        The external hostname that clients can use to reach this NOMAD installation.
-    ''')
-    api_port = Field(8000, description='''
-        The port used to expose the NOMAD app and api to clients.
-    ''')
-    api_base_path = Field('/fairdi/nomad/latest', description='''
-        The base path prefix for the NOMAD app and api.
-    ''')
-    api_secret = Field('defaultApiSecret', description='''
-        A secret that is used to issue download and other tokens.
-    ''')
-    https = Field(False, description='''
-        Set to `True`, if external clients are using *SSL* to connect to this installation.
-        Requires to setup a reverse-proxy (e.g. the one used in the docker-compose
-        based installation) that handles the *SSL* encryption.
-    ''')
-    https_upload = Field(False, description='''
-        Set to `True`, if upload curl commands should suggest the use of SSL for file
-        uploads. This can be configured independently of `https` to suggest large file
-        via regular HTTP.
-    ''')
-    admin_user_id = Field('00000000-0000-0000-0000-000000000000', description='''
-        The admin user `user_id`. All users are treated the same; there are no
-        particular authorization information attached to user accounts. However, the
-        API will grant the user with the given `user_id` more rights, e.g. using the
-        `admin` owner setting in accessing data.
-    ''')
-
-    encyclopedia_base = Field(
-        'https://nomad-lab.eu/prod/rae/encyclopedia/#', description='''
-            This enables links to the given *encyclopedia* installation in the UI.
-        ''')
-    aitoolkit_enabled = Field(False, description='''
-        If true, the UI will show a menu with links to the AI Toolkit notebooks on
-        `nomad-lab.eu`.
-    ''')
-
-    console_log_level = Field(logging.WARNING, description='''
-        The log level that controls console logging for all NOMAD services (app, worker, north).
-        The level is given in Python `logging` log level numbers.
-    ''')
-
-    upload_limit = Field(10, description='''
-        The maximum allowed unpublished uploads per user. If a user exceeds this
-        amount, the user cannot add more uploads.
-    ''')
-    force_raw_file_decoding = Field(False, description='''
-        By default, text raw-files are interpreted with utf-8 encoding. If this fails,
-        the actual encoding is guessed. With this setting, we force to assume iso-8859-1
-        encoding, if a file is not decodable with utf-8.
-    ''')
-    max_entry_download = Field(50000, description='''
-        There is an inherent limit in page-based pagination with Elasticsearch. If you
-        increased this limit with your Elasticsearch, you can also adopt this setting
-        accordingly, changing the maximum amount of entries that can be paginated with
-        page-base pagination.
-
-        Page-after-value-based pagination is independent and can be used without limitations.
-    ''')
-    unavailable_value = Field('unavailable', description='''
-        Value that is used in `results` section Enum fields (e.g. system type, spacegroup, etc.)
-        to indicate that the value could not be determined.
-    ''')
-
-
-services = Services()
-
-
-def api_url(ssl: bool = True, api: str = 'api', api_host: str = None, api_port: int = None):
-    '''
-    Returns the url of the current running nomad API. This is for server-side use.
-    This is not the NOMAD url to use as a client, use `nomad.config.client.url` instead.
-    '''
-    if api_port is None:
-        api_port = services.api_port
-    if api_host is None:
-        api_host = services.api_host
-    protocol = 'https' if services.https and ssl else 'http'
-    host_and_port = api_host
-    if api_port not in [80, 443]:
-        host_and_port += ':' + str(api_port)
-    base_path = services.api_base_path.strip('/')
-    return f'{protocol}://{host_and_port}/{base_path}/{api}'
-
-
-def gui_url(page: str = None):
-    base = api_url(True)[:-3]
-    if base.endswith('/'):
-        base = base[:-1]
-
-    if page is not None:
-        return '%s/gui/%s' % (base, page)
-
-    return '%s/gui' % base
-
-
-class Meta(NomadSettings):
-    '''
-    Metadata about the deployment and how it is presented to clients.
-    '''
-    version = Field(__version__, description='The NOMAD version string.')
-    commit = Field('', description='The source-code commit that this installation\'s NOMAD version is build from.')
-    deployment = Field(
-        'devel', description='Human-friendly name of this nomad deployment.')
-    deployment_url = Field(
-        api_url(), description='The NOMAD deployment\'s url (api url).')
-    label: str = Field(None, description='''
-        An additional log-stash data key-value pair added to all logs. Can be used
-        to differentiate deployments when analyzing logs.
-    ''')
-    service = Field('unknown nomad service', description='''
-        Name for the service that is added to all logs. Depending on how NOMAD is
-        installed, services get a name (app, worker, north) automatically.
-    ''')
-
-    name = Field(
-        'NOMAD',
-        description='Web-site title for the NOMAD UI.',
-        deprecated=True)
-    homepage = Field(
-        'https://nomad-lab.eu', description='Provider homepage.', deprecated=True)
-    source_url = Field(
-        'https://gitlab.mpcdf.mpg.de/nomad-lab/nomad-FAIR',
-        description='URL of the NOMAD source-code repository.',
-        deprecated=True)
-
-    maintainer_email = Field(
-        'markus.scheidgen@physik.hu-berlin.de',
-        description='Email of the NOMAD deployment maintainer.')
-    beta: dict = Field(None, description='''
-        Additional data that describes how the deployment is labeled as a beta-version in the UI.
-    ''')
-
-
-meta = Meta()
-
-
-class Oasis(NomadSettings):
-    '''
-    Settings related to the configuration of a NOMAD Oasis deployment.
-    '''
-    is_oasis = Field(False, description='Set to `True` to indicate that this deployment is a NOMAD Oasis.')
-    allowed_users: str = Field(None, description='''
-        A list of usernames or user account emails. These represent a white-list of
-        allowed users. With this, users will need to login right-away and only the
-        listed users might use this deployment. All API requests must have authentication
-        information as well.''')
-    uses_central_user_management = Field(False, description='''
-        Set to True to use the central user-management. Typically the NOMAD backend is
-        using the configured `keycloak` to access user data. With this, the backend will
-        use the API of the central NOMAD (`central_nomad_deployment_url`) instead.
-    ''')
-    central_nomad_deployment_url = Field('https://nomad-lab.eu/prod/v1/api', description='''
-        The URL of the API of the NOMAD deployment that is considered the *central* NOMAD.
-    ''')
-
-
-oasis = Oasis()
-
-
-_jupyterhub_config_description = '''
-    This setting is forwarded to jupyterhub; refer to the jupyterhub
-    [documentation](https://jupyterhub.readthedocs.io/en/stable/api/app.html#).
-'''
-
-
-class NorthToolMaintainer(BaseModel):
-    name: str
-    email: str
-
-
-class NorthTool(BaseModel):
-    image: str
-    description: str = None
-    short_description: str = None
-    cmd: str = None
-    privileged: bool = None
-    path_prefix: str = None
-    mount_path: str = None
-    icon: str = None
-    file_extensions: List[str] = []
-    maintainer: List[NorthToolMaintainer] = []
-
-
-class North(NomadSettings):
-    '''
-    Settings related to the operation of the NOMAD remote tools hub service *north*.
-    '''
-    enabled: str = Field(True, description='''
-        Enables or disables the NORTH API and UI views. This is independent of
-        whether you run a jupyter hub or not.
-    ''')
-    hub_connect_ip: str = Field(None, description='''
-        Overwrites the default hostname that can be used from within a north container
-        to reach the host system.
-
-        Typically has to be set for non Linux hosts. Set this to `host.docker.internal`
-        on windows/macos.
-    ''')
-    hub_connect_url: str = Field(None, description=_jupyterhub_config_description)
-    hub_ip = Field('0.0.0.0', description=_jupyterhub_config_description)
-    docker_network: str = Field(None, description=_jupyterhub_config_description)
-    hub_host = Field('localhost', description='''
-        The internal host name that NOMAD services use to connect to the jupyterhub API.
-    ''')
-    hub_port = Field(9000, description='''
-        The internal port that NOMAD services use to connect to the jupyterhub API.
-    ''')
-    jupyterhub_crypt_key: str = Field(None, description=_jupyterhub_config_description)
-
-    nomad_host: str = Field(
-        None, description='The NOMAD app host name that spawned containers use.')
-    windows = Field(
-        True, description='Enable windows OS hacks.')
-
-    tools: Union[str, Dict[str, NorthTool]] = Field(
-        'dependencies/nomad-remote-tools-hub/tools.json',
-        description='The available north tools. Either the tools definitions as dict or a path to a .json file.')
-
-    hub_service_api_token: str = Field('secret-token', description='''
-        A secret token shared between NOMAD and the NORTH jupyterhub.
-        This needs to be the token of an admin service.''')
-
-    def hub_url(self):
-        return f'http://{self.hub_host}:{self.hub_port}{services.api_base_path}/north/hub'
-
-    @validator('tools', pre=True, always=True)
-    def load_tools(cls, v):  # pylint: disable=no-self-argument
-        if isinstance(v, str):
-            # interpret as file
-            with open(v, 'rt') as f:
-                v = json.load(f)
-
-        return v
-
-
-north = North()
-
-
-CELERY_WORKER_ROUTING = 'worker'
-CELERY_QUEUE_ROUTING = 'queue'
-
-
-class RabbitMQ(NomadSettings):
-    '''
-    Configures how NOMAD is connecting to RabbitMQ.
-    '''
-    host = Field('localhost', description='The name of the host that runs RabbitMQ.')
-    user = Field('rabbitmq', description='The RabbitMQ user that is used to connect.')
-    password = Field('rabbitmq', description='The password that is used to connect.')
-
-
-rabbitmq = RabbitMQ()
-
-
-def rabbitmq_url():
-    return 'pyamqp://%s:%s@%s//' % (rabbitmq.user, rabbitmq.password, rabbitmq.host)
-
-
-class Celery(NomadSettings):
-    max_memory = 64e6  # 64 GB
-    timeout = 1800  # 1/2 h
-    acks_late = False
-    routing = CELERY_QUEUE_ROUTING
-    priorities = {
-        'Upload.process_upload': 5,
-        'Upload.delete_upload': 9,
-        'Upload.publish_upload': 10
-    }
-
-
-celery = Celery()
-
-
-class FS(NomadSettings):
-    tmp = '.volumes/fs/tmp'
-    staging = '.volumes/fs/staging'
-    staging_external: str = None
-    public = '.volumes/fs/public'
-    public_external: str = None
-    north_home = '.volumes/fs/north/users'
-    north_home_external: str = None
-    local_tmp = '/tmp'
-    prefix_size = 2
-    archive_version_suffix = 'v1'
-    working_directory = os.getcwd()
-    external_working_directory: str = None
-
-
-fs = FS()
-
-
-class Elastic(NomadSettings):
-    host = 'localhost'
-    port = 9200
-    timeout = 60
-    bulk_timeout = 600
-    bulk_size = 1000
-    entries_per_material_cap = 1000
-    entries_index = 'nomad_entries_v1'
-    materials_index = 'nomad_materials_v1'
-
-
-elastic = Elastic()
-
-
-class Keycloak(NomadSettings):
-    server_url = 'https://nomad-lab.eu/fairdi/keycloak/auth/'
-    public_server_url: str = None
-    realm_name = 'fairdi_nomad_prod'
-    username = 'admin'
-    password = 'password'
-    client_id = 'nomad_public'
-    client_secret: str = None
-
-
-keycloak = Keycloak()
-
-
-class Mongo(NomadSettings):
-    ''' Connection and usage settings for MongoDB.'''
-    host: str = Field('localhost', description='The name of the host that runs mongodb.')
-    port: int = Field(27017, description='The port to connect with mongodb.')
-    db_name: str = Field('nomad_v1', description='The used mongodb database name.')
-
-
-mongo = Mongo()
-
-
-class Logstash(NomadSettings):
-    enabled = False
-    host = 'localhost'
-    tcp_port = '5000'
-    level: int = logging.DEBUG
-
-
-logstash = Logstash()
-
-
-class Tests(NomadSettings):
-    default_timeout = 60
-
-
-tests = Tests()
-
-
-class Mail(NomadSettings):
-    enabled = False
-    with_login = False
-    host = ''
-    port = 8995
-    user = ''
-    password = ''
-    from_address = 'support@nomad-lab.eu'
-    cc_address = 'support@nomad-lab.eu'
-
-
-mail = Mail()
-
-
-class Normalize(NomadSettings):
-    system_classification_with_clusters_threshold = Field(
-        64, description='''
-            The system size limit for running the dimensionality analysis. For very
-            large systems the dimensionality analysis will get too expensive.
-        ''')
-    symmetry_tolerance = Field(
-        0.1, description='''
-            Symmetry tolerance controls the precision used by spglib in order to find
-            symmetries. The atoms are allowed to move 1/2*symmetry_tolerance from
-            their symmetry positions in order for spglib to still detect symmetries.
-            The unit is angstroms. The value of 0.1 is used e.g. by Materials Project
-            according to
-            https://pymatgen.org/pymatgen.symmetry.analyzer.html#pymatgen.symmetry.analyzer.SpacegroupAnalyzer
-        ''')
-    prototype_symmetry_tolerance = Field(
-        0.1, description='''
-            The symmetry tolerance used in aflow prototype matching. Should only be
-            changed before re-running the prototype detection.
-        ''')
-    max_2d_single_cell_size = Field(
-        7, description='''
-            Maximum number of atoms in the single cell of a 2D material for it to be
-            considered valid.
-        ''')
-    cluster_threshold = Field(
-        2.5, description='''
-            The distance tolerance between atoms for grouping them into the same
-            cluster. Used in detecting system type.
-        ''')
-
-    angle_rounding = Field(
-        float(10.0), description='''
-            Defines the "bin size" for rounding cell angles for the material hash in degree.
-        ''')
-    flat_dim_threshold = Field(
-        0.1, description='''
-            The threshold for a system to be considered "flat". Used e.g. when
-            determining if a 2D structure is purely 2-dimensional to allow extra rigid
-            transformations that are improper in 3D but proper in 2D.
-        ''')
-
-    k_space_precision = Field(
-        150e6, description='''
-            The threshold for point equality in k-space. Unit: 1/m.
-        ''')
-    band_structure_energy_tolerance = Field(
-        8.01088e-21, description='''
-            The energy threshold for how much a band can be on top or below the fermi
-            level in order to still detect a gap. Unit: Joule.
-        ''')
-    springer_db_path = Field(
-        os.path.join(os.path.dirname(os.path.abspath(__file__)), 'normalizing/data/springer.msg'))
-
-
-normalize = Normalize()
-
-
-class Resources(NomadSettings):
-    enabled = False
-    db_name = 'nomad_v1_resources'
-    max_time_in_mongo = Field(
-        60 * 60 * 24 * 365., description='''
-            Maxmimum time a resource is stored in mongodb before being updated.
-        ''')
-    download_retries = Field(
-        2, description='Number of retries when downloading resources.')
-    download_retry_delay = Field(
-        10, description='Delay between retries in seconds')
-    max_connections = Field(
-        10, description='Maximum simultaneous connections used to download resources.')
-
-
-resources = Resources()
-
-
-class Client(NomadSettings):
-    user: str = None
-    password: str = None
-    access_token: str = None
-    url = 'http://nomad-lab.eu/prod/v1/api'
-
-
-client = Client()
-
-
-class DataCite(NomadSettings):
-    mds_host = 'https://mds.datacite.org'
-    enabled = False
-    prefix = '10.17172'
-    user = '*'
-    password = '*'
-
-
-datacite = DataCite()
-
-
-class GitLab(NomadSettings):
-    private_token = 'not set'
-
-
-gitlab = GitLab()
-
-
-class Process(NomadSettings):
-    store_package_definition_in_mongo = Field(
-        False, description='Configures whether to store the corresponding package definition in mongodb.')
-    add_definition_id_to_reference = Field(False, description='''
-        Configures whether to attach definition id to `m_def`, note it is different from `m_def_id`.
-        The `m_def_id` will be exported with the `with_def_id=True` via `m_to_dict`.
-    ''')
-    write_definition_id_to_archive = Field(False, description='Write `m_def_id` to the archive.')
-    index_materials = True
-    reuse_parser = True
-    metadata_file_name = 'nomad'
-    metadata_file_extensions = ('json', 'yaml', 'yml')
-    auxfile_cutoff = 100
-    parser_matching_size = 150 * 80  # 150 lines of 80 ASCII characters per line
-    max_upload_size = 32 * (1024 ** 3)
-    use_empty_parsers = False
-    redirect_stdouts: bool = Field(False, description='''
-        True will redirect lines to stdout (e.g. print output) that occur during
-        processing (e.g. created by parsers or normalizers) as log entries.
-    ''')
-
-
-process = Process()
-
-
-class Reprocess(NomadSettings):
-    '''
-    Configures standard behaviour when reprocessing.
-    Note, the settings only matter for published uploads and entries. For uploads in
-    staging, we always reparse, add newfound entries, and delete unmatched entries.
-    '''
-    rematch_published = True
-    reprocess_existing_entries = True
-    use_original_parser = False
-    add_matched_entries_to_published = True
-    delete_unmatched_published_entries = False
-    index_individual_entries = False
-
-
-reprocess = Reprocess()
-
-
-class RFC3161Timestamp(NomadSettings):
-    server = Field(
-        'http://time.certum.pl/', description='The rfc3161ng timestamping host.')
-    cert: str = Field(
-        None, description='Path to the optional rfc3161ng timestamping server certificate.')
-    hash_algorithm = Field(
-        'sha256', description='Hash algorithm used by the rfc3161ng timestamping server.')
-    username: str = None
-    password: str = None
-
-
-rfc3161_timestamp = RFC3161Timestamp()
-
-
-class BundleExportSettings(NomadSettings):
-    include_raw_files: bool = Field(
-        True, description='If the raw files should be included in the export')
-    include_archive_files: bool = Field(
-        True, description='If the parsed archive files should be included in the export')
-    include_datasets: bool = Field(
-        True, description='If the datasets should be included in the export')
-
-
-class BundleExport(NomadSettings):
-    ''' Controls behaviour related to exporting bundles. '''
-    default_cli_bundle_export_path: str = Field(
-        './bundles', description='Default path used when exporting bundles using the CLI command.')
-    default_settings: BundleExportSettings = Field(
-        BundleExportSettings(), description='''
-            General default settings.
-        ''')
-    default_settings_cli: BundleExportSettings = Field(
-        None, description='''
-            Additional default settings, applied when exporting using the CLI. This allows
-            to override some of the settings specified in the general default settings above.
-        ''')
-
-
-bundle_export = BundleExport()
-
-
-class BundleImportSettings(NomadSettings):
-    include_raw_files: bool = Field(
-        True, description='If the raw files should be included in the import')
-    include_archive_files: bool = Field(
-        True, description='If the parsed archive files should be included in the import')
-    include_datasets: bool = Field(
-        True, description='If the datasets should be included in the import')
-
-    include_bundle_info: bool = Field(
-        True, description='If the bundle_info.json file should be kept (not necessary but may be nice to have.')
-    keep_original_timestamps: bool = Field(
-        False, description='''
-            If all timestamps (create time, publish time etc) should be imported from
-            the bundle.
-        ''')
-    set_from_oasis: bool = Field(
-        True, description='If the from_oasis flag and oasis_deployment_url should be set.')
-
-    delete_upload_on_fail: bool = Field(
-        False, description='If False, it is just removed from the ES index on failure.')
-    delete_bundle_on_fail: bool = Field(
-        True, description='Deletes the source bundle if the import fails.')
-    delete_bundle_on_success: bool = Field(
-        True, description='Deletes the source bundle if the import succeeds.')
-    delete_bundle_include_parent_folder: bool = Field(
-        True, description='When deleting the bundle, also include parent folder, if empty.')
-
-    trigger_processing: bool = Field(
-        False, description='If the upload should be processed when the import is done (not recommended).')
-    process_settings: Reprocess = Field(
-        Reprocess(
-            rematch_published=True,
-            reprocess_existing_entries=True,
-            use_original_parser=False,
-            add_matched_entries_to_published=True,
-            delete_unmatched_published_entries=False
-        ), description='''
-            When trigger_processing is set to True, these settings control the reprocessing
-            behaviour (see the config for `reprocess` for more info). NOTE: reprocessing is
-            no longer the recommended method to import bundles.
-        '''
-    )
-
-
-class BundleImport(NomadSettings):
-    ''' Controls behaviour related to importing bundles. '''
-    required_nomad_version: str = Field(
-        '1.1.2', description='Minimum  NOMAD version of bundles required for import.')
-
-    default_cli_bundle_import_path: str = Field(
-        './bundles', description='Default path used when importing bundles using the CLI command.')
-
-    allow_bundles_from_oasis: bool = Field(
-        False, description='If oasis admins can "push" bundles to this NOMAD deployment.')
-    allow_unpublished_bundles_from_oasis: bool = Field(
-        False, description='If oasis admins can "push" bundles of unpublished uploads.')
-
-    default_settings: BundleImportSettings = Field(
-        BundleImportSettings(),
-        description='''
-            General default settings.
-        ''')
-
-    default_settings_cli: BundleImportSettings = Field(
-        BundleImportSettings(
-            delete_bundle_on_fail=False,
-            delete_bundle_on_success=False
-        ),
-        description='''
-            Additional default settings, applied when importing using the CLI. This allows
-            to override some of the settings specified in the general default settings above.
-        ''')
-
-
-bundle_import = BundleImport()
-
-
-class Archive(NomadSettings):
-    block_size = 256 * 1024
-    read_buffer_size = Field(
-        256 * 1024, description='GPFS needs at least 256K to achieve decent performance.')
-    max_process_number = Field(
-        20, description='Maximum number of processes can be assigned to process archive query.')
-    min_entries_per_process = Field(
-        20, description='Minimum number of entries per process.')
-
-
-archive = Archive()
-
-
-class UIConfig(NomadSettings):
-    default_unit_system = 'Custom'
-    north_enabled = Field(True, description='This is a derived value filled with north.enabled.')
-    theme = {
-        'title': 'NOMAD'
-    }
-    entry_context: dict = {
-        'overview': {
-            '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.'},
-            }
-        }
-    }
-    search_contexts: dict = {
-        'options': {
-            'entries': {
-                'label': 'Entries',
-                'path': 'entries',
-                'resource': 'entries',
-                'breadcrumb': 'Entries',
-                'category': 'All',
-                'description': 'Search entries across all domains',
-                'help': {
-                    'title': 'Entries search',
-                    'content': inspect.cleandoc(r'''
-                        This page allows you to search **entries** within NOMAD.
-                        Entries represent any individual data items that have
-                        been uploaded to NOMAD, no matter whether they come from
-                        theoretical calculations, experiments, lab notebooks or
-                        any other source of data. This allows you to perform
-                        cross-domain queries, but if you are interested in a
-                        specific subfield, you should see if a specific
-                        application exists for it in the explore menu to get
-                        more details.
-                    '''),
-                },
-                'pagination': {
-                    'order_by': 'upload_create_time',
-                    'order': 'desc',
-                    'page_size': 20,
-                },
-                'columns': {
-                    'enable': [
-                        'entry_name',
-                        'results.material.chemical_formula_hill',
-                        'entry_type',
-                        'upload_create_time',
-                        'authors'
-                    ],
-                    'options': {
-                        'entry_name': {'label': 'Name', 'align': 'left'},
-                        'results.material.chemical_formula_hill': {'label': 'Formula', 'align': 'left'},
-                        'entry_type': {'label': 'Entry type', 'align': 'left'},
-                        'upload_create_time': {'label': 'Upload time', 'align': 'left'},
-                        'authors': {'label': 'Authors', 'align': 'left'},
-                        'results.method.method_name': {'label': 'Method name'},
-                        'results.method.simulation.program_name': {'label': 'Program name'},
-                        'results.method.simulation.dft.basis_set_name': {'label': 'Basis set name'},
-                        'results.method.simulation.dft.xc_functional_type': {'label': 'XC Functional Type'},
-                        'results.material.structural_type': {'label': 'Dimensionality'},
-                        'results.material.symmetry.crystal_system': {'label': 'Crystal system'},
-                        'results.material.symmetry.space_group_symbol': {'label': 'Space group symbol'},
-                        'results.material.symmetry.space_group_number': {'label': 'Space group number'},
-                        'results.eln.lab_ids': {'label': 'Lab IDs'},
-                        'results.eln.sections': {'label': 'Sections'},
-                        'results.eln.methods': {'label': 'Methods'},
-                        'results.eln.tags': {'label': 'Tags'},
-                        'results.eln.instruments': {'label': 'Instruments'},
-                        'mainfile': {'label': 'Mainfile', 'align': 'left'},
-                        'comment': {'label': 'Comment', 'align': 'left'},
-                        'references': {'label': 'References', 'align': 'left'},
-                        'datasets': {'label': 'Datasets', 'align': 'left'},
-                        'published': {'label': 'Access'}
-                    }
-                },
-                'rows': {
-                    'actions': {'enable': True},
-                    'details': {'enable': True},
-                    'selection': {'enable': True}
-                },
-                'filter_menus': {
-                    'options': {
-                        'material': {'label': 'Material', 'level': 0},
-                        'elements': {'label': 'Elements / Formula', 'level': 1, 'size': 'large', 'menu_items': {}},
-                        'structure': {'label': 'Structure', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'method': {'label': 'Method', 'level': 0, 'menu_items': {}},
-                        'dft': {'label': 'DFT', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'gw': {'label': 'GW', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'projection': {'label': 'Projection', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'dmft': {'label': 'DMFT', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'eels': {'label': 'EELS', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'workflow': {'label': 'Workflow', 'level': 0},
-                        'molecular_dynamics': {'label': 'Molecular dynamics', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'geometry_optimization': {'label': 'Geometry Optimization', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'properties': {'label': 'Properties', 'level': 0},
-                        'electronic': {'label': 'Electronic', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'vibrational': {'label': 'Vibrational', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'mechanical': {'label': 'Mechanical', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'usecases': {'label': 'Use Cases', 'level': 0, 'size': 'small'},
-                        'solarcell': {'label': 'Solar Cells', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'author': {'label': 'Author / Origin / Dataset', 'level': 0, 'size': 'medium', 'menu_items': {}},
-                        'metadata': {'label': 'Visibility / IDs / Schema', 'level': 0, 'size': 'small', 'menu_items': {}},
-                        'optimade': {'label': 'Optimade', 'level': 0, 'size': 'medium', 'menu_items': {}},
-                    }
-                },
-                'filters': {
-                    'exclude': ['mainfile', 'entry_name', 'combine']
-                },
-            },
-            'calculations': {
-                'label': 'Calculations',
-                'path': 'calculations',
-                'resource': 'entries',
-                'breadcrumb': 'Calculations',
-                'category': 'Theory',
-                'description': 'Search calculations',
-                'help': {
-                    'title': 'Calculations',
-                    'content': inspect.cleandoc(r'''
-                        This page allows you to search **calculations** within
-                        NOMAD.  Calculations typically come from a specific
-                        simulation software that uses an approximate model to
-                        investigate and report different physical properties.
-                    '''),
-                },
-                'pagination': {
-                    'order_by': 'upload_create_time',
-                    'order': 'desc',
-                    'page_size': 20,
-                },
-                'columns': {
-                    'enable': [
-                        'results.material.chemical_formula_hill',
-                        'results.method.simulation.program_name',
-                        'results.method.method_name',
-                        'upload_create_time',
-                        'authors'
-                    ],
-                    'options': {
-                        'results.material.chemical_formula_hill': {'label': 'Formula', 'align': 'left'},
-                        'results.method.simulation.program_name': {'label': 'Program name'},
-                        'results.method.method_name': {'label': 'Method name'},
-                        'upload_create_time': {'label': 'Upload time', 'align': 'left'},
-                        'authors': {'label': 'Authors', 'align': 'left'},
-                        'results.method.simulation.dft.basis_set_name': {'label': 'Basis set name'},
-                        'results.method.simulation.dft.xc_functional_type': {'label': 'XC Functional Type'},
-                        'results.material.structural_type': {'label': 'Dimensionality'},
-                        'results.material.symmetry.crystal_system': {'label': 'Crystal system'},
-                        'results.material.symmetry.space_group_symbol': {'label': 'Space group symbol'},
-                        'results.material.symmetry.space_group_number': {'label': 'Space group number'},
-                        'results.eln.lab_ids': {'label': 'Lab IDs'},
-                        'results.eln.sections': {'label': 'Sections'},
-                        'results.eln.methods': {'label': 'Methods'},
-                        'results.eln.tags': {'label': 'Tags'},
-                        'results.eln.instruments': {'label': 'Instruments'},
-                        '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'}
-                    }
-                },
-                'rows': {
-                    'actions': {'enable': True},
-                    'details': {'enable': True},
-                    'selection': {'enable': True}
-                },
-                'filter_menus': {
-                    'options': {
-                        'material': {'label': 'Material', 'level': 0},
-                        'elements': {'label': 'Elements / Formula', 'level': 1, 'size': 'large', 'menu_items': {}},
-                        'structure': {'label': 'Structure', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'method': {'label': 'Method', 'level': 0, 'menu_items': {}},
-                        'dft': {'label': 'DFT', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'gw': {'label': 'GW', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'projection': {'label': 'Projection', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'dmft': {'label': 'DMFT', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'workflow': {'label': 'Workflow', 'level': 0},
-                        'molecular_dynamics': {'label': 'Molecular dynamics', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'geometry_optimization': {'label': 'Geometry Optimization', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'properties': {'label': 'Properties', 'level': 0},
-                        'electronic': {'label': 'Electronic', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'vibrational': {'label': 'Vibrational', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'mechanical': {'label': 'Mechanical', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'author': {'label': 'Author / Origin / Dataset', 'level': 0, 'size': 'medium', 'menu_items': {}},
-                        'metadata': {'label': 'Visibility / IDs / Schema', 'level': 0, 'size': 'small', 'menu_items': {}},
-                        'optimade': {'label': 'Optimade', 'level': 0, 'size': 'medium', 'menu_items': {}},
-                    }
-                },
-                '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': inspect.cleandoc(r'''
-                        This page allows you to search **materials** within
-                        NOMAD. NOMAD can often automatically detect the material
-                        from individual calculations that contain the full
-                        atomistic structure and can then group the data by using
-                        these detected materials. This allows you to search
-                        individual materials which have properties that are
-                        aggregated from several entries. Following the link for
-                        a specific material will take you to the corresponding
-                        [NOMAD Encyclopedia](https://nomad-lab.eu/prod/rae/encyclopedia/#/search)
-                        page for that material. NOMAD Encyclopedia is a service
-                        that is specifically oriented towards materials property
-                        exploration.
-
-                        Notice that by default the properties that you search
-                        can be combined from several different entries. If
-                        instead you wish to search for a material with an
-                        individual entry fullfilling your search criteria,
-                        uncheck the **combine results from several
-                        entries**-checkbox.
-                    '''),
-                },
-                'pagination': {
-                    'order_by': 'chemical_formula_hill',
-                    'order': 'asc'
-                },
-                'columns': {
-                    'enable': [
-                        'chemical_formula_hill',
-                        'structural_type',
-                        'symmetry.structure_name',
-                        'symmetry.space_group_number',
-                        'symmetry.crystal_system',
-                    ],
-                    'options': {
-                        'chemical_formula_hill': {'label': 'Formula', 'align': 'left'},
-                        'structural_type': {'label': 'Dimensionality'},
-                        'symmetry.structure_name': {'label': 'Structure name'},
-                        'symmetry.space_group_number': {'label': 'Space group number'},
-                        'symmetry.crystal_system': {'label': 'Crystal system'},
-                        'symmetry.space_group_symbol': {'label': 'Space group symbol'},
-                        'material_id': {'label': 'Material ID'},
-                    }
-                },
-                'rows': {
-                    'actions': {'enable': True},
-                    'details': {'enable': False},
-                    'selection': {'enable': False}
-                },
-                'filter_menus': {
-                    'options': {
-                        'material': {'label': 'Material', 'level': 0},
-                        'elements': {'label': 'Elements / Formula', 'level': 1, 'size': 'large', 'menu_items': {}},
-                        'structure': {'label': 'Structure', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'method': {'label': 'Method', 'level': 0, 'menu_items': {}},
-                        'dft': {'label': 'DFT', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'gw': {'label': 'GW', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'projection': {'label': 'Projection', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'dmft': {'label': 'DMFT', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'workflow': {'label': 'Workflow', 'level': 0},
-                        'molecular_dynamics': {'label': 'Molecular dynamics', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'geometry_optimization': {'label': 'Geometry Optimization', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'properties': {'label': 'Properties', 'level': 0, 'size': 'small'},
-                        'electronic': {'label': 'Electronic', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'vibrational': {'label': 'Vibrational', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'mechanical': {'label': 'Mechanical', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'author': {'label': 'Author / Origin / Dataset', 'level': 0, 'size': 'medium', 'menu_items': {}},
-                        'metadata': {'label': 'Visibility / IDs / Schema', 'level': 0, 'size': 'small', 'menu_items': {}},
-                        'optimade': {'label': 'Optimade', 'level': 0, 'size': 'medium', 'menu_items': {}},
-                        'combine': {
-                            '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': inspect.cleandoc(r'''
-                        This page allows you to specifically seach **electronic
-                        lab notebooks (ELNs)** within NOMAD.  It is very similar
-                        to the entries search, but with a reduced filter set and
-                        specialized arrangement of default columns.
-                    '''),
-                },
-                'pagination': {
-                    'order_by': 'upload_create_time',
-                    'order': 'desc',
-                    'page_size': 20,
-                },
-                'columns': {
-                    'enable': [
-                        'entry_name',
-                        'entry_type',
-                        'upload_create_time',
-                        'authors'
-                    ],
-                    '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'},
-                        'results.eln.lab_ids': {'label': 'Lab IDs'},
-                        'results.eln.sections': {'label': 'Sections'},
-                        'results.eln.methods': {'label': 'Methods'},
-                        'results.eln.tags': {'label': 'Tags'},
-                        'results.eln.instruments': {'label': 'Instruments'},
-                        'mainfile': {'label': 'Mainfile', 'align': 'left'},
-                        'comment': {'label': 'Comment', 'align': 'left'},
-                        'references': {'label': 'References', 'align': 'left'},
-                        'datasets': {'label': 'Datasets', 'align': 'left'},
-                        'published': {'label': 'Access'}
-                    }
-                },
-                'rows': {
-                    'actions': {'enable': True},
-                    'details': {'enable': True},
-                    'selection': {'enable': True}
-                },
-                'filter_menus': {
-                    'options': {
-                        'material': {'label': 'Material', 'level': 0},
-                        'elements': {'label': 'Elements / Formula', 'level': 1, 'size': 'large', 'menu_items': {}},
-                        'eln': {'label': 'Electronic Lab Notebook', 'level': 0, 'size': 'small', 'menu_items': {}},
-                        'custom_quantities': {'label': 'User Defined Quantities', 'level': 0, 'size': 'large', 'menu_items': {}},
-                        'author': {'label': 'Author / Origin / Dataset', 'level': 0, 'size': 'medium', 'menu_items': {}},
-                        'metadata': {'label': 'Visibility / IDs / Schema', 'level': 0, 'size': 'small', 'menu_items': {}},
-                        'optimade': {'label': 'Optimade', 'level': 0, 'size': 'medium', 'menu_items': {}},
-                    }
-                },
-                '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': inspect.cleandoc(r'''
-                        This page allows you to spefically search **Electron
-                        Energy Loss Spectroscopy (EELS) experiments** within
-                        NOMAD. It is similar to the entries search, but with a
-                        reduced filter set and specialized arrangement of
-                        default columns.
-                    '''),
-                },
-                'pagination': {
-                    'order_by': 'upload_create_time',
-                    'order': 'desc',
-                    'page_size': 20,
-                },
-                'columns': {
-                    'enable': [
-                        'results.material.chemical_formula_hill',
-                        'results.properties.spectroscopy.eels.detector_type',
-                        'results.properties.spectroscopy.eels.resolution',
-                        'upload_create_time',
-                        'authors'
-                    ],
-                    'options': {
-                        'results.material.chemical_formula_hill': {'label': 'Formula', 'align': 'left'},
-                        'results.properties.spectroscopy.eels.detector_type': {'label': 'Detector type'},
-                        'results.properties.spectroscopy.eels.resolution': {'label': 'Resolution'},
-                        'upload_create_time': {'label': 'Upload time', 'align': 'left'},
-                        'authors': {'label': 'Authors', 'align': 'left'},
-                        'results.properties.spectroscopy.eels.min_energy': {},
-                        'results.properties.spectroscopy.eels.max_energy': {},
-                        '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'}
-                    }
-                },
-                'rows': {
-                    'actions': {'enable': True},
-                    'details': {'enable': True},
-                    'selection': {'enable': True}
-                },
-                'filter_menus': {
-                    'options': {
-                        'material': {'label': 'Material', 'level': 0},
-                        'elements': {'label': 'Elements / Formula', 'level': 1, 'size': 'large', 'menu_items': {}},
-                        'method': {'label': 'Method', 'level': 0, 'size': 'small'},
-                        'eels': {'label': 'EELS', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'author': {'label': 'Author / Origin / Dataset', 'level': 0, 'size': 'medium', 'menu_items': {}},
-                        'metadata': {'label': 'Visibility / IDs / Schema', 'level': 0, 'size': 'small', 'menu_items': {}},
-                        'optimade': {'label': 'Optimade', 'level': 0, 'size': 'medium', 'menu_items': {}},
-                    }
-                },
-                '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': inspect.cleandoc(r'''
-                        This page allows you to search **solar cell data**
-                        within NOMAD. The filter menu on the left and the shown
-                        default columns are specifically designed for solar cell
-                        exploration. The dashboard directly shows useful
-                        interactive statistics about the data.
-                    '''),
-                },
-                'pagination': {
-                    'order_by': 'results.properties.optoelectronic.solar_cell.efficiency',
-                    'order': 'desc',
-                    'page_size': 20,
-                },
-                'dashboard': {
-                    'widgets': [
-                        {
-                            'type': 'periodictable',
-                            'scale': 'linear',
-                            'quantity': 'results.material.elements',
-                            'layout': {
-                                'xxl': {'minH': 8, 'minW': 12, 'h': 8, 'w': 13, 'y': 0, 'x': 0},
-                                'xl': {'minH': 8, 'minW': 12, 'h': 8, 'w': 12, 'y': 0, 'x': 0},
-                                'lg': {'minH': 8, 'minW': 12, 'h': 8, 'w': 12, 'y': 0, 'x': 0},
-                                'md': {'minH': 8, 'minW': 12, 'h': 8, 'w': 12, 'y': 0, 'x': 0},
-                                'sm': {'minH': 8, 'minW': 12, 'h': 8, 'w': 12, 'y': 16, 'x': 0}
-                            }
-                        },
-                        {
-                            'type': 'scatterplot',
-                            'autorange': True,
-                            'size': 1000,
-                            'color': 'results.properties.optoelectronic.solar_cell.short_circuit_current_density',
-                            'y': 'results.properties.optoelectronic.solar_cell.efficiency',
-                            'x': 'results.properties.optoelectronic.solar_cell.open_circuit_voltage',
-                            'layout': {
-                                'xxl': {'minH': 3, 'minW': 3, 'h': 8, 'w': 12, 'y': 0, 'x': 24},
-                                'xl': {'minH': 3, 'minW': 3, 'h': 8, 'w': 9, 'y': 0, 'x': 12},
-                                'lg': {'minH': 3, 'minW': 3, 'h': 6, 'w': 12, 'y': 8, 'x': 0},
-                                'md': {'minH': 3, 'minW': 3, 'h': 6, 'w': 9, 'y': 8, 'x': 0},
-                                'sm': {'minH': 3, 'minW': 3, 'h': 5, 'w': 6, 'y': 0, 'x': 0}
-                            }
-                        },
-                        {
-                            'type': 'scatterplot',
-                            'autorange': True,
-                            'size': 1000,
-                            'color': 'results.properties.optoelectronic.solar_cell.device_architecture',
-                            'y': 'results.properties.optoelectronic.solar_cell.efficiency',
-                            'x': 'results.properties.optoelectronic.solar_cell.open_circuit_voltage',
-                            'layout': {
-                                'xxl': {'minH': 3, 'minW': 3, 'h': 8, 'w': 11, 'y': 0, 'x': 13},
-                                'xl': {'minH': 3, 'minW': 3, 'h': 8, 'w': 9, 'y': 0, 'x': 21},
-                                'lg': {'minH': 3, 'minW': 3, 'h': 6, 'w': 12, 'y': 14, 'x': 0},
-                                'md': {'minH': 3, 'minW': 3, 'h': 6, 'w': 9, 'y': 8, 'x': 9},
-                                'sm': {'minH': 3, 'minW': 3, 'h': 5, 'w': 6, 'y': 0, 'x': 6}
-                            }
-                        },
-                        {
-                            'type': 'terms',
-                            'inputfields': True,
-                            'scale': 'linear',
-                            'quantity': 'results.properties.optoelectronic.solar_cell.device_stack',
-                            'layout': {
-                                'xxl': {'minH': 3, 'minW': 3, 'h': 6, 'w': 6, 'y': 8, 'x': 14},
-                                'xl': {'minH': 3, 'minW': 3, 'h': 6, 'w': 6, 'y': 8, 'x': 14},
-                                'lg': {'minH': 3, 'minW': 3, 'h': 6, 'w': 6, 'y': 0, 'x': 12},
-                                'md': {'minH': 3, 'minW': 3, 'h': 4, 'w': 6, 'y': 4, 'x': 12},
-                                'sm': {'minH': 3, 'minW': 3, 'h': 6, 'w': 4, 'y': 10, 'x': 0}
-                            }
-                        },
-                        {
-                            'type': 'histogram',
-                            'autorange': True,
-                            'nbins': 30,
-                            'scale': '1/4',
-                            'quantity': 'results.properties.optoelectronic.solar_cell.illumination_intensity',
-                            'layout': {
-                                'xxl': {'minH': 3, 'minW': 3, 'h': 3, 'w': 8, 'y': 8, 'x': 0},
-                                'xl': {'minH': 3, 'minW': 3, 'h': 3, 'w': 8, 'y': 11, 'x': 0},
-                                'lg': {'minH': 3, 'minW': 3, 'h': 4, 'w': 12, 'y': 12, 'x': 12},
-                                'md': {'minH': 3, 'minW': 3, 'h': 3, 'w': 8, 'y': 17, 'x': 10},
-                                'sm': {'minH': 3, 'minW': 3, 'h': 3, 'w': 8, 'y': 13, 'x': 4}
-                            }
-                        },
-                        {
-                            'type': 'terms',
-                            'inputfields': True,
-                            'scale': 'linear',
-                            'quantity': 'results.properties.optoelectronic.solar_cell.absorber_fabrication',
-                            'layout': {
-                                'xxl': {'minH': 3, 'minW': 3, 'h': 6, 'w': 6, 'y': 8, 'x': 8},
-                                'xl': {'minH': 3, 'minW': 3, 'h': 6, 'w': 6, 'y': 8, 'x': 8},
-                                'lg': {'minH': 3, 'minW': 3, 'h': 6, 'w': 6, 'y': 0, 'x': 18},
-                                'md': {'minH': 3, 'minW': 3, 'h': 4, 'w': 6, 'y': 0, 'x': 12},
-                                'sm': {'minH': 3, 'minW': 3, 'h': 5, 'w': 4, 'y': 5, 'x': 0}
-                            }
-                        },
-                        {
-                            'type': 'histogram',
-                            'inputfields': False,
-                            'autorange': False,
-                            'nbins': 30,
-                            'scale': '1/4',
-                            'quantity': 'results.properties.electronic.band_structure_electronic.band_gap.value',
-                            'layout': {
-                                'xxl': {'minH': 3, 'minW': 8, 'h': 3, 'w': 8, 'y': 11, 'x': 0},
-                                'xl': {'minH': 3, 'minW': 8, 'h': 3, 'w': 8, 'y': 8, 'x': 0},
-                                'lg': {'minH': 3, 'minW': 8, 'h': 4, 'w': 12, 'y': 16, 'x': 12},
-                                'md': {'minH': 3, 'minW': 8, 'h': 3, 'w': 8, 'y': 14, 'x': 10},
-                                'sm': {'minH': 3, 'minW': 8, 'h': 3, 'w': 8, 'y': 10, 'x': 4}
-                            }
-                        },
-                        {
-                            'type': 'terms',
-                            'inputfields': True,
-                            'scale': 'linear',
-                            'quantity': 'results.properties.optoelectronic.solar_cell.electron_transport_layer',
-                            'layout': {
-                                'xxl': {'minH': 3, 'minW': 3, 'h': 6, 'w': 6, 'y': 8, 'x': 20},
-                                'xl': {'minH': 3, 'minW': 3, 'h': 6, 'w': 5, 'y': 8, 'x': 25},
-                                'lg': {'minH': 3, 'minW': 3, 'h': 6, 'w': 6, 'y': 6, 'x': 18},
-                                'md': {'minH': 3, 'minW': 3, 'h': 6, 'w': 5, 'y': 14, 'x': 0},
-                                'sm': {'minH': 3, 'minW': 3, 'h': 5, 'w': 4, 'y': 5, 'x': 4}
-                            }
-                        },
-                        {
-                            'type': 'terms',
-                            'inputfields': True,
-                            'scale': 'linear',
-                            'quantity': 'results.properties.optoelectronic.solar_cell.hole_transport_layer',
-                            'layout': {
-                                'xxl': {'minH': 3, 'minW': 3, 'h': 6, 'w': 6, 'y': 8, 'x': 26},
-                                'xl': {'minH': 3, 'minW': 3, 'h': 6, 'w': 5, 'y': 8, 'x': 20},
-                                'lg': {'minH': 3, 'minW': 3, 'h': 6, 'w': 6, 'y': 6, 'x': 12},
-                                'md': {'minH': 3, 'minW': 3, 'h': 6, 'w': 5, 'y': 14, 'x': 5},
-                                'sm': {'minH': 3, 'minW': 3, 'h': 5, 'w': 4, 'y': 5, 'x': 8}
-                            }
-                        }
-                    ]
-                },
-                'columns': {
-                    'enable': [
-                        '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'
-                    ],
-                    'options': {
-                        'results.material.chemical_formula_descriptive': {'label': 'Descriptive Formula', 'align': 'left'},
-                        'results.properties.optoelectronic.solar_cell.efficiency': {
-                            'label': 'Efficiency (%)',
-                            'format': {
-                                'decimals': 2,
-                                'mode': 'standard',
-                            },
-                        },
-                        'results.properties.optoelectronic.solar_cell.open_circuit_voltage': {
-                            'label': 'Open circuit voltage',
-                            'unit': 'V',
-                            'format': {
-                                'decimals': 3,
-                                'mode': 'standard',
-                            },
-                        },
-                        'results.properties.optoelectronic.solar_cell.short_circuit_current_density': {
-                            'label': 'Short circuit current density',
-                            'unit': 'A/m**2',
-                            'format': {
-                                'decimals': 3,
-                                'mode': 'standard',
-                            },
-                        },
-                        'results.properties.optoelectronic.solar_cell.fill_factor': {
-                            'label': 'Fill factor',
-                            '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'},
-                        'results.properties.optoelectronic.solar_cell.illumination_intensity': {
-                            'label': 'Illum. intensity',
-                            'unit': 'W/m**2',
-                            'format': {'decimals': 3, 'mode': 'standard'},
-                        },
-                        'results.eln.lab_ids': {'label': 'Lab IDs'},
-                        'results.eln.sections': {'label': 'Sections'},
-                        'results.eln.methods': {'label': 'Methods'},
-                        'results.eln.tags': {'label': 'Tags'},
-                        'results.eln.instruments': {'label': 'Instruments'},
-                        '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'},
-                    },
-                },
-                'rows': {
-                    'actions': {'enable': True},
-                    'details': {'enable': True},
-                    'selection': {'enable': True}
-                },
-                'filter_menus': {
-                    'options': {
-                        'material': {'label': 'Absorber Material', 'level': 0},
-                        'elements': {'label': 'Elements / Formula', 'level': 1, 'size': 'large', 'menu_items': {}},
-                        'structure': {'label': 'Structure', 'level': 1, 'size': 'small', 'menu_items': {}},
-                        'electronic': {'label': 'Electronic Properties', 'level': 0, 'size': 'small', 'menu_items': {}},
-                        'solarcell': {'label': 'Solar Cell Properties', 'level': 0, 'size': 'small', 'menu_items': {}},
-                        'eln': {'label': 'Electronic Lab Notebook', 'level': 0, 'size': 'small', 'menu_items': {}},
-                        'custom_quantities': {'label': 'User Defined Quantities', 'level': 0, 'size': 'large', 'menu_items': {}},
-                        'author': {'label': 'Author / Origin / Dataset', 'level': 0, 'size': 'medium', 'menu_items': {}},
-                        'metadata': {'label': 'Visibility / IDs / Schema', 'level': 0, 'size': 'small', 'menu_items': {}},
-                        'optimade': {'label': 'Optimade', 'level': 0, 'size': 'medium', 'menu_items': {}},
-                    }
-                },
-                'filters': {
-                    'exclude': ['mainfile', 'entry_name', 'combine']
-                },
-                'filters_locked': {
-                    'sections': 'nomad.datamodel.results.SolarCell'
-                }
-            },
-        }
-    }
-    example_uploads: dict = {
-        'include': None,
-        'exclude': None
-    }
-
-
-ui = UIConfig()
-
-
-def north_url(ssl: bool = True):
-    return api_url(ssl=ssl, api='north', api_host=north.hub_host, api_port=north.hub_port)
-
-
-def normalize_loglevel(value, default_level=logging.INFO):
-    plain_value = value
-    if plain_value is None:
-        return default_level
-    else:
-        try:
-            return int(plain_value)
-        except ValueError:
-            return getattr(logging, plain_value)
-
-
-_transformations = {
-    'console_log_level': normalize_loglevel,
-    'logstash_level': normalize_loglevel
-}
-
-
-# use std python logger, since logging is not configured while loading configuration
-logger = logging.getLogger(__name__)
-
-
-def _check_config():
-    """Used to check that the current configuration is valid. Should only be
-    called once after the final config is loaded.
-
-    Raises:
-        AssertionError: if there is a contradiction or invalid values in the
-            config file settings.
-    """
-    # TODO more if this should be translated into pydantic validations.
-
-    # The AFLOW symmetry information is checked once on import
-    proto_symmetry_tolerance = normalize.prototype_symmetry_tolerance
-    symmetry_tolerance = normalize.symmetry_tolerance
-    if proto_symmetry_tolerance != symmetry_tolerance:
-        raise AssertionError(
-            "The AFLOW prototype information is outdated due to changed tolerance "
-            "for symmetry detection. Please update the AFLOW prototype information "
-            "by running the CLI command 'nomad admin ops prototype-update "
-            "--matches-only'."
-        )
-
-    if normalize.springer_db_path and not os.path.exists(normalize.springer_db_path):
-        normalize.springer_db_path = None
-
-    if keycloak.public_server_url is None:
-        keycloak.public_server_url = keycloak.server_url
-
-    def get_external_path(path):
-        if fs.external_working_directory and not os.path.isabs(path):
-            return os.path.join(fs.external_working_directory, path)
-        return path
-
-    if fs.staging_external is None:
-        fs.staging_external = get_external_path(fs.staging)
-
-    if fs.public_external is None:
-        fs.public_external = get_external_path(fs.public)
-
-    if fs.north_home_external is None:
-        fs.north_home_external = get_external_path(fs.north_home)
-
-    ui.north_enabled = north.enabled
-
-
-def _merge(a: dict, b: dict, path: List[str] = None) -> dict:
-    '''
-    Recursively merges b into a. Will add new key-value pairs, and will
-    overwrite existing key-value pairs. Notice that this mutates the original
-    dictionary a and if you want to return a copy, you will want to first
-    (deep)copy the original dictionary.
-    '''
-    if path is None: path = []
-    for key in b:
-        if key in a:
-            if isinstance(a[key], dict) and isinstance(b[key], dict):
-                _merge(a[key], b[key], path + [str(key)])
-            else:
-                a[key] = b[key]
-        else:
-            a[key] = b[key]
-    return a
-
-
-def _apply(key, value, raise_error: bool = True) -> None:
-    '''
-    Changes the config according to given key and value. The first part of a key
-    (with ``_`` as a separator) is interpreted as a group of settings. E.g. ``fs_staging``
-    leading to ``config.fs.staging``.
-    '''
-    full_key = key
-    try:
-        group_key, config_key = full_key.split('_', 1)
-    except Exception:
-        if raise_error:
-            logger.error(f'config key does not exist: {full_key}')
-        return
-
-    current = globals()
-
-    current_value: Any = None
-    if group_key not in current:
-        if key not in current:
-            if raise_error:
-                logger.error(f'config key does not exist: {full_key}')
-            return
-    else:
-        current = current[group_key]
-        if not isinstance(current, NomadSettings):
-            if raise_error:
-                logger.error(f'config key does not exist: {full_key}')
-            return
-
-        try:
-            current_value = getattr(current, config_key)
-        except AttributeError:
-            if raise_error:
-                logger.error(f'config key does not exist: {full_key}')
-            return
-
-        key = config_key
-
-    try:
-        if current_value is not None and not isinstance(value, type(current_value)):
-            value = _transformations.get(full_key, type(current_value))(value)
-
-        if isinstance(value, dict):
-            value = _merge(current_value, value)
-
-        if isinstance(current, dict):
-            current[key] = value
-        else:
-            setattr(current, key, value)
-        logger.info(f'set config setting {full_key}={value}')
-    except Exception as e:
-        logger.error(f'cannot set config setting {full_key}={value}: {e}')
-
-
-def _apply_env_variables():
-    kwargs = {
-        key[len('NOMAD_'):].lower(): value
-        for key, value in os.environ.items()
-        if key.startswith('NOMAD_') and key != 'NOMAD_CONFIG'}
-
-    for key, value in kwargs.items():
-        _apply(key, value, raise_error=False)
-
-
-def _apply_nomad_yaml():
-    config_file = os.environ.get('NOMAD_CONFIG', 'nomad.yaml')
-
-    if not os.path.exists(config_file):
-        return
-
-    with open(config_file, 'r') as stream:
-        try:
-            config_data = yaml.load(stream, Loader=getattr(yaml, 'FullLoader'))
-        except yaml.YAMLError as e:
-            logger.error(f'cannot read nomad config: {e}')
-            return
-
-    if not config_data:
-        return
-
-    for key, value in config_data.items():
-        if isinstance(value, dict):
-            group_key = key
-            for key, value in value.items():
-                _apply(f'{group_key}_{key}', value)
-        else:
-            _apply(key, value)
-
-
-def load_config():
-    '''
-    Loads the configuration from nomad.yaml and environment.
-    '''
-    _apply_nomad_yaml()
-    _apply_env_variables()
-    _check_config()
-
-
-load_config()
diff --git a/nomad/config/__init__.py b/nomad/config/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..c38b98da0a71db2fd2bff8e4c61b820ce2625506
--- /dev/null
+++ b/nomad/config/__init__.py
@@ -0,0 +1,310 @@
+#
+# Copyright The NOMAD Authors.
+#
+# This file is part of NOMAD. See https://nomad-lab.eu for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+'''
+This module describes all configurable parameters for the nomad python code. The
+configuration is used for all executed python code including API, worker, CLI, and other
+scripts. To use the configuration in your own scripts or new modules, simply import
+this module.
+
+All parameters are structured into objects for two reasons. First, to have
+categories. Second, to allow runtime manipulation that is not effected
+by python import logic. The categories are choosen along infrastructure components:
+``mongo``, ``elastic``, etc.
+
+This module also provides utilities to read the configuration from environment variables
+and .yaml files. This is done automatically on import. The precedence is env over .yaml
+over defaults.
+'''
+
+import logging
+import os
+import os.path
+import yaml
+import warnings
+from typing import List, Any, Union
+
+from nomad.config.models import (
+    NomadSettings, Services, Meta, Oasis, NORTH, RabbitMQ, Celery, FS, Elastic, Keycloak, Mongo, Logstash, Tests, Mail, Normalize, Resources, Client, DataCite, GitLab, Process, Reprocess, RFC3161Timestamp, BundleExport, BundleImport, Archive, UI
+)
+
+
+warnings.filterwarnings('ignore', message='numpy.dtype size changed')
+warnings.filterwarnings('ignore', message='numpy.ufunc size changed')
+warnings.filterwarnings('ignore', category=DeprecationWarning)
+
+
+def api_url(ssl: bool = True, api: str = 'api', api_host: str = None, api_port: int = None):
+    '''
+    Returns the url of the current running nomad API. This is for server-side use.
+    This is not the NOMAD url to use as a client, use `nomad.config.client.url` instead.
+    '''
+    if api_port is None:
+        api_port = services.api_port
+    if api_host is None:
+        api_host = services.api_host
+    protocol = 'https' if services.https and ssl else 'http'
+    host_and_port = api_host
+    if api_port not in [80, 443]:
+        host_and_port += ':' + str(api_port)
+    base_path = services.api_base_path.strip('/')
+    return f'{protocol}://{host_and_port}/{base_path}/{api}'
+
+
+def gui_url(page: str = None):
+    base = api_url(True)[:-3]
+    if base.endswith('/'):
+        base = base[:-1]
+
+    if page is not None:
+        return '%s/gui/%s' % (base, page)
+
+    return '%s/gui' % base
+
+
+def rabbitmq_url():
+    return 'pyamqp://%s:%s@%s//' % (rabbitmq.user, rabbitmq.password, rabbitmq.host)
+
+
+def north_url(ssl: bool = True):
+    return api_url(ssl=ssl, api='north', api_host=north.hub_host, api_port=north.hub_port)
+
+
+def hub_url():
+    return f'http://{north.hub_host}:{north.hub_port}{services.api_base_path}/north/hub'
+
+
+services = Services()
+meta = Meta(deployment_url=api_url())
+oasis = Oasis()
+north = NORTH()
+rabbitmq = RabbitMQ()
+celery = Celery()
+fs = FS()
+elastic = Elastic()
+keycloak = Keycloak()
+mongo = Mongo()
+logstash = Logstash()
+tests = Tests()
+mail = Mail()
+normalize = Normalize()
+resources = Resources()
+client = Client()
+datacite = DataCite()
+gitlab = GitLab()
+process = Process()
+reprocess = Reprocess()
+rfc3161_timestamp = RFC3161Timestamp()
+bundle_export = BundleExport()
+bundle_import = BundleImport()
+archive = Archive()
+ui = UI()
+
+
+def normalize_loglevel(value, default_level=logging.INFO):
+    plain_value = value
+    if plain_value is None:
+        return default_level
+    else:
+        try:
+            return int(plain_value)
+        except ValueError:
+            return getattr(logging, plain_value)
+
+
+_transformations = {
+    'console_log_level': normalize_loglevel,
+    'logstash_level': normalize_loglevel
+}
+
+
+# use std python logger, since logging is not configured while loading configuration
+logger = logging.getLogger(__name__)
+
+
+def _check_config():
+    '''Used to check that the current configuration is valid. Should only be
+    called once after the final config is loaded.
+
+    Raises:
+        AssertionError: if there is a contradiction or invalid values in the
+            config file settings.
+    '''
+    # TODO more if this should be translated into pydantic validations.
+
+    # The AFLOW symmetry information is checked once on import
+    proto_symmetry_tolerance = normalize.prototype_symmetry_tolerance
+    symmetry_tolerance = normalize.symmetry_tolerance
+    if proto_symmetry_tolerance != symmetry_tolerance:
+        raise AssertionError(
+            "The AFLOW prototype information is outdated due to changed tolerance "
+            "for symmetry detection. Please update the AFLOW prototype information "
+            "by running the CLI command 'nomad admin ops prototype-update "
+            "--matches-only'."
+        )
+
+    if normalize.springer_db_path and not os.path.exists(normalize.springer_db_path):
+        normalize.springer_db_path = None
+
+    if keycloak.public_server_url is None:
+        keycloak.public_server_url = keycloak.server_url
+
+    def get_external_path(path):
+        if fs.external_working_directory and not os.path.isabs(path):
+            return os.path.join(fs.external_working_directory, path)
+        return path
+
+    if fs.staging_external is None:
+        fs.staging_external = get_external_path(fs.staging)
+
+    if fs.public_external is None:
+        fs.public_external = get_external_path(fs.public)
+
+    if fs.north_home_external is None:
+        fs.north_home_external = get_external_path(fs.north_home)
+
+    ui.north.enabled = north.enabled
+
+
+def _merge(a: Union[dict, NomadSettings], b: dict, path: List[str] = None) -> Union[dict, NomadSettings]:
+    '''
+    Recursively merges b into a. Will add new key-value pairs, and will
+    overwrite existing key-value pairs. Notice that this mutates the original
+    dictionary/model a and if you want to return a copy, you will want to first
+    (deep)copy the original value.
+    '''
+    def has(target, key):
+        return key in target if isinstance(target, dict) else hasattr(target, key)
+
+    def set(target, key, value):
+        if isinstance(target, dict):
+            target[key] = value
+        else:
+            setattr(target, key, value)
+
+    def get(target, key):
+        return target[key] if isinstance(target, dict) else getattr(target, key)
+
+    if path is None: path = []
+    for key in b:
+        value = b[key]
+        if has(a, key):
+            child = get(a, key)
+            if isinstance(value, dict) and isinstance(child, (NomadSettings, dict)):
+                _merge(child, value, path + [str(key)])
+            else:
+                set(a, key, value)
+        else:
+            set(a, key, value)
+    return a
+
+
+def _apply(key, value, raise_error: bool = True) -> None:
+    '''
+    Changes the config according to given key and value. The first part of a key
+    (with ``_`` as a separator) is interpreted as a group of settings. E.g. ``fs_staging``
+    leading to ``config.fs.staging``.
+    '''
+    full_key = key
+    try:
+        group_key, config_key = full_key.split('_', 1)
+    except Exception:
+        if raise_error:
+            logger.error(f'config key does not exist: {full_key}')
+        return
+
+    current = globals()
+
+    current_value: Any = None
+    if group_key not in current:
+        if key not in current:
+            if raise_error:
+                logger.error(f'config key does not exist: {full_key}')
+            return
+    else:
+        current = current[group_key]
+        if not isinstance(current, NomadSettings):
+            if raise_error:
+                logger.error(f'config key does not exist: {full_key}')
+            return
+
+        try:
+            current_value = getattr(current, config_key)
+        except AttributeError:
+            if raise_error:
+                logger.error(f'config key does not exist: {full_key}')
+            return
+
+        key = config_key
+
+    try:
+        if current_value is not None and not isinstance(value, type(current_value)):
+            value = _transformations.get(full_key, type(current_value))(value)
+        if isinstance(value, dict):
+            value = _merge(current_value, value)
+        setattr(current, key, value)
+        logger.info(f'set config setting {full_key}={value}')
+    except Exception as e:
+        logger.error(f'cannot set config setting {full_key}={value}: {e}')
+
+
+def _apply_env_variables():
+    kwargs = {
+        key[len('NOMAD_'):].lower(): value
+        for key, value in os.environ.items()
+        if key.startswith('NOMAD_') and key != 'NOMAD_CONFIG'}
+
+    for key, value in kwargs.items():
+        _apply(key, value, raise_error=False)
+
+
+def _apply_nomad_yaml():
+    config_file = os.environ.get('NOMAD_CONFIG', 'nomad.yaml')
+
+    if not os.path.exists(config_file):
+        return
+
+    with open(config_file, 'r') as stream:
+        try:
+            config_data = yaml.load(stream, Loader=getattr(yaml, 'FullLoader'))
+        except yaml.YAMLError as e:
+            logger.error(f'cannot read nomad config: {e}')
+            return
+
+    if not config_data:
+        return
+
+    for key, value in config_data.items():
+        if isinstance(value, dict):
+            group_key = key
+            for key, value in value.items():
+                _apply(f'{group_key}_{key}', value)
+        else:
+            _apply(key, value)
+
+
+def load_config():
+    '''
+    Loads the configuration from nomad.yaml and environment.
+    '''
+    _apply_nomad_yaml()
+    _apply_env_variables()
+    _check_config()
+
+
+load_config()
diff --git a/nomad/config/models.py b/nomad/config/models.py
new file mode 100644
index 0000000000000000000000000000000000000000..5eee70114faddad3392d6acfd7b51d3929597c1a
--- /dev/null
+++ b/nomad/config/models.py
@@ -0,0 +1,1619 @@
+#
+# Copyright The NOMAD Authors.
+#
+# This file is part of NOMAD. See https://nomad-lab.eu for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import os
+import json
+from enum import Enum
+import logging
+import inspect
+from typing import TypeVar, List, Dict, Any, Union, Optional, cast
+from typing_extensions import Literal, Annotated  # type: ignore
+from pydantic import BaseModel, Field, validator, Extra  # pylint: disable=unused-import
+from pkg_resources import get_distribution, DistributionNotFound
+try:
+    __version__ = get_distribution('nomad-lab').version
+except DistributionNotFound:
+    # package is not installed
+    pass
+
+
+NomadSettingsBound = TypeVar('NomadSettingsBound', bound='NomadSettings')
+
+
+class NomadSettings(BaseModel):
+    def customize(
+            self: NomadSettingsBound,
+            custom_settings: Union[NomadSettingsBound, Dict[str, Any]]) -> NomadSettingsBound:
+        '''
+        Returns a new config object, created by taking a copy of the current config and
+        updating it with the settings defined in `custom_settings`. The `custom_settings` can
+        be a NomadSettings or a dictionary (in the latter case it must not contain any new keys
+        (keys not defined in this NomadSettings). If it does, an exception will be raised.
+        '''
+
+        rv = self.copy(deep=True)
+
+        if custom_settings:
+            if isinstance(custom_settings, BaseModel):
+                for field_name in custom_settings.__fields__.keys():
+                    try:
+                        setattr(rv, field_name, getattr(custom_settings, field_name))
+                    except Exception:
+                        raise AssertionError(f'Invalid setting: {field_name}')
+            elif isinstance(custom_settings, dict):
+                for key, value in custom_settings.items():
+                    if value is None:
+                        continue
+                    try:
+                        setattr(rv, key, value)
+                    except Exception:
+                        raise AssertionError(f'Invalid setting: {field_name}')
+
+        return cast(NomadSettingsBound, rv)
+
+
+class Services(NomadSettings):
+    '''
+    Contains basic configuration of the NOMAD services (app, worker, north).
+    '''
+    api_host = Field('localhost', description='''
+        The external hostname that clients can use to reach this NOMAD installation.
+    ''')
+    api_port = Field(8000, description='''
+        The port used to expose the NOMAD app and api to clients.
+    ''')
+    api_base_path = Field('/fairdi/nomad/latest', description='''
+        The base path prefix for the NOMAD app and api.
+    ''')
+    api_secret = Field('defaultApiSecret', description='''
+        A secret that is used to issue download and other tokens.
+    ''')
+    https = Field(False, description='''
+        Set to `True`, if external clients are using *SSL* to connect to this installation.
+        Requires to setup a reverse-proxy (e.g. the one used in the docker-compose
+        based installation) that handles the *SSL* encryption.
+    ''')
+    https_upload = Field(False, description='''
+        Set to `True`, if upload curl commands should suggest the use of SSL for file
+        uploads. This can be configured independently of `https` to suggest large file
+        via regular HTTP.
+    ''')
+    admin_user_id = Field('00000000-0000-0000-0000-000000000000', description='''
+        The admin user `user_id`. All users are treated the same; there are no
+        particular authorization information attached to user accounts. However, the
+        API will grant the user with the given `user_id` more rights, e.g. using the
+        `admin` owner setting in accessing data.
+    ''')
+
+    encyclopedia_base = Field(
+        'https://nomad-lab.eu/prod/rae/encyclopedia/#', description='''
+            This enables links to the given *encyclopedia* installation in the UI.
+        ''')
+    aitoolkit_enabled = Field(False, description='''
+        If true, the UI will show a menu with links to the AI Toolkit notebooks on
+        `nomad-lab.eu`.
+    ''')
+
+    console_log_level = Field(logging.WARNING, description='''
+        The log level that controls console logging for all NOMAD services (app, worker, north).
+        The level is given in Python `logging` log level numbers.
+    ''')
+
+    upload_limit = Field(10, description='''
+        The maximum allowed unpublished uploads per user. If a user exceeds this
+        amount, the user cannot add more uploads.
+    ''')
+    force_raw_file_decoding = Field(False, description='''
+        By default, text raw-files are interpreted with utf-8 encoding. If this fails,
+        the actual encoding is guessed. With this setting, we force to assume iso-8859-1
+        encoding, if a file is not decodable with utf-8.
+    ''')
+    max_entry_download = Field(50000, description='''
+        There is an inherent limit in page-based pagination with Elasticsearch. If you
+        increased this limit with your Elasticsearch, you can also adopt this setting
+        accordingly, changing the maximum amount of entries that can be paginated with
+        page-base pagination.
+
+        Page-after-value-based pagination is independent and can be used without limitations.
+    ''')
+    unavailable_value = Field('unavailable', description='''
+        Value that is used in `results` section Enum fields (e.g. system type, spacegroup, etc.)
+        to indicate that the value could not be determined.
+    ''')
+
+
+class Meta(NomadSettings):
+    '''
+    Metadata about the deployment and how it is presented to clients.
+    '''
+    version = Field(__version__, description='The NOMAD version string.')
+    commit = Field('', description='The source-code commit that this installation\'s NOMAD version is build from.')
+    deployment = Field(
+        'devel', description='Human-friendly name of this nomad deployment.')
+    deployment_url: str = Field(description='The NOMAD deployment\'s url (api url).')
+    label: str = Field(None, description='''
+        An additional log-stash data key-value pair added to all logs. Can be used
+        to differentiate deployments when analyzing logs.
+    ''')
+    service = Field('unknown nomad service', description='''
+        Name for the service that is added to all logs. Depending on how NOMAD is
+        installed, services get a name (app, worker, north) automatically.
+    ''')
+
+    name = Field(
+        'NOMAD',
+        description='Web-site title for the NOMAD UI.',
+        deprecated=True)
+    homepage = Field(
+        'https://nomad-lab.eu', description='Provider homepage.', deprecated=True)
+    source_url = Field(
+        'https://gitlab.mpcdf.mpg.de/nomad-lab/nomad-FAIR',
+        description='URL of the NOMAD source-code repository.',
+        deprecated=True)
+
+    maintainer_email = Field(
+        'markus.scheidgen@physik.hu-berlin.de',
+        description='Email of the NOMAD deployment maintainer.')
+    beta: dict = Field(None, description='''
+        Additional data that describes how the deployment is labeled as a beta-version in the UI.
+    ''')
+
+
+class Oasis(NomadSettings):
+    '''
+    Settings related to the configuration of a NOMAD Oasis deployment.
+    '''
+    is_oasis = Field(False, description='Set to `True` to indicate that this deployment is a NOMAD Oasis.')
+    allowed_users: str = Field(None, description='''
+        A list of usernames or user account emails. These represent a white-list of
+        allowed users. With this, users will need to login right-away and only the
+        listed users might use this deployment. All API requests must have authentication
+        information as well.''')
+    uses_central_user_management = Field(False, description='''
+        Set to True to use the central user-management. Typically the NOMAD backend is
+        using the configured `keycloak` to access user data. With this, the backend will
+        use the API of the central NOMAD (`central_nomad_deployment_url`) instead.
+    ''')
+    central_nomad_deployment_url = Field('https://nomad-lab.eu/prod/v1/api', description='''
+        The URL of the API of the NOMAD deployment that is considered the *central* NOMAD.
+    ''')
+
+
+_jupyterhub_config_description = '''
+    This setting is forwarded to jupyterhub; refer to the jupyterhub
+    [documentation](https://jupyterhub.readthedocs.io/en/stable/api/app.html#).
+'''
+
+
+class NORTHToolMaintainer(BaseModel):
+    name: str
+    email: str
+
+
+class NORTHTool(BaseModel):
+    image: str
+    description: str = None
+    short_description: str = None
+    cmd: str = None
+    privileged: bool = None
+    path_prefix: str = None
+    mount_path: str = None
+    icon: str = None
+    file_extensions: List[str] = []
+    maintainer: List[NORTHToolMaintainer] = []
+
+
+class NORTH(NomadSettings):
+    '''
+    Settings related to the operation of the NOMAD remote tools hub service *north*.
+    '''
+    enabled: Optional[str] = Field(description='''
+        Enables or disables the NORTH API and UI views. This is independent of
+        whether you run a jupyter hub or not.
+    ''')
+    hub_connect_ip: str = Field(None, description='''
+        Overwrites the default hostname that can be used from within a north container
+        to reach the host system.
+
+        Typically has to be set for non Linux hosts. Set this to `host.docker.internal`
+        on windows/macos.
+    ''')
+    hub_connect_url: str = Field(None, description=_jupyterhub_config_description)
+    hub_ip = Field('0.0.0.0', description=_jupyterhub_config_description)
+    docker_network: str = Field(None, description=_jupyterhub_config_description)
+    hub_host = Field('localhost', description='''
+        The internal host name that NOMAD services use to connect to the jupyterhub API.
+    ''')
+    hub_port = Field(9000, description='''
+        The internal port that NOMAD services use to connect to the jupyterhub API.
+    ''')
+    jupyterhub_crypt_key: str = Field(None, description=_jupyterhub_config_description)
+
+    nomad_host: str = Field(
+        None, description='The NOMAD app host name that spawned containers use.')
+    windows = Field(
+        True, description='Enable windows OS hacks.')
+
+    tools: Union[str, Dict[str, NORTHTool]] = Field(
+        'dependencies/nomad-remote-tools-hub/tools.json',
+        description='The available north tools. Either the tools definitions as dict or a path to a .json file.')
+
+    hub_service_api_token: str = Field('secret-token', description='''
+        A secret token shared between NOMAD and the NORTH jupyterhub.
+        This needs to be the token of an admin service.''')
+
+    @validator('tools', pre=True, always=True)
+    def load_tools(cls, v):  # pylint: disable=no-self-argument
+        if isinstance(v, str):
+            # interpret as file
+            with open(v, 'rt') as f:
+                v = json.load(f)
+
+        return v
+
+
+class RabbitMQ(NomadSettings):
+    '''
+    Configures how NOMAD is connecting to RabbitMQ.
+    '''
+    host = Field('localhost', description='The name of the host that runs RabbitMQ.')
+    user = Field('rabbitmq', description='The RabbitMQ user that is used to connect.')
+    password = Field('rabbitmq', description='The password that is used to connect.')
+
+
+CELERY_WORKER_ROUTING = 'worker'
+CELERY_QUEUE_ROUTING = 'queue'
+
+
+class Celery(NomadSettings):
+    max_memory = 64e6  # 64 GB
+    timeout = 1800  # 1/2 h
+    acks_late = False
+    routing = CELERY_QUEUE_ROUTING
+    priorities = {
+        'Upload.process_upload': 5,
+        'Upload.delete_upload': 9,
+        'Upload.publish_upload': 10
+    }
+
+
+class FS(NomadSettings):
+    tmp = '.volumes/fs/tmp'
+    staging = '.volumes/fs/staging'
+    staging_external: str = None
+    public = '.volumes/fs/public'
+    public_external: str = None
+    north_home = '.volumes/fs/north/users'
+    north_home_external: str = None
+    local_tmp = '/tmp'
+    prefix_size = 2
+    archive_version_suffix = 'v1'
+    working_directory = os.getcwd()
+    external_working_directory: str = None
+
+
+class Elastic(NomadSettings):
+    host = 'localhost'
+    port = 9200
+    timeout = 60
+    bulk_timeout = 600
+    bulk_size = 1000
+    entries_per_material_cap = 1000
+    entries_index = 'nomad_entries_v1'
+    materials_index = 'nomad_materials_v1'
+
+
+class Keycloak(NomadSettings):
+    server_url = 'https://nomad-lab.eu/fairdi/keycloak/auth/'
+    public_server_url: str = None
+    realm_name = 'fairdi_nomad_prod'
+    username = 'admin'
+    password = 'password'
+    client_id = 'nomad_public'
+    client_secret: str = None
+
+
+class Mongo(NomadSettings):
+    ''' Connection and usage settings for MongoDB.'''
+    host: str = Field('localhost', description='The name of the host that runs mongodb.')
+    port: int = Field(27017, description='The port to connect with mongodb.')
+    db_name: str = Field('nomad_v1', description='The used mongodb database name.')
+
+
+class Logstash(NomadSettings):
+    enabled = False
+    host = 'localhost'
+    tcp_port = '5000'
+    level: int = logging.DEBUG
+
+
+class Tests(NomadSettings):
+    default_timeout = 60
+
+
+class Mail(NomadSettings):
+    enabled = False
+    with_login = False
+    host = ''
+    port = 8995
+    user = ''
+    password = ''
+    from_address = 'support@nomad-lab.eu'
+    cc_address = 'support@nomad-lab.eu'
+
+
+class Normalize(NomadSettings):
+    system_classification_with_clusters_threshold = Field(
+        64, description='''
+            The system size limit for running the dimensionality analysis. For very
+            large systems the dimensionality analysis will get too expensive.
+        ''')
+    symmetry_tolerance = Field(
+        0.1, description='''
+            Symmetry tolerance controls the precision used by spglib in order to find
+            symmetries. The atoms are allowed to move 1/2*symmetry_tolerance from
+            their symmetry positions in order for spglib to still detect symmetries.
+            The unit is angstroms. The value of 0.1 is used e.g. by Materials Project
+            according to
+            https://pymatgen.org/pymatgen.symmetry.analyzer.html#pymatgen.symmetry.analyzer.SpacegroupAnalyzer
+        ''')
+    prototype_symmetry_tolerance = Field(
+        0.1, description='''
+            The symmetry tolerance used in aflow prototype matching. Should only be
+            changed before re-running the prototype detection.
+        ''')
+    max_2d_single_cell_size = Field(
+        7, description='''
+            Maximum number of atoms in the single cell of a 2D material for it to be
+            considered valid.
+        ''')
+    cluster_threshold = Field(
+        2.5, description='''
+            The distance tolerance between atoms for grouping them into the same
+            cluster. Used in detecting system type.
+        ''')
+
+    angle_rounding = Field(
+        float(10.0), description='''
+            Defines the "bin size" for rounding cell angles for the material hash in degree.
+        ''')
+    flat_dim_threshold = Field(
+        0.1, description='''
+            The threshold for a system to be considered "flat". Used e.g. when
+            determining if a 2D structure is purely 2-dimensional to allow extra rigid
+            transformations that are improper in 3D but proper in 2D.
+        ''')
+
+    k_space_precision = Field(
+        150e6, description='''
+            The threshold for point equality in k-space. Unit: 1/m.
+        ''')
+    band_structure_energy_tolerance = Field(
+        8.01088e-21, description='''
+            The energy threshold for how much a band can be on top or below the fermi
+            level in order to still detect a gap. Unit: Joule.
+        ''')
+    springer_db_path = Field(
+        os.path.join(os.path.dirname(os.path.abspath(__file__)), 'normalizing/data/springer.msg'))
+
+
+class Resources(NomadSettings):
+    enabled = False
+    db_name = 'nomad_v1_resources'
+    max_time_in_mongo = Field(
+        60 * 60 * 24 * 365., description='''
+            Maxmimum time a resource is stored in mongodb before being updated.
+        ''')
+    download_retries = Field(
+        2, description='Number of retries when downloading resources.')
+    download_retry_delay = Field(
+        10, description='Delay between retries in seconds')
+    max_connections = Field(
+        10, description='Maximum simultaneous connections used to download resources.')
+
+
+class Client(NomadSettings):
+    user: str = None
+    password: str = None
+    access_token: str = None
+    url = 'http://nomad-lab.eu/prod/v1/api'
+
+
+class DataCite(NomadSettings):
+    mds_host = 'https://mds.datacite.org'
+    enabled = False
+    prefix = '10.17172'
+    user = '*'
+    password = '*'
+
+
+class GitLab(NomadSettings):
+    private_token = 'not set'
+
+
+class Process(NomadSettings):
+    store_package_definition_in_mongo = Field(
+        False, description='Configures whether to store the corresponding package definition in mongodb.')
+    add_definition_id_to_reference = Field(False, description='''
+        Configures whether to attach definition id to `m_def`, note it is different from `m_def_id`.
+        The `m_def_id` will be exported with the `with_def_id=True` via `m_to_dict`.
+    ''')
+    write_definition_id_to_archive = Field(False, description='Write `m_def_id` to the archive.')
+    index_materials = True
+    reuse_parser = True
+    metadata_file_name = 'nomad'
+    metadata_file_extensions = ('json', 'yaml', 'yml')
+    auxfile_cutoff = 100
+    parser_matching_size = 150 * 80  # 150 lines of 80 ASCII characters per line
+    max_upload_size = 32 * (1024 ** 3)
+    use_empty_parsers = False
+    redirect_stdouts: bool = Field(False, description='''
+        True will redirect lines to stdout (e.g. print output) that occur during
+        processing (e.g. created by parsers or normalizers) as log entries.
+    ''')
+
+
+class Reprocess(NomadSettings):
+    '''
+    Configures standard behaviour when reprocessing.
+    Note, the settings only matter for published uploads and entries. For uploads in
+    staging, we always reparse, add newfound entries, and delete unmatched entries.
+    '''
+    rematch_published = True
+    reprocess_existing_entries = True
+    use_original_parser = False
+    add_matched_entries_to_published = True
+    delete_unmatched_published_entries = False
+    index_individual_entries = False
+
+
+class RFC3161Timestamp(NomadSettings):
+    server = Field(
+        'http://time.certum.pl/', description='The rfc3161ng timestamping host.')
+    cert: str = Field(
+        None, description='Path to the optional rfc3161ng timestamping server certificate.')
+    hash_algorithm = Field(
+        'sha256', description='Hash algorithm used by the rfc3161ng timestamping server.')
+    username: str = None
+    password: str = None
+
+
+class BundleExportSettings(NomadSettings):
+    include_raw_files: bool = Field(
+        True, description='If the raw files should be included in the export')
+    include_archive_files: bool = Field(
+        True, description='If the parsed archive files should be included in the export')
+    include_datasets: bool = Field(
+        True, description='If the datasets should be included in the export')
+
+
+class BundleExport(NomadSettings):
+    ''' Controls behaviour related to exporting bundles. '''
+    default_cli_bundle_export_path: str = Field(
+        './bundles', description='Default path used when exporting bundles using the CLI command.')
+    default_settings: BundleExportSettings = Field(
+        BundleExportSettings(), description='''
+            General default settings.
+        ''')
+    default_settings_cli: BundleExportSettings = Field(
+        None, description='''
+            Additional default settings, applied when exporting using the CLI. This allows
+            to override some of the settings specified in the general default settings above.
+        ''')
+
+
+class BundleImportSettings(NomadSettings):
+    include_raw_files: bool = Field(
+        True, description='If the raw files should be included in the import')
+    include_archive_files: bool = Field(
+        True, description='If the parsed archive files should be included in the import')
+    include_datasets: bool = Field(
+        True, description='If the datasets should be included in the import')
+
+    include_bundle_info: bool = Field(
+        True, description='If the bundle_info.json file should be kept (not necessary but may be nice to have.')
+    keep_original_timestamps: bool = Field(
+        False, description='''
+            If all timestamps (create time, publish time etc) should be imported from
+            the bundle.
+        ''')
+    set_from_oasis: bool = Field(
+        True, description='If the from_oasis flag and oasis_deployment_url should be set.')
+
+    delete_upload_on_fail: bool = Field(
+        False, description='If False, it is just removed from the ES index on failure.')
+    delete_bundle_on_fail: bool = Field(
+        True, description='Deletes the source bundle if the import fails.')
+    delete_bundle_on_success: bool = Field(
+        True, description='Deletes the source bundle if the import succeeds.')
+    delete_bundle_include_parent_folder: bool = Field(
+        True, description='When deleting the bundle, also include parent folder, if empty.')
+
+    trigger_processing: bool = Field(
+        False, description='If the upload should be processed when the import is done (not recommended).')
+    process_settings: Reprocess = Field(
+        Reprocess(
+            rematch_published=True,
+            reprocess_existing_entries=True,
+            use_original_parser=False,
+            add_matched_entries_to_published=True,
+            delete_unmatched_published_entries=False
+        ), description='''
+            When trigger_processing is set to True, these settings control the reprocessing
+            behaviour (see the config for `reprocess` for more info). NOTE: reprocessing is
+            no longer the recommended method to import bundles.
+        '''
+    )
+
+
+class BundleImport(NomadSettings):
+    ''' Controls behaviour related to importing bundles. '''
+    required_nomad_version: str = Field(
+        '1.1.2', description='Minimum  NOMAD version of bundles required for import.')
+
+    default_cli_bundle_import_path: str = Field(
+        './bundles', description='Default path used when importing bundles using the CLI command.')
+
+    allow_bundles_from_oasis: bool = Field(
+        False, description='If oasis admins can "push" bundles to this NOMAD deployment.')
+    allow_unpublished_bundles_from_oasis: bool = Field(
+        False, description='If oasis admins can "push" bundles of unpublished uploads.')
+
+    default_settings: BundleImportSettings = Field(
+        BundleImportSettings(),
+        description='''
+            General default settings.
+        ''')
+
+    default_settings_cli: BundleImportSettings = Field(
+        BundleImportSettings(
+            delete_bundle_on_fail=False,
+            delete_bundle_on_success=False
+        ),
+        description='''
+            Additional default settings, applied when importing using the CLI. This allows
+            to override some of the settings specified in the general default settings above.
+        ''')
+
+
+class Archive(NomadSettings):
+    block_size = 256 * 1024
+    read_buffer_size = Field(
+        256 * 1024, description='GPFS needs at least 256K to achieve decent performance.')
+    max_process_number = Field(
+        20, description='Maximum number of processes can be assigned to process archive query.')
+    min_entries_per_process = Field(
+        20, description='Minimum number of entries per process.')
+
+
+class UISetting(NomadSettings, extra=Extra.forbid):
+    '''Extra fields are not allowed in the UI models'''
+
+
+class OptionsBase(UISetting):
+    '''The most basic model for defining the availability of different UI
+    options.
+    '''
+    include: Optional[List[str]] = Field(description='''
+        List of included options. If not explicitly defined, all of the options will
+        be included by default.
+    ''')
+    exclude: Optional[List[str]] = Field(description='''
+        List of excluded options. Has higher precedence than include.
+    ''')
+
+
+class Options(OptionsBase):
+    '''Common configuration class used for enabling/disabling certain UI
+    elements and defining the configuration of each element.
+    '''
+    options: Dict[str, Any] = Field(description='Contains the available options.')
+
+
+class OptionsSingle(Options):
+    '''Represents options where one value can be selected.'''
+    selected: str = Field(description='Selected option.')
+
+
+class OptionsMulti(Options):
+    '''Represents options where multiple values can be selected.'''
+    selected: List[str] = Field(description='Selected options.')
+
+
+class UnitSystemEnum(str, Enum):
+    CUSTOM = 'Custom'
+    SI = 'SI'
+    AU = 'AU'
+
+
+class UnitSystems(UISetting):
+    '''Controls the used unit system.'''
+    selected: UnitSystemEnum = Field(description='Controls the default unit system.')
+
+
+class Theme(UISetting):
+    '''Theme and identity settings.'''
+    title: str = Field(description='Site name in the browser tab.')
+
+
+class NORTHUI(UISetting):
+    '''NORTH (NOMAD Remote Tools Hub) UI configuration.'''
+    enabled: bool = Field(True, description='''
+        Whether the NORTH tools are available in the UI.
+        The default value is read from the root-level NORTH configuration.
+    ''')
+
+
+class Card(UISetting):
+    '''Definition for a card shown in the entry overview page.'''
+    error: str = Field(description='The error message to show if an error is encountered within the card.')
+
+
+class Cards(Options):
+    '''Contains the overview page card definitions and controls their visibility.'''
+    options: Dict[str, Card] = Field(description='Contains the available card options.')
+
+
+class Entry(UISetting):
+    '''Controls the entry visualization.'''
+    cards: Cards = Field(description='Controls the cards that are displayed on the entry overview page.')
+
+
+class Help(UISetting):
+    '''Help dialog contents.'''
+    title: str = Field(description='Title of the help dialog.')
+    content: str = Field(description='Text contents of the help dialog. Supports markdown.')
+
+
+class Pagination(UISetting):
+    order_by: str = Field('upload_create_time', description='Field used for sorting.')
+    order: str = Field('desc', description='Sorting order.')
+    page_size: int = Field(20, description='Number of results on each page.')
+
+
+class ModeEnum(str, Enum):
+    STANDARD = 'standard'
+    SCIENTIFIC = 'scientific'
+    SEPARATORS = 'separators'
+
+
+class Format(UISetting):
+    '''Value formatting options.'''
+    decimals: int = Field(3, description='Number of decimals to show for numbers.')
+    mode: ModeEnum = Field('standard', description='Display mode for numbers.')
+
+
+class AlignEnum(str, Enum):
+    LEFT = 'left'
+    RIGHT = 'right'
+    CENTER = 'center'
+
+
+class Column(UISetting):
+    '''Option for a column show in the search results.'''
+    label: Optional[str] = Field(description='Label shown in the header. Defaults to the quantity name.')
+    align: AlignEnum = Field(AlignEnum.LEFT, description='Alignment in the table.')
+    unit: Optional[str] = Field(description='''
+        Unit to convert to when displaying. If not given will be displayed in
+        using the default unit in the active unit system.
+    ''')
+    format: Optional[Format] = Field(description='Controls the formatting of the values.')
+
+
+class Columns(OptionsMulti):
+    '''
+    Contains column definitions, controls their availability and specifies the default
+    selection.
+    '''
+    options: Dict[str, Column] = Field(description='''
+        All available column options. Note here that the key must correspond to a
+        quantity path that exists in the metadata.
+    ''')
+
+
+class RowActions(UISetting):
+    '''Controls the visualization of row actions that are shown at the end of each row.'''
+    enabled: bool = Field(True, description='Whether to enable row actions. ')
+
+
+class RowDetails(UISetting):
+    '''
+    Controls the visualization of row details that are shown upon pressing the row and
+    contain basic details about the entry.
+    '''
+    enabled: bool = Field(True, description='Whether to show row details.')
+
+
+class RowSelection(UISetting):
+    '''
+    Controls the selection of rows. If enabled, rows can be selected and additional
+    actions performed on them.
+    '''
+    enabled: bool = Field(True, description='Whether to show the row selection.')
+
+
+class Rows(UISetting):
+    '''Controls the visualization of rows in the search results.'''
+    actions: RowActions
+    details: RowDetails
+    selection: RowSelection
+
+
+class FilterMenuActionEnum(str, Enum):
+    CHECKBOX = 'checkbox'
+
+
+class FilterMenuAction(UISetting):
+    '''Contains definition for an action in the filter menu.'''
+    type: FilterMenuActionEnum = Field(description='Action type.')
+    label: str = Field(description='Label to show.')
+
+
+class FilterMenuActionCheckbox(FilterMenuAction):
+    '''Contains definition for checkbox action in the filter menu.'''
+    quantity: str = Field(description='Targeted quantity')
+
+
+class FilterMenuActions(Options):
+    '''Contains filter menu action definitions and controls their availability.'''
+    options: Dict[str, FilterMenuActionCheckbox] = Field(
+        description='Contains options for filter menu actions.'
+    )
+
+
+class FilterMenu(UISetting):
+    '''Defines the layout and functionality for a filter menu.'''
+    label: Optional[str] = Field(description='Menu label to show in the UI.')
+    level: Optional[int] = Field(0, description='Indentation level of the menu.')
+    size: Optional[str] = Field('small', description='Width of the menu.')
+    actions: Optional[FilterMenuActions]
+
+
+class FilterMenus(Options):
+    '''Contains filter menu definitions and controls their availability.'''
+    options: Dict[str, FilterMenu] = Field(description='Contains the available filter menu options.')
+
+
+class Filters(OptionsBase):
+    '''Controls the availability of filters.'''
+
+
+class Layout(UISetting):
+    '''Defines widget size and grid positioning for different breakpoints.'''
+    minH: int = Field(description='Minimum height in grid units.')
+    minW: int = Field(description='Minimum width in grid units.')
+    h: int = Field(description='Height in grid units')
+    w: int = Field(description='Width in grid units.')
+    x: int = Field(description='Horizontal start location in the grid.')
+    y: int = Field(description='Vertical start location in the grid.')
+
+
+class ScaleEnum(str, Enum):
+    POW1 = 'linear'
+    POW2 = '1/2'
+    POW4 = '1/4'
+    POW8 = '1/8'
+
+
+class BreakpointEnum(str, Enum):
+    SM = 'sm'
+    MD = 'md'
+    LG = 'lg'
+    XL = 'xl'
+    XXL = 'xxl'
+
+
+class Widget(UISetting):
+    '''Common configuration for all widgets.'''
+    type: str = Field(description='Used to identify the widget type.')
+    layout: Dict[BreakpointEnum, Layout] = Field(description='''
+        Defines widget size and grid positioning for different breakpoints. The
+        following breakpoints are supported: `sm`, `md`, `lg`, `xl` and `xxl`.
+    ''')
+
+
+class WidgetTerms(Widget):
+    '''Terms widget configuration.'''
+    type: Literal['terms'] = Field(description='Set as `terms` to get this widget type.')
+    quantity: str = Field(description='Targeted quantity.')
+    scale: ScaleEnum = Field(description='Statistics scaling.')
+    showinput: bool = Field(True, description='Whether to show text input field.')
+
+
+class WidgetHistogram(Widget):
+    '''Histogram widget configuration.'''
+    type: Literal['histogram'] = Field(description='Set as `histogram` to get this widget type.')
+    quantity: str = Field(description='Targeted quantity.')
+    scale: ScaleEnum = Field(description='Statistics scaling.')
+    autorange: bool = Field(
+        True,
+        description='Whether to automatically set the range according to the data limits.'
+    )
+    showinput: bool = Field(
+        True,
+        description='Whether to show input text fields for minimum and maximum value.'
+    )
+    nbins: int = Field(description='''
+        Maximum number of histogram bins. Notice that the actual number of bins
+        may be smaller if there are fewer data items available.
+    ''')
+
+
+class WidgetPeriodicTable(Widget):
+    '''Periodic table widget configuration.'''
+    type: Literal['periodictable'] = Field(description='Set as `periodictable` to get this widget type.')
+    quantity: str = Field(description='Targeted quantity.')
+    scale: ScaleEnum = Field(description='Statistics scaling.')
+
+
+class WidgetScatterPlot(Widget):
+    '''Scatter plot widget configuration.'''
+    type: Literal['scatterplot'] = Field(description='Set as `scatterplot` to get this widget type.')
+    x: str = Field(description='X-axis quantity.')
+    y: str = Field(description='Y-axis quantity.')
+    color: Optional[str] = Field(description='Quantity used for coloring points.')
+    size: int = Field(
+        1000,
+        description='''
+        Maximum number of data points to fetch. Notice that the actual number may be less.
+        '''
+    )
+    autorange: bool = Field(
+        True,
+        description='Whether to automatically set the range according to the data limits.'
+    )
+
+
+# The 'discriminated union' feature of Pydantic is used here:
+# https://docs.pydantic.dev/usage/types/#discriminated-unions-aka-tagged-unions
+WidgetAnnotated = Annotated[
+    Union[WidgetTerms, WidgetHistogram, WidgetScatterPlot, WidgetPeriodicTable],
+    Field(discriminator="type")]
+
+
+class Dashboard(UISetting):
+    '''Dashboard configuration.'''
+    widgets: List[WidgetAnnotated] = Field(description='List of widgets contained in the dashboard.')  # type: ignore
+
+
+class ResourceEnum(str, Enum):
+    ENTRIES = 'entries'
+    MATERIALS = 'materials'
+
+
+class App(UISetting):
+    '''Defines the layout and functionality for an App.'''
+    label: str = Field(description='Name of the App.')
+    path: str = Field(description='Path used in the browser address bar.')
+    resource: ResourceEnum = Field(description='Targeted resource.')
+    breadcrumb: str = Field(description='Path displayed in the breadcrumb.')
+    category: str = Field(description='Category used to organize Apps in the explore menu.')
+    description: str = Field(description='Short description of the App.')
+    help: Help = Field(description='Help dialog contents.')
+    pagination: Pagination = Field(Pagination(), description='Default result pagination.')
+    columns: Columns = Field(description='Controls the columns shown in the results table.')
+    rows: Rows = Field(description='Controls the display of entry rows in the results table.')
+    filter_menus: FilterMenus = Field(description='Filter menus displayed on the left side of the screen.')
+    filters: Optional[Filters] = Field(description='Controls the filters that are available in this app.')
+    dashboard: Optional[Dashboard] = Field(description='Default dashboard layout.')
+    filters_locked: Optional[dict] = Field(
+        description='''
+        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.
+        '''
+    )
+
+
+class Apps(Options):
+    '''Contains App definitions and controls their availability.'''
+    options: Dict[str, App] = Field(description='Contains the available app options.')
+
+
+class ExampleUploads(OptionsBase):
+    '''Controls the availability of example uploads.'''
+
+
+class UI(UISetting):
+    '''Used to customize the user interface.'''
+    theme: Theme = Field(
+        Theme(**{
+            'title': 'NOMAD'
+        }),
+        description='Controls the site theme and identity.'
+    )
+    unit_systems: UnitSystems = Field(
+        UnitSystems(**{'selected': 'Custom'}),
+        description='Controls the available unit systems.'
+    )
+    entry: Entry = Field(
+        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.'},
+                }
+            }
+        }),
+        description='Controls the entry visualization.'
+    )
+    apps: Apps = Field(
+        Apps(**{
+            'options': {
+                'entries': {
+                    'label': 'Entries',
+                    'path': 'entries',
+                    'resource': 'entries',
+                    'breadcrumb': 'Entries',
+                    'category': 'All',
+                    'description': 'Search entries across all domains',
+                    'help': {
+                        'title': 'Entries search',
+                        'content': inspect.cleandoc(r'''
+                            This page allows you to search **entries** within NOMAD.
+                            Entries represent any individual data items that have
+                            been uploaded to NOMAD, no matter whether they come from
+                            theoretical calculations, experiments, lab notebooks or
+                            any other source of data. This allows you to perform
+                            cross-domain queries, but if you are interested in a
+                            specific subfield, you should see if a specific
+                            application exists for it in the explore menu to get
+                            more details.
+                        '''),
+                    },
+                    'columns': {
+                        'selected': [
+                            'entry_name',
+                            'results.material.chemical_formula_hill',
+                            'entry_type',
+                            'upload_create_time',
+                            'authors'
+                        ],
+                        'options': {
+                            'entry_name': {'label': 'Name', 'align': 'left'},
+                            'results.material.chemical_formula_hill': {'label': 'Formula', 'align': 'left'},
+                            'entry_type': {'label': 'Entry type', 'align': 'left'},
+                            'upload_create_time': {'label': 'Upload time', 'align': 'left'},
+                            'authors': {'label': 'Authors', 'align': 'left'},
+                            'results.method.method_name': {'label': 'Method name'},
+                            'results.method.simulation.program_name': {'label': 'Program name'},
+                            'results.method.simulation.dft.basis_set_name': {'label': 'Basis set name'},
+                            'results.method.simulation.dft.xc_functional_type': {'label': 'XC Functional Type'},
+                            'results.material.structural_type': {'label': 'Dimensionality'},
+                            'results.material.symmetry.crystal_system': {'label': 'Crystal system'},
+                            'results.material.symmetry.space_group_symbol': {'label': 'Space group symbol'},
+                            'results.material.symmetry.space_group_number': {'label': 'Space group number'},
+                            'results.eln.lab_ids': {'label': 'Lab IDs'},
+                            'results.eln.sections': {'label': 'Sections'},
+                            'results.eln.methods': {'label': 'Methods'},
+                            'results.eln.tags': {'label': 'Tags'},
+                            'results.eln.instruments': {'label': 'Instruments'},
+                            'mainfile': {'label': 'Mainfile', 'align': 'left'},
+                            'comment': {'label': 'Comment', 'align': 'left'},
+                            'references': {'label': 'References', 'align': 'left'},
+                            'datasets': {'label': 'Datasets', 'align': 'left'},
+                            'published': {'label': 'Access'}
+                        }
+                    },
+                    'rows': {
+                        'actions': {'enabled': True},
+                        'details': {'enabled': True},
+                        'selection': {'enabled': True}
+                    },
+                    'filter_menus': {
+                        'options': {
+                            'material': {'label': 'Material', 'level': 0},
+                            'elements': {'label': 'Elements / Formula', 'level': 1, 'size': 'large'},
+                            'structure': {'label': 'Structure', 'level': 1, 'size': 'small'},
+                            'method': {'label': 'Method', 'level': 0},
+                            'dft': {'label': 'DFT', 'level': 1, 'size': 'small'},
+                            'gw': {'label': 'GW', 'level': 1, 'size': 'small'},
+                            'projection': {'label': 'Projection', 'level': 1, 'size': 'small'},
+                            'dmft': {'label': 'DMFT', 'level': 1, 'size': 'small'},
+                            'eels': {'label': 'EELS', 'level': 1, 'size': 'small'},
+                            'workflow': {'label': 'Workflow', 'level': 0},
+                            'molecular_dynamics': {'label': 'Molecular dynamics', 'level': 1, 'size': 'small'},
+                            'geometry_optimization': {'label': 'Geometry Optimization', 'level': 1, 'size': 'small'},
+                            'properties': {'label': 'Properties', 'level': 0},
+                            'electronic': {'label': 'Electronic', 'level': 1, 'size': 'small'},
+                            'vibrational': {'label': 'Vibrational', 'level': 1, 'size': 'small'},
+                            'mechanical': {'label': 'Mechanical', 'level': 1, 'size': 'small'},
+                            'usecases': {'label': 'Use Cases', 'level': 0, 'size': 'small'},
+                            'solarcell': {'label': 'Solar Cells', 'level': 1, 'size': 'small'},
+                            'author': {'label': 'Author / Origin / Dataset', 'level': 0, 'size': 'medium'},
+                            'metadata': {'label': 'Visibility / IDs / Schema', 'level': 0, 'size': 'small'},
+                            'optimade': {'label': 'Optimade', 'level': 0, 'size': 'medium'},
+                        }
+                    },
+                    'filters': {
+                        'exclude': ['mainfile', 'entry_name', 'combine']
+                    },
+                },
+                'calculations': {
+                    'label': 'Calculations',
+                    'path': 'calculations',
+                    'resource': 'entries',
+                    'breadcrumb': 'Calculations',
+                    'category': 'Theory',
+                    'description': 'Search calculations',
+                    'help': {
+                        'title': 'Calculations',
+                        'content': inspect.cleandoc(r'''
+                            This page allows you to search **calculations** within
+                            NOMAD.  Calculations typically come from a specific
+                            simulation software that uses an approximate model to
+                            investigate and report different physical properties.
+                        '''),
+                    },
+                    'columns': {
+                        'selected': [
+                            'results.material.chemical_formula_hill',
+                            'results.method.simulation.program_name',
+                            'results.method.method_name',
+                            'upload_create_time',
+                            'authors'
+                        ],
+                        'options': {
+                            'results.material.chemical_formula_hill': {'label': 'Formula', 'align': 'left'},
+                            'results.method.simulation.program_name': {'label': 'Program name'},
+                            'results.method.method_name': {'label': 'Method name'},
+                            'upload_create_time': {'label': 'Upload time', 'align': 'left'},
+                            'authors': {'label': 'Authors', 'align': 'left'},
+                            'results.method.simulation.dft.basis_set_name': {'label': 'Basis set name'},
+                            'results.method.simulation.dft.xc_functional_type': {'label': 'XC Functional Type'},
+                            'results.material.structural_type': {'label': 'Dimensionality'},
+                            'results.material.symmetry.crystal_system': {'label': 'Crystal system'},
+                            'results.material.symmetry.space_group_symbol': {'label': 'Space group symbol'},
+                            'results.material.symmetry.space_group_number': {'label': 'Space group number'},
+                            'results.eln.lab_ids': {'label': 'Lab IDs'},
+                            'results.eln.sections': {'label': 'Sections'},
+                            'results.eln.methods': {'label': 'Methods'},
+                            'results.eln.tags': {'label': 'Tags'},
+                            'results.eln.instruments': {'label': 'Instruments'},
+                            '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'}
+                        }
+                    },
+                    'rows': {
+                        'actions': {'enabled': True},
+                        'details': {'enabled': True},
+                        'selection': {'enabled': True}
+                    },
+                    'filter_menus': {
+                        'options': {
+                            'material': {'label': 'Material', 'level': 0},
+                            'elements': {'label': 'Elements / Formula', 'level': 1, 'size': 'large'},
+                            'structure': {'label': 'Structure', 'level': 1, 'size': 'small'},
+                            'method': {'label': 'Method', 'level': 0},
+                            'dft': {'label': 'DFT', 'level': 1, 'size': 'small'},
+                            'gw': {'label': 'GW', 'level': 1, 'size': 'small'},
+                            'projection': {'label': 'Projection', 'level': 1, 'size': 'small'},
+                            'dmft': {'label': 'DMFT', 'level': 1, 'size': 'small'},
+                            'workflow': {'label': 'Workflow', 'level': 0},
+                            'molecular_dynamics': {'label': 'Molecular dynamics', 'level': 1, 'size': 'small'},
+                            'geometry_optimization': {'label': 'Geometry Optimization', 'level': 1, 'size': 'small'},
+                            'properties': {'label': 'Properties', 'level': 0},
+                            'electronic': {'label': 'Electronic', 'level': 1, 'size': 'small'},
+                            'vibrational': {'label': 'Vibrational', 'level': 1, 'size': 'small'},
+                            'mechanical': {'label': 'Mechanical', 'level': 1, 'size': 'small'},
+                            'author': {'label': 'Author / Origin / Dataset', 'level': 0, 'size': 'medium'},
+                            'metadata': {'label': 'Visibility / IDs / Schema', 'level': 0, 'size': 'small'},
+                            'optimade': {'label': 'Optimade', 'level': 0, 'size': 'medium'},
+                        }
+                    },
+                    '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': inspect.cleandoc(r'''
+                            This page allows you to search **materials** within
+                            NOMAD. NOMAD can often automatically detect the material
+                            from individual calculations that contain the full
+                            atomistic structure and can then group the data by using
+                            these detected materials. This allows you to search
+                            individual materials which have properties that are
+                            aggregated from several entries. Following the link for
+                            a specific material will take you to the corresponding
+                            [NOMAD Encyclopedia](https://nomad-lab.eu/prod/rae/encyclopedia/#/search)
+                            page for that material. NOMAD Encyclopedia is a service
+                            that is specifically oriented towards materials property
+                            exploration.
+
+                            Notice that by default the properties that you search
+                            can be combined from several different entries. If
+                            instead you wish to search for a material with an
+                            individual entry fullfilling your search criteria,
+                            uncheck the **combine results from several
+                            entries**-checkbox.
+                        '''),
+                    },
+                    'pagination': {
+                        'order_by': 'chemical_formula_hill',
+                        'order': 'asc'
+                    },
+                    'columns': {
+                        'selected': [
+                            'chemical_formula_hill',
+                            'structural_type',
+                            'symmetry.structure_name',
+                            'symmetry.space_group_number',
+                            'symmetry.crystal_system',
+                        ],
+                        'options': {
+                            'chemical_formula_hill': {'label': 'Formula', 'align': 'left'},
+                            'structural_type': {'label': 'Dimensionality'},
+                            'symmetry.structure_name': {'label': 'Structure name'},
+                            'symmetry.space_group_number': {'label': 'Space group number'},
+                            'symmetry.crystal_system': {'label': 'Crystal system'},
+                            'symmetry.space_group_symbol': {'label': 'Space group symbol'},
+                            'material_id': {'label': 'Material ID'},
+                        }
+                    },
+                    'rows': {
+                        'actions': {'enabled': True},
+                        'details': {'enabled': False},
+                        'selection': {'enabled': False}
+                    },
+                    'filter_menus': {
+                        'options': {
+                            'material': {'label': 'Material', 'level': 0},
+                            'elements': {'label': 'Elements / Formula', 'level': 1, 'size': 'large'},
+                            'structure': {'label': 'Structure', 'level': 1, 'size': 'small'},
+                            'method': {'label': 'Method', 'level': 0},
+                            'dft': {'label': 'DFT', 'level': 1, 'size': 'small'},
+                            'gw': {'label': 'GW', 'level': 1, 'size': 'small'},
+                            'projection': {'label': 'Projection', 'level': 1, 'size': 'small'},
+                            'dmft': {'label': 'DMFT', 'level': 1, 'size': 'small'},
+                            'workflow': {'label': 'Workflow', 'level': 0},
+                            'molecular_dynamics': {'label': 'Molecular dynamics', 'level': 1, 'size': 'small'},
+                            'geometry_optimization': {'label': 'Geometry Optimization', 'level': 1, 'size': 'small'},
+                            'properties': {'label': 'Properties', 'level': 0, 'size': 'small'},
+                            'electronic': {'label': 'Electronic', 'level': 1, 'size': 'small'},
+                            'vibrational': {'label': 'Vibrational', 'level': 1, 'size': 'small'},
+                            'mechanical': {'label': 'Mechanical', 'level': 1, 'size': 'small'},
+                            'author': {'label': 'Author / Origin / Dataset', 'level': 0, 'size': 'medium'},
+                            'metadata': {'label': 'Visibility / IDs / Schema', 'level': 0, 'size': 'small'},
+                            'optimade': {'label': 'Optimade', 'level': 0, 'size': 'medium'},
+                            'combine': {
+                                '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': inspect.cleandoc(r'''
+                            This page allows you to specifically seach **electronic
+                            lab notebooks (ELNs)** within NOMAD.  It is very similar
+                            to the entries search, but with a reduced filter set and
+                            specialized arrangement of default columns.
+                        '''),
+                    },
+                    'columns': {
+                        'selected': [
+                            'entry_name',
+                            'entry_type',
+                            'upload_create_time',
+                            'authors'
+                        ],
+                        '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'},
+                            'results.eln.lab_ids': {'label': 'Lab IDs'},
+                            'results.eln.sections': {'label': 'Sections'},
+                            'results.eln.methods': {'label': 'Methods'},
+                            'results.eln.tags': {'label': 'Tags'},
+                            'results.eln.instruments': {'label': 'Instruments'},
+                            'mainfile': {'label': 'Mainfile', 'align': 'left'},
+                            'comment': {'label': 'Comment', 'align': 'left'},
+                            'references': {'label': 'References', 'align': 'left'},
+                            'datasets': {'label': 'Datasets', 'align': 'left'},
+                            'published': {'label': 'Access'}
+                        }
+                    },
+                    'rows': {
+                        'actions': {'enabled': True},
+                        'details': {'enabled': True},
+                        'selection': {'enabled': True}
+                    },
+                    'filter_menus': {
+                        'options': {
+                            'material': {'label': 'Material', 'level': 0},
+                            'elements': {'label': 'Elements / Formula', 'level': 1, 'size': 'large'},
+                            'eln': {'label': 'Electronic Lab Notebook', 'level': 0, 'size': 'small'},
+                            'custom_quantities': {'label': 'User Defined Quantities', 'level': 0, 'size': 'large'},
+                            'author': {'label': 'Author / Origin / Dataset', 'level': 0, 'size': 'medium'},
+                            'metadata': {'label': 'Visibility / IDs / Schema', 'level': 0, 'size': 'small'},
+                            'optimade': {'label': 'Optimade', 'level': 0, 'size': 'medium'},
+                        }
+                    },
+                    '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': inspect.cleandoc(r'''
+                            This page allows you to spefically search **Electron
+                            Energy Loss Spectroscopy (EELS) experiments** within
+                            NOMAD. It is similar to the entries search, but with a
+                            reduced filter set and specialized arrangement of
+                            default columns.
+                        '''),
+                    },
+                    'columns': {
+                        'selected': [
+                            'results.material.chemical_formula_hill',
+                            'results.properties.spectroscopy.eels.detector_type',
+                            'results.properties.spectroscopy.eels.resolution',
+                            'upload_create_time',
+                            'authors'
+                        ],
+                        'options': {
+                            'results.material.chemical_formula_hill': {'label': 'Formula', 'align': 'left'},
+                            'results.properties.spectroscopy.eels.detector_type': {'label': 'Detector type'},
+                            'results.properties.spectroscopy.eels.resolution': {'label': 'Resolution'},
+                            'upload_create_time': {'label': 'Upload time', 'align': 'left'},
+                            'authors': {'label': 'Authors', 'align': 'left'},
+                            'results.properties.spectroscopy.eels.min_energy': {},
+                            'results.properties.spectroscopy.eels.max_energy': {},
+                            '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'}
+                        }
+                    },
+                    'rows': {
+                        'actions': {'enabled': True},
+                        'details': {'enabled': True},
+                        'selection': {'enabled': True}
+                    },
+                    'filter_menus': {
+                        'options': {
+                            'material': {'label': 'Material', 'level': 0},
+                            'elements': {'label': 'Elements / Formula', 'level': 1, 'size': 'large'},
+                            'method': {'label': 'Method', 'level': 0, 'size': 'small'},
+                            'eels': {'label': 'EELS', 'level': 1, 'size': 'small'},
+                            'author': {'label': 'Author / Origin / Dataset', 'level': 0, 'size': 'medium'},
+                            'metadata': {'label': 'Visibility / IDs / Schema', 'level': 0, 'size': 'small'},
+                            'optimade': {'label': 'Optimade', 'level': 0, 'size': 'medium'},
+                        }
+                    },
+                    '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': inspect.cleandoc(r'''
+                            This page allows you to search **solar cell data**
+                            within NOMAD. The filter menu on the left and the shown
+                            default columns are specifically designed for solar cell
+                            exploration. The dashboard directly shows useful
+                            interactive statistics about the data.
+                        '''),
+                    },
+                    'pagination': {
+                        'order_by': 'results.properties.optoelectronic.solar_cell.efficiency',
+                    },
+                    'dashboard': {
+                        'widgets': [
+                            {
+                                'type': 'periodictable',
+                                'scale': 'linear',
+                                'quantity': 'results.material.elements',
+                                'layout': {
+                                    'xxl': {'minH': 8, 'minW': 12, 'h': 8, 'w': 13, 'y': 0, 'x': 0},
+                                    'xl': {'minH': 8, 'minW': 12, 'h': 8, 'w': 12, 'y': 0, 'x': 0},
+                                    'lg': {'minH': 8, 'minW': 12, 'h': 8, 'w': 12, 'y': 0, 'x': 0},
+                                    'md': {'minH': 8, 'minW': 12, 'h': 8, 'w': 12, 'y': 0, 'x': 0},
+                                    'sm': {'minH': 8, 'minW': 12, 'h': 8, 'w': 12, 'y': 16, 'x': 0}
+                                },
+                            },
+                            {
+                                'type': 'scatterplot',
+                                'autorange': True,
+                                'size': 1000,
+                                'color': 'results.properties.optoelectronic.solar_cell.short_circuit_current_density',
+                                'y': 'results.properties.optoelectronic.solar_cell.efficiency',
+                                'x': 'results.properties.optoelectronic.solar_cell.open_circuit_voltage',
+                                'layout': {
+                                    'xxl': {'minH': 3, 'minW': 3, 'h': 8, 'w': 12, 'y': 0, 'x': 24},
+                                    'xl': {'minH': 3, 'minW': 3, 'h': 8, 'w': 9, 'y': 0, 'x': 12},
+                                    'lg': {'minH': 3, 'minW': 3, 'h': 6, 'w': 12, 'y': 8, 'x': 0},
+                                    'md': {'minH': 3, 'minW': 3, 'h': 6, 'w': 9, 'y': 8, 'x': 0},
+                                    'sm': {'minH': 3, 'minW': 3, 'h': 5, 'w': 6, 'y': 0, 'x': 0}
+                                },
+                            },
+                            {
+                                'type': 'scatterplot',
+                                'autorange': True,
+                                'size': 1000,
+                                'color': 'results.properties.optoelectronic.solar_cell.device_architecture',
+                                'y': 'results.properties.optoelectronic.solar_cell.efficiency',
+                                'x': 'results.properties.optoelectronic.solar_cell.open_circuit_voltage',
+                                'layout': {
+                                    'xxl': {'minH': 3, 'minW': 3, 'h': 8, 'w': 11, 'y': 0, 'x': 13},
+                                    'xl': {'minH': 3, 'minW': 3, 'h': 8, 'w': 9, 'y': 0, 'x': 21},
+                                    'lg': {'minH': 3, 'minW': 3, 'h': 6, 'w': 12, 'y': 14, 'x': 0},
+                                    'md': {'minH': 3, 'minW': 3, 'h': 6, 'w': 9, 'y': 8, 'x': 9},
+                                    'sm': {'minH': 3, 'minW': 3, 'h': 5, 'w': 6, 'y': 0, 'x': 6}
+                                },
+                            },
+                            {
+                                'type': 'terms',
+                                'showinput': True,
+                                'scale': 'linear',
+                                'quantity': 'results.properties.optoelectronic.solar_cell.device_stack',
+                                'layout': {
+                                    'xxl': {'minH': 3, 'minW': 3, 'h': 6, 'w': 6, 'y': 8, 'x': 14},
+                                    'xl': {'minH': 3, 'minW': 3, 'h': 6, 'w': 6, 'y': 8, 'x': 14},
+                                    'lg': {'minH': 3, 'minW': 3, 'h': 6, 'w': 6, 'y': 0, 'x': 12},
+                                    'md': {'minH': 3, 'minW': 3, 'h': 4, 'w': 6, 'y': 4, 'x': 12},
+                                    'sm': {'minH': 3, 'minW': 3, 'h': 6, 'w': 4, 'y': 10, 'x': 0}
+                                },
+                            },
+                            {
+                                'type': 'histogram',
+                                'autorange': True,
+                                'nbins': 30,
+                                'scale': '1/4',
+                                'quantity': 'results.properties.optoelectronic.solar_cell.illumination_intensity',
+                                'layout': {
+                                    'xxl': {'minH': 3, 'minW': 3, 'h': 3, 'w': 8, 'y': 8, 'x': 0},
+                                    'xl': {'minH': 3, 'minW': 3, 'h': 3, 'w': 8, 'y': 11, 'x': 0},
+                                    'lg': {'minH': 3, 'minW': 3, 'h': 4, 'w': 12, 'y': 12, 'x': 12},
+                                    'md': {'minH': 3, 'minW': 3, 'h': 3, 'w': 8, 'y': 17, 'x': 10},
+                                    'sm': {'minH': 3, 'minW': 3, 'h': 3, 'w': 8, 'y': 13, 'x': 4}
+                                },
+                            },
+                            {
+                                'type': 'terms',
+                                'showinput': True,
+                                'scale': 'linear',
+                                'quantity': 'results.properties.optoelectronic.solar_cell.absorber_fabrication',
+                                'layout': {
+                                    'xxl': {'minH': 3, 'minW': 3, 'h': 6, 'w': 6, 'y': 8, 'x': 8},
+                                    'xl': {'minH': 3, 'minW': 3, 'h': 6, 'w': 6, 'y': 8, 'x': 8},
+                                    'lg': {'minH': 3, 'minW': 3, 'h': 6, 'w': 6, 'y': 0, 'x': 18},
+                                    'md': {'minH': 3, 'minW': 3, 'h': 4, 'w': 6, 'y': 0, 'x': 12},
+                                    'sm': {'minH': 3, 'minW': 3, 'h': 5, 'w': 4, 'y': 5, 'x': 0}
+                                },
+                            },
+                            {
+                                'type': 'histogram',
+                                'showinput': False,
+                                'autorange': False,
+                                'nbins': 30,
+                                'scale': '1/4',
+                                'quantity': 'results.properties.electronic.band_structure_electronic.band_gap.value',
+                                'layout': {
+                                    'xxl': {'minH': 3, 'minW': 8, 'h': 3, 'w': 8, 'y': 11, 'x': 0},
+                                    'xl': {'minH': 3, 'minW': 8, 'h': 3, 'w': 8, 'y': 8, 'x': 0},
+                                    'lg': {'minH': 3, 'minW': 8, 'h': 4, 'w': 12, 'y': 16, 'x': 12},
+                                    'md': {'minH': 3, 'minW': 8, 'h': 3, 'w': 8, 'y': 14, 'x': 10},
+                                    'sm': {'minH': 3, 'minW': 8, 'h': 3, 'w': 8, 'y': 10, 'x': 4}
+                                },
+                            },
+                            {
+                                'type': 'terms',
+                                'showinput': True,
+                                'scale': 'linear',
+                                'quantity': 'results.properties.optoelectronic.solar_cell.electron_transport_layer',
+                                'layout': {
+                                    'xxl': {'minH': 3, 'minW': 3, 'h': 6, 'w': 6, 'y': 8, 'x': 20},
+                                    'xl': {'minH': 3, 'minW': 3, 'h': 6, 'w': 5, 'y': 8, 'x': 25},
+                                    'lg': {'minH': 3, 'minW': 3, 'h': 6, 'w': 6, 'y': 6, 'x': 18},
+                                    'md': {'minH': 3, 'minW': 3, 'h': 6, 'w': 5, 'y': 14, 'x': 0},
+                                    'sm': {'minH': 3, 'minW': 3, 'h': 5, 'w': 4, 'y': 5, 'x': 4}
+                                },
+                            },
+                            {
+                                'type': 'terms',
+                                'showinput': True,
+                                'scale': 'linear',
+                                'quantity': 'results.properties.optoelectronic.solar_cell.hole_transport_layer',
+                                'layout': {
+                                    'xxl': {'minH': 3, 'minW': 3, 'h': 6, 'w': 6, 'y': 8, 'x': 26},
+                                    'xl': {'minH': 3, 'minW': 3, 'h': 6, 'w': 5, 'y': 8, 'x': 20},
+                                    'lg': {'minH': 3, 'minW': 3, 'h': 6, 'w': 6, 'y': 6, 'x': 12},
+                                    'md': {'minH': 3, 'minW': 3, 'h': 6, 'w': 5, 'y': 14, 'x': 5},
+                                    'sm': {'minH': 3, 'minW': 3, 'h': 5, 'w': 4, 'y': 5, 'x': 8}
+                                },
+                            }
+                        ]
+                    },
+                    'columns': {
+                        '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'
+                        ],
+                        'options': {
+                            'results.material.chemical_formula_descriptive': {'label': 'Descriptive Formula', 'align': 'left'},
+                            'results.properties.optoelectronic.solar_cell.efficiency': {
+                                'label': 'Efficiency (%)',
+                                'format': {
+                                    'decimals': 2,
+                                    'mode': 'standard',
+                                },
+                            },
+                            'results.properties.optoelectronic.solar_cell.open_circuit_voltage': {
+                                'label': 'Open circuit voltage',
+                                'unit': 'V',
+                                'format': {
+                                    'decimals': 3,
+                                    'mode': 'standard',
+                                },
+                            },
+                            'results.properties.optoelectronic.solar_cell.short_circuit_current_density': {
+                                'label': 'Short circuit current density',
+                                'unit': 'A/m**2',
+                                'format': {
+                                    'decimals': 3,
+                                    'mode': 'standard',
+                                },
+                            },
+                            'results.properties.optoelectronic.solar_cell.fill_factor': {
+                                'label': 'Fill factor',
+                                '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'},
+                            'results.properties.optoelectronic.solar_cell.illumination_intensity': {
+                                'label': 'Illum. intensity',
+                                'unit': 'W/m**2',
+                                'format': {'decimals': 3, 'mode': 'standard'},
+                            },
+                            'results.eln.lab_ids': {'label': 'Lab IDs'},
+                            'results.eln.sections': {'label': 'Sections'},
+                            'results.eln.methods': {'label': 'Methods'},
+                            'results.eln.tags': {'label': 'Tags'},
+                            'results.eln.instruments': {'label': 'Instruments'},
+                            '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'},
+                        }
+                    },
+                    'rows': {
+                        'actions': {'enabled': True},
+                        'details': {'enabled': True},
+                        'selection': {'enabled': True}
+                    },
+                    'filter_menus': {
+                        'options': {
+                            'material': {'label': 'Absorber Material', 'level': 0},
+                            'elements': {'label': 'Elements / Formula', 'level': 1, 'size': 'large'},
+                            'structure': {'label': 'Structure', 'level': 1, 'size': 'small'},
+                            'electronic': {'label': 'Electronic Properties', 'level': 0, 'size': 'small'},
+                            'solarcell': {'label': 'Solar Cell Properties', 'level': 0, 'size': 'small'},
+                            'eln': {'label': 'Electronic Lab Notebook', 'level': 0, 'size': 'small'},
+                            'custom_quantities': {'label': 'User Defined Quantities', 'level': 0, 'size': 'large'},
+                            'author': {'label': 'Author / Origin / Dataset', 'level': 0, 'size': 'medium'},
+                            'metadata': {'label': 'Visibility / IDs / Schema', 'level': 0, 'size': 'small'},
+                            'optimade': {'label': 'Optimade', 'level': 0, 'size': 'medium'},
+                        }
+                    },
+                    'filters': {
+                        'exclude': ['mainfile', 'entry_name', 'combine']
+                    },
+                    'filters_locked': {
+                        'sections': 'nomad.datamodel.results.SolarCell'
+                    }
+                },
+            }
+        }),
+        description='Contains the App definitions.'
+    )
+    north: NORTHUI = Field(
+        NORTHUI(),
+        description='NORTH (NOMAD Remote Tools Hub) UI configuration.'
+    )
+    example_uploads: ExampleUploads = Field(
+        ExampleUploads(),
+        description='Controls the available example uploads.'
+    )
diff --git a/nomad/datamodel/metainfo/eln/ikz_hall/__init__.py b/nomad/datamodel/metainfo/eln/ikz_hall/__init__.py
index 1bc47fc36b426c595982842b64cce1968c0c603e..e59be6b870cfcd27336519497b8af93c67a41737 100644
--- a/nomad/datamodel/metainfo/eln/ikz_hall/__init__.py
+++ b/nomad/datamodel/metainfo/eln/ikz_hall/__init__.py
@@ -17,7 +17,7 @@
 #
 from nomad.metainfo import Package, Quantity, SubSection
 from nomad.datamodel.data import EntryData
-import nexusutils.dataconverter.readers.hall.reader as hall_reader
+import nexusutils.dataconverter.readers.hall.reader as hall_reader  # pylint: disable=import-error
 from .measurement import Measurement
 from .hall_instrument import Instrument
 from .nexus_to_msection import get_measurements, get_instrument
diff --git a/nomad/datamodel/metainfo/eln/ikz_hall/nexus_to_msection.py b/nomad/datamodel/metainfo/eln/ikz_hall/nexus_to_msection.py
index 08ee11bea2b5452ddc462680a0d7ef3828e69c49..91786ad3d1710bffcb270fe837362d463a3c858f 100644
--- a/nomad/datamodel/metainfo/eln/ikz_hall/nexus_to_msection.py
+++ b/nomad/datamodel/metainfo/eln/ikz_hall/nexus_to_msection.py
@@ -43,7 +43,7 @@ from .hall_instrument import (
     TemperatureDomain
 )
 
-import nexusutils.dataconverter.readers.hall.helpers as hall_helpers
+import nexusutils.dataconverter.readers.hall.helpers as hall_helpers  # pylint: disable=import-error
 from nomad.units import ureg
 
 
diff --git a/nomad/datamodel/metainfo/eln/nexus_data_converter.py b/nomad/datamodel/metainfo/eln/nexus_data_converter.py
index 50762cea3c9c50ec9b709231a3bcd78da845b85a..e6cb4c68a81760dad9ee18bfce68baec71fab289 100644
--- a/nomad/datamodel/metainfo/eln/nexus_data_converter.py
+++ b/nomad/datamodel/metainfo/eln/nexus_data_converter.py
@@ -7,8 +7,8 @@ from nomad.datamodel.data import EntryData
 from nomad.metainfo import Package, Quantity, MEnum
 from nomad.units import ureg
 
-from nexusutils.dataconverter.convert import get_names_of_all_readers, convert
-from nexusutils.nexus.nexus import get_app_defs_names
+from nexusutils.dataconverter.convert import get_names_of_all_readers, convert  # pylint: disable=import-error
+from nexusutils.nexus.nexus import get_app_defs_names  # pylint: disable=import-error
 
 
 m_package = Package(name='nexus_data_converter')
diff --git a/nomad/datamodel/metainfo/eln/perovskite_solar_cell_database/eqe_parser.py b/nomad/datamodel/metainfo/eln/perovskite_solar_cell_database/eqe_parser.py
index 02f47c87c1bdc0e2f4c8d5aa5d2186c651262cdf..67ca61820624648fb99d7eda307df49ea907fb8e 100644
--- a/nomad/datamodel/metainfo/eln/perovskite_solar_cell_database/eqe_parser.py
+++ b/nomad/datamodel/metainfo/eln/perovskite_solar_cell_database/eqe_parser.py
@@ -186,7 +186,7 @@ class EQEAnalyzer():
         start, stop = self.select_range(y, min_eqe_fit, max_eqe_fit)
         self.start = start
         self.stop = stop
-        popt, pcov = optimize.curve_fit(
+        popt, pcov = optimize.curve_fit(  # pylint: disable=unbalanced-tuple-unpacking
             self.linear,
             x[start:stop],
             np.log(y[start:stop]),
diff --git a/nomad/files.py b/nomad/files.py
index 89362e50a82941cb605f55310c4167760649479c..0059926c4e9b6cff2297562aaa4901df8d9c5f7d 100644
--- a/nomad/files.py
+++ b/nomad/files.py
@@ -61,6 +61,7 @@ import yaml
 import magic
 
 from nomad import config, utils, datamodel
+from nomad.config.models import BundleImportSettings, BundleExportSettings
 from nomad.archive import write_archive, read_archive, ArchiveReader
 
 # TODO this should become obsolete, once we are going beyong python 3.6. For now
@@ -680,7 +681,7 @@ class UploadFiles(DirectoryObject, metaclass=ABCMeta):
                     utils.get_logger(__name__).error(
                         'could not remove empty prefix dir', directory=parent_directory, exc_info=e)
 
-    def files_to_bundle(self, export_settings: config.BundleExportSettings) -> Iterable[FileSource]:
+    def files_to_bundle(self, export_settings: BundleExportSettings) -> Iterable[FileSource]:
         '''
         A generator of :class:`FileSource` objects, defining the files/folders to be included in an
         upload bundle when *exporting*. The arguments allows for further filtering of what to include.
@@ -692,7 +693,7 @@ class UploadFiles(DirectoryObject, metaclass=ABCMeta):
 
     @classmethod
     def files_from_bundle(
-            cls, bundle_file_source: BrowsableFileSource, import_settings: config.BundleImportSettings) -> Iterable[FileSource]:
+            cls, bundle_file_source: BrowsableFileSource, import_settings: BundleImportSettings) -> Iterable[FileSource]:
         '''
         Returns an Iterable of :class:`FileSource`, defining the files/folders to be included in an
         upload bundle when *importing*. Only the files specified by the import_settings are included.
@@ -1175,7 +1176,7 @@ class StagingUploadFiles(UploadFiles):
             hash.update(mainfile_key.encode('utf8'))
         return utils.make_websave(hash)
 
-    def files_to_bundle(self, export_settings: config.BundleExportSettings) -> Iterable[FileSource]:
+    def files_to_bundle(self, export_settings: BundleExportSettings) -> Iterable[FileSource]:
         # Defines files for upload bundles of staging uploads.
         if export_settings.include_raw_files:
             yield DiskFileSource(self.os_path, 'raw')
@@ -1184,7 +1185,7 @@ class StagingUploadFiles(UploadFiles):
 
     @classmethod
     def files_from_bundle(
-            cls, bundle_file_source: BrowsableFileSource, import_settings: config.BundleImportSettings) -> Iterable[FileSource]:
+            cls, bundle_file_source: BrowsableFileSource, import_settings: BundleImportSettings) -> Iterable[FileSource]:
         # Files to import for a staging upload
         if import_settings.include_raw_files:
             yield bundle_file_source.sub_source('raw')
@@ -1520,7 +1521,7 @@ class PublicUploadFiles(UploadFiles):
         self._raw_zip_file = self._raw_zip_file_object = None
         self._archive_msg_file = self._archive_msg_file_object = None
 
-    def files_to_bundle(self, export_settings: config.BundleExportSettings) -> Iterable[FileSource]:
+    def files_to_bundle(self, export_settings: BundleExportSettings) -> Iterable[FileSource]:
         # Defines files for upload bundles of published uploads.
         for filename in sorted(os.listdir(self.os_path)):
             if filename.startswith('raw-') and export_settings.include_raw_files:
@@ -1530,7 +1531,7 @@ class PublicUploadFiles(UploadFiles):
 
     @classmethod
     def files_from_bundle(
-            cls, bundle_file_source: BrowsableFileSource, import_settings: config.BundleImportSettings) -> Iterable[FileSource]:
+            cls, bundle_file_source: BrowsableFileSource, import_settings: BundleImportSettings) -> Iterable[FileSource]:
         for filename in bundle_file_source.directory_list(''):
             if filename.startswith('raw-') and import_settings.include_raw_files:
                 yield bundle_file_source.sub_source(filename)
diff --git a/nomad/metainfo/nexus.py b/nomad/metainfo/nexus.py
index a0e0a6aaaf90d15fa1b5b13f2d8ebf30ffe3902d..712b2423f37b6b5f1ebdb208c0b7d3eb524cbf9c 100644
--- a/nomad/metainfo/nexus.py
+++ b/nomad/metainfo/nexus.py
@@ -27,7 +27,7 @@ from typing import Dict, List, Optional, Union
 import numpy as np
 from toposort import toposort_flatten
 
-from nexusutils.nexus import nexus
+from nexusutils.nexus import nexus  # pylint: disable=import-error
 from nomad.datamodel import EntryArchive
 from nomad.metainfo import (
     Attribute, Bytes, Datetime, Definition, MEnum, Package, Property, Quantity, Section, SubSection)
diff --git a/nomad/mkdocs.py b/nomad/mkdocs.py
index 3fb4af956fd0e2c9bc120279cf092d59022b3a20..0864e5ac59d82f2e460692f53505cbe4979b0b45 100644
--- a/nomad/mkdocs.py
+++ b/nomad/mkdocs.py
@@ -22,14 +22,19 @@ Definitions that are used in the documentation via mkdocs-macro-plugin.
 
 import yaml
 import json
+from enum import Enum
+from pydantic import BaseModel
+from pydantic.fields import ModelField
 import os.path
+from typing import List, Set, Tuple, Any, Optional, Dict
+from typing_extensions import Literal, _AnnotatedAlias  # type: ignore
 from inspect import isclass
 
 from nomad.app.v1.models import (
     query_documentation,
     owner_documentation)
 from nomad.app.v1.routers.entries import archive_required_documentation
-from nomad import utils, config
+from nomad import utils
 
 
 exported_config_models = set()  # type: ignore
@@ -42,6 +47,150 @@ doc_snippets = {
 }
 
 
+def get_field_type_info(field: ModelField) -> Tuple[str, Set[Any]]:
+    '''Used to recursively walk through a type definition, building up a cleaned
+    up type name and returning all of the classes that were used.
+
+    Args:
+        type_: The type to inspect. Can be any valid type definition.
+
+    Returns:
+        Tuple containing the cleaned up type name and a set of classes
+        found inside.
+    '''
+    # Notice that pydantic does not store the full type in field.type_, but instead in
+    # field.outer_type_
+    type_ = field.outer_type_
+    type_name: List[str] = []
+    models = set()
+
+    def fetch_models(type_, type_name):
+        '''Used to recursively walk through a type definition, building up a
+        pretty type name and adding any found models to the docs.
+        '''
+        # Get the type name
+        early_stop = False
+        skip_parent = False
+        cls = type_
+        name = None
+
+        # All string subclasses displayed as str
+        if isclass(type_) and issubclass(type_, str):
+            name = 'str'
+        elif isclass(type_) and issubclass(type_, int):
+            name = 'int'
+        # Special handling for type definitions
+        elif hasattr(type_, '__origin__'):
+            origin = type_.__origin__
+            # For literals we report the actual data type that is stored inside.
+            if origin == Literal:
+                arg = type_.__args__[0]
+                cls = type(arg)
+                early_stop = True
+            # Skip the annotated container. In newer python versions the
+            # identification of 'Annotated' could be done with
+            # `get_origin(a) is Annotated``, but this is the cleanest
+            # solution with Python 3.7.
+            elif type(cls) == _AnnotatedAlias:
+                skip_parent = True
+            else:
+                name = str(cls).split("[", 1)[0].rsplit('.')[-1]
+
+        if not skip_parent:
+            if not name:
+                try:
+                    name = cls.__name__
+                except Exception:
+                    name = str(cls)
+            type_name.append(name)
+            if hasattr(type_, '__origin__'):
+                models.add(type_.__origin__)
+            else:
+                models.add(type_)
+
+        if not early_stop:
+            if hasattr(type_, '__args__'):
+                if not skip_parent:
+                    type_name.append('[')
+                origin = type_.__origin__
+                for iarg, arg in enumerate(type_.__args__):
+                    fetch_models(arg, type_name)
+                    if iarg + 1 != len(type_.__args__):
+                        type_name.append(', ')
+                if not skip_parent:
+                    type_name.append(']')
+
+    fetch_models(type_, type_name)
+
+    return ''.join(type_name), models
+
+
+def get_field_description(field: ModelField) -> Optional[str]:
+    '''Retrieves the description for a pydantic field as a markdown string.
+
+    Args:
+        field: The pydantic field to inspect.
+
+    Returns:
+        Markdown string for the description.
+    '''
+    value = field.field_info.description
+    if value:
+        value = utils.strip(value)
+        value = value.replace('\n\n', '<br/>').replace('\n', ' ')
+
+    return value
+
+
+def get_field_default(field: ModelField) -> Optional[str]:
+    '''Retrieves the default value from a pydantic field as a markdown string.
+
+    Args:
+        field: The pydantic field to inspect.
+
+    Returns:
+        Markdown string for the default value.
+    '''
+    default_value = field.default
+    if default_value is not None:
+        if isinstance(default_value, (dict, BaseModel)):
+            default_value = 'Complex object, default value not displayed.'
+        elif default_value == '':
+            default_value = '""'
+        else:
+            default_value = f'`{default_value}`'
+    return default_value
+
+
+def get_field_options(field: ModelField) -> Dict[str, Optional[str]]:
+    '''Retrieves a dictionary of value-description pairs from a pydantic field.
+
+    Args:
+        field: The pydantic field to inspect.
+
+    Returns:
+        Dictionary containing the possible options and their description for
+        this field. The description may be None indicating that it does not exist.
+    '''
+    options: Dict[str, Optional[str]] = {}
+    if isclass(field.type_) and issubclass(field.type_, Enum):
+        for x in field.type_:
+            options[str(x.value)] = None
+    return options
+
+
+def get_field_deprecated(field: ModelField) -> bool:
+    '''Returns whether the given pydantic field is deprecated or not.
+
+    Args:
+        field: The pydantic field to inspect.
+
+    Returns:
+        Whether the field is deprecated.
+    '''
+    return field.field_info.extra.get('deprecated', False)
+
+
 class MyYamlDumper(yaml.Dumper):
     '''
     A custom dumper that always shows objects in yaml and not json syntax
@@ -149,46 +298,32 @@ def define_env(env):
 
         exported_config_models.add(name)
 
-        def description(field):
-            value = field.field_info.description
-
-            if not value:
-                return ''
-
-            value = utils.strip(value)
-            value = value.replace('\n\n', '<br/>').replace('\n', ' ')
-            return value
-
         def content(field):
-            result = ''
-            if field.field_info.description:
-                result += f'{description(field)}<br/> '
-
-            default_value = field.default
-            if default_value is not None:
-                if isinstance(default_value, dict):
-                    default_value = '<complex dict>'
-                else:
-                    default_value = f'`{default_value}`'
-
-                result += f'*default:* {default_value}'
-
-            if field.field_info.extra.get('deprecated', False):
-                result += '<br/>**deprecated**'
-
-            return result
+            result = []
+            description = get_field_description(field)
+            if description:
+                result.append(description)
+            default = get_field_default(field)
+            if default:
+                result.append(f'*default:* {default}')
+            options = get_field_options(field)
+            if options:
+                option_list = '*options:*<br/>'
+                for name, desc in options.items():
+                    option_list += f' - `{name}{f": {desc}" if desc else ""}`<br/>'
+                result.append(option_list)
+            if get_field_deprecated(field):
+                result.append('**deprecated**')
+
+            return '</br>'.join(result)
 
         def field_row(field):
             if field.name.startswith('m_'):
                 return ''
-            type_ = field.type_
-            if isclass(type_) and issubclass(type_, config.NomadSettings):
-                required_models.add(type_)
-            try:
-                type_name = type_.__name__
-            except Exception:
-                type_name = str(type_)
-            return f'|{field.name}|{type_name}|{content(field)}|\n'
+            type_name, classes = get_field_type_info(field)
+            nonlocal required_models
+            required_models |= {cls for cls in classes if isclass(cls) and issubclass(cls, BaseModel)}
+            return f'|{field.name}|`{type_name}`|{content(field)}|\n'
 
         if heading is None:
             result = f'### {name}\n'
diff --git a/nomad/normalizing/common.py b/nomad/normalizing/common.py
index 8574477a2f4ddc624d3cf0a718256555523a1d9d..3d06f3ed72d0a7c2f8d1563ed03e0659efa1ddfe 100644
--- a/nomad/normalizing/common.py
+++ b/nomad/normalizing/common.py
@@ -19,9 +19,9 @@ import numpy as np
 from ase import Atoms
 from typing import List, Set, Any, Optional
 from nptyping import NDArray
-from matid import SymmetryAnalyzer
-from matid.symmetry.wyckoffset import WyckoffSet as WyckoffSetMatID
-import matid.geometry
+from matid import SymmetryAnalyzer  # pylint: disable=import-error
+from matid.symmetry.wyckoffset import WyckoffSet as WyckoffSetMatID  # pylint: disable=import-error
+import matid.geometry  # pylint: disable=import-error
 
 from nomad import atomutils
 from nomad import config
diff --git a/nomad/normalizing/dos.py b/nomad/normalizing/dos.py
index 21156f29db7f6782b69f89371c4c7697b8be5554..debefd62508785ae2140050d876bdc75ebea21b1 100644
--- a/nomad/normalizing/dos.py
+++ b/nomad/normalizing/dos.py
@@ -20,7 +20,7 @@ import numpy as np
 from nptyping import NDArray
 
 from nomad import config
-from nomad_dos_fingerprints import DOSFingerprint
+from nomad_dos_fingerprints import DOSFingerprint  # pylint: disable=import-error
 from nomad.datamodel.metainfo.simulation.calculation import (
     Dos, DosFingerprint, BandGap)
 
diff --git a/nomad/normalizing/material.py b/nomad/normalizing/material.py
index 02fce442cd84edd0f6c648343ff8ed6f84f9a061..afb64a574106af605ce3948f4fed3e33d0aa6ad3 100644
--- a/nomad/normalizing/material.py
+++ b/nomad/normalizing/material.py
@@ -25,11 +25,11 @@ import ase.data
 from ase import Atoms
 from ase.geometry.cell import complete_cell
 import numpy as np
-import matid.geometry
-from matid.classification.structureclusterer import StructureClusterer
-from matid import Classifier
-from matid.classifications import Class0D, Atom, Class1D, Material2D, Surface, Class3D, Class2D, Unknown
-from matid.symmetry.symmetryanalyzer import SymmetryAnalyzer
+import matid.geometry  # pylint: disable=import-error
+from matid.classification.structureclusterer import StructureClusterer  # pylint: disable=import-error
+from matid import Classifier  # pylint: disable=import-error
+from matid.classifications import Class0D, Atom, Class1D, Material2D, Surface, Class3D, Class2D, Unknown  # pylint: disable=import-error
+from matid.symmetry.symmetryanalyzer import SymmetryAnalyzer  # pylint: disable=import-error
 
 from nomad.datamodel.results import Symmetry, Material, System, Relation, Structure, Prototype, structure_name_map
 from nomad import atomutils
diff --git a/nomad/normalizing/metainfo.py b/nomad/normalizing/metainfo.py
index 5a5679e24cee6c8c7395cc2e81619c5a0bf1eab0..4b3cffd94f190852fd95be81ec20de26a52fc0de 100644
--- a/nomad/normalizing/metainfo.py
+++ b/nomad/normalizing/metainfo.py
@@ -27,7 +27,7 @@ class MetainfoNormalizer(Normalizer):
         normalize = None
         try:
             normalize = getattr(section, 'normalize')
-        except Exception as e:
+        except Exception:
             pass
 
         if normalize:
diff --git a/nomad/normalizing/method.py b/nomad/normalizing/method.py
index 7554323445ce2987572c848a6dee5b30a00346c9..85db676dbfdc4adbfaf1c23d912c2a7c4f7d49d6 100644
--- a/nomad/normalizing/method.py
+++ b/nomad/normalizing/method.py
@@ -592,7 +592,7 @@ class MethodNormalizer():
         '''Assign the exact exachange mixing factor to `results` section when explicitly stated.
         Else, fall back on XC functional default.'''
         def scan_patterns(patterns, xc_name) -> bool:
-            return any([x for x in patterns if re.search('_' + x + '$', xc_name)])
+            return any(x for x in patterns if re.search('_' + x + '$', xc_name))
 
         if self.repr_method.dft:
             xc_functional = self.repr_method.dft.xc_functional
diff --git a/nomad/normalizing/results.py b/nomad/normalizing/results.py
index f24f8d9e60d0dc1d2edb36fd233ef6fd058663b7..22d1a93073c326203b468180c1997e46c7c0c8e2 100644
--- a/nomad/normalizing/results.py
+++ b/nomad/normalizing/results.py
@@ -21,8 +21,8 @@ from nomad.datamodel.metainfo.workflow import Workflow
 import numpy as np
 from typing import List, Union, Any, Set, Optional
 import ase.data
-from matid import SymmetryAnalyzer
-import matid.geometry
+from matid import SymmetryAnalyzer  # pylint: disable=import-error
+import matid.geometry  # pylint: disable=import-error
 
 from nomad import config
 from nomad import atomutils
diff --git a/nomad/normalizing/system.py b/nomad/normalizing/system.py
index fdd46cdcb4e70298dbca8b4e32cabb9f11036e2f..64d1c9dad474b3dfd54373d1001c0f05e630f4e8 100644
--- a/nomad/normalizing/system.py
+++ b/nomad/normalizing/system.py
@@ -23,8 +23,8 @@ from ase import Atoms
 import numpy as np
 import json
 import re
-from matid import SymmetryAnalyzer, Classifier
-from matid.classifications import Class0D, Atom, Class1D, Material2D, Surface, Class3D
+from matid import SymmetryAnalyzer, Classifier  # pylint: disable=import-error
+from matid.classifications import Class0D, Atom, Class1D, Material2D, Surface, Class3D  # pylint: disable=import-error
 
 from nomad import atomutils, archive
 from nomad.atomutils import Formula
diff --git a/nomad/parsing/file_parser/text_parser.py b/nomad/parsing/file_parser/text_parser.py
index 9bfe885d73862bebf497889fac22debc4c4b0ffc..4527799ce798970f1b3a29484cd72120b8bcb5c0 100644
--- a/nomad/parsing/file_parser/text_parser.py
+++ b/nomad/parsing/file_parser/text_parser.py
@@ -460,18 +460,13 @@ class TextParser(FileParser):
             if res is None:
                 continue
             if quantity._sub_parser is not None:
-                span = np.array(res.span()) + self.file_offset
                 sub_parser = quantity._sub_parser.copy()
                 sub_parser.mainfile = self.mainfile
                 sub_parser.logger = self.logger
-                if (span[1] - span[0]) < mmap.PAGESIZE or True:
-                    # self.logger.warn(
-                    #     'Cannot use sub parser on quantity %s with blocks with size <'
-                    #     '%d. Will try to parse string' % (quantity.name, mmap.PAGESIZE))
-                    sub_parser._file_handler = b' '.join([g for g in res.groups() if g])
-                else:
-                    sub_parser.file_offset = span[0]
-                    sub_parser.file_length = span[1] - sub_parser.file_offset
+                # self.logger.warn(
+                #     'Cannot use sub parser on quantity %s with blocks with size <'
+                #     '%d. Will try to parse string' % (quantity.name, mmap.PAGESIZE))
+                sub_parser._file_handler = b' '.join([g for g in res.groups() if g])
                 value.append(sub_parser.parse())
 
             else:
diff --git a/nomad/parsing/nexus.py b/nomad/parsing/nexus.py
index 2f0bc1cad02aca256ce3adc987244045b42055f3..3a2aa576fd3808e4465469254d1ba6c58a175f67 100644
--- a/nomad/parsing/nexus.py
+++ b/nomad/parsing/nexus.py
@@ -21,7 +21,7 @@ from typing import Optional
 
 import numpy as np
 
-from nexusutils.nexus import nexus as read_nexus
+from nexusutils.nexus import nexus as read_nexus  # pylint: disable=import-error
 from nomad.datamodel import EntryArchive
 from nomad.metainfo import MSection, nexus
 from nomad.metainfo.util import MQuantity, resolve_variadic_name
diff --git a/nomad/patch.py b/nomad/patch.py
index 40fc3b2b4d090987fa1d471879fac3f2137cfb5f..975076767de4a9621bcf990bac550a31b2149153 100644
--- a/nomad/patch.py
+++ b/nomad/patch.py
@@ -16,8 +16,8 @@
 # limitations under the License.
 #
 
-import matid.symmetry.symmetryanalyzer
-import matid.utils.segfault_protect
+import matid.symmetry.symmetryanalyzer  # pylint: disable=import-error
+import matid.utils.segfault_protect  # pylint: disable=import-error
 
 
 # A patch for the segfault protection of systax (internally uses protection for spglib calls.)
diff --git a/nomad/processing/base.py b/nomad/processing/base.py
index 9a788dcd54e45d1f6e92b9dfccffb9d050a627ad..02eff25666e4c2b79ff62b71c40f6353a74a25e4 100644
--- a/nomad/processing/base.py
+++ b/nomad/processing/base.py
@@ -35,6 +35,7 @@ from datetime import datetime
 import functools
 
 from nomad import config, utils, infrastructure
+from nomad.config.models import CELERY_WORKER_ROUTING
 import nomad.patch  # pylint: disable=unused-import
 
 
@@ -82,7 +83,7 @@ def on_worker_process_shutdown(*args, **kwargs):
 app = Celery('nomad.processing', broker=config.rabbitmq_url())
 app.conf.update(worker_hijack_root_logger=False)
 app.conf.update(worker_max_memory_per_child=config.celery.max_memory)
-if config.celery.routing == config.CELERY_WORKER_ROUTING:
+if config.celery.routing == CELERY_WORKER_ROUTING:
     app.conf.update(worker_direct=True)
 
 app.conf.task_queue_max_priority = 10
@@ -211,6 +212,7 @@ class Proc(Document):
             NOTE: This value is managed by the framework, do not tamper with this value.
     '''
 
+    id_field: str = None
     meta: Any = {
         'abstract': True,
     }
@@ -332,7 +334,7 @@ class Proc(Document):
     def get_by_id(cls, id: str, id_field: str):
         try:
             obj = cls.objects(**{id_field: id}).first()
-        except ValidationError as e:
+        except ValidationError:
             raise InvalidId('%s is not a valid id' % id)
         except ConnectionFailure as e:
             raise e
@@ -465,7 +467,7 @@ class Proc(Document):
         cls_name = self.__class__.__name__
 
         queue = None
-        if config.celery.routing == config.CELERY_WORKER_ROUTING and self.worker_hostname is not None:
+        if config.celery.routing == CELERY_WORKER_ROUTING and self.worker_hostname is not None:
             queue = worker_direct(self.worker_hostname).name
 
         priority = config.celery.priorities.get('%s.%s' % (cls_name, func_name), 1)
diff --git a/nomad/processing/data.py b/nomad/processing/data.py
index 55eb1830f9adebd33a6189e58ab3f83eec5d6c2a..194a76833475d73f2ca5c6329b37497e0223c812 100644
--- a/nomad/processing/data.py
+++ b/nomad/processing/data.py
@@ -46,6 +46,7 @@ from pydantic.error_wrappers import ErrorWrapper
 import validators
 
 from nomad import utils, config, infrastructure, search, datamodel, metainfo, parsing, client
+from nomad.config.models import CELERY_WORKER_ROUTING
 from nomad.datamodel.datamodel import RFC3161Timestamp
 from nomad.files import (
     RawPathInfo, PathObject, UploadFiles, PublicUploadFiles, StagingUploadFiles,
@@ -1251,7 +1252,7 @@ class Entry(Proc):
         try:
             return self.upload_files.write_archive(
                 self.entry_id, archive.m_to_dict(with_def_id=config.process.write_definition_id_to_archive))
-        except Exception as e:
+        except Exception:
             # most likely failed due to domain data, try to write metadata and processing logs
             archive = datamodel.EntryArchive(m_context=self.upload.archive_context)
             archive.m_add_sub_section(datamodel.EntryArchive.metadata, self._entry_metadata)
@@ -1388,7 +1389,10 @@ class Upload(Proc):
         if 'upload_id' not in kwargs:
             kwargs.update(upload_id=utils.create_uuid())
         kwargs.update(main_author=main_author.user_id)
-        self = super().create(**kwargs)
+        # Pylint has trouble recognizing the correct type returned by this overridden
+        # class method, so instead of using super().create(**kwargs), we use this
+        # alternative as discussed in https://github.com/PyCQA/pylint/issues/981
+        self = Proc.__dict__["create"].__func__(cls, **kwargs)
 
         return self
 
@@ -1578,7 +1582,7 @@ class Upload(Proc):
                 'Settings do no allow reprocessing of a published upload')
 
         # TODO remove after worker_hostnames are handled correctly
-        if config.celery.routing == config.CELERY_WORKER_ROUTING:
+        if config.celery.routing == CELERY_WORKER_ROUTING:
             if self.worker_hostname is None:
                 self.worker_hostname = worker_hostname
                 Entry._get_collection().update_many(
@@ -2133,7 +2137,7 @@ class Upload(Proc):
             except Exception as e:
                 # probably due to email configuration problems
                 # don't fail or present this error to clients
-                self.logger.error('could not send after processing email', exc_info=e)
+                logger.error('could not send after processing email', exc_info=e)
 
     def _cleanup_staging_files(self):
         if self.published and PublicUploadFiles.exists_for(self.upload_id):
diff --git a/nomad/search.py b/nomad/search.py
index cc044e20d72ffbb4bf7a224d335e89c2bfad6c6d..b301584200a69d6640b1927941f4bbe2d47c5bb6 100644
--- a/nomad/search.py
+++ b/nomad/search.py
@@ -1220,7 +1220,7 @@ def search(
         if required.exclude:
             excludes += required.exclude
         includes = required.include
-    search = search.source(includes=includes, excludes=excludes)
+    search = search.source(includes=includes, excludes=excludes)  # pylint: disable=no-member
 
     # aggregations
     aggs = [(name, _specific_agg(agg)) for name, agg in aggregations.items()]
diff --git a/pyproject.toml b/pyproject.toml
index ee0cec54f51fed27fcb5a373e57467e09f30f5f2..e67e48950f2b21043647eaee990bb9d82e9d5ce6 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -116,9 +116,9 @@ dev = [
     'mypy==0.730',
     'typed-ast>=1.4.2',
     'astroid>=2.5.1',
-    'pylint==2.3.1',
-    'pylint_plugin_utils==0.5',
-    'pylint_mongoengine==0.3.3',
+    'pylint==2.13.9',
+    'pylint_plugin_utils==0.7',
+    'pylint_mongoengine==0.4.0',
     'pycodestyle==2.8.0',
     'pytest==3.10.0',
     'pytest-timeout==1.4.2',
diff --git a/requirements-dev.txt b/requirements-dev.txt
index e9f34f2f91078db6494fdc58c3dda3e9b33a2711..3c1c5b852691ea5c09a4619c24fe7183417c10de 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -10,7 +10,7 @@ alembic==1.9.1            # via -r requirements.txt, jupyterhub
 amqp==2.6.1               # via -r requirements.txt, kombu
 aniso8601==7.0.0          # via -r requirements.txt, nomad-lab (pyproject.toml)
 anyio==3.6.2              # via -r requirements.txt, httpcore, jupyter-server, watchfiles
-appnope==0.1.3            # via -r requirements.txt, ipykernel, ipython
+appnope==0.1.3            # via -r requirements.txt
 argon2-cffi==21.3.0       # via -r requirements.txt, jupyter-server
 argon2-cffi-bindings==21.2.0  # via -r requirements.txt, argon2-cffi
 arrow==1.2.3              # via -r requirements.txt, isoduration
@@ -18,7 +18,7 @@ asciitree==0.3.3          # via -r requirements.txt, zarr
 ase==3.19.0               # via -r requirements.txt, asr, matid (dependencies/matid/pyproject.toml), nexusutils (dependencies/parsers/nexus/pyproject.toml), nomad-lab (pyproject.toml)
 asgiref==3.6.0            # via -r requirements.txt, nomad-lab (pyproject.toml)
 asr==0.4.1                # via -r requirements.txt, nomad-lab (pyproject.toml), workflowparsers (dependencies/parsers/workflow/pyproject.toml)
-astroid==2.5.1            # via nexusutils (dependencies/parsers/nexus/pyproject.toml), nomad-lab (pyproject.toml), pylint
+astroid==2.11.7           # via nomad-lab (pyproject.toml), pylint
 asttokens==2.1.0          # via devtools
 astunparse==1.6.3         # via -r requirements.txt, mdtraj
 async-generator==1.10     # via -r requirements.txt, jupyterhub
@@ -50,14 +50,14 @@ cloudpickle==2.2.0        # via -r requirements.txt, dask
 colorama==0.4.6           # via twine
 commonmark==0.9.1         # via -r requirements.txt, recommonmark
 coverage==6.5.0           # via pytest-cov
-cryptography==39.0.0      # via -r requirements.txt, nomad-lab (pyproject.toml), pyjwt, pyopenssl, rfc3161ng
+cryptography==39.0.0      # via -r requirements.txt, nomad-lab (pyproject.toml), pyjwt, pyopenssl, rfc3161ng, secretstorage
 cycler==0.11.0            # via -r requirements.txt, matplotlib
 dask[array]==2022.2.0     # via -r requirements.txt, hyperspy
 debugpy==1.6.5            # via -r requirements.txt, ipykernel
 decorator==5.1.1          # via -r requirements.txt, ipyparallel, ipython, validators
 defusedxml==0.7.1         # via -r requirements.txt, nbconvert
 devtools==0.8.0           # via nomad-lab (pyproject.toml)
-dill==0.3.6               # via -r requirements.txt, hyperspy
+dill==0.3.6               # via -r requirements.txt, hyperspy, pylint
 dnspython==2.2.1          # via -r requirements.txt, email-validator
 docker==6.0.1             # via -r requirements.txt, dockerspawner
 dockerspawner==12.1.0     # via -r requirements.txt, nomad-lab (pyproject.toml)
@@ -90,7 +90,7 @@ griddataformats==0.7.0    # via -r requirements.txt, mdanalysis
 gsd==2.7.0                # via -r requirements.txt, mdanalysis
 h11==0.12.0               # via -r requirements.txt, httpcore, uvicorn
 h5grove==1.2.0            # via -r requirements.txt, jupyterlab-h5web, nomad-lab (pyproject.toml)
-h5py==3.6.0               # via -r requirements.txt, electronicparsers (dependencies/parsers/electronic/pyproject.toml), h5grove, h5py-wrapper, hyperspy, jupyterlab-h5web, nexusutils (dependencies/parsers/nexus/pyproject.toml), nomad-lab (pyproject.toml), phonopy, pyscf, workflowparsers (dependencies/parsers/workflow/pyproject.toml)
+h5py==3.6.0               # via -r requirements.txt, electronicparsers (dependencies/parsers/electronic/pyproject.toml), h5grove, h5py-wrapper, hyperspy, jupyterlab-h5web, nexusutils (dependencies/parsers/nexus/pyproject.toml), nomad-lab (pyproject.toml), phonopy, workflowparsers (dependencies/parsers/workflow/pyproject.toml)
 h5py-wrapper==1.1.0       # via -r requirements.txt, hdfdict
 hdfdict @ git+https://github.com/shabihsherjeel/hdfdict@f48a586  # via -r requirements.txt, nexusutils (dependencies/parsers/nexus/pyproject.toml)
 hjson==3.0.2              # via -r requirements.txt, nomad-lab (pyproject.toml)
@@ -114,6 +114,7 @@ isort==4.3.21             # via pylint
 itsdangerous==2.0.1       # via -r requirements.txt, flask, nomad-lab (pyproject.toml)
 jaraco-classes==3.2.3     # via keyring
 jedi==0.18.2              # via -r requirements.txt, ipython
+jeepney==0.8.0            # via keyring, secretstorage
 jinja2==3.0.3             # via -r requirements.txt, flask, hyperspy, jupyter-server, jupyterhub, mkdocs, mkdocs-macros-plugin, mkdocs-material, nbconvert, nomad-lab (pyproject.toml), sphinx
 jmespath==0.10.0          # via -r requirements.txt, nomad-lab (pyproject.toml)
 joblib==1.1.0             # via -r requirements.txt, mdanalysis, nomad-lab (pyproject.toml), scikit-learn
@@ -154,7 +155,7 @@ mmtf-python==1.1.3        # via -r requirements.txt, mdanalysis
 mongoengine==0.25.0       # via -r requirements.txt, nomad-lab (pyproject.toml)
 mongomock==4.1.2          # via -r requirements.txt, optimade
 monty==2022.9.9           # via -r requirements.txt, pymatgen
-more-itertools==9.0.0     # via pytest
+more-itertools==9.0.0     # via jaraco-classes, pytest
 mpmath==1.2.1             # via -r requirements.txt, sympy
 mrcfile==1.4.3            # via -r requirements.txt, griddataformats
 msgpack==1.0.4            # via -r requirements.txt, mmtf-python, nomad-lab (pyproject.toml)
@@ -172,7 +173,7 @@ nptyping==1.4.4           # via -r requirements.txt, nomad-lab (pyproject.toml)
 numba==0.56.4             # via -r requirements.txt, hyperspy, sparse
 numcodecs==0.10.2         # via -r requirements.txt, zarr
 numexpr==2.8.4            # via -r requirements.txt, hyperspy
-numpy==1.21.6             # via -r requirements.txt, ase, biopython, cftime, dask, griddataformats, gsd, h5grove, h5py, hyperspy, imageio, matid (dependencies/matid/pyproject.toml), matplotlib, mdanalysis, mdtraj, mrcfile, netcdf4, nexusutils (dependencies/parsers/nexus/pyproject.toml), nomad-dos-fingerprints (dependencies/nomad-dos-fingerprints/pyproject.toml), nptyping, numba, numcodecs, numexpr, pandas, phonopy, pymatgen, pyscf, pywavelets, scikit-image, scikit-learn, scipy, sparse, spglib, tifffile, xarray, xrdtools, zarr
+numpy==1.21.6             # via -r requirements.txt, ase, biopython, cftime, dask, griddataformats, gsd, h5grove, h5py, hyperspy, imageio, matid (dependencies/matid/pyproject.toml), matplotlib, mdanalysis, mdtraj, mrcfile, netcdf4, nexusutils (dependencies/parsers/nexus/pyproject.toml), nomad-dos-fingerprints (dependencies/nomad-dos-fingerprints/pyproject.toml), nptyping, numba, numcodecs, numexpr, pandas, phonopy, pymatgen, pywavelets, scikit-image, scikit-learn, scipy, sparse, spglib, tifffile, xarray, xrdtools, zarr
 oauthenticator==14.2.0    # via -r requirements.txt, nomad-lab (pyproject.toml)
 oauthlib==3.2.2           # via -r requirements.txt, jupyterhub
 openpyxl==3.0.9           # via -r requirements.txt, nomad-lab (pyproject.toml)
@@ -196,6 +197,7 @@ pillow==9.4.0             # via -r requirements.txt, imageio, matplotlib, scikit
 pint==0.17                # via -r requirements.txt, hyperspy, nomad-lab (pyproject.toml)
 pip-tools==6.9.0          # via nomad-lab (pyproject.toml)
 pkginfo==1.8.3            # via twine
+platformdirs==3.0.0       # via pylint
 plotly==5.11.0            # via -r requirements.txt, asr, pymatgen
 pluggy==1.0.0             # via pytest
 prettytable==3.6.0        # via -r requirements.txt, hyperspy
@@ -211,16 +213,15 @@ pycparser==2.21           # via -r requirements.txt, cffi
 pydantic==1.9.1           # via -r requirements.txt, fastapi, nomad-lab (pyproject.toml), optimade
 pygments==2.14.0          # via -r requirements.txt, ipython, mkdocs-material, nbconvert, readme-renderer, sphinx
 pyjwt[crypto]==1.7.1      # via -r requirements.txt, nomad-lab (pyproject.toml)
-pylint==2.3.1             # via nexusutils (dependencies/parsers/nexus/pyproject.toml), nomad-lab (pyproject.toml), pylint-mongoengine, pylint-plugin-utils
-pylint-mongoengine==0.3.3  # via nomad-lab (pyproject.toml)
-pylint-plugin-utils==0.5  # via nomad-lab (pyproject.toml), pylint-mongoengine
+pylint==2.13.9            # via nexusutils (dependencies/parsers/nexus/pyproject.toml), nomad-lab (pyproject.toml), pylint-mongoengine, pylint-plugin-utils
+pylint-mongoengine==0.4.0  # via nomad-lab (pyproject.toml)
+pylint-plugin-utils==0.7  # via nomad-lab (pyproject.toml), pylint-mongoengine
 pymatgen==2022.0.17       # via -r requirements.txt, asr, nomad-lab (pyproject.toml)
 pymdown-extensions==9.7   # via mkdocs-material
 pymongo==3.12.1           # via -r requirements.txt, mongoengine, nomad-lab (pyproject.toml), optimade
 pyopenssl==23.0.0         # via -r requirements.txt, certipy
 pyparsing==3.0.9          # via -r requirements.txt, matplotlib, mdtraj, rdflib
 pyrsistent==0.19.3        # via -r requirements.txt, jsonschema
-pyscf==2.0.1 ; sys_platform == "darwin"  # via -r requirements.txt, electronicparsers (dependencies/parsers/electronic/pyproject.toml)
 pytest==3.10.0            # via nexusutils (dependencies/parsers/nexus/pyproject.toml), nomad-lab (pyproject.toml), pytest-cov, pytest-timeout
 pytest-cov==2.7.1         # via nexusutils (dependencies/parsers/nexus/pyproject.toml), nomad-lab (pyproject.toml)
 pytest-runner==6.0.0      # via -r requirements.txt, h5py-wrapper
@@ -256,7 +257,8 @@ ruamel-yaml-clib==0.2.7   # via -r requirements.txt, ruamel-yaml
 runstats==2.0.0           # via -r requirements.txt, nomad-lab (pyproject.toml)
 scikit-image==0.19.3      # via -r requirements.txt, hyperspy
 scikit-learn==1.0.2       # via -r requirements.txt, matid (dependencies/matid/pyproject.toml)
-scipy==1.7.1              # via -r requirements.txt, ase, atomisticparsers (dependencies/parsers/atomistic/pyproject.toml), griddataformats, hyperspy, matid (dependencies/matid/pyproject.toml), mdanalysis, mdtraj, nomad-lab (pyproject.toml), pymatgen, pyscf, scikit-image, scikit-learn, sparse
+scipy==1.7.1              # via -r requirements.txt, ase, atomisticparsers (dependencies/parsers/atomistic/pyproject.toml), griddataformats, hyperspy, matid (dependencies/matid/pyproject.toml), mdanalysis, mdtraj, nomad-lab (pyproject.toml), pymatgen, scikit-image, scikit-learn, sparse
+secretstorage==3.3.3      # via keyring
 send2trash==1.8.0         # via -r requirements.txt, jupyter-server
 sentinels==1.0.0          # via -r requirements.txt, mongomock
 six==1.16.0               # via -r requirements.txt, asttokens, astunparse, basicauth, bcrypt, bleach, ecdsa, elasticsearch-dsl, griddataformats, html5lib, isodate, pytest, python-dateutil, python-multipart, rdflib, rfc3339-validator, validators
@@ -284,7 +286,7 @@ terminado==0.17.1         # via -r requirements.txt, jupyter-server
 threadpoolctl==3.1.0      # via -r requirements.txt, mdanalysis, scikit-learn
 tifffile==2021.11.2       # via -r requirements.txt, h5grove, hyperspy, scikit-image
 tinycss2==1.2.1           # via -r requirements.txt, nbconvert
-tomli==2.0.1              # via build, pep517
+tomli==2.0.1              # via build, pep517, pylint
 toolz==0.12.0             # via -r requirements.txt, dask, hyperspy, partd
 toposort==1.9             # via -r requirements.txt, nomad-lab (pyproject.toml)
 tornado==6.2              # via -r requirements.txt, ipykernel, ipyparallel, jupyter-client, jupyter-server, jupyterhub, terminado
@@ -292,10 +294,10 @@ tqdm==4.64.1              # via -r requirements.txt, hyperspy, ipyparallel, mdan
 traitlets==5.8.1          # via -r requirements.txt, ipykernel, ipyparallel, ipython, jupyter-client, jupyter-core, jupyter-server, jupyter-telemetry, jupyterhub, matplotlib-inline, nbclient, nbconvert, nbformat
 traits==6.4.1             # via -r requirements.txt, hyperspy
 twine==3.4.2              # via nomad-lab (pyproject.toml)
-typed-ast==1.4.2          # via astroid, mypy, nexusutils (dependencies/parsers/nexus/pyproject.toml), nomad-lab (pyproject.toml)
+typed-ast==1.4.2          # via astroid, mypy, nomad-lab (pyproject.toml)
 types-pytz==2022.7.0.0    # via nexusutils (dependencies/parsers/nexus/pyproject.toml)
 types-pyyaml==6.0.12.1    # via nexusutils (dependencies/parsers/nexus/pyproject.toml)
-typing-extensions==4.4.0  # via -r requirements.txt, anyio, argon2-cffi, arrow, asgiref, async-timeout, gitpython, importlib-metadata, kiwisolver, mypy, numcodecs, optimade, pydantic, pymatgen, redis, structlog, uvicorn, xarray
+typing-extensions==4.4.0  # via -r requirements.txt, anyio, argon2-cffi, arrow, asgiref, astroid, async-timeout, gitpython, importlib-metadata, kiwisolver, mypy, numcodecs, optimade, platformdirs, pydantic, pylint, pymatgen, redis, structlog, uvicorn, xarray
 typish==1.9.3             # via -r requirements.txt, nptyping
 uncertainties==3.1.7      # via -r requirements.txt, pymatgen
 unidecode==1.3.2          # via -r requirements.txt, nomad-lab (pyproject.toml)
diff --git a/tests/app/v1/routers/test_uploads.py b/tests/app/v1/routers/test_uploads.py
index dd8bc92cd09ac454a6f01fc012dc39f01bbce944..c847c1a47866965d2a01994dd5cf1399211a328c 100644
--- a/tests/app/v1/routers/test_uploads.py
+++ b/tests/app/v1/routers/test_uploads.py
@@ -35,6 +35,7 @@ from tests.processing.test_edit_metadata import (
     assert_metadata_edited, all_coauthor_metadata, all_admin_metadata)
 from tests.app.v1.routers.common import assert_response, assert_browser_download_headers
 from nomad import config, files, infrastructure
+from nomad.config.models import BundleImportSettings
 from nomad.processing import Upload, Entry, ProcessStatus
 from nomad.files import UploadFiles, StagingUploadFiles, PublicUploadFiles
 from nomad.bundles import BundleExporter
@@ -1621,11 +1622,11 @@ def test_post_upload_action_publish(
 
 @pytest.mark.parametrize('import_settings, query_args', [
     pytest.param(
-        config.BundleImportSettings(include_archive_files=False, trigger_processing=True),
+        BundleImportSettings(include_archive_files=False, trigger_processing=True),
         dict(embargo_length=0),
         id='trigger-processing'),
     pytest.param(
-        config.BundleImportSettings(include_archive_files=True, trigger_processing=False),
+        BundleImportSettings(include_archive_files=True, trigger_processing=False),
         dict(embargo_length=28),
         id='no-processing')
 ])
diff --git a/tests/normalizing/conftest.py b/tests/normalizing/conftest.py
index 1a868b21a352e1c534d66cdbe438a2e45b618a4c..89a1699091c6868e836038dd6c1f640fac270126 100644
--- a/tests/normalizing/conftest.py
+++ b/tests/normalizing/conftest.py
@@ -15,8 +15,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-
-# from atomisticparsers.utils.mdanalysis import mean_squared_displacement
 import numpy as np
 from typing import List, Union
 import pytest
@@ -25,7 +23,6 @@ import ase.build
 import re
 import yaml
 from warnings import warn
-# from nomad.datamodel.results import MeanSquaredDisplacement
 
 from nomad.utils import strip
 from nomad.units import ureg
@@ -184,7 +181,7 @@ def get_template_eels() -> EntryArchive:
     '''Returns a basic archive template for an EELS experiment.
     '''
     # Ensure that the eels schema is loaded
-    from eelsdbparser import eelsdb_parser  # pylint: disable=unused-import
+    from eelsdbparser import eelsdb_parser  # pylint: disable=unused-import,import-error
     dct_data = yaml.safe_load(strip(f'''
         results:
             properties:
diff --git a/tests/normalizing/test_dos.py b/tests/normalizing/test_dos.py
index 0bb95e477be7bef50e73d104cddabe318a6a8fac..2fee370ddfaa9779ae2813b3967999fa81fe5c89 100644
--- a/tests/normalizing/test_dos.py
+++ b/tests/normalizing/test_dos.py
@@ -28,7 +28,7 @@ from tests.normalizing.conftest import (  # pylint: disable=unused-import
     run_normalize,
 )
 
-from nomad_dos_fingerprints import DOSFingerprint
+from nomad_dos_fingerprints import DOSFingerprint  # pylint: disable=import-error
 from nomad.normalizing.dos_integrator import integrate_dos
 
 
diff --git a/tests/normalizing/test_material.py b/tests/normalizing/test_material.py
index 8628ff5a79f482d7b3b3246d7d79abcc31330145..28eb3ebebfc310c9b013108924cf552ce90fe705 100644
--- a/tests/normalizing/test_material.py
+++ b/tests/normalizing/test_material.py
@@ -20,7 +20,7 @@ import numpy as np
 import pytest
 from ase import Atoms
 import ase.build
-from matid.symmetry.wyckoffset import WyckoffSet
+from matid.symmetry.wyckoffset import WyckoffSet  # pylint: disable=import-error
 
 from nomad.units import ureg
 from nomad import atomutils
diff --git a/tests/processing/test_data.py b/tests/processing/test_data.py
index f1b730f397603967caabdb587197f107ca5640eb..17ba7eb4b11554e1f349b8397eeda38a12e6ef39 100644
--- a/tests/processing/test_data.py
+++ b/tests/processing/test_data.py
@@ -27,6 +27,7 @@ import json
 import yaml
 
 from nomad import utils, infrastructure, config
+from nomad.config.models import BundleImportSettings
 from nomad.archive import read_partial_archive_from_mongo
 from nomad.files import UploadFiles, StagingUploadFiles, PublicUploadFiles
 from nomad.parsing.parser import Parser
@@ -319,7 +320,7 @@ def test_publish_failed(
     #     config.BundleImportSettings(include_archive_files=True, trigger_processing=False), 0,
     #     id='no-processing'),
     pytest.param(
-        config.BundleImportSettings(include_archive_files=False, trigger_processing=True), 17,
+        BundleImportSettings(include_archive_files=False, trigger_processing=True), 17,
         id='trigger-processing')
 ])
 def test_publish_to_central_nomad(
diff --git a/tests/processing/test_edit_metadata.py b/tests/processing/test_edit_metadata.py
index 5efa8740d6d6e7473217dfee30fe0c258a4ade06..2f57efca00aa2a7c945b774b7ec795478e8850f0 100644
--- a/tests/processing/test_edit_metadata.py
+++ b/tests/processing/test_edit_metadata.py
@@ -73,6 +73,7 @@ def assert_edit_request(user, **kwargs):
         query=query, owner=owner, metadata=metadata, entries=entries, entries_key=entries_key,
         verify=verify_only)
     edit_start = datetime.utcnow().isoformat()[0:22]
+    error_locs = []
     try:
         MetadataEditRequestHandler.edit_metadata(edit_request_json, upload_id, user)
     except RequestValidationError as e:
diff --git a/tests/test_mkdocs.py b/tests/test_mkdocs.py
new file mode 100644
index 0000000000000000000000000000000000000000..027a65d2debfed338a58f65810c0488622c929ae
--- /dev/null
+++ b/tests/test_mkdocs.py
@@ -0,0 +1,140 @@
+#
+# Copyright The NOMAD Authors.
+#
+# This file is part of NOMAD. See https://nomad-lab.eu for further info.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+from enum import Enum
+import pytest
+from typing import Union, List, Dict, Optional
+from typing_extensions import Literal, Annotated  # type: ignore
+from pydantic import BaseModel, Field
+from nomad.config.models import WidgetHistogram, WidgetTerms
+from nomad.mkdocs import (
+    get_field_type_info,
+    get_field_description,
+    get_field_default,
+    get_field_options,
+    get_field_deprecated
+)
+
+
+class MyStrEnum(str, Enum):
+    TEST = 'test'
+
+
+class MyIntEnum(int, Enum):
+    TEST = 1
+
+
+@pytest.mark.parametrize(
+    'type_, name, classes',
+    [
+        pytest.param(str, 'str', {str}, id='str'),
+        pytest.param(int, 'int', {int}, id='int'),
+        pytest.param(float, 'float', {float}, id='float'),
+        pytest.param(list, 'list', {list}, id='list'),
+        pytest.param(dict, 'dict', {dict}, id='dict'),
+        pytest.param(set, 'set', {set}, id='set'),
+        pytest.param(tuple, 'tuple', {tuple}, id='tuple'),
+        pytest.param(WidgetHistogram, 'WidgetHistogram', {WidgetHistogram}, id='basemodel'),
+        pytest.param(Enum, 'Enum', {Enum}, id='class'),
+        pytest.param(Optional[WidgetHistogram], 'WidgetHistogram', {WidgetHistogram}, id='optional-ignored'),
+        pytest.param(Union[str, WidgetHistogram], 'Union[str, WidgetHistogram]', {Union, str, WidgetHistogram}, id='union'),
+        pytest.param(List[Union[str, WidgetHistogram]], 'List[Union[str, WidgetHistogram]]', {list, Union, str, WidgetHistogram}, id='list-with-union'),
+        pytest.param(Dict[str, WidgetHistogram], 'Dict[str, WidgetHistogram]', {dict, str, WidgetHistogram}, id='dict'),
+        pytest.param(Literal["test"], 'str', {Literal}, id='literal-not-shown-str'),
+        pytest.param(Literal[1], 'int', {Literal}, id='literal-not-shown-int'),
+        pytest.param(MyStrEnum, 'str', {MyStrEnum}, id='enum-string'),
+        pytest.param(MyIntEnum, 'int', {MyIntEnum}, id='enum-int'),
+        pytest.param(
+            List[Annotated[Union[WidgetTerms, WidgetHistogram], Field(discriminator="type")]],  # type: ignore
+            'List[Union[WidgetTerms, WidgetHistogram]]',
+            {list, Union, WidgetTerms, WidgetHistogram},
+            id='annotated-ignored'
+        ),
+    ]
+)
+def test_field_type_info(type_, name, classes):
+
+    class Test(BaseModel):
+        a: type_ = Field()
+
+    name_found, classes_found = get_field_type_info(Test.__fields__['a'])
+    assert name_found == name
+    assert classes_found == classes
+
+
+@pytest.mark.parametrize(
+    'description',
+    [
+        pytest.param(None, id='no-description'),
+        pytest.param('This is a test description.', id='string-description'),
+    ]
+)
+def test_field_description(description):
+
+    class Test(BaseModel):
+        a: str = Field(description=description)
+
+    description_found = get_field_description(Test.__fields__['a'])
+    assert description_found == description
+
+
+@pytest.mark.parametrize(
+    'default, default_str',
+    [
+        pytest.param(None, None, id='no-default'),
+        pytest.param('test', '`test`', id='str-default'),
+        pytest.param(1, '`1`', id='int-default'),
+        pytest.param({'test': 'test'}, 'Complex object, default value not displayed.', id='complex-default'),
+    ]
+)
+def test_field_default(default, default_str):
+
+    class Test(BaseModel):
+        a: str = Field(default)
+
+    default_found = get_field_default(Test.__fields__['a'])
+    assert default_found == default_str
+
+
+@pytest.mark.parametrize(
+    'type_, options',
+    [
+        pytest.param(str, {}, id='no-options'),
+        pytest.param(MyStrEnum, {'test': None}, id='str-options'),
+        pytest.param(MyIntEnum, {'1': None}, id='int-options'),
+    ]
+)
+def test_field_options(type_, options):
+
+    class Test(BaseModel):
+        a: type_ = Field()
+
+    options_found = get_field_options(Test.__fields__['a'])
+    assert len(options_found) == len(options)
+    for key in options_found:
+        assert options_found[key] == options[key]
+
+
+@pytest.mark.parametrize('deprecated', [True, False])
+def test_field_deprecated(deprecated):
+
+    class Test(BaseModel):
+        a: str = Field(deprecated=deprecated)
+
+    deprecated_found = get_field_deprecated(Test.__fields__['a'])
+    assert deprecated_found == deprecated