feat(uti): Dash demo 72/34872/4
authorPeter Mikus <pmikus@cisco.com>
Mon, 10 Jan 2022 12:19:31 +0000 (13:19 +0100)
committerTibor Frank <tifrank@cisco.com>
Tue, 11 Jan 2022 13:09:38 +0000 (14:09 +0100)
- displays the table with trending data downloaded from trending pages

Signed-off-by: Peter Mikus <pmikus@cisco.com>
Change-Id: Ic0d48290105ccd846c7de9ee4d8acb78e0b72f00

21 files changed:
resources/tools/dash/Dockerfile [new file with mode: 0644]
resources/tools/dash/app/app.ini [new file with mode: 0644]
resources/tools/dash/app/config.py [new file with mode: 0644]
resources/tools/dash/app/pal/__init__.py [new file with mode: 0644]
resources/tools/dash/app/pal/assets.py [new file with mode: 0644]
resources/tools/dash/app/pal/routes.py [new file with mode: 0644]
resources/tools/dash/app/pal/static/dist/css/styles.css [new file with mode: 0644]
resources/tools/dash/app/pal/static/dist/img/favicon.svg [new file with mode: 0644]
resources/tools/dash/app/pal/static/img/logo.svg [new file with mode: 0644]
resources/tools/dash/app/pal/static/less/global.less [new file with mode: 0644]
resources/tools/dash/app/pal/static/less/header.less [new file with mode: 0644]
resources/tools/dash/app/pal/static/less/home.less [new file with mode: 0644]
resources/tools/dash/app/pal/static/less/table.less [new file with mode: 0644]
resources/tools/dash/app/pal/templates/index.jinja2 [new file with mode: 0644]
resources/tools/dash/app/pal/templates/layout.jinja2 [new file with mode: 0644]
resources/tools/dash/app/pal/trending/dashboard.py [new file with mode: 0644]
resources/tools/dash/app/pal/trending/data.py [new file with mode: 0644]
resources/tools/dash/app/pal/trending/layout.py [new file with mode: 0644]
resources/tools/dash/app/requirements.txt [new file with mode: 0644]
resources/tools/dash/app/wsgi.py [new file with mode: 0644]
resources/tools/dash/docker-compose.yaml [new file with mode: 0644]

diff --git a/resources/tools/dash/Dockerfile b/resources/tools/dash/Dockerfile
new file mode 100644 (file)
index 0000000..cd7eab7
--- /dev/null
@@ -0,0 +1,12 @@
+ARG PYTHON_VERSION=3.10
+FROM python:${PYTHON_VERSION}-buster
+
+WORKDIR /app
+
+COPY ./app/requirements.txt .
+
+RUN pip3 install -r requirements.txt
+
+EXPOSE 5000
+
+CMD [ "uwsgi", "app.ini" ]
\ No newline at end of file
diff --git a/resources/tools/dash/app/app.ini b/resources/tools/dash/app/app.ini
new file mode 100644 (file)
index 0000000..bbf2943
--- /dev/null
@@ -0,0 +1,17 @@
+[uwsgi]
+ini = :pal
+
+[pal]
+module = wsgi:app
+
+processes = 2
+threads = 2
+plugin = python3
+
+master = true
+http-socket = :5000
+socket = /tmp/app.sock
+chmod-socket = 666
+vacuum = true
+
+die-on-term = true
\ No newline at end of file
diff --git a/resources/tools/dash/app/config.py b/resources/tools/dash/app/config.py
new file mode 100644 (file)
index 0000000..279317b
--- /dev/null
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3
+
+# 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.
+
+from os import environ
+
+
+class Config:
+    """Flask configuration variables."""
+
+    # General Config
+    FLASK_APP = environ.get("FLASK_APP")
+    FLASK_ENV = environ.get("FLASK_ENV")
+    SECRET_KEY = environ.get("SECRET_KEY")
+
+    # Assets
+    LESS_BIN = environ.get("LESS_BIN")
+    ASSETS_DEBUG = environ.get("ASSETS_DEBUG")
+    LESS_RUN_IN_DEBUG = environ.get("LESS_RUN_IN_DEBUG")
+
+    # Static Assets
+    STATIC_FOLDER = "static"
+    TEMPLATES_FOLDER = "templates"
+    COMPRESSOR_DEBUG = environ.get("COMPRESSOR_DEBUG")
diff --git a/resources/tools/dash/app/pal/__init__.py b/resources/tools/dash/app/pal/__init__.py
new file mode 100644 (file)
index 0000000..9950def
--- /dev/null
@@ -0,0 +1,39 @@
+# 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.
+
+"""Initialize Flask app."""
+from flask import Flask
+from flask_assets import Environment
+
+
+def init_app():
+    """Construct core Flask application with embedded Dash app."""
+    app = Flask(__name__, instance_relative_config=False)
+    app.config.from_object("config.Config")
+    assets = Environment()
+    assets.init_app(app)
+
+    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)
+
+        # Compile static assets.
+        compile_static_assets(assets)
+
+        return app
diff --git a/resources/tools/dash/app/pal/assets.py b/resources/tools/dash/app/pal/assets.py
new file mode 100644 (file)
index 0000000..4237707
--- /dev/null
@@ -0,0 +1,38 @@
+# 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.
+
+"""Compile static assets."""
+from flask import current_app as app
+from flask_assets import Bundle
+
+
+def compile_static_assets(assets):
+    """Compile stylesheets if in development mode.
+
+    :param assets: Flask-Assets Environment.
+    :type assets: Environment
+    :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"},
+    )
+    assets.register("less_all", less_bundle)
+    if app.config["FLASK_ENV"] == "development":
+        less_bundle.build()
+    return assets
diff --git a/resources/tools/dash/app/pal/routes.py b/resources/tools/dash/app/pal/routes.py
new file mode 100644 (file)
index 0000000..16680c4
--- /dev/null
@@ -0,0 +1,27 @@
+# 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.
+
+"""Routes for parent Flask app."""
+from flask import current_app as app
+from flask import render_template
+
+
+@app.route("/")
+def home():
+    """Landing page."""
+    return render_template(
+        "index.jinja2",
+        title="FD.io CSIT",
+        description="Performance Dashboard",
+        template="home-template"
+    )
diff --git a/resources/tools/dash/app/pal/static/dist/css/styles.css b/resources/tools/dash/app/pal/static/dist/css/styles.css
new file mode 100644 (file)
index 0000000..d77f95a
--- /dev/null
@@ -0,0 +1 @@
+body,html{height:100%!important;margin:0;padding:0;background:#e7ecf7!important;font-family:'Lato',sans-serif}.row .col{padding:0!important}header{position:relative;width:100%;padding:30px 0!important;background:white!important;box-shadow:0 0 5px #bec6cf;font-family:'Lato',sans-serif}header .nav-wrapper{display:flex;align-items:center;justify-content:space-between;width:1000px;max-width:90%;margin:auto}header .nav-wrapper nav{display:flex}header .nav-wrapper .logo{display:inline-block;width:40px}header .nav-wrapper a{color:#70829d;font-size:1em;text-decoration:none;transition:all .3s ease-out}.home-template .container{margin:0!important}.home-template .card{width:800px;max-width:93%;height:fit-content;margin:50px auto;padding:60px;background:white;box-shadow:0 0 5px rgba(65,67,144,0.15);text-align:center}@media(max-width:800px){.home-template .card{max-width:78%;width:unset;padding:40px}}.home-template .card .logo{width:50px;margin:auto}.home-template .card .site-title{margin:10px 0 3px;color:#5f6988;font-family:proxima-nova,sans-serif;font-size:2.3em;line-height:1;text-transform:uppercase}.home-template .card p{margin:0;color:#7f92af;font-size:1.08em}.home-template .card a{color:#79aec8;font-weight:500;text-decoration:none;transition:all .2s ease}.home-template .card .dash-link{display:block;margin-top:30px;font-size:1.1em;font-weight:600}.home-template .card .dash-link:hover{opacity:.7}.home-template .card .dash-link i{margin-left:6px;font-size:.9em}body,html{height:100%!important;padding:0;background:#e7ecf7!important;font-family:'Lato',sans-serif;margin:0}.dash-template h1{display:inline-block;margin:0;font-size:1.5em}.dash-template .logo{width:40px;margin-right:20px}.dash-template .logo:hover{opacity:.7}.dash-template .nav-wrapper a{display:flex;align-items:center}#dash-container{width:1200px;max-width:95%;margin:50px auto 0}.container{margin:90px 30px;color:#182635}#_dash-app-content{max-width:95%!important;margin:90px auto!important;overflow:hidden}nav a{display:flex;align-items:center;justify-content:space-between;color:#59657b;line-height:1;text-decoration:none;transition:all .3s ease-out}nav a:hover{cursor:pointer;opacity:.7}nav a i{margin-right:5px}.dash-spreadsheet-container{max-width:100%;margin:0 auto 20px!important;overflow:hidden;border:0;border-radius:4px;box-shadow:0 0 4px #cdd3e2;font-family:'Lato',sans-serif}.dash-spreadsheet-container *{box-shadow:none!important;font-family:Lato,sans-serif}.dash-spreadsheet-container th,.dash-spreadsheet-container tr{box-shadow:none!important}.dash-spreadsheet-container td:first-of-type,.dash-spreadsheet-container tr:first-of-type{width:55px}.dash-spreadsheet-container td:last-of-type,.dash-spreadsheet-container tr:last-of-type{width:100px}.dash-spreadsheet-container th{padding:25px 12px!important;border-top:0!important;border-right:0!important;border-bottom:1px solid #e5e7eb!important;border-left:0!important;background:white!important;color:#404552}.dash-spreadsheet-container th .column-header--sort{margin-right:7px;transition:all .2s ease-out}.dash-spreadsheet-container th .column-header--sort:hover{color:#9bd2eb!important}.dash-spreadsheet-container th .column-header--sort svg{width:.5em}.dash-spreadsheet-container th .sort{order:2;color:#aeaeae!important;transition:all .3s ease-out}.dash-spreadsheet-container th .sort:hover{text-decoration:none;cursor:pointer;opacity:.7}.dash-spreadsheet-container th>div{display:flex;align-items:center;width:fit-content}.dash-spreadsheet-container th>div span:last-of-type{font-size:.8em;font-weight:600;text-transform:uppercase}.dash-spreadsheet-container td{padding:12px!important;font-size:.95em;text-align:left!important}.dash-spreadsheet-container td:first-of-type,.dash-spreadsheet-container td:last-of-type{max-width:50px!important}.dash-spreadsheet-container td .dash-cell-value{display:block!important;max-width:500px;overflow:hidden!important;font-size:1.2em;font-weight:300;text-align:left;text-overflow:ellipsis;white-space:nowrap;opacity:.8}.dash-spreadsheet-container td[data-dash-column="command"],.dash-spreadsheet-container .column-1{max-width:200px!important}.dash-spreadsheet-container td[data-dash-column="index"]{text-align:center!important}.dash-spreadsheet-container td[data-dash-column="index"] div{text-align:center!important}.dash-spreadsheet-container td[data-dash-column="index"]{color:#939da4;font-size:1.1em;font-weight:500}.dash-spreadsheet-container tr:nth-child(even){background:#f5f8fc!important}table tbody{box-shadow:0 0 7px #bdbdd2!important}table tbody tr{border:0!important}table tbody tr:nth-child(even){background:#e7eefa!important}table tbody tr td{padding:12px 10px!important;overflow:hidden!important;border:0!important;font-size:.65em!important;line-height:1.25!important;text-align:center!important}.dash-spreadsheet-container .dash-spreadsheet-inner td.focused{background-color:rgba(154,212,255,0.2)!important;box-shadow:0 0 4px #cdd3e2}.dash-spreadsheet-container .dash-spreadsheet-inner td .input-cell-value-shadow{width:100%}.dash-spreadsheet-container .dash-spreadsheet-inner td input.dash-cell-value.unfocused{caret-color:#317ed1!important}.dash-spreadsheet-inner .input-active{overflow:visible!important;color:#829ab2;text-align:left!important}.dash-spreadsheet-inner input::placeholder{color:grey}#histogram-graph{margin-bottom:30px;padding-bottom:176px!important;overflow:hidden;border-radius:4px;background:white;box-shadow:0 0 4px #cdd3e2}.main-svg{overflow:visible!important}header .nav-wrapper{width:1200px!important}
\ No newline at end of file
diff --git a/resources/tools/dash/app/pal/static/dist/img/favicon.svg b/resources/tools/dash/app/pal/static/dist/img/favicon.svg
new file mode 100644 (file)
index 0000000..689757e
--- /dev/null
@@ -0,0 +1,348 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+        viewBox="0 0 1000 568.31" style="enable-background:new 0 0 1000 568.31;" xml:space="preserve">
+<style type="text/css">
+       .st0{fill:#1DCAD3;}
+       .st1{fill:#36B0C9;}
+       .st2{fill:#231F20;}
+       .st3{fill:#FFFFFF;}
+       .st4{fill:#9164CC;}
+       .st5{clip-path:url(#SVGID_2_);fill:url(#SVGID_3_);}
+       .st6{fill:#201747;}
+       .st7{fill-rule:evenodd;clip-rule:evenodd;fill:#10CFC9;}
+       .st8{clip-path:url(#SVGID_5_);fill:#231F20;}
+       .st9{fill-rule:evenodd;clip-rule:evenodd;fill:#231F20;}
+       .st10{clip-path:url(#SVGID_7_);fill:#FFFFFF;}
+       .st11{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}
+       .st12{fill:#8CCEAF;}
+       .st13{fill:#008476;}
+       .st14{fill:#25BCBD;}
+       .st15{fill:#004D70;}
+       .st16{fill:#20BBBB;}
+       .st17{fill:#024D70;}
+       .st18{fill-rule:evenodd;clip-rule:evenodd;fill:#F58B1F;}
+       .st19{fill-rule:evenodd;clip-rule:evenodd;fill:#18335B;}
+       .st20{clip-path:url(#SVGID_9_);}
+       .st21{clip-path:url(#SVGID_11_);}
+       .st22{fill:#18335B;}
+       .st23{fill:#F58B1F;}
+       .st24{clip-path:url(#SVGID_15_);}
+       .st25{clip-path:url(#SVGID_17_);}
+       .st26{clip-path:url(#SVGID_21_);}
+       .st27{clip-path:url(#SVGID_23_);}
+       .st28{clip-path:url(#SVGID_27_);}
+       .st29{clip-path:url(#SVGID_29_);}
+       .st30{clip-path:url(#SVGID_33_);}
+       .st31{clip-path:url(#SVGID_35_);}
+       .st32{clip-path:url(#SVGID_39_);}
+       .st33{clip-path:url(#SVGID_41_);}
+       .st34{fill:#416BA9;}
+       .st35{fill:#73C3D5;}
+       .st36{opacity:0.8;}
+       .st37{fill:#3A3A3A;}
+       .st38{fill:url(#SVGID_44_);}
+       .st39{fill:none;stroke:#000000;stroke-width:6.3384;}
+       .st40{fill:none;stroke:#000000;stroke-width:3.1692;}
+       .st41{fill:#48494B;}
+       .st42{fill:#C1986C;}
+       .st43{fill:url(#SVGID_63_);}
+       .st44{fill:url(#SVGID_64_);}
+       .st45{fill:url(#SVGID_65_);}
+       .st46{fill:url(#SVGID_66_);}
+       .st47{fill:url(#SVGID_67_);}
+       .st48{fill:#4D4E4E;}
+       .st49{fill:#27B373;}
+       .st50{fill:#5DC4CD;}
+       .st51{fill:#1E8756;}
+       .st52{fill:#3D1152;}
+       .st53{fill:#922C48;}
+       .st54{fill-rule:evenodd;clip-rule:evenodd;fill:#922C48;}
+       .st55{fill:#404041;}
+       .st56{fill:#EC1C24;}
+       .st57{fill:#373A36;}
+       .st58{fill:#808184;}
+       .st59{fill:#262261;}
+       .st60{fill:#6FCBDC;}
+       .st61{fill:#2F3436;}
+       .st62{fill:#5F97D0;}
+       .st63{fill:#132428;}
+       .st64{fill:#85C041;}
+       .st65{fill:#677784;}
+       .st66{fill:url(#SVGID_68_);}
+       .st67{opacity:0.2;clip-path:url(#SVGID_70_);}
+       .st68{fill:#FFFEFA;}
+       .st69{opacity:0.1;}
+       .st70{fill:url(#SVGID_71_);}
+       .st71{opacity:0.3;}
+       .st72{opacity:0.08;}
+       .st73{opacity:0.1;fill:url(#Wordmark_1_);}
+       .st74{fill:url(#SVGID_104_);}
+       .st75{opacity:0.6;fill:url(#SVGID_107_);}
+       .st76{opacity:0.4;}
+       .st77{fill:url(#SVGID_110_);}
+       .st78{opacity:0.6;fill:url(#SVGID_113_);}
+       .st79{fill:url(#SVGID_116_);}
+       .st80{opacity:0.6;fill:url(#SVGID_119_);}
+       .st81{fill:url(#SVGID_122_);}
+       .st82{opacity:0.6;fill:url(#SVGID_125_);}
+       .st83{fill:url(#SVGID_128_);}
+       .st84{opacity:0.6;fill:url(#SVGID_131_);}
+       .st85{fill:#221F1F;}
+       .st86{fill:none;}
+       .st87{fill:#00416B;}
+       .st88{opacity:0.8;fill:url(#XMLID_323_);}
+       .st89{fill:#4197CB;}
+       .st90{fill:#003E52;}
+       .st91{fill:#3F96B4;}
+       .st92{fill:#B9DBE5;}
+       .st93{opacity:0.3;fill:#231F20;}
+       .st94{opacity:0.3;fill:#FFFFFF;}
+       .st95{fill:#050013;}
+       .st96{fill:#E87200;}
+       .st97{fill:#FCB813;}
+       .st98{fill:#3D3935;}
+       .st99{fill:#FFB600;}
+       .st100{fill:#FCB814;}
+       .st101{fill:#F48120;}
+       .st102{fill:#EF4E25;}
+       .st103{fill:#ED3024;}
+       .st104{fill:#E0592A;}
+       .st105{fill:#00ADBB;}
+       .st106{fill:#00829B;}
+       .st107{fill:#93D500;}
+       .st108{fill:#4D5A31;}
+       .st109{fill:#6BA43A;}
+       .st110{fill:#424143;}
+       .st111{fill-rule:evenodd;clip-rule:evenodd;fill:#C7E6B4;}
+       .st112{fill-rule:evenodd;clip-rule:evenodd;fill:#5A9891;}
+       .st113{fill-rule:evenodd;clip-rule:evenodd;fill:#127870;}
+       .st114{fill-rule:evenodd;clip-rule:evenodd;fill:#5CCFD5;}
+       .st115{fill-rule:evenodd;clip-rule:evenodd;fill:#ACD5CD;}
+       .st116{fill-rule:evenodd;clip-rule:evenodd;fill:#B5ECC9;}
+       .st117{fill-rule:evenodd;clip-rule:evenodd;fill:#A1D683;}
+       .st118{fill-rule:evenodd;clip-rule:evenodd;fill:#DEF0D3;}
+       .st119{fill-rule:evenodd;clip-rule:evenodd;fill:#91B9B4;}
+       .st120{fill-rule:evenodd;clip-rule:evenodd;fill:#006860;}
+       .st121{fill-rule:evenodd;clip-rule:evenodd;fill:#00ADBB;}
+       .st122{fill-rule:evenodd;clip-rule:evenodd;fill:#B4E7E9;}
+       .st123{fill-rule:evenodd;clip-rule:evenodd;fill:#007565;}
+       .st124{fill-rule:evenodd;clip-rule:evenodd;fill:#00CE7C;}
+       .st125{fill-rule:evenodd;clip-rule:evenodd;fill:#5FD896;}
+       .st126{fill:#007DA5;}
+       .st127{fill:#313032;}
+       .st128{fill:#24272A;}
+       .st129{fill:#00AFAA;}
+       .st130{fill:#66C9BA;}
+       .st131{fill:#0069A7;}
+       .st132{fill:#002F87;}
+       .st133{fill:#8BC53F;}
+       .st134{fill:#1A1A1A;}
+       .st135{fill:#0095D6;}
+       .st136{fill:#003F5F;}
+       .st137{fill:#2D317C;}
+       .st138{fill:#41BFBF;}
+       .st139{fill:#293C97;}
+       .st140{fill:#52C2BD;}
+       .st141{fill:url(#SVGID_134_);}
+       .st142{fill:url(#SVGID_135_);}
+       .st143{fill:url(#SVGID_136_);}
+       .st144{fill:#0DBEEA;}
+       .st145{fill:#097EC2;}
+       .st146{fill:#133C63;}
+       .st147{fill:#3B91CF;}
+       .st148{fill:#C8DEE8;}
+       .st149{fill:#629BBA;}
+       .st150{fill:#F8BE19;}
+       .st151{fill:url(#SVGID_137_);}
+       .st152{fill:url(#SVGID_138_);}
+       .st153{fill:url(#SVGID_139_);}
+       .st154{fill:#00233B;}
+       .st155{fill:url(#SVGID_140_);}
+       .st156{fill:url(#SVGID_141_);}
+       .st157{fill:url(#SVGID_142_);}
+       .st158{fill:url(#SVGID_143_);}
+       .st159{fill:url(#SVGID_144_);}
+       .st160{fill:url(#SVGID_145_);}
+       .st161{fill:url(#SVGID_146_);}
+       .st162{fill:url(#SVGID_147_);}
+       .st163{fill:url(#SVGID_148_);}
+       .st164{fill:url(#SVGID_149_);}
+       .st165{fill:url(#SVGID_150_);}
+       .st166{fill:url(#SVGID_151_);}
+       .st167{fill:url(#SVGID_152_);}
+       .st168{fill:url(#SVGID_153_);}
+       .st169{fill:url(#SVGID_154_);}
+       .st170{fill:url(#SVGID_155_);}
+       .st171{fill:url(#SVGID_156_);}
+       .st172{fill:url(#SVGID_157_);}
+       .st173{fill:url(#SVGID_158_);}
+       .st174{fill:url(#SVGID_159_);}
+       .st175{fill:url(#SVGID_160_);}
+       .st176{fill:url(#SVGID_161_);}
+       .st177{fill:url(#SVGID_162_);}
+       .st178{fill:url(#SVGID_163_);}
+       .st179{fill:url(#SVGID_164_);}
+       .st180{fill:url(#SVGID_165_);}
+       .st181{fill:url(#SVGID_166_);}
+       .st182{fill:url(#SVGID_167_);}
+       .st183{fill:url(#SVGID_168_);}
+       .st184{fill:url(#SVGID_169_);}
+       .st185{fill:url(#SVGID_170_);}
+       .st186{fill:url(#SVGID_171_);}
+       .st187{fill:url(#SVGID_172_);}
+       .st188{fill:url(#SVGID_173_);}
+       .st189{fill:url(#SVGID_174_);}
+       .st190{fill:url(#SVGID_175_);}
+       .st191{fill:url(#SVGID_176_);}
+       .st192{fill:url(#SVGID_177_);}
+       .st193{fill:url(#SVGID_178_);}
+       .st194{fill:#C31230;}
+       .st195{fill:#807F82;}
+       .st196{fill-rule:evenodd;clip-rule:evenodd;fill:#C31230;}
+       .st197{fill-rule:evenodd;clip-rule:evenodd;fill:#807F82;}
+       .st198{fill:#2D2D2D;}
+       .st199{display:none;fill:#2D2D2D;}
+       .st200{fill:#D11F3C;}
+       .st201{fill:#E42C4C;stroke:#E42C4C;stroke-width:1.0503;stroke-miterlimit:10;}
+       .st202{display:none;fill:#231F20;}
+       .st203{display:none;fill:#FFFFFF;}
+       .st204{fill:#FF7F30;}
+       .st205{opacity:0.3;fill:#FF7F30;}
+       .st206{opacity:0.6;fill:#FF7F30;}
+       .st207{opacity:0.7;fill:#FF7F30;}
+       .st208{fill:#221C35;}
+       .st209{fill:#1B98D5;}
+       .st210{fill:#173963;}
+       .st211{fill:#009ADE;}
+       .st212{fill:#003764;}
+       .st213{fill:#2A7DE1;}
+       .st214{opacity:0.4;clip-path:url(#XMLID_324_);fill:#221F1F;}
+       .st215{fill:#002A3A;}
+       .st216{fill:#0033A1;}
+       .st217{fill:url(#SVGID_179_);}
+       .st218{fill:url(#SVGID_180_);}
+       .st219{fill:url(#SVGID_181_);}
+       .st220{fill:url(#SVGID_182_);}
+       .st221{fill:#007EC4;}
+       .st222{fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_183_);}
+       .st223{fill-rule:evenodd;clip-rule:evenodd;fill:#E6E7E8;}
+       .st224{fill:#009345;}
+       .st225{fill:#BBBCB8;}
+       .st226{fill:#72C0EB;}
+       .st227{fill:#939598;}
+       .st228{fill-rule:evenodd;clip-rule:evenodd;fill:#2CB8EB;}
+       .st229{fill:#2CB8EB;}
+       .st230{fill:#81B83A;}
+       .st231{fill-rule:evenodd;clip-rule:evenodd;fill:#81B83A;}
+       .st232{enable-background:new    ;}
+       .st233{fill:#FF6F3E;}
+       .st234{fill:#12143D;}
+       .st235{fill:url(#SVGID_184_);}
+       .st236{fill:url(#SVGID_185_);}
+       .st237{fill:url(#SVGID_186_);}
+       .st238{fill:url(#SVGID_187_);}
+       .st239{fill:url(#SVGID_188_);}
+       .st240{fill:url(#SVGID_189_);}
+       .st241{fill:url(#SVGID_190_);}
+       .st242{fill:url(#SVGID_191_);}
+       .st243{fill:url(#SVGID_192_);}
+       .st244{fill:#7C51A0;}
+       .st245{fill:#9F66A9;}
+       .st246{fill:#9F80B9;}
+       .st247{fill:url(#SVGID_193_);}
+       .st248{fill:url(#SVGID_194_);}
+       .st249{fill:url(#SVGID_195_);}
+       .st250{fill:url(#SVGID_196_);}
+       .st251{fill:#2D3136;}
+       .st252{fill:#76777A;}
+       .st253{fill:#A7A8A9;}
+       .st254{fill:#0082CA;}
+       .st255{fill:#FFB259;}
+       .st256{fill:#385CAD;}
+       .st257{fill:#7BA0C4;}
+       .st258{fill:#EBA900;}
+       .st259{fill:#929497;}
+       .st260{opacity:0.7;fill:#FFFFFF;}
+       .st261{fill:#016BAF;}
+       .st262{fill:#343432;}
+       .st263{fill:#6D6E70;}
+       .st264{fill:#F4B01B;}
+       .st265{fill:#293271;}
+       .st266{fill:#A1D33C;}
+       .st267{fill:#212322;}
+       .st268{fill:#0047BA;}
+       .st269{fill:#969CDE;}
+       .st270{fill:#047BC1;}
+       .st271{fill:url(#SVGID_197_);}
+       .st272{fill:url(#SVGID_198_);}
+       .st273{fill:url(#SVGID_199_);}
+       .st274{fill:url(#SVGID_200_);}
+       .st275{fill:url(#SVGID_201_);}
+       .st276{fill:url(#SVGID_202_);}
+       .st277{fill:url(#SVGID_203_);}
+       .st278{fill:#13517C;}
+       .st279{fill:#0077A6;}
+       .st280{fill:none;stroke:#231F20;stroke-width:5.9036;stroke-miterlimit:10;}
+       .st281{fill:#00A94F;}
+       .st282{fill:none;stroke:#231F20;stroke-width:3.2172;stroke-miterlimit:10;}
+       .st283{fill:#59595C;}
+       .st284{opacity:0.349;fill:#F9AE19;}
+       .st285{opacity:0.349;fill:#E99F22;}
+       .st286{opacity:0.349;fill:#E47D25;}
+       .st287{fill:#F9AE19;}
+       .st288{fill:#E99F22;}
+       .st289{fill:#F09B20;}
+       .st290{fill:#E47D25;}
+       .st291{fill:#E89223;}
+       .st292{opacity:0.651;fill:#F9AE19;}
+       .st293{fill:#E68825;}
+       .st294{opacity:0.651;fill:#E99F22;}
+       .st295{fill:#EB8D23;}
+       .st296{opacity:0.7725;fill:#EF9B21;}
+       .st297{opacity:0.651;fill:#E47D25;}
+       .st298{opacity:0.7725;fill:#EA9622;}
+       .st299{fill:url(#SVGID_204_);}
+       .st300{fill:#55575B;}
+       .st301{fill:#EE424E;}
+       .st302{fill:#34424B;}
+</style>
+<g>
+       <g>
+               <path class="st55" d="M772.88,526c9.95,0,15.7,5.53,15.7,15.48c0,10.17-5.75,15.48-15.7,15.48c-9.95,0-15.48-5.31-15.48-15.48
+                       C757.4,531.53,762.93,526,772.88,526z"/>
+               <path class="st55" d="M832.94,393.35c8.18,0,13.71,3.32,13.71,12.38c0,9.29-5.53,12.6-13.71,12.6c-8.4,0-14.15-3.32-14.15-12.6
+                       C818.79,396.67,824.54,393.35,832.94,393.35z M821.22,438.67h22.99V554.3h-22.99V438.67z"/>
+               <path class="st55" d="M934.56,435.58c36.25,0,61.9,26.09,61.9,61.24c0,34.71-25.65,60.58-61.9,60.58
+                       c-35.82,0-61.69-25.87-61.69-60.58C872.88,461.67,898.75,435.58,934.56,435.58z M934.56,536.18c23.66,0,39.79-17.03,39.79-39.36
+                       c0-22.77-16.14-40.02-39.79-40.02c-23.44,0-39.36,17.25-39.36,40.02C895.21,519.15,911.13,536.18,934.56,536.18z"/>
+       </g>
+       <g>
+               <path class="st56" d="M724.15,245.36c-0.97-8.4-17.24-16.23-17.24-16.23c-6.81-4.71-8.03-16.23-7.16-20.6
+                       c0.87-4.36,15.01-36.66,15.01-36.66c11.52-11.35,31.6-35.44,33.87-48.88c1.07-6.31-3.14-38.93,5.41-49.75
+                       c5.63-7.12,22.35-15.36,25.84-18.16c3.49-2.79,6.28-11,4.71-14.84c-1.57-3.84-27.41-4.02-27.41-4.02s-2.16-10.79-17.62-11.8
+                       c-6.05-0.2-10.18,1.3-10.18,1.3c1.78-0.89,4.69-2.18,6.78-3.1c-0.54-3.56-1.89-11.45-3.24-11.72c-1.43-0.29-4.73,5.28-6.27,7.21
+                       l0.28,3.42l-1.11-2.85l-1.63-4.2l-0.02-0.06h0c-0.64-1.74-1.36-3.37-2.08-3.53c-1.57-0.34-38.06,50.81-42.6,57.44
+                       c-4.54,6.63-9.25,17.81-33.87,25.49c-17.37,5.42-53.43,6.32-81.13,15.16l0,0c-14.25,1.43-53.29,33.79-83.15,32.62
+                       c-20.05-0.78-35.61-6.28-39.57-15.25c0,0,4.19,41.32,25.6,43.64c0,0,13.5,2.33,26.53-9.31c0,0-3.72,7.36-8.38,9.5
+                       c0,0,18.07-4.62,29.06-12.94c3.37-7.06,9.22-17.4,17.46-25.17c0.53-0.52,0.86-0.8,0.86-0.8c-0.29,0.26-0.57,0.53-0.86,0.8
+                       c-2.65,2.6-10.68,11.48-11.86,24.73c-0.24,2.64-0.21,5.44,0.2,8.41c0,8.9,9.66,20.78,9.66,26.19c0,7.16-16.76,20.25-17.81,23.74
+                       c-0.58,1.95-2.35,16.91,0.69,29.77h-59.09c-56.8,0-78.68,56.8-78.68,56.8l-24.4,68h-10.52c-8.25,0-14.94,6.69-14.94,14.94
+                       c0,8.19,6.59,14.81,14.75,14.92l-0.01,0.02h0.2h5.92c8.25,0,14.94,6.69,14.94,14.94c0,8.25-6.69,14.94-14.94,14.94H197.01
+                       c-8.25,0-14.94,6.69-14.94,14.94c0,8.25,6.69,14.94,14.94,14.94h154.15c8.25,0,14.94,6.69,14.94,14.94
+                       c0,8.25-6.69,14.94-14.94,14.94h-32.39H146.48h-32.39c-8.25,0-14.94,6.69-14.94,14.94c0,8.25,6.69,14.94,14.94,14.94h183.08
+                       l-13.71,38.21h113.6l35.67-99.4h1.72c79.83,0,109.91-85.2,109.91-85.2h-81.05l15.29-42.6l88.87,0c56.8,0,83.71-85.2,83.71-85.2
+                       H539.99c-2.48-4.72-5.03-9.43-5.03-13.01c0-18.5,49.93-30.55,48.36-60.4c-4.08-16.85,2.51-25.92,2.51-25.92
+                       c-4.2,9.35,0.01,19.79,2.11,24c0.03,0,0.06,0,0.1,0c13.97,0,41.9,15.36,59.18,15.36c11.43,0,20.41-2.37,25.19-3.97
+                       c0.69-3.96,0.82-14.29-14.36-20.96c0,0,18.37,0.16,18.15,19.52c0,0-2.09,31.42,0.7,54.29c0.55,4.48,1.7,8.11,3.22,11.09h-0.52
+                       c-13.7,55.08-55.31,85.2-55.31,85.2h60.7l-51.92,142.02l-0.04-0.01H510.67c-56.8,0-85.2,85.2-85.2,85.2H601.9
+                       c56.8,0,111.69,5.33,144.18-85.2l40.77-113.6C810.25,293.33,776.46,249.98,724.15,245.36z"/>
+               <path class="st56" d="M56.25,489.25H18.47c-8.25,0-14.94,6.69-14.94,14.94c0,8.25,6.69,14.94,14.94,14.94h37.78
+                       c8.25,0,14.94-6.69,14.94-14.94C71.19,495.94,64.5,489.25,56.25,489.25z"/>
+               <path class="st56" d="M171.38,399.61h120.5c8.25,0,14.94-6.69,14.94-14.94c0-8.25-6.69-14.94-14.94-14.94H102.14
+                       c-8.25,0-14.94,6.69-14.94,14.94c0,8.25,6.69,14.94,14.94,14.94H171.38z"/>
+               <path class="st56" d="M180.84,339.85h162.85c8.25,0,14.94-6.69,14.94-14.94c0-8.25-6.69-14.94-14.94-14.94H180.84
+                       c-8.25,0-14.94,6.69-14.94,14.94C165.9,333.17,172.59,339.85,180.84,339.85z"/>
+       </g>
+</g>
+</svg>
diff --git a/resources/tools/dash/app/pal/static/img/logo.svg b/resources/tools/dash/app/pal/static/img/logo.svg
new file mode 100644 (file)
index 0000000..689757e
--- /dev/null
@@ -0,0 +1,348 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+        viewBox="0 0 1000 568.31" style="enable-background:new 0 0 1000 568.31;" xml:space="preserve">
+<style type="text/css">
+       .st0{fill:#1DCAD3;}
+       .st1{fill:#36B0C9;}
+       .st2{fill:#231F20;}
+       .st3{fill:#FFFFFF;}
+       .st4{fill:#9164CC;}
+       .st5{clip-path:url(#SVGID_2_);fill:url(#SVGID_3_);}
+       .st6{fill:#201747;}
+       .st7{fill-rule:evenodd;clip-rule:evenodd;fill:#10CFC9;}
+       .st8{clip-path:url(#SVGID_5_);fill:#231F20;}
+       .st9{fill-rule:evenodd;clip-rule:evenodd;fill:#231F20;}
+       .st10{clip-path:url(#SVGID_7_);fill:#FFFFFF;}
+       .st11{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}
+       .st12{fill:#8CCEAF;}
+       .st13{fill:#008476;}
+       .st14{fill:#25BCBD;}
+       .st15{fill:#004D70;}
+       .st16{fill:#20BBBB;}
+       .st17{fill:#024D70;}
+       .st18{fill-rule:evenodd;clip-rule:evenodd;fill:#F58B1F;}
+       .st19{fill-rule:evenodd;clip-rule:evenodd;fill:#18335B;}
+       .st20{clip-path:url(#SVGID_9_);}
+       .st21{clip-path:url(#SVGID_11_);}
+       .st22{fill:#18335B;}
+       .st23{fill:#F58B1F;}
+       .st24{clip-path:url(#SVGID_15_);}
+       .st25{clip-path:url(#SVGID_17_);}
+       .st26{clip-path:url(#SVGID_21_);}
+       .st27{clip-path:url(#SVGID_23_);}
+       .st28{clip-path:url(#SVGID_27_);}
+       .st29{clip-path:url(#SVGID_29_);}
+       .st30{clip-path:url(#SVGID_33_);}
+       .st31{clip-path:url(#SVGID_35_);}
+       .st32{clip-path:url(#SVGID_39_);}
+       .st33{clip-path:url(#SVGID_41_);}
+       .st34{fill:#416BA9;}
+       .st35{fill:#73C3D5;}
+       .st36{opacity:0.8;}
+       .st37{fill:#3A3A3A;}
+       .st38{fill:url(#SVGID_44_);}
+       .st39{fill:none;stroke:#000000;stroke-width:6.3384;}
+       .st40{fill:none;stroke:#000000;stroke-width:3.1692;}
+       .st41{fill:#48494B;}
+       .st42{fill:#C1986C;}
+       .st43{fill:url(#SVGID_63_);}
+       .st44{fill:url(#SVGID_64_);}
+       .st45{fill:url(#SVGID_65_);}
+       .st46{fill:url(#SVGID_66_);}
+       .st47{fill:url(#SVGID_67_);}
+       .st48{fill:#4D4E4E;}
+       .st49{fill:#27B373;}
+       .st50{fill:#5DC4CD;}
+       .st51{fill:#1E8756;}
+       .st52{fill:#3D1152;}
+       .st53{fill:#922C48;}
+       .st54{fill-rule:evenodd;clip-rule:evenodd;fill:#922C48;}
+       .st55{fill:#404041;}
+       .st56{fill:#EC1C24;}
+       .st57{fill:#373A36;}
+       .st58{fill:#808184;}
+       .st59{fill:#262261;}
+       .st60{fill:#6FCBDC;}
+       .st61{fill:#2F3436;}
+       .st62{fill:#5F97D0;}
+       .st63{fill:#132428;}
+       .st64{fill:#85C041;}
+       .st65{fill:#677784;}
+       .st66{fill:url(#SVGID_68_);}
+       .st67{opacity:0.2;clip-path:url(#SVGID_70_);}
+       .st68{fill:#FFFEFA;}
+       .st69{opacity:0.1;}
+       .st70{fill:url(#SVGID_71_);}
+       .st71{opacity:0.3;}
+       .st72{opacity:0.08;}
+       .st73{opacity:0.1;fill:url(#Wordmark_1_);}
+       .st74{fill:url(#SVGID_104_);}
+       .st75{opacity:0.6;fill:url(#SVGID_107_);}
+       .st76{opacity:0.4;}
+       .st77{fill:url(#SVGID_110_);}
+       .st78{opacity:0.6;fill:url(#SVGID_113_);}
+       .st79{fill:url(#SVGID_116_);}
+       .st80{opacity:0.6;fill:url(#SVGID_119_);}
+       .st81{fill:url(#SVGID_122_);}
+       .st82{opacity:0.6;fill:url(#SVGID_125_);}
+       .st83{fill:url(#SVGID_128_);}
+       .st84{opacity:0.6;fill:url(#SVGID_131_);}
+       .st85{fill:#221F1F;}
+       .st86{fill:none;}
+       .st87{fill:#00416B;}
+       .st88{opacity:0.8;fill:url(#XMLID_323_);}
+       .st89{fill:#4197CB;}
+       .st90{fill:#003E52;}
+       .st91{fill:#3F96B4;}
+       .st92{fill:#B9DBE5;}
+       .st93{opacity:0.3;fill:#231F20;}
+       .st94{opacity:0.3;fill:#FFFFFF;}
+       .st95{fill:#050013;}
+       .st96{fill:#E87200;}
+       .st97{fill:#FCB813;}
+       .st98{fill:#3D3935;}
+       .st99{fill:#FFB600;}
+       .st100{fill:#FCB814;}
+       .st101{fill:#F48120;}
+       .st102{fill:#EF4E25;}
+       .st103{fill:#ED3024;}
+       .st104{fill:#E0592A;}
+       .st105{fill:#00ADBB;}
+       .st106{fill:#00829B;}
+       .st107{fill:#93D500;}
+       .st108{fill:#4D5A31;}
+       .st109{fill:#6BA43A;}
+       .st110{fill:#424143;}
+       .st111{fill-rule:evenodd;clip-rule:evenodd;fill:#C7E6B4;}
+       .st112{fill-rule:evenodd;clip-rule:evenodd;fill:#5A9891;}
+       .st113{fill-rule:evenodd;clip-rule:evenodd;fill:#127870;}
+       .st114{fill-rule:evenodd;clip-rule:evenodd;fill:#5CCFD5;}
+       .st115{fill-rule:evenodd;clip-rule:evenodd;fill:#ACD5CD;}
+       .st116{fill-rule:evenodd;clip-rule:evenodd;fill:#B5ECC9;}
+       .st117{fill-rule:evenodd;clip-rule:evenodd;fill:#A1D683;}
+       .st118{fill-rule:evenodd;clip-rule:evenodd;fill:#DEF0D3;}
+       .st119{fill-rule:evenodd;clip-rule:evenodd;fill:#91B9B4;}
+       .st120{fill-rule:evenodd;clip-rule:evenodd;fill:#006860;}
+       .st121{fill-rule:evenodd;clip-rule:evenodd;fill:#00ADBB;}
+       .st122{fill-rule:evenodd;clip-rule:evenodd;fill:#B4E7E9;}
+       .st123{fill-rule:evenodd;clip-rule:evenodd;fill:#007565;}
+       .st124{fill-rule:evenodd;clip-rule:evenodd;fill:#00CE7C;}
+       .st125{fill-rule:evenodd;clip-rule:evenodd;fill:#5FD896;}
+       .st126{fill:#007DA5;}
+       .st127{fill:#313032;}
+       .st128{fill:#24272A;}
+       .st129{fill:#00AFAA;}
+       .st130{fill:#66C9BA;}
+       .st131{fill:#0069A7;}
+       .st132{fill:#002F87;}
+       .st133{fill:#8BC53F;}
+       .st134{fill:#1A1A1A;}
+       .st135{fill:#0095D6;}
+       .st136{fill:#003F5F;}
+       .st137{fill:#2D317C;}
+       .st138{fill:#41BFBF;}
+       .st139{fill:#293C97;}
+       .st140{fill:#52C2BD;}
+       .st141{fill:url(#SVGID_134_);}
+       .st142{fill:url(#SVGID_135_);}
+       .st143{fill:url(#SVGID_136_);}
+       .st144{fill:#0DBEEA;}
+       .st145{fill:#097EC2;}
+       .st146{fill:#133C63;}
+       .st147{fill:#3B91CF;}
+       .st148{fill:#C8DEE8;}
+       .st149{fill:#629BBA;}
+       .st150{fill:#F8BE19;}
+       .st151{fill:url(#SVGID_137_);}
+       .st152{fill:url(#SVGID_138_);}
+       .st153{fill:url(#SVGID_139_);}
+       .st154{fill:#00233B;}
+       .st155{fill:url(#SVGID_140_);}
+       .st156{fill:url(#SVGID_141_);}
+       .st157{fill:url(#SVGID_142_);}
+       .st158{fill:url(#SVGID_143_);}
+       .st159{fill:url(#SVGID_144_);}
+       .st160{fill:url(#SVGID_145_);}
+       .st161{fill:url(#SVGID_146_);}
+       .st162{fill:url(#SVGID_147_);}
+       .st163{fill:url(#SVGID_148_);}
+       .st164{fill:url(#SVGID_149_);}
+       .st165{fill:url(#SVGID_150_);}
+       .st166{fill:url(#SVGID_151_);}
+       .st167{fill:url(#SVGID_152_);}
+       .st168{fill:url(#SVGID_153_);}
+       .st169{fill:url(#SVGID_154_);}
+       .st170{fill:url(#SVGID_155_);}
+       .st171{fill:url(#SVGID_156_);}
+       .st172{fill:url(#SVGID_157_);}
+       .st173{fill:url(#SVGID_158_);}
+       .st174{fill:url(#SVGID_159_);}
+       .st175{fill:url(#SVGID_160_);}
+       .st176{fill:url(#SVGID_161_);}
+       .st177{fill:url(#SVGID_162_);}
+       .st178{fill:url(#SVGID_163_);}
+       .st179{fill:url(#SVGID_164_);}
+       .st180{fill:url(#SVGID_165_);}
+       .st181{fill:url(#SVGID_166_);}
+       .st182{fill:url(#SVGID_167_);}
+       .st183{fill:url(#SVGID_168_);}
+       .st184{fill:url(#SVGID_169_);}
+       .st185{fill:url(#SVGID_170_);}
+       .st186{fill:url(#SVGID_171_);}
+       .st187{fill:url(#SVGID_172_);}
+       .st188{fill:url(#SVGID_173_);}
+       .st189{fill:url(#SVGID_174_);}
+       .st190{fill:url(#SVGID_175_);}
+       .st191{fill:url(#SVGID_176_);}
+       .st192{fill:url(#SVGID_177_);}
+       .st193{fill:url(#SVGID_178_);}
+       .st194{fill:#C31230;}
+       .st195{fill:#807F82;}
+       .st196{fill-rule:evenodd;clip-rule:evenodd;fill:#C31230;}
+       .st197{fill-rule:evenodd;clip-rule:evenodd;fill:#807F82;}
+       .st198{fill:#2D2D2D;}
+       .st199{display:none;fill:#2D2D2D;}
+       .st200{fill:#D11F3C;}
+       .st201{fill:#E42C4C;stroke:#E42C4C;stroke-width:1.0503;stroke-miterlimit:10;}
+       .st202{display:none;fill:#231F20;}
+       .st203{display:none;fill:#FFFFFF;}
+       .st204{fill:#FF7F30;}
+       .st205{opacity:0.3;fill:#FF7F30;}
+       .st206{opacity:0.6;fill:#FF7F30;}
+       .st207{opacity:0.7;fill:#FF7F30;}
+       .st208{fill:#221C35;}
+       .st209{fill:#1B98D5;}
+       .st210{fill:#173963;}
+       .st211{fill:#009ADE;}
+       .st212{fill:#003764;}
+       .st213{fill:#2A7DE1;}
+       .st214{opacity:0.4;clip-path:url(#XMLID_324_);fill:#221F1F;}
+       .st215{fill:#002A3A;}
+       .st216{fill:#0033A1;}
+       .st217{fill:url(#SVGID_179_);}
+       .st218{fill:url(#SVGID_180_);}
+       .st219{fill:url(#SVGID_181_);}
+       .st220{fill:url(#SVGID_182_);}
+       .st221{fill:#007EC4;}
+       .st222{fill-rule:evenodd;clip-rule:evenodd;fill:url(#SVGID_183_);}
+       .st223{fill-rule:evenodd;clip-rule:evenodd;fill:#E6E7E8;}
+       .st224{fill:#009345;}
+       .st225{fill:#BBBCB8;}
+       .st226{fill:#72C0EB;}
+       .st227{fill:#939598;}
+       .st228{fill-rule:evenodd;clip-rule:evenodd;fill:#2CB8EB;}
+       .st229{fill:#2CB8EB;}
+       .st230{fill:#81B83A;}
+       .st231{fill-rule:evenodd;clip-rule:evenodd;fill:#81B83A;}
+       .st232{enable-background:new    ;}
+       .st233{fill:#FF6F3E;}
+       .st234{fill:#12143D;}
+       .st235{fill:url(#SVGID_184_);}
+       .st236{fill:url(#SVGID_185_);}
+       .st237{fill:url(#SVGID_186_);}
+       .st238{fill:url(#SVGID_187_);}
+       .st239{fill:url(#SVGID_188_);}
+       .st240{fill:url(#SVGID_189_);}
+       .st241{fill:url(#SVGID_190_);}
+       .st242{fill:url(#SVGID_191_);}
+       .st243{fill:url(#SVGID_192_);}
+       .st244{fill:#7C51A0;}
+       .st245{fill:#9F66A9;}
+       .st246{fill:#9F80B9;}
+       .st247{fill:url(#SVGID_193_);}
+       .st248{fill:url(#SVGID_194_);}
+       .st249{fill:url(#SVGID_195_);}
+       .st250{fill:url(#SVGID_196_);}
+       .st251{fill:#2D3136;}
+       .st252{fill:#76777A;}
+       .st253{fill:#A7A8A9;}
+       .st254{fill:#0082CA;}
+       .st255{fill:#FFB259;}
+       .st256{fill:#385CAD;}
+       .st257{fill:#7BA0C4;}
+       .st258{fill:#EBA900;}
+       .st259{fill:#929497;}
+       .st260{opacity:0.7;fill:#FFFFFF;}
+       .st261{fill:#016BAF;}
+       .st262{fill:#343432;}
+       .st263{fill:#6D6E70;}
+       .st264{fill:#F4B01B;}
+       .st265{fill:#293271;}
+       .st266{fill:#A1D33C;}
+       .st267{fill:#212322;}
+       .st268{fill:#0047BA;}
+       .st269{fill:#969CDE;}
+       .st270{fill:#047BC1;}
+       .st271{fill:url(#SVGID_197_);}
+       .st272{fill:url(#SVGID_198_);}
+       .st273{fill:url(#SVGID_199_);}
+       .st274{fill:url(#SVGID_200_);}
+       .st275{fill:url(#SVGID_201_);}
+       .st276{fill:url(#SVGID_202_);}
+       .st277{fill:url(#SVGID_203_);}
+       .st278{fill:#13517C;}
+       .st279{fill:#0077A6;}
+       .st280{fill:none;stroke:#231F20;stroke-width:5.9036;stroke-miterlimit:10;}
+       .st281{fill:#00A94F;}
+       .st282{fill:none;stroke:#231F20;stroke-width:3.2172;stroke-miterlimit:10;}
+       .st283{fill:#59595C;}
+       .st284{opacity:0.349;fill:#F9AE19;}
+       .st285{opacity:0.349;fill:#E99F22;}
+       .st286{opacity:0.349;fill:#E47D25;}
+       .st287{fill:#F9AE19;}
+       .st288{fill:#E99F22;}
+       .st289{fill:#F09B20;}
+       .st290{fill:#E47D25;}
+       .st291{fill:#E89223;}
+       .st292{opacity:0.651;fill:#F9AE19;}
+       .st293{fill:#E68825;}
+       .st294{opacity:0.651;fill:#E99F22;}
+       .st295{fill:#EB8D23;}
+       .st296{opacity:0.7725;fill:#EF9B21;}
+       .st297{opacity:0.651;fill:#E47D25;}
+       .st298{opacity:0.7725;fill:#EA9622;}
+       .st299{fill:url(#SVGID_204_);}
+       .st300{fill:#55575B;}
+       .st301{fill:#EE424E;}
+       .st302{fill:#34424B;}
+</style>
+<g>
+       <g>
+               <path class="st55" d="M772.88,526c9.95,0,15.7,5.53,15.7,15.48c0,10.17-5.75,15.48-15.7,15.48c-9.95,0-15.48-5.31-15.48-15.48
+                       C757.4,531.53,762.93,526,772.88,526z"/>
+               <path class="st55" d="M832.94,393.35c8.18,0,13.71,3.32,13.71,12.38c0,9.29-5.53,12.6-13.71,12.6c-8.4,0-14.15-3.32-14.15-12.6
+                       C818.79,396.67,824.54,393.35,832.94,393.35z M821.22,438.67h22.99V554.3h-22.99V438.67z"/>
+               <path class="st55" d="M934.56,435.58c36.25,0,61.9,26.09,61.9,61.24c0,34.71-25.65,60.58-61.9,60.58
+                       c-35.82,0-61.69-25.87-61.69-60.58C872.88,461.67,898.75,435.58,934.56,435.58z M934.56,536.18c23.66,0,39.79-17.03,39.79-39.36
+                       c0-22.77-16.14-40.02-39.79-40.02c-23.44,0-39.36,17.25-39.36,40.02C895.21,519.15,911.13,536.18,934.56,536.18z"/>
+       </g>
+       <g>
+               <path class="st56" d="M724.15,245.36c-0.97-8.4-17.24-16.23-17.24-16.23c-6.81-4.71-8.03-16.23-7.16-20.6
+                       c0.87-4.36,15.01-36.66,15.01-36.66c11.52-11.35,31.6-35.44,33.87-48.88c1.07-6.31-3.14-38.93,5.41-49.75
+                       c5.63-7.12,22.35-15.36,25.84-18.16c3.49-2.79,6.28-11,4.71-14.84c-1.57-3.84-27.41-4.02-27.41-4.02s-2.16-10.79-17.62-11.8
+                       c-6.05-0.2-10.18,1.3-10.18,1.3c1.78-0.89,4.69-2.18,6.78-3.1c-0.54-3.56-1.89-11.45-3.24-11.72c-1.43-0.29-4.73,5.28-6.27,7.21
+                       l0.28,3.42l-1.11-2.85l-1.63-4.2l-0.02-0.06h0c-0.64-1.74-1.36-3.37-2.08-3.53c-1.57-0.34-38.06,50.81-42.6,57.44
+                       c-4.54,6.63-9.25,17.81-33.87,25.49c-17.37,5.42-53.43,6.32-81.13,15.16l0,0c-14.25,1.43-53.29,33.79-83.15,32.62
+                       c-20.05-0.78-35.61-6.28-39.57-15.25c0,0,4.19,41.32,25.6,43.64c0,0,13.5,2.33,26.53-9.31c0,0-3.72,7.36-8.38,9.5
+                       c0,0,18.07-4.62,29.06-12.94c3.37-7.06,9.22-17.4,17.46-25.17c0.53-0.52,0.86-0.8,0.86-0.8c-0.29,0.26-0.57,0.53-0.86,0.8
+                       c-2.65,2.6-10.68,11.48-11.86,24.73c-0.24,2.64-0.21,5.44,0.2,8.41c0,8.9,9.66,20.78,9.66,26.19c0,7.16-16.76,20.25-17.81,23.74
+                       c-0.58,1.95-2.35,16.91,0.69,29.77h-59.09c-56.8,0-78.68,56.8-78.68,56.8l-24.4,68h-10.52c-8.25,0-14.94,6.69-14.94,14.94
+                       c0,8.19,6.59,14.81,14.75,14.92l-0.01,0.02h0.2h5.92c8.25,0,14.94,6.69,14.94,14.94c0,8.25-6.69,14.94-14.94,14.94H197.01
+                       c-8.25,0-14.94,6.69-14.94,14.94c0,8.25,6.69,14.94,14.94,14.94h154.15c8.25,0,14.94,6.69,14.94,14.94
+                       c0,8.25-6.69,14.94-14.94,14.94h-32.39H146.48h-32.39c-8.25,0-14.94,6.69-14.94,14.94c0,8.25,6.69,14.94,14.94,14.94h183.08
+                       l-13.71,38.21h113.6l35.67-99.4h1.72c79.83,0,109.91-85.2,109.91-85.2h-81.05l15.29-42.6l88.87,0c56.8,0,83.71-85.2,83.71-85.2
+                       H539.99c-2.48-4.72-5.03-9.43-5.03-13.01c0-18.5,49.93-30.55,48.36-60.4c-4.08-16.85,2.51-25.92,2.51-25.92
+                       c-4.2,9.35,0.01,19.79,2.11,24c0.03,0,0.06,0,0.1,0c13.97,0,41.9,15.36,59.18,15.36c11.43,0,20.41-2.37,25.19-3.97
+                       c0.69-3.96,0.82-14.29-14.36-20.96c0,0,18.37,0.16,18.15,19.52c0,0-2.09,31.42,0.7,54.29c0.55,4.48,1.7,8.11,3.22,11.09h-0.52
+                       c-13.7,55.08-55.31,85.2-55.31,85.2h60.7l-51.92,142.02l-0.04-0.01H510.67c-56.8,0-85.2,85.2-85.2,85.2H601.9
+                       c56.8,0,111.69,5.33,144.18-85.2l40.77-113.6C810.25,293.33,776.46,249.98,724.15,245.36z"/>
+               <path class="st56" d="M56.25,489.25H18.47c-8.25,0-14.94,6.69-14.94,14.94c0,8.25,6.69,14.94,14.94,14.94h37.78
+                       c8.25,0,14.94-6.69,14.94-14.94C71.19,495.94,64.5,489.25,56.25,489.25z"/>
+               <path class="st56" d="M171.38,399.61h120.5c8.25,0,14.94-6.69,14.94-14.94c0-8.25-6.69-14.94-14.94-14.94H102.14
+                       c-8.25,0-14.94,6.69-14.94,14.94c0,8.25,6.69,14.94,14.94,14.94H171.38z"/>
+               <path class="st56" d="M180.84,339.85h162.85c8.25,0,14.94-6.69,14.94-14.94c0-8.25-6.69-14.94-14.94-14.94H180.84
+                       c-8.25,0-14.94,6.69-14.94,14.94C165.9,333.17,172.59,339.85,180.84,339.85z"/>
+       </g>
+</g>
+</svg>
diff --git a/resources/tools/dash/app/pal/static/less/global.less b/resources/tools/dash/app/pal/static/less/global.less
new file mode 100644 (file)
index 0000000..78b7ae4
--- /dev/null
@@ -0,0 +1,12 @@
+body,
+html {
+  height: 100% !important;
+  margin: 0;
+  padding: 0;
+  background: #e7ecf7 !important;
+  font-family: 'Lato', sans-serif;
+}
+
+.row .col {
+  padding: 0 !important;
+}
diff --git a/resources/tools/dash/app/pal/static/less/header.less b/resources/tools/dash/app/pal/static/less/header.less
new file mode 100644 (file)
index 0000000..49f6769
--- /dev/null
@@ -0,0 +1,33 @@
+header {
+  position: relative;
+  width: 100%;
+  padding: 30px 0 !important;
+  background: white !important;
+  box-shadow: 0 0 5px #bec6cf;
+  font-family: 'Lato', sans-serif;
+
+  .nav-wrapper {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    width: 1000px;
+    max-width: 90%;
+    margin: auto;
+
+    nav {
+      display: flex;
+    }
+
+    .logo {
+      display: inline-block;
+      width: 40px;
+    }
+
+    a {
+      color: #70829d;
+      font-size: 1em;
+      text-decoration: none;
+      transition: all 0.3s ease-out;
+    }
+  }
+}
diff --git a/resources/tools/dash/app/pal/static/less/home.less b/resources/tools/dash/app/pal/static/less/home.less
new file mode 100644 (file)
index 0000000..ebd66a2
--- /dev/null
@@ -0,0 +1,64 @@
+.home-template {
+  .container {
+    margin: 0 !important;
+  }
+
+  .card {
+    width: 800px;
+    max-width: 93%;
+    height: fit-content;
+    margin: 50px auto;
+    padding: 60px;
+    background: white;
+    box-shadow: 0 0 5px rgba(65, 67, 144, 0.15);
+    text-align: center;
+    @media (max-width: 800px) {
+      max-width: 78%;
+      width: unset;
+      padding: 40px;
+    }
+
+    .logo {
+      width: 50px;
+      margin: auto;
+    }
+
+    .site-title {
+      margin: 10px 0 3px;
+      color: #5f6988;
+      font-family: proxima-nova, sans-serif;
+      font-size: 2.3em;
+      line-height: 1;
+      text-transform: uppercase;
+    }
+
+    p {
+      margin: 0;
+      color: #7f92af;
+      font-size: 1.08em;
+    }
+
+    a {
+      color: #79aec8;
+      font-weight: 500;
+      text-decoration: none;
+      transition: all .2s ease;
+    }
+
+    .dash-link {
+      display: block;
+      margin-top: 30px;
+      font-size: 1.1em;
+      font-weight: 600;
+
+      &:hover {
+        opacity: .7;
+      }
+
+      i {
+        margin-left: 6px;
+        font-size: .9em;
+      }
+    }
+  }
+}
diff --git a/resources/tools/dash/app/pal/static/less/table.less b/resources/tools/dash/app/pal/static/less/table.less
new file mode 100644 (file)
index 0000000..40e18e0
--- /dev/null
@@ -0,0 +1,256 @@
+body,
+html {
+  height: 100% !important;
+  padding: 0;
+  background: #e7ecf7 !important;
+  font-family: 'Lato', sans-serif;
+  margin: 0;
+}
+
+.dash-template {
+  h1 {
+    display: inline-block;
+    margin: 0;
+    font-size: 1.5em;
+  }
+
+  .logo {
+    width: 40px;
+    margin-right: 20px;
+
+    &:hover {
+      opacity: .7;
+    }
+  }
+
+  .nav-wrapper a {
+    display: flex;
+    align-items: center;
+  }
+}
+
+#dash-container {
+  width: 1200px;
+  max-width: 95%;
+  margin: 50px auto 0;
+}
+
+.container {
+  margin: 90px 30px;
+  color: #182635;
+}
+
+#_dash-app-content {
+  max-width: 95% !important;
+  margin: 90px auto !important;
+  overflow: hidden;
+}
+
+nav {
+  a {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #59657b;
+    line-height: 1;
+    text-decoration: none;
+    transition: all 0.3s ease-out;
+
+    &:hover {
+      cursor: pointer;
+      opacity: 0.7;
+    }
+
+    i {
+      margin-right: 5px;
+    }
+  }
+}
+
+.dash-spreadsheet-container {
+  max-width: 100%;
+  margin: 0 auto 20px !important;
+  overflow: hidden;
+  border: 0;
+  border-radius: 4px;
+  box-shadow: 0 0 4px #cdd3e2;
+  font-family: 'Lato', sans-serif;
+
+  * {
+    box-shadow: none !important;
+    font-family: Lato, sans-serif;
+  }
+
+  th,
+  tr {
+    box-shadow: none !important;
+  }
+
+  td:first-of-type,
+  tr:first-of-type {
+    width: 55px;
+  }
+
+  td:last-of-type,
+  tr:last-of-type {
+    width: 100px;
+  }
+
+  th {
+    padding: 25px 12px !important;
+    border-top: 0 !important;
+    border-right: 0 !important;
+    border-bottom: 1px solid #e5e7eb !important;
+    border-left: 0 !important;
+    background: white !important;
+    color: #404552;
+
+    .column-header--sort {
+      margin-right: 7px;
+      transition: all 0.2s ease-out;
+
+      &:hover {
+        color: #9bd2eb !important;
+      }
+
+      svg {
+        width: 0.5em;
+      }
+    }
+
+    .sort {
+      order: 2;
+      color: #aeaeae !important;
+      transition: all 0.3s ease-out;
+
+      &:hover {
+        text-decoration: none;
+        cursor: pointer;
+        opacity: 0.7;
+      }
+    }
+
+    & > div {
+      display: flex;
+      align-items: center;
+      width: fit-content;
+
+      span:last-of-type {
+        font-size: 0.8em;
+        font-weight: 600;
+        text-transform: uppercase;
+      }
+    }
+  }
+
+  td {
+    padding: 12px !important;
+    font-size: .95em;
+    text-align: left !important;
+
+    &:first-of-type,
+    &:last-of-type {
+      max-width: 50px !important;
+    }
+
+    .dash-cell-value {
+      display: block !important;
+      max-width: 500px;
+      overflow: hidden !important;
+      font-size: 1.2em;
+      font-weight: 300;
+      text-align: left;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      opacity: .8;
+    }
+  }
+
+  td[data-dash-column="command"],
+  .column-1 {
+    max-width: 200px !important;
+  }
+
+  td[data-dash-column="index"] {
+    text-align: center !important;
+
+    div {
+      text-align: center !important;
+    }
+  }
+
+  td[data-dash-column="index"] {
+    color: #939da4;
+    font-size: 1.1em;
+    font-weight: 500;
+  }
+
+  tr:nth-child(even) {
+    background: #f5f8fc !important;
+  }
+}
+
+table {
+  tbody {
+    box-shadow: 0 0 7px #bdbdd2 !important;
+
+    tr {
+      border: 0 !important;
+
+      &:nth-child(even) {
+        background: #e7eefa !important;
+      }
+
+      td {
+        padding: 12px 10px !important;
+        overflow: hidden !important;
+        border: 0 !important;
+        font-size: 0.65em !important;
+        line-height: 1.25 !important;
+        text-align: center !important;
+      }
+    }
+  }
+}
+
+.dash-spreadsheet-container .dash-spreadsheet-inner td.focused {
+  background-color: rgba(154, 212, 255, 0.2) !important;
+  box-shadow: 0 0 4px #cdd3e2;
+}
+
+.dash-spreadsheet-container .dash-spreadsheet-inner td .input-cell-value-shadow {
+  width: 100%;
+}
+
+.dash-spreadsheet-container .dash-spreadsheet-inner td input.dash-cell-value.unfocused {
+  caret-color: #317ed1 !important;
+}
+
+.dash-spreadsheet-inner .input-active {
+  overflow: visible !important;
+  color: #829ab2;
+  text-align: left !important;
+}
+
+.dash-spreadsheet-inner input {
+  &::placeholder {
+    color: grey;
+  }
+}
+
+#histogram-graph {
+  margin-bottom: 30px;
+  padding-bottom: 176px !important;
+  overflow: hidden;
+  border-radius: 4px;
+  background: white;
+  box-shadow: 0 0 4px #cdd3e2;
+}
+
+.main-svg {
+  overflow: visible !important;
+}
+
+header .nav-wrapper {
+  width: 1200px !important;
+}
diff --git a/resources/tools/dash/app/pal/templates/index.jinja2 b/resources/tools/dash/app/pal/templates/index.jinja2
new file mode 100644 (file)
index 0000000..8ab5c84
--- /dev/null
@@ -0,0 +1,13 @@
+{% extends "layout.jinja2" %}
+
+{% block content %}
+<div class="card">
+  <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>
+{% endblock %}
diff --git a/resources/tools/dash/app/pal/templates/layout.jinja2 b/resources/tools/dash/app/pal/templates/layout.jinja2
new file mode 100644 (file)
index 0000000..59abba3
--- /dev/null
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <meta charset="utf-8" />
+  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+  <title>{{ title }}</title>
+  <meta property="og:site_name" content="{{ title }}"/>
+  <meta property="og:type" content="website"/>
+  <meta property="og:title" content="{{ title }}"/>
+  <meta property="og:description" content="{{ description }}"/>
+  <meta property="og:url" content="https://fd.io/"/>
+  <meta name="HandheldFriendly" content="True" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
+  <link href="https://fonts.googleapis.com/css?family=Lato" rel="stylesheet">
+  <link rel="stylesheet" href="{{ url_for('static', filename='dist/css/styles.css') }}" />
+  <link rel="stylesheet" href="https://use.typekit.net/uqq2lcv.css">
+  <link rel="shortcut icon" href="{{ url_for('static', filename='dist/img/favicon.svg') }}" type="image/x-icon" />
+  <link
+      rel="stylesheet"
+      href="https://use.fontawesome.com/releases/v5.8.1/css/all.css"
+      integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf"
+      crossorigin="anonymous"
+  />
+</head>
+
+<body class="{{template}}">
+  <div class="container">
+         {% block content %}{% endblock %}
+  </div>
+</body>
+
+</html>
diff --git a/resources/tools/dash/app/pal/trending/dashboard.py b/resources/tools/dash/app/pal/trending/dashboard.py
new file mode 100644 (file)
index 0000000..ee5ea51
--- /dev/null
@@ -0,0 +1,69 @@
+# 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 a Dash app."""
+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 html_layout
+
+
+def init_dashboard(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="/trending/",
+        external_stylesheets=[
+            "/static/dist/css/styles.css",
+            "https://fonts.googleapis.com/css?family=Lato",
+        ],
+    )
+
+    # Load DataFrame
+    df = create_dataframe()
+
+    # Custom HTML layout
+    dash_app.index_string = html_layout
+
+    # Create Layout
+    dash_app.layout = html.Div(
+        children=[
+            create_data_table(df),
+        ],
+        id="dash-container",
+    )
+    return dash_app.server
+
+
+def create_data_table(df):
+    """Create Dash datatable from Pandas DataFrame."""
+    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,
+    )
+    return table
diff --git a/resources/tools/dash/app/pal/trending/data.py b/resources/tools/dash/app/pal/trending/data.py
new file mode 100644 (file)
index 0000000..06466a9
--- /dev/null
@@ -0,0 +1,24 @@
+# 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(
+        "https://s3-docs.fd.io/csit/master/trending/_static/vpp/"
+        "csit-vpp-perf-mrr-daily-master-2n-zn2-trending.csv"
+    )
diff --git a/resources/tools/dash/app/pal/trending/layout.py b/resources/tools/dash/app/pal/trending/layout.py
new file mode 100644 (file)
index 0000000..1f60aec
--- /dev/null
@@ -0,0 +1,43 @@
+# 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 = """
+<!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>
+"""
diff --git a/resources/tools/dash/app/requirements.txt b/resources/tools/dash/app/requirements.txt
new file mode 100644 (file)
index 0000000..90e595e
--- /dev/null
@@ -0,0 +1,35 @@
+attrs==21.2.0
+Brotli==1.0.9
+click==8.0.3
+dash==2.0.0
+dash-core-components==2.0.0
+dash-html-components==2.0.0
+dash-renderer==1.9.1
+dash-table==5.0.0
+Flask==2.0.2
+Flask-Assets==2.0
+Flask-Compress==1.10.1
+future==0.18.2
+intervaltree==3.1.0
+itsdangerous==2.0.1
+Jinja2==3.0.3
+MarkupSafe==2.0.1
+numpy==1.21.4
+packaging==21.3
+pandas==1.3.5
+pip==21.2.4
+plotly==5.4.0
+protobuf==3.19.1
+pyparsing==3.0.6
+python-dateutil==2.8.2
+python-dotenv==0.19.2
+pytz==2022.3
+retrying==1.3.3
+setuptools==57.5.0
+six==1.16.0
+sortedcontainers==2.4.0
+tenacity==8.0.1
+uWSGI==2.0.20
+webassets==2.0
+Werkzeug==2.0.2
+wheel==0.37.0
\ No newline at end of file
diff --git a/resources/tools/dash/app/wsgi.py b/resources/tools/dash/app/wsgi.py
new file mode 100644 (file)
index 0000000..c2832e2
--- /dev/null
@@ -0,0 +1,20 @@
+# 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.
+
+from pal import init_app
+
+app = init_app()
+
+if __name__ == "__main__":
+    # Main entry point.
+    app.run(host="0.0.0.0")
diff --git a/resources/tools/dash/docker-compose.yaml b/resources/tools/dash/docker-compose.yaml
new file mode 100644 (file)
index 0000000..335bcda
--- /dev/null
@@ -0,0 +1,19 @@
+version: "3"
+services:
+  dash:
+    build: "."
+    command: "uwsgi app.ini"
+    environment:
+      AWS_ACCESS_KEY_ID: ""
+      AWS_SECRET_ACCESS_KEY: ""
+      FLASK_APP: "wsgi.py"
+      FLASK_ENV: "production"
+      LESS_BIN: "/usr/local/bin/lessc"
+      ASSETS_DEBUG: "False"
+      LESS_RUN_IN_DEBUG: "False"
+      COMPRESSOR_DEBUG: "True"
+    ports:
+      - "5000:5000"
+    volumes:
+      - "./app/:/app"
+    working_dir: "/app"