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 ast import literal_eval
31 from ..utils.constants import Constants as C
32 from ..utils.utils import show_tooltip, label, sync_checklists, list_tests, \
33 gen_new_url, generate_options
34 from ..utils.url_processing import url_decode
35 from ..data.data import Data
36 from .graphs import graph_iterative, table_comparison, get_short_version, \
41 """The layout of the dash app and the callbacks.
44 def __init__(self, app: Flask, releases: list, html_layout_file: str,
45 graph_layout_file: str, data_spec_file: str, tooltip_file: str) -> None:
47 - save the input parameters,
48 - read and pre-process the data,
49 - prepare data for the control panel,
50 - read HTML layout file,
51 - read tooltips from the tooltip file.
53 :param app: Flask application running the dash application.
54 :param releases: Lis of releases to be displayed.
55 :param html_layout_file: Path and name of the file specifying the HTML
56 layout of the dash application.
57 :param graph_layout_file: Path and name of the file with layout of
59 :param data_spec_file: Path and name of the file specifying the data to
60 be read from parquets for this application.
61 :param tooltip_file: Path and name of the yaml file specifying the
65 :type html_layout_file: str
66 :type graph_layout_file: str
67 :type data_spec_file: str
68 :type tooltip_file: str
73 self.releases = releases
74 self._html_layout_file = html_layout_file
75 self._graph_layout_file = graph_layout_file
76 self._data_spec_file = data_spec_file
77 self._tooltip_file = tooltip_file
80 self._data = pd.DataFrame()
82 data_mrr = Data(self._data_spec_file, True).\
83 read_iterative_mrr(release=rls.replace("csit", "rls"))
84 data_mrr["release"] = rls
85 data_ndrpdr = Data(self._data_spec_file, True).\
86 read_iterative_ndrpdr(release=rls.replace("csit", "rls"))
87 data_ndrpdr["release"] = rls
88 self._data = pd.concat(
89 [self._data, data_mrr, data_ndrpdr], ignore_index=True)
91 # Get structure of tests:
93 cols = ["job", "test_id", "test_type", "dut_version", "release"]
94 for _, row in self._data[cols].drop_duplicates().iterrows():
96 ttype = row["test_type"]
97 lst_job = row["job"].split("-")
99 d_ver = get_short_version(row["dut_version"], dut)
100 tbed = "-".join(lst_job[-2:])
101 lst_test_id = row["test_id"].split(".")
105 area = "-".join(lst_test_id[3:-2])
106 suite = lst_test_id[-2].replace("2n1l-", "").replace("1n1l-", "").\
108 test = lst_test_id[-1]
109 nic = suite.split("-")[0]
110 for drv in C.DRIVERS:
112 driver = drv.replace("-", "_")
113 test = test.replace(f"{drv}-", "")
117 infra = "-".join((tbed, nic, driver))
118 lst_test = test.split("-")
119 framesize = lst_test[0]
120 core = lst_test[1] if lst_test[1] else "8C"
121 test = "-".join(lst_test[2: -1])
123 if tbs.get(rls, None) is None:
125 if tbs[rls].get(dut, None) is None:
126 tbs[rls][dut] = dict()
127 if tbs[rls][dut].get(d_ver, None) is None:
128 tbs[rls][dut][d_ver] = dict()
129 if tbs[rls][dut][d_ver].get(infra, None) is None:
130 tbs[rls][dut][d_ver][infra] = dict()
131 if tbs[rls][dut][d_ver][infra].get(area, None) is None:
132 tbs[rls][dut][d_ver][infra][area] = dict()
133 if tbs[rls][dut][d_ver][infra][area].get(test, None) is None:
134 tbs[rls][dut][d_ver][infra][area][test] = dict()
135 tbs[rls][dut][d_ver][infra][area][test]["core"] = list()
136 tbs[rls][dut][d_ver][infra][area][test]["frame-size"] = list()
137 tbs[rls][dut][d_ver][infra][area][test]["test-type"] = list()
138 if core.upper() not in \
139 tbs[rls][dut][d_ver][infra][area][test]["core"]:
140 tbs[rls][dut][d_ver][infra][area][test]["core"].append(
142 if framesize.upper() not in \
143 tbs[rls][dut][d_ver][infra][area][test]["frame-size"]:
144 tbs[rls][dut][d_ver][infra][area][test]["frame-size"].append(
148 tbs[rls][dut][d_ver][infra][area][test]["test-type"]:
149 tbs[rls][dut][d_ver][infra][area][test]["test-type"].append(
151 elif ttype == "ndrpdr":
153 tbs[rls][dut][d_ver][infra][area][test]["test-type"]:
154 tbs[rls][dut][d_ver][infra][area][test]["test-type"].extend(
159 self._html_layout = ""
160 self._graph_layout = None
161 self._tooltips = dict()
164 with open(self._html_layout_file, "r") as file_read:
165 self._html_layout = file_read.read().\
166 replace("_title_", C.REPORT_TITLE)
167 except IOError as err:
169 f"Not possible to open the file {self._html_layout_file}\n{err}"
173 with open(self._graph_layout_file, "r") as file_read:
174 self._graph_layout = load(file_read, Loader=FullLoader)
175 except IOError as err:
177 f"Not possible to open the file {self._graph_layout_file}\n"
180 except YAMLError as err:
182 f"An error occurred while parsing the specification file "
183 f"{self._graph_layout_file}\n{err}"
187 with open(self._tooltip_file, "r") as file_read:
188 self._tooltips = load(file_read, Loader=FullLoader)
189 except IOError as err:
191 f"Not possible to open the file {self._tooltip_file}\n{err}"
193 except YAMLError as err:
195 f"An error occurred while parsing the specification file "
196 f"{self._tooltip_file}\n{err}"
200 if self._app is not None and hasattr(self, 'callbacks'):
201 self.callbacks(self._app)
204 def html_layout(self):
205 return self._html_layout
209 return self._spec_tbs
217 return self._graph_layout
219 def add_content(self):
220 """Top level method which generated the web page.
223 - Store for user input data,
225 - Main area with control panel and ploting area.
227 If no HTML layout is provided, an error message is displayed instead.
229 :returns: The HTML div with the whole page.
233 if self.html_layout and self.spec_tbs:
248 id="offcanvas-metadata",
249 title="Throughput And Latency",
253 dbc.Row(id="metadata-tput-lat"),
254 dbc.Row(id="metadata-hdrh-graph"),
262 dcc.Store(id="selected-tests"),
263 dcc.Store(id="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 and tables. It is placed on the right side.
325 :returns: Column with tables.
329 id="col-plotting-area",
334 class_name="g-0 p-2",
337 dbc.Row( # Throughput
339 class_name="g-0 p-2",
340 children=[C.PLACEHOLDER, ]
347 class_name="g-0 p-2",
348 children=[C.PLACEHOLDER, ]
356 class_name="g-0 p-2",
357 children=[C.PLACEHOLDER, ]
360 id="row-btn-download",
361 class_name="g-0 p-2",
362 children=[C.PLACEHOLDER, ]
370 def _add_ctrl_panel(self) -> dbc.Row:
371 """Add control panel.
373 :returns: Control panel.
378 class_name="g-0 p-1",
383 children=show_tooltip(self._tooltips,
384 "help-release", "CSIT Release")
388 placeholder=("Select a Release..."),
391 {"label": k, "value": k} \
392 for k in self.spec_tbs.keys()
394 key=lambda d: d["label"]
403 class_name="g-0 p-1",
408 children=show_tooltip(self._tooltips,
414 "Select a Device under Test..."
423 class_name="g-0 p-1",
428 children=show_tooltip(self._tooltips,
429 "help-dut-ver", "DUT Version")
434 "Select a Version of "
435 "Device under Test..."
444 class_name="g-0 p-1",
449 children=show_tooltip(self._tooltips,
450 "help-infra", "Infra")
455 "Select a Physical Test Bed "
465 class_name="g-0 p-1",
470 children=show_tooltip(self._tooltips,
475 placeholder="Select an Area...",
484 class_name="g-0 p-1",
489 children=show_tooltip(self._tooltips,
494 placeholder="Select a Test...",
503 class_name="g-0 p-1",
506 children=show_tooltip(self._tooltips,
507 "help-framesize", "Frame Size"),
512 id="cl-ctrl-framesize-all",
513 options=C.CL_ALL_DISABLED,
523 id="cl-ctrl-framesize",
532 class_name="g-0 p-1",
535 children=show_tooltip(self._tooltips,
536 "help-cores", "Number of Cores"),
541 id="cl-ctrl-core-all",
542 options=C.CL_ALL_DISABLED,
561 class_name="g-0 p-1",
564 children=show_tooltip(self._tooltips,
565 "help-ttype", "Test Type"),
570 id="cl-ctrl-testtype-all",
571 options=C.CL_ALL_DISABLED,
581 id="cl-ctrl-testtype",
590 class_name="g-0 p-1",
593 children=show_tooltip(self._tooltips,
594 "help-normalize", "Normalize"),
599 id="cl-ctrl-normalize",
601 "value": "normalize",
603 "Normalize results to CPU "
616 class_name="g-0 p-1",
622 children="Add Selected",
630 id="row-card-sel-tests",
631 class_name="g-0 p-1",
632 style=C.STYLE_DISABLED,
634 dbc.Label("Selected tests"),
636 class_name="overflow-auto",
640 style={"max-height": "20em"},
645 id="row-btns-sel-tests",
646 class_name="g-0 p-1",
647 style=C.STYLE_DISABLED,
653 children="Remove Selected",
659 id="btn-sel-remove-all",
660 children="Remove All",
672 """A class representing the control panel.
675 def __init__(self, panel: dict) -> None:
676 """Initialisation of the control pannel by default values. If
677 particular values are provided (parameter "panel") they are set
680 :param panel: Custom values to be set to the control panel.
681 :param default: Default values to be set to the control panel.
686 # Defines also the order of keys
688 "dd-rls-value": str(),
689 "dd-dut-options": list(),
690 "dd-dut-disabled": True,
691 "dd-dut-value": str(),
692 "dd-dutver-options": list(),
693 "dd-dutver-disabled": True,
694 "dd-dutver-value": str(),
695 "dd-phy-options": list(),
696 "dd-phy-disabled": True,
697 "dd-phy-value": str(),
698 "dd-area-options": list(),
699 "dd-area-disabled": True,
700 "dd-area-value": str(),
701 "dd-test-options": list(),
702 "dd-test-disabled": True,
703 "dd-test-value": str(),
704 "cl-core-options": list(),
705 "cl-core-value": list(),
706 "cl-core-all-value": list(),
707 "cl-core-all-options": C.CL_ALL_DISABLED,
708 "cl-framesize-options": list(),
709 "cl-framesize-value": list(),
710 "cl-framesize-all-value": list(),
711 "cl-framesize-all-options": C.CL_ALL_DISABLED,
712 "cl-testtype-options": list(),
713 "cl-testtype-value": list(),
714 "cl-testtype-all-value": list(),
715 "cl-testtype-all-options": C.CL_ALL_DISABLED,
716 "btn-add-disabled": True,
717 "cl-normalize-value": list(),
718 "cl-selected-options": list()
721 self._panel = deepcopy(self._defaults)
723 for key in self._defaults:
724 self._panel[key] = panel[key]
727 def defaults(self) -> dict:
728 return self._defaults
731 def panel(self) -> dict:
734 def set(self, kwargs: dict) -> None:
735 """Set the values of the Control panel.
737 :param kwargs: key - value pairs to be set.
739 :raises KeyError: If the key in kwargs is not present in the Control
742 for key, val in kwargs.items():
743 if key in self._panel:
744 self._panel[key] = val
746 raise KeyError(f"The key {key} is not defined.")
748 def get(self, key: str) -> any:
749 """Returns the value of a key from the Control panel.
751 :param key: The key which value should be returned.
753 :returns: The value of the key.
755 :raises KeyError: If the key in kwargs is not present in the Control
758 return self._panel[key]
760 def values(self) -> tuple:
761 """Returns the values from the Control panel as a list.
763 :returns: The values from the Control panel.
766 return tuple(self._panel.values())
768 def callbacks(self, app):
769 """Callbacks for the whole application.
771 :param app: The application.
775 def _generate_plotting_area(figs: tuple, table: pd.DataFrame,
777 """Generate the plotting area with all its content.
779 :param figs: Figures to be placed in the plotting area.
780 :param table: A table to be placed in the plotting area bellow the
782 :param utl: The URL to be placed in the plotting area bellow the
784 :type figs: tuple of plotly.graph_objects.Figure
785 :type table: pandas.DataFrame
787 :returns: tuple of elements to be shown in the plotting area.
789 (dcc.Graph, dcc.Graph, dbc.Table, list(dbc.Col, dbc.Col))
792 (fig_tput, fig_lat) = figs
794 row_fig_tput = C.PLACEHOLDER
795 row_fig_lat = C.PLACEHOLDER
796 row_table = C.PLACEHOLDER
797 row_btn_dwnld = C.PLACEHOLDER
802 id={"type": "graph", "index": "tput"},
810 dcc.Loading(children=[
812 id="btn-download-data",
813 children=show_tooltip(self._tooltips,
814 "help-download", "Download Data"),
818 dcc.Download(id="download-data")
830 children=show_tooltip(self._tooltips,
831 "help-url", "URL", "input-url")
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-ctrl-normalize", "value"),
904 Output("cl-selected", "options"), # User selection
905 State("control-panel", "data"), # Store
906 State("selected-tests", "data"), # Store
907 State("cl-selected", "value"), # User selection
908 Input("dd-ctrl-rls", "value"),
909 Input("dd-ctrl-dut", "value"),
910 Input("dd-ctrl-dutver", "value"),
911 Input("dd-ctrl-phy", "value"),
912 Input("dd-ctrl-area", "value"),
913 Input("dd-ctrl-test", "value"),
914 Input("cl-ctrl-core", "value"),
915 Input("cl-ctrl-core-all", "value"),
916 Input("cl-ctrl-framesize", "value"),
917 Input("cl-ctrl-framesize-all", "value"),
918 Input("cl-ctrl-testtype", "value"),
919 Input("cl-ctrl-testtype-all", "value"),
920 Input("cl-ctrl-normalize", "value"),
921 Input("btn-ctrl-add", "n_clicks"),
922 Input("btn-sel-remove", "n_clicks"),
923 Input("btn-sel-remove-all", "n_clicks"),
926 def _update_ctrl_panel(cp_data: dict, store_sel: list, list_sel: list,
927 dd_rls: str, dd_dut: str, dd_dutver: str, dd_phy: str, dd_area: str,
928 dd_test: str, cl_core: list, cl_core_all: list, cl_framesize: list,
929 cl_framesize_all: list, cl_testtype: list, cl_testtype_all: list,
930 cl_normalize: list, btn_add: int, btn_remove: int,
931 btn_remove_all: int, href: str) -> tuple:
932 """Update the application when the event is detected.
934 :param cp_data: Current status of the control panel stored in
936 :param store_sel: List of tests selected by user stored in the
938 :param list_sel: List of tests selected by the user shown in the
940 :param dd_rls: Input - Releases.
941 :param dd_dut: Input - DUTs.
942 :param dd_dutver: Input - Version of DUT.
943 :param dd_phy: Input - topo- arch-nic-driver.
944 :param dd_area: Input - Tested area.
945 :param dd_test: Input - Test.
946 :param cl_core: Input - Number of cores.
947 :param cl_core_all: Input - All numbers of cores.
948 :param cl_framesize: Input - Frame sizes.
949 :param cl_framesize_all: Input - All frame sizes.
950 :param cl_testtype: Input - Test type (NDR, PDR, MRR).
951 :param cl_testtype_all: Input - All test types.
952 :param cl_normalize: Input - Normalize the results.
953 :param btn_add: Input - Button "Add Selected" tests.
954 :param btn_remove: Input - Button "Remove selected" tests.
955 :param btn_remove_all: Input - Button "Remove All" tests.
956 :param href: Input - The URL provided by the browser.
958 :type store_sel: list
967 :type cl_core_all: list
968 :type cl_framesize: list
969 :type cl_framesize_all: list
970 :type cl_testtype: list
971 :type cl_testtype_all: list
972 :type cl_normalize: list
974 :type btn_remove: int
975 :type btn_remove_all: int
977 :returns: New values for web page elements.
981 ctrl_panel = self.ControlPanel(cp_data)
985 parsed_url = url_decode(href)
987 url_params = parsed_url["params"]
991 row_fig_tput = no_update
992 row_fig_lat = no_update
993 row_table = no_update
994 row_btn_dwnld = no_update
995 row_card_sel_tests = no_update
996 row_btns_sel_tests = no_update
998 trigger_id = callback_context.triggered[0]["prop_id"].split(".")[0]
1000 if trigger_id == "dd-ctrl-rls":
1003 generate_options(sorted(self.spec_tbs[dd_rls].keys()))
1009 "dd-rls-value": dd_rls,
1010 "dd-dut-value": str(),
1011 "dd-dut-options": options,
1012 "dd-dut-disabled": disabled,
1013 "dd-dutver-value": str(),
1014 "dd-dutver-options": list(),
1015 "dd-dutver-disabled": True,
1016 "dd-phy-value": str(),
1017 "dd-phy-options": list(),
1018 "dd-phy-disabled": True,
1019 "dd-area-value": str(),
1020 "dd-area-options": list(),
1021 "dd-area-disabled": True,
1022 "dd-test-value": str(),
1023 "dd-test-options": list(),
1024 "dd-test-disabled": True,
1025 "cl-core-options": list(),
1026 "cl-core-value": list(),
1027 "cl-core-all-value": list(),
1028 "cl-core-all-options": C.CL_ALL_DISABLED,
1029 "cl-framesize-options": list(),
1030 "cl-framesize-value": list(),
1031 "cl-framesize-all-value": list(),
1032 "cl-framesize-all-options": C.CL_ALL_DISABLED,
1033 "cl-testtype-options": list(),
1034 "cl-testtype-value": list(),
1035 "cl-testtype-all-value": list(),
1036 "cl-testtype-all-options": C.CL_ALL_DISABLED
1038 elif trigger_id == "dd-ctrl-dut":
1040 rls = ctrl_panel.get("dd-rls-value")
1041 dut = self.spec_tbs[rls][dd_dut]
1042 options = generate_options(sorted(dut.keys()))
1048 "dd-dut-value": dd_dut,
1049 "dd-dutver-value": str(),
1050 "dd-dutver-options": options,
1051 "dd-dutver-disabled": disabled,
1052 "dd-phy-value": str(),
1053 "dd-phy-options": list(),
1054 "dd-phy-disabled": True,
1055 "dd-area-value": str(),
1056 "dd-area-options": list(),
1057 "dd-area-disabled": True,
1058 "dd-test-value": str(),
1059 "dd-test-options": list(),
1060 "dd-test-disabled": True,
1061 "cl-core-options": list(),
1062 "cl-core-value": list(),
1063 "cl-core-all-value": list(),
1064 "cl-core-all-options": C.CL_ALL_DISABLED,
1065 "cl-framesize-options": list(),
1066 "cl-framesize-value": list(),
1067 "cl-framesize-all-value": list(),
1068 "cl-framesize-all-options": C.CL_ALL_DISABLED,
1069 "cl-testtype-options": list(),
1070 "cl-testtype-value": list(),
1071 "cl-testtype-all-value": list(),
1072 "cl-testtype-all-options": C.CL_ALL_DISABLED
1074 elif trigger_id == "dd-ctrl-dutver":
1076 rls = ctrl_panel.get("dd-rls-value")
1077 dut = ctrl_panel.get("dd-dut-value")
1078 dutver = self.spec_tbs[rls][dut][dd_dutver]
1079 options = generate_options(sorted(dutver.keys()))
1085 "dd-dutver-value": dd_dutver,
1086 "dd-phy-value": str(),
1087 "dd-phy-options": options,
1088 "dd-phy-disabled": disabled,
1089 "dd-area-value": str(),
1090 "dd-area-options": list(),
1091 "dd-area-disabled": True,
1092 "dd-test-value": str(),
1093 "dd-test-options": list(),
1094 "dd-test-disabled": True,
1095 "cl-core-options": list(),
1096 "cl-core-value": list(),
1097 "cl-core-all-value": list(),
1098 "cl-core-all-options": C.CL_ALL_DISABLED,
1099 "cl-framesize-options": list(),
1100 "cl-framesize-value": list(),
1101 "cl-framesize-all-value": list(),
1102 "cl-framesize-all-options": C.CL_ALL_DISABLED,
1103 "cl-testtype-options": list(),
1104 "cl-testtype-value": list(),
1105 "cl-testtype-all-value": list(),
1106 "cl-testtype-all-options": C.CL_ALL_DISABLED
1108 elif trigger_id == "dd-ctrl-phy":
1110 rls = ctrl_panel.get("dd-rls-value")
1111 dut = ctrl_panel.get("dd-dut-value")
1112 dutver = ctrl_panel.get("dd-dutver-value")
1113 phy = self.spec_tbs[rls][dut][dutver][dd_phy]
1114 options = [{"label": label(v), "value": v} \
1115 for v in sorted(phy.keys())]
1121 "dd-phy-value": dd_phy,
1122 "dd-area-value": str(),
1123 "dd-area-options": options,
1124 "dd-area-disabled": disabled,
1125 "dd-test-value": str(),
1126 "dd-test-options": list(),
1127 "dd-test-disabled": True,
1128 "cl-core-options": list(),
1129 "cl-core-value": list(),
1130 "cl-core-all-value": list(),
1131 "cl-core-all-options": C.CL_ALL_DISABLED,
1132 "cl-framesize-options": list(),
1133 "cl-framesize-value": list(),
1134 "cl-framesize-all-value": list(),
1135 "cl-framesize-all-options": C.CL_ALL_DISABLED,
1136 "cl-testtype-options": list(),
1137 "cl-testtype-value": list(),
1138 "cl-testtype-all-value": list(),
1139 "cl-testtype-all-options": C.CL_ALL_DISABLED
1141 elif trigger_id == "dd-ctrl-area":
1143 rls = ctrl_panel.get("dd-rls-value")
1144 dut = ctrl_panel.get("dd-dut-value")
1145 dutver = ctrl_panel.get("dd-dutver-value")
1146 phy = ctrl_panel.get("dd-phy-value")
1147 area = self.spec_tbs[rls][dut][dutver][phy][dd_area]
1148 options = generate_options(sorted(area.keys()))
1154 "dd-area-value": dd_area,
1155 "dd-test-value": str(),
1156 "dd-test-options": options,
1157 "dd-test-disabled": disabled,
1158 "cl-core-options": list(),
1159 "cl-core-value": list(),
1160 "cl-core-all-value": list(),
1161 "cl-core-all-options": C.CL_ALL_DISABLED,
1162 "cl-framesize-options": list(),
1163 "cl-framesize-value": list(),
1164 "cl-framesize-all-value": list(),
1165 "cl-framesize-all-options": C.CL_ALL_DISABLED,
1166 "cl-testtype-options": list(),
1167 "cl-testtype-value": list(),
1168 "cl-testtype-all-value": list(),
1169 "cl-testtype-all-options": C.CL_ALL_DISABLED
1171 elif trigger_id == "dd-ctrl-test":
1172 rls = ctrl_panel.get("dd-rls-value")
1173 dut = ctrl_panel.get("dd-dut-value")
1174 dutver = ctrl_panel.get("dd-dutver-value")
1175 phy = ctrl_panel.get("dd-phy-value")
1176 area = ctrl_panel.get("dd-area-value")
1177 if all((rls, dut, dutver, phy, area, dd_test, )):
1178 test = self.spec_tbs[rls][dut][dutver][phy][area][dd_test]
1180 "dd-test-value": dd_test,
1181 "cl-core-options": \
1182 generate_options(sorted(test["core"])),
1183 "cl-core-value": list(),
1184 "cl-core-all-value": list(),
1185 "cl-core-all-options": C.CL_ALL_ENABLED,
1186 "cl-framesize-options": \
1187 generate_options(sorted(test["frame-size"])),
1188 "cl-framesize-value": list(),
1189 "cl-framesize-all-value": list(),
1190 "cl-framesize-all-options": C.CL_ALL_ENABLED,
1191 "cl-testtype-options": \
1192 generate_options(sorted(test["test-type"])),
1193 "cl-testtype-value": list(),
1194 "cl-testtype-all-value": list(),
1195 "cl-testtype-all-options": C.CL_ALL_ENABLED,
1197 elif trigger_id == "cl-ctrl-core":
1198 val_sel, val_all = sync_checklists(
1199 options=ctrl_panel.get("cl-core-options"),
1205 "cl-core-value": val_sel,
1206 "cl-core-all-value": val_all,
1208 elif trigger_id == "cl-ctrl-core-all":
1209 val_sel, val_all = sync_checklists(
1210 options = ctrl_panel.get("cl-core-options"),
1216 "cl-core-value": val_sel,
1217 "cl-core-all-value": val_all,
1219 elif trigger_id == "cl-ctrl-framesize":
1220 val_sel, val_all = sync_checklists(
1221 options = ctrl_panel.get("cl-framesize-options"),
1227 "cl-framesize-value": val_sel,
1228 "cl-framesize-all-value": val_all,
1230 elif trigger_id == "cl-ctrl-framesize-all":
1231 val_sel, val_all = sync_checklists(
1232 options = ctrl_panel.get("cl-framesize-options"),
1234 all=cl_framesize_all,
1238 "cl-framesize-value": val_sel,
1239 "cl-framesize-all-value": val_all,
1241 elif trigger_id == "cl-ctrl-testtype":
1242 val_sel, val_all = sync_checklists(
1243 options = ctrl_panel.get("cl-testtype-options"),
1249 "cl-testtype-value": val_sel,
1250 "cl-testtype-all-value": val_all,
1252 elif trigger_id == "cl-ctrl-testtype-all":
1253 val_sel, val_all = sync_checklists(
1254 options = ctrl_panel.get("cl-testtype-options"),
1256 all=cl_testtype_all,
1260 "cl-testtype-value": val_sel,
1261 "cl-testtype-all-value": val_all,
1263 elif trigger_id == "btn-ctrl-add":
1265 rls = ctrl_panel.get("dd-rls-value")
1266 dut = ctrl_panel.get("dd-dut-value")
1267 dutver = ctrl_panel.get("dd-dutver-value")
1268 phy = ctrl_panel.get("dd-phy-value")
1269 area = ctrl_panel.get("dd-area-value")
1270 test = ctrl_panel.get("dd-test-value")
1271 cores = ctrl_panel.get("cl-core-value")
1272 framesizes = ctrl_panel.get("cl-framesize-value")
1273 testtypes = ctrl_panel.get("cl-testtype-value")
1274 # Add selected test to the list of tests in store:
1275 if all((rls, dut, dutver, phy, area, test, cores, framesizes,
1277 if store_sel is None:
1280 for framesize in framesizes:
1281 for ttype in testtypes:
1284 tid = "-".join((rls, dut, dutver,
1285 phy.replace('af_xdp', 'af-xdp'), area,
1286 framesize.lower(), core.lower(), test,
1288 if tid not in [itm["id"] for itm in store_sel]:
1297 "framesize": framesize.lower(),
1298 "core": core.lower(),
1299 "testtype": ttype.lower()
1301 store_sel = sorted(store_sel, key=lambda d: d["id"])
1302 row_card_sel_tests = C.STYLE_ENABLED
1303 row_btns_sel_tests = C.STYLE_ENABLED
1304 if C.CLEAR_ALL_INPUTS:
1305 ctrl_panel.set(ctrl_panel.defaults)
1307 "cl-selected-options": list_tests(store_sel)
1309 elif trigger_id == "btn-sel-remove-all":
1311 row_fig_tput = C.PLACEHOLDER
1312 row_fig_lat = C.PLACEHOLDER
1313 row_table = C.PLACEHOLDER
1314 row_btn_dwnld = C.PLACEHOLDER
1315 row_card_sel_tests = C.STYLE_DISABLED
1316 row_btns_sel_tests = C.STYLE_DISABLED
1318 ctrl_panel.set({"cl-selected-options": list()})
1319 elif trigger_id == "btn-sel-remove":
1322 new_store_sel = list()
1323 for item in store_sel:
1324 if item["id"] not in list_sel:
1325 new_store_sel.append(item)
1326 store_sel = new_store_sel
1327 elif trigger_id == "url":
1330 store_sel = literal_eval(url_params["store_sel"][0])
1331 norm = literal_eval(url_params["norm"][0])
1332 except (KeyError, IndexError):
1335 row_card_sel_tests = C.STYLE_ENABLED
1336 row_btns_sel_tests = C.STYLE_ENABLED
1337 last_test = store_sel[-1]
1338 test = self.spec_tbs[last_test["rls"]]\
1339 [last_test["dut"]][last_test["dutver"]]\
1340 [last_test["phy"]][last_test["area"]]\
1343 "dd-rls-value": last_test["rls"],
1344 "dd-dut-value": last_test["dut"],
1345 "dd-dut-options": generate_options(sorted(
1346 self.spec_tbs[last_test["rls"]].keys())),
1347 "dd-dut-disabled": False,
1348 "dd-dutver-value": last_test["dutver"],
1349 "dd-dutver-options": generate_options(sorted(
1350 self.spec_tbs[last_test["rls"]]\
1351 [last_test["dut"]].keys())),
1352 "dd-dutver-disabled": False,
1353 "dd-phy-value": last_test["phy"],
1354 "dd-phy-options": generate_options(sorted(
1355 self.spec_tbs[last_test["rls"]]\
1357 [last_test["dutver"]].keys())),
1358 "dd-phy-disabled": False,
1359 "dd-area-value": last_test["area"],
1360 "dd-area-options": [
1361 {"label": label(v), "value": v} for v in \
1362 sorted(self.spec_tbs[last_test["rls"]]\
1363 [last_test["dut"]][last_test["dutver"]]\
1364 [last_test["phy"]].keys())
1366 "dd-area-disabled": False,
1367 "dd-test-value": last_test["test"],
1368 "dd-test-options": generate_options(sorted(
1369 self.spec_tbs[last_test["rls"]]\
1370 [last_test["dut"]][last_test["dutver"]]\
1372 [last_test["area"]].keys())),
1373 "dd-test-disabled": False,
1374 "cl-core-options": generate_options(sorted(
1376 "cl-core-value": [last_test["core"].upper(), ],
1377 "cl-core-all-value": list(),
1378 "cl-core-all-options": C.CL_ALL_ENABLED,
1379 "cl-framesize-options": generate_options(
1380 sorted(test["frame-size"])),
1381 "cl-framesize-value": \
1382 [last_test["framesize"].upper(), ],
1383 "cl-framesize-all-value": list(),
1384 "cl-framesize-all-options": C.CL_ALL_ENABLED,
1385 "cl-testtype-options": generate_options(sorted(
1386 test["test-type"])),
1387 "cl-testtype-value": \
1388 [last_test["testtype"].upper(), ],
1389 "cl-testtype-all-value": list(),
1390 "cl-testtype-all-options": C.CL_ALL_ENABLED
1393 if trigger_id in ("btn-ctrl-add", "url", "btn-sel-remove",
1394 "cl-ctrl-normalize"):
1396 row_fig_tput, row_fig_lat, row_table, row_btn_dwnld = \
1397 _generate_plotting_area(
1399 self.data, store_sel, self.layout, bool(norm)
1402 self.data, store_sel, bool(norm)
1406 {"store_sel": store_sel, "norm": norm}
1410 "cl-selected-options": list_tests(store_sel)
1413 row_fig_tput = C.PLACEHOLDER
1414 row_fig_lat = C.PLACEHOLDER
1415 row_table = C.PLACEHOLDER
1416 row_btn_dwnld = C.PLACEHOLDER
1417 row_card_sel_tests = C.STYLE_DISABLED
1418 row_btns_sel_tests = C.STYLE_DISABLED
1420 ctrl_panel.set({"cl-selected-options": list()})
1422 if ctrl_panel.get("cl-core-value") and \
1423 ctrl_panel.get("cl-framesize-value") and \
1424 ctrl_panel.get("cl-testtype-value"):
1429 "btn-add-disabled": disabled,
1430 "cl-normalize-value": norm
1434 ctrl_panel.panel, store_sel,
1435 row_fig_tput, row_fig_lat, row_table, row_btn_dwnld,
1436 row_card_sel_tests, row_btns_sel_tests
1438 ret_val.extend(ctrl_panel.values())
1442 Output("download-data", "data"),
1443 State("selected-tests", "data"),
1444 Input("btn-download-data", "n_clicks"),
1445 prevent_initial_call=True
1447 def _download_data(store_sel, n_clicks):
1448 """Download the data
1450 :param store_sel: List of tests selected by user stored in the
1452 :param n_clicks: Number of clicks on the button "Download".
1453 :type store_sel: list
1455 :returns: dict of data frame content (base64 encoded) and meta data
1456 used by the Download component.
1467 for itm in store_sel:
1468 sel_data = select_iterative_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, C.REPORT_DOWNLOAD_FILE_NAME)