1 # Copyright (c) 2022 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
14 """Plotly Dash HTML layout override.
19 import dash_bootstrap_components as dbc
21 from flask import Flask
24 from dash import callback_context, no_update, ALL
25 from dash import Input, Output, State
26 from dash.exceptions import PreventUpdate
27 from yaml import load, FullLoader, YAMLError
28 from copy import deepcopy
29 from json import loads, JSONDecodeError
30 from ast import literal_eval
32 from pprint import pformat
34 from ..data.data import Data
35 from ..data.url_processing import url_decode, url_encode
36 from .graphs import graph_iterative, table_comparison, get_short_version
43 # If True, clear all inputs in control panel when button "ADD SELECTED" is
45 CLEAR_ALL_INPUTS = False
47 STYLE_DISABLED = {"display": "none"}
48 STYLE_ENABLED = {"display": "inherit"}
61 PLACEHOLDER = html.Nobr("")
63 DRIVERS = ("avf", "af-xdp", "rdma", "dpdk")
67 "container_memif": "LXC/DRC Container Memif",
68 "crypto": "IPSec IPv4 Routing",
69 "ip4": "IPv4 Routing",
70 "ip6": "IPv6 Routing",
71 "ip4_tunnels": "IPv4 Tunnels",
72 "l2": "L2 Ethernet Switching",
73 "srv6": "SRv6 Routing",
74 "vm_vhost": "VMs vhost-user",
75 "nfv_density-dcr_memif-chain_ipsec": "CNF Service Chains Routing IPSec",
76 "nfv_density-vm_vhost-chain_dot1qip4vxlan":"VNF Service Chains Tunnels",
77 "nfv_density-vm_vhost-chain": "VNF Service Chains Routing",
78 "nfv_density-dcr_memif-pipeline": "CNF Service Pipelines Routing",
79 "nfv_density-dcr_memif-chain": "CNF Service Chains Routing",
83 "background-color": "#d2ebf5",
84 "border-color": "#bce1f1",
88 def __init__(self, app: Flask, releases: list, html_layout_file: str,
89 graph_layout_file: str, data_spec_file: str, tooltip_file: str) -> None:
95 self.releases = releases
96 self._html_layout_file = html_layout_file
97 self._graph_layout_file = graph_layout_file
98 self._data_spec_file = data_spec_file
99 self._tooltip_file = tooltip_file
102 self._data = pd.DataFrame()
104 data_mrr = Data(self._data_spec_file, True).\
105 read_iterative_mrr(release=rls)
106 data_mrr["release"] = rls
107 data_ndrpdr = Data(self._data_spec_file, True).\
108 read_iterative_ndrpdr(release=rls)
109 data_ndrpdr["release"] = rls
110 self._data = pd.concat(
111 [self._data, data_mrr, data_ndrpdr], ignore_index=True)
113 # Get structure of tests:
115 cols = ["job", "test_id", "test_type", "dut_version", "release"]
116 for _, row in self._data[cols].drop_duplicates().iterrows():
118 ttype = row["test_type"]
119 lst_job = row["job"].split("-")
121 d_ver = get_short_version(row["dut_version"], dut)
122 tbed = "-".join(lst_job[-2:])
123 lst_test_id = row["test_id"].split(".")
127 area = "-".join(lst_test_id[3:-2])
128 suite = lst_test_id[-2].replace("2n1l-", "").replace("1n1l-", "").\
130 test = lst_test_id[-1]
131 nic = suite.split("-")[0]
132 for drv in self.DRIVERS:
134 driver = drv.replace("-", "_")
135 test = test.replace(f"{drv}-", "")
139 infra = "-".join((tbed, nic, driver))
140 lst_test = test.split("-")
141 framesize = lst_test[0]
142 core = lst_test[1] if lst_test[1] else "8C"
143 test = "-".join(lst_test[2: -1])
145 if tbs.get(rls, None) is None:
147 if tbs[rls].get(dut, None) is None:
148 tbs[rls][dut] = dict()
149 if tbs[rls][dut].get(d_ver, None) is None:
150 tbs[rls][dut][d_ver] = dict()
151 if tbs[rls][dut][d_ver].get(infra, None) is None:
152 tbs[rls][dut][d_ver][infra] = dict()
153 if tbs[rls][dut][d_ver][infra].get(area, None) is None:
154 tbs[rls][dut][d_ver][infra][area] = dict()
155 if tbs[rls][dut][d_ver][infra][area].get(test, None) is None:
156 tbs[rls][dut][d_ver][infra][area][test] = dict()
157 tbs[rls][dut][d_ver][infra][area][test]["core"] = list()
158 tbs[rls][dut][d_ver][infra][area][test]["frame-size"] = list()
159 tbs[rls][dut][d_ver][infra][area][test]["test-type"] = list()
160 if core.upper() not in \
161 tbs[rls][dut][d_ver][infra][area][test]["core"]:
162 tbs[rls][dut][d_ver][infra][area][test]["core"].append(
164 if framesize.upper() not in \
165 tbs[rls][dut][d_ver][infra][area][test]["frame-size"]:
166 tbs[rls][dut][d_ver][infra][area][test]["frame-size"].append(
170 tbs[rls][dut][d_ver][infra][area][test]["test-type"]:
171 tbs[rls][dut][d_ver][infra][area][test]["test-type"].append(
173 elif ttype == "ndrpdr":
175 tbs[rls][dut][d_ver][infra][area][test]["test-type"]:
176 tbs[rls][dut][d_ver][infra][area][test]["test-type"].extend(
181 self._html_layout = ""
182 self._graph_layout = None
183 self._tooltips = dict()
186 with open(self._html_layout_file, "r") as file_read:
187 self._html_layout = file_read.read()
188 except IOError as err:
190 f"Not possible to open the file {self._html_layout_file}\n{err}"
194 with open(self._graph_layout_file, "r") as file_read:
195 self._graph_layout = load(file_read, Loader=FullLoader)
196 except IOError as err:
198 f"Not possible to open the file {self._graph_layout_file}\n"
201 except YAMLError as err:
203 f"An error occurred while parsing the specification file "
204 f"{self._graph_layout_file}\n{err}"
208 with open(self._tooltip_file, "r") as file_read:
209 self._tooltips = load(file_read, Loader=FullLoader)
210 except IOError as err:
212 f"Not possible to open the file {self._tooltip_file}\n{err}"
214 except YAMLError as err:
216 f"An error occurred while parsing the specification file "
217 f"{self._tooltip_file}\n{err}"
221 if self._app is not None and hasattr(self, 'callbacks'):
222 self.callbacks(self._app)
225 def html_layout(self):
226 return self._html_layout
230 return self._spec_tbs
238 return self._graph_layout
240 def label(self, key: str) -> str:
241 return self.LABELS.get(key, key)
243 def _show_tooltip(self, id: str, title: str,
244 clipboard_id: str=None) -> list:
248 dcc.Clipboard(target_id=clipboard_id, title="Copy URL") \
249 if clipboard_id else str(),
257 class_name="border ms-1",
260 children=self._tooltips.get(id, str()),
266 def add_content(self):
269 if self.html_layout and self.spec_tbs:
283 id="offcanvas-metadata",
284 title="Throughput And Latency",
288 dbc.Row(id="metadata-tput-lat"),
289 dbc.Row(id="metadata-hdrh-graph"),
297 dcc.Store(id="selected-tests"),
298 dcc.Store(id="control-panel"),
299 dcc.Location(id="url", refresh=False),
300 self._add_ctrl_col(),
301 self._add_plotting_col(),
319 def _add_navbar(self):
320 """Add nav element with navigation panel. It is placed on the top.
322 return dbc.NavbarSimple(
323 id="navbarsimple-main",
327 "Iterative Test Runs",
336 brand_external_link=True,
341 def _add_ctrl_col(self) -> dbc.Col:
342 """Add column with controls. It is placed on the left side.
347 self._add_ctrl_panel(),
351 def _add_plotting_col(self) -> dbc.Col:
352 """Add column with plots and tables. It is placed on the right side.
355 id="col-plotting-area",
360 class_name="g-0 p-2",
363 dbc.Row( # Throughput
365 class_name="g-0 p-2",
375 class_name="g-0 p-2",
386 class_name="g-0 p-2",
392 id="row-btn-download",
393 class_name="g-0 p-2",
404 def _add_ctrl_panel(self) -> dbc.Row:
409 class_name="g-0 p-2",
417 children=self._show_tooltip(
418 "help-release", "Release")
422 placeholder=("Select a Release..."),
425 {"label": k, "value": k} \
426 for k in self.spec_tbs.keys()
428 key=lambda d: d["label"]
443 children=self._show_tooltip(
449 "Select a Device under Test..."
464 children=self._show_tooltip(
465 "help-dut-ver", "DUT Version")
470 "Select a Version of "
471 "Device under Test..."
486 children=self._show_tooltip(
487 "help-infra", "Infra")
492 "Select a Physical Test Bed "
508 children=self._show_tooltip(
513 placeholder="Select an Area...",
528 children=self._show_tooltip(
533 placeholder="Select a Test...",
543 id="row-ctrl-framesize",
547 children=self._show_tooltip(
548 "help-framesize", "Frame Size"),
554 id="cl-ctrl-framesize-all",
555 options=self.CL_ALL_DISABLED,
565 id="cl-ctrl-framesize",
578 children=self._show_tooltip(
579 "help-cores", "Number of Cores"),
585 id="cl-ctrl-core-all",
586 options=self.CL_ALL_DISABLED,
605 id="row-ctrl-testtype",
609 children=self._show_tooltip(
610 "help-ttype", "Test Type"),
616 id="cl-ctrl-testtype-all",
617 options=self.CL_ALL_DISABLED,
627 id="cl-ctrl-testtype",
636 class_name="gy-1 p-0",
642 children="Add Selected",
652 id="row-card-sel-tests",
654 style=self.STYLE_DISABLED,
661 class_name="overflow-auto",
665 style={"max-height": "12em"},
670 id="row-btns-sel-tests",
671 style=self.STYLE_DISABLED,
678 children="Remove Selected",
679 class_name="w-100 me-1",
684 id="btn-sel-remove-all",
685 children="Remove All",
686 class_name="w-100 me-1",
699 def __init__(self, panel: dict) -> None:
707 # Defines also the order of keys
709 "dd-rls-value": str(),
710 "dd-dut-options": list(),
711 "dd-dut-disabled": True,
712 "dd-dut-value": str(),
713 "dd-dutver-options": list(),
714 "dd-dutver-disabled": True,
715 "dd-dutver-value": str(),
716 "dd-phy-options": list(),
717 "dd-phy-disabled": True,
718 "dd-phy-value": str(),
719 "dd-area-options": list(),
720 "dd-area-disabled": True,
721 "dd-area-value": str(),
722 "dd-test-options": list(),
723 "dd-test-disabled": True,
724 "dd-test-value": str(),
725 "cl-core-options": list(),
726 "cl-core-value": list(),
727 "cl-core-all-value": list(),
728 "cl-core-all-options": CL_ALL_DISABLED,
729 "cl-framesize-options": list(),
730 "cl-framesize-value": list(),
731 "cl-framesize-all-value": list(),
732 "cl-framesize-all-options": CL_ALL_DISABLED,
733 "cl-testtype-options": list(),
734 "cl-testtype-value": list(),
735 "cl-testtype-all-value": list(),
736 "cl-testtype-all-options": CL_ALL_DISABLED,
737 "btn-add-disabled": True,
738 "cl-selected-options": list()
741 self._panel = deepcopy(self._defaults)
743 for key in self._defaults:
744 self._panel[key] = panel[key]
747 def defaults(self) -> dict:
748 return self._defaults
751 def panel(self) -> dict:
754 def set(self, kwargs: dict) -> None:
755 for key, val in kwargs.items():
756 if key in self._panel:
757 self._panel[key] = val
759 raise KeyError(f"The key {key} is not defined.")
761 def get(self, key: str) -> any:
762 return self._panel[key]
764 def values(self) -> tuple:
765 return tuple(self._panel.values())
768 def _sync_checklists(opt: list, sel: list, all: list, id: str) -> tuple:
771 options = {v["value"] for v in opt}
773 sel = list(options) if all else list()
775 all = ["all", ] if set(sel) == options else list()
779 def _list_tests(selection: dict) -> list:
780 """Display selected tests with checkboxes
783 return [{"label": v["id"], "value": v["id"]} for v in selection]
787 def callbacks(self, app):
789 def _generate_plotting_area(figs: tuple, table: pd.DataFrame,
794 (fig_tput, fig_lat) = figs
796 row_fig_tput = self.PLACEHOLDER
797 row_fig_lat = self.PLACEHOLDER
798 row_table = self.PLACEHOLDER
799 row_btn_dwnld = self.PLACEHOLDER
804 id={"type": "graph", "index": "tput"},
812 dcc.Loading(children=[
814 id="btn-download-data",
815 children=self._show_tooltip(
816 "help-download", "Download Data"),
820 dcc.Download(id="download-data")
831 style=self.URL_STYLE,
832 children=self._show_tooltip(
833 "help-url", "URL", "input-url")
839 style=self.URL_STYLE,
850 id={"type": "graph", "index": "lat"},
856 dbc.Table.from_dataframe(
858 id={"type": "table", "index": "compare"},
865 return row_fig_tput, row_fig_lat, row_table, row_btn_dwnld
868 Output("control-panel", "data"), # Store
869 Output("selected-tests", "data"), # Store
870 Output("row-graph-tput", "children"),
871 Output("row-graph-lat", "children"),
872 Output("row-table", "children"),
873 Output("row-btn-download", "children"),
874 Output("row-card-sel-tests", "style"),
875 Output("row-btns-sel-tests", "style"),
876 Output("dd-ctrl-rls", "value"),
877 Output("dd-ctrl-dut", "options"),
878 Output("dd-ctrl-dut", "disabled"),
879 Output("dd-ctrl-dut", "value"),
880 Output("dd-ctrl-dutver", "options"),
881 Output("dd-ctrl-dutver", "disabled"),
882 Output("dd-ctrl-dutver", "value"),
883 Output("dd-ctrl-phy", "options"),
884 Output("dd-ctrl-phy", "disabled"),
885 Output("dd-ctrl-phy", "value"),
886 Output("dd-ctrl-area", "options"),
887 Output("dd-ctrl-area", "disabled"),
888 Output("dd-ctrl-area", "value"),
889 Output("dd-ctrl-test", "options"),
890 Output("dd-ctrl-test", "disabled"),
891 Output("dd-ctrl-test", "value"),
892 Output("cl-ctrl-core", "options"),
893 Output("cl-ctrl-core", "value"),
894 Output("cl-ctrl-core-all", "value"),
895 Output("cl-ctrl-core-all", "options"),
896 Output("cl-ctrl-framesize", "options"),
897 Output("cl-ctrl-framesize", "value"),
898 Output("cl-ctrl-framesize-all", "value"),
899 Output("cl-ctrl-framesize-all", "options"),
900 Output("cl-ctrl-testtype", "options"),
901 Output("cl-ctrl-testtype", "value"),
902 Output("cl-ctrl-testtype-all", "value"),
903 Output("cl-ctrl-testtype-all", "options"),
904 Output("btn-ctrl-add", "disabled"),
905 Output("cl-selected", "options"), # User selection
906 State("control-panel", "data"), # Store
907 State("selected-tests", "data"), # Store
908 State("cl-selected", "value"), # User selection
909 Input("dd-ctrl-rls", "value"),
910 Input("dd-ctrl-dut", "value"),
911 Input("dd-ctrl-dutver", "value"),
912 Input("dd-ctrl-phy", "value"),
913 Input("dd-ctrl-area", "value"),
914 Input("dd-ctrl-test", "value"),
915 Input("cl-ctrl-core", "value"),
916 Input("cl-ctrl-core-all", "value"),
917 Input("cl-ctrl-framesize", "value"),
918 Input("cl-ctrl-framesize-all", "value"),
919 Input("cl-ctrl-testtype", "value"),
920 Input("cl-ctrl-testtype-all", "value"),
921 Input("btn-ctrl-add", "n_clicks"),
922 Input("btn-sel-remove", "n_clicks"),
923 Input("btn-sel-remove-all", "n_clicks"),
926 def _update_ctrl_panel(cp_data: dict, store_sel: list, list_sel: list,
927 dd_rls: str, dd_dut: str, dd_dutver: str, dd_phy: str, dd_area: str,
928 dd_test: str, cl_core: list, cl_core_all: list, cl_framesize: list,
929 cl_framesize_all: list, cl_testtype: list, cl_testtype_all: list,
930 btn_add: int, btn_remove: int, btn_remove_all: int,
935 def _gen_new_url(parsed_url: dict, store_sel: list) -> str:
938 new_url = url_encode({
939 "scheme": parsed_url["scheme"],
940 "netloc": parsed_url["netloc"],
941 "path": parsed_url["path"],
943 "store_sel": store_sel,
951 ctrl_panel = self.ControlPanel(cp_data)
954 parsed_url = url_decode(href)
956 row_fig_tput = no_update
957 row_fig_lat = no_update
958 row_table = no_update
959 row_btn_dwnld = no_update
960 row_card_sel_tests = no_update
961 row_btns_sel_tests = no_update
963 trigger_id = callback_context.triggered[0]["prop_id"].split(".")[0]
965 if trigger_id == "dd-ctrl-rls":
967 rls = self.spec_tbs[dd_rls]
969 [{"label": v, "value": v} for v in rls.keys()],
970 key=lambda d: d["label"]
977 "dd-rls-value": dd_rls,
978 "dd-dut-value": str(),
979 "dd-dut-options": options,
980 "dd-dut-disabled": disabled,
981 "dd-dutver-value": str(),
982 "dd-dutver-options": list(),
983 "dd-dutver-disabled": True,
984 "dd-phy-value": str(),
985 "dd-phy-options": list(),
986 "dd-phy-disabled": True,
987 "dd-area-value": str(),
988 "dd-area-options": list(),
989 "dd-area-disabled": True,
990 "dd-test-value": str(),
991 "dd-test-options": list(),
992 "dd-test-disabled": True,
993 "cl-core-options": list(),
994 "cl-core-value": list(),
995 "cl-core-all-value": list(),
996 "cl-core-all-options": self.CL_ALL_DISABLED,
997 "cl-framesize-options": list(),
998 "cl-framesize-value": list(),
999 "cl-framesize-all-value": list(),
1000 "cl-framesize-all-options": self.CL_ALL_DISABLED,
1001 "cl-testtype-options": list(),
1002 "cl-testtype-value": list(),
1003 "cl-testtype-all-value": list(),
1004 "cl-testtype-all-options": self.CL_ALL_DISABLED
1006 if trigger_id == "dd-ctrl-dut":
1008 rls = ctrl_panel.get("dd-rls-value")
1009 dut = self.spec_tbs[rls][dd_dut]
1011 [{"label": v, "value": v} for v in dut.keys()],
1012 key=lambda d: d["label"]
1019 "dd-dut-value": dd_dut,
1020 "dd-dutver-value": str(),
1021 "dd-dutver-options": options,
1022 "dd-dutver-disabled": disabled,
1023 "dd-phy-value": str(),
1024 "dd-phy-options": list(),
1025 "dd-phy-disabled": True,
1026 "dd-area-value": str(),
1027 "dd-area-options": list(),
1028 "dd-area-disabled": True,
1029 "dd-test-value": str(),
1030 "dd-test-options": list(),
1031 "dd-test-disabled": True,
1032 "cl-core-options": list(),
1033 "cl-core-value": list(),
1034 "cl-core-all-value": list(),
1035 "cl-core-all-options": self.CL_ALL_DISABLED,
1036 "cl-framesize-options": list(),
1037 "cl-framesize-value": list(),
1038 "cl-framesize-all-value": list(),
1039 "cl-framesize-all-options": self.CL_ALL_DISABLED,
1040 "cl-testtype-options": list(),
1041 "cl-testtype-value": list(),
1042 "cl-testtype-all-value": list(),
1043 "cl-testtype-all-options": self.CL_ALL_DISABLED
1045 elif trigger_id == "dd-ctrl-dutver":
1047 rls = ctrl_panel.get("dd-rls-value")
1048 dut = ctrl_panel.get("dd-dut-value")
1049 dutver = self.spec_tbs[rls][dut][dd_dutver]
1051 [{"label": v, "value": v} for v in dutver.keys()],
1052 key=lambda d: d["label"]
1059 "dd-dutver-value": dd_dutver,
1060 "dd-phy-value": str(),
1061 "dd-phy-options": options,
1062 "dd-phy-disabled": disabled,
1063 "dd-area-value": str(),
1064 "dd-area-options": list(),
1065 "dd-area-disabled": True,
1066 "dd-test-value": str(),
1067 "dd-test-options": list(),
1068 "dd-test-disabled": True,
1069 "cl-core-options": list(),
1070 "cl-core-value": list(),
1071 "cl-core-all-value": list(),
1072 "cl-core-all-options": self.CL_ALL_DISABLED,
1073 "cl-framesize-options": list(),
1074 "cl-framesize-value": list(),
1075 "cl-framesize-all-value": list(),
1076 "cl-framesize-all-options": self.CL_ALL_DISABLED,
1077 "cl-testtype-options": list(),
1078 "cl-testtype-value": list(),
1079 "cl-testtype-all-value": list(),
1080 "cl-testtype-all-options": self.CL_ALL_DISABLED
1082 elif trigger_id == "dd-ctrl-phy":
1084 rls = ctrl_panel.get("dd-rls-value")
1085 dut = ctrl_panel.get("dd-dut-value")
1086 dutver = ctrl_panel.get("dd-dutver-value")
1087 phy = self.spec_tbs[rls][dut][dutver][dd_phy]
1089 [{"label": self.label(v), "value": v}
1090 for v in phy.keys()],
1091 key=lambda d: d["label"]
1098 "dd-phy-value": dd_phy,
1099 "dd-area-value": str(),
1100 "dd-area-options": options,
1101 "dd-area-disabled": disabled,
1102 "dd-test-value": str(),
1103 "dd-test-options": list(),
1104 "dd-test-disabled": True,
1105 "cl-core-options": list(),
1106 "cl-core-value": list(),
1107 "cl-core-all-value": list(),
1108 "cl-core-all-options": self.CL_ALL_DISABLED,
1109 "cl-framesize-options": list(),
1110 "cl-framesize-value": list(),
1111 "cl-framesize-all-value": list(),
1112 "cl-framesize-all-options": self.CL_ALL_DISABLED,
1113 "cl-testtype-options": list(),
1114 "cl-testtype-value": list(),
1115 "cl-testtype-all-value": list(),
1116 "cl-testtype-all-options": self.CL_ALL_DISABLED
1118 elif trigger_id == "dd-ctrl-area":
1120 rls = ctrl_panel.get("dd-rls-value")
1121 dut = ctrl_panel.get("dd-dut-value")
1122 dutver = ctrl_panel.get("dd-dutver-value")
1123 phy = ctrl_panel.get("dd-phy-value")
1124 area = self.spec_tbs[rls][dut][dutver][phy][dd_area]
1126 [{"label": v, "value": v} for v in area.keys()],
1127 key=lambda d: d["label"]
1134 "dd-area-value": dd_area,
1135 "dd-test-value": str(),
1136 "dd-test-options": options,
1137 "dd-test-disabled": disabled,
1138 "cl-core-options": list(),
1139 "cl-core-value": list(),
1140 "cl-core-all-value": list(),
1141 "cl-core-all-options": self.CL_ALL_DISABLED,
1142 "cl-framesize-options": list(),
1143 "cl-framesize-value": list(),
1144 "cl-framesize-all-value": list(),
1145 "cl-framesize-all-options": self.CL_ALL_DISABLED,
1146 "cl-testtype-options": list(),
1147 "cl-testtype-value": list(),
1148 "cl-testtype-all-value": list(),
1149 "cl-testtype-all-options": self.CL_ALL_DISABLED
1151 elif trigger_id == "dd-ctrl-test":
1152 rls = ctrl_panel.get("dd-rls-value")
1153 dut = ctrl_panel.get("dd-dut-value")
1154 dutver = ctrl_panel.get("dd-dutver-value")
1155 phy = ctrl_panel.get("dd-phy-value")
1156 area = ctrl_panel.get("dd-area-value")
1157 test = self.spec_tbs[rls][dut][dutver][phy][area][dd_test]
1158 if dut and phy and area and dd_test:
1160 "dd-test-value": dd_test,
1161 "cl-core-options": [{"label": v, "value": v}
1162 for v in sorted(test["core"])],
1163 "cl-core-value": list(),
1164 "cl-core-all-value": list(),
1165 "cl-core-all-options": self.CL_ALL_ENABLED,
1166 "cl-framesize-options": [{"label": v, "value": v}
1167 for v in sorted(test["frame-size"])],
1168 "cl-framesize-value": list(),
1169 "cl-framesize-all-value": list(),
1170 "cl-framesize-all-options": self.CL_ALL_ENABLED,
1171 "cl-testtype-options": [{"label": v, "value": v}
1172 for v in sorted(test["test-type"])],
1173 "cl-testtype-value": list(),
1174 "cl-testtype-all-value": list(),
1175 "cl-testtype-all-options": self.CL_ALL_ENABLED,
1177 elif trigger_id == "cl-ctrl-core":
1178 val_sel, val_all = self._sync_checklists(
1179 opt=ctrl_panel.get("cl-core-options"),
1185 "cl-core-value": val_sel,
1186 "cl-core-all-value": val_all,
1188 elif trigger_id == "cl-ctrl-core-all":
1189 val_sel, val_all = self._sync_checklists(
1190 opt = ctrl_panel.get("cl-core-options"),
1196 "cl-core-value": val_sel,
1197 "cl-core-all-value": val_all,
1199 elif trigger_id == "cl-ctrl-framesize":
1200 val_sel, val_all = self._sync_checklists(
1201 opt = ctrl_panel.get("cl-framesize-options"),
1207 "cl-framesize-value": val_sel,
1208 "cl-framesize-all-value": val_all,
1210 elif trigger_id == "cl-ctrl-framesize-all":
1211 val_sel, val_all = self._sync_checklists(
1212 opt = ctrl_panel.get("cl-framesize-options"),
1214 all=cl_framesize_all,
1218 "cl-framesize-value": val_sel,
1219 "cl-framesize-all-value": val_all,
1221 elif trigger_id == "cl-ctrl-testtype":
1222 val_sel, val_all = self._sync_checklists(
1223 opt = ctrl_panel.get("cl-testtype-options"),
1229 "cl-testtype-value": val_sel,
1230 "cl-testtype-all-value": val_all,
1232 elif trigger_id == "cl-ctrl-testtype-all":
1233 val_sel, val_all = self._sync_checklists(
1234 opt = ctrl_panel.get("cl-testtype-options"),
1236 all=cl_testtype_all,
1240 "cl-testtype-value": val_sel,
1241 "cl-testtype-all-value": val_all,
1243 elif trigger_id == "btn-ctrl-add":
1245 rls = ctrl_panel.get("dd-rls-value")
1246 dut = ctrl_panel.get("dd-dut-value")
1247 dutver = ctrl_panel.get("dd-dutver-value")
1248 phy = ctrl_panel.get("dd-phy-value")
1249 area = ctrl_panel.get("dd-area-value")
1250 test = ctrl_panel.get("dd-test-value")
1251 cores = ctrl_panel.get("cl-core-value")
1252 framesizes = ctrl_panel.get("cl-framesize-value")
1253 testtypes = ctrl_panel.get("cl-testtype-value")
1254 # Add selected test to the list of tests in store:
1255 if all((rls, dut, dutver, phy, area, test, cores, framesizes,
1257 if store_sel is None:
1260 for framesize in framesizes:
1261 for ttype in testtypes:
1264 tid = "-".join((rls, dut, dutver,
1265 phy.replace('af_xdp', 'af-xdp'), area,
1266 framesize.lower(), core.lower(), test,
1268 if tid not in [itm["id"] for itm in store_sel]:
1277 "framesize": framesize.lower(),
1278 "core": core.lower(),
1279 "testtype": ttype.lower()
1281 store_sel = sorted(store_sel, key=lambda d: d["id"])
1282 row_card_sel_tests = self.STYLE_ENABLED
1283 row_btns_sel_tests = self.STYLE_ENABLED
1284 if self.CLEAR_ALL_INPUTS:
1285 ctrl_panel.set(ctrl_panel.defaults)
1287 "cl-selected-options": self._list_tests(store_sel)
1289 row_fig_tput, row_fig_lat, row_table, row_btn_dwnld = \
1290 _generate_plotting_area(
1291 graph_iterative(self.data, store_sel, self.layout),
1292 table_comparison(self.data, store_sel),
1293 _gen_new_url(parsed_url, store_sel)
1295 elif trigger_id == "btn-sel-remove-all":
1297 row_fig_tput = self.PLACEHOLDER
1298 row_fig_lat = self.PLACEHOLDER
1299 row_table = self.PLACEHOLDER
1300 row_btn_dwnld = self.PLACEHOLDER
1301 row_card_sel_tests = self.STYLE_DISABLED
1302 row_btns_sel_tests = self.STYLE_DISABLED
1304 ctrl_panel.set({"cl-selected-options": list()})
1305 elif trigger_id == "btn-sel-remove":
1308 new_store_sel = list()
1309 for item in store_sel:
1310 if item["id"] not in list_sel:
1311 new_store_sel.append(item)
1312 store_sel = new_store_sel
1314 row_fig_tput, row_fig_lat, row_table, row_btn_dwnld = \
1315 _generate_plotting_area(
1316 graph_iterative(self.data, store_sel, self.layout),
1317 table_comparison(self.data, store_sel),
1318 _gen_new_url(parsed_url, store_sel)
1321 "cl-selected-options": self._list_tests(store_sel)
1324 row_fig_tput = self.PLACEHOLDER
1325 row_fig_lat = self.PLACEHOLDER
1326 row_table = self.PLACEHOLDER
1327 row_btn_dwnld = self.PLACEHOLDER
1328 row_card_sel_tests = self.STYLE_DISABLED
1329 row_btns_sel_tests = self.STYLE_DISABLED
1331 ctrl_panel.set({"cl-selected-options": list()})
1332 elif trigger_id == "url":
1333 # TODO: Add verification
1334 url_params = parsed_url["params"]
1336 store_sel = literal_eval(
1337 url_params.get("store_sel", list())[0])
1339 row_fig_tput, row_fig_lat, row_table, row_btn_dwnld = \
1340 _generate_plotting_area(
1341 graph_iterative(self.data, store_sel,
1343 table_comparison(self.data, store_sel),
1344 _gen_new_url(parsed_url, store_sel)
1346 row_card_sel_tests = self.STYLE_ENABLED
1347 row_btns_sel_tests = self.STYLE_ENABLED
1349 "cl-selected-options": self._list_tests(store_sel)
1352 row_fig_tput = self.PLACEHOLDER
1353 row_fig_lat = self.PLACEHOLDER
1354 row_table = self.PLACEHOLDER
1355 row_btn_dwnld = self.PLACEHOLDER
1356 row_card_sel_tests = self.STYLE_DISABLED
1357 row_btns_sel_tests = self.STYLE_DISABLED
1359 ctrl_panel.set({"cl-selected-options": list()})
1361 if ctrl_panel.get("cl-core-value") and \
1362 ctrl_panel.get("cl-framesize-value") and \
1363 ctrl_panel.get("cl-testtype-value"):
1367 ctrl_panel.set({"btn-add-disabled": disabled})
1370 ctrl_panel.panel, store_sel,
1371 row_fig_tput, row_fig_lat, row_table, row_btn_dwnld,
1372 row_card_sel_tests, row_btns_sel_tests
1374 ret_val.extend(ctrl_panel.values())
1378 # Output("metadata-tput-lat", "children"),
1379 # Output("metadata-hdrh-graph", "children"),
1380 # Output("offcanvas-metadata", "is_open"),
1381 # Input({"type": "graph", "index": ALL}, "clickData"),
1382 # prevent_initial_call=True
1384 # def _show_metadata_from_graphs(graph_data: dict) -> tuple:
1388 # trigger_id = loads(
1389 # callback_context.triggered[0]["prop_id"].split(".")[0]
1391 # idx = 0 if trigger_id == "tput" else 1
1392 # graph_data = graph_data[idx]["points"][0]
1393 # except (JSONDecodeError, IndexError, KeyError, ValueError,
1395 # raise PreventUpdate
1397 # metadata = no_update
1401 # dbc.ListGroupItem(
1402 # [dbc.Badge(x.split(":")[0]), x.split(": ")[1]]
1403 # ) for x in graph_data.get("text", "").split("<br>")
1405 # if trigger_id == "tput":
1406 # title = "Throughput"
1407 # elif trigger_id == "lat":
1409 # hdrh_data = graph_data.get("customdata", None)
1411 # graph = [dbc.Card(
1412 # class_name="gy-2 p-0",
1414 # dbc.CardHeader(hdrh_data.pop("name")),
1415 # dbc.CardBody(children=[
1417 # id="hdrh-latency-graph",
1418 # figure=graph_hdrh_latency(
1419 # hdrh_data, self.layout
1427 # class_name="gy-2 p-0",
1429 # dbc.CardHeader(children=[
1431 # target_id="tput-lat-metadata",
1433 # style={"display": "inline-block"}
1438 # id="tput-lat-metadata",
1440 # children=[dbc.ListGroup(children, flush=True), ]
1446 # return metadata, graph, True
1449 # Output("download-data", "data"),
1450 # State("selected-tests", "data"),
1451 # Input("btn-download-data", "n_clicks"),
1452 # prevent_initial_call=True
1454 # def _download_data(store_sel, n_clicks):
1459 # raise PreventUpdate
1462 # raise PreventUpdate
1464 # df = pd.DataFrame()
1465 # for itm in store_sel:
1466 # sel_data = select_trending_data(self.data, itm)
1467 # if sel_data is None:
1469 # df = pd.concat([df, sel_data], ignore_index=True)
1471 # return dcc.send_data_frame(df.to_csv, "trending_data.csv")