gap=2,
children=[
dbc.Button(
+ "Add Telemetry Panel",
id={"type": "telemetry-btn", "index": "open"},
- children="Telemetry",
- color="info"
- ),
- dbc.Button(
- id="plot-btn-url",
- children="Show URL",
color="info"
),
+ dbc.Button("Show URL", id="plot-btn-url", color="info"),
dbc.Modal(
[
dbc.ModalHeader(dbc.ModalTitle("URL")),
style=C.STYLE_DISABLED,
)
- def _plotting_area_trending(
- self,
- tests: list,
- normalize: bool
- ) -> dbc.Col:
+ @staticmethod
+ def _plotting_area_trending(graphs: list) -> dbc.Col:
"""Generate the plotting area with all its content.
- :param tests: A list of tests to be displayed in the trending graphs.
- :param normalize: If True, the data in graphs is normalized.
- :type tests: list
- :type normalize: bool
+ :param graphs: A list of graphs to be displayed in the trending page.
+ :type graphs: list
:returns: A collumn with trending graphs (tput and latency) in tabs.
:rtype: dbc.Col
"""
- if not tests:
+ if not graphs:
return C.PLACEHOLDER
- figs = graph_trending(self._data, tests, self._graph_layout, normalize)
-
- if not figs[0]:
+ if not graphs[0]:
return C.PLACEHOLDER
tab_items = [
dbc.Tab(
children=dcc.Graph(
id={"type": "graph", "index": "tput"},
- figure=figs[0]
+ figure=graphs[0]
),
label="Throughput",
tab_id="tab-tput"
)
]
- if figs[1]:
+ if graphs[1]:
tab_items.append(
dbc.Tab(
children=dcc.Graph(
id={"type": "graph", "index": "lat"},
- figure=figs[1]
+ figure=graphs[1]
),
label="Latency",
tab_id="tab-lat"
)
trending = [
- dbc.Row(children=[
+ dbc.Row(
dbc.Tabs(
children=tab_items,
id="tabs",
active_tab="tab-tput",
- )
- ]),
+ ),
+ class_name="g-0 p-0"
+ ),
dbc.Row(
- [
- dbc.Col([html.Div(
- [
- dbc.Button(
- id="plot-btn-download",
- children="Download Data",
- class_name="me-1",
- color="info",
- style={"padding": "0rem 1rem"}
- ),
- dcc.Download(id="download-trending-data")
- ],
- className=\
- "d-grid gap-0 d-md-flex justify-content-md-end"
- )])
- ],
+ html.Div(
+ [
+ dbc.Button(
+ "Download Data",
+ id="plot-btn-download",
+ class_name="me-1",
+ color="info",
+ style={"padding": "0rem 1rem"}
+ ),
+ dcc.Download(id="download-trending-data")
+ ],
+ className="d-grid gap-0 d-md-flex justify-content-md-end"
+ ),
class_name="g-0 p-0"
)
]
return dbc.Col(
children=[
- dbc.Row(
- dbc.Accordion(
- children=[
- dbc.AccordionItem(
- title="Trending",
- children=trending
- )
- ],
- class_name="g-0 p-1",
- start_collapsed=False,
- always_open=True,
- active_item=["item-0", ]
- ),
- class_name="g-0 p-0",
+ dbc.Accordion(
+ dbc.AccordionItem(trending, title="Trending"),
+ class_name="g-0 p-1",
+ start_collapsed=False,
+ always_open=True,
+ active_item=["item-0", ]
),
dbc.Modal(
[
close_button=False
),
dbc.Spinner(
- dbc.ModalBody(self._get_telemetry_step_1()),
+ dbc.ModalBody(Layout._get_telemetry_step_1()),
delay_show=2 * C.SPINNER_DELAY
),
dbc.ModalFooter([
dbc.Button(
"Select",
id={"type": "telemetry-btn", "index": "select"},
+ color="success",
disabled=True
),
dbc.Button(
"Cancel",
id={"type": "telemetry-btn", "index": "cancel"},
+ color="info",
disabled=False
),
dbc.Button(
- "Remove",
- id={"type": "telemetry-btn", "index": "remove"},
+ "Remove All",
+ id={"type": "telemetry-btn", "index": "rm-all"},
+ color="danger",
disabled=False
)
])
close_button=False
),
dbc.Spinner(
- dbc.ModalBody(self._get_telemetry_step_2()),
+ dbc.ModalBody(Layout._get_telemetry_step_2()),
delay_show=2 * C.SPINNER_DELAY
),
dbc.ModalFooter([
dbc.Button(
"Back",
id={"type": "telemetry-btn", "index": "back"},
+ color="info",
disabled=False
),
dbc.Button(
- "Display Telemetry",
+ "Add Telemetry Panel",
id={"type": "telemetry-btn", "index": "add"},
+ color="success",
disabled=True
),
dbc.Button(
"Cancel",
id={"type": "telemetry-btn", "index": "cancel"},
+ color="info",
disabled=False
)
])
]
)
- def _plotting_area_telemetry(self, graphs: list) -> dbc.Col:
+ @staticmethod
+ def _plotting_area_telemetry(graphs: list) -> dbc.Col:
"""Generate the plotting area with telemetry.
+
+ :param graphs: A list of graphs to be displayed in the telemetry page.
+ :type graphs: list
+ :returns: A collumn with telemetry trending graphs.
+ :rtype: dbc.Col
"""
if not graphs:
return C.PLACEHOLDER
- acc_items = list()
- for graph in graphs:
- acc_items.append(
+ def _plural(iterative):
+ return "s" if len(iterative) > 1 else str()
+
+ panels = list()
+ for idx, graph_set in enumerate(graphs):
+ acc_items = list()
+ for graph in graph_set[0]:
+ graph_name = ", ".join(graph[1])
+ acc_items.append(
+ dbc.AccordionItem(
+ dcc.Graph(
+ id={"type": "graph-telemetry", "index": graph_name},
+ figure=graph[0]
+ ),
+ title=(f"Test{_plural(graph[1])}: {graph_name}"),
+ class_name="g-0 p-0"
+ )
+ )
+ panels.append(
dbc.AccordionItem(
- title=f"Telemetry: {graph[1]}" if graph[1] else "Telemetry",
- children=dcc.Graph(
- id={"type": "graph-telemetry", "index": graph[1]},
- figure=graph[0]
+ [
+ dbc.Row(
+ dbc.Accordion(
+ children=acc_items,
+ class_name="g-0 p-0",
+ start_collapsed=True,
+ always_open=True,
+ flush=True
+ ),
+ class_name="g-0 p-0"
+ ),
+ dbc.Row(
+ html.Div(
+ [
+ dbc.Button(
+ "Remove",
+ id={
+ "type": "tm-btn-remove",
+ "index": idx
+ },
+ class_name="me-1",
+ color="danger",
+ style={"padding": "0rem 1rem"}
+ ),
+ dbc.Button(
+ "Download Data",
+ id={
+ "type": "tm-btn-download",
+ "index": idx
+ },
+ class_name="me-1",
+ color="info",
+ style={"padding": "0rem 1rem"}
+ )
+ ],
+ className=\
+ "d-grid gap-0 d-md-flex justify-content-md-end"
+ ),
+ class_name="g-0 p-0"
+ )
+ ],
+ class_name="g-0 p-0",
+ title=(
+ f"Metric{_plural(graph_set[1])}: ",
+ ", ".join(graph_set[1])
)
)
)
return dbc.Col(
- children=[
- dbc.Row(
- dbc.Accordion(
- children=acc_items,
- class_name="g-0 p-1",
- start_collapsed=False,
- always_open=True,
- active_item=[f"item-{i}" for i in range(len(acc_items))]
- ),
- class_name="g-0 p-0",
- )
- ]
+ dbc.Accordion(
+ panels,
+ class_name="g-0 p-1",
+ start_collapsed=True,
+ always_open=True
+ )
)
@staticmethod
State({"type": "telemetry-btn", "index": ALL}, "disabled"),
State({"type": "tm-container", "index": ALL}, "children"),
State({"type": "tm-list-metrics", "index": ALL}, "value"),
+ State({"type": "tele-cl", "index": ALL}, "value"),
Input("url", "href"),
- Input({"type": "tele-cl", "index": ALL}, "value"),
Input({"type": "tm-dd", "index": ALL}, "value"),
Input("normalize", "value"),
Input({"type": "telemetry-search-in", "index": ALL}, "value"),
Input({"type": "telemetry-btn", "index": ALL}, "n_clicks"),
+ Input({"type": "tm-btn-remove", "index": ALL}, "n_clicks"),
Input({"type": "ctrl-dd", "index": ALL}, "value"),
Input({"type": "ctrl-cl", "index": ALL}, "value"),
Input({"type": "ctrl-btn", "index": ALL}, "n_clicks"),
tm_btns_disabled: list,
tm_dd: list,
list_metrics: list,
- href: str,
cl_metrics: list,
+ href: str,
tm_dd_in: list,
*_
) -> tuple:
store = {
"control-panel": dict(),
"selected-tests": list(),
+ "trending-graphs": None,
"telemetry-data": dict(),
"selected-metrics": dict(),
+ "telemetry-panels": list(),
+ "telemetry-all-in-one": list(),
+ "telemetry-graphs": list(),
"url": str()
}
store_sel = store["selected-tests"]
tm_data = store["telemetry-data"]
tm_user = store["selected-metrics"]
+ tm_panels = store["telemetry-panels"]
+ tm_all_in_one = store["telemetry-all-in-one"]
plotting_area_telemetry = no_update
on_draw = [False, False] # 0 --> trending, 1 --> telemetry
store_sel = literal_eval(url_params["store_sel"][0])
normalize = literal_eval(url_params["norm"][0])
telemetry = literal_eval(url_params["telemetry"][0])
- all_in_one = literal_eval(url_params["all-in-one"][0])
- except (KeyError, IndexError, AttributeError):
+ tm_all_in_one = literal_eval(url_params["all-in-one"][0])
+ if not isinstance(telemetry, list):
+ telemetry = [telemetry, ]
+ tm_all_in_one = [tm_all_in_one, ]
+ except (KeyError, IndexError, AttributeError, ValueError):
pass
if store_sel:
last_test = store_sel[-1]
"cl-normalize-val": normalize,
"btn-add-dis": False
})
+ store["trending-graphs"] = None
+ store["telemetry-graphs"] = list()
on_draw[0] = True
if telemetry:
tm = TelemetryData(store_sel)
tm.from_dataframe(self._data)
tm_data = tm.to_json()
tm.from_json(tm_data)
- tm_user["selected_metrics_with_labels"] = telemetry
+ tm_panels = telemetry
on_draw[1] = True
elif trigger.type == "normalize":
ctrl_panel.set({"cl-normalize-val": trigger.value})
+ store["trending-graphs"] = None
on_draw[0] = True
elif trigger.type == "ctrl-dd":
if trigger.idx == "dut":
f"cl-{param}-val": val_sel,
f"cl-{param}-all-val": val_all,
})
- if all((ctrl_panel.get("cl-core-val"),
+ if all((ctrl_panel.get("cl-core-val"),
ctrl_panel.get("cl-frmsize-val"),
ctrl_panel.get("cl-tsttype-val"), )):
ctrl_panel.set({"btn-add-dis": False})
else:
ctrl_panel.set({"btn-add-dis": True})
elif trigger.type == "ctrl-btn":
- on_draw[0] = True
+ tm_panels = list()
+ tm_all_in_one = list()
+ store["trending-graphs"] = None
+ store["telemetry-graphs"] = list()
+ # on_draw[0] = True
+ on_draw = [True, True]
if trigger.idx == "add-test":
dut = ctrl_panel.get("dd-dut-val")
phy = ctrl_panel.get("dd-phy-val")
"tele-cl", False),
)
is_open = (True, False)
- tm_btns_disabled[1], tm_btns_disabled[5] = True, True
+ tm_btns_disabled[1], tm_btns_disabled[5] = False, True
elif trigger.idx == "select":
- tm.from_json(tm_data)
if any(cl_metrics):
+ tm.from_json(tm_data)
if not tm_user["selected_metrics"]:
tm_user["selected_metrics"] = \
tm_user["unique_metrics"]
tm_btns_disabled[4] = False
is_open = (False, True)
else:
- tm_user = None
- is_open = (False, False)
+ is_open = (True, False)
elif trigger.idx == "add":
tm.from_json(tm_data)
+ tm_panels.append(tm_user["selected_metrics_with_labels"])
+ tm_all_in_one.append(all_in_one)
is_open = (False, False)
tm_btns_disabled[1], tm_btns_disabled[5] = True, True
on_draw = [True, True]
elif trigger.idx == "cancel":
is_open = (False, False)
tm_btns_disabled[1], tm_btns_disabled[5] = True, True
- elif trigger.idx == "remove":
+ elif trigger.idx == "rm-all":
+ tm_panels = list()
+ tm_all_in_one = list()
tm_user = None
is_open = (False, False)
tm_btns_disabled[1], tm_btns_disabled[5] = True, True
colorize=False
), )
is_open = (True, False)
- elif trigger.type == "tele-cl":
- if any(cl_metrics):
- tm_btns_disabled[1] = False
- else:
- tm_btns_disabled[1] = True
- is_open = (True, False)
elif trigger.type == "tm-dd":
tm.from_metrics_with_labels(
tm_user["unique_metrics_with_labels"]
tm_btns_disabled[5] = False
else:
list_metrics[0] = str()
+ elif trigger.type == "tm-btn-remove":
+ del tm_panels[trigger.idx]
+ del tm_all_in_one[trigger.idx]
+ del store["telemetry-graphs"][trigger.idx]
+ tm.from_json(tm_data)
+ on_draw = [True, True]
new_url_params = {
"store_sel": store_sel,
"norm": ctrl_panel.get("cl-normalize-val")
}
- if tm_user and tm_user.get("selected_metrics_with_labels", None):
- new_url_params["telemetry"] = \
- tm_user["selected_metrics_with_labels"]
- new_url_params["all-in-one"] = all_in_one
+ if tm_panels:
+ new_url_params["telemetry"] = tm_panels
+ new_url_params["all-in-one"] = tm_all_in_one
if on_draw[0]: # Trending
if store_sel:
lg_selected = get_list_group_items(store_sel, "sel-cl")
- plotting_area_trending = self._plotting_area_trending(
- store_sel,
- bool(ctrl_panel.get("cl-normalize-val"))
- )
- if on_draw[1]: # Telemetry
- plotting_area_telemetry = self._plotting_area_telemetry(
- graph_tm_trending(
- tm.select_tm_trending_data(
- tm_user["selected_metrics_with_labels"]
- ),
+ if store["trending-graphs"]:
+ graphs = store["trending-graphs"]
+ else:
+ graphs = graph_trending(
+ self._data,
+ store_sel,
+ self._graph_layout,
+ bool(ctrl_panel.get("cl-normalize-val"))
+ )
+ if graphs and graphs[0]:
+ store["trending-graphs"] = graphs
+ plotting_area_trending = \
+ Layout._plotting_area_trending(graphs)
+
+ # Telemetry
+ start_idx = len(store["telemetry-graphs"])
+ end_idx = len(tm_panels)
+ if not end_idx:
+ plotting_area_telemetry = C.PLACEHOLDER
+ elif on_draw[1] and (end_idx >= start_idx):
+ for idx in range(start_idx, end_idx):
+ store["telemetry-graphs"].append(graph_tm_trending(
+ tm.select_tm_trending_data(tm_panels[idx]),
self._graph_layout,
- False if not all_in_one else all_in_one[0]
+ bool(tm_all_in_one[idx][0])
+ ))
+ plotting_area_telemetry = \
+ Layout._plotting_area_telemetry(
+ store["telemetry-graphs"]
)
- )
col_plotting_area = C.STYLE_ENABLED
row_card_sel_tests = C.STYLE_ENABLED
row_btns_sel_tests = C.STYLE_ENABLED
row_btns_add_tm = C.STYLE_DISABLED
lg_selected = no_update
store_sel = list()
+ tm_panels = list()
+ tm_all_in_one = list()
tm_user = None
else:
plotting_area_trending = no_update
store["selected-tests"] = store_sel
store["telemetry-data"] = tm_data
store["selected-metrics"] = tm_user
+ store["telemetry-panels"] = tm_panels
+ store["telemetry-all-in-one"] = tm_all_in_one
ret_val = [
store,
plotting_area_trending,
Output("download-trending-data", "data"),
State("store", "data"),
Input("plot-btn-download", "n_clicks"),
+ Input({"type": "tm-btn-download", "index": ALL}, "n_clicks"),
prevent_initial_call=True
)
- def _download_trending_data(store: list, _) -> dict:
+ def _download_data(store: list, *_) -> dict:
"""Download the data
:param store_sel: List of tests selected by user stored in the
raise PreventUpdate
if not store["selected-tests"]:
raise PreventUpdate
-
+
df = pd.DataFrame()
- for itm in store["selected-tests"]:
- sel_data = select_trending_data(self._data, itm)
- if sel_data is None:
- continue
- df = pd.concat([df, sel_data], ignore_index=True, copy=False)
+
+ trigger = Trigger(callback_context.triggered)
+ if not trigger.value:
+ raise PreventUpdate
+
+ if trigger.type == "plot-btn-download":
+ data = list()
+ for itm in store["selected-tests"]:
+ sel_data = select_trending_data(self._data, itm)
+ if sel_data is None:
+ continue
+ data.append(sel_data)
+ df = pd.concat(data, ignore_index=True, copy=False)
+ file_name = C.TREND_DOWNLOAD_FILE_NAME
+ elif trigger.type == "tm-btn-download":
+ tm = TelemetryData(store["selected-tests"])
+ tm.from_json(store["telemetry-data"])
+ df = tm.select_tm_trending_data(
+ store["telemetry-panels"][trigger.idx]
+ )
+ file_name = C.TELEMETRY_DOWNLOAD_FILE_NAME
+ else:
+ raise PreventUpdate
- return dcc.send_data_frame(df.to_csv, C.TREND_DOWNLOAD_FILE_NAME)
+ return dcc.send_data_frame(df.to_csv, file_name)