X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;ds=sidebyside;f=resources%2Ftools%2Fdash%2Fapp%2Fpal%2Ftrending%2Flayout.py;h=66af2f0de24a5fd14b6a1b05034c0ff62e7d488e;hb=45615cddd926540756f19328cad7078cfc9a1219;hp=6369a027cf2fac6b556fa3f62e453ac7b617f192;hpb=1996a8e398190a1930607acf72c6f5ce16091e6d;p=csit.git
diff --git a/resources/tools/dash/app/pal/trending/layout.py b/resources/tools/dash/app/pal/trending/layout.py
index 6369a027cf..66af2f0de2 100644
--- a/resources/tools/dash/app/pal/trending/layout.py
+++ b/resources/tools/dash/app/pal/trending/layout.py
@@ -14,25 +14,34 @@
"""Plotly Dash HTML layout override.
"""
-
import pandas as pd
from dash import dcc
from dash import html
from dash import callback_context, no_update
-from dash import Input, Output, State, callback
+from dash import Input, Output, State
from dash.exceptions import PreventUpdate
+import dash_bootstrap_components as dbc
from yaml import load, FullLoader, YAMLError
from datetime import datetime, timedelta
from ..data.data import Data
-from .graphs import trending_tput
+from .graphs import graph_trending, graph_hdrh_latency, \
+ select_trending_data
class Layout:
"""
"""
+ STYLE_HIDEN = {"display": "none"}
+ STYLE_BLOCK = {"display": "block", "vertical-align": "top"}
+ STYLE_INLINE ={
+ "display": "inline-block",
+ "vertical-align": "top"
+ }
+ NO_GRAPH = {"data": [], "layout": {}, "frames": []}
+
def __init__(self, app, html_layout_file, spec_file, graph_layout_file,
data_spec_file):
"""
@@ -128,6 +137,7 @@ class Layout:
id="div-main",
children=[
dcc.Store(id="selected-tests"),
+ self._add_navbar(),
self._add_ctrl_div(),
self._add_plotting_div()
]
@@ -138,6 +148,22 @@ class Layout:
children="An Error Occured."
)
+ def _add_navbar(self):
+ """Add nav element with navigation panel. It is placed on the top.
+ """
+ return dbc.NavbarSimple(
+ children=[
+ dbc.NavItem(
+ dbc.NavLink("Continuous Performance Trending", href="#")
+ )
+ ],
+ brand="Dashboard",
+ brand_href="/",
+ color="dark",
+ dark=True,
+ fluid=True,
+ )
+
def _add_ctrl_div(self):
"""Add div with controls. It is placed on the left side.
"""
@@ -154,8 +180,7 @@ class Layout:
],
style={
"display": "inline-block",
- "width": "18%",
- "padding": "5px"
+ "width": "20%"
}
)
@@ -165,21 +190,107 @@ class Layout:
return html.Div(
id="div-plotting-area",
children=[
- dcc.Loading(
- id="loading-graph",
- children=[
- dcc.Graph(
- id="graph"
- )
- ],
- type="circle"
- )
+ html.Table(children=[
+ html.Tr(
+ id="div-tput",
+ style=self.STYLE_HIDEN,
+ children=[
+ html.Td(children=[
+ dcc.Loading(
+ dcc.Graph(
+ id="graph-tput"
+ ),
+ )
+ ], style={"width": "80%"}),
+ html.Td(children=[
+ dcc.Clipboard(
+ target_id="tput-metadata",
+ title="Copy",
+ style={"display": "inline-block"}
+ ),
+ html.Nobr(" "),
+ html.Nobr(" "),
+ dcc.Markdown(
+ children="**Throughput**",
+ style={"display": "inline-block"}
+ ),
+ html.Pre(
+ id="tput-metadata",
+ children="Click on data point in the graph"
+ ),
+ html.Div(
+ id="div-lat-metadata",
+ style=self.STYLE_HIDEN,
+ children=[
+ dcc.Clipboard(
+ target_id="lat-metadata",
+ title="Copy",
+ style={"display": "inline-block"}
+ ),
+ html.Nobr(" "),
+ html.Nobr(" "),
+ dcc.Markdown(
+ children="**Latency**",
+ style={"display": "inline-block"}
+ ),
+ html.Pre(
+ id="lat-metadata",
+ children= \
+ "Click on data point in the graph"
+ )
+ ]
+ )
+ ], style={"width": "20%"}),
+ ]
+ ),
+ html.Tr(
+ id="div-latency",
+ style=self.STYLE_HIDEN,
+ children=[
+ html.Td(children=[
+ dcc.Loading(
+ dcc.Graph(
+ id="graph-latency"
+ )
+ )
+ ], style={"width": "80%"}),
+ html.Td(children=[
+ dcc.Loading(
+ dcc.Graph(
+ id="graph-latency-hdrh",
+ style=self.STYLE_INLINE,
+ figure=self.NO_GRAPH
+ )
+ )
+ ], style={"width": "20%"}),
+ ]
+ ),
+ html.Tr(
+ id="div-download",
+ style=self.STYLE_HIDEN,
+ children=[
+ html.Td(children=[
+ dcc.Loading(
+ children=[
+ html.Button(
+ id="btn-download-data",
+ children=["Download Data"]
+ ),
+ dcc.Download(id="download-data")
+ ]
+ )
+ ], style={"width": "80%"}),
+ html.Td(children=[
+ html.Nobr(" ")
+ ], style={"width": "20%"}),
+ ]
+ ),
+ ]),
],
style={
"vertical-align": "top",
- "display": "none",
- "width": "80%",
- "padding": "5px"
+ "display": "inline-block",
+ "width": "80%"
}
)
@@ -458,13 +569,17 @@ class Layout:
return _sync_checklists(opt, sel, all, "cl-ctrl-testtype")
@app.callback(
- Output("graph", "figure"),
+ Output("graph-tput", "figure"),
+ Output("graph-latency", "figure"),
+ Output("div-tput", "style"),
+ Output("div-latency", "style"),
+ Output("div-lat-metadata", "style"),
+ Output("div-download", "style"),
Output("selected-tests", "data"), # Store
Output("cl-selected", "options"), # User selection
Output("dd-ctrl-phy", "value"),
Output("dd-ctrl-area", "value"),
Output("dd-ctrl-test", "value"),
- Output("div-plotting-area", "style"),
State("selected-tests", "data"), # Store
State("cl-selected", "value"),
State("dd-ctrl-phy", "value"),
@@ -498,24 +613,46 @@ class Layout:
else:
return list()
+ class RetunValue:
+ def __init__(self) -> None:
+ self._output = {
+ "graph-tput-figure": no_update,
+ "graph-lat-figure": no_update,
+ "div-tput-style": no_update,
+ "div-latency-style": no_update,
+ "div-lat-metadata-style": no_update,
+ "div-download-style": no_update,
+ "selected-tests-data": no_update,
+ "cl-selected-options": no_update,
+ "dd-ctrl-phy-value": no_update,
+ "dd-ctrl-area-value": no_update,
+ "dd-ctrl-test-value": no_update,
+ }
+
+ def value(self):
+ return tuple(self._output.values())
+
+ def set_values(self, kwargs: dict) -> None:
+ for key, val in kwargs.items():
+ if key in self._output:
+ self._output[key] = val
+ else:
+ raise KeyError(f"The key {key} is not defined.")
+
+
trigger_id = callback_context.triggered[0]["prop_id"].split(".")[0]
- d_start = datetime(
- int(d_start[0:4]), int(d_start[5:7]), int(d_start[8:10])
- )
- d_end = datetime(
- int(d_end[0:4]), int(d_end[5:7]), int(d_end[8:10])
- )
+ d_start = datetime(int(d_start[0:4]), int(d_start[5:7]),
+ int(d_start[8:10]))
+ d_end = datetime(int(d_end[0:4]), int(d_end[5:7]), int(d_end[8:10]))
+
+ output = RetunValue()
if trigger_id == "btn-ctrl-add":
# Add selected test to the list of tests in store:
if phy and area and test and cores and framesizes and testtypes:
-
- # TODO: Add validation
-
if store_sel is None:
store_sel = list()
-
for core in cores:
for framesize in framesizes:
for ttype in testtypes:
@@ -537,15 +674,32 @@ class Layout:
"core": core.lower(),
"testtype": ttype.lower()
})
- return (no_update, store_sel, _list_tests(), None,
- None, None, no_update)
+ output.set_values({
+ "selected-tests-data": store_sel,
+ "cl-selected-options": _list_tests(),
+ "dd-ctrl-phy-value": None,
+ "dd-ctrl-area-value": None,
+ "dd-ctrl-test-value": None,
+ })
elif trigger_id in ("btn-sel-display", "dpr-period"):
- fig, style = trending_tput(
+ fig_tput, fig_lat = graph_trending(
self.data, store_sel, self.layout, d_start, d_end
)
- return (fig, no_update, no_update,
- no_update, no_update, no_update, style)
+ output.set_values({
+ "graph-tput-figure": \
+ fig_tput if fig_tput else self.NO_GRAPH,
+ "graph-lat-figure": \
+ fig_lat if fig_lat else self.NO_GRAPH,
+ "div-tput-style": \
+ self.STYLE_BLOCK if fig_tput else self.STYLE_HIDEN,
+ "div-latency-style": \
+ self.STYLE_BLOCK if fig_lat else self.STYLE_HIDEN,
+ "div-lat-metadata-style": \
+ self.STYLE_BLOCK if fig_lat else self.STYLE_HIDEN,
+ "div-download-style": \
+ self.STYLE_BLOCK if fig_tput else self.STYLE_HIDEN,
+ })
elif trigger_id == "btn-sel-remove":
if list_sel:
@@ -555,17 +709,92 @@ class Layout:
new_store_sel.append(item)
store_sel = new_store_sel
if store_sel:
- fig, style = trending_tput(
+ fig_tput, fig_lat = graph_trending(
self.data, store_sel, self.layout, d_start, d_end
)
- return (fig, store_sel, _list_tests(),
- no_update, no_update, no_update, style)
+ output.set_values({
+ "graph-tput-figure": \
+ fig_tput if fig_tput else self.NO_GRAPH,
+ "graph-lat-figure": \
+ fig_lat if fig_lat else self.NO_GRAPH,
+ "div-tput-style": \
+ self.STYLE_BLOCK if fig_tput else self.STYLE_HIDEN,
+ "div-latency-style": \
+ self.STYLE_BLOCK if fig_lat else self.STYLE_HIDEN,
+ "div-lat-metadata-style": \
+ self.STYLE_BLOCK if fig_lat else self.STYLE_HIDEN,
+ "div-download-style": \
+ self.STYLE_BLOCK if fig_tput else self.STYLE_HIDEN,
+ "selected-tests-data": store_sel,
+ "cl-selected-options": _list_tests()
+ })
else:
- style={
- "vertical-align": "top",
- "display": "none",
- "width": "80%",
- "padding": "5px"
- }
- return (no_update, store_sel, _list_tests(),
- no_update, no_update, no_update, style)
+ output.set_values({
+ "graph-tput-figure": self.NO_GRAPH,
+ "graph-lat-figure": self.NO_GRAPH,
+ "div-tput-style": self.STYLE_HIDEN,
+ "div-latency-style": self.STYLE_HIDEN,
+ "div-lat-metadata-style": self.STYLE_HIDEN,
+ "div-download-style": self.STYLE_HIDEN,
+ "selected-tests-data": store_sel,
+ "cl-selected-options": _list_tests()
+ })
+
+ return output.value()
+
+ @app.callback(
+ Output("tput-metadata", "children"),
+ Input("graph-tput", "clickData")
+ )
+ def _show_tput_metadata(hover_data):
+ """
+ """
+ if not hover_data:
+ raise PreventUpdate
+
+ return hover_data["points"][0]["text"].replace("
", "\n")
+
+ @app.callback(
+ Output("graph-latency-hdrh", "figure"),
+ Output("graph-latency-hdrh", "style"),
+ Output("lat-metadata", "children"),
+ Input("graph-latency", "clickData")
+ )
+ def _show_latency_hdhr(hover_data):
+ """
+ """
+ if not hover_data:
+ raise PreventUpdate
+
+ graph = no_update
+ hdrh_data = hover_data["points"][0].get("customdata", None)
+ if hdrh_data:
+ graph = graph_hdrh_latency(hdrh_data, self.layout)
+
+ return (
+ graph,
+ self.STYLE_INLINE,
+ hover_data["points"][0]["text"].replace("
", "\n")
+ )
+
+ @app.callback(
+ Output("download-data", "data"),
+ State("selected-tests", "data"),
+ Input("btn-download-data", "n_clicks"),
+ prevent_initial_call=True
+ )
+ def _download_data(store_sel, n_clicks):
+ """
+ """
+
+ if not n_clicks:
+ raise PreventUpdate
+
+ df = pd.DataFrame()
+ for itm in store_sel:
+ sel_data = select_trending_data(self.data, itm)
+ if sel_data is None:
+ continue
+ df = pd.concat([df, sel_data], ignore_index=True)
+
+ return dcc.send_data_frame(df.to_csv, "trending_data.csv")