C-Dash: Add search in tests
[csit.git] / csit.infra.dash / app / cdash / trending / layout.py
index 409038a..f6f96d7 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 Cisco and/or its affiliates.
+# Copyright (c) 2024 Cisco and/or its affiliates.
 # 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:
@@ -34,7 +34,8 @@ from ..utils.control_panel import ControlPanel
 from ..utils.trigger import Trigger
 from ..utils.telemetry_data import TelemetryData
 from ..utils.utils import show_tooltip, label, sync_checklists, gen_new_url, \
-    generate_options, get_list_group_items, graph_hdrh_latency
+    generate_options, get_list_group_items, navbar_trending, \
+    show_trending_graph_data
 from ..utils.url_processing import url_decode
 from .graphs import graph_trending, select_trending_data, graph_tm_trending
 
@@ -142,39 +143,34 @@ class Layout:
 
             if tbs.get(dut, None) is None:
                 tbs[dut] = dict()
-            if tbs[dut].get(infra, None) is None:
-                tbs[dut][infra] = dict()
-            if tbs[dut][infra].get(area, None) is None:
-                tbs[dut][infra][area] = dict()
-            if tbs[dut][infra][area].get(test, None) is None:
-                tbs[dut][infra][area][test] = dict()
-                tbs[dut][infra][area][test]["core"] = list()
-                tbs[dut][infra][area][test]["frame-size"] = list()
-                tbs[dut][infra][area][test]["test-type"] = list()
-            if core.upper() not in tbs[dut][infra][area][test]["core"]:
-                tbs[dut][infra][area][test]["core"].append(core.upper())
-            if framesize.upper() not in \
-                    tbs[dut][infra][area][test]["frame-size"]:
-                tbs[dut][infra][area][test]["frame-size"].append(
-                    framesize.upper()
-                )
+            if tbs[dut].get(area, None) is None:
+                tbs[dut][area] = dict()
+            if tbs[dut][area].get(test, None) is None:
+                tbs[dut][area][test] = dict()
+            if tbs[dut][area][test].get(infra, None) is None:
+                tbs[dut][area][test][infra] = {
+                    "core": list(),
+                    "frame-size": list(),
+                    "test-type": list()
+                }
+            tst_params = tbs[dut][area][test][infra]
+            if core.upper() not in tst_params["core"]:
+                tst_params["core"].append(core.upper())
+            if framesize.upper() not in tst_params["frame-size"]:
+                tst_params["frame-size"].append(framesize.upper())
             if row["test_type"] == "mrr":
-                if "MRR" not in tbs[dut][infra][area][test]["test-type"]:
-                    tbs[dut][infra][area][test]["test-type"].append("MRR")
+                if "MRR" not in tst_params["test-type"]:
+                    tst_params["test-type"].append("MRR")
             elif row["test_type"] == "ndrpdr":
-                if "NDR" not in tbs[dut][infra][area][test]["test-type"]:
-                    tbs[dut][infra][area][test]["test-type"].extend(
-                        ("NDR", "PDR")
-                    )
+                if "NDR" not in tst_params["test-type"]:
+                    tst_params["test-type"].extend(("NDR", "PDR"))
             elif row["test_type"] == "hoststack":
                 if row["tg_type"] in ("iperf", "vpp"):
-                    if "BPS" not in tbs[dut][infra][area][test]["test-type"]:
-                        tbs[dut][infra][area][test]["test-type"].append("BPS")
+                    if "BPS" not in tst_params["test-type"]:
+                        tst_params["test-type"].append("BPS")
                 elif row["tg_type"] == "ab":
-                    if "CPS" not in tbs[dut][infra][area][test]["test-type"]:
-                        tbs[dut][infra][area][test]["test-type"].extend(
-                            ("CPS", "RPS")
-                        )
+                    if "CPS" not in tst_params["test-type"]:
+                        tst_params["test-type"].extend(("CPS", "RPS"))
         self._spec_tbs = tbs
 
         # Read from files:
@@ -249,9 +245,7 @@ class Layout:
                     dbc.Row(
                         id="row-navbar",
                         class_name="g-0",
-                        children=[
-                            self._add_navbar()
-                        ]
+                        children=[navbar_trending((True, False, False, False))]
                     ),
                     dbc.Row(
                         id="row-main",
@@ -265,7 +259,7 @@ class Layout:
                         dbc.Offcanvas(
                             class_name="w-50",
                             id="offcanvas-metadata",
-                            title="Throughput And Latency",
+                            title="Detailed Information",
                             placement="end",
                             is_open=False,
                             children=[
@@ -274,6 +268,18 @@ class Layout:
                             ]
                         ),
                         delay_show=C.SPINNER_DELAY
+                    ),
+                    dbc.Offcanvas(
+                        class_name="w-75",
+                        id="offcanvas-documentation",
+                        title="Documentation",
+                        placement="end",
+                        is_open=False,
+                        children=html.Iframe(
+                            src=C.URL_DOC_TRENDING,
+                            width="100%",
+                            height="100%"
+                        )
                     )
                 ]
             )
@@ -283,29 +289,6 @@ class Layout:
                 id="div-main-error"
             )
 
-    def _add_navbar(self):
-        """Add nav element with navigation panel. It is placed on the top.
-
-        :returns: Navigation bar.
-        :rtype: dbc.NavbarSimple
-        """
-        return dbc.NavbarSimple(
-            dbc.NavItem(
-                dbc.NavLink(
-                    C.TREND_TITLE,
-                    disabled=True,
-                    external_link=True,
-                    href="#"
-                )
-            ),
-            id="navbarsimple-main",
-            brand=C.BRAND,
-            brand_href="/",
-            brand_external_link=True,
-            class_name="p-2",
-            fluid=True
-        )
-
     def _add_ctrl_col(self) -> dbc.Col:
         """Add column with controls. It is placed on the left side.
 
@@ -347,11 +330,11 @@ class Layout:
                 dbc.InputGroup(
                     [
                         dbc.InputGroupText(
-                            show_tooltip(self._tooltips, "help-infra", "Infra")
+                            show_tooltip(self._tooltips, "help-area", "Area")
                         ),
                         dbc.Select(
-                            id={"type": "ctrl-dd", "index": "phy"},
-                            placeholder="Select a Physical Test Bed Topology..."
+                            id={"type": "ctrl-dd", "index": "area"},
+                            placeholder="Select an Area..."
                         )
                     ],
                     size="sm"
@@ -362,11 +345,11 @@ class Layout:
                 dbc.InputGroup(
                     [
                         dbc.InputGroupText(
-                            show_tooltip(self._tooltips, "help-area", "Area")
+                            show_tooltip(self._tooltips, "help-test", "Test")
                         ),
                         dbc.Select(
-                            id={"type": "ctrl-dd", "index": "area"},
-                            placeholder="Select an Area..."
+                            id={"type": "ctrl-dd", "index": "test"},
+                            placeholder="Select a Test..."
                         )
                     ],
                     size="sm"
@@ -377,11 +360,11 @@ class Layout:
                 dbc.InputGroup(
                     [
                         dbc.InputGroupText(
-                            show_tooltip(self._tooltips, "help-test", "Test")
+                            show_tooltip(self._tooltips, "help-infra", "Infra")
                         ),
                         dbc.Select(
-                            id={"type": "ctrl-dd", "index": "test"},
-                            placeholder="Select a Test..."
+                            id={"type": "ctrl-dd", "index": "phy"},
+                            placeholder="Select a Physical Test Bed Topology..."
                         )
                     ],
                     size="sm"
@@ -620,9 +603,21 @@ class Layout:
             tab_items.append(
                 dbc.Tab(
                     children=dcc.Graph(
-                        id={"type": "graph", "index": "lat"},
+                        id={"type": "graph", "index": "bandwidth"},
                         figure=graphs[1]
                     ),
+                    label="Bandwidth",
+                    tab_id="tab-bandwidth"
+                )
+            )
+
+        if graphs[2]:
+            tab_items.append(
+                dbc.Tab(
+                    children=dcc.Graph(
+                        id={"type": "graph", "index": "lat"},
+                        figure=graphs[2]
+                    ),
                     label="Latency",
                     tab_id="tab-lat"
                 )
@@ -1073,29 +1068,28 @@ class Layout:
                     pass
                 if store_sel:
                     last_test = store_sel[-1]
-                    test = self._spec_tbs[last_test["dut"]][last_test["phy"]]\
-                        [last_test["area"]][last_test["test"]]
+                    test = self._spec_tbs[last_test["dut"]]\
+                        [last_test["area"]][last_test["test"]][last_test["phy"]]
                     ctrl_panel.set({
                         "dd-dut-val": last_test["dut"],
-                        "dd-phy-val": last_test["phy"],
-                        "dd-phy-opt": generate_options(
-                            self._spec_tbs[last_test["dut"]].keys()
-                        ),
-                        "dd-phy-dis": False,
                         "dd-area-val": last_test["area"],
                         "dd-area-opt": [
                             {"label": label(v), "value": v} for v in sorted(
-                                self._spec_tbs[last_test["dut"]]\
-                                    [last_test["phy"]].keys()
-                            )
+                                self._spec_tbs[last_test["dut"]].keys())
                         ],
                         "dd-area-dis": False,
                         "dd-test-val": last_test["test"],
                         "dd-test-opt": generate_options(
-                            self._spec_tbs[last_test["dut"]][last_test["phy"]]\
+                            self._spec_tbs[last_test["dut"]]\
                                 [last_test["area"]].keys()
                         ),
                         "dd-test-dis": False,
+                        "dd-phy-val": last_test["phy"],
+                        "dd-phy-opt": generate_options(
+                            self._spec_tbs[last_test["dut"]][last_test["area"]]\
+                                [last_test["test"]].keys()
+                        ),
+                        "dd-phy-dis": False,
                         "cl-core-opt": generate_options(test["core"]),
                         "cl-core-val": [last_test["core"].upper(), ],
                         "cl-core-all-val": list(),
@@ -1128,24 +1122,24 @@ class Layout:
             elif trigger.type == "ctrl-dd":
                 if trigger.idx == "dut":
                     try:
-                        options = generate_options(
-                            self._spec_tbs[trigger.value].keys()
-                        )
+                        dut = self._spec_tbs[trigger.value]
+                        options = [{"label": label(v), "value": v} \
+                            for v in sorted(dut.keys())]
                         disabled = False
                     except KeyError:
                         options = list()
                         disabled = True
                     ctrl_panel.set({
                         "dd-dut-val": trigger.value,
-                        "dd-phy-val": str(),
-                        "dd-phy-opt": options,
-                        "dd-phy-dis": disabled,
                         "dd-area-val": str(),
-                        "dd-area-opt": list(),
-                        "dd-area-dis": True,
+                        "dd-area-opt": options,
+                        "dd-area-dis": disabled,
                         "dd-test-val": str(),
                         "dd-test-opt": list(),
                         "dd-test-dis": True,
+                        "dd-phy-val": str(),
+                        "dd-phy-opt": list(),
+                        "dd-phy-dis": True,
                         "cl-core-opt": list(),
                         "cl-core-val": list(),
                         "cl-core-all-val": list(),
@@ -1160,24 +1154,23 @@ class Layout:
                         "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
                         "btn-add-dis": True
                     })
-                elif trigger.idx == "phy":
+                if trigger.idx == "area":
                     try:
                         dut = ctrl_panel.get("dd-dut-val")
-                        phy = self._spec_tbs[dut][trigger.value]
-                        options = [{"label": label(v), "value": v} \
-                            for v in sorted(phy.keys())]
+                        area = self._spec_tbs[dut][trigger.value]
+                        options = generate_options(area.keys())
                         disabled = False
                     except KeyError:
                         options = list()
                         disabled = True
                     ctrl_panel.set({
-                        "dd-phy-val": trigger.value,
-                        "dd-area-val": str(),
-                        "dd-area-opt": options,
-                        "dd-area-dis": disabled,
+                        "dd-area-val": trigger.value,
                         "dd-test-val": str(),
-                        "dd-test-opt": list(),
-                        "dd-test-dis": True,
+                        "dd-test-opt": options,
+                        "dd-test-dis": disabled,
+                        "dd-phy-val": str(),
+                        "dd-phy-opt": list(),
+                        "dd-phy-dis": True,
                         "cl-core-opt": list(),
                         "cl-core-val": list(),
                         "cl-core-all-val": list(),
@@ -1191,22 +1184,22 @@ class Layout:
                         "cl-tsttype-all-val": list(),
                         "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
                         "btn-add-dis": True
-                    })  
-                elif trigger.idx == "area":
+                    })
+                if trigger.idx == "test":
                     try:
                         dut = ctrl_panel.get("dd-dut-val")
-                        phy = ctrl_panel.get("dd-phy-val")
-                        area = self._spec_tbs[dut][phy][trigger.value]
-                        options = generate_options(area.keys())
+                        area = ctrl_panel.get("dd-area-val")
+                        test = self._spec_tbs[dut][area][trigger.value]
+                        options = generate_options(test.keys())
                         disabled = False
                     except KeyError:
                         options = list()
                         disabled = True
                     ctrl_panel.set({
-                        "dd-area-val": trigger.value,
-                        "dd-test-val": str(),
-                        "dd-test-opt": options,
-                        "dd-test-dis": disabled,
+                        "dd-test-val": trigger.value,
+                        "dd-phy-val": str(),
+                        "dd-phy-opt": options,
+                        "dd-phy-dis": disabled,
                         "cl-core-opt": list(),
                         "cl-core-val": list(),
                         "cl-core-all-val": list(),
@@ -1221,25 +1214,25 @@ class Layout:
                         "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
                         "btn-add-dis": True
                     })
-                elif trigger.idx == "test":
+                if trigger.idx == "phy":
                     dut = ctrl_panel.get("dd-dut-val")
-                    phy = ctrl_panel.get("dd-phy-val")
                     area = ctrl_panel.get("dd-area-val")
-                    if all((dut, phy, area, trigger.value, )):
-                        test = self._spec_tbs[dut][phy][area][trigger.value]
+                    test = ctrl_panel.get("dd-test-val")
+                    if all((dut, area, test, trigger.value, )):
+                        phy = self._spec_tbs[dut][area][test][trigger.value]
                         ctrl_panel.set({
-                            "dd-test-val": trigger.value,
-                            "cl-core-opt": generate_options(test["core"]),
+                            "dd-phy-val": trigger.value,
+                            "cl-core-opt": generate_options(phy["core"]),
                             "cl-core-val": list(),
                             "cl-core-all-val": list(),
                             "cl-core-all-opt": C.CL_ALL_ENABLED,
                             "cl-frmsize-opt": \
-                                generate_options(test["frame-size"]),
+                                generate_options(phy["frame-size"]),
                             "cl-frmsize-val": list(),
                             "cl-frmsize-all-val": list(),
                             "cl-frmsize-all-opt": C.CL_ALL_ENABLED,
                             "cl-tsttype-opt": \
-                                generate_options(test["test-type"]),
+                                generate_options(phy["test-type"]),
                             "cl-tsttype-val": list(),
                             "cl-tsttype-all-val": list(),
                             "cl-tsttype-all-opt": C.CL_ALL_ENABLED,
@@ -1273,7 +1266,6 @@ class Layout:
                 tm_ignore_host = list()
                 store["trending-graphs"] = None
                 store["telemetry-graphs"] = list()
-                # on_draw[0] = True
                 on_draw = [True, True]
                 if trigger.idx == "add-test":
                     dut = ctrl_panel.get("dd-dut-val")
@@ -1662,66 +1654,11 @@ class Layout:
             """
 
             trigger = Trigger(callback_context.triggered)
-
-            try:
-                idx = 0 if trigger.idx == "tput" else 1
-                graph_data = graph_data[idx]["points"][0]
-            except (IndexError, KeyError, ValueError, TypeError):
-                raise PreventUpdate
-
-            metadata = no_update
-            graph = list()
-
-            children = [
-                dbc.ListGroupItem(
-                    [dbc.Badge(x.split(":")[0]), x.split(": ")[1]]
-                ) for x in graph_data.get("text", "").split("<br>")
-            ]
-            if trigger.idx == "tput":
-                title = "Throughput"
-            elif trigger.idx == "lat":
-                title = "Latency"
-                hdrh_data = graph_data.get("customdata", None)
-                if hdrh_data:
-                    graph = [dbc.Card(
-                        class_name="gy-2 p-0",
-                        children=[
-                            dbc.CardHeader(hdrh_data.pop("name")),
-                            dbc.CardBody(children=[
-                                dcc.Graph(
-                                    id="hdrh-latency-graph",
-                                    figure=graph_hdrh_latency(
-                                        hdrh_data, self._graph_layout
-                                    )
-                                )
-                            ])
-                        ])
-                    ]
-            else:
+            if not trigger.value:
                 raise PreventUpdate
-
-            metadata = [
-                dbc.Card(
-                    class_name="gy-2 p-0",
-                    children=[
-                        dbc.CardHeader(children=[
-                            dcc.Clipboard(
-                                target_id="tput-lat-metadata",
-                                title="Copy",
-                                style={"display": "inline-block"}
-                            ),
-                            title
-                        ]),
-                        dbc.CardBody(
-                            id="tput-lat-metadata",
-                            class_name="p-0",
-                            children=[dbc.ListGroup(children, flush=True), ]
-                        )
-                    ]
-                )
-            ]
-
-            return metadata, graph, True
+            
+            return show_trending_graph_data(
+                    trigger, graph_data, self._graph_layout)
 
         @app.callback(
             Output("download-trending-data", "data"),
@@ -1772,3 +1709,13 @@ class Layout:
                 raise PreventUpdate
 
             return dcc.send_data_frame(df.to_csv, file_name)
+
+        @app.callback(
+            Output("offcanvas-documentation", "is_open"),
+            Input("btn-documentation", "n_clicks"),
+            State("offcanvas-documentation", "is_open")
+        )
+        def toggle_offcanvas_documentation(n_clicks, is_open):
+            if n_clicks:
+                return not is_open
+            return is_open