UTI: PoC - Make detailed hover information copyable
[csit.git] / resources / tools / dash / app / pal / trending / layout.py
index 9153081..6be71ac 100644 (file)
@@ -15,7 +15,8 @@
 """
 
 
-import plotly.graph_objects as go
+import json
+import pandas as pd
 
 from dash import dcc
 from dash import html
@@ -25,16 +26,16 @@ from dash.exceptions import PreventUpdate
 from yaml import load, FullLoader, YAMLError
 from datetime import datetime, timedelta
 
-from pprint import pformat
-
-from .data import read_data
+from ..data.data import Data
+from .graphs import trending_tput
 
 
 class Layout:
     """
     """
 
-    def __init__(self, app, html_layout_file, spec_file, graph_layout_file):
+    def __init__(self, app, html_layout_file, spec_file, graph_layout_file,
+        data_spec_file):
         """
         """
 
@@ -43,9 +44,20 @@ class Layout:
         self._html_layout_file = html_layout_file
         self._spec_file = spec_file
         self._graph_layout_file = graph_layout_file
+        self._data_spec_file = data_spec_file
 
         # Read the data:
-        self._data = read_data()
+        data_mrr = Data(
+            data_spec_file=self._data_spec_file,
+            debug=True
+        ).read_trending_mrr()
+
+        data_ndrpdr = Data(
+            data_spec_file=self._data_spec_file,
+            debug=True
+        ).read_trending_ndrpdr()
+
+        self._data = pd.concat([data_mrr, data_ndrpdr], ignore_index=True)
 
         # Read from files:
         self._html_layout = ""
@@ -105,6 +117,10 @@ class Layout:
     def data(self):
         return self._data
 
+    @property
+    def layout(self):
+        return self._graph_layout
+
     def add_content(self):
         """
         """
@@ -158,6 +174,18 @@ class Layout:
                         )
                     ],
                     type="circle"
+                ),
+                html.Div(
+                    children=[
+                        dcc.Markdown("""
+                        **Metadata**
+
+                        Click on data points in the graph.
+                        """),
+                        html.Pre(
+                            id="hover-metadata"
+                        )
+                    ]
                 )
             ],
             style={
@@ -195,9 +223,6 @@ class Layout:
                         )
                     ]
                 ),
-                # Debug output, TODO: Remove
-                html.H5("Debug output"),
-                html.Pre(id="div-ctrl-info")
             ]
         )
 
@@ -289,11 +314,12 @@ class Layout:
                 html.Br(),
                 dcc.DatePickerRange(
                     id="dpr-period",
-                    min_date_allowed=datetime(2021, 1, 1),
+                    min_date_allowed=datetime.utcnow() - timedelta(days=180),
                     max_date_allowed=datetime.utcnow(),
                     initial_visible_month=datetime.utcnow(),
                     start_date=datetime.utcnow() - timedelta(days=180),
-                    end_date=datetime.utcnow()
+                    end_date=datetime.utcnow(),
+                    display_format="D MMMM YY"
                 )
             ]
         )
@@ -446,7 +472,6 @@ class Layout:
 
         @app.callback(
             Output("graph", "figure"),
-            Output("div-ctrl-info", "children"),  # Debug output TODO: Remove
             Output("selected-tests", "data"),  # Store
             Output("cl-selected", "options"),  # User selection
             Output("dd-ctrl-phy", "value"),
@@ -508,7 +533,7 @@ class Layout:
                         for framesize in framesizes:
                             for ttype in testtypes:
                                 tid = (
-                                    f"{phy}-"
+                                    f"{phy.replace('af_xdp', 'af-xdp')}-"
                                     f"{area}-"
                                     f"{framesize.lower()}-"
                                     f"{core.lower()}-"
@@ -525,12 +550,14 @@ class Layout:
                                         "core": core.lower(),
                                         "testtype": ttype.lower()
                                     })
-                return (no_update, no_update, store_sel, _list_tests(), None,
+                return (no_update, store_sel, _list_tests(), None,
                     None, None, no_update)
 
             elif trigger_id in ("btn-sel-display", "dpr-period"):
-                fig, style = _update_graph(store_sel, d_start, d_end)
-                return (fig, pformat(store_sel), no_update, no_update,
+                fig, style = trending_tput(
+                    self.data, store_sel, self.layout, d_start, d_end
+                )
+                return (fig, no_update, no_update,
                     no_update, no_update, no_update, style)
 
             elif trigger_id == "btn-sel-remove":
@@ -540,91 +567,27 @@ class Layout:
                         if item["id"] not in list_sel:
                             new_store_sel.append(item)
                     store_sel = new_store_sel
-                    fig, style = _update_graph(store_sel, d_start, d_end)
-                return (fig, pformat(store_sel), store_sel, _list_tests(),
-                    no_update, no_update, no_update, style)
-
-        def _update_graph(sel, start, end):
-            """
-            """
-
-            if not sel:
-                return no_update, no_update
-
-            def _is_selected(label, sel):
-                for itm in sel:
-                    phy = itm["phy"].split("-")
-                    if len(phy) == 4:
-                        topo, arch, nic, drv = phy
-                    else:
-                        continue
-                    if nic not in label:
-                        continue
-                    if drv != "dpdk" and drv not in label:
-                        continue
-                    if itm["test"] not in label:
-                        continue
-                    if itm["framesize"] not in label:
-                        continue
-                    if itm["core"] not in label:
-                        continue
-                    if itm["testtype"] not in label:
-                        continue
-                    return (
-                        f"{itm['phy']}-{itm['framesize']}-{itm['core']}-"
-                        f"{itm['test']}-{itm['testtype']}"
+                if store_sel:
+                    fig, style = trending_tput(
+                        self.data, store_sel, self.layout, d_start, d_end
                     )
+                    return (fig, store_sel, _list_tests(),
+                    no_update, no_update, no_update, style)
                 else:
-                    return None
-
-            style={
-                "vertical-align": "top",
-                "display": "inline-block",
-                "width": "80%",
-                "padding": "5px"
-            }
-
-            fig = go.Figure()
-            dates = self.data.iloc[[0], 1:].values.flatten().tolist()[::-1]
-            x_data = [
-                datetime(
-                    int(date[0:4]), int(date[4:6]), int(date[6:8]),
-                    int(date[9:11]), int(date[12:])
-                ) for date in dates
-            ]
-            x_data_range = [
-                date for date in x_data if date >= start and date <= end
-            ]
-            vpp = self.data.iloc[[1], 1:].values.flatten().tolist()[::-1]
-            csit = list(self.data.columns[1:])[::-1]
-            labels = list(self.data["Build Number:"][3:])
-            for i in range(3, len(self.data)):
-                name = _is_selected(labels[i-3], sel)
-                if not name:
-                    continue
-                y_data = [
-                    float(v) / 1e6 for v in \
-                        self.data.iloc[[i], 1:].values.flatten().tolist()[::-1]
-                ]
-                hover_txt = list()
-                for x_idx, x_itm in enumerate(x_data):
-                    hover_txt.append(
-                        f"date: {x_itm}<br>"
-                        f"average [Mpps]: {y_data[x_idx]}<br>"
-                        f"vpp-ref: {vpp[x_idx]}<br>"
-                        f"csit-ref: {csit[x_idx]}"
-                    )
-                fig.add_trace(
-                    go.Scatter(
-                        x=x_data_range,
-                        y= y_data,
-                        name=name,
-                        mode="markers+lines",
-                        text=hover_txt,
-                        hoverinfo=u"text+name"
-                    )
-                )
-            layout = self._graph_layout.get("plot-trending", dict())
-            fig.update_layout(layout)
+                    style={
+                        "vertical-align": "top",
+                        "display": "none",
+                        "width": "80%",
+                        "padding": "5px"
+                    }
+                    return (no_update, store_sel, _list_tests(),
+                        no_update, no_update, no_update, style)
 
-            return fig, style
+        @app.callback(
+            Output("hover-metadata", "children"),
+            Input("graph", "clickData")
+        )
+        def _show_metadata(hover_data):
+            if not hover_data:
+                raise PreventUpdate
+            return json.dumps(hover_data, indent=2)