feat(core): Adjust T-Rex for external topologies
[csit.git] / csit.infra.dash / app / cdash / report / layout.py
index 765af4e..2d325b0 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:
@@ -14,6 +14,7 @@
 """Plotly Dash HTML layout override.
 """
 
+
 import logging
 import pandas as pd
 import dash_bootstrap_components as dbc
@@ -31,7 +32,8 @@ from ..utils.constants import Constants as C
 from ..utils.control_panel import ControlPanel
 from ..utils.trigger import Trigger
 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_report, \
+    show_iterative_graph_data
 from ..utils.url_processing import url_decode
 from .graphs import graph_iterative, select_iterative_data
 
@@ -164,10 +166,7 @@ class Layout:
                 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 tst_params["test-type"]:
-                    tst_params["test-type"].append("MRR")
-            elif row["test_type"] == "ndrpdr":
+            if row["test_type"] == "ndrpdr":
                 if "NDR" not in tst_params["test-type"]:
                     tst_params["test-type"].extend(("NDR", "PDR", ))
             elif row["test_type"] == "hoststack" and \
@@ -177,6 +176,9 @@ class Layout:
             elif row["test_type"] == "hoststack" and row["tg_type"] == "ab":
                 if "CPS" not in tst_params["test-type"]:
                     tst_params["test-type"].extend(("CPS", "RPS"))
+            else:  # MRR, SOAK
+                if row["test_type"].upper() not in tst_params["test-type"]:
+                    tst_params["test-type"].append(row["test_type"].upper())
         self._spec_tbs = tbs
 
         # Read from files:
@@ -249,9 +251,7 @@ class Layout:
                     dbc.Row(
                         id="row-navbar",
                         class_name="g-0",
-                        children=[
-                            self._add_navbar()
-                        ]
+                        children=[navbar_report((True, False, False, False)), ]
                     ),
                     dbc.Row(
                         id="row-main",
@@ -305,43 +305,6 @@ class Layout:
                 ]
             )
 
-    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(
-            id="navbarsimple-main",
-            children=[
-                dbc.NavItem(dbc.NavLink(
-                    C.REPORT_TITLE,
-                    active=True,
-                    external_link=True,
-                    href="/report"
-                )),
-                dbc.NavItem(dbc.NavLink(
-                    "Comparisons",
-                    external_link=True,
-                    href="/comparisons"
-                )),
-                dbc.NavItem(dbc.NavLink(
-                    "Coverage Data",
-                    external_link=True,
-                    href="/coverage"
-                )),
-                dbc.NavItem(dbc.NavLink(
-                    "Documentation",
-                    id="btn-documentation",
-                ))
-            ],
-            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.
 
@@ -385,19 +348,17 @@ class Layout:
         :returns: Control panel.
         :rtype: list
         """
-        return [
+        test_selection = [
             dbc.Row(
                 class_name="g-0 p-1",
                 children=[
                     dbc.InputGroup(
                         [
-                            dbc.InputGroupText(
-                                children=show_tooltip(
-                                    self._tooltips,
-                                    "help-release",
-                                    "CSIT Release"
-                                )
-                            ),
+                            dbc.InputGroupText(show_tooltip(
+                                self._tooltips,
+                                "help-release",
+                                "CSIT Release"
+                            )),
                             dbc.Select(
                                 id={"type": "ctrl-dd", "index": "rls"},
                                 placeholder="Select a Release...",
@@ -419,13 +380,11 @@ class Layout:
                 children=[
                     dbc.InputGroup(
                         [
-                            dbc.InputGroupText(
-                                children=show_tooltip(
-                                    self._tooltips,
-                                    "help-dut",
-                                    "DUT"
-                                )
-                            ),
+                            dbc.InputGroupText(show_tooltip(
+                                self._tooltips,
+                                "help-dut",
+                                "DUT"
+                            )),
                             dbc.Select(
                                 id={"type": "ctrl-dd", "index": "dut"},
                                 placeholder="Select a Device under Test..."
@@ -440,13 +399,11 @@ class Layout:
                 children=[
                     dbc.InputGroup(
                         [
-                            dbc.InputGroupText(
-                                children=show_tooltip(
-                                    self._tooltips,
-                                    "help-dut-ver",
-                                    "DUT Version"
-                                )
-                            ),
+                            dbc.InputGroupText(show_tooltip(
+                                self._tooltips,
+                                "help-dut-ver",
+                                "DUT Version"
+                            )),
                             dbc.Select(
                                 id={"type": "ctrl-dd", "index": "dutver"},
                                 placeholder=\
@@ -462,13 +419,11 @@ class Layout:
                 children=[
                     dbc.InputGroup(
                         [
-                            dbc.InputGroupText(
-                                children=show_tooltip(
-                                    self._tooltips,
-                                    "help-area",
-                                    "Area"
-                                )
-                            ),
+                            dbc.InputGroupText(show_tooltip(
+                                self._tooltips,
+                                "help-area",
+                                "Area"
+                            )),
                             dbc.Select(
                                 id={"type": "ctrl-dd", "index": "area"},
                                 placeholder="Select an Area..."
@@ -483,13 +438,11 @@ class Layout:
                 children=[
                     dbc.InputGroup(
                         [
-                            dbc.InputGroupText(
-                                children=show_tooltip(
-                                    self._tooltips,
-                                    "help-test",
-                                    "Test"
-                                )
-                            ),
+                            dbc.InputGroupText(show_tooltip(
+                                self._tooltips,
+                                "help-test",
+                                "Test"
+                            )),
                             dbc.Select(
                                 id={"type": "ctrl-dd", "index": "test"},
                                 placeholder="Select a Test..."
@@ -504,13 +457,11 @@ class Layout:
                 children=[
                     dbc.InputGroup(
                         [
-                            dbc.InputGroupText(
-                                children=show_tooltip(
-                                    self._tooltips,
-                                    "help-infra",
-                                    "Infra"
-                                )
-                            ),
+                            dbc.InputGroupText(show_tooltip(
+                                self._tooltips,
+                                "help-infra",
+                                "Infra"
+                            )),
                             dbc.Select(
                                 id={"type": "ctrl-dd", "index": "phy"},
                                 placeholder=\
@@ -653,52 +604,36 @@ class Layout:
             dbc.Row(
                 class_name="g-0 p-1",
                 children=[
-                    dbc.InputGroup(
-                        [
-                            dbc.InputGroupText(
-                                children=show_tooltip(
-                                    self._tooltips,
-                                    "help-normalize",
-                                    "Normalization"
-                                )
-                            ),
-                            dbc.Col(
-                                children=[
-                                    dbc.Checklist(
-                                        id="normalize",
-                                        options=[{
-                                            "value": "normalize",
-                                            "label": (
-                                                "Normalize to CPU frequency "
-                                                "2GHz"
-                                            )
-                                        }],
-                                        value=[],
-                                        inline=True,
-                                        class_name="ms-2"
-                                    )
-                                ]
-                            )
-                        ],
-                        style={"align-items": "center"},
-                        size="sm"
+                    dbc.Button(
+                        id={"type": "ctrl-btn", "index": "add-test"},
+                        children="Add Selected",
+                        color="info",
+                        class_name="p-1"
                     )
                 ]
-            ),
+            )
+        ]
+        processing = [
             dbc.Row(
                 class_name="g-0 p-1",
                 children=[
-                    dbc.Button(
-                        id={"type": "ctrl-btn", "index": "add-test"},
-                        children="Add Selected",
-                        color="info"
+                    dbc.Checklist(
+                        id="normalize",
+                        options=[{
+                            "value": "normalize",
+                            "label": "Normalize to 2GHz CPU frequency"
+                        }],
+                        value=[],
+                        inline=True,
+                        class_name="ms-2"
                     )
                 ]
-            ),
+            )
+        ]
+        test_list = [
             dbc.Row(
                 id="row-card-sel-tests",
                 class_name="g-0 p-1",
-                style=C.STYLE_DISABLED,
                 children=[
                     dbc.ListGroup(
                         class_name="overflow-auto p-0",
@@ -709,33 +644,100 @@ class Layout:
                     )
                 ]
             ),
-            dbc.Row(
+            dbc.Stack(
                 id="row-btns-sel-tests",
                 class_name="g-0 p-1",
-                style=C.STYLE_DISABLED,
+                gap=2,
                 children=[
-                    dbc.ButtonGroup(
-                        children=[
-                            dbc.Button(
-                                id={"type": "ctrl-btn", "index": "rm-test"},
-                                children="Remove Selected",
-                                class_name="w-100",
-                                color="info",
-                                disabled=False
-                            ),
-                            dbc.Button(
-                                id={"type": "ctrl-btn", "index": "rm-test-all"},
-                                children="Remove All",
-                                class_name="w-100",
-                                color="info",
-                                disabled=False
-                            )
-                        ]
-                    )
+                    dbc.ButtonGroup(children=[
+                        dbc.Button(
+                            id={"type": "ctrl-btn", "index": "rm-test"},
+                            children="Remove Selected",
+                            class_name="w-100 p-1",
+                            color="info",
+                            disabled=False
+                        ),
+                        dbc.Button(
+                            id={"type": "ctrl-btn", "index": "rm-test-all"},
+                            children="Remove All",
+                            class_name="w-100 p-1",
+                            color="info",
+                            disabled=False
+                        )
+                    ]),
+                    dbc.ButtonGroup(children=[
+                        dbc.Button(
+                            id="plot-btn-url",
+                            children="Show URL",
+                            class_name="w-100 p-1",
+                            color="info",
+                            disabled=False
+                        ),
+                        dbc.Button(
+                            id="plot-btn-download",
+                            children="Download Data",
+                            class_name="w-100 p-1",
+                            color="info",
+                            disabled=False
+                        )
+                    ])
                 ]
             )
         ]
 
+        return [
+            dbc.Row(
+                dbc.Card(
+                    [
+                        dbc.CardHeader(
+                            html.H5("Test Selection")
+                        ),
+                        dbc.CardBody(
+                            children=test_selection,
+                            class_name="g-0 p-0"
+                        )
+                    ],
+                    color="secondary",
+                    outline=True
+                ),
+                class_name="g-0 p-1"
+            ),
+            dbc.Row(
+                dbc.Card(
+                    [
+                        dbc.CardHeader(
+                            html.H5("Data Manipulations")
+                        ),
+                        dbc.CardBody(
+                            children=processing,
+                            class_name="g-0 p-0"
+                        )
+                    ],
+                    color="secondary",
+                    outline=True
+                ),
+                class_name="g-0 p-1"
+            ),
+            dbc.Row(
+                dbc.Card(
+                    [
+                        dbc.CardHeader(
+                            html.H5("Selected Tests")
+                        ),
+                        dbc.CardBody(
+                            children=test_list,
+                            class_name="g-0 p-0"
+                        )
+                    ],
+                    color="secondary",
+                    outline=True
+                ),
+                id = "row-selected-tests",
+                class_name="g-0 p-1",
+                style=C.STYLE_DISABLED,
+            )
+        ]
+
     def _get_plotting_area(
             self,
             tests: list,
@@ -757,81 +759,67 @@ class Layout:
         if not tests:
             return C.PLACEHOLDER
 
-        figs = graph_iterative(self._data, tests, self._graph_layout, normalize)
+        graphs = \
+            graph_iterative(self._data, tests, self._graph_layout, normalize)
 
-        if not figs[0]:
+        if not graphs[0]:
             return C.PLACEHOLDER
-
-        row_items = [
-            dbc.Col(
+        
+        tab_items = [
+            dbc.Tab(
                 children=dcc.Graph(
                     id={"type": "graph", "index": "tput"},
-                    figure=figs[0]
+                    figure=graphs[0]
                 ),
-                class_name="g-0 p-1",
-                width=6
+                label="Throughput",
+                tab_id="tab-tput"
             )
         ]
 
-        if figs[1]:
-            row_items.append(
-                dbc.Col(
+        if graphs[1]:
+            tab_items.append(
+                dbc.Tab(
+                    children=dcc.Graph(
+                        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=figs[1]
+                        figure=graphs[2]
                     ),
-                    class_name="g-0 p-1",
-                    width=6
+                    label="Latency",
+                    tab_id="tab-lat"
                 )
             )
 
         return [
             dbc.Row(
-                children=row_items,
-                class_name="g-0 p-0",
+                dbc.Tabs(
+                    children=tab_items,
+                    id="tabs",
+                    active_tab="tab-tput",
+                ),
+                class_name="g-0 p-0"
             ),
-            dbc.Row(
+            dbc.Modal(
                 [
-                    dbc.Col([html.Div(
-                        [
-                            dbc.Button(
-                                id="plot-btn-url",
-                                children="Show URL",
-                                class_name="me-1",
-                                color="info",
-                                style={
-                                    "text-transform": "none",
-                                    "padding": "0rem 1rem"
-                                }
-                            ),
-                            dbc.Modal(
-                                [
-                                    dbc.ModalHeader(dbc.ModalTitle("URL")),
-                                    dbc.ModalBody(url)
-                                ],
-                                id="plot-mod-url",
-                                size="xl",
-                                is_open=False,
-                                scrollable=True
-                            ),
-                            dbc.Button(
-                                id="plot-btn-download",
-                                children="Download Data",
-                                class_name="me-1",
-                                color="info",
-                                style={
-                                    "text-transform": "none",
-                                    "padding": "0rem 1rem"
-                                }
-                            ),
-                            dcc.Download(id="download-iterative-data")
-                        ],
-                        className=\
-                            "d-grid gap-0 d-md-flex justify-content-md-end"
-                    )])
+                    dbc.ModalHeader(dbc.ModalTitle("URL")),
+                    dbc.ModalBody(url)
                 ],
-                class_name="g-0 p-0"
-            )
+                id="plot-mod-url",
+                size="xl",
+                is_open=False,
+                scrollable=True
+            ),
+            dcc.Download(id="download-iterative-data")
         ]
 
     def callbacks(self, app):
@@ -846,8 +834,7 @@ class Layout:
                 Output("store-control-panel", "data"),
                 Output("store-selected-tests", "data"),
                 Output("plotting-area", "children"),
-                Output("row-card-sel-tests", "style"),
-                Output("row-btns-sel-tests", "style"),
+                Output("row-selected-tests", "style"),
                 Output("lg-selected", "children"),
 
                 Output({"type": "ctrl-dd", "index": "rls"}, "value"),
@@ -917,8 +904,7 @@ class Layout:
                 url_params = None
 
             plotting_area = no_update
-            row_card_sel_tests = no_update
-            row_btns_sel_tests = no_update
+            row_sel_tests = no_update
             lg_selected = no_update
 
             trigger = Trigger(callback_context.triggered)
@@ -930,8 +916,7 @@ class Layout:
                 except (KeyError, IndexError, AttributeError):
                     pass
                 if store_sel:
-                    row_card_sel_tests = C.STYLE_ENABLED
-                    row_btns_sel_tests = C.STYLE_ENABLED
+                    row_sel_tests = C.STYLE_ENABLED
                     last_test = store_sel[-1]
                     test = self._spec_tbs[last_test["rls"]][last_test["dut"]]\
                         [last_test["dutver"]][last_test["area"]]\
@@ -1283,20 +1268,17 @@ class Layout:
                             {"store_sel": store_sel, "norm": normalize}
                         )
                     )
-                    row_card_sel_tests = C.STYLE_ENABLED
-                    row_btns_sel_tests = C.STYLE_ENABLED
+                    row_sel_tests = C.STYLE_ENABLED
                 else:
                     plotting_area = C.PLACEHOLDER
-                    row_card_sel_tests = C.STYLE_DISABLED
-                    row_btns_sel_tests = C.STYLE_DISABLED
+                    row_sel_tests = C.STYLE_DISABLED
                     store_sel = list()
 
             ret_val = [
                 ctrl_panel.panel,
                 store_sel,
                 plotting_area,
-                row_card_sel_tests,
-                row_btns_sel_tests,
+                row_sel_tests,
                 lg_selected
             ]
             ret_val.extend(ctrl_panel.values)
@@ -1304,15 +1286,16 @@ class Layout:
 
         @app.callback(
             Output("plot-mod-url", "is_open"),
-            [Input("plot-btn-url", "n_clicks")],
-            [State("plot-mod-url", "is_open")],
+            Output("plot-btn-url", "n_clicks"),
+            Input("plot-btn-url", "n_clicks"),
+            State("plot-mod-url", "is_open")
         )
         def toggle_plot_mod_url(n, is_open):
             """Toggle the modal window with url.
             """
             if n:
-                return not is_open
-            return is_open
+                return not is_open, 0
+            return is_open, 0
 
         @app.callback(
             Output("download-iterative-data", "data"),
@@ -1362,100 +1345,11 @@ class Layout:
             """
 
             trigger = Trigger(callback_context.triggered)
-
-            try:
-                idx = 0 if trigger.idx == "tput" else 1
-                graph_data = graph_data[idx]["points"]
-            except (IndexError, KeyError, ValueError, TypeError):
+            if not trigger.value:
                 raise PreventUpdate
 
-            def _process_stats(data: list, param: str) -> list:
-                """Process statistical data provided by plot.ly box graph.
-
-                :param data: Statistical data provided by plot.ly box graph.
-                :param param: Parameter saying if the data come from "tput" or
-                    "lat" graph.
-                :type data: list
-                :type param: str
-                :returns: Listo of tuples where the first value is the
-                    statistic's name and the secont one it's value.
-                :rtype: list
-                """
-                if len(data) == 7:
-                    stats = ("max", "upper fence", "q3", "median", "q1",
-                            "lower fence", "min")
-                elif len(data) == 9:
-                    stats = ("outlier", "max", "upper fence", "q3", "median",
-                            "q1", "lower fence", "min", "outlier")
-                elif len(data) == 1:
-                    if param == "lat":
-                        stats = ("Average Latency at 50% PDR", )
-                    else:
-                        stats = ("Throughput", )
-                else:
-                    return list()
-                unit = " [us]" if param == "lat" else str()
-                return [(f"{stat}{unit}", f"{value['y']:,.0f}")
-                        for stat, value in zip(stats, data)]
-
-            graph = list()
-            if trigger.idx == "tput":
-                title = "Throughput"
-            elif trigger.idx == "lat":
-                title = "Latency"
-                if len(graph_data) == 1:
-                    hdrh_data = graph_data[0].get("customdata", None)
-                    if hdrh_data:
-                        name = hdrh_data.pop("name")
-                        graph = [dbc.Card(
-                            class_name="gy-2 p-0",
-                            children=[
-                                dbc.CardHeader(html.A(
-                                    name,
-                                    href=f"{C.URL_JENKINS}{name}",
-                                    target="_blank"
-                                )),
-                                dbc.CardBody(dcc.Graph(
-                                    id="hdrh-latency-graph",
-                                    figure=graph_hdrh_latency(
-                                        hdrh_data, self._graph_layout
-                                    )
-                                ))
-                            ])
-                        ]
-            else:
-                raise PreventUpdate
-            list_group_items = list()
-            for k, v in _process_stats(graph_data, trigger.idx):
-                list_group_items.append(dbc.ListGroupItem([dbc.Badge(k), v]))
-            if trigger.idx == "tput" and len(list_group_items) == 1:
-                job = graph_data[0].get("customdata", "")
-                list_group_items.append(dbc.ListGroupItem([
-                    dbc.Badge("csit-ref"),
-                    html.A(job, href=f"{C.URL_JENKINS}{job}", target="_blank")
-                ]))
-            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(
-                            dbc.ListGroup(list_group_items, flush=True),
-                            id="tput-lat-metadata",
-                            class_name="p-0"
-                        )
-                    ]
-                )
-            ]
-
-            return metadata, graph, True
+            return show_iterative_graph_data(
+                    trigger, graph_data, self._graph_layout)
 
         @app.callback(
             Output("offcanvas-documentation", "is_open"),