aboutsummaryrefslogtreecommitdiffstats
path: root/resources/tools/dash/app/pal/trending/layout.py
diff options
context:
space:
mode:
Diffstat (limited to 'resources/tools/dash/app/pal/trending/layout.py')
-rw-r--r--resources/tools/dash/app/pal/trending/layout.py364
1 files changed, 335 insertions, 29 deletions
diff --git a/resources/tools/dash/app/pal/trending/layout.py b/resources/tools/dash/app/pal/trending/layout.py
index 1f60aecd83..69ce0c416d 100644
--- a/resources/tools/dash/app/pal/trending/layout.py
+++ b/resources/tools/dash/app/pal/trending/layout.py
@@ -11,33 +11,339 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Plotly Dash HTML layout override."""
-
-html_layout = """
-<!DOCTYPE html>
- <html>
- <head>
- {%metas%}
- <title>{%title%}</title>
- {%favicon%}
- {%css%}
- </head>
- <body class="dash-template">
- <header>
- <div class="nav-wrapper">
- <a href="/">
- <h1>Continuous Performance Trending</h1>
- </a>
- <nav>
- </nav>
- </div>
- </header>
- {%app_entry%}
- <footer>
- {%config%}
- {%scripts%}
- {%renderer%}
- </footer>
- </body>
- </html>
+"""Plotly Dash HTML layout override.
"""
+
+import logging
+
+from dash import dcc
+from dash import html
+from dash import Input, Output, callback
+from dash.exceptions import PreventUpdate
+from yaml import load, FullLoader, YAMLError
+
+
+class Layout:
+ """
+ """
+
+ def __init__(self, app, html_layout_file, spec_file):
+ """
+ """
+
+ # Inputs
+ self._app = app
+ self._html_layout_file = html_layout_file
+ self._spec_file = spec_file
+
+ # Read from files:
+ self._html_layout = ""
+ self._spec_test = None
+
+ try:
+ with open(self._html_layout_file, "r") as layout_file:
+ self._html_layout = layout_file.read()
+ except IOError as err:
+ logging.error(f"Not possible to open the file {layout_file}\n{err}")
+
+ try:
+ with open(self._spec_file, "r") as file_read:
+ self._spec_test = load(file_read, Loader=FullLoader)
+ except IOError as err:
+ logging.error(f"Not possible to open the file {spec_file}\n{err}")
+ except YAMLError as err:
+ logging.error(
+ f"An error occurred while parsing the specification file "
+ f"{spec_file}\n"
+ f"{err}"
+ )
+
+ # Callbacks:
+ if self._app is not None and hasattr(self, 'callbacks'):
+ self.callbacks(self._app)
+
+ # User choice (one test):
+ self._test_selection = {
+ "phy": "",
+ "area": "",
+ "test": "",
+ "core": "",
+ "frame-size": "",
+ "test-type": ""
+ }
+
+ @property
+ def html_layout(self):
+ return self._html_layout
+
+ @property
+ def spec_test(self):
+ return self._spec_test
+
+ def _reset_test_selection(self):
+ self._test_selection = {
+ "phy": "",
+ "area": "",
+ "test": "",
+ "core": "",
+ "frame-size": "",
+ "test-type": ""
+ }
+
+ def add_content(self):
+ """
+ """
+ if self._html_layout and self._spec_test:
+ return html.Div(
+ id="div-main",
+ children=[
+ self._add_ctrl_div(),
+ self._add_plotting_div()
+ ]
+ )
+ else:
+ return html.Div(
+ id="div-main-error",
+ children="An Error Occured."
+ )
+
+ def _add_ctrl_div(self):
+ """Add div with controls. It is placed on the left side.
+ """
+ return html.Div(
+ id="div-controls",
+ children=[
+ html.Div(
+ id="div-controls-tabs",
+ children=[
+ self._add_ctrl_select(),
+ self._add_ctrl_shown()
+ ]
+ )
+ ],
+ style={
+ "display": "inline-block",
+ "width": "18%",
+ "padding": "5px"
+ }
+ )
+
+ def _add_plotting_div(self):
+ """Add div with plots and tables. It is placed on the right side.
+ """
+ return html.Div(
+ id="div-plotting-area",
+ children=[
+ # Only a visible note.
+ # TODO: Add content.
+ html.H3(
+ "Graphs and Tables",
+ style={
+ "vertical-align": "middle",
+ "text-align": "center"
+ }
+ )
+ ],
+ style={
+ "vertical-align": "middle",
+ "display": "inline-block",
+ "width": "80%",
+ "padding": "5px"
+ }
+ )
+
+ def _add_ctrl_shown(self):
+ """
+ """
+ return html.Div(
+ id="div-ctrl-shown",
+ children="List of selected tests"
+ )
+
+ def _add_ctrl_select(self):
+ """
+ """
+ return html.Div(
+ id="div-ctrl-select",
+ children=[
+ html.Br(),
+ html.Div(
+ children="Physical Test Bed Topology, NIC and Driver"
+ ),
+ dcc.Dropdown(
+ id="dd-ctrl-phy",
+ placeholder="Select a Physical Test Bed Topology...",
+ multi=False,
+ clearable=False,
+ options=[
+ {"label": k, "value": k} for k in self._spec_test.keys()
+ ],
+ ),
+ html.Br(),
+ html.Div(
+ children="Area"
+ ),
+ dcc.Dropdown(
+ id="dd-ctrl-area",
+ placeholder="Select an Area...",
+ disabled=True,
+ multi=False,
+ clearable=False,
+ ),
+ html.Br(),
+ html.Div(
+ children="Test"
+ ),
+ dcc.Dropdown(
+ id="dd-ctrl-test",
+ placeholder="Select a Test...",
+ disabled=True,
+ multi=False,
+ clearable=False,
+ ),
+
+ # Change to radio buttons:
+ html.Br(),
+ html.Div(
+ children="Number of Cores"
+ ),
+ dcc.Dropdown(
+ id="dd-ctrl-core",
+ placeholder="Select a Number of Cores...",
+ disabled=True,
+ multi=False,
+ clearable=False,
+ ),
+ html.Br(),
+ html.Div(
+ children="Frame Size"
+ ),
+ dcc.Dropdown(
+ id="dd-ctrl-framesize",
+ placeholder="Select a Frame Size...",
+ disabled=True,
+ multi=False,
+ clearable=False,
+ ),
+ html.Br(),
+ html.Div(
+ children="Test Type"
+ ),
+ dcc.Dropdown(
+ id="dd-ctrl-testtype",
+ placeholder="Select a Test Type...",
+ disabled=True,
+ multi=False,
+ clearable=False,
+ ),
+ html.Br(),
+ html.Button(
+ id="btn-ctrl-add",
+ children="Add",
+ disabled=True
+ ),
+ html.Br(),
+ html.Div(
+ id="div-ctrl-info"
+ )
+ ]
+ )
+
+ def callbacks(self, app):
+
+ @app.callback(
+ Output("dd-ctrl-area", "options"),
+ Output("dd-ctrl-area", "disabled"),
+ Input("dd-ctrl-phy", "value"),
+ )
+ def _update_dd_area(phy):
+ """
+ """
+
+ if phy is None:
+ raise PreventUpdate
+
+ try:
+ options = [
+ {"label": self._spec_test[phy][v]["label"], "value": v}
+ for v in [v for v in self._spec_test[phy].keys()]
+ ]
+ except KeyError:
+ options = list()
+
+ return options, False
+
+ @app.callback(
+ Output("dd-ctrl-test", "options"),
+ Output("dd-ctrl-test", "disabled"),
+ Input("dd-ctrl-phy", "value"),
+ Input("dd-ctrl-area", "value"),
+ )
+ def _update_dd_test(phy, area):
+ """
+ """
+
+ if not all((phy, area, )):
+ raise PreventUpdate
+
+ try:
+ options = [
+ {"label": v, "value": v}
+ for v in self._spec_test[phy][area]["test"]
+ ]
+ except KeyError:
+ options = list()
+
+ return options, False
+
+ @app.callback(
+ Output("btn-ctrl-add", "disabled"),
+ Input("dd-ctrl-phy", "value"),
+ Input("dd-ctrl-area", "value"),
+ Input("dd-ctrl-test", "value"),
+ )
+ def _update_btn_add(phy, area, test):
+ """
+ """
+
+ if all((phy, area, test, )):
+ self._test_selection["phy"] = phy
+ self._test_selection["area"] = area
+ self._test_selection["test"] = test
+ return False
+ else:
+ return True
+
+ @app.callback(
+ Output("div-ctrl-info", "children"),
+ Output("dd-ctrl-phy", "value"),
+ Output("dd-ctrl-area", "value"),
+ Output("dd-ctrl-test", "value"),
+ Output("btn-ctrl-add", "n_clicks"),
+ Input("btn-ctrl-add", "n_clicks")
+ )
+ def _print_user_selection(n_clicks):
+ """
+ """
+
+ logging.info(f"\n\n{n_clicks}\n\n")
+
+ if not n_clicks:
+ raise PreventUpdate
+
+ selected = (
+ f"{self._test_selection['phy']} # "
+ f"{self._test_selection['area']} # "
+ f"{self._test_selection['test']} # "
+ f"{n_clicks}\n"
+ )
+
+ self._reset_test_selection()
+
+ return (
+ selected,
+ None,
+ None,
+ None,
+ 0,
+ )