1 # Copyright (c) 2024 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.
20 import dash_bootstrap_components as dbc
22 from flask import Flask
25 from dash import callback_context, no_update, ALL
26 from dash import Input, Output, State
27 from dash.exceptions import PreventUpdate
28 from yaml import load, FullLoader, YAMLError
29 from ast import literal_eval
31 from ..utils.constants import Constants as C
32 from ..utils.control_panel import ControlPanel
33 from ..utils.trigger import Trigger
34 from ..utils.utils import show_tooltip, label, sync_checklists, gen_new_url, \
35 generate_options, get_list_group_items, navbar_report, \
36 show_iterative_graph_data
37 from ..utils.url_processing import url_decode
38 from .graphs import graph_iterative, select_iterative_data
41 # Control panel partameters and their default values.
47 "dd-dutver-opt": list(),
48 "dd-dutver-dis": True,
49 "dd-dutver-val": str(),
53 "dd-area-opt": list(),
56 "dd-test-opt": list(),
59 "cl-core-opt": list(),
60 "cl-core-val": list(),
61 "cl-core-all-val": list(),
62 "cl-core-all-opt": C.CL_ALL_DISABLED,
63 "cl-frmsize-opt": list(),
64 "cl-frmsize-val": list(),
65 "cl-frmsize-all-val": list(),
66 "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
67 "cl-tsttype-opt": list(),
68 "cl-tsttype-val": list(),
69 "cl-tsttype-all-val": list(),
70 "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
72 "cl-normalize-val": list()
77 """The layout of the dash app and the callbacks.
83 data_iterative: pd.DataFrame,
84 html_layout_file: str,
85 graph_layout_file: str,
89 - save the input parameters,
90 - read and pre-process the data,
91 - prepare data for the control panel,
92 - read HTML layout file,
93 - read tooltips from the tooltip file.
95 :param app: Flask application running the dash application.
96 :param html_layout_file: Path and name of the file specifying the HTML
97 layout of the dash application.
98 :param graph_layout_file: Path and name of the file with layout of
100 :param tooltip_file: Path and name of the yaml file specifying the
103 :type html_layout_file: str
104 :type graph_layout_file: str
105 :type tooltip_file: str
110 self._html_layout_file = html_layout_file
111 self._graph_layout_file = graph_layout_file
112 self._tooltip_file = tooltip_file
113 self._data = data_iterative
115 # Get structure of tests:
118 "job", "test_id", "test_type", "dut_version", "tg_type", "release"
120 for _, row in self._data[cols].drop_duplicates().iterrows():
122 lst_job = row["job"].split("-")
124 d_ver = row["dut_version"]
125 tbed = "-".join(lst_job[-2:])
126 lst_test_id = row["test_id"].split(".")
130 area = ".".join(lst_test_id[3:-2])
131 suite = lst_test_id[-2].replace("2n1l-", "").replace("1n1l-", "").\
133 test = lst_test_id[-1]
134 nic = suite.split("-")[0]
135 for drv in C.DRIVERS:
137 driver = drv.replace("-", "_")
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(area, None) is None:
155 tbs[rls][dut][d_ver][area] = dict()
156 if tbs[rls][dut][d_ver][area].get(test, None) is None:
157 tbs[rls][dut][d_ver][area][test] = dict()
158 if tbs[rls][dut][d_ver][area][test].get(infra, None) is None:
159 tbs[rls][dut][d_ver][area][test][infra] = {
161 "frame-size": list(),
164 tst_params = tbs[rls][dut][d_ver][area][test][infra]
165 if core.upper() not in tst_params["core"]:
166 tst_params["core"].append(core.upper())
167 if framesize.upper() not in tst_params["frame-size"]:
168 tst_params["frame-size"].append(framesize.upper())
169 if row["test_type"] == "mrr":
170 if "MRR" not in tst_params["test-type"]:
171 tst_params["test-type"].append("MRR")
172 elif row["test_type"] == "ndrpdr":
173 if "NDR" not in tst_params["test-type"]:
174 tst_params["test-type"].extend(("NDR", "PDR", ))
175 elif row["test_type"] == "hoststack" and \
176 row["tg_type"] in ("iperf", "vpp"):
177 if "BPS" not in tst_params["test-type"]:
178 tst_params["test-type"].append("BPS")
179 elif row["test_type"] == "hoststack" and row["tg_type"] == "ab":
180 if "CPS" not in tst_params["test-type"]:
181 tst_params["test-type"].extend(("CPS", "RPS"))
185 self._html_layout = str()
186 self._graph_layout = None
187 self._tooltips = dict()
190 with open(self._html_layout_file, "r") as file_read:
191 self._html_layout = file_read.read()
192 except IOError as err:
194 f"Not possible to open the file {self._html_layout_file}\n{err}"
198 with open(self._graph_layout_file, "r") as file_read:
199 self._graph_layout = load(file_read, Loader=FullLoader)
200 except IOError as err:
202 f"Not possible to open the file {self._graph_layout_file}\n"
205 except YAMLError as err:
207 f"An error occurred while parsing the specification file "
208 f"{self._graph_layout_file}\n{err}"
212 with open(self._tooltip_file, "r") as file_read:
213 self._tooltips = load(file_read, Loader=FullLoader)
214 except IOError as err:
216 f"Not possible to open the file {self._tooltip_file}\n{err}"
218 except YAMLError as err:
220 f"An error occurred while parsing the specification file "
221 f"{self._tooltip_file}\n{err}"
225 if self._app is not None and hasattr(self, "callbacks"):
226 self.callbacks(self._app)
229 def html_layout(self):
230 return self._html_layout
232 def add_content(self):
233 """Top level method which generated the web page.
236 - Store for user input data,
238 - Main area with control panel and ploting area.
240 If no HTML layout is provided, an error message is displayed instead.
242 :returns: The HTML div with the whole page.
246 if self.html_layout and self._spec_tbs:
254 children=[navbar_report((True, False, False, False)), ]
260 dcc.Store(id="store-selected-tests"),
261 dcc.Store(id="store-control-panel"),
262 dcc.Location(id="url", refresh=False),
263 self._add_ctrl_col(),
264 self._add_plotting_col()
270 id="offcanvas-metadata",
271 title="Throughput And Latency",
275 dbc.Row(id="metadata-tput-lat"),
276 dbc.Row(id="metadata-hdrh-graph")
279 delay_show=C.SPINNER_DELAY
283 id="offcanvas-documentation",
284 title="Documentation",
287 children=html.Iframe(
288 src=C.URL_DOC_REL_NOTES,
308 def _add_ctrl_col(self) -> dbc.Col:
309 """Add column with controls. It is placed on the left side.
311 :returns: Column with the control panel.
316 children=self._add_ctrl_panel(),
317 className="sticky-top"
321 def _add_plotting_col(self) -> dbc.Col:
322 """Add column with plots. It is placed on the right side.
324 :returns: Column with plots.
328 id="col-plotting-area",
334 class_name="g-0 p-0",
345 def _add_ctrl_panel(self) -> list:
346 """Add control panel.
348 :returns: Control panel.
353 class_name="g-0 p-1",
357 dbc.InputGroupText(show_tooltip(
363 id={"type": "ctrl-dd", "index": "rls"},
364 placeholder="Select a Release...",
367 {"label": k, "value": k} \
368 for k in self._spec_tbs.keys()
370 key=lambda d: d["label"]
379 class_name="g-0 p-1",
383 dbc.InputGroupText(show_tooltip(
389 id={"type": "ctrl-dd", "index": "dut"},
390 placeholder="Select a Device under Test..."
398 class_name="g-0 p-1",
402 dbc.InputGroupText(show_tooltip(
408 id={"type": "ctrl-dd", "index": "dutver"},
410 "Select a Version of Device under Test..."
418 class_name="g-0 p-1",
422 dbc.InputGroupText(show_tooltip(
428 id={"type": "ctrl-dd", "index": "area"},
429 placeholder="Select an Area..."
437 class_name="g-0 p-1",
441 dbc.InputGroupText(show_tooltip(
447 id={"type": "ctrl-dd", "index": "test"},
448 placeholder="Select a Test..."
456 class_name="g-0 p-1",
460 dbc.InputGroupText(show_tooltip(
466 id={"type": "ctrl-dd", "index": "phy"},
468 "Select a Physical Test Bed Topology..."
476 class_name="g-0 p-1",
481 children=show_tooltip(
492 "index": "frmsize-all"
494 options=C.CL_ALL_DISABLED,
513 style={"align-items": "center"},
519 class_name="g-0 p-1",
524 children=show_tooltip(
537 options=C.CL_ALL_DISABLED,
556 style={"align-items": "center"},
562 class_name="g-0 p-1",
567 children=show_tooltip(
578 "index": "tsttype-all"
580 options=C.CL_ALL_DISABLED,
599 style={"align-items": "center"},
605 class_name="g-0 p-1",
610 children=show_tooltip(
621 "value": "normalize",
623 "Normalize to CPU frequency "
634 style={"align-items": "center"},
640 class_name="g-0 p-1",
643 id={"type": "ctrl-btn", "index": "add-test"},
644 children="Add Selected",
650 id="row-card-sel-tests",
651 class_name="g-0 p-1",
652 style=C.STYLE_DISABLED,
655 class_name="overflow-auto p-0",
658 style={"max-height": "20em"},
664 id="row-btns-sel-tests",
665 class_name="g-0 p-1",
666 style=C.STYLE_DISABLED,
669 dbc.ButtonGroup(children=[
671 id={"type": "ctrl-btn", "index": "rm-test"},
672 children="Remove Selected",
678 id={"type": "ctrl-btn", "index": "rm-test-all"},
679 children="Remove All",
685 dbc.ButtonGroup(children=[
694 id="plot-btn-download",
695 children="Download Data",
705 def _get_plotting_area(
711 """Generate the plotting area with all its content.
713 :param tests: List of tests to be displayed in the graphs.
714 :param normalize: If true, the values in graphs are normalized.
715 :param url: URL to be displayed in the modal window.
717 :type normalize: bool
719 :returns: List of rows with elements to be displayed in the plotting
727 graph_iterative(self._data, tests, self._graph_layout, normalize)
735 id={"type": "graph", "index": "tput"},
747 id={"type": "graph", "index": "bandwidth"},
751 tab_id="tab-bandwidth"
759 id={"type": "graph", "index": "lat"},
772 active_tab="tab-tput",
778 dbc.ModalHeader(dbc.ModalTitle("URL")),
786 dcc.Download(id="download-iterative-data")
789 def callbacks(self, app):
790 """Callbacks for the whole application.
792 :param app: The application.
798 Output("store-control-panel", "data"),
799 Output("store-selected-tests", "data"),
800 Output("plotting-area", "children"),
801 Output("row-card-sel-tests", "style"),
802 Output("row-btns-sel-tests", "style"),
803 Output("lg-selected", "children"),
805 Output({"type": "ctrl-dd", "index": "rls"}, "value"),
806 Output({"type": "ctrl-dd", "index": "dut"}, "options"),
807 Output({"type": "ctrl-dd", "index": "dut"}, "disabled"),
808 Output({"type": "ctrl-dd", "index": "dut"}, "value"),
809 Output({"type": "ctrl-dd", "index": "dutver"}, "options"),
810 Output({"type": "ctrl-dd", "index": "dutver"}, "disabled"),
811 Output({"type": "ctrl-dd", "index": "dutver"}, "value"),
812 Output({"type": "ctrl-dd", "index": "phy"}, "options"),
813 Output({"type": "ctrl-dd", "index": "phy"}, "disabled"),
814 Output({"type": "ctrl-dd", "index": "phy"}, "value"),
815 Output({"type": "ctrl-dd", "index": "area"}, "options"),
816 Output({"type": "ctrl-dd", "index": "area"}, "disabled"),
817 Output({"type": "ctrl-dd", "index": "area"}, "value"),
818 Output({"type": "ctrl-dd", "index": "test"}, "options"),
819 Output({"type": "ctrl-dd", "index": "test"}, "disabled"),
820 Output({"type": "ctrl-dd", "index": "test"}, "value"),
821 Output({"type": "ctrl-cl", "index": "core"}, "options"),
822 Output({"type": "ctrl-cl", "index": "core"}, "value"),
823 Output({"type": "ctrl-cl", "index": "core-all"}, "value"),
824 Output({"type": "ctrl-cl", "index": "core-all"}, "options"),
825 Output({"type": "ctrl-cl", "index": "frmsize"}, "options"),
826 Output({"type": "ctrl-cl", "index": "frmsize"}, "value"),
827 Output({"type": "ctrl-cl", "index": "frmsize-all"}, "value"),
828 Output({"type": "ctrl-cl", "index": "frmsize-all"}, "options"),
829 Output({"type": "ctrl-cl", "index": "tsttype"}, "options"),
830 Output({"type": "ctrl-cl", "index": "tsttype"}, "value"),
831 Output({"type": "ctrl-cl", "index": "tsttype-all"}, "value"),
832 Output({"type": "ctrl-cl", "index": "tsttype-all"}, "options"),
833 Output({"type": "ctrl-btn", "index": "add-test"}, "disabled"),
834 Output("normalize", "value")
837 State("store-control-panel", "data"),
838 State("store-selected-tests", "data"),
839 State({"type": "sel-cl", "index": ALL}, "value")
842 Input("url", "href"),
843 Input("normalize", "value"),
845 Input({"type": "ctrl-dd", "index": ALL}, "value"),
846 Input({"type": "ctrl-cl", "index": ALL}, "value"),
847 Input({"type": "ctrl-btn", "index": ALL}, "n_clicks")
850 def _update_application(
858 """Update the application when the event is detected.
861 ctrl_panel = ControlPanel(CP_PARAMS, control_panel)
865 parsed_url = url_decode(href)
867 url_params = parsed_url["params"]
871 plotting_area = no_update
872 row_card_sel_tests = no_update
873 row_btns_sel_tests = no_update
874 lg_selected = no_update
876 trigger = Trigger(callback_context.triggered)
878 if trigger.type == "url" and url_params:
880 store_sel = literal_eval(url_params["store_sel"][0])
881 normalize = literal_eval(url_params["norm"][0])
882 except (KeyError, IndexError, AttributeError):
885 row_card_sel_tests = C.STYLE_ENABLED
886 row_btns_sel_tests = C.STYLE_ENABLED
887 last_test = store_sel[-1]
888 test = self._spec_tbs[last_test["rls"]][last_test["dut"]]\
889 [last_test["dutver"]][last_test["area"]]\
890 [last_test["test"]][last_test["phy"]]
892 "dd-rls-val": last_test["rls"],
893 "dd-dut-val": last_test["dut"],
894 "dd-dut-opt": generate_options(
895 self._spec_tbs[last_test["rls"]].keys()
898 "dd-dutver-val": last_test["dutver"],
899 "dd-dutver-opt": generate_options(
900 self._spec_tbs[last_test["rls"]]\
901 [last_test["dut"]].keys()
903 "dd-dutver-dis": False,
904 "dd-area-val": last_test["area"],
906 {"label": label(v), "value": v} for v in \
907 sorted(self._spec_tbs[last_test["rls"]]\
909 [last_test["dutver"]].keys())
911 "dd-area-dis": False,
912 "dd-test-val": last_test["test"],
913 "dd-test-opt": generate_options(
914 self._spec_tbs[last_test["rls"]][last_test["dut"]]\
915 [last_test["dutver"]][last_test["area"]].keys()
917 "dd-test-dis": False,
918 "dd-phy-val": last_test["phy"],
919 "dd-phy-opt": generate_options(
920 self._spec_tbs[last_test["rls"]][last_test["dut"]]\
921 [last_test["dutver"]][last_test["area"]]\
922 [last_test["test"]].keys()
925 "cl-core-opt": generate_options(test["core"]),
926 "cl-core-val": [last_test["core"].upper(), ],
927 "cl-core-all-val": list(),
928 "cl-core-all-opt": C.CL_ALL_ENABLED,
929 "cl-frmsize-opt": generate_options(test["frame-size"]),
930 "cl-frmsize-val": [last_test["framesize"].upper(), ],
931 "cl-frmsize-all-val": list(),
932 "cl-frmsize-all-opt": C.CL_ALL_ENABLED,
933 "cl-tsttype-opt": generate_options(test["test-type"]),
934 "cl-tsttype-val": [last_test["testtype"].upper(), ],
935 "cl-tsttype-all-val": list(),
936 "cl-tsttype-all-opt": C.CL_ALL_ENABLED,
937 "cl-normalize-val": normalize,
941 elif trigger.type == "normalize":
942 ctrl_panel.set({"cl-normalize-val": normalize})
944 elif trigger.type == "ctrl-dd":
945 if trigger.idx == "rls":
947 options = generate_options(
948 self._spec_tbs[trigger.value].keys()
955 "dd-rls-val": trigger.value,
957 "dd-dut-opt": options,
958 "dd-dut-dis": disabled,
959 "dd-dutver-val": str(),
960 "dd-dutver-opt": list(),
961 "dd-dutver-dis": True,
963 "dd-phy-opt": list(),
965 "dd-area-val": str(),
966 "dd-area-opt": list(),
968 "dd-test-val": str(),
969 "dd-test-opt": list(),
971 "cl-core-opt": list(),
972 "cl-core-val": list(),
973 "cl-core-all-val": list(),
974 "cl-core-all-opt": C.CL_ALL_DISABLED,
975 "cl-frmsize-opt": list(),
976 "cl-frmsize-val": list(),
977 "cl-frmsize-all-val": list(),
978 "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
979 "cl-tsttype-opt": list(),
980 "cl-tsttype-val": list(),
981 "cl-tsttype-all-val": list(),
982 "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
985 elif trigger.idx == "dut":
987 rls = ctrl_panel.get("dd-rls-val")
988 dut = self._spec_tbs[rls][trigger.value]
989 options = generate_options(dut.keys())
995 "dd-dut-val": trigger.value,
996 "dd-dutver-val": str(),
997 "dd-dutver-opt": options,
998 "dd-dutver-dis": disabled,
1000 "dd-phy-opt": list(),
1002 "dd-area-val": str(),
1003 "dd-area-opt": list(),
1004 "dd-area-dis": True,
1005 "dd-test-val": str(),
1006 "dd-test-opt": list(),
1007 "dd-test-dis": True,
1008 "cl-core-opt": list(),
1009 "cl-core-val": list(),
1010 "cl-core-all-val": list(),
1011 "cl-core-all-opt": C.CL_ALL_DISABLED,
1012 "cl-frmsize-opt": list(),
1013 "cl-frmsize-val": list(),
1014 "cl-frmsize-all-val": list(),
1015 "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
1016 "cl-tsttype-opt": list(),
1017 "cl-tsttype-val": list(),
1018 "cl-tsttype-all-val": list(),
1019 "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
1022 elif trigger.idx == "dutver":
1024 rls = ctrl_panel.get("dd-rls-val")
1025 dut = ctrl_panel.get("dd-dut-val")
1026 dutver = self._spec_tbs[rls][dut][trigger.value]
1027 options = [{"label": label(v), "value": v} \
1028 for v in sorted(dutver.keys())]
1034 "dd-dutver-val": trigger.value,
1035 "dd-area-val": str(),
1036 "dd-area-opt": options,
1037 "dd-area-dis": disabled,
1038 "dd-test-val": str(),
1039 "dd-test-opt": list(),
1040 "dd-test-dis": True,
1041 "dd-phy-val": str(),
1042 "dd-phy-opt": list(),
1044 "cl-core-opt": list(),
1045 "cl-core-val": list(),
1046 "cl-core-all-val": list(),
1047 "cl-core-all-opt": C.CL_ALL_DISABLED,
1048 "cl-frmsize-opt": list(),
1049 "cl-frmsize-val": list(),
1050 "cl-frmsize-all-val": list(),
1051 "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
1052 "cl-tsttype-opt": list(),
1053 "cl-tsttype-val": list(),
1054 "cl-tsttype-all-val": list(),
1055 "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
1058 elif trigger.idx == "area":
1060 rls = ctrl_panel.get("dd-rls-val")
1061 dut = ctrl_panel.get("dd-dut-val")
1062 dutver = ctrl_panel.get("dd-dutver-val")
1063 area = self._spec_tbs[rls][dut][dutver][trigger.value]
1064 options = generate_options(area.keys())
1070 "dd-area-val": trigger.value,
1071 "dd-test-val": str(),
1072 "dd-test-opt": options,
1073 "dd-test-dis": disabled,
1074 "dd-phy-val": str(),
1075 "dd-phy-opt": list(),
1077 "cl-core-opt": list(),
1078 "cl-core-val": list(),
1079 "cl-core-all-val": list(),
1080 "cl-core-all-opt": C.CL_ALL_DISABLED,
1081 "cl-frmsize-opt": list(),
1082 "cl-frmsize-val": list(),
1083 "cl-frmsize-all-val": list(),
1084 "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
1085 "cl-tsttype-opt": list(),
1086 "cl-tsttype-val": list(),
1087 "cl-tsttype-all-val": list(),
1088 "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
1091 elif trigger.idx == "test":
1093 rls = ctrl_panel.get("dd-rls-val")
1094 dut = ctrl_panel.get("dd-dut-val")
1095 dutver = ctrl_panel.get("dd-dutver-val")
1096 area = ctrl_panel.get("dd-area-val")
1097 test = self._spec_tbs[rls][dut][dutver][area]\
1099 options = generate_options(test.keys())
1105 "dd-test-val": trigger.value,
1106 "dd-phy-val": str(),
1107 "dd-phy-opt": options,
1108 "dd-phy-dis": disabled,
1109 "cl-core-opt": list(),
1110 "cl-core-val": list(),
1111 "cl-core-all-val": list(),
1112 "cl-core-all-opt": C.CL_ALL_DISABLED,
1113 "cl-frmsize-opt": list(),
1114 "cl-frmsize-val": list(),
1115 "cl-frmsize-all-val": list(),
1116 "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
1117 "cl-tsttype-opt": list(),
1118 "cl-tsttype-val": list(),
1119 "cl-tsttype-all-val": list(),
1120 "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
1123 elif trigger.idx == "phy":
1124 rls = ctrl_panel.get("dd-rls-val")
1125 dut = ctrl_panel.get("dd-dut-val")
1126 dutver = ctrl_panel.get("dd-dutver-val")
1127 area = ctrl_panel.get("dd-area-val")
1128 test = ctrl_panel.get("dd-test-val")
1129 if all((rls, dut, dutver, area, test, trigger.value, )):
1130 phy = self._spec_tbs[rls][dut][dutver][area][test]\
1133 "dd-phy-val": trigger.value,
1134 "cl-core-opt": generate_options(phy["core"]),
1135 "cl-core-val": list(),
1136 "cl-core-all-val": list(),
1137 "cl-core-all-opt": C.CL_ALL_ENABLED,
1139 generate_options(phy["frame-size"]),
1140 "cl-frmsize-val": list(),
1141 "cl-frmsize-all-val": list(),
1142 "cl-frmsize-all-opt": C.CL_ALL_ENABLED,
1144 generate_options(phy["test-type"]),
1145 "cl-tsttype-val": list(),
1146 "cl-tsttype-all-val": list(),
1147 "cl-tsttype-all-opt": C.CL_ALL_ENABLED,
1150 elif trigger.type == "ctrl-cl":
1151 param = trigger.idx.split("-")[0]
1152 if "-all" in trigger.idx:
1153 c_sel, c_all, c_id = list(), trigger.value, "all"
1155 c_sel, c_all, c_id = trigger.value, list(), str()
1156 val_sel, val_all = sync_checklists(
1157 options=ctrl_panel.get(f"cl-{param}-opt"),
1163 f"cl-{param}-val": val_sel,
1164 f"cl-{param}-all-val": val_all,
1166 if all((ctrl_panel.get("cl-core-val"),
1167 ctrl_panel.get("cl-frmsize-val"),
1168 ctrl_panel.get("cl-tsttype-val"), )):
1169 ctrl_panel.set({"btn-add-dis": False})
1171 ctrl_panel.set({"btn-add-dis": True})
1172 elif trigger.type == "ctrl-btn":
1174 if trigger.idx == "add-test":
1175 rls = ctrl_panel.get("dd-rls-val")
1176 dut = ctrl_panel.get("dd-dut-val")
1177 dutver = ctrl_panel.get("dd-dutver-val")
1178 phy = ctrl_panel.get("dd-phy-val")
1179 area = ctrl_panel.get("dd-area-val")
1180 test = ctrl_panel.get("dd-test-val")
1181 # Add selected test to the list of tests in store:
1182 if store_sel is None:
1184 for core in ctrl_panel.get("cl-core-val"):
1185 for framesize in ctrl_panel.get("cl-frmsize-val"):
1186 for ttype in ctrl_panel.get("cl-tsttype-val"):
1193 phy.replace("af_xdp", "af-xdp"),
1200 if tid not in [i["id"] for i in store_sel]:
1209 "framesize": framesize.lower(),
1210 "core": core.lower(),
1211 "testtype": ttype.lower()
1213 store_sel = sorted(store_sel, key=lambda d: d["id"])
1214 if C.CLEAR_ALL_INPUTS:
1215 ctrl_panel.set(ctrl_panel.defaults)
1216 elif trigger.idx == "rm-test" and lst_sel:
1217 new_store_sel = list()
1218 for idx, item in enumerate(store_sel):
1219 if not lst_sel[idx]:
1220 new_store_sel.append(item)
1221 store_sel = new_store_sel
1222 elif trigger.idx == "rm-test-all":
1227 lg_selected = get_list_group_items(
1228 store_sel, "sel-cl", add_index=True
1230 plotting_area = self._get_plotting_area(
1235 {"store_sel": store_sel, "norm": normalize}
1238 row_card_sel_tests = C.STYLE_ENABLED
1239 row_btns_sel_tests = C.STYLE_ENABLED
1241 plotting_area = C.PLACEHOLDER
1242 row_card_sel_tests = C.STYLE_DISABLED
1243 row_btns_sel_tests = C.STYLE_DISABLED
1254 ret_val.extend(ctrl_panel.values)
1258 Output("plot-mod-url", "is_open"),
1259 Output("plot-btn-url", "n_clicks"),
1260 Input("plot-btn-url", "n_clicks"),
1261 State("plot-mod-url", "is_open")
1263 def toggle_plot_mod_url(n, is_open):
1264 """Toggle the modal window with url.
1267 return not is_open, 0
1271 Output("download-iterative-data", "data"),
1272 State("store-selected-tests", "data"),
1273 Input("plot-btn-download", "n_clicks"),
1274 prevent_initial_call=True
1276 def _download_iterative_data(store_sel, _):
1277 """Download the data
1279 :param store_sel: List of tests selected by user stored in the
1281 :type store_sel: list
1282 :returns: dict of data frame content (base64 encoded) and meta data
1283 used by the Download component.
1291 for itm in store_sel:
1292 sel_data = select_iterative_data(self._data, itm)
1293 if sel_data is None:
1295 df = pd.concat([df, sel_data], ignore_index=True)
1297 return dcc.send_data_frame(df.to_csv, C.REPORT_DOWNLOAD_FILE_NAME)
1300 Output("metadata-tput-lat", "children"),
1301 Output("metadata-hdrh-graph", "children"),
1302 Output("offcanvas-metadata", "is_open"),
1303 Input({"type": "graph", "index": ALL}, "clickData"),
1304 prevent_initial_call=True
1306 def _show_metadata_from_graphs(graph_data: dict) -> tuple:
1307 """Generates the data for the offcanvas displayed when a particular
1308 point in a graph is clicked on.
1310 :param graph_data: The data from the clicked point in the graph.
1311 :type graph_data: dict
1312 :returns: The data to be displayed on the offcanvas and the
1313 information to show the offcanvas.
1314 :rtype: tuple(list, list, bool)
1317 trigger = Trigger(callback_context.triggered)
1318 if not trigger.value:
1321 return show_iterative_graph_data(
1322 trigger, graph_data, self._graph_layout)
1325 Output("offcanvas-documentation", "is_open"),
1326 Input("btn-documentation", "n_clicks"),
1327 State("offcanvas-documentation", "is_open")
1329 def toggle_offcanvas_documentation(n_clicks, is_open):