feat(uti): optimize offcanvas
[csit.git] / resources / tools / dash / app / pal / trending / layout.py
index dd097b4..a3b3fa8 100644 (file)
@@ -19,12 +19,13 @@ import dash_bootstrap_components as dbc
 
 from dash import dcc
 from dash import html
 
 from dash import dcc
 from dash import html
-from dash import callback_context, no_update
+from dash import callback_context, no_update, ALL
 from dash import Input, Output, State
 from dash.exceptions import PreventUpdate
 from yaml import load, FullLoader, YAMLError
 from datetime import datetime, timedelta
 from copy import deepcopy
 from dash import Input, Output, State
 from dash.exceptions import PreventUpdate
 from yaml import load, FullLoader, YAMLError
 from datetime import datetime, timedelta
 from copy import deepcopy
+from json import loads, JSONDecodeError
 
 from ..data.data import Data
 from .graphs import graph_trending, graph_hdrh_latency, \
 
 from ..data.data import Data
 from .graphs import graph_trending, graph_hdrh_latency, \
@@ -35,20 +36,22 @@ class Layout:
     """
     """
 
     """
     """
 
-    NO_GRAPH = {"data": [], "layout": {}, "frames": []}
+    STYLE_DISABLED = {"display": "none"}
+    STYLE_ENABLED = {"display": "inherit"}
 
     CL_ALL_DISABLED = [{
         "label": "All",
         "value": "all",
         "disabled": True
     }]
 
     CL_ALL_DISABLED = [{
         "label": "All",
         "value": "all",
         "disabled": True
     }]
-
     CL_ALL_ENABLED = [{
         "label": "All",
         "value": "all",
         "disabled": False
     }]
 
     CL_ALL_ENABLED = [{
         "label": "All",
         "value": "all",
         "disabled": False
     }]
 
+    PLACEHOLDER = html.Nobr("")
+
     def __init__(self, app, html_layout_file, spec_file, graph_layout_file,
         data_spec_file):
         """
     def __init__(self, app, html_layout_file, spec_file, graph_layout_file,
         data_spec_file):
         """
@@ -152,6 +155,7 @@ class Layout:
                     ),
                     dcc.Loading(
                         dbc.Offcanvas(
                     ),
                     dcc.Loading(
                         dbc.Offcanvas(
+                            class_name="w-50",
                             id="offcanvas-metadata",
                             title="Throughput And Latency",
                             placement="end",
                             id="offcanvas-metadata",
                             title="Throughput And Latency",
                             placement="end",
@@ -233,31 +237,21 @@ class Layout:
                     id="row-graph-tput",
                     class_name="g-0 p-2",
                     children=[
                     id="row-graph-tput",
                     class_name="g-0 p-2",
                     children=[
-                        dcc.Loading(
-                            dcc.Graph(id="graph-tput")
-                        )
+                        self.PLACEHOLDER
                     ]
                 ),
                 dbc.Row(  # Latency
                     id="row-graph-lat",
                     class_name="g-0 p-2",
                     children=[
                     ]
                 ),
                 dbc.Row(  # Latency
                     id="row-graph-lat",
                     class_name="g-0 p-2",
                     children=[
-                        dcc.Loading(
-                            dcc.Graph(id="graph-latency")
-                        )
+                        self.PLACEHOLDER
                     ]
                 ),
                 dbc.Row(  # Download
                     ]
                 ),
                 dbc.Row(  # Download
-                    id="div-download",
-                    class_name="g-0",
+                    id="row-btn-download",
+                    class_name="g-0 p-2",
                     children=[
                     children=[
-                        dcc.Loading(children=[
-                            dbc.Button(
-                                id="btn-download-data",
-                                children=["Download Data"]
-                            ),
-                            dcc.Download(id="download-data")
-                        ])
+                        self.PLACEHOLDER
                     ]
                 )
             ],
                     ]
                 )
             ],
@@ -428,6 +422,7 @@ class Layout:
                     children=[
                         dcc.DatePickerRange(
                             id="dpr-period",
                     children=[
                         dcc.DatePickerRange(
                             id="dpr-period",
+                            className="d-flex justify-content-center",
                             min_date_allowed=\
                                 datetime.utcnow()-timedelta(days=180),
                             max_date_allowed=datetime.utcnow(),
                             min_date_allowed=\
                                 datetime.utcnow()-timedelta(days=180),
                             max_date_allowed=datetime.utcnow(),
@@ -439,30 +434,29 @@ class Layout:
                     ]
                 ),
                 dbc.Row(
                     ]
                 ),
                 dbc.Row(
+                    id="row-card-sel-tests",
                     class_name="gy-1",
                     class_name="gy-1",
+                    style=self.STYLE_DISABLED,
                     children=[
                     children=[
-                        dbc.Card(
-                            class_name="p-0",
-                            children=[
-                                dbc.Label(
-                                    "Selected tests",
-                                    class_name="p-0"
-                                ),
-                                dbc.Checklist(
-                                    id="cl-selected",
-                                    options=[],
-                                    inline=False
-                                )
-                            ],
-                            color="light",
-                            outline=True
+                        dbc.Label(
+                            "Selected tests",
+                            class_name="p-0"
+                        ),
+                        dbc.Checklist(
+                            class_name="overflow-auto",
+                            id="cl-selected",
+                            options=[],
+                            inline=False,
+                            style={"max-height": "12em"},
                         )
                         )
-                    ]
+                    ],
                 ),
                 dbc.Row(
                 ),
                 dbc.Row(
+                    id="row-btns-sel-tests",
+                    style=self.STYLE_DISABLED,
                     children=[
                         dbc.ButtonGroup(
                     children=[
                         dbc.ButtonGroup(
-                            [
+                            children=[
                                 dbc.Button(
                                     id="btn-sel-remove-all",
                                     children="Remove All",
                                 dbc.Button(
                                     id="btn-sel-remove-all",
                                     children="Remove All",
@@ -477,16 +471,9 @@ class Layout:
                                     color="secondary",
                                     disabled=False
                                 ),
                                     color="secondary",
                                     disabled=False
                                 ),
-                                dbc.Button(
-                                    id="btn-sel-display",
-                                    children="Display",
-                                    class_name="w-100",
-                                    color="secondary",
-                                    disabled=False
-                                )
                             ],
                             size="md",
                             ],
                             size="md",
-                        ),
+                        )
                     ]
                 ),
             ]
                     ]
                 ),
             ]
@@ -576,11 +563,54 @@ class Layout:
 
     def callbacks(self, app):
 
 
     def callbacks(self, app):
 
+        def _generate_plotting_arrea(args: tuple) -> tuple:
+            """
+            """
+
+            (fig_tput, fig_lat) = args
+
+            row_fig_tput = self.PLACEHOLDER
+            row_fig_lat = self.PLACEHOLDER
+            row_btn_dwnld = self.PLACEHOLDER
+
+            if fig_tput:
+                row_fig_tput = [
+                    dcc.Loading(
+                        dcc.Graph(
+                            id={"type": "graph", "index": "tput"},
+                            figure=fig_tput
+                        )
+                    ),
+                ]
+                row_btn_dwnld = [
+                    dcc.Loading(children=[
+                        dbc.Button(
+                            id="btn-download-data",
+                            children=["Download Data"]
+                        ),
+                        dcc.Download(id="download-data")
+                    ]),
+                ]
+            if fig_lat:
+                row_fig_lat = [
+                    dcc.Loading(
+                        dcc.Graph(
+                            id={"type": "graph", "index": "lat"},
+                            figure=fig_lat
+                        )
+                    )
+                ]
+
+            return row_fig_tput, row_fig_lat, row_btn_dwnld
+
         @app.callback(
             Output("control-panel", "data"),  # Store
             Output("selected-tests", "data"),  # Store
         @app.callback(
             Output("control-panel", "data"),  # Store
             Output("selected-tests", "data"),  # Store
-            Output("graph-tput", "figure"),
-            Output("graph-latency", "figure"),
+            Output("row-graph-tput", "children"),
+            Output("row-graph-lat", "children"),
+            Output("row-btn-download", "children"),
+            Output("row-card-sel-tests", "style"),
+            Output("row-btns-sel-tests", "style"),
             Output("dd-ctrl-phy", "value"),
             Output("dd-ctrl-area", "options"),
             Output("dd-ctrl-area", "disabled"),
             Output("dd-ctrl-phy", "value"),
             Output("dd-ctrl-area", "options"),
             Output("dd-ctrl-area", "disabled"),
@@ -617,7 +647,6 @@ class Layout:
             Input("btn-ctrl-add", "n_clicks"),
             Input("dpr-period", "start_date"),
             Input("dpr-period", "end_date"),
             Input("btn-ctrl-add", "n_clicks"),
             Input("dpr-period", "start_date"),
             Input("dpr-period", "end_date"),
-            Input("btn-sel-display", "n_clicks"),
             Input("btn-sel-remove", "n_clicks"),
             Input("btn-sel-remove-all", "n_clicks"),
         )
             Input("btn-sel-remove", "n_clicks"),
             Input("btn-sel-remove-all", "n_clicks"),
         )
@@ -625,7 +654,7 @@ class Layout:
             dd_phy: str, dd_area: str, dd_test: str, cl_core: list,
             cl_core_all: list, cl_framesize: list, cl_framesize_all: list,
             cl_testtype: list, cl_testtype_all: list, btn_add: int,
             dd_phy: str, dd_area: str, dd_test: str, cl_core: list,
             cl_core_all: list, cl_framesize: list, cl_framesize_all: list,
             cl_testtype: list, cl_testtype_all: list, btn_add: int,
-            d_start: str, d_end: str, btn_display: int, btn_remove: int,
+            d_start: str, d_end: str, btn_remove: int,
             btn_remove_all: int) -> tuple:
             """
             """
             btn_remove_all: int) -> tuple:
             """
             """
@@ -634,8 +663,11 @@ class Layout:
                 int(d_start[8:10]))
             d_end = datetime(int(d_end[0:4]), int(d_end[5:7]), int(d_end[8:10]))
 
                 int(d_start[8:10]))
             d_end = datetime(int(d_end[0:4]), int(d_end[5:7]), int(d_end[8:10]))
 
-            fig_tput = no_update
-            fig_lat = no_update
+            row_fig_tput = no_update
+            row_fig_lat = no_update
+            row_btn_dwnld = no_update
+            row_card_sel_tests = no_update
+            row_btns_sel_tests = no_update
 
             ctrl_panel = self.ControlPanel(cp_data)
 
 
             ctrl_panel = self.ControlPanel(cp_data)
 
@@ -836,21 +868,32 @@ class Layout:
                                         "core": core.lower(),
                                         "testtype": ttype.lower()
                                     })
                                         "core": core.lower(),
                                         "testtype": ttype.lower()
                                     })
+                    row_card_sel_tests = self.STYLE_ENABLED
+                    row_btns_sel_tests = self.STYLE_ENABLED
                     ctrl_panel.set(ctrl_panel.defaults)
                     ctrl_panel.set({
                         "cl-selected-options": self._list_tests(store_sel)
                     })
                     ctrl_panel.set(ctrl_panel.defaults)
                     ctrl_panel.set({
                         "cl-selected-options": self._list_tests(store_sel)
                     })
-            elif trigger_id in ("btn-sel-display", "dpr-period"):
-                _ = btn_display
-                fig_tput, fig_lat = graph_trending(
-                    self.data, store_sel, self.layout, d_start, d_end
-                )
-                fig_tput = fig_tput if fig_tput else self.NO_GRAPH
-                fig_lat = fig_lat if fig_lat else self.NO_GRAPH
+                    row_fig_tput, row_fig_lat, row_btn_dwnld = \
+                    _generate_plotting_arrea(
+                        graph_trending(
+                            self.data, store_sel, self.layout, d_start, d_end
+                        )
+                    )
+            elif trigger_id == "dpr-period":
+                row_fig_tput, row_fig_lat, row_btn_dwnld = \
+                    _generate_plotting_arrea(
+                        graph_trending(
+                            self.data, store_sel, self.layout, d_start, d_end
+                        )
+                    )
             elif trigger_id == "btn-sel-remove-all":
                 _ = btn_remove_all
             elif trigger_id == "btn-sel-remove-all":
                 _ = btn_remove_all
-                fig_tput = self.NO_GRAPH
-                fig_lat = self.NO_GRAPH
+                row_fig_tput = self.PLACEHOLDER
+                row_fig_lat = self.PLACEHOLDER
+                row_btn_dwnld = self.PLACEHOLDER
+                row_card_sel_tests = self.STYLE_DISABLED
+                row_btns_sel_tests = self.STYLE_DISABLED
                 store_sel = list()
                 ctrl_panel.set({
                         "cl-selected-options": list()
                 store_sel = list()
                 ctrl_panel.set({
                         "cl-selected-options": list()
@@ -864,23 +907,31 @@ class Layout:
                             new_store_sel.append(item)
                     store_sel = new_store_sel
                 if store_sel:
                             new_store_sel.append(item)
                     store_sel = new_store_sel
                 if store_sel:
-                    fig_tput, fig_lat = graph_trending(
-                        self.data, store_sel, self.layout, d_start, d_end
+                    row_fig_tput, row_fig_lat, row_btn_dwnld = \
+                    _generate_plotting_arrea(
+                        graph_trending(
+                            self.data, store_sel, self.layout, d_start, d_end
+                        )
                     )
                     )
-                    fig_tput = fig_tput if fig_tput else self.NO_GRAPH
-                    fig_lat = fig_lat if fig_lat else self.NO_GRAPH
                     ctrl_panel.set({
                         "cl-selected-options": self._list_tests(store_sel)
                     })
                 else:
                     ctrl_panel.set({
                         "cl-selected-options": self._list_tests(store_sel)
                     })
                 else:
-                    fig_tput = self.NO_GRAPH
-                    fig_lat = self.NO_GRAPH
+                    row_fig_tput = self.PLACEHOLDER
+                    row_fig_lat = self.PLACEHOLDER
+                    row_btn_dwnld = self.PLACEHOLDER
+                    row_card_sel_tests = self.STYLE_DISABLED
+                    row_btns_sel_tests = self.STYLE_DISABLED
                     store_sel = list()
                     ctrl_panel.set({
                             "cl-selected-options": list()
                     })
 
                     store_sel = list()
                     ctrl_panel.set({
                             "cl-selected-options": list()
                     })
 
-            ret_val = [ctrl_panel.panel, store_sel, fig_tput, fig_lat]
+            ret_val = [
+                ctrl_panel.panel, store_sel,
+                row_fig_tput, row_fig_lat, row_btn_dwnld,
+                row_card_sel_tests, row_btns_sel_tests
+            ]
             ret_val.extend(ctrl_panel.values())
             return ret_val
 
             ret_val.extend(ctrl_panel.values())
             return ret_val
 
@@ -888,30 +939,38 @@ class Layout:
             Output("metadata-tput-lat", "children"),
             Output("metadata-hdrh-graph", "children"),
             Output("offcanvas-metadata", "is_open"),
             Output("metadata-tput-lat", "children"),
             Output("metadata-hdrh-graph", "children"),
             Output("offcanvas-metadata", "is_open"),
-            Input("graph-tput", "clickData"),
-            Input("graph-latency", "clickData")
+            Input({"type": "graph", "index": ALL}, "clickData"),
+            prevent_initial_call=True
         )
         )
-        def _show_metadata_from_graphs(
-            tput_data: dict, lat_data: dict) -> tuple:
+        def _show_metadata_from_graphs(graph_data: dict) -> tuple:
             """
             """
             """
             """
-            if not (tput_data or lat_data):
+            try:
+                trigger_id = loads(
+                    callback_context.triggered[0]["prop_id"].split(".")[0]
+                )["index"]
+                idx = 0 if trigger_id == "tput" else 1
+                graph_data = graph_data[idx]["points"][0]
+            except (JSONDecodeError, IndexError, KeyError, ValueError,
+                    TypeError):
                 raise PreventUpdate
 
             metadata = no_update
             graph = list()
 
                 raise PreventUpdate
 
             metadata = no_update
             graph = list()
 
-            trigger_id = callback_context.triggered[0]["prop_id"].split(".")[0]
-            if trigger_id == "graph-tput":
+            children = [
+                dbc.ListGroupItem(
+                    [dbc.Badge(x.split(":")[0]), x.split(": ")[1]]
+                ) for x in graph_data.get("text", "").split("<br>")
+            ]
+            if trigger_id == "tput":
                 title = "Throughput"
                 title = "Throughput"
-                txt = tput_data["points"][0]["text"].replace("<br>", "\n")
-            elif trigger_id == "graph-latency":
+            elif trigger_id == "lat":
                 title = "Latency"
                 title = "Latency"
-                txt = lat_data["points"][0]["text"].replace("<br>", "\n")
-                hdrh_data = lat_data["points"][0].get("customdata", None)
+                hdrh_data = graph_data.get("customdata", None)
                 if hdrh_data:
                     graph = [dbc.Card(
                 if hdrh_data:
                     graph = [dbc.Card(
-                        class_name="g-0",
+                        class_name="gy-2 p-0",
                         children=[
                             dbc.CardHeader(hdrh_data.pop("name")),
                             dbc.CardBody(children=[
                         children=[
                             dbc.CardHeader(hdrh_data.pop("name")),
                             dbc.CardBody(children=[
@@ -926,7 +985,7 @@ class Layout:
                     ]
             metadata = [
                 dbc.Card(
                     ]
             metadata = [
                 dbc.Card(
-                    class_name="g-0",
+                    class_name="gy-2 p-0",
                     children=[
                         dbc.CardHeader(children=[
                             dcc.Clipboard(
                     children=[
                         dbc.CardHeader(children=[
                             dcc.Clipboard(
@@ -938,7 +997,8 @@ class Layout:
                         ]),
                         dbc.CardBody(
                             id="tput-lat-metadata",
                         ]),
                         dbc.CardBody(
                             id="tput-lat-metadata",
-                            children=[txt]
+                            class_name="p-0",
+                            children=[dbc.ListGroup(children, flush=True), ]
                         )
                     ]
                 )
                         )
                     ]
                 )
@@ -959,6 +1019,9 @@ class Layout:
             if not n_clicks:
                 raise PreventUpdate
 
             if not n_clicks:
                 raise PreventUpdate
 
+            if not store_sel:
+                raise PreventUpdate
+
             df = pd.DataFrame()
             for itm in store_sel:
                 sel_data = select_trending_data(self.data, itm)
             df = pd.DataFrame()
             for itm in store_sel:
                 sel_data = select_trending_data(self.data, itm)