UTI: Set params in ctrl panel from url
[csit.git] / resources / tools / dash / app / pal / trending / layout.py
index 3776214..187860d 100644 (file)
 """Plotly Dash HTML layout override.
 """
 
 """Plotly Dash HTML layout override.
 """
 
-
-import json
+import logging
 import pandas as pd
 import pandas as pd
+import dash_bootstrap_components as dbc
 
 
+from flask import Flask
 from dash import dcc
 from dash import html
 from dash import dcc
 from dash import html
-from dash import callback_context, no_update
-from dash import Input, Output, State, callback
+from dash import callback_context, no_update, ALL
+from dash import Input, Output, State
 from dash.exceptions import PreventUpdate
 from yaml import load, FullLoader, YAMLError
 from datetime import datetime, timedelta
 from dash.exceptions import PreventUpdate
 from yaml import load, FullLoader, YAMLError
 from datetime import datetime, timedelta
+from copy import deepcopy
+from json import loads, JSONDecodeError
+from ast import literal_eval
 
 
+from ..utils.constants import Constants as C
+from ..utils.utils import show_tooltip, label, sync_checklists, list_tests, \
+    get_date, gen_new_url, generate_options
+from ..utils.url_processing import url_decode
 from ..data.data import Data
 from ..data.data import Data
-from .graphs import trending_tput
+from .graphs import graph_trending, graph_hdrh_latency, \
+    select_trending_data
 
 
 class Layout:
 
 
 class Layout:
-    """
+    """The layout of the dash app and the callbacks.
     """
 
     """
 
-    STYLE_HIDEN = {"display": "none"}
-    STYLE_BLOCK = {"display": "block"}
-    STYLE_INLINE ={"display": "inline-block", "width": "50%"}
-    NO_GRAPH = {"data": [], "layout": {}, "frames": []}
+    def __init__(self, app: Flask, html_layout_file: str,
+        graph_layout_file: str, data_spec_file: str, tooltip_file: str,
+        time_period: str=None) -> None:
+        """Initialization:
+        - save the input parameters,
+        - read and pre-process the data,
+        - prepare data for the control panel,
+        - read HTML layout file,
+        - read tooltips from the tooltip file.
 
 
-    def __init__(self, app, html_layout_file, spec_file, graph_layout_file,
-        data_spec_file):
-        """
+        :param app: Flask application running the dash application.
+        :param html_layout_file: Path and name of the file specifying the HTML
+            layout of the dash application.
+        :param graph_layout_file: Path and name of the file with layout of
+            plot.ly graphs.
+        :param data_spec_file: Path and name of the file specifying the data to
+            be read from parquets for this application.
+        :param tooltip_file: Path and name of the yaml file specifying the
+            tooltips.
+        :param time_period: It defines the time period for data read from the
+            parquets in days from now back to the past.
+        :type app: Flask
+        :type html_layout_file: str
+        :type graph_layout_file: str
+        :type data_spec_file: str
+        :type tooltip_file: str
+        :type time_period: int
         """
 
         # Inputs
         self._app = app
         self._html_layout_file = html_layout_file
         """
 
         # Inputs
         self._app = app
         self._html_layout_file = html_layout_file
-        self._spec_file = spec_file
         self._graph_layout_file = graph_layout_file
         self._data_spec_file = data_spec_file
         self._graph_layout_file = graph_layout_file
         self._data_spec_file = data_spec_file
+        self._tooltip_file = tooltip_file
+        self._time_period = time_period
 
         # Read the data:
         data_mrr = Data(
             data_spec_file=self._data_spec_file,
             debug=True
 
         # Read the data:
         data_mrr = Data(
             data_spec_file=self._data_spec_file,
             debug=True
-        ).read_trending_mrr()
+        ).read_trending_mrr(days=self._time_period)
 
         data_ndrpdr = Data(
             data_spec_file=self._data_spec_file,
             debug=True
 
         data_ndrpdr = Data(
             data_spec_file=self._data_spec_file,
             debug=True
-        ).read_trending_ndrpdr()
+        ).read_trending_ndrpdr(days=self._time_period)
 
         self._data = pd.concat([data_mrr, data_ndrpdr], ignore_index=True)
 
 
         self._data = pd.concat([data_mrr, data_ndrpdr], ignore_index=True)
 
+        data_time_period = \
+            (datetime.utcnow() - self._data["start_time"].min()).days
+        if self._time_period > data_time_period:
+            self._time_period = data_time_period
+
+
+        # Get structure of tests:
+        tbs = dict()
+        for _, row in self._data[["job", "test_id"]].drop_duplicates().\
+                iterrows():
+            lst_job = row["job"].split("-")
+            dut = lst_job[1]
+            ttype = lst_job[3]
+            tbed = "-".join(lst_job[-2:])
+            lst_test = row["test_id"].split(".")
+            if dut == "dpdk":
+                area = "dpdk"
+            else:
+                area = "-".join(lst_test[3:-2])
+            suite = lst_test[-2].replace("2n1l-", "").replace("1n1l-", "").\
+                replace("2n-", "")
+            test = lst_test[-1]
+            nic = suite.split("-")[0]
+            for drv in C.DRIVERS:
+                if drv in test:
+                    if drv == "af-xdp":
+                        driver = "af_xdp"
+                    else:
+                        driver = drv
+                    test = test.replace(f"{drv}-", "")
+                    break
+            else:
+                driver = "dpdk"
+            infra = "-".join((tbed, nic, driver))
+            lst_test = test.split("-")
+            framesize = lst_test[0]
+            core = lst_test[1] if lst_test[1] else "8C"
+            test = "-".join(lst_test[2: -1])
+
+            if tbs.get(dut, None) is None:
+                tbs[dut] = dict()
+            if tbs[dut].get(infra, None) is None:
+                tbs[dut][infra] = dict()
+            if tbs[dut][infra].get(area, None) is None:
+                tbs[dut][infra][area] = dict()
+            if tbs[dut][infra][area].get(test, None) is None:
+                tbs[dut][infra][area][test] = dict()
+                tbs[dut][infra][area][test]["core"] = list()
+                tbs[dut][infra][area][test]["frame-size"] = list()
+                tbs[dut][infra][area][test]["test-type"] = list()
+            if core.upper() not in tbs[dut][infra][area][test]["core"]:
+                tbs[dut][infra][area][test]["core"].append(core.upper())
+            if framesize.upper() not in \
+                    tbs[dut][infra][area][test]["frame-size"]:
+                tbs[dut][infra][area][test]["frame-size"].append(
+                    framesize.upper())
+            if ttype == "mrr":
+                if "MRR" not in tbs[dut][infra][area][test]["test-type"]:
+                    tbs[dut][infra][area][test]["test-type"].append("MRR")
+            elif ttype == "ndrpdr":
+                if "NDR" not in tbs[dut][infra][area][test]["test-type"]:
+                    tbs[dut][infra][area][test]["test-type"].extend(
+                        ("NDR", "PDR"))
+        self._spec_tbs = tbs
+
         # Read from files:
         self._html_layout = ""
         # Read from files:
         self._html_layout = ""
-        self._spec_tbs = None
         self._graph_layout = None
         self._graph_layout = None
+        self._tooltips = dict()
 
         try:
             with open(self._html_layout_file, "r") as file_read:
 
         try:
             with open(self._html_layout_file, "r") as file_read:
@@ -78,32 +172,30 @@ class Layout:
             )
 
         try:
             )
 
         try:
-            with open(self._spec_file, "r") as file_read:
-                self._spec_tbs = load(file_read, Loader=FullLoader)
+            with open(self._graph_layout_file, "r") as file_read:
+                self._graph_layout = load(file_read, Loader=FullLoader)
         except IOError as err:
             raise RuntimeError(
         except IOError as err:
             raise RuntimeError(
-                f"Not possible to open the file {self._spec_file,}\n{err}"
+                f"Not possible to open the file {self._graph_layout_file}\n"
+                f"{err}"
             )
         except YAMLError as err:
             raise RuntimeError(
                 f"An error occurred while parsing the specification file "
             )
         except YAMLError as err:
             raise RuntimeError(
                 f"An error occurred while parsing the specification file "
-                f"{self._spec_file,}\n"
-                f"{err}"
+                f"{self._graph_layout_file}\n{err}"
             )
 
         try:
             )
 
         try:
-            with open(self._graph_layout_file, "r") as file_read:
-                self._graph_layout = load(file_read, Loader=FullLoader)
+            with open(self._tooltip_file, "r") as file_read:
+                self._tooltips = load(file_read, Loader=FullLoader)
         except IOError as err:
         except IOError as err:
-            raise RuntimeError(
-                f"Not possible to open the file {self._graph_layout_file}\n"
-                f"{err}"
+            logging.warning(
+                f"Not possible to open the file {self._tooltip_file}\n{err}"
             )
         except YAMLError as err:
             )
         except YAMLError as err:
-            raise RuntimeError(
+            logging.warning(
                 f"An error occurred while parsing the specification file "
                 f"An error occurred while parsing the specification file "
-                f"{self._graph_layout_file}\n"
-                f"{err}"
+                f"{self._tooltip_file}\n{err}"
             )
 
         # Callbacks:
             )
 
         # Callbacks:
@@ -126,479 +218,968 @@ class Layout:
     def layout(self):
         return self._graph_layout
 
     def layout(self):
         return self._graph_layout
 
+    @property
+    def time_period(self):
+        return self._time_period
+
     def add_content(self):
     def add_content(self):
+        """Top level method which generated the web page.
+
+        It generates:
+        - Store for user input data,
+        - Navigation bar,
+        - Main area with control panel and ploting area.
+
+        If no HTML layout is provided, an error message is displayed instead.
+
+        :returns: The HTML div with the whole page.
+        :rtype: html.Div
         """
         """
-        """
+
         if self.html_layout and self.spec_tbs:
             return html.Div(
                 id="div-main",
                 children=[
         if self.html_layout and self.spec_tbs:
             return html.Div(
                 id="div-main",
                 children=[
-                    dcc.Store(id="selected-tests"),
-                    self._add_ctrl_div(),
-                    self._add_plotting_div()
+                    dbc.Row(
+                        id="row-navbar",
+                        class_name="g-0",
+                        children=[
+                            self._add_navbar(),
+                        ]
+                    ),
+                    dcc.Loading(
+                        dbc.Offcanvas(
+                            class_name="w-50",
+                            id="offcanvas-metadata",
+                            title="Throughput And Latency",
+                            placement="end",
+                            is_open=False,
+                            children=[
+                                dbc.Row(id="metadata-tput-lat"),
+                                dbc.Row(id="metadata-hdrh-graph"),
+                            ]
+                        )
+                    ),
+                    dbc.Row(
+                        id="row-main",
+                        class_name="g-0",
+                        children=[
+                            dcc.Store(id="selected-tests"),
+                            dcc.Store(id="control-panel"),
+                            dcc.Location(id="url", refresh=False),
+                            self._add_ctrl_col(),
+                            self._add_plotting_col(),
+                        ]
+                    )
                 ]
             )
         else:
             return html.Div(
                 ]
             )
         else:
             return html.Div(
-            id="div-main-error",
-            children="An Error Occured."
-        )
+                id="div-main-error",
+                children=[
+                    dbc.Alert(
+                        [
+                            "An Error Occured",
+                        ],
+                        color="danger",
+                    ),
+                ]
+            )
 
 
-    def _add_ctrl_div(self):
-        """Add div with controls. It is placed on the left side.
+    def _add_navbar(self):
+        """Add nav element with navigation panel. It is placed on the top.
+
+        :returns: Navigation bar.
+        :rtype: dbc.NavbarSimple
         """
         """
-        return html.Div(
-            id="div-controls",
+        return dbc.NavbarSimple(
+            id="navbarsimple-main",
             children=[
             children=[
-                html.Div(
-                    id="div-controls-tabs",
-                    children=[
-                        self._add_ctrl_select(),
-                        self._add_ctrl_shown()
-                    ]
+                dbc.NavItem(
+                    dbc.NavLink(
+                        "Continuous Performance Trending",
+                        disabled=True,
+                        external_link=True,
+                        href="#"
+                    )
                 )
             ],
                 )
             ],
-            style={
-                "display": "inline-block",
-                "width": "18%",
-                "padding": "5px"
-            }
+            brand="Dashboard",
+            brand_href="/",
+            brand_external_link=True,
+            class_name="p-2",
+            fluid=True,
+        )
+
+    def _add_ctrl_col(self) -> dbc.Col:
+        """Add column with controls. It is placed on the left side.
+
+        :returns: Column with the control panel.
+        :rtype: dbc.Col
+        """
+        return dbc.Col(
+            id="col-controls",
+            children=[
+                self._add_ctrl_panel(),
+            ],
         )
 
         )
 
-    def _add_plotting_div(self):
-        """Add div with plots and tables. It is placed on the right side.
+    def _add_plotting_col(self) -> dbc.Col:
+        """Add column with plots and tables. It is placed on the right side.
+
+        :returns: Column with tables.
+        :rtype: dbc.Col
         """
         """
-        return html.Div(
-            id="div-plotting-area",
+        return dbc.Col(
+            id="col-plotting-area",
             children=[
                 dcc.Loading(
             children=[
                 dcc.Loading(
-                    id="loading-graph",
                     children=[
                     children=[
-                        dcc.Graph(
-                            id="graph-tput",
-                            style=self.STYLE_HIDEN
+                        dbc.Row(  # Throughput
+                            id="row-graph-tput",
+                            class_name="g-0 p-2",
+                            children=[
+                                C.PLACEHOLDER
+                            ]
                         ),
                         ),
-                        dcc.Graph(
-                            id="graph-latency",
-                            style=self.STYLE_HIDEN
-                        )
-                    ],
-                    type="circle"
-                ),
-                html.Div(
-                    children=[
-                        html.Div(
-                            id="div-tput-metadata",
+                        dbc.Row(  # Latency
+                            id="row-graph-lat",
+                            class_name="g-0 p-2",
                             children=[
                             children=[
-                                dcc.Markdown("""
-                                **Metadata**
-
-                                Click on data points in the graph.
-                                """),
-                                html.Pre(
-                                    id="tput-metadata"
-                                )
-                            ],
-                            style=self.STYLE_HIDEN
+                                C.PLACEHOLDER
+                            ]
                         ),
                         ),
-                        html.Div(
-                            id="div-latency-metadata",
+                        dbc.Row(  # Download
+                            id="row-btn-download",
+                            class_name="g-0 p-2",
                             children=[
                             children=[
-                                dcc.Markdown("""
-                                **Metadata**
-
-                                Click on data points in the graph.
-                                """),
-                                html.Pre(
-                                    id="latency-metadata"
-                                )
-                            ],
-                            style=self.STYLE_HIDEN
+                                C.PLACEHOLDER
+                            ]
                         )
                     ]
                 )
             ],
                         )
                     ]
                 )
             ],
-            style={
-                "vertical-align": "top",
-                "display": "inline-block",
-                "width": "80%",
-                "padding": "5px"
-            }
+            width=9,
         )
 
         )
 
-    def _add_ctrl_shown(self):
-        """
+    def _add_ctrl_panel(self) -> dbc.Row:
+        """Add control panel.
+
+        :returns: Control panel.
+        :rtype: dbc.Row
         """
         """
-        return html.Div(
-            id="div-ctrl-shown",
+        return dbc.Row(
+            id="row-ctrl-panel",
+            class_name="g-0 p-2",
             children=[
             children=[
-                html.H5("Selected tests"),
-                html.Div(
-                    id="container-selected-tests",
+                dbc.Row(
+                    class_name="g-0",
                     children=[
                     children=[
-                        dcc.Checklist(
-                            id="cl-selected",
-                            options=[],
-                            labelStyle={"display": "block"}
+                        dbc.InputGroup(
+                            [
+                                dbc.InputGroupText(
+                                    children=show_tooltip(self._tooltips,
+                                        "help-dut", "DUT")
+                                ),
+                                dbc.Select(
+                                    id="dd-ctrl-dut",
+                                    placeholder=(
+                                        "Select a Device under Test..."
+                                    ),
+                                    options=sorted(
+                                        [
+                                            {"label": k, "value": k} \
+                                                for k in self.spec_tbs.keys()
+                                        ],
+                                        key=lambda d: d["label"]
+                                    )
+                                )
+                            ],
+                            class_name="mb-3",
+                            size="sm",
                         ),
                         ),
-                        html.Button(
-                            id="btn-sel-remove",
-                            children="Remove Selected",
-                            disabled=False
+                    ]
+                ),
+                dbc.Row(
+                    class_name="g-0",
+                    children=[
+                        dbc.InputGroup(
+                            [
+                                dbc.InputGroupText(
+                                    children=show_tooltip(self._tooltips,
+                                        "help-infra", "Infra")
+                                ),
+                                dbc.Select(
+                                    id="dd-ctrl-phy",
+                                    placeholder=(
+                                        "Select a Physical Test Bed "
+                                        "Topology..."
+                                    )
+                                )
+                            ],
+                            class_name="mb-3",
+                            size="sm",
                         ),
                         ),
-                        html.Button(
-                            id="btn-sel-display",
-                            children="Display",
-                            disabled=False
-                        )
                     ]
                 ),
                     ]
                 ),
-            ]
-        )
-
-    def _add_ctrl_select(self):
-        """
-        """
-        return html.Div(
-            id="div-ctrl-select",
-            children=[
-                html.H5("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_tbs.keys()
-                    ],
+                dbc.Row(
+                    class_name="g-0",
+                    children=[
+                        dbc.InputGroup(
+                            [
+                                dbc.InputGroupText(
+                                    children=show_tooltip(self._tooltips,
+                                        "help-area", "Area")
+                                ),
+                                dbc.Select(
+                                    id="dd-ctrl-area",
+                                    placeholder="Select an Area...",
+                                    disabled=True,
+                                ),
+                            ],
+                            class_name="mb-3",
+                            size="sm",
+                        ),
+                    ]
                 ),
                 ),
-                html.H5("Area"),
-                dcc.Dropdown(
-                    id="dd-ctrl-area",
-                    placeholder="Select an Area...",
-                    disabled=True,
-                    multi=False,
-                    clearable=False,
+                dbc.Row(
+                    class_name="g-0",
+                    children=[
+                        dbc.InputGroup(
+                            [
+                                dbc.InputGroupText(
+                                    children=show_tooltip(self._tooltips,
+                                        "help-test", "Test")
+                                ),
+                                dbc.Select(
+                                    id="dd-ctrl-test",
+                                    placeholder="Select a Test...",
+                                    disabled=True,
+                                ),
+                            ],
+                            class_name="mb-3",
+                            size="sm",
+                        ),
+                    ]
                 ),
                 ),
-                html.H5("Test"),
-                dcc.Dropdown(
-                    id="dd-ctrl-test",
-                    placeholder="Select a Test...",
-                    disabled=True,
-                    multi=False,
-                    clearable=False,
+                dbc.Row(
+                    id="row-ctrl-framesize",
+                    class_name="gy-1",
+                    children=[
+                        dbc.Label(
+                            children=show_tooltip(self._tooltips,
+                                "help-framesize", "Frame Size"),
+                            class_name="p-0"
+                        ),
+                        dbc.Col(
+                            children=[
+                                dbc.Checklist(
+                                    id="cl-ctrl-framesize-all",
+                                    options=C.CL_ALL_DISABLED,
+                                    inline=True,
+                                    switch=False
+                                ),
+                            ],
+                            width=3
+                        ),
+                        dbc.Col(
+                            children=[
+                                dbc.Checklist(
+                                    id="cl-ctrl-framesize",
+                                    inline=True,
+                                    switch=False
+                                )
+                            ]
+                        )
+                    ]
                 ),
                 ),
-                html.Div(
-                    id="div-ctrl-core",
+                dbc.Row(
+                    id="row-ctrl-core",
+                    class_name="gy-1",
                     children=[
                     children=[
-                        html.H5("Number of Cores"),
-                        dcc.Checklist(
-                            id="cl-ctrl-core-all",
-                            options=[{"label": "All", "value": "all"}, ],
-                            labelStyle={"display": "inline-block"}
+                        dbc.Label(
+                            children=show_tooltip(self._tooltips,
+                                "help-cores", "Number of Cores"),
+                            class_name="p-0"
                         ),
                         ),
-                        dcc.Checklist(
-                            id="cl-ctrl-core",
-                            labelStyle={"display": "inline-block"}
+                        dbc.Col(
+                            children=[
+                                dbc.Checklist(
+                                    id="cl-ctrl-core-all",
+                                    options=C.CL_ALL_DISABLED,
+                                    inline=False,
+                                    switch=False
+                                )
+                            ],
+                            width=3
+                        ),
+                        dbc.Col(
+                            children=[
+                                dbc.Checklist(
+                                    id="cl-ctrl-core",
+                                    inline=True,
+                                    switch=False
+                                )
+                            ]
                         )
                         )
-                    ],
-                    style={"display": "none"}
+                    ]
                 ),
                 ),
-                html.Div(
-                    id="div-ctrl-framesize",
+                dbc.Row(
+                    id="row-ctrl-testtype",
+                    class_name="gy-1",
                     children=[
                     children=[
-                        html.H5("Frame Size"),
-                        dcc.Checklist(
-                            id="cl-ctrl-framesize-all",
-                            options=[{"label": "All", "value": "all"}, ],
-                            labelStyle={"display": "inline-block"}
+                        dbc.Label(
+                            children=show_tooltip(self._tooltips,
+                                "help-ttype", "Test Type"),
+                            class_name="p-0"
                         ),
                         ),
-                        dcc.Checklist(
-                            id="cl-ctrl-framesize",
-                            labelStyle={"display": "inline-block"}
+                        dbc.Col(
+                            children=[
+                                dbc.Checklist(
+                                    id="cl-ctrl-testtype-all",
+                                    options=C.CL_ALL_DISABLED,
+                                    inline=True,
+                                    switch=False
+                                ),
+                            ],
+                            width=3
+                        ),
+                        dbc.Col(
+                            children=[
+                                dbc.Checklist(
+                                    id="cl-ctrl-testtype",
+                                    inline=True,
+                                    switch=False
+                                )
+                            ]
                         )
                         )
-                    ],
-                    style={"display": "none"}
+                    ]
                 ),
                 ),
-                html.Div(
-                    id="div-ctrl-testtype",
+                dbc.Row(
+                    id="row-ctrl-normalize",
+                    class_name="gy-1",
                     children=[
                     children=[
-                        html.H5("Test Type"),
-                        dcc.Checklist(
-                            id="cl-ctrl-testtype-all",
-                            options=[{"label": "All", "value": "all"}, ],
-                            labelStyle={"display": "inline-block"}
+                        dbc.Label(
+                            children=show_tooltip(self._tooltips,
+                                "help-normalize", "Normalize"),
+                            class_name="p-0"
                         ),
                         ),
-                        dcc.Checklist(
-                            id="cl-ctrl-testtype",
-                            labelStyle={"display": "inline-block"}
+                        dbc.Col(
+                            children=[
+                                dbc.Checklist(
+                                    id="cl-ctrl-normalize",
+                                    options=[{
+                                        "value": "normalize",
+                                        "label": (
+                                            "Normalize results to CPU"
+                                            "frequency 2GHz"
+                                        )
+                                    }],
+                                    value=[],
+                                    inline=True,
+                                    switch=False
+                                ),
+                            ]
+                        )
+                    ]
+                ),
+                dbc.Row(
+                    class_name="gy-1 p-0",
+                    children=[
+                        dbc.ButtonGroup(
+                            [
+                                dbc.Button(
+                                    id="btn-ctrl-add",
+                                    children="Add Selected",
+                                    class_name="me-1",
+                                    color="info"
+                                )
+                            ],
+                            size="md",
+                        )
+                    ]
+                ),
+                dbc.Row(
+                    class_name="gy-1",
+                    children=[
+                        dbc.Label(
+                            class_name="gy-1",
+                            children=show_tooltip(self._tooltips,
+                                "help-time-period", "Time Period"),
+                        ),
+                        dcc.DatePickerRange(
+                            id="dpr-period",
+                            className="d-flex justify-content-center",
+                            min_date_allowed=\
+                                datetime.utcnow() - timedelta(
+                                    days=self.time_period),
+                            max_date_allowed=datetime.utcnow(),
+                            initial_visible_month=datetime.utcnow(),
+                            start_date=\
+                                datetime.utcnow() - timedelta(
+                                    days=self.time_period),
+                            end_date=datetime.utcnow(),
+                            display_format="D MMM YY"
+                        )
+                    ]
+                ),
+                dbc.Row(
+                    id="row-card-sel-tests",
+                    class_name="gy-1",
+                    style=C.STYLE_DISABLED,
+                    children=[
+                        dbc.Label(
+                            "Selected tests",
+                            class_name="p-0"
+                        ),
+                        dbc.Checklist(
+                            class_name="overflow-auto",
+                            id="cl-selected",
+                            options=[],
+                            inline=False,
+                            style={"max-height": "12em"},
                         )
                     ],
                         )
                     ],
-                    style={"display": "none"}
                 ),
                 ),
-                html.Button(
-                    id="btn-ctrl-add",
-                    children="Add",
-                    disabled=True
+                dbc.Row(
+                    id="row-btns-sel-tests",
+                    style=C.STYLE_DISABLED,
+                    children=[
+                        dbc.ButtonGroup(
+                            class_name="gy-2",
+                            children=[
+                                dbc.Button(
+                                    id="btn-sel-remove",
+                                    children="Remove Selected",
+                                    class_name="w-100 me-1",
+                                    color="info",
+                                    disabled=False
+                                ),
+                                dbc.Button(
+                                    id="btn-sel-remove-all",
+                                    children="Remove All",
+                                    class_name="w-100 me-1",
+                                    color="info",
+                                    disabled=False
+                                ),
+                            ],
+                            size="md",
+                        )
+                    ]
                 ),
                 ),
-                html.Br(),
-                dcc.DatePickerRange(
-                    id="dpr-period",
-                    min_date_allowed=datetime.utcnow() - timedelta(days=180),
-                    max_date_allowed=datetime.utcnow(),
-                    initial_visible_month=datetime.utcnow(),
-                    start_date=datetime.utcnow() - timedelta(days=180),
-                    end_date=datetime.utcnow(),
-                    display_format="D MMMM YY"
-                )
             ]
         )
 
             ]
         )
 
-    def callbacks(self, app):
+    class ControlPanel:
+        """A class representing the control panel.
+        """
 
 
-        @app.callback(
-            Output("dd-ctrl-area", "options"),
-            Output("dd-ctrl-area", "disabled"),
-            Input("dd-ctrl-phy", "value"),
-        )
-        def _update_dd_area(phy):
-            """
+        def __init__(self, panel: dict) -> None:
+            """Initialisation of the control pannel by default values. If
+            particular values are provided (parameter "panel") they are set
+            afterwards.
+
+            :param panel: Custom values to be set to the control panel.
+            :param default: Default values to be set to the control panel.
+            :type panel: dict
+            :type defaults: dict
             """
 
             """
 
-            if phy is None:
-                raise PreventUpdate
+            # Defines also the order of keys
+            self._defaults = {
+                "dd-ctrl-dut-value": str(),
+                "dd-ctrl-phy-options": list(),
+                "dd-ctrl-phy-disabled": True,
+                "dd-ctrl-phy-value": str(),
+                "dd-ctrl-area-options": list(),
+                "dd-ctrl-area-disabled": True,
+                "dd-ctrl-area-value": str(),
+                "dd-ctrl-test-options": list(),
+                "dd-ctrl-test-disabled": True,
+                "dd-ctrl-test-value": str(),
+                "cl-ctrl-core-options": list(),
+                "cl-ctrl-core-value": list(),
+                "cl-ctrl-core-all-value": list(),
+                "cl-ctrl-core-all-options": C.CL_ALL_DISABLED,
+                "cl-ctrl-framesize-options": list(),
+                "cl-ctrl-framesize-value": list(),
+                "cl-ctrl-framesize-all-value": list(),
+                "cl-ctrl-framesize-all-options": C.CL_ALL_DISABLED,
+                "cl-ctrl-testtype-options": list(),
+                "cl-ctrl-testtype-value": list(),
+                "cl-ctrl-testtype-all-value": list(),
+                "cl-ctrl-testtype-all-options": C.CL_ALL_DISABLED,
+                "btn-ctrl-add-disabled": True,
+                "cl-normalize-value": list(),
+                "cl-selected-options": list(),
+            }
 
 
-            try:
-                options = [
-                    {"label": self.spec_tbs[phy][v]["label"], "value": v}
-                        for v in [v for v in self.spec_tbs[phy].keys()]
-                ]
-                disable = False
-            except KeyError:
-                options = list()
-                disable = True
+            self._panel = deepcopy(self._defaults)
+            if panel:
+                for key in self._defaults:
+                    self._panel[key] = panel[key]
 
 
-            return options, disable
+        @property
+        def defaults(self) -> dict:
+            return self._defaults
 
 
-        @app.callback(
-            Output("dd-ctrl-test", "options"),
-            Output("dd-ctrl-test", "disabled"),
-            State("dd-ctrl-phy", "value"),
-            Input("dd-ctrl-area", "value"),
-        )
-        def _update_dd_test(phy, area):
-            """
+        @property
+        def panel(self) -> dict:
+            return self._panel
+
+        def set(self, kwargs: dict) -> None:
+            """Set the values of the Control panel.
+
+            :param kwargs: key - value pairs to be set.
+            :type kwargs: dict
+            :raises KeyError: If the key in kwargs is not present in the Control
+                panel.
             """
             """
+            for key, val in kwargs.items():
+                if key in self._panel:
+                    self._panel[key] = val
+                else:
+                    raise KeyError(f"The key {key} is not defined.")
 
 
-            if not area:
-                raise PreventUpdate
+        def get(self, key: str) -> any:
+            """Returns the value of a key from the Control panel.
 
 
-            try:
-                options = [
-                    {"label": v, "value": v}
-                        for v in self.spec_tbs[phy][area]["test"]
-                ]
-                disable = False
-            except KeyError:
-                options = list()
-                disable = True
+            :param key: The key which value should be returned.
+            :type key: str
+            :returns: The value of the key.
+            :rtype: any
+            :raises KeyError: If the key in kwargs is not present in the Control
+                panel.
+            """
+            return self._panel[key]
 
 
-            return options, disable
+        def values(self) -> tuple:
+            """Returns the values from the Control panel as a list.
 
 
-        @app.callback(
-            Output("div-ctrl-core", "style"),
-            Output("cl-ctrl-core", "options"),
-            Output("div-ctrl-framesize", "style"),
-            Output("cl-ctrl-framesize", "options"),
-            Output("div-ctrl-testtype", "style"),
-            Output("cl-ctrl-testtype", "options"),
-            Output("btn-ctrl-add", "disabled"),
-            State("dd-ctrl-phy", "value"),
-            State("dd-ctrl-area", "value"),
-            Input("dd-ctrl-test", "value"),
-        )
-        def _update_btn_add(phy, area, test):
+            :returns: The values from the Control panel.
+            :rtype: list
             """
             """
+            return tuple(self._panel.values())
+
+    def callbacks(self, app):
+        """Callbacks for the whole application.
+
+        :param app: The application.
+        :type app: Flask
+        """
+
+        def _generate_plotting_area(figs: tuple, url: str) -> tuple:
+            """Generate the plotting area with all its content.
+
+            :param figs: Figures to be placed in the plotting area.
+            :param utl: The URL to be placed in the plotting area bellow the
+                tables.
+            :type figs: tuple of plotly.graph_objects.Figure
+            :type url: str
+            :returns: tuple of elements to be shown in the plotting area.
+            :rtype: tuple(dcc.Graph, dcc.Graph, list(dbc.Col, dbc.Col))
             """
 
             """
 
-            if test is None:
-                raise PreventUpdate
+            (fig_tput, fig_lat) = figs
 
 
-            core_style = {"display": "none"}
-            core_opts = []
-            framesize_style = {"display": "none"}
-            framesize_opts = []
-            testtype_style = {"display": "none"}
-            testtype_opts = []
-            add_disabled = True
-            if phy and area and test:
-                core_style = {"display": "block"}
-                core_opts = [
-                    {"label": v, "value": v}
-                        for v in self.spec_tbs[phy][area]["core"]
+            row_fig_tput = C.PLACEHOLDER
+            row_fig_lat = C.PLACEHOLDER
+            row_btn_dwnld = C.PLACEHOLDER
+
+            if fig_tput:
+                row_fig_tput = [
+                    dcc.Graph(
+                        id={"type": "graph", "index": "tput"},
+                        figure=fig_tput
+                    )
                 ]
                 ]
-                framesize_style = {"display": "block"}
-                framesize_opts = [
-                    {"label": v, "value": v}
-                        for v in self.spec_tbs[phy][area]["frame-size"]
+                row_btn_dwnld = [
+                    dbc.Col(  # Download
+                        width=2,
+                        children=[
+                            dcc.Loading(children=[
+                                dbc.Button(
+                                    id="btn-download-data",
+                                    children=show_tooltip(self._tooltips,
+                                        "help-download", "Download Data"),
+                                    class_name="me-1",
+                                    color="info"
+                                ),
+                                dcc.Download(id="download-data")
+                            ]),
+                        ]
+                    ),
+                    dbc.Col(  # Show URL
+                        width=10,
+                        children=[
+                            dbc.InputGroup(
+                                class_name="me-1",
+                                children=[
+                                    dbc.InputGroupText(
+                                        style=C.URL_STYLE,
+                                        children=show_tooltip(self._tooltips,
+                                            "help-url", "URL", "input-url")
+                                    ),
+                                    dbc.Input(
+                                        id="input-url",
+                                        readonly=True,
+                                        type="url",
+                                        style=C.URL_STYLE,
+                                        value=url
+                                    )
+                                ]
+                            )
+                        ]
+                    )
                 ]
                 ]
-                testtype_style = {"display": "block"}
-                testtype_opts = [
-                    {"label": v, "value": v}
-                        for v in self.spec_tbs[phy][area]["test-type"]
+            if fig_lat:
+                row_fig_lat = [
+                    dcc.Graph(
+                        id={"type": "graph", "index": "lat"},
+                        figure=fig_lat
+                    )
                 ]
                 ]
-                add_disabled = False
 
 
-            return (
-                core_style, core_opts,
-                framesize_style, framesize_opts,
-                testtype_style, testtype_opts,
-                add_disabled
-            )
-
-        def _sync_checklists(opt, sel, all, id):
-            """
-            """
-            options = {v["value"] for v in opt}
-            input_id = callback_context.triggered[0]["prop_id"].split(".")[0]
-            if input_id == id:
-                all = ["all"] if set(sel) == options else list()
-            else:
-                sel = list(options) if all else list()
-            return sel, all
+            return row_fig_tput, row_fig_lat, row_btn_dwnld
 
         @app.callback(
 
         @app.callback(
+            Output("control-panel", "data"),  # Store
+            Output("selected-tests", "data"),  # Store
+            Output("row-graph-tput", "children"),
+            Output("row-graph-lat", "children"),
+            Output("row-btn-download", "children"),
+            Output("row-card-sel-tests", "style"),
+            Output("row-btns-sel-tests", "style"),
+            Output("dd-ctrl-dut", "value"),
+            Output("dd-ctrl-phy", "options"),
+            Output("dd-ctrl-phy", "disabled"),
+            Output("dd-ctrl-phy", "value"),
+            Output("dd-ctrl-area", "options"),
+            Output("dd-ctrl-area", "disabled"),
+            Output("dd-ctrl-area", "value"),
+            Output("dd-ctrl-test", "options"),
+            Output("dd-ctrl-test", "disabled"),
+            Output("dd-ctrl-test", "value"),
+            Output("cl-ctrl-core", "options"),
             Output("cl-ctrl-core", "value"),
             Output("cl-ctrl-core-all", "value"),
             Output("cl-ctrl-core", "value"),
             Output("cl-ctrl-core-all", "value"),
-            State("cl-ctrl-core", "options"),
-            Input("cl-ctrl-core", "value"),
-            Input("cl-ctrl-core-all", "value"),
-            prevent_initial_call=True
-        )
-        def _sync_cl_core(opt, sel, all):
-            return _sync_checklists(opt, sel, all, "cl-ctrl-core")
-
-        @app.callback(
+            Output("cl-ctrl-core-all", "options"),
+            Output("cl-ctrl-framesize", "options"),
             Output("cl-ctrl-framesize", "value"),
             Output("cl-ctrl-framesize-all", "value"),
             Output("cl-ctrl-framesize", "value"),
             Output("cl-ctrl-framesize-all", "value"),
-            State("cl-ctrl-framesize", "options"),
-            Input("cl-ctrl-framesize", "value"),
-            Input("cl-ctrl-framesize-all", "value"),
-            prevent_initial_call=True
-        )
-        def _sync_cl_framesize(opt, sel, all):
-            return _sync_checklists(opt, sel, all, "cl-ctrl-framesize")
-
-        @app.callback(
+            Output("cl-ctrl-framesize-all", "options"),
+            Output("cl-ctrl-testtype", "options"),
             Output("cl-ctrl-testtype", "value"),
             Output("cl-ctrl-testtype-all", "value"),
             Output("cl-ctrl-testtype", "value"),
             Output("cl-ctrl-testtype-all", "value"),
-            State("cl-ctrl-testtype", "options"),
-            Input("cl-ctrl-testtype", "value"),
-            Input("cl-ctrl-testtype-all", "value"),
-            prevent_initial_call=True
-        )
-        def _sync_cl_testtype(opt, sel, all):
-            return _sync_checklists(opt, sel, all, "cl-ctrl-testtype")
-
-        @app.callback(
-            Output("graph-tput", "figure"),
-            Output("graph-tput", "style"),
-            Output("div-tput-metadata", "style"),
-            Output("graph-latency", "figure"),
-            Output("graph-latency", "style"),
-            Output("div-latency-metadata", "style"),
-            Output("selected-tests", "data"),  # Store
+            Output("cl-ctrl-testtype-all", "options"),
+            Output("btn-ctrl-add", "disabled"),
+            Output("cl-ctrl-normalize", "value"),
             Output("cl-selected", "options"),  # User selection
             Output("cl-selected", "options"),  # User selection
-            Output("dd-ctrl-phy", "value"),
-            Output("dd-ctrl-area", "value"),
-            Output("dd-ctrl-test", "value"),
+            State("control-panel", "data"),  # Store
             State("selected-tests", "data"),  # Store
             State("selected-tests", "data"),  # Store
-            State("cl-selected", "value"),
-            State("dd-ctrl-phy", "value"),
-            State("dd-ctrl-area", "value"),
-            State("dd-ctrl-test", "value"),
-            State("cl-ctrl-core", "value"),
-            State("cl-ctrl-framesize", "value"),
-            State("cl-ctrl-testtype", "value"),
+            State("cl-selected", "value"),  # User selection
+            Input("dd-ctrl-dut", "value"),
+            Input("dd-ctrl-phy", "value"),
+            Input("dd-ctrl-area", "value"),
+            Input("dd-ctrl-test", "value"),
+            Input("cl-ctrl-core", "value"),
+            Input("cl-ctrl-core-all", "value"),
+            Input("cl-ctrl-framesize", "value"),
+            Input("cl-ctrl-framesize-all", "value"),
+            Input("cl-ctrl-testtype", "value"),
+            Input("cl-ctrl-testtype-all", "value"),
+            Input("cl-ctrl-normalize", "value"),
             Input("btn-ctrl-add", "n_clicks"),
             Input("btn-ctrl-add", "n_clicks"),
-            Input("btn-sel-display", "n_clicks"),
-            Input("btn-sel-remove", "n_clicks"),
             Input("dpr-period", "start_date"),
             Input("dpr-period", "end_date"),
             Input("dpr-period", "start_date"),
             Input("dpr-period", "end_date"),
-            prevent_initial_call=True
+            Input("btn-sel-remove", "n_clicks"),
+            Input("btn-sel-remove-all", "n_clicks"),
+            Input("url", "href")
         )
         )
-        def _process_list(store_sel, list_sel, phy, area, test, cores,
-                framesizes, testtypes, btn_add, btn_display, btn_remove,
-                d_start, d_end):
-            """
+        def _update_ctrl_panel(cp_data: dict, store_sel: list, list_sel: list,
+            dd_dut: str, dd_phy: str, dd_area: str, dd_test: str, cl_core: list,
+            cl_core_all: list, cl_framesize: list, cl_framesize_all: list,
+            cl_testtype: list, cl_testtype_all: list, cl_normalize: list,
+            btn_add: int, d_start: str, d_end: str, btn_remove: int,
+            btn_remove_all: int, href: str) -> tuple:
+            """Update the application when the event is detected.
+
+            :param cp_data: Current status of the control panel stored in
+                browser.
+            :param store_sel: List of tests selected by user stored in the
+                browser.
+            :param list_sel: List of tests selected by the user shown in the
+                checklist.
+            :param dd_dut: Input - DUTs.
+            :param dd_phy: Input - topo- arch-nic-driver.
+            :param dd_area: Input - Tested area.
+            :param dd_test: Input - Test.
+            :param cl_core: Input - Number of cores.
+            :param cl_core_all: Input - All numbers of cores.
+            :param cl_framesize: Input - Frame sizes.
+            :param cl_framesize_all: Input - All frame sizes.
+            :param cl_testtype: Input - Test type (NDR, PDR, MRR).
+            :param cl_testtype_all: Input - All test types.
+            :param cl_normalize: Input - Normalize the results.
+            :param btn_add: Input - Button "Add Selected" tests.
+            :param d_start: Date and time where the data processing starts.
+            :param d_end: Date and time where the data processing ends.
+            :param btn_remove: Input - Button "Remove selected" tests.
+            :param btn_remove_all: Input - Button "Remove All" tests.
+            :param href: Input - The URL provided by the browser.
+            :type cp_data: dict
+            :type store_sel: list
+            :type list_sel: list
+            :type dd_dut: str
+            :type dd_phy: str
+            :type dd_area: str
+            :type dd_test: str
+            :type cl_core: list
+            :type cl_core_all: list
+            :type cl_framesize: list
+            :type cl_framesize_all: list
+            :type cl_testtype: list
+            :type cl_testtype_all: list
+            :type cl_normalize: list
+            :type btn_add: int
+            :type d_start: str
+            :type d_end: str
+            :type btn_remove: int
+            :type btn_remove_all: int
+            :type href: str
+            :returns: New values for web page elements.
+            :rtype: tuple
             """
 
             """
 
-            if not (btn_add or btn_display or btn_remove or d_start or d_end):
-                raise PreventUpdate
-
-            def _list_tests():
-                # Display selected tests with checkboxes:
-                if store_sel:
-                    return [
-                        {"label": v["id"], "value": v["id"]} for v in store_sel
-                    ]
-                else:
-                    return list()
-
-            class RetunValue:
-                def __init__(self) -> None:
-                    self._output = {
-                        "graph-tput-figure": no_update,
-                        "graph-tput-style": no_update,
-                        "div-tput-metadata-style": no_update,
-                        "graph-lat-figure": no_update,
-                        "graph-lat-style": no_update,
-                        "div-lat-metadata-style": no_update,
-                        "selected-tests-data": no_update,
-                        "cl-selected-options": no_update,
-                        "dd-ctrl-phy-value": no_update,
-                        "dd-ctrl-area-value": no_update,
-                        "dd-ctrl-test-value": no_update,
-                    }
-
-                def value(self):
-                    return tuple(self._output.values())
-
-                def set_values(self, kwargs: dict) -> None:
-                    for key, val in kwargs.items():
-                        if key in self._output:
-                            self._output[key] = val
-                        else:
-                            raise KeyError(f"The key {key} is not defined.")
+            ctrl_panel = self.ControlPanel(cp_data)
+            norm = cl_normalize
 
 
+            d_start = get_date(d_start)
+            d_end = get_date(d_end)
 
 
-            trigger_id = callback_context.triggered[0]["prop_id"].split(".")[0]
+            # Parse the url:
+            parsed_url = url_decode(href)
+            if parsed_url:
+                url_params = parsed_url["params"]
+            else:
+                url_params = None
 
 
-            d_start = datetime(int(d_start[0:4]), int(d_start[5:7]),
-                int(d_start[8:10]))
-            d_end = datetime(int(d_end[0:4]), int(d_end[5:7]), int(d_end[8:10]))
+            row_fig_tput = no_update
+            row_fig_lat = no_update
+            row_btn_dwnld = no_update
+            row_card_sel_tests = no_update
+            row_btns_sel_tests = no_update
 
 
-            output = RetunValue()
+            trigger_id = callback_context.triggered[0]["prop_id"].split(".")[0]
 
 
-            if trigger_id == "btn-ctrl-add":
+            if trigger_id == "dd-ctrl-dut":
+                try:
+                    options = \
+                        generate_options(sorted(self.spec_tbs[dd_dut].keys()))
+                    disabled = False
+                except KeyError:
+                    options = list()
+                    disabled = True
+                ctrl_panel.set({
+                    "dd-ctrl-dut-value": dd_dut,
+                    "dd-ctrl-phy-value": str(),
+                    "dd-ctrl-phy-options": options,
+                    "dd-ctrl-phy-disabled": disabled,
+                    "dd-ctrl-area-value": str(),
+                    "dd-ctrl-area-options": list(),
+                    "dd-ctrl-area-disabled": True,
+                    "dd-ctrl-test-value": str(),
+                    "dd-ctrl-test-options": list(),
+                    "dd-ctrl-test-disabled": True,
+                    "cl-ctrl-core-options": list(),
+                    "cl-ctrl-core-value": list(),
+                    "cl-ctrl-core-all-value": list(),
+                    "cl-ctrl-core-all-options": C.CL_ALL_DISABLED,
+                    "cl-ctrl-framesize-options": list(),
+                    "cl-ctrl-framesize-value": list(),
+                    "cl-ctrl-framesize-all-value": list(),
+                    "cl-ctrl-framesize-all-options": C.CL_ALL_DISABLED,
+                    "cl-ctrl-testtype-options": list(),
+                    "cl-ctrl-testtype-value": list(),
+                    "cl-ctrl-testtype-all-value": list(),
+                    "cl-ctrl-testtype-all-options": C.CL_ALL_DISABLED,
+                })
+            elif trigger_id == "dd-ctrl-phy":
+                try:
+                    dut = ctrl_panel.get("dd-ctrl-dut-value")
+                    phy = self.spec_tbs[dut][dd_phy]
+                    options = [{"label": label(v), "value": v} \
+                        for v in sorted(phy.keys())]
+                    disabled = False
+                except KeyError:
+                    options = list()
+                    disabled = True
+                ctrl_panel.set({
+                    "dd-ctrl-phy-value": dd_phy,
+                    "dd-ctrl-area-value": str(),
+                    "dd-ctrl-area-options": options,
+                    "dd-ctrl-area-disabled": disabled,
+                    "dd-ctrl-test-value": str(),
+                    "dd-ctrl-test-options": list(),
+                    "dd-ctrl-test-disabled": True,
+                    "cl-ctrl-core-options": list(),
+                    "cl-ctrl-core-value": list(),
+                    "cl-ctrl-core-all-value": list(),
+                    "cl-ctrl-core-all-options": C.CL_ALL_DISABLED,
+                    "cl-ctrl-framesize-options": list(),
+                    "cl-ctrl-framesize-value": list(),
+                    "cl-ctrl-framesize-all-value": list(),
+                    "cl-ctrl-framesize-all-options": C.CL_ALL_DISABLED,
+                    "cl-ctrl-testtype-options": list(),
+                    "cl-ctrl-testtype-value": list(),
+                    "cl-ctrl-testtype-all-value": list(),
+                    "cl-ctrl-testtype-all-options": C.CL_ALL_DISABLED,
+                })
+            elif trigger_id == "dd-ctrl-area":
+                try:
+                    dut = ctrl_panel.get("dd-ctrl-dut-value")
+                    phy = ctrl_panel.get("dd-ctrl-phy-value")
+                    area = self.spec_tbs[dut][phy][dd_area]
+                    options = generate_options(sorted(area.keys()))
+                    disabled = False
+                except KeyError:
+                    options = list()
+                    disabled = True
+                ctrl_panel.set({
+                    "dd-ctrl-area-value": dd_area,
+                    "dd-ctrl-test-value": str(),
+                    "dd-ctrl-test-options": options,
+                    "dd-ctrl-test-disabled": disabled,
+                    "cl-ctrl-core-options": list(),
+                    "cl-ctrl-core-value": list(),
+                    "cl-ctrl-core-all-value": list(),
+                    "cl-ctrl-core-all-options": C.CL_ALL_DISABLED,
+                    "cl-ctrl-framesize-options": list(),
+                    "cl-ctrl-framesize-value": list(),
+                    "cl-ctrl-framesize-all-value": list(),
+                    "cl-ctrl-framesize-all-options": C.CL_ALL_DISABLED,
+                    "cl-ctrl-testtype-options": list(),
+                    "cl-ctrl-testtype-value": list(),
+                    "cl-ctrl-testtype-all-value": list(),
+                    "cl-ctrl-testtype-all-options": C.CL_ALL_DISABLED,
+                })
+            elif trigger_id == "dd-ctrl-test":
+                dut = ctrl_panel.get("dd-ctrl-dut-value")
+                phy = ctrl_panel.get("dd-ctrl-phy-value")
+                area = ctrl_panel.get("dd-ctrl-area-value")
+                if all((dut, phy, area, dd_test, )):
+                    test = self.spec_tbs[dut][phy][area][dd_test]
+                    ctrl_panel.set({
+                        "dd-ctrl-test-value": dd_test,
+                        "cl-ctrl-core-options": \
+                            generate_options(sorted(test["core"])),
+                        "cl-ctrl-core-value": list(),
+                        "cl-ctrl-core-all-value": list(),
+                        "cl-ctrl-core-all-options": C.CL_ALL_ENABLED,
+                        "cl-ctrl-framesize-options": \
+                            generate_options(sorted(test["frame-size"])),
+                        "cl-ctrl-framesize-value": list(),
+                        "cl-ctrl-framesize-all-value": list(),
+                        "cl-ctrl-framesize-all-options": C.CL_ALL_ENABLED,
+                        "cl-ctrl-testtype-options": \
+                            generate_options(sorted(test["test-type"])),
+                        "cl-ctrl-testtype-value": list(),
+                        "cl-ctrl-testtype-all-value": list(),
+                        "cl-ctrl-testtype-all-options": C.CL_ALL_ENABLED,
+                    })
+            elif trigger_id == "cl-ctrl-core":
+                val_sel, val_all = sync_checklists(
+                    options=ctrl_panel.get("cl-ctrl-core-options"),
+                    sel=cl_core,
+                    all=list(),
+                    id=""
+                )
+                ctrl_panel.set({
+                    "cl-ctrl-core-value": val_sel,
+                    "cl-ctrl-core-all-value": val_all,
+                })
+            elif trigger_id == "cl-ctrl-core-all":
+                val_sel, val_all = sync_checklists(
+                    options = ctrl_panel.get("cl-ctrl-core-options"),
+                    sel=list(),
+                    all=cl_core_all,
+                    id="all"
+                )
+                ctrl_panel.set({
+                    "cl-ctrl-core-value": val_sel,
+                    "cl-ctrl-core-all-value": val_all,
+                })
+            elif trigger_id == "cl-ctrl-framesize":
+                val_sel, val_all = sync_checklists(
+                    options = ctrl_panel.get("cl-ctrl-framesize-options"),
+                    sel=cl_framesize,
+                    all=list(),
+                    id=""
+                )
+                ctrl_panel.set({
+                    "cl-ctrl-framesize-value": val_sel,
+                    "cl-ctrl-framesize-all-value": val_all,
+                })
+            elif trigger_id == "cl-ctrl-framesize-all":
+                val_sel, val_all = sync_checklists(
+                    options = ctrl_panel.get("cl-ctrl-framesize-options"),
+                    sel=list(),
+                    all=cl_framesize_all,
+                    id="all"
+                )
+                ctrl_panel.set({
+                    "cl-ctrl-framesize-value": val_sel,
+                    "cl-ctrl-framesize-all-value": val_all,
+                })
+            elif trigger_id == "cl-ctrl-testtype":
+                val_sel, val_all = sync_checklists(
+                    options = ctrl_panel.get("cl-ctrl-testtype-options"),
+                    sel=cl_testtype,
+                    all=list(),
+                    id=""
+                )
+                ctrl_panel.set({
+                    "cl-ctrl-testtype-value": val_sel,
+                    "cl-ctrl-testtype-all-value": val_all,
+                })
+            elif trigger_id == "cl-ctrl-testtype-all":
+                val_sel, val_all = sync_checklists(
+                    options = ctrl_panel.get("cl-ctrl-testtype-options"),
+                    sel=list(),
+                    all=cl_testtype_all,
+                    id="all"
+                )
+                ctrl_panel.set({
+                    "cl-ctrl-testtype-value": val_sel,
+                    "cl-ctrl-testtype-all-value": val_all,
+                })
+            elif trigger_id == "btn-ctrl-add":
+                _ = btn_add
+                dut = ctrl_panel.get("dd-ctrl-dut-value")
+                phy = ctrl_panel.get("dd-ctrl-phy-value")
+                area = ctrl_panel.get("dd-ctrl-area-value")
+                test = ctrl_panel.get("dd-ctrl-test-value")
+                cores = ctrl_panel.get("cl-ctrl-core-value")
+                framesizes = ctrl_panel.get("cl-ctrl-framesize-value")
+                testtypes = ctrl_panel.get("cl-ctrl-testtype-value")
                 # Add selected test to the list of tests in store:
                 # Add selected test to the list of tests in store:
-                if phy and area and test and cores and framesizes and testtypes:
+                if all((dut, phy, area, test, cores, framesizes, testtypes)):
                     if store_sel is None:
                         store_sel = list()
                     for core in cores:
                         for framesize in framesizes:
                             for ttype in testtypes:
                     if store_sel is None:
                         store_sel = list()
                     for core in cores:
                         for framesize in framesizes:
                             for ttype in testtypes:
-                                tid = (
-                                    f"{phy.replace('af_xdp', 'af-xdp')}-"
-                                    f"{area}-"
-                                    f"{framesize.lower()}-"
-                                    f"{core.lower()}-"
-                                    f"{test}-"
-                                    f"{ttype.lower()}"
-                                )
+                                if dut == "trex":
+                                    core = str()
+                                tid = "-".join((
+                                    dut, phy.replace('af_xdp', 'af-xdp'), area,
+                                    framesize.lower(), core.lower(), test,
+                                    ttype.lower()
+                                ))
                                 if tid not in [itm["id"] for itm in store_sel]:
                                     store_sel.append({
                                         "id": tid,
                                 if tid not in [itm["id"] for itm in store_sel]:
                                     store_sel.append({
                                         "id": tid,
+                                        "dut": dut,
                                         "phy": phy,
                                         "area": area,
                                         "test": test,
                                         "phy": phy,
                                         "area": area,
                                         "test": test,
@@ -606,88 +1187,239 @@ class Layout:
                                         "core": core.lower(),
                                         "testtype": ttype.lower()
                                     })
                                         "core": core.lower(),
                                         "testtype": ttype.lower()
                                     })
-                output.set_values({
-                    "selected-tests-data": store_sel,
-                    "cl-selected-options": _list_tests(),
-                    "dd-ctrl-phy-value": None,
-                    "dd-ctrl-area-value": None,
-                    "dd-ctrl-test-value": None,
-                })
-
-            elif trigger_id in ("btn-sel-display", "dpr-period"):
-                fig_tput, fig_lat = trending_tput(
-                    self.data, store_sel, self.layout, d_start, d_end
-                )
-                output.set_values({
-                    "graph-tput-figure": \
-                        fig_tput if fig_tput else self.NO_GRAPH,
-                    "graph-tput-style": \
-                        self.STYLE_BLOCK if fig_tput else self.STYLE_HIDEN,
-                    "div-tput-metadata-style": \
-                        self.STYLE_INLINE if fig_tput else self.STYLE_HIDEN,
-                    "graph-lat-figure": \
-                        fig_lat if fig_lat else self.NO_GRAPH,
-                    "graph-lat-style": \
-                        self.STYLE_BLOCK if fig_lat else self.STYLE_HIDEN,
-                    "div-lat-metadata-style": \
-                        self.STYLE_INLINE if fig_lat else self.STYLE_HIDEN
-                })
-
+                    store_sel = sorted(store_sel, key=lambda d: d["id"])
+                    row_card_sel_tests = C.STYLE_ENABLED
+                    row_btns_sel_tests = C.STYLE_ENABLED
+                    if C.CLEAR_ALL_INPUTS:
+                        ctrl_panel.set(ctrl_panel.defaults)
+            elif trigger_id == "btn-sel-remove-all":
+                _ = btn_remove_all
+                row_fig_tput = C.PLACEHOLDER
+                row_fig_lat = C.PLACEHOLDER
+                row_btn_dwnld = C.PLACEHOLDER
+                row_card_sel_tests = C.STYLE_DISABLED
+                row_btns_sel_tests = C.STYLE_DISABLED
+                store_sel = list()
+                ctrl_panel.set({"cl-selected-options": list()})
             elif trigger_id == "btn-sel-remove":
             elif trigger_id == "btn-sel-remove":
+                _ = btn_remove
                 if list_sel:
                     new_store_sel = list()
                     for item in store_sel:
                         if item["id"] not in list_sel:
                             new_store_sel.append(item)
                     store_sel = new_store_sel
                 if list_sel:
                     new_store_sel = list()
                     for item in store_sel:
                         if item["id"] not in list_sel:
                             new_store_sel.append(item)
                     store_sel = new_store_sel
+            elif trigger_id == "url":
+                if url_params:
+                    try:
+                        store_sel = literal_eval(url_params["store_sel"][0])
+                        d_start = get_date(url_params["start"][0])
+                        d_end = get_date(url_params["end"][0])
+                        norm = literal_eval(url_params["norm"][0])
+                    except (KeyError, IndexError):
+                        pass
+                    if store_sel:
+                        row_card_sel_tests = C.STYLE_ENABLED
+                        row_btns_sel_tests = C.STYLE_ENABLED
+                        last_test = store_sel[-1]
+                        test = self.spec_tbs[last_test["dut"]]\
+                            [last_test["phy"]][last_test["area"]]\
+                                [last_test["test"]]
+                        ctrl_panel.set({
+                            "dd-ctrl-dut-value": last_test["dut"],
+                            "dd-ctrl-phy-value": last_test["phy"],
+                            "dd-ctrl-phy-options": generate_options(sorted(
+                                self.spec_tbs[last_test["dut"]].keys())),
+                            "dd-ctrl-phy-disabled": False,
+                            "dd-ctrl-area-value": last_test["area"],
+                            "dd-ctrl-area-options": [
+                                {"label": label(v), "value": v} \
+                                    for v in sorted(
+                                        self.spec_tbs[last_test["dut"]]\
+                                            [last_test["phy"]].keys())
+                            ],
+                            "dd-ctrl-area-disabled": False,
+                            "dd-ctrl-test-value": last_test["test"],
+                            "dd-ctrl-test-options": generate_options(sorted(
+                                self.spec_tbs[last_test["dut"]]\
+                                    [last_test["phy"]]\
+                                        [last_test["area"]].keys())),
+                            "dd-ctrl-test-disabled": False,
+                            "cl-ctrl-core-options": generate_options(sorted(
+                                test["core"])),
+                            "cl-ctrl-core-value": [last_test["core"].upper(), ],
+                            "cl-ctrl-core-all-value": list(),
+                            "cl-ctrl-core-all-options": C.CL_ALL_ENABLED,
+                            "cl-ctrl-framesize-options": generate_options(
+                                sorted(test["frame-size"])),
+                            "cl-ctrl-framesize-value": \
+                                [last_test["framesize"].upper(), ],
+                            "cl-ctrl-framesize-all-value": list(),
+                            "cl-ctrl-framesize-all-options": C.CL_ALL_ENABLED,
+                            "cl-ctrl-testtype-options": generate_options(sorted(
+                                test["test-type"])),
+                            "cl-ctrl-testtype-value": \
+                                [last_test["testtype"].upper(), ],
+                            "cl-ctrl-testtype-all-value": list(),
+                            "cl-ctrl-testtype-all-options": C.CL_ALL_ENABLED
+                        })
+
+            if trigger_id in ("btn-ctrl-add", "url", "dpr-period",
+                    "btn-sel-remove", "cl-ctrl-normalize"):
                 if store_sel:
                 if store_sel:
-                    fig_tput, fig_lat = trending_tput(
-                        self.data, store_sel, self.layout, d_start, d_end
-                    )
-                    output.set_values({
-                        "graph-tput-figure": \
-                            fig_tput if fig_tput else self.NO_GRAPH,
-                        "graph-tput-style": \
-                            self.STYLE_BLOCK if fig_tput else self.STYLE_HIDEN,
-                        "div-tput-metadata-style": \
-                            self.STYLE_INLINE if fig_tput else self.STYLE_HIDEN,
-                        "graph-lat-figure": \
-                            fig_lat if fig_lat else self.NO_GRAPH,
-                        "graph-lat-style": \
-                            self.STYLE_BLOCK if fig_lat else self.STYLE_HIDEN,
-                        "div-lat-metadata-style": \
-                            self.STYLE_INLINE if fig_lat else self.STYLE_HIDEN,
-                        "selected-tests-data": store_sel,
-                        "cl-selected-options": _list_tests()
+                    row_fig_tput, row_fig_lat, row_btn_dwnld = \
+                        _generate_plotting_area(
+                            graph_trending(self.data, store_sel, self.layout,
+                                d_start, d_end, bool(norm)),
+                            gen_new_url(
+                                parsed_url,
+                                {
+                                    "store_sel": store_sel,
+                                    "start": d_start,
+                                    "end": d_end,
+                                    "norm": norm
+                                }
+                            )
+                        )
+                    ctrl_panel.set({
+                        "cl-selected-options": list_tests(store_sel)
                     })
                 else:
                     })
                 else:
-                    output.set_values({
-                        "graph-tput-figure": self.NO_GRAPH,
-                        "graph-tput-style": self.STYLE_HIDEN,
-                        "div-tput-metadata-style": self.STYLE_HIDEN,
-                        "graph-lat-figure": self.NO_GRAPH,
-                        "graph-lat-style": self.STYLE_HIDEN,
-                        "div-lat-metadata-style": self.STYLE_HIDEN,
-                        "selected-tests-data": store_sel,
-                        "cl-selected-options": _list_tests()
-                    })
+                    row_fig_tput = C.PLACEHOLDER
+                    row_fig_lat = C.PLACEHOLDER
+                    row_btn_dwnld = C.PLACEHOLDER
+                    row_card_sel_tests = C.STYLE_DISABLED
+                    row_btns_sel_tests = C.STYLE_DISABLED
+                    store_sel = list()
+                    ctrl_panel.set({"cl-selected-options": list()})
 
 
-            return output.value()
+            if ctrl_panel.get("cl-ctrl-core-value") and \
+                    ctrl_panel.get("cl-ctrl-framesize-value") and \
+                    ctrl_panel.get("cl-ctrl-testtype-value"):
+                disabled = False
+            else:
+                disabled = True
+            ctrl_panel.set({
+                "btn-ctrl-add-disabled": disabled,
+                "cl-normalize-value": norm
+            })
+
+            ret_val = [
+                ctrl_panel.panel, store_sel,
+                row_fig_tput, row_fig_lat, row_btn_dwnld,
+                row_card_sel_tests, row_btns_sel_tests
+            ]
+            ret_val.extend(ctrl_panel.values())
+            return ret_val
 
         @app.callback(
 
         @app.callback(
-            Output("tput-metadata", "children"),
-            Input("graph-tput", "clickData")
+            Output("metadata-tput-lat", "children"),
+            Output("metadata-hdrh-graph", "children"),
+            Output("offcanvas-metadata", "is_open"),
+            Input({"type": "graph", "index": ALL}, "clickData"),
+            prevent_initial_call=True
         )
         )
-        def _show_tput_metadata(hover_data):
-            if not hover_data:
+        def _show_metadata_from_graphs(graph_data: dict) -> tuple:
+            """Generates the data for the offcanvas displayed when a particular
+            point in a graph is clicked on.
+
+            :param graph_data: The data from the clicked point in the graph.
+            :type graph_data: dict
+            :returns: The data to be displayed on the offcanvas and the
+                information to show the offcanvas.
+            :rtype: tuple(list, list, bool)
+            """
+            try:
+                trigger_id = loads(
+                    callback_context.triggered[0]["prop_id"].split(".")[0]
+                )["index"]
+                idx = 0 if trigger_id == "tput" else 1
+                graph_data = graph_data[idx]["points"][0]
+            except (JSONDecodeError, IndexError, KeyError, ValueError,
+                    TypeError):
                 raise PreventUpdate
                 raise PreventUpdate
-            return json.dumps(hover_data, indent=2)
+
+            metadata = no_update
+            graph = list()
+
+            children = [
+                dbc.ListGroupItem(
+                    [dbc.Badge(x.split(":")[0]), x.split(": ")[1]]
+                ) for x in graph_data.get("text", "").split("<br>")
+            ]
+            if trigger_id == "tput":
+                title = "Throughput"
+            elif trigger_id == "lat":
+                title = "Latency"
+                hdrh_data = graph_data.get("customdata", None)
+                if hdrh_data:
+                    graph = [dbc.Card(
+                        class_name="gy-2 p-0",
+                        children=[
+                            dbc.CardHeader(hdrh_data.pop("name")),
+                            dbc.CardBody(children=[
+                                dcc.Graph(
+                                    id="hdrh-latency-graph",
+                                    figure=graph_hdrh_latency(
+                                        hdrh_data, self.layout
+                                    )
+                                )
+                            ])
+                        ])
+                    ]
+            metadata = [
+                dbc.Card(
+                    class_name="gy-2 p-0",
+                    children=[
+                        dbc.CardHeader(children=[
+                            dcc.Clipboard(
+                                target_id="tput-lat-metadata",
+                                title="Copy",
+                                style={"display": "inline-block"}
+                            ),
+                            title
+                        ]),
+                        dbc.CardBody(
+                            id="tput-lat-metadata",
+                            class_name="p-0",
+                            children=[dbc.ListGroup(children, flush=True), ]
+                        )
+                    ]
+                )
+            ]
+
+            return metadata, graph, True
 
         @app.callback(
 
         @app.callback(
-            Output("latency-metadata", "children"),
-            Input("graph-latency", "clickData")
+            Output("download-data", "data"),
+            State("selected-tests", "data"),
+            Input("btn-download-data", "n_clicks"),
+            prevent_initial_call=True
         )
         )
-        def _show_latency_metadata(hover_data):
-            if not hover_data:
+        def _download_data(store_sel, n_clicks):
+            """Download the data
+
+            :param store_sel: List of tests selected by user stored in the
+                browser.
+            :param n_clicks: Number of clicks on the button "Download".
+            :type store_sel: list
+            :type n_clicks: int
+            :returns: dict of data frame content (base64 encoded) and meta data
+                used by the Download component.
+            :rtype: dict
+            """
+
+            if not n_clicks:
                 raise PreventUpdate
                 raise PreventUpdate
-            return json.dumps(hover_data, indent=2)
+
+            if not store_sel:
+                raise PreventUpdate
+
+            df = pd.DataFrame()
+            for itm in store_sel:
+                sel_data = select_trending_data(self.data, itm)
+                if sel_data is None:
+                    continue
+                df = pd.concat([df, sel_data], ignore_index=True)
+
+            return dcc.send_data_frame(df.to_csv, C.TREND_DOWNLOAD_FILE_NAME)