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
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 d_ver = row["dut_version"]
120 lst_job = row["job"].split("-")
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:
138 test = test.replace(f"{drv}-", "")
142 infra = "-".join((tbed, nic, driver))
143 lst_test = test.split("-")
144 framesize = lst_test[0]
145 core = lst_test[1] if lst_test[1] else "8C"
146 test = "-".join(lst_test[2: -1])
148 if tbs.get(rls, None) is None:
150 if tbs[rls].get(dut, None) is None:
151 tbs[rls][dut] = dict()
152 if tbs[rls][dut].get(d_ver, None) is None:
153 tbs[rls][dut][d_ver] = dict()
154 if tbs[rls][dut][d_ver].get(infra, None) is None:
155 tbs[rls][dut][d_ver][infra] = dict()
156 if tbs[rls][dut][d_ver][infra].get(area, None) is None:
157 tbs[rls][dut][d_ver][infra][area] = dict()
158 if tbs[rls][dut][d_ver][infra][area].get(test, None) is None:
159 tbs[rls][dut][d_ver][infra][area][test] = dict()
160 tbs_test = tbs[rls][dut][d_ver][infra][area][test]
161 tbs_test["core"] = list()
162 tbs_test["frame-size"] = list()
163 tbs_test["test-type"] = list()
164 if core.upper() not in tbs_test["core"]:
165 tbs_test["core"].append(core.upper())
166 if framesize.upper() not in tbs_test["frame-size"]:
167 tbs_test["frame-size"].append(framesize.upper())
169 if "MRR" not in tbs_test["test-type"]:
170 tbs_test["test-type"].append("MRR")
171 elif ttype == "ndrpdr":
172 if "NDR" not in tbs_test["test-type"]:
173 tbs_test["test-type"].extend(("NDR", "PDR", ))
177 self._html_layout = ""
178 self._graph_layout = None
179 self._tooltips = dict()
182 with open(self._html_layout_file, "r") as file_read:
183 self._html_layout = file_read.read()
184 except IOError as err:
186 f"Not possible to open the file {self._html_layout_file}\n{err}"
190 with open(self._graph_layout_file, "r") as file_read:
191 self._graph_layout = load(file_read, Loader=FullLoader)
192 except IOError as err:
194 f"Not possible to open the file {self._graph_layout_file}\n"
197 except YAMLError as err:
199 f"An error occurred while parsing the specification file "
200 f"{self._graph_layout_file}\n{err}"
204 with open(self._tooltip_file, "r") as file_read:
205 self._tooltips = load(file_read, Loader=FullLoader)
206 except IOError as err:
208 f"Not possible to open the file {self._tooltip_file}\n{err}"
210 except YAMLError as err:
212 f"An error occurred while parsing the specification file "
213 f"{self._tooltip_file}\n{err}"
217 if self._app is not None and hasattr(self, 'callbacks'):
218 self.callbacks(self._app)
221 def html_layout(self):
222 return self._html_layout
226 return self._spec_tbs
234 return self._graph_layout
236 def label(self, key: str) -> str:
237 return self.LABELS.get(key, key)
239 def _show_tooltip(self, id: str, title: str,
240 clipboard_id: str=None) -> list:
244 dcc.Clipboard(target_id=clipboard_id, title="Copy URL") \
245 if clipboard_id else str(),
253 class_name="border ms-1",
256 children=self._tooltips.get(id, str()),
262 def add_content(self):
265 if self.html_layout and self.spec_tbs:
279 id="offcanvas-metadata",
280 title="Throughput And Latency",
284 dbc.Row(id="metadata-tput-lat"),
285 dbc.Row(id="metadata-hdrh-graph"),
293 dcc.Store(id="selected-tests"),
294 dcc.Store(id="control-panel"),
295 dcc.Location(id="url", refresh=False),
296 self._add_ctrl_col(),
297 self._add_plotting_col(),
315 def _add_navbar(self):
316 """Add nav element with navigation panel. It is placed on the top.
318 return dbc.NavbarSimple(
319 id="navbarsimple-main",
323 "Iterative Test Runs",
332 brand_external_link=True,
337 def _add_ctrl_col(self) -> dbc.Col:
338 """Add column with controls. It is placed on the left side.
343 self._add_ctrl_panel(),
347 def _add_plotting_col(self) -> dbc.Col:
348 """Add column with plots and tables. It is placed on the right side.
351 id="col-plotting-area",
356 class_name="g-0 p-2",
359 dbc.Row( # Throughput
361 class_name="g-0 p-2",
371 class_name="g-0 p-2",
382 class_name="g-0 p-2",
388 id="row-btn-download",
389 class_name="g-0 p-2",
400 def _add_ctrl_panel(self) -> dbc.Row:
405 class_name="g-0 p-2",
413 children=self._show_tooltip(
414 "help-release", "Release")
418 placeholder=("Select a Release..."),
421 {"label": k, "value": k} \
422 for k in self.spec_tbs.keys()
424 key=lambda d: d["label"]
439 children=self._show_tooltip(
445 "Select a Device under Test..."
460 children=self._show_tooltip(
461 "help-dut-ver", "DUT Version")
466 "Select a Version of "
467 "Device under Test..."
482 children=self._show_tooltip(
483 "help-infra", "Infra")
488 "Select a Physical Test Bed "
504 children=self._show_tooltip(
509 placeholder="Select an Area...",
524 children=self._show_tooltip(
529 placeholder="Select a Test...",
539 id="row-ctrl-framesize",
543 children=self._show_tooltip(
544 "help-framesize", "Frame Size"),
550 id="cl-ctrl-framesize-all",
551 options=self.CL_ALL_DISABLED,
561 id="cl-ctrl-framesize",
574 children=self._show_tooltip(
575 "help-cores", "Number of Cores"),
581 id="cl-ctrl-core-all",
582 options=self.CL_ALL_DISABLED,
601 id="row-ctrl-testtype",
605 children=self._show_tooltip(
606 "help-ttype", "Test Type"),
612 id="cl-ctrl-testtype-all",
613 options=self.CL_ALL_DISABLED,
623 id="cl-ctrl-testtype",
632 class_name="gy-1 p-0",
638 children="Add Selected",
648 id="row-card-sel-tests",
650 style=self.STYLE_DISABLED,
657 class_name="overflow-auto",
661 style={"max-height": "12em"},
666 id="row-btns-sel-tests",
667 style=self.STYLE_DISABLED,
674 children="Remove Selected",
675 class_name="w-100 me-1",
680 id="btn-sel-remove-all",
681 children="Remove All",
682 class_name="w-100 me-1",
695 def __init__(self, panel: dict) -> None:
703 # Defines also the order of keys
705 "dd-rls-value": str(),
706 "dd-dut-options": list(),
707 "dd-dut-disabled": True,
708 "dd-dut-value": str(),
709 "dd-dutver-options": list(),
710 "dd-dutver-disabled": True,
711 "dd-dutver-value": str(),
712 "dd-phy-options": list(),
713 "dd-phy-disabled": True,
714 "dd-phy-value": str(),
715 "dd-area-options": list(),
716 "dd-area-disabled": True,
717 "dd-area-value": str(),
718 "dd-test-options": list(),
719 "dd-test-disabled": True,
720 "dd-test-value": str(),
721 "cl-core-options": list(),
722 "cl-core-value": list(),
723 "cl-core-all-value": list(),
724 "cl-core-all-options": CL_ALL_DISABLED,
725 "cl-framesize-options": list(),
726 "cl-framesize-value": list(),
727 "cl-framesize-all-value": list(),
728 "cl-framesize-all-options": CL_ALL_DISABLED,
729 "cl-testtype-options": list(),
730 "cl-testtype-value": list(),
731 "cl-testtype-all-value": list(),
732 "cl-testtype-all-options": CL_ALL_DISABLED,
733 "btn-add-disabled": True,
734 "cl-selected-options": list()
737 self._panel = deepcopy(self._defaults)
739 for key in self._defaults:
740 self._panel[key] = panel[key]
743 def defaults(self) -> dict:
744 return self._defaults
747 def panel(self) -> dict:
750 def set(self, kwargs: dict) -> None:
751 for key, val in kwargs.items():
752 if key in self._panel:
753 self._panel[key] = val
755 raise KeyError(f"The key {key} is not defined.")
757 def get(self, key: str) -> any:
758 return self._panel[key]
760 def values(self) -> tuple:
761 return tuple(self._panel.values())
764 def _sync_checklists(opt: list, sel: list, all: list, id: str) -> tuple:
767 options = {v["value"] for v in opt}
769 sel = list(options) if all else list()
771 all = ["all", ] if set(sel) == options else list()
775 def _list_tests(selection: dict) -> list:
776 """Display selected tests with checkboxes
779 return [{"label": v["id"], "value": v["id"]} for v in selection]
783 def callbacks(self, app):
785 def _generate_plotting_area(figs: tuple, table: pd.DataFrame,
790 (fig_tput, fig_tsa) = figs
792 row_fig_tput = self.PLACEHOLDER
793 row_fig_tsa = self.PLACEHOLDER
794 row_table = self.PLACEHOLDER
795 row_btn_dwnld = self.PLACEHOLDER
800 id={"type": "graph", "index": "tput"},
808 dcc.Loading(children=[
810 id="btn-download-data",
811 children=self._show_tooltip(
812 "help-download", "Download Data"),
816 dcc.Download(id="download-data")
827 style=self.URL_STYLE,
828 children=self._show_tooltip(
829 "help-url", "URL", "input-url")
835 style=self.URL_STYLE,
846 id={"type": "graph", "index": "lat"},
852 dbc.Table.from_dataframe(
854 id={"type": "table", "index": "compare"},
861 return row_fig_tput, row_fig_tsa, row_table, row_btn_dwnld
864 Output("control-panel", "data"), # Store
865 Output("selected-tests", "data"), # Store
866 Output("row-graph-tput", "children"),
867 Output("row-graph-tsa", "children"),
868 Output("row-table", "children"),
869 Output("row-btn-download", "children"),
870 Output("row-card-sel-tests", "style"),
871 Output("row-btns-sel-tests", "style"),
872 Output("dd-ctrl-rls", "value"),
873 Output("dd-ctrl-dut", "options"),
874 Output("dd-ctrl-dut", "disabled"),
875 Output("dd-ctrl-dut", "value"),
876 Output("dd-ctrl-dutver", "options"),
877 Output("dd-ctrl-dutver", "disabled"),
878 Output("dd-ctrl-dutver", "value"),
879 Output("dd-ctrl-phy", "options"),
880 Output("dd-ctrl-phy", "disabled"),
881 Output("dd-ctrl-phy", "value"),
882 Output("dd-ctrl-area", "options"),
883 Output("dd-ctrl-area", "disabled"),
884 Output("dd-ctrl-area", "value"),
885 Output("dd-ctrl-test", "options"),
886 Output("dd-ctrl-test", "disabled"),
887 Output("dd-ctrl-test", "value"),
888 Output("cl-ctrl-core", "options"),
889 Output("cl-ctrl-core", "value"),
890 Output("cl-ctrl-core-all", "value"),
891 Output("cl-ctrl-core-all", "options"),
892 Output("cl-ctrl-framesize", "options"),
893 Output("cl-ctrl-framesize", "value"),
894 Output("cl-ctrl-framesize-all", "value"),
895 Output("cl-ctrl-framesize-all", "options"),
896 Output("cl-ctrl-testtype", "options"),
897 Output("cl-ctrl-testtype", "value"),
898 Output("cl-ctrl-testtype-all", "value"),
899 Output("cl-ctrl-testtype-all", "options"),
900 Output("btn-ctrl-add", "disabled"),
901 Output("cl-selected", "options"), # User selection
902 State("control-panel", "data"), # Store
903 State("selected-tests", "data"), # Store
904 State("cl-selected", "value"), # User selection
905 Input("dd-ctrl-rls", "value"),
906 Input("dd-ctrl-dut", "value"),
907 Input("dd-ctrl-dutver", "value"),
908 Input("dd-ctrl-phy", "value"),
909 Input("dd-ctrl-area", "value"),
910 Input("dd-ctrl-test", "value"),
911 Input("cl-ctrl-core", "value"),
912 Input("cl-ctrl-core-all", "value"),
913 Input("cl-ctrl-framesize", "value"),
914 Input("cl-ctrl-framesize-all", "value"),
915 Input("cl-ctrl-testtype", "value"),
916 Input("cl-ctrl-testtype-all", "value"),
917 Input("btn-ctrl-add", "n_clicks"),
918 Input("btn-sel-remove", "n_clicks"),
919 Input("btn-sel-remove-all", "n_clicks"),
922 def _update_ctrl_panel(cp_data: dict, store_sel: list, list_sel: list,
923 dd_rls: str, dd_dut: str, dd_dutver: str, dd_phy: str, dd_area: str,
924 dd_test: str, cl_core: list, cl_core_all: list, cl_framesize: list,
925 cl_framesize_all: list, cl_testtype: list, cl_testtype_all: list,
926 btn_add: int, btn_remove: int, btn_remove_all: int,
931 def _gen_new_url(parsed_url: dict, store_sel: list) -> str:
934 new_url = url_encode({
935 "scheme": parsed_url["scheme"],
936 "netloc": parsed_url["netloc"],
937 "path": parsed_url["path"],
939 "store_sel": store_sel,
947 ctrl_panel = self.ControlPanel(cp_data)
950 parsed_url = url_decode(href)
952 row_fig_tput = no_update
953 row_fig_tsa = no_update
954 row_table = no_update
955 row_btn_dwnld = no_update
956 row_card_sel_tests = no_update
957 row_btns_sel_tests = no_update
959 trigger_id = callback_context.triggered[0]["prop_id"].split(".")[0]
961 if trigger_id == "dd-ctrl-rls":
963 rls = self.spec_tbs[dd_rls]
965 [{"label": v, "value": v} for v in rls.keys()],
966 key=lambda d: d["label"]
973 "dd-rls-value": dd_rls,
974 "dd-dut-value": str(),
975 "dd-dut-options": options,
976 "dd-dut-disabled": disabled,
977 "dd-dutver-value": str(),
978 "dd-dutver-options": list(),
979 "dd-dutver-disabled": True,
980 "dd-phy-value": str(),
981 "dd-phy-options": list(),
982 "dd-phy-disabled": True,
983 "dd-area-value": str(),
984 "dd-area-options": list(),
985 "dd-area-disabled": True,
986 "dd-test-value": str(),
987 "dd-test-options": list(),
988 "dd-test-disabled": True,
989 "cl-core-options": list(),
990 "cl-core-value": list(),
991 "cl-core-all-value": list(),
992 "cl-core-all-options": self.CL_ALL_DISABLED,
993 "cl-framesize-options": list(),
994 "cl-framesize-value": list(),
995 "cl-framesize-all-value": list(),
996 "cl-framesize-all-options": self.CL_ALL_DISABLED,
997 "cl-testtype-options": list(),
998 "cl-testtype-value": list(),
999 "cl-testtype-all-value": list(),
1000 "cl-testtype-all-options": self.CL_ALL_DISABLED
1002 if trigger_id == "dd-ctrl-dut":
1004 rls = ctrl_panel.get("dd-rls-value")
1005 dut = self.spec_tbs[rls][dd_dut]
1007 [{"label": v, "value": v} for v in dut.keys()],
1008 key=lambda d: d["label"]
1015 "dd-dut-value": dd_dut,
1016 "dd-dutver-value": str(),
1017 "dd-dutver-options": options,
1018 "dd-dutver-disabled": disabled,
1019 "dd-phy-value": str(),
1020 "dd-phy-options": list(),
1021 "dd-phy-disabled": True,
1022 "dd-area-value": str(),
1023 "dd-area-options": list(),
1024 "dd-area-disabled": True,
1025 "dd-test-value": str(),
1026 "dd-test-options": list(),
1027 "dd-test-disabled": True,
1028 "cl-core-options": list(),
1029 "cl-core-value": list(),
1030 "cl-core-all-value": list(),
1031 "cl-core-all-options": self.CL_ALL_DISABLED,
1032 "cl-framesize-options": list(),
1033 "cl-framesize-value": list(),
1034 "cl-framesize-all-value": list(),
1035 "cl-framesize-all-options": self.CL_ALL_DISABLED,
1036 "cl-testtype-options": list(),
1037 "cl-testtype-value": list(),
1038 "cl-testtype-all-value": list(),
1039 "cl-testtype-all-options": self.CL_ALL_DISABLED
1041 elif trigger_id == "dd-ctrl-dutver":
1043 rls = ctrl_panel.get("dd-rls-value")
1044 dut = ctrl_panel.get("dd-dut-value")
1045 dutver = self.spec_tbs[rls][dut][dd_dutver]
1047 [{"label": v, "value": v} for v in dutver.keys()],
1048 key=lambda d: d["label"]
1055 "dd-dutver-value": dd_dutver,
1056 "dd-phy-value": str(),
1057 "dd-phy-options": options,
1058 "dd-phy-disabled": disabled,
1059 "dd-area-value": str(),
1060 "dd-area-options": list(),
1061 "dd-area-disabled": True,
1062 "dd-test-value": str(),
1063 "dd-test-options": list(),
1064 "dd-test-disabled": True,
1065 "cl-core-options": list(),
1066 "cl-core-value": list(),
1067 "cl-core-all-value": list(),
1068 "cl-core-all-options": self.CL_ALL_DISABLED,
1069 "cl-framesize-options": list(),
1070 "cl-framesize-value": list(),
1071 "cl-framesize-all-value": list(),
1072 "cl-framesize-all-options": self.CL_ALL_DISABLED,
1073 "cl-testtype-options": list(),
1074 "cl-testtype-value": list(),
1075 "cl-testtype-all-value": list(),
1076 "cl-testtype-all-options": self.CL_ALL_DISABLED
1078 elif trigger_id == "dd-ctrl-phy":
1080 rls = ctrl_panel.get("dd-rls-value")
1081 dut = ctrl_panel.get("dd-dut-value")
1082 dutver = ctrl_panel.get("dd-dutver-value")
1083 phy = self.spec_tbs[rls][dut][dutver][dd_phy]
1085 [{"label": self.label(v), "value": v}
1086 for v in phy.keys()],
1087 key=lambda d: d["label"]
1094 "dd-phy-value": dd_phy,
1095 "dd-area-value": str(),
1096 "dd-area-options": options,
1097 "dd-area-disabled": disabled,
1098 "dd-test-value": str(),
1099 "dd-test-options": list(),
1100 "dd-test-disabled": True,
1101 "cl-core-options": list(),
1102 "cl-core-value": list(),
1103 "cl-core-all-value": list(),
1104 "cl-core-all-options": self.CL_ALL_DISABLED,
1105 "cl-framesize-options": list(),
1106 "cl-framesize-value": list(),
1107 "cl-framesize-all-value": list(),
1108 "cl-framesize-all-options": self.CL_ALL_DISABLED,
1109 "cl-testtype-options": list(),
1110 "cl-testtype-value": list(),
1111 "cl-testtype-all-value": list(),
1112 "cl-testtype-all-options": self.CL_ALL_DISABLED
1114 elif trigger_id == "dd-ctrl-area":
1116 rls = ctrl_panel.get("dd-rls-value")
1117 dut = ctrl_panel.get("dd-dut-value")
1118 dutver = ctrl_panel.get("dd-dutver-value")
1119 phy = ctrl_panel.get("dd-phy-value")
1120 area = self.spec_tbs[rls][dut][dutver][phy][dd_area]
1122 [{"label": v, "value": v} for v in area.keys()],
1123 key=lambda d: d["label"]
1130 "dd-area-value": dd_area,
1131 "dd-test-value": str(),
1132 "dd-test-options": options,
1133 "dd-test-disabled": disabled,
1134 "cl-core-options": list(),
1135 "cl-core-value": list(),
1136 "cl-core-all-value": list(),
1137 "cl-core-all-options": self.CL_ALL_DISABLED,
1138 "cl-framesize-options": list(),
1139 "cl-framesize-value": list(),
1140 "cl-framesize-all-value": list(),
1141 "cl-framesize-all-options": self.CL_ALL_DISABLED,
1142 "cl-testtype-options": list(),
1143 "cl-testtype-value": list(),
1144 "cl-testtype-all-value": list(),
1145 "cl-testtype-all-options": self.CL_ALL_DISABLED
1147 elif trigger_id == "dd-ctrl-test":
1148 rls = ctrl_panel.get("dd-rls-value")
1149 dut = ctrl_panel.get("dd-dut-value")
1150 dutver = ctrl_panel.get("dd-dutver-value")
1151 phy = ctrl_panel.get("dd-phy-value")
1152 area = ctrl_panel.get("dd-area-value")
1153 test = self.spec_tbs[rls][dut][dutver][phy][area][dd_test]
1154 if dut and phy and area and dd_test:
1156 "dd-test-value": dd_test,
1157 "cl-core-options": [{"label": v, "value": v}
1158 for v in sorted(test["core"])],
1159 "cl-core-value": list(),
1160 "cl-core-all-value": list(),
1161 "cl-core-all-options": self.CL_ALL_ENABLED,
1162 "cl-framesize-options": [{"label": v, "value": v}
1163 for v in sorted(test["frame-size"])],
1164 "cl-framesize-value": list(),
1165 "cl-framesize-all-value": list(),
1166 "cl-framesize-all-options": self.CL_ALL_ENABLED,
1167 "cl-testtype-options": [{"label": v, "value": v}
1168 for v in sorted(test["test-type"])],
1169 "cl-testtype-value": list(),
1170 "cl-testtype-all-value": list(),
1171 "cl-testtype-all-options": self.CL_ALL_ENABLED,
1173 elif trigger_id == "cl-ctrl-core":
1174 val_sel, val_all = self._sync_checklists(
1175 opt=ctrl_panel.get("cl-core-options"),
1181 "cl-core-value": val_sel,
1182 "cl-core-all-value": val_all,
1184 elif trigger_id == "cl-ctrl-core-all":
1185 val_sel, val_all = self._sync_checklists(
1186 opt = ctrl_panel.get("cl-core-options"),
1192 "cl-core-value": val_sel,
1193 "cl-core-all-value": val_all,
1195 elif trigger_id == "cl-ctrl-framesize":
1196 val_sel, val_all = self._sync_checklists(
1197 opt = ctrl_panel.get("cl-framesize-options"),
1203 "cl-framesize-value": val_sel,
1204 "cl-framesize-all-value": val_all,
1206 elif trigger_id == "cl-ctrl-framesize-all":
1207 val_sel, val_all = self._sync_checklists(
1208 opt = ctrl_panel.get("cl-framesize-options"),
1210 all=cl_framesize_all,
1214 "cl-framesize-value": val_sel,
1215 "cl-framesize-all-value": val_all,
1217 elif trigger_id == "cl-ctrl-testtype":
1218 val_sel, val_all = self._sync_checklists(
1219 opt = ctrl_panel.get("cl-testtype-options"),
1225 "cl-testtype-value": val_sel,
1226 "cl-testtype-all-value": val_all,
1228 elif trigger_id == "cl-ctrl-testtype-all":
1229 val_sel, val_all = self._sync_checklists(
1230 opt = ctrl_panel.get("cl-testtype-options"),
1232 all=cl_testtype_all,
1236 "cl-testtype-value": val_sel,
1237 "cl-testtype-all-value": val_all,
1239 elif trigger_id == "btn-ctrl-add":
1241 rls = ctrl_panel.get("dd-rls-value")
1242 dut = ctrl_panel.get("dd-dut-value")
1243 dutver = ctrl_panel.get("dd-dutver-value")
1244 phy = ctrl_panel.get("dd-phy-value")
1245 area = ctrl_panel.get("dd-area-value")
1246 test = ctrl_panel.get("dd-test-value")
1247 cores = ctrl_panel.get("cl-core-value")
1248 framesizes = ctrl_panel.get("cl-framesize-value")
1249 testtypes = ctrl_panel.get("cl-testtype-value")
1250 # Add selected test to the list of tests in store:
1251 if all((rls, dut, dutver, phy, area, test, cores, framesizes,
1253 if store_sel is None:
1256 for framesize in framesizes:
1257 for ttype in testtypes:
1260 tid = "-".join((rls, dut, dutver,
1261 phy.replace('af_xdp', 'af-xdp'), area,
1262 framesize.lower(), core.lower(), test,
1264 if tid not in [itm["id"] for itm in store_sel]:
1273 "framesize": framesize.lower(),
1274 "core": core.lower(),
1275 "testtype": ttype.lower()
1277 store_sel = sorted(store_sel, key=lambda d: d["id"])
1278 row_card_sel_tests = self.STYLE_ENABLED
1279 row_btns_sel_tests = self.STYLE_ENABLED
1280 if self.CLEAR_ALL_INPUTS:
1281 ctrl_panel.set(ctrl_panel.defaults)
1283 "cl-selected-options": self._list_tests(store_sel)
1285 row_fig_tput, row_fig_tsa, row_table, row_btn_dwnld = \
1286 _generate_plotting_area(
1287 graph_iterative(self.data, store_sel, self.layout),
1288 table_comparison(self.data, store_sel),
1289 _gen_new_url(parsed_url, store_sel)
1291 elif trigger_id == "btn-sel-remove-all":
1293 row_fig_tput = self.PLACEHOLDER
1294 row_fig_tsa = self.PLACEHOLDER
1295 row_table = self.PLACEHOLDER
1296 row_btn_dwnld = self.PLACEHOLDER
1297 row_card_sel_tests = self.STYLE_DISABLED
1298 row_btns_sel_tests = self.STYLE_DISABLED
1300 ctrl_panel.set({"cl-selected-options": list()})
1301 elif trigger_id == "btn-sel-remove":
1304 new_store_sel = list()
1305 for item in store_sel:
1306 if item["id"] not in list_sel:
1307 new_store_sel.append(item)
1308 store_sel = new_store_sel
1310 row_fig_tput, row_fig_tsa, row_table, row_btn_dwnld = \
1311 _generate_plotting_area(
1312 graph_iterative(self.data, store_sel, self.layout),
1313 table_comparison(self.data, store_sel),
1314 _gen_new_url(parsed_url, store_sel)
1317 "cl-selected-options": self._list_tests(store_sel)
1320 row_fig_tput = self.PLACEHOLDER
1321 row_fig_tsa = self.PLACEHOLDER
1322 row_table = self.PLACEHOLDER
1323 row_btn_dwnld = self.PLACEHOLDER
1324 row_card_sel_tests = self.STYLE_DISABLED
1325 row_btns_sel_tests = self.STYLE_DISABLED
1327 ctrl_panel.set({"cl-selected-options": list()})
1328 elif trigger_id == "url":
1329 # TODO: Add verification
1330 url_params = parsed_url["params"]
1332 store_sel = literal_eval(
1333 url_params.get("store_sel", list())[0])
1335 row_fig_tput, row_fig_tsa, row_table, row_btn_dwnld = \
1336 _generate_plotting_area(
1337 graph_iterative(self.data, store_sel,
1339 table_comparison(self.data, store_sel),
1340 _gen_new_url(parsed_url, store_sel)
1342 row_card_sel_tests = self.STYLE_ENABLED
1343 row_btns_sel_tests = self.STYLE_ENABLED
1345 "cl-selected-options": self._list_tests(store_sel)
1348 row_fig_tput = self.PLACEHOLDER
1349 row_fig_tsa = self.PLACEHOLDER
1350 row_table = self.PLACEHOLDER
1351 row_btn_dwnld = self.PLACEHOLDER
1352 row_card_sel_tests = self.STYLE_DISABLED
1353 row_btns_sel_tests = self.STYLE_DISABLED
1355 ctrl_panel.set({"cl-selected-options": list()})
1357 if ctrl_panel.get("cl-core-value") and \
1358 ctrl_panel.get("cl-framesize-value") and \
1359 ctrl_panel.get("cl-testtype-value"):
1363 ctrl_panel.set({"btn-add-disabled": disabled})
1366 ctrl_panel.panel, store_sel,
1367 row_fig_tput, row_fig_tsa, row_table, row_btn_dwnld,
1368 row_card_sel_tests, row_btns_sel_tests
1370 ret_val.extend(ctrl_panel.values())
1374 # Output("metadata-tput-lat", "children"),
1375 # Output("metadata-hdrh-graph", "children"),
1376 # Output("offcanvas-metadata", "is_open"),
1377 # Input({"type": "graph", "index": ALL}, "clickData"),
1378 # prevent_initial_call=True
1380 # def _show_metadata_from_graphs(graph_data: dict) -> tuple:
1384 # trigger_id = loads(
1385 # callback_context.triggered[0]["prop_id"].split(".")[0]
1387 # idx = 0 if trigger_id == "tput" else 1
1388 # graph_data = graph_data[idx]["points"][0]
1389 # except (JSONDecodeError, IndexError, KeyError, ValueError,
1391 # raise PreventUpdate
1393 # metadata = no_update
1397 # dbc.ListGroupItem(
1398 # [dbc.Badge(x.split(":")[0]), x.split(": ")[1]]
1399 # ) for x in graph_data.get("text", "").split("<br>")
1401 # if trigger_id == "tput":
1402 # title = "Throughput"
1403 # elif trigger_id == "lat":
1405 # hdrh_data = graph_data.get("customdata", None)
1407 # graph = [dbc.Card(
1408 # class_name="gy-2 p-0",
1410 # dbc.CardHeader(hdrh_data.pop("name")),
1411 # dbc.CardBody(children=[
1413 # id="hdrh-latency-graph",
1414 # figure=graph_hdrh_latency(
1415 # hdrh_data, self.layout
1423 # class_name="gy-2 p-0",
1425 # dbc.CardHeader(children=[
1427 # target_id="tput-lat-metadata",
1429 # style={"display": "inline-block"}
1434 # id="tput-lat-metadata",
1436 # children=[dbc.ListGroup(children, flush=True), ]
1442 # return metadata, graph, True
1445 # Output("download-data", "data"),
1446 # State("selected-tests", "data"),
1447 # Input("btn-download-data", "n_clicks"),
1448 # prevent_initial_call=True
1450 # def _download_data(store_sel, n_clicks):
1455 # raise PreventUpdate
1458 # raise PreventUpdate
1460 # df = pd.DataFrame()
1461 # for itm in store_sel:
1462 # sel_data = select_trending_data(self.data, itm)
1463 # if sel_data is None:
1465 # df = pd.concat([df, sel_data], ignore_index=True)
1467 # return dcc.send_data_frame(df.to_csv, "trending_data.csv")