From e3d4c8bafc271c77f98f4e2ff3eb0907971e29cb Mon Sep 17 00:00:00 2001
From: Mohammad Nakhaee <nakhaee@physik.hu-berlin.de>
Date: Thu, 22 Feb 2024 13:00:00 +0000
Subject: [PATCH] Resolve "plotly config dictionary can't be fed in from
 normalize function"

Changelog: Fixed
---
 docs/reference/annotations.md              | 95 +++++++++++++++++++++-
 gui/src/components/archive/PlotlyFigure.js |  2 +-
 gui/src/components/plotting/Plot.js        |  3 +-
 gui/tests/artifacts.js                     |  3 +
 nomad/datamodel/metainfo/plot.py           |  3 +-
 5 files changed, 102 insertions(+), 4 deletions(-)

diff --git a/docs/reference/annotations.md b/docs/reference/annotations.md
index fd92de53f9..224c32ca93 100644
--- a/docs/reference/annotations.md
+++ b/docs/reference/annotations.md
@@ -174,7 +174,15 @@ class CustomSection(PlotSection, EntryData):
 
         heatmap = go.Heatmap(z=heatmap_data, showscale=False, connectgaps=True, zsmooth='best')
         figure3 = go.Figure(data=heatmap)
-        self.figures.append(PlotlyFigure(label='figure 3', index=0, figure=figure3.to_plotly_json()))
+        figure_json = figure3.to_plotly_json()
+        figure_json['config'] = {'staticPlot': True}
+        self.figures.append(PlotlyFigure(label='figure 3', index=0, figure=figure_json)
+```
+
+To customize the plot configuration in python one can add the config to the generated json by to_plotly_json().
+
+```
+figure_json['config'] = {'staticPlot': True}
 ```
 
 In YAML schemas, plots can be defined by using the PlotSection as a base class,
@@ -183,4 +191,89 @@ and additionally utilizing different flavours of plot annotations. The different
 {{ pydantic_model('nomad.datamodel.metainfo.annotations.PlotlyGraphObjectAnnotation', heading='### PlotlyGraphObjectAnnotation') }}
 {{ pydantic_model('nomad.datamodel.metainfo.annotations.PlotlyExpressAnnotation', heading='### PlotlyExpressAnnotation') }}
 {{ pydantic_model('nomad.datamodel.metainfo.annotations.PlotlySubplotsAnnotation', heading='### PlotlySubplotsAnnotation') }}
+
+### plot annotations in python
+For simple plots in Python schema one could use the annotations without normalizer:
+
+```python
+from nomad.datamodel.metainfo.plot import PlotSection
+from nomad.metainfo import Quantity, Section
+from nomad.datamodel.data import EntryData
+
+class CustomSection(PlotSection, EntryData):
+    m_def = Section(
+        a_plotly_graph_object=[
+            {
+                'label': 'graph object 1',
+                'data': {'x': '#time', 'y': '#chamber_pressure'},
+                'layout': {
+                    'title': {
+                        'text': 'Plot in section level'
+                    },
+                    'xaxis': {
+                        'title': {
+                            'text': 'x data'
+                        }
+                    },
+                    'yaxis': {
+                        'title': {
+                            'text': 'y data'
+                        }
+                    }
+                }
+            }, {
+                'label': 'graph object 2',
+                'data': {'x': '#time', 'y': '#substrate_temperature'}
+            }
+        ],
+        a_plotly_express={
+            'label': 'fig 2',
+            'index': 2,
+            'method': 'scatter',
+            'x': '#substrate_temperature',
+            'y': '#chamber_pressure',
+            'color': '#chamber_pressure'
+        },
+        a_plotly_subplots={
+            'label': 'fig 1',
+            'index': 1,
+            'parameters': {'rows': 2, 'cols': 2},
+            'layout': {
+                'title': {
+                    'text': 'All plots'
+                }
+            },
+            'plotly_express': [
+                {
+                    'method': 'scatter',
+                    'x': '#time',
+                    'y': '#chamber_pressure',
+                    'color': '#chamber_pressure'
+                },
+                {
+                    'method': 'scatter',
+                    'x': '#time',
+                    'y': '#substrate_temperature',
+                    'color': '#substrate_temperature'
+                },
+                {
+                    'method': 'scatter',
+                    'x': '#substrate_temperature',
+                    'y': '#chamber_pressure',
+                    'color': '#chamber_pressure'
+                },
+                {
+                    'method': 'scatter',
+                    'x': '#substrate_temperature',
+                    'y': '#chamber_pressure',
+                    'color': '#substrate_temperature'
+                }
+            ]
+        }
+    )
+    time = Quantity(type=float, shape=['*'], unit='s', a_eln=dict(component='NumberEditQuantity'))
+    substrate_temperature = Quantity(type=float, shape=['*'], unit='K', a_eln=dict(component='NumberEditQuantity'))
+    chamber_pressure = Quantity(type=float, shape=['*'], unit='Pa', a_eln=dict(component='NumberEditQuantity'))
+```
+
 {{ pydantic_model('nomad.datamodel.metainfo.annotations.PlotAnnotation', heading='### PlotAnnotation (Deprecated)') }}
diff --git a/gui/src/components/archive/PlotlyFigure.js b/gui/src/components/archive/PlotlyFigure.js
index 793d59e4cb..b0b9dc026b 100644
--- a/gui/src/components/archive/PlotlyFigure.js
+++ b/gui/src/components/archive/PlotlyFigure.js
@@ -232,7 +232,7 @@ const PlotlyFigure = React.memo(function PlotlyFigure({plot, section, sectionDef
     return plotlyGraphObj
   }, [plot, section, sectionDef, units])
 
-  return <Box minWidth={500} height={500}>
+  return <Box minWidth={500} height={plotlyGraphObj?.layout?.height || plotlyGraphObj?.layout?.template?.layout?.height || 500}>
     <Plot
       data={plotlyGraphObj.data}
       layout={plotlyGraphObj.layout}
diff --git a/gui/src/components/plotting/Plot.js b/gui/src/components/plotting/Plot.js
index 5384eff183..0cd785215c 100644
--- a/gui/src/components/plotting/Plot.js
+++ b/gui/src/components/plotting/Plot.js
@@ -200,6 +200,7 @@ const Plot = React.memo(forwardRef(({
   // Set the final layout. It is a combination of a default layout, the layout
   // set by the user and some properties of the curretly used layout.
   const finalLayout = useMemo(() => {
+    const withTitle = layout?.title?.text || layout?.template?.title?.text || layout?.annotations?.some(item => item?.text)
     const defaultLayout = {
       dragmode: 'pan',
       hovermode: false,
@@ -210,7 +211,7 @@ const Plot = React.memo(forwardRef(({
       margin: {
         l: theme.spacing(4),
         r: theme.spacing(1.5),
-        t: theme.spacing(layout?.title?.text ? 5 : 1),
+        t: theme.spacing(withTitle ? 5 : 1),
         b: theme.spacing(6)
       },
       title: {
diff --git a/gui/tests/artifacts.js b/gui/tests/artifacts.js
index cb756dad19..fd7094cfe9 100644
--- a/gui/tests/artifacts.js
+++ b/gui/tests/artifacts.js
@@ -5776,6 +5776,9 @@ window.nomadArtifacts = {
             "m_parent_index": 2,
             "m_parent_sub_section": "section_definitions",
             "name": "PlotlyFigure",
+            "more": {
+              "label_quantity": "label"
+            },
             "base_sections": [
               "/packages/0/section_definitions/0"
             ],
diff --git a/nomad/datamodel/metainfo/plot.py b/nomad/datamodel/metainfo/plot.py
index b97ccabc09..9bd88ac583 100644
--- a/nomad/datamodel/metainfo/plot.py
+++ b/nomad/datamodel/metainfo/plot.py
@@ -16,7 +16,7 @@
 # limitations under the License.
 #
 from nomad.datamodel.data import ArchiveSection
-from nomad.metainfo import Quantity, SubSection, Package, MSection, JSON
+from nomad.metainfo import Quantity, SubSection, Package, MSection, JSON, Section
 import plotly.express as px
 import plotly.graph_objs as go
 from plotly.subplots import make_subplots
@@ -148,6 +148,7 @@ class PlotlyFigureQuantity(Quantity):
 
 
 class PlotlyFigure(Figure):
+    m_def = Section(label_quantity='label')
     figure = PlotlyFigureQuantity(
         type=JSON, description='Contains the JSON serialization for a plotly figure.'
     )
-- 
GitLab