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 ..data.data import Data
33 from ..data.url_processing import url_decode, url_encode
34 from .graphs import graph_iterative, table_comparison, get_short_version
41 # If True, clear all inputs in control panel when button "ADD SELECTED" is
43 CLEAR_ALL_INPUTS = False
45 STYLE_DISABLED = {"display": "none"}
46 STYLE_ENABLED = {"display": "inherit"}
59 PLACEHOLDER = html.Nobr("")
61 DRIVERS = ("avf", "af-xdp", "rdma", "dpdk")
65 "container_memif": "LXC/DRC Container Memif",
66 "crypto": "IPSec IPv4 Routing",
67 "ip4": "IPv4 Routing",
68 "ip6": "IPv6 Routing",
69 "ip4_tunnels": "IPv4 Tunnels",
70 "l2": "L2 Ethernet Switching",
71 "srv6": "SRv6 Routing",
72 "vm_vhost": "VMs vhost-user",
73 "nfv_density-dcr_memif-chain_ipsec": "CNF Service Chains Routing IPSec",
74 "nfv_density-vm_vhost-chain_dot1qip4vxlan":"VNF Service Chains Tunnels",
75 "nfv_density-vm_vhost-chain": "VNF Service Chains Routing",
76 "nfv_density-dcr_memif-pipeline": "CNF Service Pipelines Routing",
77 "nfv_density-dcr_memif-chain": "CNF Service Chains Routing",
81 "background-color": "#d2ebf5",
82 "border-color": "#bce1f1",
86 def __init__(self, app: Flask, releases: list, html_layout_file: str,
87 graph_layout_file: str, data_spec_file: str, tooltip_file: str) -> None:
93 self.releases = releases
94 self._html_layout_file = html_layout_file
95 self._graph_layout_file = graph_layout_file
96 self._data_spec_file = data_spec_file
97 self._tooltip_file = tooltip_file
100 self._data = pd.DataFrame()
102 data_mrr = Data(self._data_spec_file, True).\
103 read_iterative_mrr(release=rls.replace("csit", "rls"))
104 data_mrr["release"] = rls
105 data_ndrpdr = Data(self._data_spec_file, True).\
106 read_iterative_ndrpdr(release=rls.replace("csit", "rls"))
107 data_ndrpdr["release"] = rls
108 self._data = pd.concat(
109 [self._data, data_mrr, data_ndrpdr], ignore_index=True)
111 # Get structure of tests:
113 cols = ["job", "test_id", "test_type", "dut_version", "release"]
114 for _, row in self._data[cols].drop_duplicates().iterrows():
116 ttype = row["test_type"]
117 lst_job = row["job"].split("-")
119 d_ver = get_short_version(row["dut_version"], dut)
120 tbed = "-".join(lst_job[-2:])
121 lst_test_id = row["test_id"].split(".")
125 area = "-".join(lst_test_id[3:-2])
126 suite = lst_test_id[-2].replace("2n1l-", "").replace("1n1l-", "").\
128 test = lst_test_id[-1]
129 nic = suite.split("-")[0]
130 for drv in self.DRIVERS:
132 driver = drv.replace("-", "_")
133 test = test.replace(f"{drv}-", "")
137 infra = "-".join((tbed, nic, driver))
138 lst_test = test.split("-")
139 framesize = lst_test[0]
140 core = lst_test[1] if lst_test[1] else "8C"
141 test = "-".join(lst_test[2: -1])
143 if tbs.get(rls, None) is None:
145 if tbs[rls].get(dut, None) is None:
146 tbs[rls][dut] = dict()
147 if tbs[rls][dut].get(d_ver, None) is None:
148 tbs[rls][dut][d_ver] = dict()
149 if tbs[rls][dut][d_ver].get(infra, None) is None:
150 tbs[rls][dut][d_ver][infra] = dict()
151 if tbs[rls][dut][d_ver][infra].get(area, None) is None:
152 tbs[rls][dut][d_ver][infra][area] = dict()
153 if tbs[rls][dut][d_ver][infra][area].get(test, None) is None:
154 tbs[rls][dut][d_ver][infra][area][test] = dict()
155 tbs[rls][dut][d_ver][infra][area][test]["core"] = list()
156 tbs[rls][dut][d_ver][infra][area][test]["frame-size"] = list()
157 tbs[rls][dut][d_ver][infra][area][test]["test-type"] = list()
158 if core.upper() not in \
159 tbs[rls][dut][d_ver][infra][area][test]["core"]:
160 tbs[rls][dut][d_ver][infra][area][test]["core"].append(
162 if framesize.upper() not in \
163 tbs[rls][dut][d_ver][infra][area][test]["frame-size"]:
164 tbs[rls][dut][d_ver][infra][area][test]["frame-size"].append(
168 tbs[rls][dut][d_ver][infra][area][test]["test-type"]:
169 tbs[rls][dut][d_ver][infra][area][test]["test-type"].append(
171 elif ttype == "ndrpdr":
173 tbs[rls][dut][d_ver][infra][area][test]["test-type"]:
174 tbs[rls][dut][d_ver][infra][area][test]["test-type"].extend(
179 self._html_layout = ""
180 self._graph_layout = None
181 self._tooltips = dict()
184 with open(self._html_layout_file, "r") as file_read:
185 self._html_layout = file_read.read()
186 except IOError as err:
188 f"Not possible to open the file {self._html_layout_file}\n{err}"
192 with open(self._graph_layout_file, "r") as file_read:
193 self._graph_layout = load(file_read, Loader=FullLoader)
194 except IOError as err:
196 f"Not possible to open the file {self._graph_layout_file}\n"
199 except YAMLError as err:
201 f"An error occurred while parsing the specification file "
202 f"{self._graph_layout_file}\n{err}"
206 with open(self._tooltip_file, "r") as file_read:
207 self._tooltips = load(file_read, Loader=FullLoader)
208 except IOError as err:
210 f"Not possible to open the file {self._tooltip_file}\n{err}"
212 except YAMLError as err:
214 f"An error occurred while parsing the specification file "
215 f"{self._tooltip_file}\n{err}"
219 if self._app is not None and hasattr(self, 'callbacks'):
220 self.callbacks(self._app)
223 def html_layout(self):
224 return self._html_layout
228 return self._spec_tbs
236 return self._graph_layout
238 def label(self, key: str) -> str:
239 return self.LABELS.get(key, key)
241 def _show_tooltip(self, id: str, title: str,
242 clipboard_id: str=None) -> list:
246 dcc.Clipboard(target_id=clipboard_id, title="Copy URL") \
247 if clipboard_id else str(),
255 class_name="border ms-1",
258 children=self._tooltips.get(id, str()),
264 def add_content(self):
267 if self.html_layout and self.spec_tbs:
281 id="offcanvas-metadata",
282 title="Throughput And Latency",
286 dbc.Row(id="metadata-tput-lat"),
287 dbc.Row(id="metadata-hdrh-graph"),
295 dcc.Store(id="selected-tests"),
296 dcc.Store(id="control-panel"),
297 dcc.Location(id="url", refresh=False),
298 self._add_ctrl_col(),
299 self._add_plotting_col(),
317 def _add_navbar(self):
318 """Add nav element with navigation panel. It is placed on the top.
320 return dbc.NavbarSimple(
321 id="navbarsimple-main",
325 "Iterative Test Runs",
334 brand_external_link=True,
339 def _add_ctrl_col(self) -> dbc.Col:
340 """Add column with controls. It is placed on the left side.
345 self._add_ctrl_panel(),
349 def _add_plotting_col(self) -> dbc.Col:
350 """Add column with plots and tables. It is placed on the right side.
353 id="col-plotting-area",
358 class_name="g-0 p-2",
361 dbc.Row( # Throughput
363 class_name="g-0 p-2",
373 class_name="g-0 p-2",
384 class_name="g-0 p-2",
390 id="row-btn-download",
391 class_name="g-0 p-2",
402 def _add_ctrl_panel(self) -> dbc.Row:
407 class_name="g-0 p-2",
415 children=self._show_tooltip(
416 "help-release", "CSIT Release")
420 placeholder=("Select a Release..."),
423 {"label": k, "value": k} \
424 for k in self.spec_tbs.keys()
426 key=lambda d: d["label"]
441 children=self._show_tooltip(
447 "Select a Device under Test..."
462 children=self._show_tooltip(
463 "help-dut-ver", "DUT Version")
468 "Select a Version of "
469 "Device under Test..."
484 children=self._show_tooltip(
485 "help-infra", "Infra")
490 "Select a Physical Test Bed "
506 children=self._show_tooltip(
511 placeholder="Select an Area...",
526 children=self._show_tooltip(
531 placeholder="Select a Test...",
541 id="row-ctrl-framesize",
545 children=self._show_tooltip(
546 "help-framesize", "Frame Size"),
552 id="cl-ctrl-framesize-all",
553 options=self.CL_ALL_DISABLED,
563 id="cl-ctrl-framesize",
576 children=self._show_tooltip(
577 "help-cores", "Number of Cores"),
583 id="cl-ctrl-core-all",
584 options=self.CL_ALL_DISABLED,
603 id="row-ctrl-testtype",
607 children=self._show_tooltip(
608 "help-ttype", "Test Type"),
614 id="cl-ctrl-testtype-all",
615 options=self.CL_ALL_DISABLED,
625 id="cl-ctrl-testtype",
634 class_name="gy-1 p-0",
640 children="Add Selected",
650 id="row-card-sel-tests",
652 style=self.STYLE_DISABLED,
659 class_name="overflow-auto",
663 style={"max-height": "20em"},
668 id="row-btns-sel-tests",
669 style=self.STYLE_DISABLED,
676 children="Remove Selected",
677 class_name="w-100 me-1",
682 id="btn-sel-remove-all",
683 children="Remove All",
684 class_name="w-100 me-1",
697 def __init__(self, panel: dict) -> None:
705 # Defines also the order of keys
707 "dd-rls-value": str(),
708 "dd-dut-options": list(),
709 "dd-dut-disabled": True,
710 "dd-dut-value": str(),
711 "dd-dutver-options": list(),
712 "dd-dutver-disabled": True,
713 "dd-dutver-value": str(),
714 "dd-phy-options": list(),
715 "dd-phy-disabled": True,
716 "dd-phy-value": str(),
717 "dd-area-options": list(),
718 "dd-area-disabled": True,
719 "dd-area-value": str(),
720 "dd-test-options": list(),
721 "dd-test-disabled": True,
722 "dd-test-value": str(),
723 "cl-core-options": list(),
724 "cl-core-value": list(),
725 "cl-core-all-value": list(),
726 "cl-core-all-options": CL_ALL_DISABLED,
727 "cl-framesize-options": list(),
728 "cl-framesize-value": list(),
729 "cl-framesize-all-value": list(),
730 "cl-framesize-all-options": CL_ALL_DISABLED,
731 "cl-testtype-options": list(),
732 "cl-testtype-value": list(),
733 "cl-testtype-all-value": list(),
734 "cl-testtype-all-options": CL_ALL_DISABLED,
735 "btn-add-disabled": True,
736 "cl-selected-options": list()
739 self._panel = deepcopy(self._defaults)
741 for key in self._defaults:
742 self._panel[key] = panel[key]
745 def defaults(self) -> dict:
746 return self._defaults
749 def panel(self) -> dict:
752 def set(self, kwargs: dict) -> None:
753 for key, val in kwargs.items():
754 if key in self._panel:
755 self._panel[key] = val
757 raise KeyError(f"The key {key} is not defined.")
759 def get(self, key: str) -> any:
760 return self._panel[key]
762 def values(self) -> tuple:
763 return tuple(self._panel.values())
766 def _sync_checklists(opt: list, sel: list, all: list, id: str) -> tuple:
769 options = {v["value"] for v in opt}
771 sel = list(options) if all else list()
773 all = ["all", ] if set(sel) == options else list()
777 def _list_tests(selection: dict) -> list:
778 """Display selected tests with checkboxes
781 return [{"label": v["id"], "value": v["id"]} for v in selection]
785 def callbacks(self, app):
787 def _generate_plotting_area(figs: tuple, table: pd.DataFrame,
792 (fig_tput, fig_lat) = figs
794 row_fig_tput = self.PLACEHOLDER
795 row_fig_lat = self.PLACEHOLDER
796 row_table = self.PLACEHOLDER
797 row_btn_dwnld = self.PLACEHOLDER
802 id={"type": "graph", "index": "tput"},
810 dcc.Loading(children=[
812 id="btn-download-data",
813 children=self._show_tooltip(
814 "help-download", "Download Data"),
818 dcc.Download(id="download-data")
829 style=self.URL_STYLE,
830 children=self._show_tooltip(
831 "help-url", "URL", "input-url")
837 style=self.URL_STYLE,
848 id={"type": "graph", "index": "lat"},
854 dbc.Table.from_dataframe(
856 id={"type": "table", "index": "compare"},
863 return row_fig_tput, row_fig_lat, row_table, row_btn_dwnld
866 Output("control-panel", "data"), # Store
867 Output("selected-tests", "data"), # Store
868 Output("row-graph-tput", "children"),
869 Output("row-graph-lat", "children"),
870 Output("row-table", "children"),
871 Output("row-btn-download", "children"),
872 Output("row-card-sel-tests", "style"),
873 Output("row-btns-sel-tests", "style"),
874 Output("dd-ctrl-rls", "value"),
875 Output("dd-ctrl-dut", "options"),
876 Output("dd-ctrl-dut", "disabled"),
877 Output("dd-ctrl-dut", "value"),
878 Output("dd-ctrl-dutver", "options"),
879 Output("dd-ctrl-dutver", "disabled"),
880 Output("dd-ctrl-dutver", "value"),
881 Output("dd-ctrl-phy", "options"),
882 Output("dd-ctrl-phy", "disabled"),
883 Output("dd-ctrl-phy", "value"),
884 Output("dd-ctrl-area", "options"),
885 Output("dd-ctrl-area", "disabled"),
886 Output("dd-ctrl-area", "value"),
887 Output("dd-ctrl-test", "options"),
888 Output("dd-ctrl-test", "disabled"),
889 Output("dd-ctrl-test", "value"),
890 Output("cl-ctrl-core", "options"),
891 Output("cl-ctrl-core", "value"),
892 Output("cl-ctrl-core-all", "value"),
893 Output("cl-ctrl-core-all", "options"),
894 Output("cl-ctrl-framesize", "options"),
895 Output("cl-ctrl-framesize", "value"),
896 Output("cl-ctrl-framesize-all", "value"),
897 Output("cl-ctrl-framesize-all", "options"),
898 Output("cl-ctrl-testtype", "options"),
899 Output("cl-ctrl-testtype", "value"),
900 Output("cl-ctrl-testtype-all", "value"),
901 Output("cl-ctrl-testtype-all", "options"),
902 Output("btn-ctrl-add", "disabled"),
903 Output("cl-selected", "options"), # User selection
904 State("control-panel", "data"), # Store
905 State("selected-tests", "data"), # Store
906 State("cl-selected", "value"), # User selection
907 Input("dd-ctrl-rls", "value"),
908 Input("dd-ctrl-dut", "value"),
909 Input("dd-ctrl-dutver", "value"),
910 Input("dd-ctrl-phy", "value"),
911 Input("dd-ctrl-area", "value"),
912 Input("dd-ctrl-test", "value"),
913 Input("cl-ctrl-core", "value"),
914 Input("cl-ctrl-core-all", "value"),
915 Input("cl-ctrl-framesize", "value"),
916 Input("cl-ctrl-framesize-all", "value"),
917 Input("cl-ctrl-testtype", "value"),
918 Input("cl-ctrl-testtype-all", "value"),
919 Input("btn-ctrl-add", "n_clicks"),
920 Input("btn-sel-remove", "n_clicks"),
921 Input("btn-sel-remove-all", "n_clicks"),
924 def _update_ctrl_panel(cp_data: dict, store_sel: list, list_sel: list,
925 dd_rls: str, dd_dut: str, dd_dutver: str, dd_phy: str, dd_area: str,
926 dd_test: str, cl_core: list, cl_core_all: list, cl_framesize: list,
927 cl_framesize_all: list, cl_testtype: list, cl_testtype_all: list,
928 btn_add: int, btn_remove: int, btn_remove_all: int,
933 def _gen_new_url(parsed_url: dict, store_sel: list) -> str:
936 new_url = url_encode({
937 "scheme": parsed_url["scheme"],
938 "netloc": parsed_url["netloc"],
939 "path": parsed_url["path"],
941 "store_sel": store_sel,
949 ctrl_panel = self.ControlPanel(cp_data)
952 parsed_url = url_decode(href)
954 row_fig_tput = no_update
955 row_fig_lat = no_update
956 row_table = no_update
957 row_btn_dwnld = no_update
958 row_card_sel_tests = no_update
959 row_btns_sel_tests = no_update
961 trigger_id = callback_context.triggered[0]["prop_id"].split(".")[0]
963 if trigger_id == "dd-ctrl-rls":
965 rls = self.spec_tbs[dd_rls]
967 [{"label": v, "value": v} for v in rls.keys()],
968 key=lambda d: d["label"]
975 "dd-rls-value": dd_rls,
976 "dd-dut-value": str(),
977 "dd-dut-options": options,
978 "dd-dut-disabled": disabled,
979 "dd-dutver-value": str(),
980 "dd-dutver-options": list(),
981 "dd-dutver-disabled": True,
982 "dd-phy-value": str(),
983 "dd-phy-options": list(),
984 "dd-phy-disabled": True,
985 "dd-area-value": str(),
986 "dd-area-options": list(),
987 "dd-area-disabled": True,
988 "dd-test-value": str(),
989 "dd-test-options": list(),
990 "dd-test-disabled": True,
991 "cl-core-options": list(),
992 "cl-core-value": list(),
993 "cl-core-all-value": list(),
994 "cl-core-all-options": self.CL_ALL_DISABLED,
995 "cl-framesize-options": list(),
996 "cl-framesize-value": list(),
997 "cl-framesize-all-value": list(),
998 "cl-framesize-all-options": self.CL_ALL_DISABLED,
999 "cl-testtype-options": list(),
1000 "cl-testtype-value": list(),
1001 "cl-testtype-all-value": list(),
1002 "cl-testtype-all-options": self.CL_ALL_DISABLED
1004 elif trigger_id == "dd-ctrl-dut":
1006 rls = ctrl_panel.get("dd-rls-value")
1007 dut = self.spec_tbs[rls][dd_dut]
1009 [{"label": v, "value": v} for v in dut.keys()],
1010 key=lambda d: d["label"]
1017 "dd-dut-value": dd_dut,
1018 "dd-dutver-value": str(),
1019 "dd-dutver-options": options,
1020 "dd-dutver-disabled": disabled,
1021 "dd-phy-value": str(),
1022 "dd-phy-options": list(),
1023 "dd-phy-disabled": True,
1024 "dd-area-value": str(),
1025 "dd-area-options": list(),
1026 "dd-area-disabled": True,
1027 "dd-test-value": str(),
1028 "dd-test-options": list(),
1029 "dd-test-disabled": True,
1030 "cl-core-options": list(),
1031 "cl-core-value": list(),
1032 "cl-core-all-value": list(),
1033 "cl-core-all-options": self.CL_ALL_DISABLED,
1034 "cl-framesize-options": list(),
1035 "cl-framesize-value": list(),
1036 "cl-framesize-all-value": list(),
1037 "cl-framesize-all-options": self.CL_ALL_DISABLED,
1038 "cl-testtype-options": list(),
1039 "cl-testtype-value": list(),
1040 "cl-testtype-all-value": list(),
1041 "cl-testtype-all-options": self.CL_ALL_DISABLED
1043 elif trigger_id == "dd-ctrl-dutver":
1045 rls = ctrl_panel.get("dd-rls-value")
1046 dut = ctrl_panel.get("dd-dut-value")
1047 dutver = self.spec_tbs[rls][dut][dd_dutver]
1049 [{"label": v, "value": v} for v in dutver.keys()],
1050 key=lambda d: d["label"]
1057 "dd-dutver-value": dd_dutver,
1058 "dd-phy-value": str(),
1059 "dd-phy-options": options,
1060 "dd-phy-disabled": disabled,
1061 "dd-area-value": str(),
1062 "dd-area-options": list(),
1063 "dd-area-disabled": True,
1064 "dd-test-value": str(),
1065 "dd-test-options": list(),
1066 "dd-test-disabled": True,
1067 "cl-core-options": list(),
1068 "cl-core-value": list(),
1069 "cl-core-all-value": list(),
1070 "cl-core-all-options": self.CL_ALL_DISABLED,
1071 "cl-framesize-options": list(),
1072 "cl-framesize-value": list(),
1073 "cl-framesize-all-value": list(),
1074 "cl-framesize-all-options": self.CL_ALL_DISABLED,
1075 "cl-testtype-options": list(),
1076 "cl-testtype-value": list(),
1077 "cl-testtype-all-value": list(),
1078 "cl-testtype-all-options": self.CL_ALL_DISABLED
1080 elif trigger_id == "dd-ctrl-phy":
1082 rls = ctrl_panel.get("dd-rls-value")
1083 dut = ctrl_panel.get("dd-dut-value")
1084 dutver = ctrl_panel.get("dd-dutver-value")
1085 phy = self.spec_tbs[rls][dut][dutver][dd_phy]
1087 [{"label": self.label(v), "value": v}
1088 for v in phy.keys()],
1089 key=lambda d: d["label"]
1096 "dd-phy-value": dd_phy,
1097 "dd-area-value": str(),
1098 "dd-area-options": options,
1099 "dd-area-disabled": disabled,
1100 "dd-test-value": str(),
1101 "dd-test-options": list(),
1102 "dd-test-disabled": True,
1103 "cl-core-options": list(),
1104 "cl-core-value": list(),
1105 "cl-core-all-value": list(),
1106 "cl-core-all-options": self.CL_ALL_DISABLED,
1107 "cl-framesize-options": list(),
1108 "cl-framesize-value": list(),
1109 "cl-framesize-all-value": list(),
1110 "cl-framesize-all-options": self.CL_ALL_DISABLED,
1111 "cl-testtype-options": list(),
1112 "cl-testtype-value": list(),
1113 "cl-testtype-all-value": list(),
1114 "cl-testtype-all-options": self.CL_ALL_DISABLED
1116 elif trigger_id == "dd-ctrl-area":
1118 rls = ctrl_panel.get("dd-rls-value")
1119 dut = ctrl_panel.get("dd-dut-value")
1120 dutver = ctrl_panel.get("dd-dutver-value")
1121 phy = ctrl_panel.get("dd-phy-value")
1122 area = self.spec_tbs[rls][dut][dutver][phy][dd_area]
1124 [{"label": v, "value": v} for v in area.keys()],
1125 key=lambda d: d["label"]
1132 "dd-area-value": dd_area,
1133 "dd-test-value": str(),
1134 "dd-test-options": options,
1135 "dd-test-disabled": disabled,
1136 "cl-core-options": list(),
1137 "cl-core-value": list(),
1138 "cl-core-all-value": list(),
1139 "cl-core-all-options": self.CL_ALL_DISABLED,
1140 "cl-framesize-options": list(),
1141 "cl-framesize-value": list(),
1142 "cl-framesize-all-value": list(),
1143 "cl-framesize-all-options": self.CL_ALL_DISABLED,
1144 "cl-testtype-options": list(),
1145 "cl-testtype-value": list(),
1146 "cl-testtype-all-value": list(),
1147 "cl-testtype-all-options": self.CL_ALL_DISABLED
1149 elif trigger_id == "dd-ctrl-test":
1150 rls = ctrl_panel.get("dd-rls-value")
1151 dut = ctrl_panel.get("dd-dut-value")
1152 dutver = ctrl_panel.get("dd-dutver-value")
1153 phy = ctrl_panel.get("dd-phy-value")
1154 area = ctrl_panel.get("dd-area-value")
1155 test = self.spec_tbs[rls][dut][dutver][phy][area][dd_test]
1156 if dut and phy and area and dd_test:
1158 "dd-test-value": dd_test,
1159 "cl-core-options": [{"label": v, "value": v}
1160 for v in sorted(test["core"])],
1161 "cl-core-value": list(),
1162 "cl-core-all-value": list(),
1163 "cl-core-all-options": self.CL_ALL_ENABLED,
1164 "cl-framesize-options": [{"label": v, "value": v}
1165 for v in sorted(test["frame-size"])],
1166 "cl-framesize-value": list(),
1167 "cl-framesize-all-value": list(),
1168 "cl-framesize-all-options": self.CL_ALL_ENABLED,
1169 "cl-testtype-options": [{"label": v, "value": v}
1170 for v in sorted(test["test-type"])],
1171 "cl-testtype-value": list(),
1172 "cl-testtype-all-value": list(),
1173 "cl-testtype-all-options": self.CL_ALL_ENABLED,
1175 elif trigger_id == "cl-ctrl-core":
1176 val_sel, val_all = self._sync_checklists(
1177 opt=ctrl_panel.get("cl-core-options"),
1183 "cl-core-value": val_sel,
1184 "cl-core-all-value": val_all,
1186 elif trigger_id == "cl-ctrl-core-all":
1187 val_sel, val_all = self._sync_checklists(
1188 opt = ctrl_panel.get("cl-core-options"),
1194 "cl-core-value": val_sel,
1195 "cl-core-all-value": val_all,
1197 elif trigger_id == "cl-ctrl-framesize":
1198 val_sel, val_all = self._sync_checklists(
1199 opt = ctrl_panel.get("cl-framesize-options"),
1205 "cl-framesize-value": val_sel,
1206 "cl-framesize-all-value": val_all,
1208 elif trigger_id == "cl-ctrl-framesize-all":
1209 val_sel, val_all = self._sync_checklists(
1210 opt = ctrl_panel.get("cl-framesize-options"),
1212 all=cl_framesize_all,
1216 "cl-framesize-value": val_sel,
1217 "cl-framesize-all-value": val_all,
1219 elif trigger_id == "cl-ctrl-testtype":
1220 val_sel, val_all = self._sync_checklists(
1221 opt = ctrl_panel.get("cl-testtype-options"),
1227 "cl-testtype-value": val_sel,
1228 "cl-testtype-all-value": val_all,
1230 elif trigger_id == "cl-ctrl-testtype-all":
1231 val_sel, val_all = self._sync_checklists(
1232 opt = ctrl_panel.get("cl-testtype-options"),
1234 all=cl_testtype_all,
1238 "cl-testtype-value": val_sel,
1239 "cl-testtype-all-value": val_all,
1241 elif trigger_id == "btn-ctrl-add":
1243 rls = ctrl_panel.get("dd-rls-value")
1244 dut = ctrl_panel.get("dd-dut-value")
1245 dutver = ctrl_panel.get("dd-dutver-value")
1246 phy = ctrl_panel.get("dd-phy-value")
1247 area = ctrl_panel.get("dd-area-value")
1248 test = ctrl_panel.get("dd-test-value")
1249 cores = ctrl_panel.get("cl-core-value")
1250 framesizes = ctrl_panel.get("cl-framesize-value")
1251 testtypes = ctrl_panel.get("cl-testtype-value")
1252 # Add selected test to the list of tests in store:
1253 if all((rls, dut, dutver, phy, area, test, cores, framesizes,
1255 if store_sel is None:
1258 for framesize in framesizes:
1259 for ttype in testtypes:
1262 tid = "-".join((rls, dut, dutver,
1263 phy.replace('af_xdp', 'af-xdp'), area,
1264 framesize.lower(), core.lower(), test,
1266 if tid not in [itm["id"] for itm in store_sel]:
1275 "framesize": framesize.lower(),
1276 "core": core.lower(),
1277 "testtype": ttype.lower()
1279 store_sel = sorted(store_sel, key=lambda d: d["id"])
1280 row_card_sel_tests = self.STYLE_ENABLED
1281 row_btns_sel_tests = self.STYLE_ENABLED
1282 if self.CLEAR_ALL_INPUTS:
1283 ctrl_panel.set(ctrl_panel.defaults)
1285 "cl-selected-options": self._list_tests(store_sel)
1287 row_fig_tput, row_fig_lat, row_table, row_btn_dwnld = \
1288 _generate_plotting_area(
1289 graph_iterative(self.data, store_sel, self.layout),
1290 table_comparison(self.data, store_sel),
1291 _gen_new_url(parsed_url, store_sel)
1293 elif trigger_id == "btn-sel-remove-all":
1295 row_fig_tput = self.PLACEHOLDER
1296 row_fig_lat = self.PLACEHOLDER
1297 row_table = self.PLACEHOLDER
1298 row_btn_dwnld = self.PLACEHOLDER
1299 row_card_sel_tests = self.STYLE_DISABLED
1300 row_btns_sel_tests = self.STYLE_DISABLED
1302 ctrl_panel.set({"cl-selected-options": list()})
1303 elif trigger_id == "btn-sel-remove":
1306 new_store_sel = list()
1307 for item in store_sel:
1308 if item["id"] not in list_sel:
1309 new_store_sel.append(item)
1310 store_sel = new_store_sel
1312 row_fig_tput, row_fig_lat, row_table, row_btn_dwnld = \
1313 _generate_plotting_area(
1314 graph_iterative(self.data, store_sel, self.layout),
1315 table_comparison(self.data, store_sel),
1316 _gen_new_url(parsed_url, store_sel)
1319 "cl-selected-options": self._list_tests(store_sel)
1322 row_fig_tput = self.PLACEHOLDER
1323 row_fig_lat = self.PLACEHOLDER
1324 row_table = self.PLACEHOLDER
1325 row_btn_dwnld = self.PLACEHOLDER
1326 row_card_sel_tests = self.STYLE_DISABLED
1327 row_btns_sel_tests = self.STYLE_DISABLED
1329 ctrl_panel.set({"cl-selected-options": list()})
1330 elif trigger_id == "url":
1331 # TODO: Add verification
1332 url_params = parsed_url["params"]
1334 store_sel = literal_eval(
1335 url_params.get("store_sel", list())[0])
1337 row_fig_tput, row_fig_lat, row_table, row_btn_dwnld = \
1338 _generate_plotting_area(
1339 graph_iterative(self.data, store_sel,
1341 table_comparison(self.data, store_sel),
1342 _gen_new_url(parsed_url, store_sel)
1344 row_card_sel_tests = self.STYLE_ENABLED
1345 row_btns_sel_tests = self.STYLE_ENABLED
1347 "cl-selected-options": self._list_tests(store_sel)
1350 row_fig_tput = self.PLACEHOLDER
1351 row_fig_lat = self.PLACEHOLDER
1352 row_table = self.PLACEHOLDER
1353 row_btn_dwnld = self.PLACEHOLDER
1354 row_card_sel_tests = self.STYLE_DISABLED
1355 row_btns_sel_tests = self.STYLE_DISABLED
1357 ctrl_panel.set({"cl-selected-options": list()})
1359 if ctrl_panel.get("cl-core-value") and \
1360 ctrl_panel.get("cl-framesize-value") and \
1361 ctrl_panel.get("cl-testtype-value"):
1365 ctrl_panel.set({"btn-add-disabled": disabled})
1368 ctrl_panel.panel, store_sel,
1369 row_fig_tput, row_fig_lat, row_table, row_btn_dwnld,
1370 row_card_sel_tests, row_btns_sel_tests
1372 ret_val.extend(ctrl_panel.values())
1376 # Output("metadata-tput-lat", "children"),
1377 # Output("metadata-hdrh-graph", "children"),
1378 # Output("offcanvas-metadata", "is_open"),
1379 # Input({"type": "graph", "index": ALL}, "clickData"),
1380 # prevent_initial_call=True
1382 # def _show_metadata_from_graphs(graph_data: dict) -> tuple:
1386 # if not any(graph_data):
1387 # raise PreventUpdate
1390 # trigger_id = loads(
1391 # callback_context.triggered[0]["prop_id"].split(".")[0]
1393 # idx = 0 if trigger_id == "tput" else 1
1394 # graph_data = graph_data[idx]["points"][0]
1395 # except (JSONDecodeError, IndexError, KeyError, ValueError,
1397 # raise PreventUpdate
1399 # metadata = no_update
1403 # dbc.ListGroupItem(
1404 # [dbc.Badge(x.split(":")[0]), x.split(": ")[1]]
1405 # ) for x in graph_data.get("text", "").split("<br>")
1407 # if trigger_id == "tput":
1408 # title = "Throughput"
1409 # elif trigger_id == "lat":
1411 # hdrh_data = graph_data.get("customdata", None)
1413 # graph = [dbc.Card(
1414 # class_name="gy-2 p-0",
1416 # dbc.CardHeader(hdrh_data.pop("name")),
1417 # dbc.CardBody(children=[
1419 # id="hdrh-latency-graph",
1420 # figure=graph_hdrh_latency(
1421 # hdrh_data, self.layout
1429 # class_name="gy-2 p-0",
1431 # dbc.CardHeader(children=[
1433 # target_id="tput-lat-metadata",
1435 # style={"display": "inline-block"}
1440 # id="tput-lat-metadata",
1442 # children=[dbc.ListGroup(children, flush=True), ]
1448 # return metadata, graph, True
1451 # Output("download-data", "data"),
1452 # State("selected-tests", "data"),
1453 # Input("btn-download-data", "n_clicks"),
1454 # prevent_initial_call=True
1456 # def _download_data(store_sel, n_clicks):
1461 # raise PreventUpdate
1464 # raise PreventUpdate
1466 # df = pd.DataFrame()
1467 # for itm in store_sel:
1468 # sel_data = select_trending_data(self.data, itm)
1469 # if sel_data is None:
1471 # df = pd.concat([df, sel_data], ignore_index=True)
1473 # return dcc.send_data_frame(df.to_csv, "trending_data.csv")