1 # Copyright (c) 2023 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 ast import literal_eval
30 from ..utils.constants import Constants as C
31 from ..utils.control_panel import ControlPanel
32 from ..utils.trigger import Trigger
33 from ..utils.utils import show_tooltip, label, sync_checklists, gen_new_url, \
34 generate_options, get_list_group_items
35 from ..utils.url_processing import url_decode
36 from .graphs import graph_iterative, select_iterative_data
39 # Control panel partameters and their default values.
45 "dd-dutver-opt": list(),
46 "dd-dutver-dis": True,
47 "dd-dutver-val": str(),
51 "dd-area-opt": list(),
54 "dd-test-opt": list(),
57 "cl-core-opt": list(),
58 "cl-core-val": list(),
59 "cl-core-all-val": list(),
60 "cl-core-all-opt": C.CL_ALL_DISABLED,
61 "cl-frmsize-opt": list(),
62 "cl-frmsize-val": list(),
63 "cl-frmsize-all-val": list(),
64 "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
65 "cl-tsttype-opt": list(),
66 "cl-tsttype-val": list(),
67 "cl-tsttype-all-val": list(),
68 "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
70 "cl-normalize-val": list()
75 """The layout of the dash app and the callbacks.
81 data_iterative: pd.DataFrame,
82 html_layout_file: str,
83 graph_layout_file: str,
87 - save the input parameters,
88 - read and pre-process the data,
89 - prepare data for the control panel,
90 - read HTML layout file,
91 - read tooltips from the tooltip file.
93 :param app: Flask application running the dash application.
94 :param html_layout_file: Path and name of the file specifying the HTML
95 layout of the dash application.
96 :param graph_layout_file: Path and name of the file with layout of
98 :param tooltip_file: Path and name of the yaml file specifying the
101 :type html_layout_file: str
102 :type graph_layout_file: str
103 :type tooltip_file: str
108 self._html_layout_file = html_layout_file
109 self._graph_layout_file = graph_layout_file
110 self._tooltip_file = tooltip_file
111 self._data = data_iterative
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 = row["dut_version"]
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 C.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(
165 if framesize.upper() not in \
166 tbs[rls][dut][d_ver][infra][area][test]["frame-size"]:
167 tbs[rls][dut][d_ver][infra][area][test]["frame-size"].append(
172 tbs[rls][dut][d_ver][infra][area][test]["test-type"]:
173 tbs[rls][dut][d_ver][infra][area][test]["test-type"].append(
176 elif ttype == "ndrpdr":
178 tbs[rls][dut][d_ver][infra][area][test]["test-type"]:
179 tbs[rls][dut][d_ver][infra][area][test]["test-type"].extend(
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:
262 dcc.Store(id="store-selected-tests"),
263 dcc.Store(id="store-control-panel"),
264 dcc.Location(id="url", refresh=False),
265 self._add_ctrl_col(),
266 self._add_plotting_col()
284 def _add_navbar(self):
285 """Add nav element with navigation panel. It is placed on the top.
287 :returns: Navigation bar.
288 :rtype: dbc.NavbarSimple
290 return dbc.NavbarSimple(
291 id="navbarsimple-main",
304 brand_external_link=True,
309 def _add_ctrl_col(self) -> dbc.Col:
310 """Add column with controls. It is placed on the left side.
312 :returns: Column with the control panel.
317 children=self._add_ctrl_panel(),
318 className="sticky-top"
322 def _add_plotting_col(self) -> dbc.Col:
323 """Add column with plots. It is placed on the right side.
325 :returns: Column with plots.
329 id="col-plotting-area",
335 class_name="g-0 p-0",
346 def _add_ctrl_panel(self) -> list:
347 """Add control panel.
349 :returns: Control panel.
354 class_name="g-0 p-1",
359 children=show_tooltip(
366 id={"type": "ctrl-dd", "index": "rls"},
367 placeholder="Select a Release...",
370 {"label": k, "value": k} \
371 for k in self._spec_tbs.keys()
373 key=lambda d: d["label"]
382 class_name="g-0 p-1",
387 children=show_tooltip(
394 id={"type": "ctrl-dd", "index": "dut"},
395 placeholder="Select a Device under Test..."
403 class_name="g-0 p-1",
408 children=show_tooltip(
415 id={"type": "ctrl-dd", "index": "dutver"},
417 "Select a Version of Device under Test..."
425 class_name="g-0 p-1",
430 children=show_tooltip(
437 id={"type": "ctrl-dd", "index": "phy"},
439 "Select a Physical Test Bed Topology..."
447 class_name="g-0 p-1",
452 children=show_tooltip(
459 id={"type": "ctrl-dd", "index": "area"},
460 placeholder="Select an Area..."
468 class_name="g-0 p-1",
473 children=show_tooltip(
480 id={"type": "ctrl-dd", "index": "test"},
481 placeholder="Select a Test..."
489 class_name="g-0 p-1",
494 children=show_tooltip(
505 "index": "frmsize-all"
507 options=C.CL_ALL_DISABLED,
526 style={"align-items": "center"},
532 class_name="g-0 p-1",
537 children=show_tooltip(
550 options=C.CL_ALL_DISABLED,
569 style={"align-items": "center"},
575 class_name="g-0 p-1",
580 children=show_tooltip(
591 "index": "tsttype-all"
593 options=C.CL_ALL_DISABLED,
612 style={"align-items": "center"},
618 class_name="g-0 p-1",
623 children=show_tooltip(
634 "value": "normalize",
636 "Normalize to CPU frequency "
647 style={"align-items": "center"},
653 class_name="g-0 p-1",
656 id={"type": "ctrl-btn", "index": "add-test"},
657 children="Add Selected",
663 id="row-card-sel-tests",
664 class_name="g-0 p-1",
665 style=C.STYLE_DISABLED,
668 class_name="overflow-auto p-0",
671 style={"max-height": "14em"},
677 id="row-btns-sel-tests",
678 class_name="g-0 p-1",
679 style=C.STYLE_DISABLED,
684 id={"type": "ctrl-btn", "index": "rm-test"},
685 children="Remove Selected",
691 id={"type": "ctrl-btn", "index": "rm-test-all"},
692 children="Remove All",
703 def _get_plotting_area(
709 """Generate the plotting area with all its content.
711 :param tests: List of tests to be displayed in the graphs.
712 :param normalize: If true, the values in graphs are normalized.
713 :param url: URL to be displayed in the modal window.
715 :type normalize: bool
717 :returns: List of rows with elements to be displayed in the plotting
724 figs = graph_iterative(self._data, tests, self._graph_layout, normalize)
732 id={"type": "graph", "index": "tput"},
735 class_name="g-0 p-1",
744 id={"type": "graph", "index": "lat"},
747 class_name="g-0 p-1",
755 class_name="g-0 p-0",
767 "text-transform": "none",
768 "padding": "0rem 1rem"
773 dbc.ModalHeader(dbc.ModalTitle("URL")),
782 id="plot-btn-download",
783 children="Download Data",
787 "text-transform": "none",
788 "padding": "0rem 1rem"
791 dcc.Download(id="download-iterative-data")
794 "d-grid gap-0 d-md-flex justify-content-md-end"
801 def callbacks(self, app):
802 """Callbacks for the whole application.
804 :param app: The application.
810 Output("store-control-panel", "data"),
811 Output("store-selected-tests", "data"),
812 Output("plotting-area", "children"),
813 Output("row-card-sel-tests", "style"),
814 Output("row-btns-sel-tests", "style"),
815 Output("lg-selected", "children"),
817 Output({"type": "ctrl-dd", "index": "rls"}, "value"),
818 Output({"type": "ctrl-dd", "index": "dut"}, "options"),
819 Output({"type": "ctrl-dd", "index": "dut"}, "disabled"),
820 Output({"type": "ctrl-dd", "index": "dut"}, "value"),
821 Output({"type": "ctrl-dd", "index": "dutver"}, "options"),
822 Output({"type": "ctrl-dd", "index": "dutver"}, "disabled"),
823 Output({"type": "ctrl-dd", "index": "dutver"}, "value"),
824 Output({"type": "ctrl-dd", "index": "phy"}, "options"),
825 Output({"type": "ctrl-dd", "index": "phy"}, "disabled"),
826 Output({"type": "ctrl-dd", "index": "phy"}, "value"),
827 Output({"type": "ctrl-dd", "index": "area"}, "options"),
828 Output({"type": "ctrl-dd", "index": "area"}, "disabled"),
829 Output({"type": "ctrl-dd", "index": "area"}, "value"),
830 Output({"type": "ctrl-dd", "index": "test"}, "options"),
831 Output({"type": "ctrl-dd", "index": "test"}, "disabled"),
832 Output({"type": "ctrl-dd", "index": "test"}, "value"),
833 Output({"type": "ctrl-cl", "index": "core"}, "options"),
834 Output({"type": "ctrl-cl", "index": "core"}, "value"),
835 Output({"type": "ctrl-cl", "index": "core-all"}, "value"),
836 Output({"type": "ctrl-cl", "index": "core-all"}, "options"),
837 Output({"type": "ctrl-cl", "index": "frmsize"}, "options"),
838 Output({"type": "ctrl-cl", "index": "frmsize"}, "value"),
839 Output({"type": "ctrl-cl", "index": "frmsize-all"}, "value"),
840 Output({"type": "ctrl-cl", "index": "frmsize-all"}, "options"),
841 Output({"type": "ctrl-cl", "index": "tsttype"}, "options"),
842 Output({"type": "ctrl-cl", "index": "tsttype"}, "value"),
843 Output({"type": "ctrl-cl", "index": "tsttype-all"}, "value"),
844 Output({"type": "ctrl-cl", "index": "tsttype-all"}, "options"),
845 Output({"type": "ctrl-btn", "index": "add-test"}, "disabled"),
846 Output("normalize", "value")
849 State("store-control-panel", "data"),
850 State("store-selected-tests", "data"),
851 State({"type": "sel-cl", "index": ALL}, "value")
854 Input("url", "href"),
855 Input("normalize", "value"),
857 Input({"type": "ctrl-dd", "index": ALL}, "value"),
858 Input({"type": "ctrl-cl", "index": ALL}, "value"),
859 Input({"type": "ctrl-btn", "index": ALL}, "n_clicks")
862 def _update_application(
870 """Update the application when the event is detected.
873 ctrl_panel = ControlPanel(CP_PARAMS, control_panel)
877 parsed_url = url_decode(href)
879 url_params = parsed_url["params"]
883 plotting_area = no_update
884 row_card_sel_tests = no_update
885 row_btns_sel_tests = no_update
886 lg_selected = no_update
888 trigger = Trigger(callback_context.triggered)
890 if trigger.type == "url" and url_params:
892 store_sel = literal_eval(url_params["store_sel"][0])
893 normalize = literal_eval(url_params["norm"][0])
894 except (KeyError, IndexError):
897 row_card_sel_tests = C.STYLE_ENABLED
898 row_btns_sel_tests = C.STYLE_ENABLED
899 last_test = store_sel[-1]
900 test = self._spec_tbs[last_test["rls"]][last_test["dut"]]\
901 [last_test["dutver"]][last_test["phy"]]\
902 [last_test["area"]][last_test["test"]]
904 "dd-rls-val": last_test["rls"],
905 "dd-dut-val": last_test["dut"],
906 "dd-dut-opt": generate_options(
907 self._spec_tbs[last_test["rls"]].keys()
910 "dd-dutver-val": last_test["dutver"],
911 "dd-dutver-opt": generate_options(
912 self._spec_tbs[last_test["rls"]]\
913 [last_test["dut"]].keys()
915 "dd-dutver-dis": False,
916 "dd-phy-val": last_test["phy"],
917 "dd-phy-opt": generate_options(
918 self._spec_tbs[last_test["rls"]][last_test["dut"]]\
919 [last_test["dutver"]].keys()
922 "dd-area-val": last_test["area"],
924 {"label": label(v), "value": v} for v in \
925 sorted(self._spec_tbs[last_test["rls"]]\
926 [last_test["dut"]][last_test["dutver"]]\
927 [last_test["phy"]].keys())
929 "dd-area-dis": False,
930 "dd-test-val": last_test["test"],
931 "dd-test-opt": generate_options(
932 self._spec_tbs[last_test["rls"]][last_test["dut"]]\
933 [last_test["dutver"]][last_test["phy"]]\
934 [last_test["area"]].keys()
936 "dd-test-dis": False,
937 "cl-core-opt": generate_options(test["core"]),
938 "cl-core-val": [last_test["core"].upper(), ],
939 "cl-core-all-val": list(),
940 "cl-core-all-opt": C.CL_ALL_ENABLED,
941 "cl-frmsize-opt": generate_options(test["frame-size"]),
942 "cl-frmsize-val": [last_test["framesize"].upper(), ],
943 "cl-frmsize-all-val": list(),
944 "cl-frmsize-all-opt": C.CL_ALL_ENABLED,
945 "cl-tsttype-opt": generate_options(test["test-type"]),
946 "cl-tsttype-val": [last_test["testtype"].upper(), ],
947 "cl-tsttype-all-val": list(),
948 "cl-tsttype-all-opt": C.CL_ALL_ENABLED,
949 "cl-normalize-val": normalize,
953 elif trigger.type == "normalize":
954 ctrl_panel.set({"cl-normalize-val": normalize})
956 elif trigger.type == "ctrl-dd":
957 if trigger.idx == "rls":
959 options = generate_options(
960 self._spec_tbs[trigger.value].keys()
967 "dd-rls-val": trigger.value,
969 "dd-dut-opt": options,
970 "dd-dut-dis": disabled,
971 "dd-dutver-val": str(),
972 "dd-dutver-opt": list(),
973 "dd-dutver-dis": True,
975 "dd-phy-opt": list(),
977 "dd-area-val": str(),
978 "dd-area-opt": list(),
980 "dd-test-val": str(),
981 "dd-test-opt": list(),
983 "cl-core-opt": list(),
984 "cl-core-val": list(),
985 "cl-core-all-val": list(),
986 "cl-core-all-opt": C.CL_ALL_DISABLED,
987 "cl-frmsize-opt": list(),
988 "cl-frmsize-val": list(),
989 "cl-frmsize-all-val": list(),
990 "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
991 "cl-tsttype-opt": list(),
992 "cl-tsttype-val": list(),
993 "cl-tsttype-all-val": list(),
994 "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
997 elif trigger.idx == "dut":
999 rls = ctrl_panel.get("dd-rls-val")
1000 dut = self._spec_tbs[rls][trigger.value]
1001 options = generate_options(dut.keys())
1007 "dd-dut-val": trigger.value,
1008 "dd-dutver-val": str(),
1009 "dd-dutver-opt": options,
1010 "dd-dutver-dis": disabled,
1011 "dd-phy-val": str(),
1012 "dd-phy-opt": list(),
1014 "dd-area-val": str(),
1015 "dd-area-opt": list(),
1016 "dd-area-dis": True,
1017 "dd-test-val": str(),
1018 "dd-test-opt": list(),
1019 "dd-test-dis": True,
1020 "cl-core-opt": list(),
1021 "cl-core-val": list(),
1022 "cl-core-all-val": list(),
1023 "cl-core-all-opt": C.CL_ALL_DISABLED,
1024 "cl-frmsize-opt": list(),
1025 "cl-frmsize-val": list(),
1026 "cl-frmsize-all-val": list(),
1027 "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
1028 "cl-tsttype-opt": list(),
1029 "cl-tsttype-val": list(),
1030 "cl-tsttype-all-val": list(),
1031 "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
1034 elif trigger.idx == "dutver":
1036 rls = ctrl_panel.get("dd-rls-val")
1037 dut = ctrl_panel.get("dd-dut-val")
1038 dutver = self._spec_tbs[rls][dut][trigger.value]
1039 options = generate_options(dutver.keys())
1045 "dd-dutver-val": trigger.value,
1046 "dd-phy-val": str(),
1047 "dd-phy-opt": options,
1048 "dd-phy-dis": disabled,
1049 "dd-area-val": str(),
1050 "dd-area-opt": list(),
1051 "dd-area-dis": True,
1052 "dd-test-val": str(),
1053 "dd-test-opt": list(),
1054 "dd-test-dis": True,
1055 "cl-core-opt": list(),
1056 "cl-core-val": list(),
1057 "cl-core-all-val": list(),
1058 "cl-core-all-opt": C.CL_ALL_DISABLED,
1059 "cl-frmsize-opt": list(),
1060 "cl-frmsize-val": list(),
1061 "cl-frmsize-all-val": list(),
1062 "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
1063 "cl-tsttype-opt": list(),
1064 "cl-tsttype-val": list(),
1065 "cl-tsttype-all-val": list(),
1066 "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
1069 elif trigger.idx == "phy":
1071 rls = ctrl_panel.get("dd-rls-val")
1072 dut = ctrl_panel.get("dd-dut-val")
1073 dutver = ctrl_panel.get("dd-dutver-val")
1074 phy = self._spec_tbs[rls][dut][dutver][trigger.value]
1075 options = [{"label": label(v), "value": v} \
1076 for v in sorted(phy.keys())]
1082 "dd-phy-val": trigger.value,
1083 "dd-area-val": str(),
1084 "dd-area-opt": options,
1085 "dd-area-dis": disabled,
1086 "dd-test-val": str(),
1087 "dd-test-opt": list(),
1088 "dd-test-dis": True,
1089 "cl-core-opt": list(),
1090 "cl-core-val": list(),
1091 "cl-core-all-val": list(),
1092 "cl-core-all-opt": C.CL_ALL_DISABLED,
1093 "cl-frmsize-opt": list(),
1094 "cl-frmsize-val": list(),
1095 "cl-frmsize-all-val": list(),
1096 "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
1097 "cl-tsttype-opt": list(),
1098 "cl-tsttype-val": list(),
1099 "cl-tsttype-all-val": list(),
1100 "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
1103 elif trigger.idx == "area":
1105 rls = ctrl_panel.get("dd-rls-val")
1106 dut = ctrl_panel.get("dd-dut-val")
1107 dutver = ctrl_panel.get("dd-dutver-val")
1108 phy = ctrl_panel.get("dd-phy-val")
1110 self._spec_tbs[rls][dut][dutver][phy][trigger.value]
1111 options = generate_options(area.keys())
1117 "dd-area-val": trigger.value,
1118 "dd-test-val": str(),
1119 "dd-test-opt": options,
1120 "dd-test-dis": disabled,
1121 "cl-core-opt": list(),
1122 "cl-core-val": list(),
1123 "cl-core-all-val": list(),
1124 "cl-core-all-opt": C.CL_ALL_DISABLED,
1125 "cl-frmsize-opt": list(),
1126 "cl-frmsize-val": list(),
1127 "cl-frmsize-all-val": list(),
1128 "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
1129 "cl-tsttype-opt": list(),
1130 "cl-tsttype-val": list(),
1131 "cl-tsttype-all-val": list(),
1132 "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
1135 elif trigger.idx == "test":
1136 rls = ctrl_panel.get("dd-rls-val")
1137 dut = ctrl_panel.get("dd-dut-val")
1138 dutver = ctrl_panel.get("dd-dutver-val")
1139 phy = ctrl_panel.get("dd-phy-val")
1140 area = ctrl_panel.get("dd-area-val")
1141 if all((rls, dut, dutver, phy, area, trigger.value, )):
1142 test = self._spec_tbs[rls][dut][dutver][phy][area]\
1145 "dd-test-val": trigger.value,
1146 "cl-core-opt": generate_options(test["core"]),
1147 "cl-core-val": list(),
1148 "cl-core-all-val": list(),
1149 "cl-core-all-opt": C.CL_ALL_ENABLED,
1151 generate_options(test["frame-size"]),
1152 "cl-frmsize-val": list(),
1153 "cl-frmsize-all-val": list(),
1154 "cl-frmsize-all-opt": C.CL_ALL_ENABLED,
1156 generate_options(test["test-type"]),
1157 "cl-tsttype-val": list(),
1158 "cl-tsttype-all-val": list(),
1159 "cl-tsttype-all-opt": C.CL_ALL_ENABLED,
1162 elif trigger.type == "ctrl-cl":
1163 param = trigger.idx.split("-")[0]
1164 if "-all" in trigger.idx:
1165 c_sel, c_all, c_id = list(), trigger.value, "all"
1167 c_sel, c_all, c_id = trigger.value, list(), str()
1168 val_sel, val_all = sync_checklists(
1169 options=ctrl_panel.get(f"cl-{param}-opt"),
1175 f"cl-{param}-val": val_sel,
1176 f"cl-{param}-all-val": val_all,
1178 if all((ctrl_panel.get("cl-core-val"),
1179 ctrl_panel.get("cl-frmsize-val"),
1180 ctrl_panel.get("cl-tsttype-val"), )):
1181 ctrl_panel.set({"btn-add-dis": False})
1183 ctrl_panel.set({"btn-add-dis": True})
1184 elif trigger.type == "ctrl-btn":
1186 if trigger.idx == "add-test":
1187 rls = ctrl_panel.get("dd-rls-val")
1188 dut = ctrl_panel.get("dd-dut-val")
1189 dutver = ctrl_panel.get("dd-dutver-val")
1190 phy = ctrl_panel.get("dd-phy-val")
1191 area = ctrl_panel.get("dd-area-val")
1192 test = ctrl_panel.get("dd-test-val")
1193 # Add selected test to the list of tests in store:
1194 if store_sel is None:
1196 for core in ctrl_panel.get("cl-core-val"):
1197 for framesize in ctrl_panel.get("cl-frmsize-val"):
1198 for ttype in ctrl_panel.get("cl-tsttype-val"):
1205 phy.replace("af_xdp", "af-xdp"),
1212 if tid not in [i["id"] for i in store_sel]:
1221 "framesize": framesize.lower(),
1222 "core": core.lower(),
1223 "testtype": ttype.lower()
1225 store_sel = sorted(store_sel, key=lambda d: d["id"])
1226 if C.CLEAR_ALL_INPUTS:
1227 ctrl_panel.set(ctrl_panel.defaults)
1228 elif trigger.idx == "rm-test" and lst_sel:
1229 new_store_sel = list()
1230 for idx, item in enumerate(store_sel):
1231 if not lst_sel[idx]:
1232 new_store_sel.append(item)
1233 store_sel = new_store_sel
1234 elif trigger.idx == "rm-test-all":
1239 lg_selected = get_list_group_items(store_sel, "sel-cl")
1240 plotting_area = self._get_plotting_area(
1245 {"store_sel": store_sel, "norm": normalize}
1248 row_card_sel_tests = C.STYLE_ENABLED
1249 row_btns_sel_tests = C.STYLE_ENABLED
1251 plotting_area = C.PLACEHOLDER
1252 row_card_sel_tests = C.STYLE_DISABLED
1253 row_btns_sel_tests = C.STYLE_DISABLED
1264 ret_val.extend(ctrl_panel.values)
1268 Output("plot-mod-url", "is_open"),
1269 [Input("plot-btn-url", "n_clicks")],
1270 [State("plot-mod-url", "is_open")],
1272 def toggle_plot_mod_url(n, is_open):
1273 """Toggle the modal window with url.
1280 Output("download-iterative-data", "data"),
1281 State("store-selected-tests", "data"),
1282 Input("plot-btn-download", "n_clicks"),
1283 prevent_initial_call=True
1285 def _download_trending_data(store_sel, _):
1286 """Download the data
1288 :param store_sel: List of tests selected by user stored in the
1290 :type store_sel: list
1291 :returns: dict of data frame content (base64 encoded) and meta data
1292 used by the Download component.
1300 for itm in store_sel:
1301 sel_data = select_iterative_data(self._data, itm)
1302 if sel_data is None:
1304 df = pd.concat([df, sel_data], ignore_index=True)
1306 return dcc.send_data_frame(df.to_csv, C.REPORT_DOWNLOAD_FILE_NAME)