UTI: PoC - Dash application for Trending 02/35102/8
authorTibor Frank <tifrank@cisco.com>
Wed, 26 Jan 2022 08:31:43 +0000 (09:31 +0100)
committerTibor Frank <tifrank@cisco.com>
Thu, 24 Feb 2022 07:11:41 +0000 (08:11 +0100)
- delete old dash demo

Change-Id: I3e75c1dc18ee85c1826838af0343a4b779f71754
Signed-off-by: Tibor Frank <tifrank@cisco.com>
15 files changed:
resources/tools/dash/app/config.py
resources/tools/dash/app/pal/__init__.py
resources/tools/dash/app/pal/assets.py
resources/tools/dash/app/pal/report/data.py [new file with mode: 0644]
resources/tools/dash/app/pal/report/layout.py [new file with mode: 0644]
resources/tools/dash/app/pal/report/report.py [moved from resources/tools/dash/app/pal/trending/dashboard.py with 72% similarity]
resources/tools/dash/app/pal/routes.py
resources/tools/dash/app/pal/templates/index.jinja2
resources/tools/dash/app/pal/trending/data.py
resources/tools/dash/app/pal/trending/html_layout.txt [new file with mode: 0644]
resources/tools/dash/app/pal/trending/layout.py
resources/tools/dash/app/pal/trending/spec_test_selection.yaml [new file with mode: 0644]
resources/tools/dash/app/pal/trending/trending.py [new file with mode: 0644]
resources/tools/dash/app/requirements.txt
resources/tools/dash/app/wsgi.py

index 279317b..55f92cc 100644 (file)
@@ -17,7 +17,8 @@ from os import environ
 
 
 class Config:
-    """Flask configuration variables."""
+    """Flask configuration variables.
+    """
 
     # General Config
     FLASK_APP = environ.get("FLASK_APP")
index 9950def..863fd08 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-"""Initialize Flask app."""
+"""Initialize Flask app.
+"""
+
+import logging
+
 from flask import Flask
 from flask_assets import Environment
 
 
 def init_app():
-    """Construct core Flask application with embedded Dash app."""
+    """Construct core Flask application with embedded Dash app.
+    """
+
+    logging.basicConfig(
+        format=u"%(asctime)s: %(levelname)s: %(message)s",
+        datefmt=u"%Y/%m/%d %H:%M:%S",
+        level=logging.INFO
+    )
+
+    logging.info("Application started.")
+
     app = Flask(__name__, instance_relative_config=False)
-    app.config.from_object("config.Config")
-    assets = Environment()
-    assets.init_app(app)
+    app.config.from_object(u"config.Config")
 
     with app.app_context():
         # Import parts of our core Flask app.
         from . import routes
         from .assets import compile_static_assets
 
-        # Import Trending Dash application.
-        from .trending.dashboard import init_dashboard
-
-        app = init_dashboard(app)
+        assets = Environment()
+        assets.init_app(app)
 
         # Compile static assets.
         compile_static_assets(assets)
 
-        return app
+        # Import Dash applications.
+        from .trending.trending import init_trending
+        app = init_trending(app)
+
+        from .report.report import init_report
+        app = init_report(app)
+
+    return app
+
+app = init_app()
index 4237707..63fac16 100644 (file)
@@ -11,7 +11,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-"""Compile static assets."""
+"""Compile static assets.
+"""
+
 from flask import current_app as app
 from flask_assets import Bundle
 
@@ -24,15 +26,17 @@ def compile_static_assets(assets):
     :returns: Compiled stylesheets.
     :rtype: Environment
     """
+
     assets.auto_build = True
     assets.debug = False
     less_bundle = Bundle(
-        "less/*.less",
-        filters="less,cssmin",
-        output="dist/css/styles.css",
-        extra={"rel": "stylesheet/less"},
+        u"less/*.less",
+        filters=u"less,cssmin",
+        output=u"dist/css/styles.css",
+        extra={u"rel": u"stylesheet/less"},
     )
-    assets.register("less_all", less_bundle)
-    if app.config["FLASK_ENV"] == "development":
+    assets.register(u"less_all", less_bundle)
+    if app.config[u"FLASK_ENV"] == u"development":
         less_bundle.build()
+
     return assets
diff --git a/resources/tools/dash/app/pal/report/data.py b/resources/tools/dash/app/pal/report/data.py
new file mode 100644 (file)
index 0000000..43e0239
--- /dev/null
@@ -0,0 +1,26 @@
+# Copyright (c) 2022 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Prepare data for Plotly Dash."""
+
+import pandas as pd
+
+
+def create_dataframe():
+    """Create Pandas DataFrame from local CSV.
+    """
+
+    return pd.read_csv(
+        u"https://s3-docs.fd.io/csit/master/trending/_static/vpp/"
+        u"csit-vpp-perf-mrr-daily-master-2n-skx-trending.csv"
+    )
diff --git a/resources/tools/dash/app/pal/report/layout.py b/resources/tools/dash/app/pal/report/layout.py
new file mode 100644 (file)
index 0000000..70fe727
--- /dev/null
@@ -0,0 +1,46 @@
+# Copyright (c) 2022 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Plotly Dash HTML layout override."""
+
+html_layout = u"""
+<!DOCTYPE html>
+    <html>
+        <head>
+            {%metas%}
+            <title>{%title%}</title>
+            {%favicon%}
+            {%css%}
+        </head>
+        <body class="dash-template">
+            <header>
+              <div class="nav-wrapper">
+                <a href="/">
+                    <h1>FD.io CSIT</h1>
+                </a>
+                <a href="">
+                  <h1>Report</h1>
+                </a>
+                <nav>
+                </nav>
+              </div>
+            </header>
+            {%app_entry%}
+            <footer>
+                {%config%}
+                {%scripts%}
+                {%renderer%}
+            </footer>
+        </body>
+    </html>
+"""
@@ -11,7 +11,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-"""Instantiate a Dash app."""
+"""Instantiate the Report Dash application.
+"""
+
 import dash
 from dash import dcc
 from dash import html
@@ -23,7 +25,7 @@ from .data import create_dataframe
 from .layout import html_layout
 
 
-def init_dashboard(server):
+def init_report(server):
     """Create a Plotly Dash dashboard.
 
     :param server: Flask server.
@@ -31,12 +33,13 @@ def init_dashboard(server):
     :returns: Dash app server.
     :rtype: Dash
     """
+
     dash_app = dash.Dash(
         server=server,
-        routes_pathname_prefix="/trending/",
+        routes_pathname_prefix=u"/report/",
         external_stylesheets=[
-            "/static/dist/css/styles.css",
-            "https://fonts.googleapis.com/css?family=Lato",
+            u"/static/dist/css/styles.css",
+            u"https://fonts.googleapis.com/css?family=Lato",
         ],
     )
 
@@ -51,19 +54,23 @@ def init_dashboard(server):
         children=[
             create_data_table(df),
         ],
-        id="dash-container",
+        id=u"dash-container",
     )
     return dash_app.server
 
 
 def create_data_table(df):
-    """Create Dash datatable from Pandas DataFrame."""
+    """Create Dash datatable from Pandas DataFrame.
+
+    DEMO
+    """
+
     table = dash_table.DataTable(
-        id="database-table",
-        columns=[{"name": i, "id": i} for i in df.columns],
-        data=df.to_dict("records"),
-        sort_action="native",
-        sort_mode="native",
-        page_size=300,
+        id=u"database-table",
+        columns=[{u"name": i, u"id": i} for i in df.columns],
+        data=df.to_dict(u"records"),
+        sort_action=u"native",
+        sort_mode=u"native",
+        page_size=5,
     )
     return table
index 16680c4..200d7af 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-"""Routes for parent Flask app."""
+"""Routes for parent Flask app.
+"""
+
 from flask import current_app as app
 from flask import render_template
 
 
-@app.route("/")
+@app.route(u"/")
 def home():
-    """Landing page."""
+    """Landing page.
+    """
+
     return render_template(
-        "index.jinja2",
-        title="FD.io CSIT",
-        description="Performance Dashboard",
-        template="home-template"
+        u"index.jinja2",
+        title=u"FD.io CSIT",
+        description=u"Performance Dashboard",
+        template=u"home-template"
     )
index 8ab5c84..bd53346 100644 (file)
@@ -5,9 +5,15 @@
   <img src="{{ url_for('static', filename='img/logo.svg') }}" class="logo" />
   <h1 class="site-title">{{ title }}</h1>
   <p class="subtitle">{{ description }}</p>
-  <a href="/trending/" class="dash-link">
-    <span>trending</span>
-    <i class="fas fa-arrow-right"></i>
-  </a>
+  <div>
+    <a href="/trending/" class="dash-link">
+      <span>Trending</span>
+      <i class="fas fa-arrow-right"></i>
+    </a>
+    <a href="/report/" class="dash-link">
+      <span>Report</span>
+      <i class="fas fa-arrow-right"></i>
+    </a>
+  </div>
 </div>
 {% endblock %}
index 06466a9..298a405 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-"""Prepare data for Plotly Dash."""
+"""Prepare data for Plotly Dash.
+"""
 
 import pandas as pd
 
 
 def create_dataframe():
-    """Create Pandas DataFrame from local CSV."""
+    """Create Pandas DataFrame from local CSV.
+
+    DEMO
+    """
+
     return pd.read_csv(
-        "https://s3-docs.fd.io/csit/master/trending/_static/vpp/"
-        "csit-vpp-perf-mrr-daily-master-2n-zn2-trending.csv"
+        u"https://s3-docs.fd.io/csit/master/trending/_static/vpp/"
+        u"csit-vpp-perf-mrr-daily-master-2n-zn2-trending.csv"
     )
diff --git a/resources/tools/dash/app/pal/trending/html_layout.txt b/resources/tools/dash/app/pal/trending/html_layout.txt
new file mode 100644 (file)
index 0000000..3f0aa0c
--- /dev/null
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        {%metas%}
+        <title>Continuous Performance Trending</title>
+        {%favicon%}
+        {%css%}
+    </head>
+    <body class="dash-template">
+        <header>
+            <div class="nav-wrapper">
+            <a href="/">
+                <h1>FD.io CSIT</h1>
+            </a>
+            <a href="">
+                <h1>Continuous Performance Trending</h1>
+            </a>
+            <nav>
+            </nav>
+            </div>
+        </header>
+        {%app_entry%}
+        <footer>
+            {%config%}
+            {%scripts%}
+            {%renderer%}
+        </footer>
+    </body>
+</html>
index 1f60aec..69ce0c4 100644 (file)
 # 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,
+            )
diff --git a/resources/tools/dash/app/pal/trending/spec_test_selection.yaml b/resources/tools/dash/app/pal/trending/spec_test_selection.yaml
new file mode 100644 (file)
index 0000000..dae9022
--- /dev/null
@@ -0,0 +1,106 @@
+2n-aws-nitro-50g-ena:
+  ip4-base:
+    label: IPv4 Routing Base
+    test:
+      - ethip4-ip4base
+    core: [1C, 2C]
+    frame-size: [64B, 1518B]
+    test-type: [MRR, NDR, PDR]
+  ip4-scale:
+    label: IPv4 Routing Scale
+    test:
+      - ethip4-ip4scale20k
+      - ethip4-ip4scale20k-rnd
+    core: [1C, 2C]
+    frame-size: [64B, 1518B]
+    test-type: [MRR, NDR, PDR]
+  ip6-base:
+    label: IPv6 Routing Base
+    test:
+      - ethip6-ip4base
+    core: [1C, 2C]
+    frame-size: [78B, 1518B]
+    test-type: [MRR, NDR, PDR]
+  ip6-scale:
+    label: IPv6 Routing Scale
+    test:
+      - ethip6-ip4scale20k
+      - ethip6-ip4scale20k-rnd
+    core: [1C, 2C]
+    frame-size: [78B, 1518B]
+    test-type: [MRR, NDR, PDR]
+# 2n-clx-cx556a-rdma:
+# 2n-clx-x710-avf:
+# 2n-clx-x710-dpdk:
+# 2n-clx-xxv710-af-xdp:
+# 2n-clx-xxv710-avf:
+#     l2-base:
+#     l2-scale:
+#     ip4-base:
+#     ip4-scale:
+#     ip4-features:
+#     ip6-base:
+#     ip6-scale:
+#     ethip4-ethip4udpgeneve:
+#     nat44det-ip4-stl-bidir:
+#     nat44ed-ip4-stl-unidir:
+#     nat44ed-ip4-udp-stf-cps:
+#     nat44ed-ip4-tcp-stf-cps:
+#     nat44ed-ip4-udp-stf-pps:
+#     nat44ed-ip4-tcp-stf-pps:
+#     nat44ed-ip4-udp-tput:
+#     nat44ed-ip4-tcp-tput:
+#     vhost-base:
+#     memif-base:
+#     vnf-service-chains-routing:
+#     cnf-service-chains-routing:
+#     cnf-service-pipelines-routing:
+#     vnf-service-chains-tunnels:
+# 2n-clx-xxv710-dpdk:
+# 2n-dnv-x553-ixgbe:
+# 2n-icx-xxv710-avf:
+# 2n-icx-xxv710-dpdk:
+# 2n-skx-x710-avf:
+# 2n-skx-x710-dpdk:
+# 2n-skx-xxv710-avf:
+# 2n-skx-xxv710-dpdk:
+# 2n-tx2-xl710-af-xdp:
+# 2n-tx2-xl710-avf:
+# 2n-tx2-xl710-dpdk:
+# 2n-zn2-x710-avf:
+# 2n-zn2-x710-dpdk:
+# 2n-zn2-cx556a-rdma:
+# 2n-zn2-xxv710-avf:
+# 2n-zn2-xxv710-dpdk:
+3n-aws-nitro-50g-ena:
+  ip4-base:
+    label: IPv4 Routing Base
+    test:
+      - ethip4-ip4base
+    core: [1C, 2C]
+    frame-size: [64B, 1518B]
+    test-type: [MRR, NDR, PDR]
+
+  ip4-scale:
+    label: IPv4 Routing Scale
+    test:
+      - ethip4-ip4scale20k
+      - ethip4-ip4scale20k-rnd
+    core: [1C, 2C]
+    frame-size: [64B, 1518B]
+    test-type: [MRR, NDR, PDR]
+
+  ipsec-base:
+    label: IPSec IPv4 Routing Base
+    test:
+      - ethip4ipsec40tnlsw-ip4base-int-aes256gcm
+    core: [1C, 2C]
+    frame-size: [IMIX, 1518B]
+    test-type: [MRR, NDR, PDR]
+# 3n-dnv-x553-ixgbe:
+# 3n-icx-xxv710-avf:
+# 3n-icx-xxv710-dpdk:
+# 3n-skx-x710-avf:
+# 3n-skx-xxv710-avf:
+# 3n-skx-xxv710-dpdk:
+# 3n-tsh-x520-ixgbe:
diff --git a/resources/tools/dash/app/pal/trending/trending.py b/resources/tools/dash/app/pal/trending/trending.py
new file mode 100644 (file)
index 0000000..4edf831
--- /dev/null
@@ -0,0 +1,74 @@
+# Copyright (c) 2022 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Instantiate the Trending Dash applocation.
+"""
+import dash
+from dash import dcc
+from dash import html
+from dash import dash_table
+import numpy as np
+import pandas as pd
+
+from .data import create_dataframe
+from .layout import Layout
+
+
+def init_trending(server):
+    """Create a Plotly Dash dashboard.
+
+    :param server: Flask server.
+    :type server: Flask
+    :returns: Dash app server.
+    :rtype: Dash
+    """
+
+    dash_app = dash.Dash(
+        server=server,
+        routes_pathname_prefix=u"/trending/",
+        external_stylesheets=[
+            u"/static/dist/css/styles.css",
+            u"https://fonts.googleapis.com/css?family=Lato",
+        ],
+    )
+
+    # Load DataFrame
+    df = create_dataframe()
+
+    # Custom HTML layout
+    layout = Layout(
+        app=dash_app,
+        html_layout_file="pal/trending/html_layout.txt",
+        spec_file="pal/trending/spec_test_selection.yaml"
+    )
+    dash_app.index_string = layout.html_layout
+    dash_app.layout = layout.add_content()
+
+    return dash_app.server
+
+
+def create_data_table(df):
+    """Create Dash datatable from Pandas DataFrame.
+
+    DEMO
+    """
+
+    table = dash_table.DataTable(
+        id=u"database-table",
+        columns=[{u"name": i, u"id": i} for i in df.columns],
+        data=df.to_dict(u"records"),
+        sort_action=u"native",
+        sort_mode=u"native",
+        page_size=10,
+    )
+    return table
index 90e595e..befdc8a 100644 (file)
@@ -23,7 +23,8 @@ protobuf==3.19.1
 pyparsing==3.0.6
 python-dateutil==2.8.2
 python-dotenv==0.19.2
-pytz==2022.3
+pytz==2021.3
+PyYAML==5.1
 retrying==1.3.3
 setuptools==57.5.0
 six==1.16.0
index c2832e2..316901c 100644 (file)
@@ -11,9 +11,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from pal import init_app
 
-app = init_app()
+from pal import app
+
 
 if __name__ == "__main__":
     # Main entry point.