-# 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:
"""
+import logging
import pandas as pd
import dash_bootstrap_components as dbc
from dash import Input, Output, State
from dash.exceptions import PreventUpdate
from ast import literal_eval
+from yaml import load, FullLoader, YAMLError
from ..utils.constants import Constants as C
from ..utils.control_panel import ControlPanel
from ..utils.trigger import Trigger
-from ..utils.utils import label, gen_new_url, generate_options
+from ..utils.utils import label, gen_new_url, generate_options, navbar_report, \
+ show_tooltip
from ..utils.url_processing import url_decode
from .tables import coverage_tables, select_coverage_data
"phy-val": str(),
"area-opt": list(),
"area-dis": True,
- "area-val": str()
+ "area-val": str(),
+ "show-latency": ["show_latency", ]
}
self,
app: Flask,
data_coverage: pd.DataFrame,
- html_layout_file: str
+ html_layout_file: str,
+ tooltip_file: str
) -> None:
"""Initialization:
- save the input parameters,
:param app: Flask application running the dash application.
:param html_layout_file: Path and name of the file specifying the HTML
layout of the dash application.
+ :param tooltip_file: Path and name of the yaml file specifying the
+ tooltips.
:type app: Flask
:type html_layout_file: str
+ :type tooltip_file: str
"""
# Inputs
self._app = app
- self._html_layout_file = html_layout_file
self._data = data_coverage
+ self._html_layout_file = html_layout_file
+ self._tooltip_file = tooltip_file
# Get structure of tests:
tbs = dict()
if dut == "dpdk":
area = "dpdk"
else:
- area = "-".join(lst_test_id[3:-2])
+ area = ".".join(lst_test_id[3:-2])
suite = lst_test_id[-2].replace("2n1l-", "").replace("1n1l-", "").\
replace("2n-", "")
test = lst_test_id[-1]
tbs[rls][dut] = dict()
if tbs[rls][dut].get(d_ver, None) is None:
tbs[rls][dut][d_ver] = dict()
- if tbs[rls][dut][d_ver].get(infra, None) is None:
- tbs[rls][dut][d_ver][infra] = list()
- if area not in tbs[rls][dut][d_ver][infra]:
- tbs[rls][dut][d_ver][infra].append(area)
+ if tbs[rls][dut][d_ver].get(area, None) is None:
+ tbs[rls][dut][d_ver][area] = list()
+ if infra not in tbs[rls][dut][d_ver][area]:
+ tbs[rls][dut][d_ver][area].append(infra)
self._spec_tbs = tbs
f"Not possible to open the file {self._html_layout_file}\n{err}"
)
+ try:
+ with open(self._tooltip_file, "r") as file_read:
+ self._tooltips = load(file_read, Loader=FullLoader)
+ except IOError as err:
+ logging.warning(
+ f"Not possible to open the file {self._tooltip_file}\n{err}"
+ )
+ except YAMLError as err:
+ logging.warning(
+ f"An error occurred while parsing the specification file "
+ f"{self._tooltip_file}\n{err}"
+ )
+
# Callbacks:
if self._app is not None and hasattr(self, "callbacks"):
self.callbacks(self._app)
dbc.Row(
id="row-navbar",
class_name="g-0",
- children=[
- self._add_navbar()
- ]
+ children=[navbar_report((False, False, True, False)), ]
),
dbc.Row(
id="row-main",
self._add_ctrl_col(),
self._add_plotting_col()
]
+ ),
+ dbc.Offcanvas(
+ class_name="w-75",
+ id="offcanvas-documentation",
+ title="Documentation",
+ placement="end",
+ is_open=False,
+ children=html.Iframe(
+ src=C.URL_DOC_REL_NOTES,
+ width="100%",
+ height="100%"
+ )
)
]
)
]
)
- 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.COVERAGE_TITLE,
- disabled=True,
- external_link=True,
- href="#"
- )
- )
- ],
- 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.
children=[
dbc.InputGroup(
[
- dbc.InputGroupText("CSIT Release"),
+ dbc.InputGroupText(show_tooltip(
+ self._tooltips,
+ "help-release",
+ "CSIT Release"
+ )),
dbc.Select(
id={"type": "ctrl-dd", "index": "rls"},
placeholder="Select a Release...",
children=[
dbc.InputGroup(
[
- dbc.InputGroupText("DUT"),
+ dbc.InputGroupText(show_tooltip(
+ self._tooltips,
+ "help-dut",
+ "DUT"
+ )),
dbc.Select(
id={"type": "ctrl-dd", "index": "dut"},
placeholder="Select a Device under Test..."
children=[
dbc.InputGroup(
[
- dbc.InputGroupText("DUT Version"),
+ dbc.InputGroupText(show_tooltip(
+ self._tooltips,
+ "help-dut-ver",
+ "DUT Version"
+ )),
dbc.Select(
id={"type": "ctrl-dd", "index": "dutver"},
placeholder=\
children=[
dbc.InputGroup(
[
- dbc.InputGroupText("Infra"),
+ dbc.InputGroupText(show_tooltip(
+ self._tooltips,
+ "help-area",
+ "Area"
+ )),
+ dbc.Select(
+ id={"type": "ctrl-dd", "index": "area"},
+ placeholder="Select an Area..."
+ )
+ ],
+ size="sm"
+ )
+ ]
+ ),
+ dbc.Row(
+ class_name="g-0 p-1",
+ children=[
+ dbc.InputGroup(
+ [
+ dbc.InputGroupText(show_tooltip(
+ self._tooltips,
+ "help-infra",
+ "Infra"
+ )),
dbc.Select(
id={"type": "ctrl-dd", "index": "phy"},
placeholder=\
children=[
dbc.InputGroup(
[
- dbc.InputGroupText("Area"),
- dbc.Select(
- id={"type": "ctrl-dd", "index": "area"},
- placeholder="Select an Area..."
+ dbc.InputGroupText(show_tooltip(
+ self._tooltips,
+ "help-show-latency",
+ "Latency"
+ )),
+ dbc.Checklist(
+ id="show-latency",
+ options=[{
+ "value": "show_latency",
+ "label": "Show Latency"
+ }],
+ value=["show_latency"],
+ inline=True,
+ class_name="ms-2"
)
],
+ style={"align-items": "center"},
size="sm"
)
]
)
]
- def _get_plotting_area(self, selected: dict, url: str) -> list:
+ def _get_plotting_area(
+ self,
+ selected: dict,
+ url: str,
+ show_latency: bool
+ ) -> list:
"""Generate the plotting area with all its content.
:param selected: Selected parameters of tests.
:param url: URL to be displayed in the modal window.
+ :param show_latency: If True, latency is displayed in the tables.
:type selected: dict
:type url: str
+ :type show_latency: bool
:returns: List of rows with elements to be displayed in the plotting
area.
:rtype: list
return [
dbc.Row(
- children=coverage_tables(self._data, selected),
+ children=coverage_tables(self._data, selected, show_latency),
class_name="g-0 p-0",
),
dbc.Row(
Output({"type": "ctrl-dd", "index": "area"}, "options"),
Output({"type": "ctrl-dd", "index": "area"}, "disabled"),
Output({"type": "ctrl-dd", "index": "area"}, "value"),
+ Output("show-latency", "value"),
],
[
State("store-control-panel", "data"),
],
[
Input("url", "href"),
+ Input("show-latency", "value"),
Input({"type": "ctrl-dd", "index": ALL}, "value")
]
)
control_panel: dict,
selected: dict,
href: str,
+ show_latency: list,
*_
) -> tuple:
"""Update the application when the event is detected.
if trigger.type == "url" and url_params:
try:
+ show_latency = literal_eval(url_params["show_latency"][0])
selected = literal_eval(url_params["selection"][0])
- except (KeyError, IndexError):
+ except (KeyError, IndexError, AttributeError):
pass
if selected:
ctrl_panel.set({
[selected["dut"]].keys()
),
"dutver-dis": False,
+ "area-val": selected["area"],
+ "area-opt": [
+ {"label": label(v), "value": v} \
+ for v in sorted(self._spec_tbs[selected["rls"]]\
+ [selected["dut"]]\
+ [selected["dutver"]].keys())
+ ],
+ "area-dis": False,
"phy-val": selected["phy"],
"phy-opt": generate_options(
self._spec_tbs[selected["rls"]][selected["dut"]]\
- [selected["dutver"]].keys()
+ [selected["dutver"]][selected["area"]]
),
"phy-dis": False,
- "area-val": selected["area"],
- "area-opt": [
- {"label": label(v), "value": v} for v in sorted(
- self._spec_tbs[selected["rls"]]\
- [selected["dut"]][selected["dutver"]]\
- [selected["phy"]]
- )
- ],
- "area-dis": False
+ "show-latency": show_latency
})
on_draw = True
+ elif trigger.type == "show-latency":
+ ctrl_panel.set({"show-latency": show_latency})
+ on_draw = True
elif trigger.type == "ctrl-dd":
if trigger.idx == "rls":
try:
try:
rls = ctrl_panel.get("rls-val")
dut = ctrl_panel.get("dut-val")
- dutver = self._spec_tbs[rls][dut][trigger.value]
- options = generate_options(dutver.keys())
+ ver = self._spec_tbs[rls][dut][trigger.value]
+ options = [
+ {"label": label(v), "value": v} for v in sorted(ver)
+ ]
disabled = False
except KeyError:
options = list()
disabled = True
ctrl_panel.set({
"dutver-val": trigger.value,
- "phy-val": str(),
- "phy-opt": options,
- "phy-dis": disabled,
"area-val": str(),
- "area-opt": list(),
- "area-dis": True
+ "area-opt": options,
+ "area-dis": disabled,
+ "phy-val": str(),
+ "phy-opt": list(),
+ "phy-dis": True
})
- elif trigger.idx == "phy":
+ elif trigger.idx == "area":
try:
rls = ctrl_panel.get("rls-val")
dut = ctrl_panel.get("dut-val")
- dutver = ctrl_panel.get("dutver-val")
- phy = self._spec_tbs[rls][dut][dutver][trigger.value]
- options = [
- {"label": label(v), "value": v} for v in sorted(phy)
- ]
+ ver = ctrl_panel.get("dutver-val")
+ options = generate_options(
+ self._spec_tbs[rls][dut][ver][trigger.value])
disabled = False
except KeyError:
options = list()
disabled = True
ctrl_panel.set({
- "phy-val": trigger.value,
- "area-val": str(),
- "area-opt": options,
- "area-dis": disabled
+ "area-val": trigger.value,
+ "phy-val": str(),
+ "phy-opt": options,
+ "phy-dis": disabled
})
- elif trigger.idx == "area":
- ctrl_panel.set({"area-val": trigger.value})
+ elif trigger.idx == "phy":
+ ctrl_panel.set({"phy-val": trigger.value})
selected = {
"rls": ctrl_panel.get("rls-val"),
"dut": ctrl_panel.get("dut-val"),
if selected:
plotting_area = self._get_plotting_area(
selected,
- gen_new_url(parsed_url, {"selection": selected})
+ gen_new_url(
+ parsed_url,
+ {
+ "selection": selected,
+ "show_latency": show_latency
+ }
+ ),
+ show_latency=bool(show_latency)
)
else:
plotting_area = C.PLACEHOLDER
@app.callback(
Output("download-iterative-data", "data"),
State("store-selected-tests", "data"),
+ State("show-latency", "value"),
Input("plot-btn-download", "n_clicks"),
prevent_initial_call=True
)
- def _download_coverage_data(selection, _):
+ def _download_coverage_data(selection, show_latency, _):
"""Download the data
:param selection: List of tests selected by user stored in the
browser.
+ :param show_latency: If True, latency is displayed in the tables.
:type selection: dict
+ :type show_latency: bool
:returns: dict of data frame content (base64 encoded) and meta data
used by the Download component.
:rtype: dict
if not selection:
raise PreventUpdate
- df = select_coverage_data(self._data, selection, csv=True)
+ df = select_coverage_data(
+ self._data,
+ selection,
+ csv=True,
+ show_latency=bool(show_latency)
+ )
return dcc.send_data_frame(df.to_csv, C.COVERAGE_DOWNLOAD_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