aboutsummaryrefslogtreecommitdiffstats
path: root/resources/tools/dash/app/pal/trending
diff options
context:
space:
mode:
Diffstat (limited to 'resources/tools/dash/app/pal/trending')
-rw-r--r--resources/tools/dash/app/pal/trending/__init__.py12
-rw-r--r--resources/tools/dash/app/pal/trending/graphs.py408
-rw-r--r--resources/tools/dash/app/pal/trending/layout.py1387
-rw-r--r--resources/tools/dash/app/pal/trending/layout.yaml129
-rw-r--r--resources/tools/dash/app/pal/trending/trending.py48
5 files changed, 0 insertions, 1984 deletions
diff --git a/resources/tools/dash/app/pal/trending/__init__.py b/resources/tools/dash/app/pal/trending/__init__.py
deleted file mode 100644
index 5692432123..0000000000
--- a/resources/tools/dash/app/pal/trending/__init__.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright (c) 2022 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:
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
diff --git a/resources/tools/dash/app/pal/trending/graphs.py b/resources/tools/dash/app/pal/trending/graphs.py
deleted file mode 100644
index 1eff4aa889..0000000000
--- a/resources/tools/dash/app/pal/trending/graphs.py
+++ /dev/null
@@ -1,408 +0,0 @@
-# Copyright (c) 2022 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:
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""
-"""
-
-import plotly.graph_objects as go
-import pandas as pd
-
-import hdrh.histogram
-import hdrh.codec
-
-from datetime import datetime
-
-from ..utils.constants import Constants as C
-from ..utils.utils import classify_anomalies, get_color
-
-
-def _get_hdrh_latencies(row: pd.Series, name: str) -> dict:
- """Get the HDRH latencies from the test data.
-
- :param row: A row fron the data frame with test data.
- :param name: The test name to be displayed as the graph title.
- :type row: pandas.Series
- :type name: str
- :returns: Dictionary with HDRH latencies.
- :rtype: dict
- """
-
- latencies = {"name": name}
- for key in C.LAT_HDRH:
- try:
- latencies[key] = row[key]
- except KeyError:
- return None
-
- return latencies
-
-
-def select_trending_data(data: pd.DataFrame, itm:dict) -> pd.DataFrame:
- """Select the data for graphs from the provided data frame.
-
- :param data: Data frame with data for graphs.
- :param itm: Item (in this case job name) which data will be selected from
- the input data frame.
- :type data: pandas.DataFrame
- :type itm: str
- :returns: A data frame with selected data.
- :rtype: pandas.DataFrame
- """
-
- phy = itm["phy"].split("-")
- if len(phy) == 4:
- topo, arch, nic, drv = phy
- if drv == "dpdk":
- drv = ""
- else:
- drv += "-"
- drv = drv.replace("_", "-")
- else:
- return None
-
- core = str() if itm["dut"] == "trex" else f"{itm['core']}"
- ttype = "ndrpdr" if itm["testtype"] in ("ndr", "pdr") else itm["testtype"]
- dut_v100 = "none" if itm["dut"] == "trex" else itm["dut"]
- dut_v101 = itm["dut"]
-
- df = data.loc[(
- (
- (
- (data["version"] == "1.0.0") &
- (data["dut_type"].str.lower() == dut_v100)
- ) |
- (
- (data["version"] == "1.0.1") &
- (data["dut_type"].str.lower() == dut_v101)
- )
- ) &
- (data["test_type"] == ttype) &
- (data["passed"] == True)
- )]
- df = df[df.job.str.endswith(f"{topo}-{arch}")]
- df = df[df.test_id.str.contains(
- f"^.*[.|-]{nic}.*{itm['framesize']}-{core}-{drv}{itm['test']}-{ttype}$",
- regex=True
- )].sort_values(by="start_time", ignore_index=True)
-
- return df
-
-
-def _generate_trending_traces(ttype: str, name: str, df: pd.DataFrame,
- color: str, norm_factor: float) -> list:
- """Generate the trending traces for the trending graph.
-
- :param ttype: Test type (MRR, NDR, PDR).
- :param name: The test name to be displayed as the graph title.
- :param df: Data frame with test data.
- :param color: The color of the trace (samples and trend line).
- :param norm_factor: The factor used for normalization of the results to CPU
- frequency set to Constants.NORM_FREQUENCY.
- :type ttype: str
- :type name: str
- :type df: pandas.DataFrame
- :type color: str
- :type norm_factor: float
- :returns: Traces (samples, trending line, anomalies)
- :rtype: list
- """
-
- df = df.dropna(subset=[C.VALUE[ttype], ])
- if df.empty:
- return list()
- if df.empty:
- return list()
-
- x_axis = df["start_time"].tolist()
- if ttype == "pdr-lat":
- y_data = [(itm / norm_factor) for itm in df[C.VALUE[ttype]].tolist()]
- else:
- y_data = [(itm * norm_factor) for itm in df[C.VALUE[ttype]].tolist()]
-
- anomalies, trend_avg, trend_stdev = classify_anomalies(
- {k: v for k, v in zip(x_axis, y_data)}
- )
-
- hover = list()
- customdata = list()
- for idx, (_, row) in enumerate(df.iterrows()):
- d_type = "trex" if row["dut_type"] == "none" else row["dut_type"]
- hover_itm = (
- f"date: {row['start_time'].strftime('%Y-%m-%d %H:%M:%S')}<br>"
- f"<prop> [{row[C.UNIT[ttype]]}]: {y_data[idx]:,.0f}<br>"
- f"<stdev>"
- f"{d_type}-ref: {row['dut_version']}<br>"
- f"csit-ref: {row['job']}/{row['build']}<br>"
- f"hosts: {', '.join(row['hosts'])}"
- )
- if ttype == "mrr":
- stdev = (
- f"stdev [{row['result_receive_rate_rate_unit']}]: "
- f"{row['result_receive_rate_rate_stdev']:,.0f}<br>"
- )
- else:
- stdev = ""
- hover_itm = hover_itm.replace(
- "<prop>", "latency" if ttype == "pdr-lat" else "average"
- ).replace("<stdev>", stdev)
- hover.append(hover_itm)
- if ttype == "pdr-lat":
- customdata.append(_get_hdrh_latencies(row, name))
-
- hover_trend = list()
- for avg, stdev, (_, row) in zip(trend_avg, trend_stdev, df.iterrows()):
- d_type = "trex" if row["dut_type"] == "none" else row["dut_type"]
- hover_itm = (
- f"date: {row['start_time'].strftime('%Y-%m-%d %H:%M:%S')}<br>"
- f"trend [pps]: {avg:,.0f}<br>"
- f"stdev [pps]: {stdev:,.0f}<br>"
- f"{d_type}-ref: {row['dut_version']}<br>"
- f"csit-ref: {row['job']}/{row['build']}<br>"
- f"hosts: {', '.join(row['hosts'])}"
- )
- if ttype == "pdr-lat":
- hover_itm = hover_itm.replace("[pps]", "[us]")
- hover_trend.append(hover_itm)
-
- traces = [
- go.Scatter( # Samples
- x=x_axis,
- y=y_data,
- name=name,
- mode="markers",
- marker={
- "size": 5,
- "color": color,
- "symbol": "circle",
- },
- text=hover,
- hoverinfo="text+name",
- showlegend=True,
- legendgroup=name,
- customdata=customdata
- ),
- go.Scatter( # Trend line
- x=x_axis,
- y=trend_avg,
- name=name,
- mode="lines",
- line={
- "shape": "linear",
- "width": 1,
- "color": color,
- },
- text=hover_trend,
- hoverinfo="text+name",
- showlegend=False,
- legendgroup=name,
- )
- ]
-
- if anomalies:
- anomaly_x = list()
- anomaly_y = list()
- anomaly_color = list()
- hover = list()
- for idx, anomaly in enumerate(anomalies):
- if anomaly in ("regression", "progression"):
- anomaly_x.append(x_axis[idx])
- anomaly_y.append(trend_avg[idx])
- anomaly_color.append(C.ANOMALY_COLOR[anomaly])
- hover_itm = (
- f"date: {x_axis[idx].strftime('%Y-%m-%d %H:%M:%S')}<br>"
- f"trend [pps]: {trend_avg[idx]:,.0f}<br>"
- f"classification: {anomaly}"
- )
- if ttype == "pdr-lat":
- hover_itm = hover_itm.replace("[pps]", "[us]")
- hover.append(hover_itm)
- anomaly_color.extend([0.0, 0.5, 1.0])
- traces.append(
- go.Scatter(
- x=anomaly_x,
- y=anomaly_y,
- mode="markers",
- text=hover,
- hoverinfo="text+name",
- showlegend=False,
- legendgroup=name,
- name=name,
- marker={
- "size": 15,
- "symbol": "circle-open",
- "color": anomaly_color,
- "colorscale": C.COLORSCALE_LAT \
- if ttype == "pdr-lat" else C.COLORSCALE_TPUT,
- "showscale": True,
- "line": {
- "width": 2
- },
- "colorbar": {
- "y": 0.5,
- "len": 0.8,
- "title": "Circles Marking Data Classification",
- "titleside": "right",
- "tickmode": "array",
- "tickvals": [0.167, 0.500, 0.833],
- "ticktext": C.TICK_TEXT_LAT \
- if ttype == "pdr-lat" else C.TICK_TEXT_TPUT,
- "ticks": "",
- "ticklen": 0,
- "tickangle": -90,
- "thickness": 10
- }
- }
- )
- )
-
- return traces
-
-
-def graph_trending(data: pd.DataFrame, sel:dict, layout: dict,
- normalize: bool) -> tuple:
- """Generate the trending graph(s) - MRR, NDR, PDR and for PDR also Latences
- (result_latency_forward_pdr_50_avg).
-
- :param data: Data frame with test results.
- :param sel: Selected tests.
- :param layout: Layout of plot.ly graph.
- :param normalize: If True, the data is normalized to CPU frquency
- Constants.NORM_FREQUENCY.
- :type data: pandas.DataFrame
- :type sel: dict
- :type layout: dict
- :type normalize: bool
- :returns: Trending graph(s)
- :rtype: tuple(plotly.graph_objects.Figure, plotly.graph_objects.Figure)
- """
-
- if not sel:
- return None, None
-
- fig_tput = None
- fig_lat = None
- for idx, itm in enumerate(sel):
-
- df = select_trending_data(data, itm)
- if df is None or df.empty:
- continue
-
- name = "-".join((itm["dut"], itm["phy"], itm["framesize"], itm["core"],
- itm["test"], itm["testtype"], ))
- if normalize:
- phy = itm["phy"].split("-")
- topo_arch = f"{phy[0]}-{phy[1]}" if len(phy) == 4 else str()
- norm_factor = (C.NORM_FREQUENCY / C.FREQUENCY[topo_arch]) \
- if topo_arch else 1.0
- else:
- norm_factor = 1.0
- traces = _generate_trending_traces(
- itm["testtype"], name, df, get_color(idx), norm_factor
- )
- if traces:
- if not fig_tput:
- fig_tput = go.Figure()
- fig_tput.add_traces(traces)
-
- if itm["testtype"] == "pdr":
- traces = _generate_trending_traces(
- "pdr-lat", name, df, get_color(idx), norm_factor
- )
- if traces:
- if not fig_lat:
- fig_lat = go.Figure()
- fig_lat.add_traces(traces)
-
- if fig_tput:
- fig_tput.update_layout(layout.get("plot-trending-tput", dict()))
- if fig_lat:
- fig_lat.update_layout(layout.get("plot-trending-lat", dict()))
-
- return fig_tput, fig_lat
-
-
-def graph_hdrh_latency(data: dict, layout: dict) -> go.Figure:
- """Generate HDR Latency histogram graphs.
-
- :param data: HDRH data.
- :param layout: Layout of plot.ly graph.
- :type data: dict
- :type layout: dict
- :returns: HDR latency Histogram.
- :rtype: plotly.graph_objects.Figure
- """
-
- fig = None
-
- traces = list()
- for idx, (lat_name, lat_hdrh) in enumerate(data.items()):
- try:
- decoded = hdrh.histogram.HdrHistogram.decode(lat_hdrh)
- except (hdrh.codec.HdrLengthException, TypeError) as err:
- continue
- previous_x = 0.0
- prev_perc = 0.0
- xaxis = list()
- yaxis = list()
- hovertext = list()
- for item in decoded.get_recorded_iterator():
- # The real value is "percentile".
- # For 100%, we cut that down to "x_perc" to avoid
- # infinity.
- percentile = item.percentile_level_iterated_to
- x_perc = min(percentile, C.PERCENTILE_MAX)
- xaxis.append(previous_x)
- yaxis.append(item.value_iterated_to)
- hovertext.append(
- f"<b>{C.GRAPH_LAT_HDRH_DESC[lat_name]}</b><br>"
- f"Direction: {('W-E', 'E-W')[idx % 2]}<br>"
- f"Percentile: {prev_perc:.5f}-{percentile:.5f}%<br>"
- f"Latency: {item.value_iterated_to}uSec"
- )
- next_x = 100.0 / (100.0 - x_perc)
- xaxis.append(next_x)
- yaxis.append(item.value_iterated_to)
- hovertext.append(
- f"<b>{C.GRAPH_LAT_HDRH_DESC[lat_name]}</b><br>"
- f"Direction: {('W-E', 'E-W')[idx % 2]}<br>"
- f"Percentile: {prev_perc:.5f}-{percentile:.5f}%<br>"
- f"Latency: {item.value_iterated_to}uSec"
- )
- previous_x = next_x
- prev_perc = percentile
-
- traces.append(
- go.Scatter(
- x=xaxis,
- y=yaxis,
- name=C.GRAPH_LAT_HDRH_DESC[lat_name],
- mode="lines",
- legendgroup=C.GRAPH_LAT_HDRH_DESC[lat_name],
- showlegend=bool(idx % 2),
- line=dict(
- color=get_color(int(idx/2)),
- dash="solid",
- width=1 if idx % 2 else 2
- ),
- hovertext=hovertext,
- hoverinfo="text"
- )
- )
- if traces:
- fig = go.Figure()
- fig.add_traces(traces)
- layout_hdrh = layout.get("plot-hdrh-latency", None)
- if lat_hdrh:
- fig.update_layout(layout_hdrh)
-
- return fig
diff --git a/resources/tools/dash/app/pal/trending/layout.py b/resources/tools/dash/app/pal/trending/layout.py
deleted file mode 100644
index eac02ced6f..0000000000
--- a/resources/tools/dash/app/pal/trending/layout.py
+++ /dev/null
@@ -1,1387 +0,0 @@
-# Copyright (c) 2022 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:
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""Plotly Dash HTML layout override.
-"""
-
-import logging
-import pandas as pd
-import dash_bootstrap_components as dbc
-
-from flask import Flask
-from dash import dcc
-from dash import html
-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
-from copy import deepcopy
-from json import loads, JSONDecodeError
-from ast import literal_eval
-
-from ..utils.constants import Constants as C
-from ..utils.utils import show_tooltip, label, sync_checklists, list_tests, \
- gen_new_url, generate_options
-from ..utils.url_processing import url_decode
-from ..data.data import Data
-from .graphs import graph_trending, graph_hdrh_latency, \
- select_trending_data
-
-
-class Layout:
- """The layout of the dash app and the callbacks.
- """
-
- def __init__(self, app: Flask, html_layout_file: str,
- graph_layout_file: str, data_spec_file: str, tooltip_file: str,
- time_period: str=None) -> None:
- """Initialization:
- - save the input parameters,
- - read and pre-process the data,
- - prepare data for the control panel,
- - read HTML layout file,
- - read tooltips from the tooltip file.
-
- :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 graph_layout_file: Path and name of the file with layout of
- plot.ly graphs.
- :param data_spec_file: Path and name of the file specifying the data to
- be read from parquets for this application.
- :param tooltip_file: Path and name of the yaml file specifying the
- tooltips.
- :param time_period: It defines the time period for data read from the
- parquets in days from now back to the past.
- :type app: Flask
- :type html_layout_file: str
- :type graph_layout_file: str
- :type data_spec_file: str
- :type tooltip_file: str
- :type time_period: int
- """
-
- # Inputs
- self._app = app
- self._html_layout_file = html_layout_file
- self._graph_layout_file = graph_layout_file
- self._data_spec_file = data_spec_file
- self._tooltip_file = tooltip_file
- self._time_period = time_period
-
- # Read the data:
- data_mrr = Data(
- data_spec_file=self._data_spec_file,
- debug=True
- ).read_trending_mrr(days=self._time_period)
-
- data_ndrpdr = Data(
- data_spec_file=self._data_spec_file,
- debug=True
- ).read_trending_ndrpdr(days=self._time_period)
-
- self._data = pd.concat([data_mrr, data_ndrpdr], ignore_index=True)
-
- data_time_period = \
- (datetime.utcnow() - self._data["start_time"].min()).days
- if self._time_period > data_time_period:
- self._time_period = data_time_period
-
-
- # Get structure of tests:
- tbs = dict()
- for _, row in self._data[["job", "test_id"]].drop_duplicates().\
- iterrows():
- lst_job = row["job"].split("-")
- dut = lst_job[1]
- ttype = lst_job[3]
- tbed = "-".join(lst_job[-2:])
- lst_test = row["test_id"].split(".")
- if dut == "dpdk":
- area = "dpdk"
- else:
- area = "-".join(lst_test[3:-2])
- suite = lst_test[-2].replace("2n1l-", "").replace("1n1l-", "").\
- replace("2n-", "")
- test = lst_test[-1]
- nic = suite.split("-")[0]
- for drv in C.DRIVERS:
- if drv in test:
- if drv == "af-xdp":
- driver = "af_xdp"
- else:
- driver = drv
- test = test.replace(f"{drv}-", "")
- break
- else:
- driver = "dpdk"
- infra = "-".join((tbed, nic, driver))
- lst_test = test.split("-")
- framesize = lst_test[0]
- core = lst_test[1] if lst_test[1] else "8C"
- test = "-".join(lst_test[2: -1])
-
- 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 ttype == "mrr":
- if "MRR" not in tbs[dut][infra][area][test]["test-type"]:
- tbs[dut][infra][area][test]["test-type"].append("MRR")
- elif ttype == "ndrpdr":
- if "NDR" not in tbs[dut][infra][area][test]["test-type"]:
- tbs[dut][infra][area][test]["test-type"].extend(
- ("NDR", "PDR"))
- self._spec_tbs = tbs
-
- # Read from files:
- self._html_layout = ""
- self._graph_layout = None
- self._tooltips = dict()
-
- try:
- with open(self._html_layout_file, "r") as file_read:
- self._html_layout = file_read.read()
- except IOError as err:
- raise RuntimeError(
- f"Not possible to open the file {self._html_layout_file}\n{err}"
- )
-
- try:
- with open(self._graph_layout_file, "r") as file_read:
- self._graph_layout = load(file_read, Loader=FullLoader)
- except IOError as err:
- raise RuntimeError(
- f"Not possible to open the file {self._graph_layout_file}\n"
- f"{err}"
- )
- except YAMLError as err:
- raise RuntimeError(
- f"An error occurred while parsing the specification file "
- f"{self._graph_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)
-
- @property
- def html_layout(self):
- return self._html_layout
-
- @property
- def spec_tbs(self):
- return self._spec_tbs
-
- @property
- def data(self):
- return self._data
-
- @property
- def layout(self):
- return self._graph_layout
-
- @property
- def time_period(self):
- return self._time_period
-
- def add_content(self):
- """Top level method which generated the web page.
-
- It generates:
- - Store for user input data,
- - Navigation bar,
- - Main area with control panel and ploting area.
-
- If no HTML layout is provided, an error message is displayed instead.
-
- :returns: The HTML div with the whole page.
- :rtype: html.Div
- """
-
- if self.html_layout and self.spec_tbs:
- return html.Div(
- id="div-main",
- className="small",
- children=[
- dbc.Row(
- id="row-navbar",
- class_name="g-0",
- children=[
- self._add_navbar(),
- ]
- ),
- dcc.Loading(
- dbc.Offcanvas(
- class_name="w-50",
- id="offcanvas-metadata",
- title="Throughput And Latency",
- placement="end",
- is_open=False,
- children=[
- dbc.Row(id="metadata-tput-lat"),
- dbc.Row(id="metadata-hdrh-graph"),
- ]
- )
- ),
- dbc.Row(
- id="row-main",
- class_name="g-0",
- children=[
- dcc.Store(id="selected-tests"),
- dcc.Store(id="control-panel"),
- dcc.Location(id="url", refresh=False),
- self._add_ctrl_col(),
- self._add_plotting_col(),
- ]
- )
- ]
- )
- else:
- return html.Div(
- id="div-main-error",
- children=[
- dbc.Alert(
- [
- "An Error Occured",
- ],
- color="danger",
- ),
- ]
- )
-
- 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(
- "Continuous Performance Trending",
- disabled=True,
- external_link=True,
- href="#"
- )
- )
- ],
- brand="Dashboard",
- 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.
-
- :returns: Column with the control panel.
- :rtype: dbc.Col
- """
- return dbc.Col(
- id="col-controls",
- children=[
- self._add_ctrl_panel(),
- ],
- )
-
- def _add_plotting_col(self) -> dbc.Col:
- """Add column with plots and tables. It is placed on the right side.
-
- :returns: Column with tables.
- :rtype: dbc.Col
- """
- return dbc.Col(
- id="col-plotting-area",
- children=[
- dcc.Loading(
- children=[
- dbc.Row( # Throughput
- id="row-graph-tput",
- class_name="g-0 p-2",
- children=[
- C.PLACEHOLDER
- ]
- ),
- dbc.Row( # Latency
- id="row-graph-lat",
- class_name="g-0 p-2",
- children=[
- C.PLACEHOLDER
- ]
- ),
- dbc.Row( # Download
- id="row-btn-download",
- class_name="g-0 p-2",
- children=[
- C.PLACEHOLDER
- ]
- )
- ]
- )
- ],
- width=9,
- )
-
- def _add_ctrl_panel(self) -> dbc.Row:
- """Add control panel.
-
- :returns: Control panel.
- :rtype: dbc.Row
- """
- return dbc.Row(
- id="row-ctrl-panel",
- class_name="g-0 p-2",
- children=[
- dbc.Row(
- class_name="g-0",
- children=[
- dbc.InputGroup(
- [
- dbc.InputGroupText(
- children=show_tooltip(self._tooltips,
- "help-dut", "DUT")
- ),
- dbc.Select(
- id="dd-ctrl-dut",
- placeholder=(
- "Select a Device under Test..."
- ),
- options=sorted(
- [
- {"label": k, "value": k} \
- for k in self.spec_tbs.keys()
- ],
- key=lambda d: d["label"]
- )
- )
- ],
- class_name="mb-3",
- size="sm",
- ),
- ]
- ),
- dbc.Row(
- class_name="g-0",
- children=[
- dbc.InputGroup(
- [
- dbc.InputGroupText(
- children=show_tooltip(self._tooltips,
- "help-infra", "Infra")
- ),
- dbc.Select(
- id="dd-ctrl-phy",
- placeholder=(
- "Select a Physical Test Bed "
- "Topology..."
- )
- )
- ],
- class_name="mb-3",
- size="sm",
- ),
- ]
- ),
- dbc.Row(
- class_name="g-0",
- children=[
- dbc.InputGroup(
- [
- dbc.InputGroupText(
- children=show_tooltip(self._tooltips,
- "help-area", "Area")
- ),
- dbc.Select(
- id="dd-ctrl-area",
- placeholder="Select an Area...",
- disabled=True,
- ),
- ],
- class_name="mb-3",
- size="sm",
- ),
- ]
- ),
- dbc.Row(
- class_name="g-0",
- children=[
- dbc.InputGroup(
- [
- dbc.InputGroupText(
- children=show_tooltip(self._tooltips,
- "help-test", "Test")
- ),
- dbc.Select(
- id="dd-ctrl-test",
- placeholder="Select a Test...",
- disabled=True,
- ),
- ],
- class_name="mb-3",
- size="sm",
- ),
- ]
- ),
- dbc.Row(
- id="row-ctrl-framesize",
- class_name="gy-1",
- children=[
- dbc.Label(
- children=show_tooltip(self._tooltips,
- "help-framesize", "Frame Size"),
- class_name="p-0"
- ),
- dbc.Col(
- children=[
- dbc.Checklist(
- id="cl-ctrl-framesize-all",
- options=C.CL_ALL_DISABLED,
- inline=True,
- switch=False
- ),
- ],
- width=3
- ),
- dbc.Col(
- children=[
- dbc.Checklist(
- id="cl-ctrl-framesize",
- inline=True,
- switch=False
- )
- ]
- )
- ]
- ),
- dbc.Row(
- id="row-ctrl-core",
- class_name="gy-1",
- children=[
- dbc.Label(
- children=show_tooltip(self._tooltips,
- "help-cores", "Number of Cores"),
- class_name="p-0"
- ),
- dbc.Col(
- children=[
- dbc.Checklist(
- id="cl-ctrl-core-all",
- options=C.CL_ALL_DISABLED,
- inline=False,
- switch=False
- )
- ],
- width=3
- ),
- dbc.Col(
- children=[
- dbc.Checklist(
- id="cl-ctrl-core",
- inline=True,
- switch=False
- )
- ]
- )
- ]
- ),
- dbc.Row(
- id="row-ctrl-testtype",
- class_name="gy-1",
- children=[
- dbc.Label(
- children=show_tooltip(self._tooltips,
- "help-ttype", "Test Type"),
- class_name="p-0"
- ),
- dbc.Col(
- children=[
- dbc.Checklist(
- id="cl-ctrl-testtype-all",
- options=C.CL_ALL_DISABLED,
- inline=True,
- switch=False
- ),
- ],
- width=3
- ),
- dbc.Col(
- children=[
- dbc.Checklist(
- id="cl-ctrl-testtype",
- inline=True,
- switch=False
- )
- ]
- )
- ]
- ),
- dbc.Row(
- id="row-ctrl-normalize",
- class_name="gy-1",
- children=[
- dbc.Label(
- children=show_tooltip(self._tooltips,
- "help-normalize", "Normalize"),
- class_name="p-0"
- ),
- dbc.Col(
- children=[
- dbc.Checklist(
- id="cl-ctrl-normalize",
- options=[{
- "value": "normalize",
- "label": (
- "Normalize results to CPU"
- "frequency 2GHz"
- )
- }],
- value=[],
- inline=True,
- switch=False
- ),
- ]
- )
- ]
- ),
- dbc.Row(
- class_name="gy-1 p-0",
- children=[
- dbc.ButtonGroup(
- [
- dbc.Button(
- id="btn-ctrl-add",
- children="Add Selected",
- class_name="me-1",
- color="info"
- )
- ]
- )
- ]
- ),
- dbc.Row(
- id="row-card-sel-tests",
- class_name="gy-1",
- style=C.STYLE_DISABLED,
- children=[
- 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(
- id="row-btns-sel-tests",
- style=C.STYLE_DISABLED,
- children=[
- dbc.ButtonGroup(
- class_name="gy-2",
- children=[
- dbc.Button(
- id="btn-sel-remove",
- children="Remove Selected",
- class_name="w-100 me-1",
- color="info",
- disabled=False
- ),
- dbc.Button(
- id="btn-sel-remove-all",
- children="Remove All",
- class_name="w-100 me-1",
- color="info",
- disabled=False
- ),
- ]
- )
- ]
- ),
- ]
- )
-
- class ControlPanel:
- """A class representing the control panel.
- """
-
- def __init__(self, panel: dict) -> None:
- """Initialisation of the control pannel by default values. If
- particular values are provided (parameter "panel") they are set
- afterwards.
-
- :param panel: Custom values to be set to the control panel.
- :param default: Default values to be set to the control panel.
- :type panel: dict
- :type defaults: dict
- """
-
- # Defines also the order of keys
- self._defaults = {
- "dd-ctrl-dut-value": str(),
- "dd-ctrl-phy-options": list(),
- "dd-ctrl-phy-disabled": True,
- "dd-ctrl-phy-value": str(),
- "dd-ctrl-area-options": list(),
- "dd-ctrl-area-disabled": True,
- "dd-ctrl-area-value": str(),
- "dd-ctrl-test-options": list(),
- "dd-ctrl-test-disabled": True,
- "dd-ctrl-test-value": str(),
- "cl-ctrl-core-options": list(),
- "cl-ctrl-core-value": list(),
- "cl-ctrl-core-all-value": list(),
- "cl-ctrl-core-all-options": C.CL_ALL_DISABLED,
- "cl-ctrl-framesize-options": list(),
- "cl-ctrl-framesize-value": list(),
- "cl-ctrl-framesize-all-value": list(),
- "cl-ctrl-framesize-all-options": C.CL_ALL_DISABLED,
- "cl-ctrl-testtype-options": list(),
- "cl-ctrl-testtype-value": list(),
- "cl-ctrl-testtype-all-value": list(),
- "cl-ctrl-testtype-all-options": C.CL_ALL_DISABLED,
- "btn-ctrl-add-disabled": True,
- "cl-normalize-value": list(),
- "cl-selected-options": list()
- }
-
- self._panel = deepcopy(self._defaults)
- if panel:
- for key in self._defaults:
- self._panel[key] = panel[key]
-
- @property
- def defaults(self) -> dict:
- return self._defaults
-
- @property
- def panel(self) -> dict:
- return self._panel
-
- def set(self, kwargs: dict) -> None:
- """Set the values of the Control panel.
-
- :param kwargs: key - value pairs to be set.
- :type kwargs: dict
- :raises KeyError: If the key in kwargs is not present in the Control
- panel.
- """
- for key, val in kwargs.items():
- if key in self._panel:
- self._panel[key] = val
- else:
- raise KeyError(f"The key {key} is not defined.")
-
- def get(self, key: str) -> any:
- """Returns the value of a key from the Control panel.
-
- :param key: The key which value should be returned.
- :type key: str
- :returns: The value of the key.
- :rtype: any
- :raises KeyError: If the key in kwargs is not present in the Control
- panel.
- """
- return self._panel[key]
-
- def values(self) -> tuple:
- """Returns the values from the Control panel as a list.
-
- :returns: The values from the Control panel.
- :rtype: list
- """
- return tuple(self._panel.values())
-
- def callbacks(self, app):
- """Callbacks for the whole application.
-
- :param app: The application.
- :type app: Flask
- """
-
- def _generate_plotting_area(figs: tuple, url: str) -> tuple:
- """Generate the plotting area with all its content.
-
- :param figs: Figures to be placed in the plotting area.
- :param utl: The URL to be placed in the plotting area bellow the
- tables.
- :type figs: tuple of plotly.graph_objects.Figure
- :type url: str
- :returns: tuple of elements to be shown in the plotting area.
- :rtype: tuple(dcc.Graph, dcc.Graph, list(dbc.Col, dbc.Col))
- """
-
- (fig_tput, fig_lat) = figs
-
- row_fig_tput = C.PLACEHOLDER
- row_fig_lat = C.PLACEHOLDER
- row_btn_dwnld = C.PLACEHOLDER
-
- if fig_tput:
- row_fig_tput = [
- dcc.Graph(
- id={"type": "graph", "index": "tput"},
- figure=fig_tput
- )
- ]
- row_btn_dwnld = [
- dbc.Col( # Download
- width=2,
- children=[
- dcc.Loading(children=[
- dbc.Button(
- id="btn-download-data",
- children=show_tooltip(self._tooltips,
- "help-download", "Download Data"),
- class_name="me-1",
- color="info"
- ),
- dcc.Download(id="download-data")
- ]),
- ]
- ),
- dbc.Col( # Show URL
- width=10,
- children=[
- dbc.InputGroup(
- class_name="me-1",
- children=[
- dbc.InputGroupText(
- style=C.URL_STYLE,
- children=show_tooltip(self._tooltips,
- "help-url", "URL", "input-url")
- ),
- dbc.Input(
- id="input-url",
- readonly=True,
- type="url",
- style=C.URL_STYLE,
- value=url
- )
- ]
- )
- ]
- )
- ]
- if fig_lat:
- row_fig_lat = [
- 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
- 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-dut", "value"),
- Output("dd-ctrl-phy", "options"),
- Output("dd-ctrl-phy", "disabled"),
- Output("dd-ctrl-phy", "value"),
- Output("dd-ctrl-area", "options"),
- Output("dd-ctrl-area", "disabled"),
- Output("dd-ctrl-area", "value"),
- Output("dd-ctrl-test", "options"),
- Output("dd-ctrl-test", "disabled"),
- Output("dd-ctrl-test", "value"),
- Output("cl-ctrl-core", "options"),
- Output("cl-ctrl-core", "value"),
- Output("cl-ctrl-core-all", "value"),
- Output("cl-ctrl-core-all", "options"),
- Output("cl-ctrl-framesize", "options"),
- Output("cl-ctrl-framesize", "value"),
- Output("cl-ctrl-framesize-all", "value"),
- Output("cl-ctrl-framesize-all", "options"),
- Output("cl-ctrl-testtype", "options"),
- Output("cl-ctrl-testtype", "value"),
- Output("cl-ctrl-testtype-all", "value"),
- Output("cl-ctrl-testtype-all", "options"),
- Output("btn-ctrl-add", "disabled"),
- Output("cl-ctrl-normalize", "value"),
- Output("cl-selected", "options"), # User selection
- State("control-panel", "data"), # Store
- State("selected-tests", "data"), # Store
- State("cl-selected", "value"), # User selection
- Input("dd-ctrl-dut", "value"),
- Input("dd-ctrl-phy", "value"),
- Input("dd-ctrl-area", "value"),
- Input("dd-ctrl-test", "value"),
- Input("cl-ctrl-core", "value"),
- Input("cl-ctrl-core-all", "value"),
- Input("cl-ctrl-framesize", "value"),
- Input("cl-ctrl-framesize-all", "value"),
- Input("cl-ctrl-testtype", "value"),
- Input("cl-ctrl-testtype-all", "value"),
- Input("cl-ctrl-normalize", "value"),
- Input("btn-ctrl-add", "n_clicks"),
- Input("btn-sel-remove", "n_clicks"),
- Input("btn-sel-remove-all", "n_clicks"),
- Input("url", "href")
- )
- def _update_ctrl_panel(cp_data: dict, store_sel: list, list_sel: list,
- dd_dut: str, 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, cl_normalize: list,
- btn_add: int, btn_remove: int,
- btn_remove_all: int, href: str) -> tuple:
- """Update the application when the event is detected.
-
- :param cp_data: Current status of the control panel stored in
- browser.
- :param store_sel: List of tests selected by user stored in the
- browser.
- :param list_sel: List of tests selected by the user shown in the
- checklist.
- :param dd_dut: Input - DUTs.
- :param dd_phy: Input - topo- arch-nic-driver.
- :param dd_area: Input - Tested area.
- :param dd_test: Input - Test.
- :param cl_core: Input - Number of cores.
- :param cl_core_all: Input - All numbers of cores.
- :param cl_framesize: Input - Frame sizes.
- :param cl_framesize_all: Input - All frame sizes.
- :param cl_testtype: Input - Test type (NDR, PDR, MRR).
- :param cl_testtype_all: Input - All test types.
- :param cl_normalize: Input - Normalize the results.
- :param btn_add: Input - Button "Add Selected" tests.
- :param btn_remove: Input - Button "Remove selected" tests.
- :param btn_remove_all: Input - Button "Remove All" tests.
- :param href: Input - The URL provided by the browser.
- :type cp_data: dict
- :type store_sel: list
- :type list_sel: list
- :type dd_dut: str
- :type dd_phy: str
- :type dd_area: str
- :type dd_test: str
- :type cl_core: list
- :type cl_core_all: list
- :type cl_framesize: list
- :type cl_framesize_all: list
- :type cl_testtype: list
- :type cl_testtype_all: list
- :type cl_normalize: list
- :type btn_add: int
- :type btn_remove: int
- :type btn_remove_all: int
- :type href: str
- :returns: New values for web page elements.
- :rtype: tuple
- """
-
- ctrl_panel = self.ControlPanel(cp_data)
- norm = cl_normalize
-
- # Parse the url:
- parsed_url = url_decode(href)
- if parsed_url:
- url_params = parsed_url["params"]
- else:
- url_params = None
-
- 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
-
- trigger_id = callback_context.triggered[0]["prop_id"].split(".")[0]
-
- if trigger_id == "dd-ctrl-dut":
- try:
- options = \
- generate_options(sorted(self.spec_tbs[dd_dut].keys()))
- disabled = False
- except KeyError:
- options = list()
- disabled = True
- ctrl_panel.set({
- "dd-ctrl-dut-value": dd_dut,
- "dd-ctrl-phy-value": str(),
- "dd-ctrl-phy-options": options,
- "dd-ctrl-phy-disabled": disabled,
- "dd-ctrl-area-value": str(),
- "dd-ctrl-area-options": list(),
- "dd-ctrl-area-disabled": True,
- "dd-ctrl-test-value": str(),
- "dd-ctrl-test-options": list(),
- "dd-ctrl-test-disabled": True,
- "cl-ctrl-core-options": list(),
- "cl-ctrl-core-value": list(),
- "cl-ctrl-core-all-value": list(),
- "cl-ctrl-core-all-options": C.CL_ALL_DISABLED,
- "cl-ctrl-framesize-options": list(),
- "cl-ctrl-framesize-value": list(),
- "cl-ctrl-framesize-all-value": list(),
- "cl-ctrl-framesize-all-options": C.CL_ALL_DISABLED,
- "cl-ctrl-testtype-options": list(),
- "cl-ctrl-testtype-value": list(),
- "cl-ctrl-testtype-all-value": list(),
- "cl-ctrl-testtype-all-options": C.CL_ALL_DISABLED,
- })
- elif trigger_id == "dd-ctrl-phy":
- try:
- dut = ctrl_panel.get("dd-ctrl-dut-value")
- phy = self.spec_tbs[dut][dd_phy]
- options = [{"label": label(v), "value": v} \
- for v in sorted(phy.keys())]
- disabled = False
- except KeyError:
- options = list()
- disabled = True
- ctrl_panel.set({
- "dd-ctrl-phy-value": dd_phy,
- "dd-ctrl-area-value": str(),
- "dd-ctrl-area-options": options,
- "dd-ctrl-area-disabled": disabled,
- "dd-ctrl-test-value": str(),
- "dd-ctrl-test-options": list(),
- "dd-ctrl-test-disabled": True,
- "cl-ctrl-core-options": list(),
- "cl-ctrl-core-value": list(),
- "cl-ctrl-core-all-value": list(),
- "cl-ctrl-core-all-options": C.CL_ALL_DISABLED,
- "cl-ctrl-framesize-options": list(),
- "cl-ctrl-framesize-value": list(),
- "cl-ctrl-framesize-all-value": list(),
- "cl-ctrl-framesize-all-options": C.CL_ALL_DISABLED,
- "cl-ctrl-testtype-options": list(),
- "cl-ctrl-testtype-value": list(),
- "cl-ctrl-testtype-all-value": list(),
- "cl-ctrl-testtype-all-options": C.CL_ALL_DISABLED,
- })
- elif trigger_id == "dd-ctrl-area":
- try:
- dut = ctrl_panel.get("dd-ctrl-dut-value")
- phy = ctrl_panel.get("dd-ctrl-phy-value")
- area = self.spec_tbs[dut][phy][dd_area]
- options = generate_options(sorted(area.keys()))
- disabled = False
- except KeyError:
- options = list()
- disabled = True
- ctrl_panel.set({
- "dd-ctrl-area-value": dd_area,
- "dd-ctrl-test-value": str(),
- "dd-ctrl-test-options": options,
- "dd-ctrl-test-disabled": disabled,
- "cl-ctrl-core-options": list(),
- "cl-ctrl-core-value": list(),
- "cl-ctrl-core-all-value": list(),
- "cl-ctrl-core-all-options": C.CL_ALL_DISABLED,
- "cl-ctrl-framesize-options": list(),
- "cl-ctrl-framesize-value": list(),
- "cl-ctrl-framesize-all-value": list(),
- "cl-ctrl-framesize-all-options": C.CL_ALL_DISABLED,
- "cl-ctrl-testtype-options": list(),
- "cl-ctrl-testtype-value": list(),
- "cl-ctrl-testtype-all-value": list(),
- "cl-ctrl-testtype-all-options": C.CL_ALL_DISABLED,
- })
- elif trigger_id == "dd-ctrl-test":
- dut = ctrl_panel.get("dd-ctrl-dut-value")
- phy = ctrl_panel.get("dd-ctrl-phy-value")
- area = ctrl_panel.get("dd-ctrl-area-value")
- if all((dut, phy, area, dd_test, )):
- test = self.spec_tbs[dut][phy][area][dd_test]
- ctrl_panel.set({
- "dd-ctrl-test-value": dd_test,
- "cl-ctrl-core-options": \
- generate_options(sorted(test["core"])),
- "cl-ctrl-core-value": list(),
- "cl-ctrl-core-all-value": list(),
- "cl-ctrl-core-all-options": C.CL_ALL_ENABLED,
- "cl-ctrl-framesize-options": \
- generate_options(sorted(test["frame-size"])),
- "cl-ctrl-framesize-value": list(),
- "cl-ctrl-framesize-all-value": list(),
- "cl-ctrl-framesize-all-options": C.CL_ALL_ENABLED,
- "cl-ctrl-testtype-options": \
- generate_options(sorted(test["test-type"])),
- "cl-ctrl-testtype-value": list(),
- "cl-ctrl-testtype-all-value": list(),
- "cl-ctrl-testtype-all-options": C.CL_ALL_ENABLED,
- })
- elif trigger_id == "cl-ctrl-core":
- val_sel, val_all = sync_checklists(
- options=ctrl_panel.get("cl-ctrl-core-options"),
- sel=cl_core,
- all=list(),
- id=""
- )
- ctrl_panel.set({
- "cl-ctrl-core-value": val_sel,
- "cl-ctrl-core-all-value": val_all,
- })
- elif trigger_id == "cl-ctrl-core-all":
- val_sel, val_all = sync_checklists(
- options = ctrl_panel.get("cl-ctrl-core-options"),
- sel=list(),
- all=cl_core_all,
- id="all"
- )
- ctrl_panel.set({
- "cl-ctrl-core-value": val_sel,
- "cl-ctrl-core-all-value": val_all,
- })
- elif trigger_id == "cl-ctrl-framesize":
- val_sel, val_all = sync_checklists(
- options = ctrl_panel.get("cl-ctrl-framesize-options"),
- sel=cl_framesize,
- all=list(),
- id=""
- )
- ctrl_panel.set({
- "cl-ctrl-framesize-value": val_sel,
- "cl-ctrl-framesize-all-value": val_all,
- })
- elif trigger_id == "cl-ctrl-framesize-all":
- val_sel, val_all = sync_checklists(
- options = ctrl_panel.get("cl-ctrl-framesize-options"),
- sel=list(),
- all=cl_framesize_all,
- id="all"
- )
- ctrl_panel.set({
- "cl-ctrl-framesize-value": val_sel,
- "cl-ctrl-framesize-all-value": val_all,
- })
- elif trigger_id == "cl-ctrl-testtype":
- val_sel, val_all = sync_checklists(
- options = ctrl_panel.get("cl-ctrl-testtype-options"),
- sel=cl_testtype,
- all=list(),
- id=""
- )
- ctrl_panel.set({
- "cl-ctrl-testtype-value": val_sel,
- "cl-ctrl-testtype-all-value": val_all,
- })
- elif trigger_id == "cl-ctrl-testtype-all":
- val_sel, val_all = sync_checklists(
- options = ctrl_panel.get("cl-ctrl-testtype-options"),
- sel=list(),
- all=cl_testtype_all,
- id="all"
- )
- ctrl_panel.set({
- "cl-ctrl-testtype-value": val_sel,
- "cl-ctrl-testtype-all-value": val_all,
- })
- elif trigger_id == "btn-ctrl-add":
- _ = btn_add
- dut = ctrl_panel.get("dd-ctrl-dut-value")
- phy = ctrl_panel.get("dd-ctrl-phy-value")
- area = ctrl_panel.get("dd-ctrl-area-value")
- test = ctrl_panel.get("dd-ctrl-test-value")
- cores = ctrl_panel.get("cl-ctrl-core-value")
- framesizes = ctrl_panel.get("cl-ctrl-framesize-value")
- testtypes = ctrl_panel.get("cl-ctrl-testtype-value")
- # Add selected test to the list of tests in store:
- if all((dut, phy, area, test, cores, framesizes, testtypes)):
- if store_sel is None:
- store_sel = list()
- for core in cores:
- for framesize in framesizes:
- for ttype in testtypes:
- if dut == "trex":
- core = str()
- tid = "-".join((
- dut, phy.replace('af_xdp', 'af-xdp'), area,
- framesize.lower(), core.lower(), test,
- ttype.lower()
- ))
- if tid not in [itm["id"] for itm in store_sel]:
- store_sel.append({
- "id": tid,
- "dut": dut,
- "phy": phy,
- "area": area,
- "test": test,
- "framesize": framesize.lower(),
- "core": core.lower(),
- "testtype": ttype.lower()
- })
- store_sel = sorted(store_sel, key=lambda d: d["id"])
- row_card_sel_tests = C.STYLE_ENABLED
- row_btns_sel_tests = C.STYLE_ENABLED
- if C.CLEAR_ALL_INPUTS:
- ctrl_panel.set(ctrl_panel.defaults)
- elif trigger_id == "btn-sel-remove-all":
- _ = btn_remove_all
- row_fig_tput = C.PLACEHOLDER
- row_fig_lat = C.PLACEHOLDER
- row_btn_dwnld = C.PLACEHOLDER
- row_card_sel_tests = C.STYLE_DISABLED
- row_btns_sel_tests = C.STYLE_DISABLED
- store_sel = list()
- ctrl_panel.set({"cl-selected-options": list()})
- elif trigger_id == "btn-sel-remove":
- _ = btn_remove
- if list_sel:
- new_store_sel = list()
- for item in store_sel:
- if item["id"] not in list_sel:
- new_store_sel.append(item)
- store_sel = new_store_sel
- elif trigger_id == "url":
- if url_params:
- try:
- store_sel = literal_eval(url_params["store_sel"][0])
- norm = literal_eval(url_params["norm"][0])
- except (KeyError, IndexError):
- pass
- if store_sel:
- row_card_sel_tests = C.STYLE_ENABLED
- row_btns_sel_tests = C.STYLE_ENABLED
- last_test = store_sel[-1]
- test = self.spec_tbs[last_test["dut"]]\
- [last_test["phy"]][last_test["area"]]\
- [last_test["test"]]
- ctrl_panel.set({
- "dd-ctrl-dut-value": last_test["dut"],
- "dd-ctrl-phy-value": last_test["phy"],
- "dd-ctrl-phy-options": generate_options(sorted(
- self.spec_tbs[last_test["dut"]].keys())),
- "dd-ctrl-phy-disabled": False,
- "dd-ctrl-area-value": last_test["area"],
- "dd-ctrl-area-options": [
- {"label": label(v), "value": v} \
- for v in sorted(
- self.spec_tbs[last_test["dut"]]\
- [last_test["phy"]].keys())
- ],
- "dd-ctrl-area-disabled": False,
- "dd-ctrl-test-value": last_test["test"],
- "dd-ctrl-test-options": generate_options(sorted(
- self.spec_tbs[last_test["dut"]]\
- [last_test["phy"]]\
- [last_test["area"]].keys())),
- "dd-ctrl-test-disabled": False,
- "cl-ctrl-core-options": generate_options(sorted(
- test["core"])),
- "cl-ctrl-core-value": [last_test["core"].upper(), ],
- "cl-ctrl-core-all-value": list(),
- "cl-ctrl-core-all-options": C.CL_ALL_ENABLED,
- "cl-ctrl-framesize-options": generate_options(
- sorted(test["frame-size"])),
- "cl-ctrl-framesize-value": \
- [last_test["framesize"].upper(), ],
- "cl-ctrl-framesize-all-value": list(),
- "cl-ctrl-framesize-all-options": C.CL_ALL_ENABLED,
- "cl-ctrl-testtype-options": generate_options(sorted(
- test["test-type"])),
- "cl-ctrl-testtype-value": \
- [last_test["testtype"].upper(), ],
- "cl-ctrl-testtype-all-value": list(),
- "cl-ctrl-testtype-all-options": C.CL_ALL_ENABLED
- })
-
- if trigger_id in ("btn-ctrl-add", "url", "btn-sel-remove",
- "cl-ctrl-normalize"):
- if store_sel:
- row_fig_tput, row_fig_lat, row_btn_dwnld = \
- _generate_plotting_area(
- graph_trending(self.data, store_sel, self.layout,
- bool(norm)),
- gen_new_url(
- parsed_url,
- {
- "store_sel": store_sel,
- "norm": norm
- }
- )
- )
- ctrl_panel.set({
- "cl-selected-options": list_tests(store_sel)
- })
- else:
- row_fig_tput = C.PLACEHOLDER
- row_fig_lat = C.PLACEHOLDER
- row_btn_dwnld = C.PLACEHOLDER
- row_card_sel_tests = C.STYLE_DISABLED
- row_btns_sel_tests = C.STYLE_DISABLED
- store_sel = list()
- ctrl_panel.set({"cl-selected-options": list()})
-
- if ctrl_panel.get("cl-ctrl-core-value") and \
- ctrl_panel.get("cl-ctrl-framesize-value") and \
- ctrl_panel.get("cl-ctrl-testtype-value"):
- disabled = False
- else:
- disabled = True
- ctrl_panel.set({
- "btn-ctrl-add-disabled": disabled,
- "cl-normalize-value": norm
- })
-
- 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
-
- @app.callback(
- Output("metadata-tput-lat", "children"),
- Output("metadata-hdrh-graph", "children"),
- Output("offcanvas-metadata", "is_open"),
- Input({"type": "graph", "index": ALL}, "clickData"),
- prevent_initial_call=True
- )
- def _show_metadata_from_graphs(graph_data: dict) -> tuple:
- """Generates the data for the offcanvas displayed when a particular
- point in a graph is clicked on.
-
- :param graph_data: The data from the clicked point in the graph.
- :type graph_data: dict
- :returns: The data to be displayed on the offcanvas and the
- information to show the offcanvas.
- :rtype: tuple(list, list, bool)
- """
- 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()
-
- 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"
- elif trigger_id == "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.layout
- )
- )
- ])
- ])
- ]
- 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
-
- @app.callback(
- Output("download-data", "data"),
- State("selected-tests", "data"),
- Input("btn-download-data", "n_clicks"),
- prevent_initial_call=True
- )
- def _download_data(store_sel, n_clicks):
- """Download the data
-
- :param store_sel: List of tests selected by user stored in the
- browser.
- :param n_clicks: Number of clicks on the button "Download".
- :type store_sel: list
- :type n_clicks: int
- :returns: dict of data frame content (base64 encoded) and meta data
- used by the Download component.
- :rtype: dict
- """
-
- 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)
- if sel_data is None:
- continue
- df = pd.concat([df, sel_data], ignore_index=True)
-
- return dcc.send_data_frame(df.to_csv, C.TREND_DOWNLOAD_FILE_NAME)
diff --git a/resources/tools/dash/app/pal/trending/layout.yaml b/resources/tools/dash/app/pal/trending/layout.yaml
deleted file mode 100644
index 1beb5226b1..0000000000
--- a/resources/tools/dash/app/pal/trending/layout.yaml
+++ /dev/null
@@ -1,129 +0,0 @@
-plot-trending-tput:
- autosize: True
- showlegend: True
- yaxis:
- showticklabels: True
- tickformat: ".3s"
- title: "Throughput [pps]"
- hoverformat: ".5s"
- gridcolor: "rgb(238, 238, 238)"
- linecolor: "rgb(238, 238, 238)"
- showline: True
- zeroline: False
- tickcolor: "rgb(238, 238, 238)"
- linewidth: 1
- showgrid: True
- xaxis:
- title: 'Date [MMDD]'
- type: "date"
- autorange: True
- fixedrange: False
- showgrid: True
- gridcolor: "rgb(238, 238, 238)"
- showline: True
- linecolor: "rgb(238, 238, 238)"
- zeroline: False
- linewidth: 1
- showticklabels: True
- tickcolor: "rgb(238, 238, 238)"
- tickmode: "auto"
- tickformat: "%m%d"
- margin:
- r: 20
- b: 0
- t: 5
- l: 70
- legend:
- orientation: "h"
- y: -0.18
- xanchor: "auto"
- traceorder: "normal"
- bordercolor: "rgb(238, 238, 238)"
- paper_bgcolor: "#fff"
- plot_bgcolor: "#fff"
- hoverlabel:
- namelength: -1
-
-plot-trending-lat:
- autosize: True
- showlegend: True
- yaxis:
- showticklabels: True
- tickformat: ".3s"
- title: "Average Latency at 50% PDR [us]"
- hoverformat: ".5s"
- gridcolor: "rgb(238, 238, 238)"
- linecolor: "rgb(238, 238, 238)"
- showline: True
- zeroline: False
- tickcolor: "rgb(238, 238, 238)"
- linewidth: 1
- showgrid: True
- xaxis:
- title: 'Date [MMDD]'
- type: "date"
- autorange: True
- fixedrange: False
- showgrid: True
- gridcolor: "rgb(238, 238, 238)"
- showline: True
- linecolor: "rgb(238, 238, 238)"
- zeroline: False
- linewidth: 1
- showticklabels: True
- tickcolor: "rgb(238, 238, 238)"
- tickmode: "auto"
- tickformat: "%m%d"
- margin:
- r: 20
- b: 0
- t: 5
- l: 70
- legend:
- orientation: "h"
- y: -0.18
- xanchor: "auto"
- traceorder: "normal"
- bordercolor: "rgb(238, 238, 238)"
- paper_bgcolor: "#fff"
- plot_bgcolor: "#fff"
- hoverlabel:
- namelength: -1
-
-plot-hdrh-latency:
- showlegend: True
- legend:
- traceorder: "normal"
- orientation: "h"
- xanchor: "left"
- yanchor: "top"
- x: 0
- y: -0.25
- bgcolor: "rgba(255, 255, 255, 0)"
- bordercolor: "rgba(255, 255, 255, 0)"
- xaxis:
- type: "log"
- title: "Percentile [%]"
- autorange: False
- fixedrange: True
- gridcolor: "rgb(230, 230, 230)"
- linecolor: "rgb(220, 220, 220)"
- linewidth: 1
- showgrid: True
- showline: True
- showticklabels: True
- tickcolor: "rgb(220, 220, 220)"
- tickvals: [1, 2, 1e1, 20, 1e2, 1e3, 1e4, 1e5, 1e6]
- ticktext: [0, 50, 90, 95, 99, 99.9, 99.99, 99.999, 99.9999]
- yaxis:
- title: "One-Way Latency per Direction [us]"
- gridcolor: "rgb(230, 230, 230)"
- linecolor: "rgb(220, 220, 220)"
- linewidth: 1
- showgrid: True
- showline: True
- showticklabels: True
- tickcolor: "rgb(220, 220, 220)"
- autosize: True
- paper_bgcolor: "white"
- plot_bgcolor: "white"
diff --git a/resources/tools/dash/app/pal/trending/trending.py b/resources/tools/dash/app/pal/trending/trending.py
deleted file mode 100644
index af1dc79722..0000000000
--- a/resources/tools/dash/app/pal/trending/trending.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright (c) 2022 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:
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""Instantiate the Trending Dash application.
-"""
-import dash
-
-from ..utils.constants import Constants as C
-from .layout import Layout
-
-
-def init_trending(server, time_period=None):
- """Create a Plotly Dash dashboard.
-
- :param server: Flask server.
- :type server: Flask
- :returns: Dash app server.
- :rtype: Dash
- """
-
- dash_app = dash.Dash(
- server=server,
- routes_pathname_prefix=C.TREND_ROUTES_PATHNAME_PREFIX,
- external_stylesheets=C.EXTERNAL_STYLESHEETS
- )
-
- layout = Layout(
- app=dash_app,
- html_layout_file=C.TREND_HTML_LAYOUT_FILE,
- graph_layout_file=C.TREND_GRAPH_LAYOUT_FILE,
- data_spec_file=C.DATA_SPEC_FILE,
- tooltip_file=C.TOOLTIP_FILE,
- time_period=time_period
- )
- dash_app.index_string = layout.html_layout
- dash_app.layout = layout.add_content()
-
- return dash_app.server