C-Dash: Prepare layout for telemetry in trending 51/37151/29
authorTibor Frank <tifrank@cisco.com>
Mon, 19 Sep 2022 09:39:26 +0000 (11:39 +0200)
committerTibor Frank <tifrank@cisco.com>
Thu, 20 Oct 2022 13:41:14 +0000 (15:41 +0200)
Signed-off-by: Tibor Frank <tifrank@cisco.com>
Change-Id: I6bd248922d7e6c61ab17eee580af2937022f8371

12 files changed:
csit.infra.dash/app/cdash/news/layout.py
csit.infra.dash/app/cdash/report/graphs.py
csit.infra.dash/app/cdash/report/layout.py
csit.infra.dash/app/cdash/report/layout.yaml
csit.infra.dash/app/cdash/stats/layout.py
csit.infra.dash/app/cdash/templates/base_layout.jinja2
csit.infra.dash/app/cdash/trending/layout.py
csit.infra.dash/app/cdash/trending/layout.yaml
csit.infra.dash/app/cdash/utils/constants.py
csit.infra.dash/app/cdash/utils/control_panel.py [new file with mode: 0644]
csit.infra.dash/app/cdash/utils/trigger.py [new file with mode: 0644]
csit.infra.dash/app/cdash/utils/utils.py

index dfe6eba..0227521 100644 (file)
@@ -22,7 +22,7 @@ from flask import Flask
 from dash import dcc
 from dash import html
 from dash import callback_context
 from dash import dcc
 from dash import html
 from dash import callback_context
-from dash import Input, Output
+from dash import Input, Output, State
 from yaml import load, FullLoader, YAMLError
 
 from ..data.data import Data
 from yaml import load, FullLoader, YAMLError
 
 from ..data.data import Data
@@ -251,8 +251,6 @@ class Layout:
 
         self._default_period = C.NEWS_SHORT
         self._default_active = (False, True, False)
 
         self._default_period = C.NEWS_SHORT
         self._default_active = (False, True, False)
-        self._default_table = \
-            table_summary(self._data, self._jobs, self._default_period)
 
         # Callbacks:
         if self._app is not None and hasattr(self, 'callbacks'):
 
         # Callbacks:
         if self._app is not None and hasattr(self, 'callbacks'):
@@ -286,7 +284,7 @@ class Layout:
                         id="row-navbar",
                         class_name="g-0",
                         children=[
                         id="row-navbar",
                         class_name="g-0",
                         children=[
-                            self._add_navbar(),
+                            self._add_navbar()
                         ]
                     ),
                     dbc.Row(
                         ]
                     ),
                     dbc.Row(
@@ -294,7 +292,7 @@ class Layout:
                         class_name="g-0",
                         children=[
                             self._add_ctrl_col(),
                         class_name="g-0",
                         children=[
                             self._add_ctrl_col(),
-                            self._add_plotting_col(),
+                            self._add_plotting_col()
                         ]
                     )
                 ]
                         ]
                     )
                 ]
@@ -305,10 +303,10 @@ class Layout:
                 children=[
                     dbc.Alert(
                         [
                 children=[
                     dbc.Alert(
                         [
-                            "An Error Occured",
+                            "An Error Occured"
                         ],
                         ],
-                        color="danger",
-                    ),
+                        color="danger"
+                    )
                 ]
             )
 
                 ]
             )
 
@@ -335,7 +333,7 @@ class Layout:
             brand_href="/",
             brand_external_link=True,
             class_name="p-2",
             brand_href="/",
             brand_external_link=True,
             class_name="p-2",
-            fluid=True,
+            fluid=True
         )
 
     def _add_ctrl_col(self) -> dbc.Col:
         )
 
     def _add_ctrl_col(self) -> dbc.Col:
@@ -357,61 +355,38 @@ class Layout:
         :returns: Column with tables.
         :rtype: dbc.Col
         """
         :returns: Column with tables.
         :rtype: dbc.Col
         """
-
         return dbc.Col(
             id="col-plotting-area",
             children=[
                 dcc.Loading(
                     children=[
         return dbc.Col(
             id="col-plotting-area",
             children=[
                 dcc.Loading(
                     children=[
-                        dbc.Row(  # Failed tests
-                            id="row-table",
-                            class_name="g-0 p-2",
-                            children=self._default_table
-                        ),
                         dbc.Row(
                         dbc.Row(
-                            class_name="g-0 p-2",
-                            align="center",
-                            justify="start",
+                            id="plotting-area",
+                            class_name="g-0 p-0",
                             children=[
                             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=""
-                                        )
-                                    ]
-                                )
+                                C.PLACEHOLDER
                             ]
                         )
                     ]
                 )
             ],
                             ]
                         )
                     ]
                 )
             ],
-            width=9,
+            width=9
         )
 
         )
 
-    def _add_ctrl_panel(self) -> dbc.Row:
+    def _add_ctrl_panel(self) -> list:
         """Add control panel.
 
         :returns: Control panel.
         """Add control panel.
 
         :returns: Control panel.
-        :rtype: dbc.Row
+        :rtype: list
         """
         return [
             dbc.Label(
                 class_name="g-0 p-1",
         """
         return [
             dbc.Label(
                 class_name="g-0 p-1",
-                children=show_tooltip(self._tooltips,
-                    "help-summary-period", "Window")
+                children=show_tooltip(
+                    self._tooltips,
+                    "help-summary-period",
+                    "Window"
+                )
             ),
             dbc.Row(
                 class_name="g-0 p-1",
             ),
             dbc.Row(
                 class_name="g-0 p-1",
@@ -447,6 +422,59 @@ class Layout:
             )
         ]
 
             )
         ]
 
+    def _get_plotting_area(
+            self,
+            period: int,
+            url: str
+        ) -> list:
+        """Generate the plotting area with all its content.
+
+        :param period: The time period for summary tables.
+        :param url: URL to be displayed in the modal window.
+        :type period: int
+        :type url: str
+        :returns: The content of the plotting area.
+        :rtype: list
+        """
+        return [
+            dbc.Row(
+                id="row-table",
+                class_name="g-0 p-1",
+                children=table_summary(self._data, self._jobs, period)
+            ),
+            dbc.Row(
+                [
+                    dbc.Col([html.Div(
+                        [
+                            dbc.Button(
+                                id="plot-btn-url",
+                                children="URL",
+                                class_name="me-1",
+                                color="info",
+                                style={
+                                    "text-transform": "none",
+                                    "padding": "0rem 1rem"
+                                }
+                            ),
+                            dbc.Modal(
+                                [
+                                    dbc.ModalHeader(dbc.ModalTitle("URL")),
+                                    dbc.ModalBody(url)
+                                ],
+                                id="plot-mod-url",
+                                size="xl",
+                                is_open=False,
+                                scrollable=True
+                            )
+                        ],
+                        className=\
+                            "d-grid gap-0 d-md-flex justify-content-md-end"
+                    )])
+                ],
+                class_name="g-0 p-0"
+            )
+        ]
+
     def callbacks(self, app):
         """Callbacks for the whole application.
 
     def callbacks(self, app):
         """Callbacks for the whole application.
 
@@ -455,26 +483,22 @@ class Layout:
         """
 
         @app.callback(
         """
 
         @app.callback(
-            Output("row-table", "children"),
-            Output("input-url", "value"),
+            Output("plotting-area", "children"),
             Output("period-last", "active"),
             Output("period-short", "active"),
             Output("period-long", "active"),
             Output("period-last", "active"),
             Output("period-short", "active"),
             Output("period-long", "active"),
+            Input("url", "href"),
             Input("period-last", "n_clicks"),
             Input("period-short", "n_clicks"),
             Input("period-last", "n_clicks"),
             Input("period-short", "n_clicks"),
-            Input("period-long", "n_clicks"),
-            Input("url", "href")
+            Input("period-long", "n_clicks")
         )
         )
-        def _update_application(btn_last: int, btn_short: int, btn_long: int,
-            href: str) -> tuple:
+        def _update_application(href: str, *_) -> tuple:
             """Update the application when the event is detected.
 
             :returns: New values for web page elements.
             :rtype: tuple
             """
 
             """Update the application when the event is detected.
 
             :returns: New values for web page elements.
             :rtype: tuple
             """
 
-            _, _, _ = btn_last, btn_short, btn_long
-
             periods = {
                 "period-last": C.NEWS_LAST,
                 "period-short": C.NEWS_SHORT,
             periods = {
                 "period-last": C.NEWS_LAST,
                 "period-short": C.NEWS_SHORT,
@@ -497,12 +521,23 @@ class Layout:
             if trigger_id == "url" and url_params:
                 trigger_id = url_params.get("period", list())[0]
 
             if trigger_id == "url" and url_params:
                 trigger_id = url_params.get("period", list())[0]
 
-            period = periods.get(trigger_id, self._default_period)
-            active = actives.get(trigger_id, self._default_active)
-
             ret_val = [
             ret_val = [
-                table_summary(self._data, self._jobs, period),
-                gen_new_url(parsed_url, {"period": trigger_id})
+                self._get_plotting_area(
+                    periods.get(trigger_id, self._default_period),
+                    gen_new_url(parsed_url, {"period": trigger_id})
+                )
             ]
             ]
-            ret_val.extend(active)
+            ret_val.extend(actives.get(trigger_id, self._default_active))
             return ret_val
             return ret_val
+
+        @app.callback(
+            Output("plot-mod-url", "is_open"),
+            [Input("plot-btn-url", "n_clicks")],
+            [State("plot-mod-url", "is_open")],
+        )
+        def toggle_plot_mod_url(n, is_open):
+            """Toggle the modal window with url.
+            """
+            if n:
+                return not is_open
+            return is_open
index 36f28d0..0e27836 100644 (file)
@@ -216,60 +216,3 @@ def graph_iterative(data: pd.DataFrame, sel:dict, layout: dict,
         fig_lat = go.Figure(data=lat_traces, layout=pl_lat)
 
     return fig_tput, fig_lat
         fig_lat = go.Figure(data=lat_traces, layout=pl_lat)
 
     return fig_tput, fig_lat
-
-
-def table_comparison(data: pd.DataFrame, sel:dict,
-        normalize: bool) -> pd.DataFrame:
-    """Generate the comparison table with selected tests.
-
-    :param data: Data frame with iterative data.
-    :param sel: Selected tests.
-    :param normalize: If True, the data is normalized to CPU frquency
-        Constants.NORM_FREQUENCY.
-    :param data: pandas.DataFrame
-    :param sel: dict
-    :param normalize: bool
-    :returns: Comparison table.
-    :rtype: pandas.DataFrame
-    """
-    table = pd.DataFrame(
-        # {
-        #     "Test Case": [
-        #         "64b-2t1c-avf-eth-l2xcbase-eth-2memif-1dcr",
-        #         "64b-2t1c-avf-eth-l2xcbase-eth-2vhostvr1024-1vm-vppl2xc",
-        #         "64b-2t1c-avf-ethip4udp-ip4base-iacl50sl-10kflows",
-        #         "78b-2t1c-avf-ethip6-ip6scale2m-rnd "],
-        #     "2106.0-8": [
-        #         "14.45 +- 0.08",
-        #         "9.63 +- 0.05",
-        #         "9.7 +- 0.02",
-        #         "8.95 +- 0.06"],
-        #     "2110.0-8": [
-        #         "14.45 +- 0.08",
-        #         "9.63 +- 0.05",
-        #         "9.7 +- 0.02",
-        #         "8.95 +- 0.06"],
-        #     "2110.0-9": [
-        #         "14.45 +- 0.08",
-        #         "9.63 +- 0.05",
-        #         "9.7 +- 0.02",
-        #         "8.95 +- 0.06"],
-        #     "2202.0-9": [
-        #         "14.45 +- 0.08",
-        #         "9.63 +- 0.05",
-        #         "9.7 +- 0.02",
-        #         "8.95 +- 0.06"],
-        #     "2110.0-9 vs 2110.0-8": [
-        #         "-0.23 +-  0.62",
-        #         "-1.37 +-   1.3",
-        #         "+0.08 +-   0.2",
-        #         "-2.16 +-  0.83"],
-        #     "2202.0-9 vs 2110.0-9": [
-        #         "+6.95 +-  0.72",
-        #         "+5.35 +-  1.26",
-        #         "+4.48 +-  1.48",
-        #         "+4.09 +-  0.95"]
-        # }
-    )
-
-    return table
index ec33147..d4720d9 100644 (file)
@@ -25,16 +25,51 @@ 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 dash import Input, Output, State
 from dash.exceptions import PreventUpdate
 from yaml import load, FullLoader, YAMLError
-from copy import deepcopy
 from ast import literal_eval
 
 from ..utils.constants import Constants as C
 from ast import literal_eval
 
 from ..utils.constants import Constants as C
-from ..utils.utils import show_tooltip, label, sync_checklists, list_tests, \
-    gen_new_url, generate_options
+from ..utils.control_panel import ControlPanel
+from ..utils.trigger import Trigger
+from ..utils.utils import show_tooltip, label, sync_checklists, gen_new_url, \
+    generate_options, get_list_group_items
 from ..utils.url_processing import url_decode
 from ..data.data import Data
 from ..utils.url_processing import url_decode
 from ..data.data import Data
-from .graphs import graph_iterative, table_comparison, get_short_version, \
-    select_iterative_data
+from .graphs import graph_iterative, get_short_version, select_iterative_data
+
+
+# Control panel partameters and their default values.
+CP_PARAMS = {
+    "dd-rls-val": str(),
+    "dd-dut-opt": list(),
+    "dd-dut-dis": True,
+    "dd-dut-val": str(),
+    "dd-dutver-opt": list(),
+    "dd-dutver-dis": True,
+    "dd-dutver-val": str(),
+    "dd-phy-opt": list(),
+    "dd-phy-dis": True,
+    "dd-phy-val": str(),
+    "dd-area-opt": list(),
+    "dd-area-dis": True,
+    "dd-area-val": str(),
+    "dd-test-opt": list(),
+    "dd-test-dis": True,
+    "dd-test-val": str(),
+    "cl-core-opt": list(),
+    "cl-core-val": list(),
+    "cl-core-all-val": list(),
+    "cl-core-all-opt": C.CL_ALL_DISABLED,
+    "cl-frmsize-opt": list(),
+    "cl-frmsize-val": list(),
+    "cl-frmsize-all-val": list(),
+    "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
+    "cl-tsttype-opt": list(),
+    "cl-tsttype-val": list(),
+    "cl-tsttype-all-val": list(),
+    "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
+    "btn-add-dis": True,
+    "cl-normalize-val": list()
+}
 
 
 class Layout:
 
 
 class Layout:
@@ -86,7 +121,9 @@ class Layout:
                 read_iterative_ndrpdr(release=rls.replace("csit", "rls"))
             data_ndrpdr["release"] = rls
             self._data = pd.concat(
                 read_iterative_ndrpdr(release=rls.replace("csit", "rls"))
             data_ndrpdr["release"] = rls
             self._data = pd.concat(
-                [self._data, data_mrr, data_ndrpdr], ignore_index=True)
+                [self._data, data_mrr, data_ndrpdr],
+                ignore_index=True
+            )
 
         # Get structure of tests:
         tbs = dict()
 
         # Get structure of tests:
         tbs = dict()
@@ -138,25 +175,29 @@ class Layout:
             if core.upper() not in \
                     tbs[rls][dut][d_ver][infra][area][test]["core"]:
                 tbs[rls][dut][d_ver][infra][area][test]["core"].append(
             if core.upper() not in \
                     tbs[rls][dut][d_ver][infra][area][test]["core"]:
                 tbs[rls][dut][d_ver][infra][area][test]["core"].append(
-                    core.upper())
+                    core.upper()
+                )
             if framesize.upper() not in \
                         tbs[rls][dut][d_ver][infra][area][test]["frame-size"]:
                 tbs[rls][dut][d_ver][infra][area][test]["frame-size"].append(
             if framesize.upper() not in \
                         tbs[rls][dut][d_ver][infra][area][test]["frame-size"]:
                 tbs[rls][dut][d_ver][infra][area][test]["frame-size"].append(
-                    framesize.upper())
+                    framesize.upper()
+                )
             if ttype == "mrr":
                 if "MRR" not in \
                         tbs[rls][dut][d_ver][infra][area][test]["test-type"]:
                     tbs[rls][dut][d_ver][infra][area][test]["test-type"].append(
             if ttype == "mrr":
                 if "MRR" not in \
                         tbs[rls][dut][d_ver][infra][area][test]["test-type"]:
                     tbs[rls][dut][d_ver][infra][area][test]["test-type"].append(
-                        "MRR")
+                        "MRR"
+                    )
             elif ttype == "ndrpdr":
                 if "NDR" not in \
                         tbs[rls][dut][d_ver][infra][area][test]["test-type"]:
                     tbs[rls][dut][d_ver][infra][area][test]["test-type"].extend(
             elif ttype == "ndrpdr":
                 if "NDR" not in \
                         tbs[rls][dut][d_ver][infra][area][test]["test-type"]:
                     tbs[rls][dut][d_ver][infra][area][test]["test-type"].extend(
-                        ("NDR", "PDR", ))
+                        ("NDR", "PDR", )
+                    )
         self._spec_tbs = tbs
 
         # Read from files:
         self._spec_tbs = tbs
 
         # Read from files:
-        self._html_layout = ""
+        self._html_layout = str()
         self._graph_layout = None
         self._tooltips = dict()
 
         self._graph_layout = None
         self._tooltips = dict()
 
@@ -196,25 +237,13 @@ class Layout:
             )
 
         # Callbacks:
             )
 
         # Callbacks:
-        if self._app is not None and hasattr(self, 'callbacks'):
+        if self._app is not None and hasattr(self, "callbacks"):
             self.callbacks(self._app)
 
     @property
     def html_layout(self):
         return self._html_layout
 
             self.callbacks(self._app)
 
     @property
     def html_layout(self):
         return self._html_layout
 
-    @property
-    def spec_tbs(self):
-        return self._spec_tbs
-
-    @property
-    def data(self):
-        return self._data
-
-    @property
-    def layout(self):
-        return self._graph_layout
-
     def add_content(self):
         """Top level method which generated the web page.
 
     def add_content(self):
         """Top level method which generated the web page.
 
@@ -229,7 +258,7 @@ class Layout:
         :rtype: html.Div
         """
 
         :rtype: html.Div
         """
 
-        if self.html_layout and self.spec_tbs:
+        if self.html_layout and self._spec_tbs:
             return html.Div(
                 id="div-main",
                 className="small",
             return html.Div(
                 id="div-main",
                 className="small",
@@ -238,31 +267,18 @@ class Layout:
                         id="row-navbar",
                         class_name="g-0",
                         children=[
                         id="row-navbar",
                         class_name="g-0",
                         children=[
-                            self._add_navbar(),
+                            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=[
                     dbc.Row(
                         id="row-main",
                         class_name="g-0",
                         children=[
-                            dcc.Store(id="selected-tests"),
-                            dcc.Store(id="control-panel"),
+                            dcc.Store(id="store-selected-tests"),
+                            dcc.Store(id="store-control-panel"),
                             dcc.Location(id="url", refresh=False),
                             self._add_ctrl_col(),
                             dcc.Location(id="url", refresh=False),
                             self._add_ctrl_col(),
-                            self._add_plotting_col(),
+                            self._add_plotting_col()
                         ]
                     )
                 ]
                         ]
                     )
                 ]
@@ -273,10 +289,10 @@ class Layout:
                 children=[
                     dbc.Alert(
                         [
                 children=[
                     dbc.Alert(
                         [
-                            "An Error Occured",
+                            "An Error Occured"
                         ],
                         ],
-                        color="danger",
-                    ),
+                        color="danger"
+                    )
                 ]
             )
 
                 ]
             )
 
@@ -302,7 +318,7 @@ class Layout:
             brand_href="/",
             brand_external_link=True,
             class_name="p-2",
             brand_href="/",
             brand_external_link=True,
             class_name="p-2",
-            fluid=True,
+            fluid=True
         )
 
     def _add_ctrl_col(self) -> dbc.Col:
         )
 
     def _add_ctrl_col(self) -> dbc.Col:
@@ -319,9 +335,9 @@ class Layout:
         ])
 
     def _add_plotting_col(self) -> dbc.Col:
         ])
 
     def _add_plotting_col(self) -> dbc.Col:
-        """Add column with plots and tables. It is placed on the right side.
+        """Add column with plots. It is placed on the right side.
 
 
-        :returns: Column with tables.
+        :returns: Column with plots.
         :rtype: dbc.Col
         """
         return dbc.Col(
         :rtype: dbc.Col
         """
         return dbc.Col(
@@ -329,36 +345,12 @@ class Layout:
             children=[
                 dcc.Loading(
                     children=[
             children=[
                 dcc.Loading(
                     children=[
-                        dbc.Row(  # Graphs
-                            class_name="g-0 p-2",
+                        dbc.Row(
+                            id="plotting-area",
+                            class_name="g-0 p-0",
                             children=[
                             children=[
-                                dbc.Col(
-                                    dbc.Row(  # Throughput
-                                        id="row-graph-tput",
-                                        class_name="g-0 p-2",
-                                        children=[C.PLACEHOLDER, ]
-                                    ),
-                                    width=6
-                                ),
-                                dbc.Col(
-                                    dbc.Row(  # Latency
-                                        id="row-graph-lat",
-                                        class_name="g-0 p-2",
-                                        children=[C.PLACEHOLDER, ]
-                                    ),
-                                    width=6
-                                )
+                                C.PLACEHOLDER
                             ]
                             ]
-                        ),
-                        dbc.Row(  # Tables
-                            id="row-table",
-                            class_name="g-0 p-2",
-                            children=[C.PLACEHOLDER, ]
-                        ),
-                        dbc.Row(  # Download
-                            id="row-btn-download",
-                            class_name="g-0 p-2",
-                            children=[C.PLACEHOLDER, ]
                         )
                     ]
                 )
                         )
                     ]
                 )
@@ -366,11 +358,11 @@ class Layout:
             width=9
         )
 
             width=9
         )
 
-    def _add_ctrl_panel(self) -> dbc.Row:
+    def _add_ctrl_panel(self) -> list:
         """Add control panel.
 
         :returns: Control panel.
         """Add control panel.
 
         :returns: Control panel.
-        :rtype: dbc.Row
+        :rtype: list
         """
         return [
             dbc.Row(
         """
         return [
             dbc.Row(
@@ -379,23 +371,26 @@ class Layout:
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
-                                children=show_tooltip(self._tooltips,
-                                    "help-release", "CSIT Release")
+                                children=show_tooltip(
+                                    self._tooltips,
+                                    "help-release",
+                                    "CSIT Release"
+                                )
                             ),
                             dbc.Select(
                             ),
                             dbc.Select(
-                                id="dd-ctrl-rls",
-                                placeholder=("Select a Release..."),
+                                id={"type": "ctrl-dd", "index": "rls"},
+                                placeholder="Select a Release...",
                                 options=sorted(
                                     [
                                         {"label": k, "value": k} \
                                 options=sorted(
                                     [
                                         {"label": k, "value": k} \
-                                            for k in self.spec_tbs.keys()
+                                            for k in self._spec_tbs.keys()
                                     ],
                                     key=lambda d: d["label"]
                                 )
                             )
                         ],
                                     ],
                                     key=lambda d: d["label"]
                                 )
                             )
                         ],
-                        size="sm",
-                    ),
+                        size="sm"
+                    )
                 ]
             ),
             dbc.Row(
                 ]
             ),
             dbc.Row(
@@ -404,18 +399,19 @@ class Layout:
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
-                                children=show_tooltip(self._tooltips,
-                                    "help-dut", "DUT")
+                                children=show_tooltip(
+                                    self._tooltips,
+                                    "help-dut",
+                                    "DUT"
+                                )
                             ),
                             dbc.Select(
                             ),
                             dbc.Select(
-                                id="dd-ctrl-dut",
-                                placeholder=(
-                                    "Select a Device under Test..."
-                                )
+                                id={"type": "ctrl-dd", "index": "dut"},
+                                placeholder="Select a Device under Test..."
                             )
                         ],
                             )
                         ],
-                        size="sm",
-                    ),
+                        size="sm"
+                    )
                 ]
             ),
             dbc.Row(
                 ]
             ),
             dbc.Row(
@@ -424,19 +420,20 @@ class Layout:
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
-                                children=show_tooltip(self._tooltips,
-                                    "help-dut-ver", "DUT Version")
+                                children=show_tooltip(
+                                    self._tooltips,
+                                    "help-dut-ver",
+                                    "DUT Version"
+                                )
                             ),
                             dbc.Select(
                             ),
                             dbc.Select(
-                                id="dd-ctrl-dutver",
-                                placeholder=(
-                                    "Select a Version of "
-                                    "Device under Test..."
-                                )
+                                id={"type": "ctrl-dd", "index": "dutver"},
+                                placeholder=\
+                                    "Select a Version of Device under Test..."
                             )
                         ],
                             )
                         ],
-                        size="sm",
-                    ),
+                        size="sm"
+                    )
                 ]
             ),
             dbc.Row(
                 ]
             ),
             dbc.Row(
@@ -445,19 +442,20 @@ class Layout:
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
-                                children=show_tooltip(self._tooltips,
-                                    "help-infra", "Infra")
+                                children=show_tooltip(
+                                    self._tooltips,
+                                    "help-infra",
+                                    "Infra"
+                                )
                             ),
                             dbc.Select(
                             ),
                             dbc.Select(
-                                id="dd-ctrl-phy",
-                                placeholder=(
-                                    "Select a Physical Test Bed "
-                                    "Topology..."
-                                )
+                                id={"type": "ctrl-dd", "index": "phy"},
+                                placeholder=\
+                                    "Select a Physical Test Bed Topology..."
                             )
                         ],
                             )
                         ],
-                        size="sm",
-                    ),
+                        size="sm"
+                    )
                 ]
             ),
             dbc.Row(
                 ]
             ),
             dbc.Row(
@@ -466,17 +464,19 @@ class Layout:
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
-                                children=show_tooltip(self._tooltips,
-                                    "help-area", "Area")
+                                children=show_tooltip(
+                                    self._tooltips,
+                                    "help-area",
+                                    "Area"
+                                )
                             ),
                             dbc.Select(
                             ),
                             dbc.Select(
-                                id="dd-ctrl-area",
-                                placeholder="Select an Area...",
-                                disabled=True,
-                            ),
+                                id={"type": "ctrl-dd", "index": "area"},
+                                placeholder="Select an Area..."
+                            )
                         ],
                         ],
-                        size="sm",
-                    ),
+                        size="sm"
+                    )
                 ]
             ),
             dbc.Row(
                 ]
             ),
             dbc.Row(
@@ -485,43 +485,50 @@ class Layout:
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
-                                children=show_tooltip(self._tooltips,
-                                    "help-test", "Test")
+                                children=show_tooltip(
+                                    self._tooltips,
+                                    "help-test",
+                                    "Test"
+                                )
                             ),
                             dbc.Select(
                             ),
                             dbc.Select(
-                                id="dd-ctrl-test",
-                                placeholder="Select a Test...",
-                                disabled=True,
-                            ),
+                                id={"type": "ctrl-dd", "index": "test"},
+                                placeholder="Select a Test..."
+                            )
                         ],
                         ],
-                        size="sm",
-                    ),
+                        size="sm"
+                    )
                 ]
             ),
             dbc.Row(
                 class_name="g-0 p-1",
                 children=[
                     dbc.Label(
                 ]
             ),
             dbc.Row(
                 class_name="g-0 p-1",
                 children=[
                     dbc.Label(
-                        children=show_tooltip(self._tooltips,
-                            "help-framesize", "Frame Size"),
+                        children=show_tooltip(
+                            self._tooltips,
+                            "help-framesize",
+                            "Frame Size"
+                        )
                     ),
                     dbc.Col(
                         children=[
                             dbc.Checklist(
                     ),
                     dbc.Col(
                         children=[
                             dbc.Checklist(
-                                id="cl-ctrl-framesize-all",
+                                id={"type": "ctrl-cl", "index": "frmsize-all"},
                                 options=C.CL_ALL_DISABLED,
                                 inline=True,
                                 options=C.CL_ALL_DISABLED,
                                 inline=True,
-                                switch=False
-                            ),
+                                switch=False,
+                                input_class_name="border-info bg-info"
+                            )
                         ],
                         width=3
                     ),
                     dbc.Col(
                         children=[
                             dbc.Checklist(
                         ],
                         width=3
                     ),
                     dbc.Col(
                         children=[
                             dbc.Checklist(
-                                id="cl-ctrl-framesize",
+                                id={"type": "ctrl-cl", "index": "frmsize"},
                                 inline=True,
                                 inline=True,
-                                switch=False
+                                switch=False,
+                                input_class_name="border-info bg-info"
                             )
                         ]
                     )
                             )
                         ]
                     )
@@ -531,16 +538,20 @@ class Layout:
                 class_name="g-0 p-1",
                 children=[
                     dbc.Label(
                 class_name="g-0 p-1",
                 children=[
                     dbc.Label(
-                        children=show_tooltip(self._tooltips,
-                            "help-cores", "Number of Cores"),
+                        children=show_tooltip(
+                            self._tooltips,
+                            "help-cores",
+                            "Number of Cores"
+                        )
                     ),
                     dbc.Col(
                         children=[
                             dbc.Checklist(
                     ),
                     dbc.Col(
                         children=[
                             dbc.Checklist(
-                                id="cl-ctrl-core-all",
+                                id={"type": "ctrl-cl", "index": "core-all"},
                                 options=C.CL_ALL_DISABLED,
                                 inline=False,
                                 options=C.CL_ALL_DISABLED,
                                 inline=False,
-                                switch=False
+                                switch=False,
+                                input_class_name="border-info bg-info"
                             )
                         ],
                         width=3
                             )
                         ],
                         width=3
@@ -548,9 +559,10 @@ class Layout:
                     dbc.Col(
                         children=[
                             dbc.Checklist(
                     dbc.Col(
                         children=[
                             dbc.Checklist(
-                                id="cl-ctrl-core",
+                                id={"type": "ctrl-cl", "index": "core"},
                                 inline=True,
                                 inline=True,
-                                switch=False
+                                switch=False,
+                                input_class_name="border-info bg-info"
                             )
                         ]
                     )
                             )
                         ]
                     )
@@ -560,26 +572,31 @@ class Layout:
                 class_name="g-0 p-1",
                 children=[
                     dbc.Label(
                 class_name="g-0 p-1",
                 children=[
                     dbc.Label(
-                        children=show_tooltip(self._tooltips,
-                            "help-ttype", "Test Type"),
+                        children=show_tooltip(
+                            self._tooltips,
+                            "help-ttype",
+                            "Test Type"
+                        )
                     ),
                     dbc.Col(
                         children=[
                             dbc.Checklist(
                     ),
                     dbc.Col(
                         children=[
                             dbc.Checklist(
-                                id="cl-ctrl-testtype-all",
+                                id={"type": "ctrl-cl", "index": "tsttype-all"},
                                 options=C.CL_ALL_DISABLED,
                                 inline=True,
                                 options=C.CL_ALL_DISABLED,
                                 inline=True,
-                                switch=False
-                            ),
+                                switch=False,
+                                input_class_name="border-info bg-info"
+                            )
                         ],
                         width=3
                     ),
                     dbc.Col(
                         children=[
                             dbc.Checklist(
                         ],
                         width=3
                     ),
                     dbc.Col(
                         children=[
                             dbc.Checklist(
-                                id="cl-ctrl-testtype",
+                                id={"type": "ctrl-cl", "index": "tsttype"},
                                 inline=True,
                                 inline=True,
-                                switch=False
+                                switch=False,
+                                input_class_name="border-info bg-info"
                             )
                         ]
                     )
                             )
                         ]
                     )
@@ -589,13 +606,16 @@ class Layout:
                 class_name="g-0 p-1",
                 children=[
                     dbc.Label(
                 class_name="g-0 p-1",
                 children=[
                     dbc.Label(
-                        children=show_tooltip(self._tooltips,
-                            "help-normalize", "Normalize"),
+                        children=show_tooltip(
+                            self._tooltips,
+                            "help-normalize",
+                            "Normalize"
+                        )
                     ),
                     dbc.Col(
                         children=[
                             dbc.Checklist(
                     ),
                     dbc.Col(
                         children=[
                             dbc.Checklist(
-                                id="cl-ctrl-normalize",
+                                id="normalize",
                                 options=[{
                                     "value": "normalize",
                                     "label": (
                                 options=[{
                                     "value": "normalize",
                                     "label": (
@@ -605,7 +625,8 @@ class Layout:
                                 }],
                                 value=[],
                                 inline=True,
                                 }],
                                 value=[],
                                 inline=True,
-                                switch=False
+                                switch=False,
+                                input_class_name="border-info bg-info"
                             ),
                         ]
                     )
                             ),
                         ]
                     )
@@ -614,14 +635,10 @@ class Layout:
             dbc.Row(
                 class_name="g-0 p-1",
                 children=[
             dbc.Row(
                 class_name="g-0 p-1",
                 children=[
-                    dbc.ButtonGroup(
-                        [
-                            dbc.Button(
-                                id="btn-ctrl-add",
-                                children="Add Selected",
-                                color="info"
-                            )
-                        ]
+                    dbc.Button(
+                        id={"type": "ctrl-btn", "index": "add-test"},
+                        children="Add Selected",
+                        color="info"
                     )
                 ]
             ),
                     )
                 ]
             ),
@@ -630,15 +647,14 @@ class Layout:
                 class_name="g-0 p-1",
                 style=C.STYLE_DISABLED,
                 children=[
                 class_name="g-0 p-1",
                 style=C.STYLE_DISABLED,
                 children=[
-                    dbc.Label("Selected tests"),
-                    dbc.Checklist(
-                        class_name="overflow-auto",
-                        id="cl-selected",
-                        options=[],
-                        inline=False,
-                        style={"max-height": "20em"},
+                    dbc.ListGroup(
+                        class_name="overflow-auto p-0",
+                        id="lg-selected",
+                        children=[],
+                        style={"max-height": "14em"},
+                        flush=True
                     )
                     )
-                ],
+                ]
             ),
             dbc.Row(
                 id="row-btns-sel-tests",
             ),
             dbc.Row(
                 id="row-btns-sel-tests",
@@ -648,121 +664,122 @@ class Layout:
                     dbc.ButtonGroup(
                         children=[
                             dbc.Button(
                     dbc.ButtonGroup(
                         children=[
                             dbc.Button(
-                                id="btn-sel-remove",
+                                id={"type": "ctrl-btn", "index": "rm-test"},
                                 children="Remove Selected",
                                 class_name="w-100",
                                 color="info",
                                 disabled=False
                             ),
                             dbc.Button(
                                 children="Remove Selected",
                                 class_name="w-100",
                                 color="info",
                                 disabled=False
                             ),
                             dbc.Button(
-                                id="btn-sel-remove-all",
+                                id={"type": "ctrl-btn", "index": "rm-test-all"},
                                 children="Remove All",
                                 class_name="w-100",
                                 color="info",
                                 disabled=False
                                 children="Remove All",
                                 class_name="w-100",
                                 color="info",
                                 disabled=False
-                            ),
+                            )
                         ]
                     )
                 ]
                         ]
                     )
                 ]
-            ),
+            )
         ]
 
         ]
 
-    class ControlPanel:
-        """A class representing the control panel.
+    def _get_plotting_area(
+            self,
+            tests: list,
+            normalize: bool,
+            url: str
+        ) -> list:
+        """Generate the plotting area with all its content.
+
+        :param tests: List of tests to be displayed in the graphs.
+        :param normalize: If true, the values in graphs are normalized.
+        :param url: URL to be displayed in the modal window.
+        :type tests: list
+        :type normalize: bool
+        :type url: str
+        :returns: List of rows with elements to be displayed in the plotting
+            area.
+        :rtype: list
         """
         """
+        if not tests:
+            return C.PLACEHOLDER
 
 
-        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
-            """
-
-            # Defines also the order of keys
-            self._defaults = {
-                "dd-rls-value": str(),
-                "dd-dut-options": list(),
-                "dd-dut-disabled": True,
-                "dd-dut-value": str(),
-                "dd-dutver-options": list(),
-                "dd-dutver-disabled": True,
-                "dd-dutver-value": str(),
-                "dd-phy-options": list(),
-                "dd-phy-disabled": True,
-                "dd-phy-value": str(),
-                "dd-area-options": list(),
-                "dd-area-disabled": True,
-                "dd-area-value": str(),
-                "dd-test-options": list(),
-                "dd-test-disabled": True,
-                "dd-test-value": str(),
-                "cl-core-options": list(),
-                "cl-core-value": list(),
-                "cl-core-all-value": list(),
-                "cl-core-all-options": C.CL_ALL_DISABLED,
-                "cl-framesize-options": list(),
-                "cl-framesize-value": list(),
-                "cl-framesize-all-value": list(),
-                "cl-framesize-all-options": C.CL_ALL_DISABLED,
-                "cl-testtype-options": list(),
-                "cl-testtype-value": list(),
-                "cl-testtype-all-value": list(),
-                "cl-testtype-all-options": C.CL_ALL_DISABLED,
-                "btn-add-disabled": True,
-                "cl-normalize-value": list(),
-                "cl-selected-options": list()
-            }
-
-            self._panel = deepcopy(self._defaults)
-            if panel:
-                for key in self._defaults:
-                    self._panel[key] = panel[key]
-
-        @property
-        def defaults(self) -> dict:
-            return self._defaults
-
-        @property
-        def panel(self) -> dict:
-            return self._panel
+        figs = graph_iterative(self._data, tests, self._graph_layout, normalize)
 
 
-        def set(self, kwargs: dict) -> None:
-            """Set the values of the Control panel.
+        if not figs[0]:
+            return C.PLACEHOLDER
 
 
-            :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.")
-
-        def get(self, key: str) -> any:
-            """Returns the value of a key from the Control panel.
-
-            :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]
+        row_items = [
+            dbc.Col(
+                children=dcc.Graph(
+                    id={"type": "graph", "index": "tput"},
+                    figure=figs[0]
+                ),
+                class_name="g-0 p-1",
+                width=6
+            )
+        ]
 
 
-        def values(self) -> tuple:
-            """Returns the values from the Control panel as a list.
+        if figs[1]:
+            row_items.append(
+                dbc.Col(
+                    children=dcc.Graph(
+                        id={"type": "graph", "index": "lat"},
+                        figure=figs[1]
+                    ),
+                    class_name="g-0 p-1",
+                    width=6
+                )
+            )
 
 
-            :returns: The values from the Control panel.
-            :rtype: list
-            """
-            return tuple(self._panel.values())
+        return [
+            dbc.Row(
+                children=row_items,
+                class_name="g-0 p-0",
+            ),
+            dbc.Row(
+                [
+                    dbc.Col([html.Div(
+                        [
+                            dbc.Button(
+                                id="plot-btn-url",
+                                children="URL",
+                                class_name="me-1",
+                                color="info",
+                                style={
+                                    "text-transform": "none",
+                                    "padding": "0rem 1rem"
+                                }
+                            ),
+                            dbc.Modal(
+                                [
+                                    dbc.ModalHeader(dbc.ModalTitle("URL")),
+                                    dbc.ModalBody(url)
+                                ],
+                                id="plot-mod-url",
+                                size="xl",
+                                is_open=False,
+                                scrollable=True
+                            ),
+                            dbc.Button(
+                                id="plot-btn-download",
+                                children="Download Data",
+                                class_name="me-1",
+                                color="info",
+                                style={
+                                    "text-transform": "none",
+                                    "padding": "0rem 1rem"
+                                }
+                            ),
+                            dcc.Download(id="download-iterative-data")
+                        ],
+                        className=\
+                            "d-grid gap-0 d-md-flex justify-content-md-end"
+                    )])
+                ],
+                class_name="g-0 p-0"
+            )
+        ]
 
     def callbacks(self, app):
         """Callbacks for the whole application.
 
     def callbacks(self, app):
         """Callbacks for the whole application.
@@ -771,214 +788,73 @@ class Layout:
         :type app: Flask
         """
 
         :type app: Flask
         """
 
-        def _generate_plotting_area(figs: tuple, table: pd.DataFrame,
-                url: str) -> tuple:
-            """Generate the plotting area with all its content.
-
-            :param figs: Figures to be placed in the plotting area.
-            :param table: A table to be placed in the plotting area bellow the
-                figures.
-            :param utl: The URL to be placed in the plotting area bellow the
-                tables.
-            :type figs: tuple of plotly.graph_objects.Figure
-            :type table: pandas.DataFrame
-            :type url: str
-            :returns: tuple of elements to be shown in the plotting area.
-            :rtype: tuple
-                (dcc.Graph, dcc.Graph, dbc.Table, list(dbc.Col, dbc.Col))
-            """
-
-            (fig_tput, fig_lat) = figs
-
-            row_fig_tput = C.PLACEHOLDER
-            row_fig_lat = C.PLACEHOLDER
-            row_table = C.PLACEHOLDER
-            row_btn_dwnld = C.PLACEHOLDER
-
-            if fig_tput:
-                row_fig_tput = [
-                    dcc.Graph(
-                        id={"type": "graph", "index": "tput"},
-                        figure=fig_tput
-                    )
-                ]
-                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
-                                    )
-                                ]
-                            )
-                        ]
-                    )
-                ]
-            if fig_lat:
-                row_fig_lat = [
-                    dcc.Graph(
-                        id={"type": "graph", "index": "lat"},
-                        figure=fig_lat
-                    )
-                ]
-            if not table.empty:
-                row_table = [
-                    dbc.Table.from_dataframe(
-                        table,
-                        id={"type": "table", "index": "compare"},
-                        striped=True,
-                        bordered=True,
-                        hover=True
-                    )
-                ]
-
-            return row_fig_tput, row_fig_lat, row_table, 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-table", "children"),
-            Output("row-btn-download", "children"),
-            Output("row-card-sel-tests", "style"),
-            Output("row-btns-sel-tests", "style"),
-            Output("dd-ctrl-rls", "value"),
-            Output("dd-ctrl-dut", "options"),
-            Output("dd-ctrl-dut", "disabled"),
-            Output("dd-ctrl-dut", "value"),
-            Output("dd-ctrl-dutver", "options"),
-            Output("dd-ctrl-dutver", "disabled"),
-            Output("dd-ctrl-dutver", "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-all", "options"),
-            Output("cl-ctrl-framesize", "options"),
-            Output("cl-ctrl-framesize", "value"),
-            Output("cl-ctrl-framesize-all", "value"),
-            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-all", "options"),
-            Output("btn-ctrl-add", "disabled"),
-            Output("cl-ctrl-normalize", "value"),
-            Output("cl-selected", "options"),  # User selection
-            State("control-panel", "data"),  # Store
-            State("selected-tests", "data"),  # Store
-            State("cl-selected", "value"),  # User selection
-            Input("dd-ctrl-rls", "value"),
-            Input("dd-ctrl-dut", "value"),
-            Input("dd-ctrl-dutver", "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-sel-remove", "n_clicks"),
-            Input("btn-sel-remove-all", "n_clicks"),
-            Input("url", "href")
+            [
+                Output("store-control-panel", "data"),
+                Output("store-selected-tests", "data"),
+                Output("plotting-area", "children"),
+                Output("row-card-sel-tests", "style"),
+                Output("row-btns-sel-tests", "style"),
+                Output("lg-selected", "children"),
+
+                Output({"type": "ctrl-dd", "index": "rls"}, "value"),
+                Output({"type": "ctrl-dd", "index": "dut"}, "options"),
+                Output({"type": "ctrl-dd", "index": "dut"}, "disabled"),
+                Output({"type": "ctrl-dd", "index": "dut"}, "value"),
+                Output({"type": "ctrl-dd", "index": "dutver"}, "options"),
+                Output({"type": "ctrl-dd", "index": "dutver"}, "disabled"),
+                Output({"type": "ctrl-dd", "index": "dutver"}, "value"),
+                Output({"type": "ctrl-dd", "index": "phy"}, "options"),
+                Output({"type": "ctrl-dd", "index": "phy"}, "disabled"),
+                Output({"type": "ctrl-dd", "index": "phy"}, "value"),
+                Output({"type": "ctrl-dd", "index": "area"}, "options"),
+                Output({"type": "ctrl-dd", "index": "area"}, "disabled"),
+                Output({"type": "ctrl-dd", "index": "area"}, "value"),
+                Output({"type": "ctrl-dd", "index": "test"}, "options"),
+                Output({"type": "ctrl-dd", "index": "test"}, "disabled"),
+                Output({"type": "ctrl-dd", "index": "test"}, "value"),
+                Output({"type": "ctrl-cl", "index": "core"}, "options"),
+                Output({"type": "ctrl-cl", "index": "core"}, "value"),
+                Output({"type": "ctrl-cl", "index": "core-all"}, "value"),
+                Output({"type": "ctrl-cl", "index": "core-all"}, "options"),
+                Output({"type": "ctrl-cl", "index": "frmsize"}, "options"),
+                Output({"type": "ctrl-cl", "index": "frmsize"}, "value"),
+                Output({"type": "ctrl-cl", "index": "frmsize-all"}, "value"),
+                Output({"type": "ctrl-cl", "index": "frmsize-all"}, "options"),
+                Output({"type": "ctrl-cl", "index": "tsttype"}, "options"),
+                Output({"type": "ctrl-cl", "index": "tsttype"}, "value"),
+                Output({"type": "ctrl-cl", "index": "tsttype-all"}, "value"),
+                Output({"type": "ctrl-cl", "index": "tsttype-all"}, "options"),
+                Output({"type": "ctrl-btn", "index": "add-test"}, "disabled"),
+                Output("normalize", "value")
+            ],
+            [
+                State("store-control-panel", "data"),
+                State("store-selected-tests", "data"),
+                State({"type": "sel-cl", "index": ALL}, "value")
+            ],
+            [
+                Input("url", "href"),
+                Input("normalize", "value"),
+
+                Input({"type": "ctrl-dd", "index": ALL}, "value"),
+                Input({"type": "ctrl-cl", "index": ALL}, "value"),
+                Input({"type": "ctrl-btn", "index": ALL}, "n_clicks")
+            ]
         )
         )
-        def _update_ctrl_panel(cp_data: dict, store_sel: list, list_sel: list,
-            dd_rls: str, dd_dut: str, dd_dutver: 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, btn_remove: int,
-            btn_remove_all: int, href: str) -> tuple:
+        def _update_application(
+                control_panel: dict,
+                store_sel: list,
+                lst_sel: list,
+                href: str,
+                normalize: list,
+                *_
+            ) -> tuple:
             """Update the application when the event is detected.
             """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_rls: Input - Releases.
-            :param dd_dut: Input - DUTs.
-            :param dd_dutver: Input - Version of DUT.
-            :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 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_rls: str
-            :type dd_dut: str
-            :type dd_dutver: 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 btn_remove: int
-            :type btn_remove_all: int
-            :type href: str
-            :returns: New values for web page elements.
-            :rtype: tuple
             """
 
             """
 
-            ctrl_panel = self.ControlPanel(cp_data)
-            norm = cl_normalize
+            ctrl_panel = ControlPanel(CP_PARAMS, control_panel)
+            on_draw = False
 
             # Parse the url:
             parsed_url = url_decode(href)
 
             # Parse the url:
             parsed_url = url_decode(href)
@@ -987,304 +863,336 @@ class Layout:
             else:
                 url_params = None
 
             else:
                 url_params = None
 
-            row_fig_tput = no_update
-            row_fig_lat = no_update
-            row_table = no_update
-            row_btn_dwnld = no_update
+            plotting_area = no_update
             row_card_sel_tests = no_update
             row_btns_sel_tests = no_update
             row_card_sel_tests = no_update
             row_btns_sel_tests = no_update
+            lg_selected = no_update
 
 
-            trigger_id = callback_context.triggered[0]["prop_id"].split(".")[0]
+            trigger = Trigger(callback_context.triggered)
 
 
-            if trigger_id == "dd-ctrl-rls":
-                try:
-                    options = \
-                        generate_options(sorted(self.spec_tbs[dd_rls].keys()))
-                    disabled = False
-                except KeyError:
-                    options = list()
-                    disabled = True
-                ctrl_panel.set({
-                    "dd-rls-value": dd_rls,
-                    "dd-dut-value": str(),
-                    "dd-dut-options": options,
-                    "dd-dut-disabled": disabled,
-                    "dd-dutver-value": str(),
-                    "dd-dutver-options": list(),
-                    "dd-dutver-disabled": True,
-                    "dd-phy-value": str(),
-                    "dd-phy-options": list(),
-                    "dd-phy-disabled": True,
-                    "dd-area-value": str(),
-                    "dd-area-options": list(),
-                    "dd-area-disabled": True,
-                    "dd-test-value": str(),
-                    "dd-test-options": list(),
-                    "dd-test-disabled": True,
-                    "cl-core-options": list(),
-                    "cl-core-value": list(),
-                    "cl-core-all-value": list(),
-                    "cl-core-all-options": C.CL_ALL_DISABLED,
-                    "cl-framesize-options": list(),
-                    "cl-framesize-value": list(),
-                    "cl-framesize-all-value": list(),
-                    "cl-framesize-all-options": C.CL_ALL_DISABLED,
-                    "cl-testtype-options": list(),
-                    "cl-testtype-value": list(),
-                    "cl-testtype-all-value": list(),
-                    "cl-testtype-all-options": C.CL_ALL_DISABLED
-                })
-            elif trigger_id == "dd-ctrl-dut":
-                try:
-                    rls = ctrl_panel.get("dd-rls-value")
-                    dut = self.spec_tbs[rls][dd_dut]
-                    options = generate_options(sorted(dut.keys()))
-                    disabled = False
-                except KeyError:
-                    options = list()
-                    disabled = True
-                ctrl_panel.set({
-                    "dd-dut-value": dd_dut,
-                    "dd-dutver-value": str(),
-                    "dd-dutver-options": options,
-                    "dd-dutver-disabled": disabled,
-                    "dd-phy-value": str(),
-                    "dd-phy-options": list(),
-                    "dd-phy-disabled": True,
-                    "dd-area-value": str(),
-                    "dd-area-options": list(),
-                    "dd-area-disabled": True,
-                    "dd-test-value": str(),
-                    "dd-test-options": list(),
-                    "dd-test-disabled": True,
-                    "cl-core-options": list(),
-                    "cl-core-value": list(),
-                    "cl-core-all-value": list(),
-                    "cl-core-all-options": C.CL_ALL_DISABLED,
-                    "cl-framesize-options": list(),
-                    "cl-framesize-value": list(),
-                    "cl-framesize-all-value": list(),
-                    "cl-framesize-all-options": C.CL_ALL_DISABLED,
-                    "cl-testtype-options": list(),
-                    "cl-testtype-value": list(),
-                    "cl-testtype-all-value": list(),
-                    "cl-testtype-all-options": C.CL_ALL_DISABLED
-                })
-            elif trigger_id == "dd-ctrl-dutver":
-                try:
-                    rls = ctrl_panel.get("dd-rls-value")
-                    dut = ctrl_panel.get("dd-dut-value")
-                    dutver = self.spec_tbs[rls][dut][dd_dutver]
-                    options = generate_options(sorted(dutver.keys()))
-                    disabled = False
-                except KeyError:
-                    options = list()
-                    disabled = True
-                ctrl_panel.set({
-                    "dd-dutver-value": dd_dutver,
-                    "dd-phy-value": str(),
-                    "dd-phy-options": options,
-                    "dd-phy-disabled": disabled,
-                    "dd-area-value": str(),
-                    "dd-area-options": list(),
-                    "dd-area-disabled": True,
-                    "dd-test-value": str(),
-                    "dd-test-options": list(),
-                    "dd-test-disabled": True,
-                    "cl-core-options": list(),
-                    "cl-core-value": list(),
-                    "cl-core-all-value": list(),
-                    "cl-core-all-options": C.CL_ALL_DISABLED,
-                    "cl-framesize-options": list(),
-                    "cl-framesize-value": list(),
-                    "cl-framesize-all-value": list(),
-                    "cl-framesize-all-options": C.CL_ALL_DISABLED,
-                    "cl-testtype-options": list(),
-                    "cl-testtype-value": list(),
-                    "cl-testtype-all-value": list(),
-                    "cl-testtype-all-options": C.CL_ALL_DISABLED
-                })
-            elif trigger_id == "dd-ctrl-phy":
-                try:
-                    rls = ctrl_panel.get("dd-rls-value")
-                    dut = ctrl_panel.get("dd-dut-value")
-                    dutver = ctrl_panel.get("dd-dutver-value")
-                    phy = self.spec_tbs[rls][dut][dutver][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-phy-value": dd_phy,
-                    "dd-area-value": str(),
-                    "dd-area-options": options,
-                    "dd-area-disabled": disabled,
-                    "dd-test-value": str(),
-                    "dd-test-options": list(),
-                    "dd-test-disabled": True,
-                    "cl-core-options": list(),
-                    "cl-core-value": list(),
-                    "cl-core-all-value": list(),
-                    "cl-core-all-options": C.CL_ALL_DISABLED,
-                    "cl-framesize-options": list(),
-                    "cl-framesize-value": list(),
-                    "cl-framesize-all-value": list(),
-                    "cl-framesize-all-options": C.CL_ALL_DISABLED,
-                    "cl-testtype-options": list(),
-                    "cl-testtype-value": list(),
-                    "cl-testtype-all-value": list(),
-                    "cl-testtype-all-options": C.CL_ALL_DISABLED
-                })
-            elif trigger_id == "dd-ctrl-area":
+            if trigger.type == "url" and url_params:
                 try:
                 try:
-                    rls = ctrl_panel.get("dd-rls-value")
-                    dut = ctrl_panel.get("dd-dut-value")
-                    dutver = ctrl_panel.get("dd-dutver-value")
-                    phy = ctrl_panel.get("dd-phy-value")
-                    area = self.spec_tbs[rls][dut][dutver][phy][dd_area]
-                    options = generate_options(sorted(area.keys()))
-                    disabled = False
-                except KeyError:
-                    options = list()
-                    disabled = True
-                ctrl_panel.set({
-                    "dd-area-value": dd_area,
-                    "dd-test-value": str(),
-                    "dd-test-options": options,
-                    "dd-test-disabled": disabled,
-                    "cl-core-options": list(),
-                    "cl-core-value": list(),
-                    "cl-core-all-value": list(),
-                    "cl-core-all-options": C.CL_ALL_DISABLED,
-                    "cl-framesize-options": list(),
-                    "cl-framesize-value": list(),
-                    "cl-framesize-all-value": list(),
-                    "cl-framesize-all-options": C.CL_ALL_DISABLED,
-                    "cl-testtype-options": list(),
-                    "cl-testtype-value": list(),
-                    "cl-testtype-all-value": list(),
-                    "cl-testtype-all-options": C.CL_ALL_DISABLED
-                })
-            elif trigger_id == "dd-ctrl-test":
-                rls = ctrl_panel.get("dd-rls-value")
-                dut = ctrl_panel.get("dd-dut-value")
-                dutver = ctrl_panel.get("dd-dutver-value")
-                phy = ctrl_panel.get("dd-phy-value")
-                area = ctrl_panel.get("dd-area-value")
-                if all((rls, dut, dutver, phy, area, dd_test, )):
-                    test = self.spec_tbs[rls][dut][dutver][phy][area][dd_test]
+                    store_sel = literal_eval(url_params["store_sel"][0])
+                    normalize = 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["rls"]][last_test["dut"]]\
+                        [last_test["dutver"]][last_test["phy"]]\
+                            [last_test["area"]][last_test["test"]]
                     ctrl_panel.set({
                     ctrl_panel.set({
-                        "dd-test-value": dd_test,
-                        "cl-core-options": \
-                            generate_options(sorted(test["core"])),
-                        "cl-core-value": list(),
-                        "cl-core-all-value": list(),
-                        "cl-core-all-options": C.CL_ALL_ENABLED,
-                        "cl-framesize-options": \
-                            generate_options(sorted(test["frame-size"])),
-                        "cl-framesize-value": list(),
-                        "cl-framesize-all-value": list(),
-                        "cl-framesize-all-options": C.CL_ALL_ENABLED,
-                        "cl-testtype-options": \
-                            generate_options(sorted(test["test-type"])),
-                        "cl-testtype-value": list(),
-                        "cl-testtype-all-value": list(),
-                        "cl-testtype-all-options": C.CL_ALL_ENABLED,
+                        "dd-rls-val": last_test["rls"],
+                        "dd-dut-val": last_test["dut"],
+                        "dd-dut-opt": generate_options(
+                            self._spec_tbs[last_test["rls"]].keys()
+                        ),
+                        "dd-dut-dis": False,
+                        "dd-dutver-val": last_test["dutver"],
+                        "dd-dutver-opt": generate_options(
+                            self._spec_tbs[last_test["rls"]]\
+                                [last_test["dut"]].keys()
+                        ),
+                        "dd-dutver-dis": False,
+                        "dd-phy-val": last_test["phy"],
+                        "dd-phy-opt": generate_options(
+                            self._spec_tbs[last_test["rls"]][last_test["dut"]]\
+                                [last_test["dutver"]].keys()
+                        ),
+                        "dd-phy-dis": False,
+                        "dd-area-val": last_test["area"],
+                        "dd-area-opt": [
+                            {"label": label(v), "value": v} for v in \
+                                sorted(self._spec_tbs[last_test["rls"]]\
+                                    [last_test["dut"]][last_test["dutver"]]\
+                                        [last_test["phy"]].keys())
+                        ],
+                        "dd-area-dis": False,
+                        "dd-test-val": last_test["test"],
+                        "dd-test-opt": generate_options(
+                            self._spec_tbs[last_test["rls"]][last_test["dut"]]\
+                                [last_test["dutver"]][last_test["phy"]]\
+                                    [last_test["area"]].keys()
+                        ),
+                        "dd-test-dis": False,
+                        "cl-core-opt": generate_options(test["core"]),
+                        "cl-core-val": [last_test["core"].upper(), ],
+                        "cl-core-all-val": list(),
+                        "cl-core-all-opt": C.CL_ALL_ENABLED,
+                        "cl-frmsize-opt": generate_options(test["frame-size"]),
+                        "cl-frmsize-val": [last_test["framesize"].upper(), ],
+                        "cl-frmsize-all-val": list(),
+                        "cl-frmsize-all-opt": C.CL_ALL_ENABLED,
+                        "cl-tsttype-opt": generate_options(test["test-type"]),
+                        "cl-tsttype-val": [last_test["testtype"].upper(), ],
+                        "cl-tsttype-all-val": list(),
+                        "cl-tsttype-all-opt": C.CL_ALL_ENABLED,
+                        "cl-normalize-val": normalize,
+                        "btn-add-dis": False
                     })
                     })
-            elif trigger_id == "cl-ctrl-core":
-                val_sel, val_all = sync_checklists(
-                    options=ctrl_panel.get("cl-core-options"),
-                    sel=cl_core,
-                    all=list(),
-                    id=""
-                )
-                ctrl_panel.set({
-                    "cl-core-value": val_sel,
-                    "cl-core-all-value": val_all,
-                })
-            elif trigger_id == "cl-ctrl-core-all":
-                val_sel, val_all = sync_checklists(
-                    options = ctrl_panel.get("cl-core-options"),
-                    sel=list(),
-                    all=cl_core_all,
-                    id="all"
-                )
-                ctrl_panel.set({
-                    "cl-core-value": val_sel,
-                    "cl-core-all-value": val_all,
-                })
-            elif trigger_id == "cl-ctrl-framesize":
-                val_sel, val_all = sync_checklists(
-                    options = ctrl_panel.get("cl-framesize-options"),
-                    sel=cl_framesize,
-                    all=list(),
-                    id=""
-                )
-                ctrl_panel.set({
-                    "cl-framesize-value": val_sel,
-                    "cl-framesize-all-value": val_all,
-                })
-            elif trigger_id == "cl-ctrl-framesize-all":
-                val_sel, val_all = sync_checklists(
-                    options = ctrl_panel.get("cl-framesize-options"),
-                    sel=list(),
-                    all=cl_framesize_all,
-                    id="all"
-                )
-                ctrl_panel.set({
-                    "cl-framesize-value": val_sel,
-                    "cl-framesize-all-value": val_all,
-                })
-            elif trigger_id == "cl-ctrl-testtype":
-                val_sel, val_all = sync_checklists(
-                    options = ctrl_panel.get("cl-testtype-options"),
-                    sel=cl_testtype,
-                    all=list(),
-                    id=""
-                )
-                ctrl_panel.set({
-                    "cl-testtype-value": val_sel,
-                    "cl-testtype-all-value": val_all,
-                })
-            elif trigger_id == "cl-ctrl-testtype-all":
+                    on_draw = True
+            elif trigger.type == "normalize":
+                ctrl_panel.set({"cl-normalize-val": normalize})
+                on_draw = True
+            elif trigger.type == "ctrl-dd":
+                if trigger.idx == "rls":
+                    try:
+                        options = generate_options(
+                            self._spec_tbs[trigger.value].keys()
+                        )
+                        disabled = False
+                    except KeyError:
+                        options = list()
+                        disabled = True
+                    ctrl_panel.set({
+                        "dd-rls-val": trigger.value,
+                        "dd-dut-val": str(),
+                        "dd-dut-opt": options,
+                        "dd-dut-dis": disabled,
+                        "dd-dutver-val": str(),
+                        "dd-dutver-opt": list(),
+                        "dd-dutver-dis": True,
+                        "dd-phy-val": str(),
+                        "dd-phy-opt": list(),
+                        "dd-phy-dis": True,
+                        "dd-area-val": str(),
+                        "dd-area-opt": list(),
+                        "dd-area-dis": True,
+                        "dd-test-val": str(),
+                        "dd-test-opt": list(),
+                        "dd-test-dis": True,
+                        "cl-core-opt": list(),
+                        "cl-core-val": list(),
+                        "cl-core-all-val": list(),
+                        "cl-core-all-opt": C.CL_ALL_DISABLED,
+                        "cl-frmsize-opt": list(),
+                        "cl-frmsize-val": list(),
+                        "cl-frmsize-all-val": list(),
+                        "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
+                        "cl-tsttype-opt": list(),
+                        "cl-tsttype-val": list(),
+                        "cl-tsttype-all-val": list(),
+                        "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
+                        "btn-add-dis": True
+                    })
+                elif trigger.idx == "dut":
+                    try:
+                        rls = ctrl_panel.get("dd-rls-val")
+                        dut = self._spec_tbs[rls][trigger.value]
+                        options = generate_options(dut.keys())
+                        disabled = False
+                    except KeyError:
+                        options = list()
+                        disabled = True
+                    ctrl_panel.set({
+                        "dd-dut-val": trigger.value,
+                        "dd-dutver-val": str(),
+                        "dd-dutver-opt": options,
+                        "dd-dutver-dis": disabled,
+                        "dd-phy-val": str(),
+                        "dd-phy-opt": list(),
+                        "dd-phy-dis": True,
+                        "dd-area-val": str(),
+                        "dd-area-opt": list(),
+                        "dd-area-dis": True,
+                        "dd-test-val": str(),
+                        "dd-test-opt": list(),
+                        "dd-test-dis": True,
+                        "cl-core-opt": list(),
+                        "cl-core-val": list(),
+                        "cl-core-all-val": list(),
+                        "cl-core-all-opt": C.CL_ALL_DISABLED,
+                        "cl-frmsize-opt": list(),
+                        "cl-frmsize-val": list(),
+                        "cl-frmsize-all-val": list(),
+                        "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
+                        "cl-tsttype-opt": list(),
+                        "cl-tsttype-val": list(),
+                        "cl-tsttype-all-val": list(),
+                        "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
+                        "btn-add-dis": True
+                    })
+                elif trigger.idx == "dutver":
+                    try:
+                        rls = ctrl_panel.get("dd-rls-val")
+                        dut = ctrl_panel.get("dd-dut-val")
+                        dutver = self._spec_tbs[rls][dut][trigger.value]
+                        options = generate_options(dutver.keys())
+                        disabled = False
+                    except KeyError:
+                        options = list()
+                        disabled = True
+                    ctrl_panel.set({
+                        "dd-dutver-val": trigger.value,
+                        "dd-phy-val": str(),
+                        "dd-phy-opt": options,
+                        "dd-phy-dis": disabled,
+                        "dd-area-val": str(),
+                        "dd-area-opt": list(),
+                        "dd-area-dis": True,
+                        "dd-test-val": str(),
+                        "dd-test-opt": list(),
+                        "dd-test-dis": True,
+                        "cl-core-opt": list(),
+                        "cl-core-val": list(),
+                        "cl-core-all-val": list(),
+                        "cl-core-all-opt": C.CL_ALL_DISABLED,
+                        "cl-frmsize-opt": list(),
+                        "cl-frmsize-val": list(),
+                        "cl-frmsize-all-val": list(),
+                        "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
+                        "cl-tsttype-opt": list(),
+                        "cl-tsttype-val": list(),
+                        "cl-tsttype-all-val": list(),
+                        "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
+                        "btn-add-dis": True
+                    })
+                elif trigger.idx == "phy":
+                    try:
+                        rls = ctrl_panel.get("dd-rls-val")
+                        dut = ctrl_panel.get("dd-dut-val")
+                        dutver = ctrl_panel.get("dd-dutver-val")
+                        phy = self._spec_tbs[rls][dut][dutver][trigger.value]
+                        options = [{"label": label(v), "value": v} \
+                            for v in sorted(phy.keys())]
+                        disabled = False
+                    except KeyError:
+                        options = list()
+                        disabled = True
+                    ctrl_panel.set({
+                        "dd-phy-val": trigger.value,
+                        "dd-area-val": str(),
+                        "dd-area-opt": options,
+                        "dd-area-dis": disabled,
+                        "dd-test-val": str(),
+                        "dd-test-opt": list(),
+                        "dd-test-dis": True,
+                        "cl-core-opt": list(),
+                        "cl-core-val": list(),
+                        "cl-core-all-val": list(),
+                        "cl-core-all-opt": C.CL_ALL_DISABLED,
+                        "cl-frmsize-opt": list(),
+                        "cl-frmsize-val": list(),
+                        "cl-frmsize-all-val": list(),
+                        "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
+                        "cl-tsttype-opt": list(),
+                        "cl-tsttype-val": list(),
+                        "cl-tsttype-all-val": list(),
+                        "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
+                        "btn-add-dis": True
+                    })
+                elif trigger.idx == "area":
+                    try:
+                        rls = ctrl_panel.get("dd-rls-val")
+                        dut = ctrl_panel.get("dd-dut-val")
+                        dutver = ctrl_panel.get("dd-dutver-val")
+                        phy = ctrl_panel.get("dd-phy-val")
+                        area = \
+                            self._spec_tbs[rls][dut][dutver][phy][trigger.value]
+                        options = generate_options(area.keys())
+                        disabled = False
+                    except KeyError:
+                        options = list()
+                        disabled = True
+                    ctrl_panel.set({
+                        "dd-area-val": trigger.value,
+                        "dd-test-val": str(),
+                        "dd-test-opt": options,
+                        "dd-test-dis": disabled,
+                        "cl-core-opt": list(),
+                        "cl-core-val": list(),
+                        "cl-core-all-val": list(),
+                        "cl-core-all-opt": C.CL_ALL_DISABLED,
+                        "cl-frmsize-opt": list(),
+                        "cl-frmsize-val": list(),
+                        "cl-frmsize-all-val": list(),
+                        "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
+                        "cl-tsttype-opt": list(),
+                        "cl-tsttype-val": list(),
+                        "cl-tsttype-all-val": list(),
+                        "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
+                        "btn-add-dis": True
+                    })
+                elif trigger.idx == "test":
+                    rls = ctrl_panel.get("dd-rls-val")
+                    dut = ctrl_panel.get("dd-dut-val")
+                    dutver = ctrl_panel.get("dd-dutver-val")
+                    phy = ctrl_panel.get("dd-phy-val")
+                    area = ctrl_panel.get("dd-area-val")
+                    if all((rls, dut, dutver, phy, area, trigger.value, )):
+                        test = self._spec_tbs[rls][dut][dutver][phy][area]\
+                            [trigger.value]
+                        ctrl_panel.set({
+                            "dd-test-val": trigger.value,
+                            "cl-core-opt": generate_options(test["core"]),
+                            "cl-core-val": list(),
+                            "cl-core-all-val": list(),
+                            "cl-core-all-opt": C.CL_ALL_ENABLED,
+                            "cl-frmsize-opt": \
+                                generate_options(test["frame-size"]),
+                            "cl-frmsize-val": list(),
+                            "cl-frmsize-all-val": list(),
+                            "cl-frmsize-all-opt": C.CL_ALL_ENABLED,
+                            "cl-tsttype-opt": \
+                                generate_options(test["test-type"]),
+                            "cl-tsttype-val": list(),
+                            "cl-tsttype-all-val": list(),
+                            "cl-tsttype-all-opt": C.CL_ALL_ENABLED,
+                            "btn-add-dis": True
+                        })
+            elif trigger.type == "ctrl-cl":
+                param = trigger.idx.split("-")[0]
+                if "-all" in trigger.idx:
+                    c_sel, c_all, c_id = list(), trigger.value, "all"
+                else:
+                    c_sel, c_all, c_id = trigger.value, list(), str()
                 val_sel, val_all = sync_checklists(
                 val_sel, val_all = sync_checklists(
-                    options = ctrl_panel.get("cl-testtype-options"),
-                    sel=list(),
-                    all=cl_testtype_all,
-                    id="all"
+                    options=ctrl_panel.get(f"cl-{param}-opt"),
+                    sel=c_sel,
+                    all=c_all,
+                    id=c_id
                 )
                 ctrl_panel.set({
                 )
                 ctrl_panel.set({
-                    "cl-testtype-value": val_sel,
-                    "cl-testtype-all-value": val_all,
+                    f"cl-{param}-val": val_sel,
+                    f"cl-{param}-all-val": val_all,
                 })
                 })
-            elif trigger_id == "btn-ctrl-add":
-                _ = btn_add
-                rls = ctrl_panel.get("dd-rls-value")
-                dut = ctrl_panel.get("dd-dut-value")
-                dutver = ctrl_panel.get("dd-dutver-value")
-                phy = ctrl_panel.get("dd-phy-value")
-                area = ctrl_panel.get("dd-area-value")
-                test = ctrl_panel.get("dd-test-value")
-                cores = ctrl_panel.get("cl-core-value")
-                framesizes = ctrl_panel.get("cl-framesize-value")
-                testtypes = ctrl_panel.get("cl-testtype-value")
-                # Add selected test to the list of tests in store:
-                if all((rls, dut, dutver, phy, area, test, cores, framesizes,
-                        testtypes)):
+                if all((ctrl_panel.get("cl-core-val"), 
+                        ctrl_panel.get("cl-frmsize-val"),
+                        ctrl_panel.get("cl-tsttype-val"), )):
+                    ctrl_panel.set({"btn-add-dis": False})
+                else:
+                    ctrl_panel.set({"btn-add-dis": True})
+            elif trigger.type == "ctrl-btn":
+                on_draw = True
+                if trigger.idx == "add-test":
+                    rls = ctrl_panel.get("dd-rls-val")
+                    dut = ctrl_panel.get("dd-dut-val")
+                    dutver = ctrl_panel.get("dd-dutver-val")
+                    phy = ctrl_panel.get("dd-phy-val")
+                    area = ctrl_panel.get("dd-area-val")
+                    test = ctrl_panel.get("dd-test-val")
+                    # Add selected test to the list of tests in store:
                     if store_sel is None:
                         store_sel = list()
                     if store_sel is None:
                         store_sel = list()
-                    for core in cores:
-                        for framesize in framesizes:
-                            for ttype in testtypes:
+                    for core in ctrl_panel.get("cl-core-val"):
+                        for framesize in ctrl_panel.get("cl-frmsize-val"):
+                            for ttype in ctrl_panel.get("cl-tsttype-val"):
                                 if dut == "trex":
                                     core = str()
                                 if dut == "trex":
                                     core = str()
-                                tid = "-".join((rls, dut, dutver,
-                                    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]:
+                                tid = "-".join((
+                                    rls,
+                                    dut,
+                                    dutver,
+                                    phy.replace("af_xdp", "af-xdp"),
+                                    area,
+                                    framesize.lower(),
+                                    core.lower(),
+                                    test,
+                                    ttype.lower()
+                                ))
+                                if tid not in [i["id"] for i in store_sel]:
                                     store_sel.append({
                                         "id": tid,
                                         "rls": rls,
                                     store_sel.append({
                                         "id": tid,
                                         "rls": rls,
@@ -1298,173 +1206,82 @@ class Layout:
                                         "testtype": ttype.lower()
                                     })
                     store_sel = sorted(store_sel, key=lambda d: d["id"])
                                         "testtype": ttype.lower()
                                     })
                     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)
                     if C.CLEAR_ALL_INPUTS:
                         ctrl_panel.set(ctrl_panel.defaults)
-                    ctrl_panel.set({
-                        "cl-selected-options": list_tests(store_sel)
-                    })
-            elif trigger_id == "btn-sel-remove-all":
-                _ = btn_remove_all
-                row_fig_tput = C.PLACEHOLDER
-                row_fig_lat = C.PLACEHOLDER
-                row_table = 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":
-                _ = btn_remove
-                if list_sel:
+                elif trigger.idx == "rm-test" and lst_sel:
                     new_store_sel = list()
                     new_store_sel = list()
-                    for item in store_sel:
-                        if item["id"] not in list_sel:
+                    for idx, item in enumerate(store_sel):
+                        if not lst_sel[idx]:
                             new_store_sel.append(item)
                     store_sel = new_store_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])
-                        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["rls"]]\
-                            [last_test["dut"]][last_test["dutver"]]\
-                                [last_test["phy"]][last_test["area"]]\
-                                    [last_test["test"]]
-                        ctrl_panel.set({
-                            "dd-rls-value": last_test["rls"],
-                            "dd-dut-value": last_test["dut"],
-                            "dd-dut-options": generate_options(sorted(
-                                self.spec_tbs[last_test["rls"]].keys())),
-                            "dd-dut-disabled": False,
-                            "dd-dutver-value": last_test["dutver"],
-                            "dd-dutver-options": generate_options(sorted(
-                                self.spec_tbs[last_test["rls"]]\
-                                    [last_test["dut"]].keys())),
-                            "dd-dutver-disabled": False,
-                            "dd-phy-value": last_test["phy"],
-                            "dd-phy-options": generate_options(sorted(
-                                self.spec_tbs[last_test["rls"]]\
-                                    [last_test["dut"]]\
-                                        [last_test["dutver"]].keys())),
-                            "dd-phy-disabled": False,
-                            "dd-area-value": last_test["area"],
-                            "dd-area-options": [
-                                {"label": label(v), "value": v} for v in \
-                                    sorted(self.spec_tbs[last_test["rls"]]\
-                                        [last_test["dut"]][last_test["dutver"]]\
-                                            [last_test["phy"]].keys())
-                            ],
-                            "dd-area-disabled": False,
-                            "dd-test-value": last_test["test"],
-                            "dd-test-options": generate_options(sorted(
-                                self.spec_tbs[last_test["rls"]]\
-                                    [last_test["dut"]][last_test["dutver"]]\
-                                        [last_test["phy"]]\
-                                            [last_test["area"]].keys())),
-                            "dd-test-disabled": False,
-                            "cl-core-options": generate_options(sorted(
-                                test["core"])),
-                            "cl-core-value": [last_test["core"].upper(), ],
-                            "cl-core-all-value": list(),
-                            "cl-core-all-options": C.CL_ALL_ENABLED,
-                            "cl-framesize-options": generate_options(
-                                sorted(test["frame-size"])),
-                            "cl-framesize-value": \
-                                [last_test["framesize"].upper(), ],
-                            "cl-framesize-all-value": list(),
-                            "cl-framesize-all-options": C.CL_ALL_ENABLED,
-                            "cl-testtype-options": generate_options(sorted(
-                                test["test-type"])),
-                            "cl-testtype-value": \
-                                [last_test["testtype"].upper(), ],
-                            "cl-testtype-all-value": list(),
-                            "cl-testtype-all-options": C.CL_ALL_ENABLED
-                        })
+                elif trigger.idx == "rm-test-all":
+                    store_sel = list()
 
 
-            if trigger_id in ("btn-ctrl-add", "url", "btn-sel-remove",
-                    "cl-ctrl-normalize"):
+            if on_draw:
                 if store_sel:
                 if store_sel:
-                    row_fig_tput, row_fig_lat, row_table, row_btn_dwnld = \
-                        _generate_plotting_area(
-                            graph_iterative(
-                                self.data, store_sel, self.layout, bool(norm)
-                            ),
-                            table_comparison(
-                                self.data, store_sel, bool(norm)
-                            ),
-                            gen_new_url(
-                                parsed_url,
-                                {"store_sel": store_sel, "norm": norm}
-                            )
+                    lg_selected = get_list_group_items(store_sel)
+                    plotting_area = self._get_plotting_area(
+                        store_sel,
+                        bool(normalize),
+                        gen_new_url(
+                            parsed_url,
+                            {"store_sel": store_sel, "norm": normalize}
                         )
                         )
-                    ctrl_panel.set({
-                        "cl-selected-options": list_tests(store_sel)
-                    })
+                    )
+                    row_card_sel_tests = C.STYLE_ENABLED
+                    row_btns_sel_tests = C.STYLE_ENABLED
                 else:
                 else:
-                    row_fig_tput = C.PLACEHOLDER
-                    row_fig_lat = C.PLACEHOLDER
-                    row_table = C.PLACEHOLDER
-                    row_btn_dwnld = C.PLACEHOLDER
+                    plotting_area = C.PLACEHOLDER
                     row_card_sel_tests = C.STYLE_DISABLED
                     row_btns_sel_tests = C.STYLE_DISABLED
                     store_sel = list()
                     row_card_sel_tests = C.STYLE_DISABLED
                     row_btns_sel_tests = C.STYLE_DISABLED
                     store_sel = list()
-                    ctrl_panel.set({"cl-selected-options": list()})
-
-            if ctrl_panel.get("cl-core-value") and \
-                    ctrl_panel.get("cl-framesize-value") and \
-                    ctrl_panel.get("cl-testtype-value"):
-                disabled = False
-            else:
-                disabled = True
-            ctrl_panel.set({
-                "btn-add-disabled": disabled,
-                "cl-normalize-value": norm
-            })
 
             ret_val = [
 
             ret_val = [
-                ctrl_panel.panel, store_sel,
-                row_fig_tput, row_fig_lat, row_table, row_btn_dwnld,
-                row_card_sel_tests, row_btns_sel_tests
+                ctrl_panel.panel,
+                store_sel,
+                plotting_area,
+                row_card_sel_tests,
+                row_btns_sel_tests,
+                lg_selected
             ]
             ]
-            ret_val.extend(ctrl_panel.values())
+            ret_val.extend(ctrl_panel.values)
             return ret_val
 
         @app.callback(
             return ret_val
 
         @app.callback(
-            Output("download-data", "data"),
-            State("selected-tests", "data"),
-            Input("btn-download-data", "n_clicks"),
+            Output("plot-mod-url", "is_open"),
+            [Input("plot-btn-url", "n_clicks")],
+            [State("plot-mod-url", "is_open")],
+        )
+        def toggle_plot_mod_url(n, is_open):
+            """Toggle the modal window with url.
+            """
+            if n:
+                return not is_open
+            return is_open
+
+        @app.callback(
+            Output("download-iterative-data", "data"),
+            State("store-selected-tests", "data"),
+            Input("plot-btn-download", "n_clicks"),
             prevent_initial_call=True
         )
             prevent_initial_call=True
         )
-        def _download_data(store_sel, n_clicks):
+        def _download_trending_data(store_sel, _):
             """Download the data
 
             :param store_sel: List of tests selected by user stored in the
                 browser.
             """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 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
             """
 
             :returns: dict of data frame content (base64 encoded) and meta data
                 used by the Download component.
             :rtype: dict
             """
 
-            if not n_clicks:
-                raise PreventUpdate
-
             if not store_sel:
                 raise PreventUpdate
 
             df = pd.DataFrame()
             for itm in store_sel:
             if not store_sel:
                 raise PreventUpdate
 
             df = pd.DataFrame()
             for itm in store_sel:
-                sel_data = select_iterative_data(self.data, itm)
+                sel_data = select_iterative_data(self._data, itm)
                 if sel_data is None:
                     continue
                 df = pd.concat([df, sel_data], ignore_index=True)
                 if sel_data is None:
                     continue
                 df = pd.concat([df, sel_data], ignore_index=True)
index c4ef13b..14ecdf5 100644 (file)
@@ -31,11 +31,7 @@ plot-throughput:
     b: 0
     l: 80
     r: 20
     b: 0
     l: 80
     r: 20
-  showlegend: True
-  legend:
-    orientation: "h"
-    font:
-      size: 10
+  showlegend: False
   width: 700
   height: 900
   paper_bgcolor: "#fff"
   width: 700
   height: 900
   paper_bgcolor: "#fff"
@@ -76,11 +72,7 @@ plot-latency:
     b: 0
     l: 80
     r: 20
     b: 0
     l: 80
     r: 20
-  showlegend: True
-  legend:
-    orientation: "h"
-    font:
-      size: 10
+  showlegend: False
   width: 700
   height: 900
   paper_bgcolor: "#fff"
   width: 700
   height: 900
   paper_bgcolor: "#fff"
index 8c86600..8a206f1 100644 (file)
@@ -26,9 +26,9 @@ from dash import Input, Output, State
 from dash.exceptions import PreventUpdate
 from yaml import load, FullLoader, YAMLError
 from datetime import datetime
 from dash.exceptions import PreventUpdate
 from yaml import load, FullLoader, YAMLError
 from datetime import datetime
-from copy import deepcopy
 
 from ..utils.constants import Constants as C
 
 from ..utils.constants import Constants as C
+from ..utils.control_panel import ControlPanel
 from ..utils.utils import show_tooltip, gen_new_url, get_ttypes, get_cadences, \
     get_test_beds, get_job, generate_options, set_job_params
 from ..utils.url_processing import url_decode
 from ..utils.utils import show_tooltip, gen_new_url, get_ttypes, get_cadences, \
     get_test_beds, get_job, generate_options, set_job_params
 from ..utils.url_processing import url_decode
@@ -111,9 +111,9 @@ class Layout:
             d_job_info["ttype"].append(lst_job[3])
             d_job_info["cadence"].append(lst_job[4])
             d_job_info["tbed"].append("-".join(lst_job[-2:]))
             d_job_info["ttype"].append(lst_job[3])
             d_job_info["cadence"].append(lst_job[4])
             d_job_info["tbed"].append("-".join(lst_job[-2:]))
-        self.job_info = pd.DataFrame.from_dict(d_job_info)
+        self._job_info = pd.DataFrame.from_dict(d_job_info)
 
 
-        self._default = set_job_params(self.job_info, C.STATS_DEFAULT_JOB)
+        self._default = set_job_params(self._job_info, C.STATS_DEFAULT_JOB)
 
         tst_info = {
             "job": list(),
 
         tst_info = {
             "job": list(),
@@ -159,7 +159,7 @@ class Layout:
         self._data = data_stats.merge(pd.DataFrame.from_dict(tst_info))
 
         # Read from files:
         self._data = data_stats.merge(pd.DataFrame.from_dict(tst_info))
 
         # Read from files:
-        self._html_layout = ""
+        self._html_layout = str()
         self._graph_layout = None
         self._tooltips = dict()
 
         self._graph_layout = None
         self._tooltips = dict()
 
@@ -198,35 +198,26 @@ class Layout:
                 f"{self._tooltip_file}\n{err}"
             )
 
                 f"{self._tooltip_file}\n{err}"
             )
 
-
-        self._default_fig_passed, self._default_fig_duration = graph_statistics(
-            self.data, self._default["job"], self.layout
-        )
+        # Control panel partameters and their default values.
+        self._cp_default = {
+            "ri-ttypes-options": self._default["ttypes"],
+            "ri-cadences-options": self._default["cadences"],
+            "dd-tbeds-options": self._default["tbeds"],
+            "ri-duts-value": self._default["dut"],
+            "ri-ttypes-value": self._default["ttype"],
+            "ri-cadences-value": self._default["cadence"],
+            "dd-tbeds-value": self._default["tbed"],
+            "al-job-children": self._default["job"]
+        }
 
         # Callbacks:
 
         # Callbacks:
-        if self._app is not None and hasattr(self, 'callbacks'):
+        if self._app is not None and hasattr(self, "callbacks"):
             self.callbacks(self._app)
 
     @property
     def html_layout(self) -> dict:
         return self._html_layout
 
             self.callbacks(self._app)
 
     @property
     def html_layout(self) -> dict:
         return self._html_layout
 
-    @property
-    def data(self) -> pd.DataFrame:
-        return self._data
-
-    @property
-    def layout(self) -> dict:
-        return self._graph_layout
-
-    @property
-    def time_period(self) -> int:
-        return self._time_period
-
-    @property
-    def default(self) -> any:
-        return self._default
-
     def add_content(self):
         """Top level method which generated the web page.
 
     def add_content(self):
         """Top level method which generated the web page.
 
@@ -252,7 +243,7 @@ class Layout:
                         id="row-navbar",
                         class_name="g-0",
                         children=[
                         id="row-navbar",
                         class_name="g-0",
                         children=[
-                            self._add_navbar(),
+                            self._add_navbar()
                         ]
                     ),
                     dcc.Loading(
                         ]
                     ),
                     dcc.Loading(
@@ -272,7 +263,7 @@ class Layout:
                         class_name="g-0",
                         children=[
                             self._add_ctrl_col(),
                         class_name="g-0",
                         children=[
                             self._add_ctrl_col(),
-                            self._add_plotting_col(),
+                            self._add_plotting_col()
                         ]
                     )
                 ]
                         ]
                     )
                 ]
@@ -285,8 +276,8 @@ class Layout:
                         [
                             "An Error Occured",
                         ],
                         [
                             "An Error Occured",
                         ],
-                        color="danger",
-                    ),
+                        color="danger"
+                    )
                 ]
             )
 
                 ]
             )
 
@@ -312,7 +303,7 @@ class Layout:
             brand_href="/",
             brand_external_link=True,
             class_name="p-2",
             brand_href="/",
             brand_external_link=True,
             class_name="p-2",
-            fluid=True,
+            fluid=True
         )
 
     def _add_ctrl_col(self) -> dbc.Col:
         )
 
     def _add_ctrl_col(self) -> dbc.Col:
@@ -337,79 +328,19 @@ class Layout:
         return dbc.Col(
             id="col-plotting-area",
             children=[
         return dbc.Col(
             id="col-plotting-area",
             children=[
-                dbc.Row(  # Passed / failed tests
-                    id="row-graph-passed",
-                    class_name="g-0 p-2",
+                dcc.Loading(
                     children=[
                     children=[
-                        dcc.Loading(children=[
-                            dcc.Graph(
-                                id="graph-passed",
-                                figure=self._default_fig_passed
-                            )
-                        ])
-                    ]
-                ),
-                dbc.Row(  # Duration
-                    id="row-graph-duration",
-                    class_name="g-0 p-2",
-                    children=[
-                        dcc.Loading(children=[
-                            dcc.Graph(
-                                id="graph-duration",
-                                figure=self._default_fig_duration
-                            )
-                        ])
-                    ]
-                ),
-                dbc.Row(
-                    class_name="g-0 p-2",
-                    align="center",
-                    justify="start",
-                    children=[
-                        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,
+                        dbc.Row(
+                            id="plotting-area",
+                            class_name="g-0 p-0",
                             children=[
                             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=""
-                                        )
-                                    ]
-                                )
+                                C.PLACEHOLDER
                             ]
                         )
                     ]
                 )
             ],
                             ]
                         )
                     ]
                 )
             ],
-            width=9,
+            width=9
         )
 
     def _add_ctrl_panel(self) -> dbc.Row:
         )
 
     def _add_ctrl_panel(self) -> dbc.Row:
@@ -423,14 +354,18 @@ class Layout:
                 class_name="g-0 p-1",
                 children=[
                     dbc.Label(
                 class_name="g-0 p-1",
                 children=[
                     dbc.Label(
-                        children=show_tooltip(self._tooltips,
-                            "help-dut", "Device under Test")
+                        children=show_tooltip(
+                            self._tooltips,
+                            "help-dut",
+                            "Device under Test"
+                        )
                     ),
                     dbc.RadioItems(
                         id="ri-duts",
                         inline=True,
                     ),
                     dbc.RadioItems(
                         id="ri-duts",
                         inline=True,
-                        value=self.default["dut"],
-                        options=self.default["duts"]
+                        value=self._default["dut"],
+                        options=self._default["duts"],
+                        input_class_name="border-info bg-info"
                     )
                 ]
             ),
                     )
                 ]
             ),
@@ -438,14 +373,18 @@ class Layout:
                 class_name="g-0 p-1",
                 children=[
                     dbc.Label(
                 class_name="g-0 p-1",
                 children=[
                     dbc.Label(
-                        children=show_tooltip(self._tooltips,
-                            "help-ttype", "Test Type"),
+                        children=show_tooltip(
+                            self._tooltips,
+                            "help-ttype",
+                            "Test Type"
+                        )
                     ),
                     dbc.RadioItems(
                         id="ri-ttypes",
                         inline=True,
                     ),
                     dbc.RadioItems(
                         id="ri-ttypes",
                         inline=True,
-                        value=self.default["ttype"],
-                        options=self.default["ttypes"]
+                        value=self._default["ttype"],
+                        options=self._default["ttypes"],
+                        input_class_name="border-info bg-info"
                     )
                 ]
             ),
                     )
                 ]
             ),
@@ -453,14 +392,18 @@ class Layout:
                 class_name="g-0 p-1",
                 children=[
                     dbc.Label(
                 class_name="g-0 p-1",
                 children=[
                     dbc.Label(
-                        children=show_tooltip(self._tooltips,
-                            "help-cadence", "Cadence"),
+                        children=show_tooltip(
+                            self._tooltips,
+                            "help-cadence",
+                            "Cadence"
+                        )
                     ),
                     dbc.RadioItems(
                         id="ri-cadences",
                         inline=True,
                     ),
                     dbc.RadioItems(
                         id="ri-cadences",
                         inline=True,
-                        value=self.default["cadence"],
-                        options=self.default["cadences"]
+                        value=self._default["cadence"],
+                        options=self._default["cadences"],
+                        input_class_name="border-info bg-info"
                     )
                 ]
             ),
                     )
                 ]
             ),
@@ -468,14 +411,17 @@ class Layout:
                 class_name="g-0 p-1",
                 children=[
                     dbc.Label(
                 class_name="g-0 p-1",
                 children=[
                     dbc.Label(
-                        children=show_tooltip(self._tooltips,
-                            "help-tbed", "Test Bed"),
+                        children=show_tooltip(
+                            self._tooltips,
+                            "help-tbed",
+                            "Test Bed"
+                        )
                     ),
                     dbc.Select(
                         id="dd-tbeds",
                         placeholder="Select a test bed...",
                     ),
                     dbc.Select(
                         id="dd-tbeds",
                         placeholder="Select a test bed...",
-                        value=self.default["tbed"],
-                        options=self.default["tbeds"]
+                        value=self._default["tbed"],
+                        options=self._default["tbeds"]
                     )
                 ]
             ),
                     )
                 ]
             ),
@@ -485,84 +431,97 @@ class Layout:
                     dbc.Alert(
                         id="al-job",
                         color="info",
                     dbc.Alert(
                         id="al-job",
                         color="info",
-                        children=self.default["job"]
+                        children=self._default["job"]
                     )
                 ]
             )
         ]
 
                     )
                 ]
             )
         ]
 
-    class ControlPanel:
-        """A class representing the control panel.
+    def _get_plotting_area(
+            self,
+            job: str,
+            url: str
+        ) -> list:
+        """Generate the plotting area with all its content.
+
+        :param job: The job which data will be displayed.
+        :param url: URL to be displayed in the modal window.
+        :type job: str
+        :type url: str
+        :returns: List of rows with elements to be displayed in the plotting
+            area.
+        :rtype: list
         """
 
         """
 
-        def __init__(self, panel: dict, default: 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
-            """
-
-            self._defaults = {
-                "ri-ttypes-options": default["ttypes"],
-                "ri-cadences-options": default["cadences"],
-                "dd-tbeds-options": default["tbeds"],
-                "ri-duts-value": default["dut"],
-                "ri-ttypes-value": default["ttype"],
-                "ri-cadences-value": default["cadence"],
-                "dd-tbeds-value": default["tbed"],
-                "al-job-children": default["job"]
-            }
-            self._panel = deepcopy(self._defaults)
-            if panel:
-                for key in self._defaults:
-                    self._panel[key] = panel[key]
-
-        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.")
-
-        @property
-        def defaults(self) -> dict:
-            return self._defaults
-
-        @property
-        def panel(self) -> dict:
-            return self._panel
-
-        def get(self, key: str) -> any:
-            """Returns the value of a key from the Control panel.
-
-            :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]
+        figs = graph_statistics(self._data, job, self._graph_layout)
 
 
-        def values(self) -> list:
-            """Returns the values from the Control panel as a list.
-
-            :returns: The values from the Control panel.
-            :rtype: list
-            """
-            return list(self._panel.values())
+        if not figs[0]:
+            return C.PLACEHOLDER
 
 
+        return [
+            dbc.Row(
+                id="row-graph-passed",
+                class_name="g-0 p-1",
+                children=[
+                    dcc.Graph(
+                        id="graph-passed",
+                        figure=figs[0]
+                    )
+                ]
+            ),
+            dbc.Row(
+                id="row-graph-duration",
+                class_name="g-0 p-1",
+                children=[
+                    dcc.Graph(
+                        id="graph-duration",
+                        figure=figs[1]
+                    )
+                ]
+            ),
+            dbc.Row(
+                [
+                    dbc.Col([html.Div(
+                        [
+                            dbc.Button(
+                                id="plot-btn-url",
+                                children="URL",
+                                class_name="me-1",
+                                color="info",
+                                style={
+                                    "text-transform": "none",
+                                    "padding": "0rem 1rem"
+                                }
+                            ),
+                            dbc.Modal(
+                                [
+                                    dbc.ModalHeader(dbc.ModalTitle("URL")),
+                                    dbc.ModalBody(url)
+                                ],
+                                id="plot-mod-url",
+                                size="xl",
+                                is_open=False,
+                                scrollable=True
+                            ),
+                            dbc.Button(
+                                id="plot-btn-download",
+                                children="Download Data",
+                                class_name="me-1",
+                                color="info",
+                                style={
+                                    "text-transform": "none",
+                                    "padding": "0rem 1rem"
+                                }
+                            ),
+                            dcc.Download(id="download-stats-data")
+                        ],
+                        className=\
+                            "d-grid gap-0 d-md-flex justify-content-md-end"
+                    )])
+                ],
+                class_name="g-0 p-0"
+            )
+        ]
 
     def callbacks(self, app):
         """Callbacks for the whole application.
 
     def callbacks(self, app):
         """Callbacks for the whole application.
@@ -573,9 +532,7 @@ class Layout:
 
         @app.callback(
             Output("control-panel", "data"),  # Store
 
         @app.callback(
             Output("control-panel", "data"),  # Store
-            Output("graph-passed", "figure"),
-            Output("graph-duration", "figure"),
-            Output("input-url", "value"),
+            Output("plotting-area", "children"),
             Output("ri-ttypes", "options"),
             Output("ri-cadences", "options"),
             Output("dd-tbeds", "options"),
             Output("ri-ttypes", "options"),
             Output("ri-cadences", "options"),
             Output("dd-tbeds", "options"),
@@ -612,7 +569,7 @@ class Layout:
             :rtype: tuple
             """
 
             :rtype: tuple
             """
 
-            ctrl_panel = self.ControlPanel(cp_data, self.default)
+            ctrl_panel = ControlPanel(self._cp_default, cp_data)
 
             # Parse the url:
             parsed_url = url_decode(href)
 
             # Parse the url:
             parsed_url = url_decode(href)
@@ -623,13 +580,13 @@ class Layout:
 
             trigger_id = callback_context.triggered[0]["prop_id"].split(".")[0]
             if trigger_id == "ri-duts":
 
             trigger_id = callback_context.triggered[0]["prop_id"].split(".")[0]
             if trigger_id == "ri-duts":
-                ttype_opts = generate_options(get_ttypes(self.job_info, dut))
+                ttype_opts = generate_options(get_ttypes(self._job_info, dut))
                 ttype_val = ttype_opts[0]["value"]
                 cad_opts = generate_options(get_cadences(
                 ttype_val = ttype_opts[0]["value"]
                 cad_opts = generate_options(get_cadences(
-                    self.job_info, dut, ttype_val))
+                    self._job_info, dut, ttype_val))
                 cad_val = cad_opts[0]["value"]
                 tbed_opts = generate_options(get_test_beds(
                 cad_val = cad_opts[0]["value"]
                 tbed_opts = generate_options(get_test_beds(
-                    self.job_info, dut, ttype_val, cad_val))
+                    self._job_info, dut, ttype_val, cad_val))
                 tbed_val = tbed_opts[0]["value"]
                 ctrl_panel.set({
                     "ri-duts-value": dut,
                 tbed_val = tbed_opts[0]["value"]
                 ctrl_panel.set({
                     "ri-duts-value": dut,
@@ -642,10 +599,10 @@ class Layout:
                 })
             elif trigger_id == "ri-ttypes":
                 cad_opts = generate_options(get_cadences(
                 })
             elif trigger_id == "ri-ttypes":
                 cad_opts = generate_options(get_cadences(
-                    self.job_info, ctrl_panel.get("ri-duts-value"), ttype))
+                    self._job_info, ctrl_panel.get("ri-duts-value"), ttype))
                 cad_val = cad_opts[0]["value"]
                 tbed_opts = generate_options(get_test_beds(
                 cad_val = cad_opts[0]["value"]
                 tbed_opts = generate_options(get_test_beds(
-                    self.job_info, ctrl_panel.get("ri-duts-value"), ttype,
+                    self._job_info, ctrl_panel.get("ri-duts-value"), ttype,
                     cad_val))
                 tbed_val = tbed_opts[0]["value"]
                 ctrl_panel.set({
                     cad_val))
                 tbed_val = tbed_opts[0]["value"]
                 ctrl_panel.set({
@@ -657,7 +614,7 @@ class Layout:
                 })
             elif trigger_id == "ri-cadences":
                 tbed_opts = generate_options(get_test_beds(
                 })
             elif trigger_id == "ri-cadences":
                 tbed_opts = generate_options(get_test_beds(
-                    self.job_info, ctrl_panel.get("ri-duts-value"),
+                    self._job_info, ctrl_panel.get("ri-duts-value"),
                     ctrl_panel.get("ri-ttypes-value"), cadence))
                 tbed_val = tbed_opts[0]["value"]
                 ctrl_panel.set({
                     ctrl_panel.get("ri-ttypes-value"), cadence))
                 tbed_val = tbed_opts[0]["value"]
                 ctrl_panel.set({
@@ -673,13 +630,25 @@ class Layout:
                 if url_params:
                     new_job = url_params.get("job", list())[0]
                     if new_job:
                 if url_params:
                     new_job = url_params.get("job", list())[0]
                     if new_job:
-                        job_params = set_job_params(self.job_info, new_job)
-                        ctrl_panel = self.ControlPanel(None, job_params)
+                        job_params = set_job_params(self._job_info, new_job)
+                        ctrl_panel = ControlPanel(
+                            {
+                                "ri-ttypes-options": job_params["ttypes"],
+                                "ri-cadences-options": job_params["cadences"],
+                                "dd-tbeds-options": job_params["tbeds"],
+                                "ri-duts-value": job_params["dut"],
+                                "ri-ttypes-value": job_params["ttype"],
+                                "ri-cadences-value": job_params["cadence"],
+                                "dd-tbeds-value": job_params["tbed"],
+                                "al-job-children": job_params["job"]
+                            },
+                            None
+                        )
                 else:
                 else:
-                    ctrl_panel = self.ControlPanel(cp_data, self.default)
+                    ctrl_panel = ControlPanel(self._cp_default, cp_data)
 
             job = get_job(
 
             job = get_job(
-                self.job_info,
+                self._job_info,
                 ctrl_panel.get("ri-duts-value"),
                 ctrl_panel.get("ri-ttypes-value"),
                 ctrl_panel.get("ri-cadences-value"),
                 ctrl_panel.get("ri-duts-value"),
                 ctrl_panel.get("ri-ttypes-value"),
                 ctrl_panel.get("ri-cadences-value"),
@@ -687,22 +656,34 @@ class Layout:
             )
 
             ctrl_panel.set({"al-job-children": job})
             )
 
             ctrl_panel.set({"al-job-children": job})
-            fig_passed, fig_duration = \
-                graph_statistics(self.data, job, self.layout)
+            plotting_area = self._get_plotting_area(
+                job,
+                gen_new_url(parsed_url, {"job": job})
+            )
 
             ret_val = [
                 ctrl_panel.panel,
 
             ret_val = [
                 ctrl_panel.panel,
-                fig_passed,
-                fig_duration,
-                gen_new_url(parsed_url, {"job": job})
+                plotting_area
             ]
             ]
-            ret_val.extend(ctrl_panel.values())
+            ret_val.extend(ctrl_panel.values)
             return ret_val
 
         @app.callback(
             return ret_val
 
         @app.callback(
-            Output("download-data", "data"),
+            Output("plot-mod-url", "is_open"),
+            [Input("plot-btn-url", "n_clicks")],
+            [State("plot-mod-url", "is_open")],
+        )
+        def toggle_plot_mod_url(n, is_open):
+            """Toggle the modal window with url.
+            """
+            if n:
+                return not is_open
+            return is_open
+
+        @app.callback(
+            Output("download-stats-data", "data"),
             State("control-panel", "data"),  # Store
             State("control-panel", "data"),  # Store
-            Input("btn-download-data", "n_clicks"),
+            Input("plot-btn-download", "n_clicks"),
             prevent_initial_call=True
         )
         def _download_data(cp_data: dict, n_clicks: int):
             prevent_initial_call=True
         )
         def _download_data(cp_data: dict, n_clicks: int):
@@ -717,20 +698,20 @@ class Layout:
                 used by the Download component.
             :rtype: dict
             """
                 used by the Download component.
             :rtype: dict
             """
-            if not (n_clicks):
+            if not n_clicks:
                 raise PreventUpdate
 
                 raise PreventUpdate
 
-            ctrl_panel = self.ControlPanel(cp_data, self.default)
+            ctrl_panel = ControlPanel(self._cp_default, cp_data)
 
             job = get_job(
 
             job = get_job(
-                self.job_info,
+                self._job_info,
                 ctrl_panel.get("ri-duts-value"),
                 ctrl_panel.get("ri-ttypes-value"),
                 ctrl_panel.get("ri-cadences-value"),
                 ctrl_panel.get("dd-tbeds-value")
             )
 
                 ctrl_panel.get("ri-duts-value"),
                 ctrl_panel.get("ri-ttypes-value"),
                 ctrl_panel.get("ri-cadences-value"),
                 ctrl_panel.get("dd-tbeds-value")
             )
 
-            data = select_data(self.data, job)
+            data = select_data(self._data, job)
             data = data.drop(columns=["job", ])
 
             return dcc.send_data_frame(
             data = data.drop(columns=["job", ])
 
             return dcc.send_data_frame(
@@ -782,9 +763,9 @@ class Layout:
                         job, build = itm.split(" ")[-1].split("/")
                         break
                 if job and build:
                         job, build = itm.split(" ")[-1].split("/")
                         break
                 if job and build:
-                    fail_tests = self.data.loc[
-                        (self.data["job"] == job) &
-                        (self.data["build"] == build)
+                    fail_tests = self._data.loc[
+                        (self._data["job"] == job) &
+                        (self._data["build"] == build)
                     ]["lst_failed"].values[0]
                     if not fail_tests:
                         fail_tests = None
                     ]["lst_failed"].values[0]
                     if not fail_tests:
                         fail_tests = None
index e0bbe88..eb7fc17 100644 (file)
@@ -5,25 +5,41 @@
 
   <header class="mb-auto">
     <div>
 
   <header class="mb-auto">
     <div>
-      <h3 class="float-md-start mb-0 text-white">CSIT-Dash</h3>
+      <h3 class="float-md-start mb-0 text-white">
+        CSIT-Dash
+      </h3>
     </div>
   </header>
 
   <main class="px-3">
     <img class="d-block mx-auto mb-4" src="{{ url_for('static', filename='img/logo.svg') }}" alt="" width="72" height="57">
     </div>
   </header>
 
   <main class="px-3">
     <img class="d-block mx-auto mb-4" src="{{ url_for('static', filename='img/logo.svg') }}" alt="" width="72" height="57">
-    <h1 class="text-white">{{ title }}</h1>
-    <p class="lead">{{ description }}</p>
-    <p class="lead">
-      <a href="/trending/" class="btn btn-primary fw-bold">{{ trending_title }}</a>
-    </p>
-    <p class="lead">
-      <a href="/report/" class="btn btn-primary fw-bold">{{ report_title }}</a>
-    </p>
-    <p class="lead">
-      <a href="/stats/" class="btn btn-primary fw-bold">{{ stats_title }}</a>
+    <h1 class="text-white">
+      {{ title }}
+    </h1>
+    <p class="lead"> 
+      {{ description }}
     </p>
     <p class="lead">
     </p>
     <p class="lead">
-      <a href="/news/" class="btn btn-primary fw-bold">{{ news_title }}</a>
+      <p>
+        <a href="/trending/" class="btn btn-primary fw-bold w-25">
+          {{ trending_title }}
+        </a>
+      </p>
+      <p>
+        <a href="/report/" class="btn btn-primary fw-bold w-25"> 
+          {{ report_title }}
+        </a>
+      </p>
+      <p>
+        <a href="/stats/" class="btn btn-primary fw-bold w-25">
+          {{ stats_title }}
+        </a>
+      </p>
+      <p>
+        <a href="/news/" class="btn btn-primary fw-bold w-25">
+          {{ news_title }}
+        </a>
+      </p>
     </p>
   </main>
 
     </p>
   </main>
 
index 14493ff..a8f6b6a 100644 (file)
@@ -11,6 +11,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+
 """Plotly Dash HTML layout override.
 """
 
 """Plotly Dash HTML layout override.
 """
 
@@ -25,18 +26,45 @@ 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 dash import Input, Output, State
 from dash.exceptions import PreventUpdate
 from yaml import load, FullLoader, YAMLError
-from datetime import datetime
-from copy import deepcopy
-from json import loads, JSONDecodeError
 from ast import literal_eval
 
 from ..utils.constants import Constants as C
 from ast import literal_eval
 
 from ..utils.constants import Constants as C
-from ..utils.utils import show_tooltip, label, sync_checklists, list_tests, \
-    gen_new_url, generate_options
+from ..utils.control_panel import ControlPanel
+from ..utils.trigger import Trigger
+from ..utils.utils import show_tooltip, label, sync_checklists, gen_new_url, \
+    generate_options, get_list_group_items
 from ..utils.url_processing import url_decode
 from ..data.data import Data
 from ..utils.url_processing import url_decode
 from ..data.data import Data
-from .graphs import graph_trending, graph_hdrh_latency, \
-    select_trending_data
+from .graphs import graph_trending, graph_hdrh_latency, select_trending_data
+
+
+# Control panel partameters and their default values.
+CP_PARAMS = {
+    "dd-dut-val": str(),
+    "dd-phy-opt": list(),
+    "dd-phy-dis": True,
+    "dd-phy-val": str(),
+    "dd-area-opt": list(),
+    "dd-area-dis": True,
+    "dd-area-val": str(),
+    "dd-test-opt": list(),
+    "dd-test-dis": True,
+    "dd-test-val": str(),
+    "cl-core-opt": list(),
+    "cl-core-val": list(),
+    "cl-core-all-val": list(),
+    "cl-core-all-opt": C.CL_ALL_DISABLED,
+    "cl-frmsize-opt": list(),
+    "cl-frmsize-val": list(),
+    "cl-frmsize-all-val": list(),
+    "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
+    "cl-tsttype-opt": list(),
+    "cl-tsttype-val": list(),
+    "cl-tsttype-all-val": list(),
+    "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
+    "btn-add-dis": True,
+    "cl-normalize-val": list()
+}
 
 
 class Layout:
 
 
 class Layout:
@@ -93,12 +121,6 @@ class Layout:
 
         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().\
         # Get structure of tests:
         tbs = dict()
         for _, row in self._data[["job", "test_id"]].drop_duplicates().\
@@ -148,18 +170,20 @@ class Layout:
             if framesize.upper() not in \
                     tbs[dut][infra][area][test]["frame-size"]:
                 tbs[dut][infra][area][test]["frame-size"].append(
             if framesize.upper() not in \
                     tbs[dut][infra][area][test]["frame-size"]:
                 tbs[dut][infra][area][test]["frame-size"].append(
-                    framesize.upper())
+                    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(
             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"))
+                        ("NDR", "PDR")
+                    )
         self._spec_tbs = tbs
 
         # Read from files:
         self._spec_tbs = tbs
 
         # Read from files:
-        self._html_layout = ""
+        self._html_layout = str()
         self._graph_layout = None
         self._tooltips = dict()
 
         self._graph_layout = None
         self._tooltips = dict()
 
@@ -199,29 +223,13 @@ class Layout:
             )
 
         # Callbacks:
             )
 
         # Callbacks:
-        if self._app is not None and hasattr(self, 'callbacks'):
+        if self._app is not None and hasattr(self, "callbacks"):
             self.callbacks(self._app)
 
     @property
     def html_layout(self):
         return self._html_layout
 
             self.callbacks(self._app)
 
     @property
     def html_layout(self):
         return self._html_layout
 
-    @property
-    def spec_tbs(self):
-        return self._spec_tbs
-
-    @property
-    def data(self):
-        return self._data
-
-    @property
-    def layout(self):
-        return self._graph_layout
-
-    @property
-    def time_period(self):
-        return self._time_period
-
     def add_content(self):
         """Top level method which generated the web page.
 
     def add_content(self):
         """Top level method which generated the web page.
 
@@ -236,7 +244,7 @@ class Layout:
         :rtype: html.Div
         """
 
         :rtype: html.Div
         """
 
-        if self.html_layout and self.spec_tbs:
+        if self.html_layout and self._spec_tbs:
             return html.Div(
                 id="div-main",
                 className="small",
             return html.Div(
                 id="div-main",
                 className="small",
@@ -245,7 +253,7 @@ class Layout:
                         id="row-navbar",
                         class_name="g-0",
                         children=[
                         id="row-navbar",
                         class_name="g-0",
                         children=[
-                            self._add_navbar(),
+                            self._add_navbar()
                         ]
                     ),
                     dcc.Loading(
                         ]
                     ),
                     dcc.Loading(
@@ -257,7 +265,7 @@ class Layout:
                             is_open=False,
                             children=[
                                 dbc.Row(id="metadata-tput-lat"),
                             is_open=False,
                             children=[
                                 dbc.Row(id="metadata-tput-lat"),
-                                dbc.Row(id="metadata-hdrh-graph"),
+                                dbc.Row(id="metadata-hdrh-graph")
                             ]
                         )
                     ),
                             ]
                         )
                     ),
@@ -265,11 +273,11 @@ class Layout:
                         id="row-main",
                         class_name="g-0",
                         children=[
                         id="row-main",
                         class_name="g-0",
                         children=[
-                            dcc.Store(id="selected-tests"),
-                            dcc.Store(id="control-panel"),
+                            dcc.Store(id="store-selected-tests"),
+                            dcc.Store(id="store-control-panel"),
                             dcc.Location(id="url", refresh=False),
                             self._add_ctrl_col(),
                             dcc.Location(id="url", refresh=False),
                             self._add_ctrl_col(),
-                            self._add_plotting_col(),
+                            self._add_plotting_col()
                         ]
                     )
                 ]
                         ]
                     )
                 ]
@@ -280,10 +288,10 @@ class Layout:
                 children=[
                     dbc.Alert(
                         [
                 children=[
                     dbc.Alert(
                         [
-                            "An Error Occured",
+                            "An Error Occured"
                         ],
                         ],
-                        color="danger",
-                    ),
+                        color="danger"
+                    )
                 ]
             )
 
                 ]
             )
 
@@ -309,7 +317,7 @@ class Layout:
             brand_href="/",
             brand_external_link=True,
             class_name="p-2",
             brand_href="/",
             brand_external_link=True,
             class_name="p-2",
-            fluid=True,
+            fluid=True
         )
 
     def _add_ctrl_col(self) -> dbc.Col:
         )
 
     def _add_ctrl_col(self) -> dbc.Col:
@@ -336,23 +344,9 @@ class Layout:
             children=[
                 dcc.Loading(
                     children=[
             children=[
                 dcc.Loading(
                     children=[
-                        dbc.Row(  # Throughput
-                            id="row-graph-tput",
-                            class_name="g-0 p-2",
-                            children=[
-                                C.PLACEHOLDER
-                            ]
-                        ),
-                        dbc.Row(  # Latency
-                            id="row-graph-lat",
-                            class_name="g-0 p-2",
-                            children=[
-                                C.PLACEHOLDER
-                            ]
-                        ),
-                        dbc.Row(  # Download
-                            id="row-btn-download",
-                            class_name="g-0 p-2",
+                        dbc.Row(
+                            id="plotting-area",
+                            class_name="g-0 p-0",
                             children=[
                                 C.PLACEHOLDER
                             ]
                             children=[
                                 C.PLACEHOLDER
                             ]
@@ -360,14 +354,14 @@ class Layout:
                     ]
                 )
             ],
                     ]
                 )
             ],
-            width=9,
+            width=9
         )
 
         )
 
-    def _add_ctrl_panel(self) -> dbc.Row:
+    def _add_ctrl_panel(self) -> list:
         """Add control panel.
 
         :returns: Control panel.
         """Add control panel.
 
         :returns: Control panel.
-        :rtype: dbc.Row
+        :rtype: list
         """
         return [
             dbc.Row(
         """
         return [
             dbc.Row(
@@ -376,25 +370,26 @@ class Layout:
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
-                                children=show_tooltip(self._tooltips,
-                                    "help-dut", "DUT")
+                                children=show_tooltip(
+                                    self._tooltips,
+                                    "help-dut",
+                                    "DUT"
+                                )
                             ),
                             dbc.Select(
                             ),
                             dbc.Select(
-                                id="dd-ctrl-dut",
-                                placeholder=(
-                                    "Select a Device under Test..."
-                                ),
+                                id={"type": "ctrl-dd", "index": "dut"},
+                                placeholder="Select a Device under Test...",
                                 options=sorted(
                                     [
                                         {"label": k, "value": k} \
                                 options=sorted(
                                     [
                                         {"label": k, "value": k} \
-                                            for k in self.spec_tbs.keys()
+                                            for k in self._spec_tbs.keys()
                                     ],
                                     key=lambda d: d["label"]
                                 )
                             )
                         ],
                                     ],
                                     key=lambda d: d["label"]
                                 )
                             )
                         ],
-                        size="sm",
-                    ),
+                        size="sm"
+                    )
                 ]
             ),
             dbc.Row(
                 ]
             ),
             dbc.Row(
@@ -403,19 +398,20 @@ class Layout:
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
-                                children=show_tooltip(self._tooltips,
-                                    "help-infra", "Infra")
+                                children=show_tooltip(
+                                    self._tooltips,
+                                    "help-infra",
+                                    "Infra"
+                                )
                             ),
                             dbc.Select(
                             ),
                             dbc.Select(
-                                id="dd-ctrl-phy",
-                                placeholder=(
-                                    "Select a Physical Test Bed "
-                                    "Topology..."
-                                )
+                                id={"type": "ctrl-dd", "index": "phy"},
+                                placeholder=\
+                                    "Select a Physical Test Bed Topology..."
                             )
                         ],
                             )
                         ],
-                        size="sm",
-                    ),
+                        size="sm"
+                    )
                 ]
             ),
             dbc.Row(
                 ]
             ),
             dbc.Row(
@@ -424,17 +420,19 @@ class Layout:
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
-                                children=show_tooltip(self._tooltips,
-                                    "help-area", "Area")
+                                children=show_tooltip(
+                                    self._tooltips,
+                                    "help-area",
+                                    "Area"
+                                )
                             ),
                             dbc.Select(
                             ),
                             dbc.Select(
-                                id="dd-ctrl-area",
-                                placeholder="Select an Area...",
-                                disabled=True,
-                            ),
+                                id={"type": "ctrl-dd", "index": "area"},
+                                placeholder="Select an Area..."
+                            )
                         ],
                         ],
-                        size="sm",
-                    ),
+                        size="sm"
+                    )
                 ]
             ),
             dbc.Row(
                 ]
             ),
             dbc.Row(
@@ -443,43 +441,50 @@ class Layout:
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
                     dbc.InputGroup(
                         [
                             dbc.InputGroupText(
-                                children=show_tooltip(self._tooltips,
-                                    "help-test", "Test")
+                                children=show_tooltip(
+                                    self._tooltips,
+                                    "help-test",
+                                    "Test"
+                                )
                             ),
                             dbc.Select(
                             ),
                             dbc.Select(
-                                id="dd-ctrl-test",
-                                placeholder="Select a Test...",
-                                disabled=True,
-                            ),
+                                id={"type": "ctrl-dd", "index": "test"},
+                                placeholder="Select a Test..."
+                            )
                         ],
                         ],
-                        size="sm",
-                    ),
+                        size="sm"
+                    )
                 ]
             ),
             dbc.Row(
                 class_name="g-0 p-1",
                 children=[
                     dbc.Label(
                 ]
             ),
             dbc.Row(
                 class_name="g-0 p-1",
                 children=[
                     dbc.Label(
-                        children=show_tooltip(self._tooltips,
-                            "help-framesize", "Frame Size"),
+                        children=show_tooltip(
+                            self._tooltips,
+                            "help-framesize",
+                            "Frame Size"
+                        )
                     ),
                     dbc.Col(
                         children=[
                             dbc.Checklist(
                     ),
                     dbc.Col(
                         children=[
                             dbc.Checklist(
-                                id="cl-ctrl-framesize-all",
+                                id={"type": "ctrl-cl", "index": "frmsize-all"},
                                 options=C.CL_ALL_DISABLED,
                                 inline=True,
                                 options=C.CL_ALL_DISABLED,
                                 inline=True,
-                                switch=False
-                            ),
+                                switch=False,
+                                input_class_name="border-info bg-info"
+                            )
                         ],
                         width=3
                     ),
                     dbc.Col(
                         children=[
                             dbc.Checklist(
                         ],
                         width=3
                     ),
                     dbc.Col(
                         children=[
                             dbc.Checklist(
-                                id="cl-ctrl-framesize",
+                                id={"type": "ctrl-cl", "index": "frmsize"},
                                 inline=True,
                                 inline=True,
-                                switch=False
+                                switch=False,
+                                input_class_name="border-info bg-info"
                             )
                         ]
                     )
                             )
                         ]
                     )
@@ -489,16 +494,20 @@ class Layout:
                 class_name="g-0 p-1",
                 children=[
                     dbc.Label(
                 class_name="g-0 p-1",
                 children=[
                     dbc.Label(
-                        children=show_tooltip(self._tooltips,
-                            "help-cores", "Number of Cores"),
+                        children=show_tooltip(
+                            self._tooltips,
+                            "help-cores",
+                            "Number of Cores"
+                        )
                     ),
                     dbc.Col(
                         children=[
                             dbc.Checklist(
                     ),
                     dbc.Col(
                         children=[
                             dbc.Checklist(
-                                id="cl-ctrl-core-all",
+                                id={"type": "ctrl-cl", "index": "core-all"},
                                 options=C.CL_ALL_DISABLED,
                                 inline=False,
                                 options=C.CL_ALL_DISABLED,
                                 inline=False,
-                                switch=False
+                                switch=False,
+                                input_class_name="border-info bg-info"
                             )
                         ],
                         width=3
                             )
                         ],
                         width=3
@@ -506,9 +515,10 @@ class Layout:
                     dbc.Col(
                         children=[
                             dbc.Checklist(
                     dbc.Col(
                         children=[
                             dbc.Checklist(
-                                id="cl-ctrl-core",
+                                id={"type": "ctrl-cl", "index": "core"},
                                 inline=True,
                                 inline=True,
-                                switch=False
+                                switch=False,
+                                input_class_name="border-info bg-info"
                             )
                         ]
                     )
                             )
                         ]
                     )
@@ -518,26 +528,31 @@ class Layout:
                 class_name="g-0 p-1",
                 children=[
                     dbc.Label(
                 class_name="g-0 p-1",
                 children=[
                     dbc.Label(
-                        children=show_tooltip(self._tooltips,
-                            "help-ttype", "Test Type"),
+                        children=show_tooltip(
+                            self._tooltips,
+                            "help-ttype",
+                            "Test Type"
+                        )
                     ),
                     dbc.Col(
                         children=[
                             dbc.Checklist(
                     ),
                     dbc.Col(
                         children=[
                             dbc.Checklist(
-                                id="cl-ctrl-testtype-all",
+                                id={"type": "ctrl-cl", "index": "tsttype-all"},
                                 options=C.CL_ALL_DISABLED,
                                 inline=True,
                                 options=C.CL_ALL_DISABLED,
                                 inline=True,
-                                switch=False
-                            ),
+                                switch=False,
+                                input_class_name="border-info bg-info"
+                            )
                         ],
                         width=3
                     ),
                     dbc.Col(
                         children=[
                             dbc.Checklist(
                         ],
                         width=3
                     ),
                     dbc.Col(
                         children=[
                             dbc.Checklist(
-                                id="cl-ctrl-testtype",
+                                id={"type": "ctrl-cl", "index": "tsttype"},
                                 inline=True,
                                 inline=True,
-                                switch=False
+                                switch=False,
+                                input_class_name="border-info bg-info"
                             )
                         ]
                     )
                             )
                         ]
                     )
@@ -547,24 +562,30 @@ class Layout:
                 class_name="g-0 p-1",
                 children=[
                     dbc.Label(
                 class_name="g-0 p-1",
                 children=[
                     dbc.Label(
-                        children=show_tooltip(self._tooltips,
-                            "help-normalize", "Normalize"),
+                        children=show_tooltip(
+                            self._tooltips,
+                            "help-normalize",
+                            "Normalize"
+                        )
                     ),
                     dbc.Col(
                         children=[
                             dbc.Checklist(
                     ),
                     dbc.Col(
                         children=[
                             dbc.Checklist(
-                                id="cl-ctrl-normalize",
-                                options=[{
-                                    "value": "normalize",
-                                    "label": (
-                                        "Normalize results to CPU "
-                                        "frequency 2GHz"
-                                    )
-                                }],
+                                id="normalize",
+                                options=[
+                                    {
+                                        "value": "normalize",
+                                        "label": (
+                                            "Normalize results to CPU "
+                                            "frequency 2GHz"
+                                        )
+                                    }
+                                ],
                                 value=[],
                                 inline=True,
                                 value=[],
                                 inline=True,
-                                switch=False
-                            ),
+                                switch=False,
+                                input_class_name="border-info bg-info"
+                            )
                         ]
                     )
                 ]
                         ]
                     )
                 ]
@@ -572,14 +593,10 @@ class Layout:
             dbc.Row(
                 class_name="g-0 p-1",
                 children=[
             dbc.Row(
                 class_name="g-0 p-1",
                 children=[
-                    dbc.ButtonGroup(
-                        [
-                            dbc.Button(
-                                id="btn-ctrl-add",
-                                children="Add Selected",
-                                color="info"
-                            )
-                        ]
+                    dbc.Button(
+                        id={"type": "ctrl-btn", "index": "add-test"},
+                        children="Add Selected",
+                        color="info"
                     )
                 ]
             ),
                     )
                 ]
             ),
@@ -588,15 +605,14 @@ class Layout:
                 class_name="g-0 p-1",
                 style=C.STYLE_DISABLED,
                 children=[
                 class_name="g-0 p-1",
                 style=C.STYLE_DISABLED,
                 children=[
-                    dbc.Label("Selected tests"),
-                    dbc.Checklist(
-                        class_name="overflow-auto",
-                        id="cl-selected",
-                        options=[],
-                        inline=False,
-                        style={"max-height": "12em"},
+                    dbc.ListGroup(
+                        class_name="overflow-auto p-0",
+                        id="lg-selected",
+                        children=[],
+                        style={"max-height": "14em"},
+                        flush=True
                     )
                     )
-                ],
+                ]
             ),
             dbc.Row(
                 id="row-btns-sel-tests",
             ),
             dbc.Row(
                 id="row-btns-sel-tests",
@@ -606,115 +622,156 @@ class Layout:
                     dbc.ButtonGroup(
                         children=[
                             dbc.Button(
                     dbc.ButtonGroup(
                         children=[
                             dbc.Button(
-                                id="btn-sel-remove",
+                                id={"type": "ctrl-btn", "index": "rm-test"},
                                 children="Remove Selected",
                                 class_name="w-100",
                                 color="info",
                                 disabled=False
                             ),
                             dbc.Button(
                                 children="Remove Selected",
                                 class_name="w-100",
                                 color="info",
                                 disabled=False
                             ),
                             dbc.Button(
-                                id="btn-sel-remove-all",
+                                id={"type": "ctrl-btn", "index": "rm-test-all"},
                                 children="Remove All",
                                 class_name="w-100",
                                 color="info",
                                 disabled=False
                                 children="Remove All",
                                 class_name="w-100",
                                 color="info",
                                 disabled=False
-                            ),
+                            )
                         ]
                     )
                 ]
                         ]
                     )
                 ]
-            ),
+            )
         ]
 
         ]
 
-    class ControlPanel:
-        """A class representing the control panel.
+    def _get_plotting_area(
+            self,
+            tests: list,
+            normalize: bool,
+            url: str
+        ) -> list:
+        """Generate the plotting area with all its content.
         """
         """
+        if not tests:
+            return C.PLACEHOLDER
+
+        figs = graph_trending(self._data, tests, self._graph_layout, normalize)
+
+        if not figs[0]:
+            return C.PLACEHOLDER
+
+        tab_items = [
+            dbc.Tab(
+                children=dcc.Graph(
+                    id={"type": "graph", "index": "tput"},
+                    figure=figs[0]
+                ),
+                label="Throughput",
+                tab_id="tab-tput"
+            )
+        ]
 
 
-        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
-            """
-
-            # 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()
-            }
-
-            self._panel = deepcopy(self._defaults)
-            if panel:
-                for key in self._defaults:
-                    self._panel[key] = panel[key]
-
-        @property
-        def defaults(self) -> dict:
-            return self._defaults
-
-        @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.")
-
-        def get(self, key: str) -> any:
-            """Returns the value of a key from the Control panel.
+        if figs[1]:
+            tab_items.append(
+                dbc.Tab(
+                    children=dcc.Graph(
+                        id={"type": "graph", "index": "lat"},
+                        figure=figs[1]
+                    ),
+                    label="Latency",
+                    tab_id="tab-lat"
+                )
+            )
 
 
-            :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]
+        trending = [
+            dbc.Row(
+                children=dbc.Tabs(
+                    children=tab_items,
+                    id="tabs",
+                    active_tab="tab-tput",
+                )
+            ),
+            dbc.Row(
+                [
+                    dbc.Col([html.Div(
+                        [
+                            dbc.Button(
+                                id="plot-btn-url",
+                                children="URL",
+                                class_name="me-1",
+                                color="info",
+                                style={
+                                    "text-transform": "none",
+                                    "padding": "0rem 1rem"
+                                }
+                            ),
+                            dbc.Modal(
+                                [
+                                    dbc.ModalHeader(dbc.ModalTitle("URL")),
+                                    dbc.ModalBody(url)
+                                ],
+                                id="plot-mod-url",
+                                size="xl",
+                                is_open=False,
+                                scrollable=True
+                            ),
+                            dbc.Button(
+                                id="plot-btn-download",
+                                children="Download Data",
+                                class_name="me-1",
+                                color="info",
+                                style={
+                                    "text-transform": "none",
+                                    "padding": "0rem 1rem"
+                                }
+                            ),
+                            dcc.Download(id="download-trending-data")
+                        ],
+                        className=\
+                            "d-grid gap-0 d-md-flex justify-content-md-end"
+                    )])
+                ],
+                class_name="g-0 p-0"
+            )
+        ]
 
 
-        def values(self) -> tuple:
-            """Returns the values from the Control panel as a list.
+        acc_items = [
+            dbc.AccordionItem(
+                title="Trending",
+                children=trending
+            )
+        ]
 
 
-            :returns: The values from the Control panel.
-            :rtype: list
-            """
-            return tuple(self._panel.values())
+        return dbc.Col(
+            children=[
+                dbc.Row(
+                    dbc.Accordion(
+                        children=acc_items,
+                        class_name="g-0 p-1",
+                        start_collapsed=False,
+                        always_open=True,
+                        active_item=[f"item-{i}" for i in range(len(acc_items))]
+                    ),
+                    class_name="g-0 p-0",
+                ),
+                # dbc.Row(
+                #     dbc.Col([html.Div(
+                #         [
+                #             dbc.Button(
+                #                 id="btn-add-telemetry",
+                #                 children="Add Panel with Telemetry",
+                #                 class_name="me-1",
+                #                 color="info",
+                #                 style={
+                #                     "text-transform": "none",
+                #                     "padding": "0rem 1rem"
+                #                 }
+                #             )
+                #         ],
+                #         className=\
+                #             "d-grid gap-0 d-md-flex justify-content-md-end"
+                #     )]),
+                #     class_name="g-0 p-0"
+                # )
+            ]
+        )
 
     def callbacks(self, app):
         """Callbacks for the whole application.
 
     def callbacks(self, app):
         """Callbacks for the whole application.
@@ -722,186 +779,68 @@ class Layout:
         :param app: The application.
         :type app: Flask
         """
         :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))
-            """
-
-            (fig_tput, fig_lat) = figs
-
-            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
-                    )
-                ]
-                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
-                                    )
-                                ]
-                            )
-                        ]
-                    )
-                ]
-            if fig_lat:
-                row_fig_lat = [
-                    dcc.Graph(
-                        id={"type": "graph", "index": "lat"},
-                        figure=fig_lat
-                    )
-                ]
-
-            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-all", "options"),
-            Output("cl-ctrl-framesize", "options"),
-            Output("cl-ctrl-framesize", "value"),
-            Output("cl-ctrl-framesize-all", "value"),
-            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-all", "options"),
-            Output("btn-ctrl-add", "disabled"),
-            Output("cl-ctrl-normalize", "value"),
-            Output("cl-selected", "options"),  # User selection
-            State("control-panel", "data"),  # Store
-            State("selected-tests", "data"),  # Store
-            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-sel-remove", "n_clicks"),
-            Input("btn-sel-remove-all", "n_clicks"),
-            Input("url", "href")
+            [
+                Output("store-control-panel", "data"),
+                Output("store-selected-tests", "data"),
+                Output("plotting-area", "children"),
+                Output("row-card-sel-tests", "style"),
+                Output("row-btns-sel-tests", "style"),
+                Output("lg-selected", "children"),
+
+                Output({"type": "ctrl-dd", "index": "dut"}, "value"),
+                Output({"type": "ctrl-dd", "index": "phy"}, "options"),
+                Output({"type": "ctrl-dd", "index": "phy"}, "disabled"),
+                Output({"type": "ctrl-dd", "index": "phy"}, "value"),
+                Output({"type": "ctrl-dd", "index": "area"}, "options"),
+                Output({"type": "ctrl-dd", "index": "area"}, "disabled"),
+                Output({"type": "ctrl-dd", "index": "area"}, "value"),
+                Output({"type": "ctrl-dd", "index": "test"}, "options"),
+                Output({"type": "ctrl-dd", "index": "test"}, "disabled"),
+                Output({"type": "ctrl-dd", "index": "test"}, "value"),
+                Output({"type": "ctrl-cl", "index": "core"}, "options"),
+                Output({"type": "ctrl-cl", "index": "core"}, "value"),
+                Output({"type": "ctrl-cl", "index": "core-all"}, "value"),
+                Output({"type": "ctrl-cl", "index": "core-all"}, "options"),
+                Output({"type": "ctrl-cl", "index": "frmsize"}, "options"),
+                Output({"type": "ctrl-cl", "index": "frmsize"}, "value"),
+                Output({"type": "ctrl-cl", "index": "frmsize-all"}, "value"),
+                Output({"type": "ctrl-cl", "index": "frmsize-all"}, "options"),
+                Output({"type": "ctrl-cl", "index": "tsttype"}, "options"),
+                Output({"type": "ctrl-cl", "index": "tsttype"}, "value"),
+                Output({"type": "ctrl-cl", "index": "tsttype-all"}, "value"),
+                Output({"type": "ctrl-cl", "index": "tsttype-all"}, "options"),
+                Output({"type": "ctrl-btn", "index": "add-test"}, "disabled"),
+                Output("normalize", "value")
+            ],
+            [
+                State("store-control-panel", "data"),
+                State("store-selected-tests", "data"),
+                State({"type": "sel-cl", "index": ALL}, "value")
+            ],
+            [
+                Input("url", "href"),
+                Input("normalize", "value"),
+
+                Input({"type": "ctrl-dd", "index": ALL}, "value"),
+                Input({"type": "ctrl-cl", "index": ALL}, "value"),
+                Input({"type": "ctrl-btn", "index": ALL}, "n_clicks")
+            ]
         )
         )
-        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, btn_remove: int,
-            btn_remove_all: int, href: str) -> tuple:
+        def _update_application(
+                control_panel: dict,
+                store_sel: list,
+                lst_sel: list,
+                href: str,
+                normalize: list,
+                *_
+            ) -> tuple:
             """Update the application when the event is detected.
             """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 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 btn_remove: int
-            :type btn_remove_all: int
-            :type href: str
-            :returns: New values for web page elements.
-            :rtype: tuple
             """
 
             """
 
-            ctrl_panel = self.ControlPanel(cp_data)
-            norm = cl_normalize
+            ctrl_panel = ControlPanel(CP_PARAMS, control_panel)
+            on_draw = False
 
             # Parse the url:
             parsed_url = url_decode(href)
 
             # Parse the url:
             parsed_url = url_decode(href)
@@ -910,219 +849,230 @@ class Layout:
             else:
                 url_params = None
 
             else:
                 url_params = None
 
-            row_fig_tput = no_update
-            row_fig_lat = no_update
-            row_btn_dwnld = no_update
+            plotting_area = no_update
             row_card_sel_tests = no_update
             row_btns_sel_tests = no_update
             row_card_sel_tests = no_update
             row_btns_sel_tests = no_update
+            lg_selected = no_update
 
 
-            trigger_id = callback_context.triggered[0]["prop_id"].split(".")[0]
+            trigger = Trigger(callback_context.triggered)
 
 
-            if trigger_id == "dd-ctrl-dut":
+            if trigger.type == "url" and url_params:
                 try:
                 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]
+                    store_sel = literal_eval(url_params["store_sel"][0])
+                    normalize = literal_eval(url_params["norm"][0])
+                except (KeyError, IndexError):
+                    pass
+                if store_sel:
+                    last_test = store_sel[-1]
+                    test = self._spec_tbs[last_test["dut"]][last_test["phy"]]\
+                        [last_test["area"]][last_test["test"]]
                     ctrl_panel.set({
                     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,
+                        "dd-dut-val": last_test["dut"],
+                        "dd-phy-val": last_test["phy"],
+                        "dd-phy-opt": generate_options(
+                            self._spec_tbs[last_test["dut"]].keys()
+                        ),
+                        "dd-phy-dis": False,
+                        "dd-area-val": last_test["area"],
+                        "dd-area-opt": [
+                            {"label": label(v), "value": v} for v in sorted(
+                                self._spec_tbs[last_test["dut"]]\
+                                    [last_test["phy"]].keys()
+                            )
+                        ],
+                        "dd-area-dis": False,
+                        "dd-test-val": last_test["test"],
+                        "dd-test-opt": generate_options(
+                            self._spec_tbs[last_test["dut"]][last_test["phy"]]\
+                                [last_test["area"]].keys()
+                        ),
+                        "dd-test-dis": False,
+                        "cl-core-opt": generate_options(test["core"]),
+                        "cl-core-val": [last_test["core"].upper(), ],
+                        "cl-core-all-val": list(),
+                        "cl-core-all-opt": C.CL_ALL_ENABLED,
+                        "cl-frmsize-opt": generate_options(test["frame-size"]),
+                        "cl-frmsize-val": [last_test["framesize"].upper(), ],
+                        "cl-frmsize-all-val": list(),
+                        "cl-frmsize-all-opt": C.CL_ALL_ENABLED,
+                        "cl-tsttype-opt": generate_options(test["test-type"]),
+                        "cl-tsttype-val": [last_test["testtype"].upper(), ],
+                        "cl-tsttype-all-val": list(),
+                        "cl-tsttype-all-opt": C.CL_ALL_ENABLED,
+                        "cl-normalize-val": normalize,
+                        "btn-add-dis": False
                     })
                     })
-            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":
+                    on_draw = True
+            elif trigger.type == "normalize":
+                ctrl_panel.set({"cl-normalize-val": normalize})
+                on_draw = True
+            elif trigger.type == "ctrl-dd":
+                if trigger.idx == "dut":
+                    try:
+                        options = generate_options(
+                            self._spec_tbs[trigger.value].keys()
+                        )
+                        disabled = False
+                    except KeyError:
+                        options = list()
+                        disabled = True
+                    ctrl_panel.set({
+                        "dd-dut-val": trigger.value,
+                        "dd-phy-val": str(),
+                        "dd-phy-opt": options,
+                        "dd-phy-dis": disabled,
+                        "dd-area-val": str(),
+                        "dd-area-opt": list(),
+                        "dd-area-dis": True,
+                        "dd-test-val": str(),
+                        "dd-test-opt": list(),
+                        "dd-test-dis": True,
+                        "cl-core-opt": list(),
+                        "cl-core-val": list(),
+                        "cl-core-all-val": list(),
+                        "cl-core-all-opt": C.CL_ALL_DISABLED,
+                        "cl-frmsize-opt": list(),
+                        "cl-frmsize-val": list(),
+                        "cl-frmsize-all-val": list(),
+                        "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
+                        "cl-tsttype-opt": list(),
+                        "cl-tsttype-val": list(),
+                        "cl-tsttype-all-val": list(),
+                        "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
+                        "btn-add-dis": True
+                    })
+                elif trigger.idx == "phy":
+                    try:
+                        dut = ctrl_panel.get("dd-dut-val")
+                        phy = self._spec_tbs[dut][trigger.value]
+                        options = [{"label": label(v), "value": v} \
+                            for v in sorted(phy.keys())]
+                        disabled = False
+                    except KeyError:
+                        options = list()
+                        disabled = True
+                    ctrl_panel.set({
+                        "dd-phy-val": trigger.value,
+                        "dd-area-val": str(),
+                        "dd-area-opt": options,
+                        "dd-area-dis": disabled,
+                        "dd-test-val": str(),
+                        "dd-test-opt": list(),
+                        "dd-test-dis": True,
+                        "cl-core-opt": list(),
+                        "cl-core-val": list(),
+                        "cl-core-all-val": list(),
+                        "cl-core-all-opt": C.CL_ALL_DISABLED,
+                        "cl-frmsize-opt": list(),
+                        "cl-frmsize-val": list(),
+                        "cl-frmsize-all-val": list(),
+                        "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
+                        "cl-tsttype-opt": list(),
+                        "cl-tsttype-val": list(),
+                        "cl-tsttype-all-val": list(),
+                        "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
+                        "btn-add-dis": True
+                    })  
+                elif trigger.idx == "area":
+                    try:
+                        dut = ctrl_panel.get("dd-dut-val")
+                        phy = ctrl_panel.get("dd-phy-val")
+                        area = self._spec_tbs[dut][phy][trigger.value]
+                        options = generate_options(area.keys())
+                        disabled = False
+                    except KeyError:
+                        options = list()
+                        disabled = True
+                    ctrl_panel.set({
+                        "dd-area-val": trigger.value,
+                        "dd-test-val": str(),
+                        "dd-test-opt": options,
+                        "dd-test-dis": disabled,
+                        "cl-core-opt": list(),
+                        "cl-core-val": list(),
+                        "cl-core-all-val": list(),
+                        "cl-core-all-opt": C.CL_ALL_DISABLED,
+                        "cl-frmsize-opt": list(),
+                        "cl-frmsize-val": list(),
+                        "cl-frmsize-all-val": list(),
+                        "cl-frmsize-all-opt": C.CL_ALL_DISABLED,
+                        "cl-tsttype-opt": list(),
+                        "cl-tsttype-val": list(),
+                        "cl-tsttype-all-val": list(),
+                        "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
+                        "btn-add-dis": True
+                    })
+                elif trigger.idx == "test":
+                    dut = ctrl_panel.get("dd-dut-val")
+                    phy = ctrl_panel.get("dd-phy-val")
+                    area = ctrl_panel.get("dd-area-val")
+                    if all((dut, phy, area, trigger.value, )):
+                        test = self._spec_tbs[dut][phy][area][trigger.value]
+                        ctrl_panel.set({
+                            "dd-test-val": trigger.value,
+                            "cl-core-opt": generate_options(test["core"]),
+                            "cl-core-val": list(),
+                            "cl-core-all-val": list(),
+                            "cl-core-all-opt": C.CL_ALL_ENABLED,
+                            "cl-frmsize-opt": \
+                                generate_options(test["frame-size"]),
+                            "cl-frmsize-val": list(),
+                            "cl-frmsize-all-val": list(),
+                            "cl-frmsize-all-opt": C.CL_ALL_ENABLED,
+                            "cl-tsttype-opt": \
+                                generate_options(test["test-type"]),
+                            "cl-tsttype-val": list(),
+                            "cl-tsttype-all-val": list(),
+                            "cl-tsttype-all-opt": C.CL_ALL_ENABLED,
+                            "btn-add-dis": True
+                        })
+            elif trigger.type == "ctrl-cl":
+                param = trigger.idx.split("-")[0]
+                if "-all" in trigger.idx:
+                    c_sel, c_all, c_id = list(), trigger.value, "all"
+                else:
+                    c_sel, c_all, c_id = trigger.value, list(), str()
                 val_sel, val_all = sync_checklists(
                 val_sel, val_all = sync_checklists(
-                    options = ctrl_panel.get("cl-ctrl-testtype-options"),
-                    sel=list(),
-                    all=cl_testtype_all,
-                    id="all"
+                    options=ctrl_panel.get(f"cl-{param}-opt"),
+                    sel=c_sel,
+                    all=c_all,
+                    id=c_id
                 )
                 ctrl_panel.set({
                 )
                 ctrl_panel.set({
-                    "cl-ctrl-testtype-value": val_sel,
-                    "cl-ctrl-testtype-all-value": val_all,
+                    f"cl-{param}-val": val_sel,
+                    f"cl-{param}-all-val": 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:
-                if all((dut, phy, area, test, cores, framesizes, testtypes)):
+                if all((ctrl_panel.get("cl-core-val"), 
+                        ctrl_panel.get("cl-frmsize-val"),
+                        ctrl_panel.get("cl-tsttype-val"), )):
+                    ctrl_panel.set({"btn-add-dis": False})
+                else:
+                    ctrl_panel.set({"btn-add-dis": True})
+            elif trigger.type == "ctrl-btn":
+                on_draw = True
+                if trigger.idx == "add-test":
+                    dut = ctrl_panel.get("dd-dut-val")
+                    phy = ctrl_panel.get("dd-phy-val")
+                    area = ctrl_panel.get("dd-area-val")
+                    test = ctrl_panel.get("dd-test-val")
+                    # Add selected test(s) to the list of tests in store:
                     if store_sel is None:
                         store_sel = list()
                     if store_sel is None:
                         store_sel = list()
-                    for core in cores:
-                        for framesize in framesizes:
-                            for ttype in testtypes:
+                    for core in ctrl_panel.get("cl-core-val"):
+                        for framesize in ctrl_panel.get("cl-frmsize-val"):
+                            for ttype in ctrl_panel.get("cl-tsttype-val"):
                                 if dut == "trex":
                                     core = str()
                                 tid = "-".join((
                                 if dut == "trex":
                                     core = str()
                                 tid = "-".join((
-                                    dut, phy.replace('af_xdp', 'af-xdp'), area,
-                                    framesize.lower(), core.lower(), test,
+                                    dut,
+                                    phy.replace('af_xdp', 'af-xdp'),
+                                    area,
+                                    framesize.lower(),
+                                    core.lower(),
+                                    test,
                                     ttype.lower()
                                 ))
                                     ttype.lower()
                                 ))
-                                if tid not in [itm["id"] for itm in store_sel]:
+                                if tid not in [i["id"] for i in store_sel]:
                                     store_sel.append({
                                         "id": tid,
                                         "dut": dut,
                                     store_sel.append({
                                         "id": tid,
                                         "dut": dut,
@@ -1134,126 +1084,59 @@ class Layout:
                                         "testtype": ttype.lower()
                                     })
                     store_sel = sorted(store_sel, key=lambda d: d["id"])
                                         "testtype": ttype.lower()
                                     })
                     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)
                     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":
-                _ = btn_remove
-                if list_sel:
+                elif trigger.idx == "rm-test" and lst_sel:
                     new_store_sel = list()
                     new_store_sel = list()
-                    for item in store_sel:
-                        if item["id"] not in list_sel:
+                    for idx, item in enumerate(store_sel):
+                        if not lst_sel[idx]:
                             new_store_sel.append(item)
                     store_sel = new_store_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])
-                        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", "btn-sel-remove",
-                    "cl-ctrl-normalize"):
+                elif trigger.idx == "rm-test-all":
+                    store_sel = list()
+                
+            if on_draw:
                 if store_sel:
                 if store_sel:
-                    row_fig_tput, row_fig_lat, row_btn_dwnld = \
-                        _generate_plotting_area(
-                            graph_trending(self.data, store_sel, self.layout,
-                                bool(norm)),
-                            gen_new_url(
-                                parsed_url,
-                                {
-                                    "store_sel": store_sel,
-                                    "norm": norm
-                                }
-                            )
+                    lg_selected = get_list_group_items(store_sel)
+                    plotting_area = self._get_plotting_area(
+                        store_sel,
+                        bool(normalize),
+                        gen_new_url(
+                            parsed_url,
+                            {"store_sel": store_sel, "norm": normalize}
                         )
                         )
-                    ctrl_panel.set({
-                        "cl-selected-options": list_tests(store_sel)
-                    })
+                    )
+                    row_card_sel_tests = C.STYLE_ENABLED
+                    row_btns_sel_tests = C.STYLE_ENABLED
                 else:
                 else:
-                    row_fig_tput = C.PLACEHOLDER
-                    row_fig_lat = C.PLACEHOLDER
-                    row_btn_dwnld = C.PLACEHOLDER
+                    plotting_area = C.PLACEHOLDER
                     row_card_sel_tests = C.STYLE_DISABLED
                     row_btns_sel_tests = C.STYLE_DISABLED
                     store_sel = list()
                     row_card_sel_tests = C.STYLE_DISABLED
                     row_btns_sel_tests = C.STYLE_DISABLED
                     store_sel = list()
-                    ctrl_panel.set({"cl-selected-options": list()})
-
-            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 = [
 
             ret_val = [
-                ctrl_panel.panel, store_sel,
-                row_fig_tput, row_fig_lat, row_btn_dwnld,
-                row_card_sel_tests, row_btns_sel_tests
+                ctrl_panel.panel,
+                store_sel,
+                plotting_area,
+                row_card_sel_tests,
+                row_btns_sel_tests,
+                lg_selected
             ]
             ]
-            ret_val.extend(ctrl_panel.values())
+            ret_val.extend(ctrl_panel.values)
             return ret_val
 
             return ret_val
 
+        @app.callback(
+            Output("plot-mod-url", "is_open"),
+            [Input("plot-btn-url", "n_clicks")],
+            [State("plot-mod-url", "is_open")],
+        )
+        def toggle_plot_mod_url(n, is_open):
+            """Toggle the modal window with url.
+            """
+            if n:
+                return not is_open
+            return is_open
+
         @app.callback(
             Output("metadata-tput-lat", "children"),
             Output("metadata-hdrh-graph", "children"),
         @app.callback(
             Output("metadata-tput-lat", "children"),
             Output("metadata-hdrh-graph", "children"),
@@ -1271,14 +1154,13 @@ class Layout:
                 information to show the offcanvas.
             :rtype: tuple(list, list, bool)
             """
                 information to show the offcanvas.
             :rtype: tuple(list, list, bool)
             """
+
+            trigger = Trigger(callback_context.triggered)
+
             try:
             try:
-                trigger_id = loads(
-                    callback_context.triggered[0]["prop_id"].split(".")[0]
-                )["index"]
-                idx = 0 if trigger_id == "tput" else 1
+                idx = 0 if trigger.idx == "tput" else 1
                 graph_data = graph_data[idx]["points"][0]
                 graph_data = graph_data[idx]["points"][0]
-            except (JSONDecodeError, IndexError, KeyError, ValueError,
-                    TypeError):
+            except (IndexError, KeyError, ValueError, TypeError):
                 raise PreventUpdate
 
             metadata = no_update
                 raise PreventUpdate
 
             metadata = no_update
@@ -1289,9 +1171,9 @@ class Layout:
                     [dbc.Badge(x.split(":")[0]), x.split(": ")[1]]
                 ) for x in graph_data.get("text", "").split("<br>")
             ]
                     [dbc.Badge(x.split(":")[0]), x.split(": ")[1]]
                 ) for x in graph_data.get("text", "").split("<br>")
             ]
-            if trigger_id == "tput":
+            if trigger.idx == "tput":
                 title = "Throughput"
                 title = "Throughput"
-            elif trigger_id == "lat":
+            elif trigger.idx == "lat":
                 title = "Latency"
                 hdrh_data = graph_data.get("customdata", None)
                 if hdrh_data:
                 title = "Latency"
                 hdrh_data = graph_data.get("customdata", None)
                 if hdrh_data:
@@ -1303,12 +1185,15 @@ class Layout:
                                 dcc.Graph(
                                     id="hdrh-latency-graph",
                                     figure=graph_hdrh_latency(
                                 dcc.Graph(
                                     id="hdrh-latency-graph",
                                     figure=graph_hdrh_latency(
-                                        hdrh_data, self.layout
+                                        hdrh_data, self._graph_layout
                                     )
                                 )
                             ])
                         ])
                     ]
                                     )
                                 )
                             ])
                         ])
                     ]
+            else:
+                raise PreventUpdate
+
             metadata = [
                 dbc.Card(
                     class_name="gy-2 p-0",
             metadata = [
                 dbc.Card(
                     class_name="gy-2 p-0",
@@ -1333,33 +1218,28 @@ class Layout:
             return metadata, graph, True
 
         @app.callback(
             return metadata, graph, True
 
         @app.callback(
-            Output("download-data", "data"),
-            State("selected-tests", "data"),
-            Input("btn-download-data", "n_clicks"),
+            Output("download-trending-data", "data"),
+            State("store-selected-tests", "data"),
+            Input("plot-btn-download", "n_clicks"),
             prevent_initial_call=True
         )
             prevent_initial_call=True
         )
-        def _download_data(store_sel, n_clicks):
+        def _download_trending_data(store_sel, _):
             """Download the data
 
             :param store_sel: List of tests selected by user stored in the
                 browser.
             """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 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
             """
 
             :returns: dict of data frame content (base64 encoded) and meta data
                 used by the Download component.
             :rtype: dict
             """
 
-            if not n_clicks:
-                raise PreventUpdate
-
             if not store_sel:
                 raise PreventUpdate
 
             df = pd.DataFrame()
             for itm in store_sel:
             if not store_sel:
                 raise PreventUpdate
 
             df = pd.DataFrame()
             for itm in store_sel:
-                sel_data = select_trending_data(self.data, itm)
+                sel_data = select_trending_data(self._data, itm)
                 if sel_data is None:
                     continue
                 df = pd.concat([df, sel_data], ignore_index=True)
                 if sel_data is None:
                     continue
                 df = pd.concat([df, sel_data], ignore_index=True)
index 1beb522..0eada51 100644 (file)
@@ -1,6 +1,6 @@
 plot-trending-tput:
   autosize: True
 plot-trending-tput:
   autosize: True
-  showlegend: True
+  showlegend: False
   yaxis:
     showticklabels: True
     tickformat: ".3s"
   yaxis:
     showticklabels: True
     tickformat: ".3s"
@@ -33,12 +33,6 @@ plot-trending-tput:
     b: 0
     t: 5
     l: 70
     b: 0
     t: 5
     l: 70
-  legend:
-    orientation: "h"
-    y: -0.18
-    xanchor: "auto"
-    traceorder: "normal"
-    bordercolor: "rgb(238, 238, 238)"
   paper_bgcolor: "#fff"
   plot_bgcolor: "#fff"
   hoverlabel:
   paper_bgcolor: "#fff"
   plot_bgcolor: "#fff"
   hoverlabel:
@@ -46,7 +40,7 @@ plot-trending-tput:
 
 plot-trending-lat:
   autosize: True
 
 plot-trending-lat:
   autosize: True
-  showlegend: True
+  showlegend: False
   yaxis:
     showticklabels: True
     tickformat: ".3s"
   yaxis:
     showticklabels: True
     tickformat: ".3s"
@@ -79,12 +73,6 @@ plot-trending-lat:
     b: 0
     t: 5
     l: 70
     b: 0
     t: 5
     l: 70
-  legend:
-    orientation: "h"
-    y: -0.18
-    xanchor: "auto"
-    traceorder: "normal"
-    bordercolor: "rgb(238, 238, 238)"
   paper_bgcolor: "#fff"
   plot_bgcolor: "#fff"
   hoverlabel:
   paper_bgcolor: "#fff"
   plot_bgcolor: "#fff"
   hoverlabel:
index 6e973b8..cf16491 100644 (file)
@@ -39,7 +39,7 @@ class Constants:
     BRAND = "CSIT-Dash"
 
     # The application description.
     BRAND = "CSIT-Dash"
 
     # The application description.
-    DESCRIPTION = 'Performance Dashboard "CSIT-Dash"'
+    DESCRIPTION = "Performance Dashboard"
 
     # External stylesheets.
     EXTERNAL_STYLESHEETS = ["/static/dist/css/bootstrap.css", ]
 
     # External stylesheets.
     EXTERNAL_STYLESHEETS = ["/static/dist/css/bootstrap.css", ]
@@ -101,7 +101,7 @@ class Constants:
         }
     ]
 
         }
     ]
 
-    # Checklist "All" is enable, visible and unchecked.
+    # Checklist "All" is enabled, visible and unchecked.
     CL_ALL_ENABLED = [
         {
             "label": "All",
     CL_ALL_ENABLED = [
         {
             "label": "All",
@@ -158,7 +158,8 @@ class Constants:
         "3n-dnv": 2.000,
         "3n-icx": 2.600,
         "3n-skx": 2.500,
         "3n-dnv": 2.000,
         "3n-icx": 2.600,
         "3n-skx": 2.500,
-        "3n-tsh": 2.200
+        "3n-tsh": 2.200,
+        "3n-snr": 2.200
     }
 
     ############################################################################
     }
 
     ############################################################################
@@ -253,7 +254,7 @@ class Constants:
     # News.
 
     # The title.
     # News.
 
     # The title.
-    NEWS_TITLE = "CI TEST FAILURE AND ANOMALY STATISTICS"
+    NEWS_TITLE = "Failures and Anomalies"
 
     # The pathname prefix for the application.
     NEWS_ROUTES_PATHNAME_PREFIX = "/news/"
 
     # The pathname prefix for the application.
     NEWS_ROUTES_PATHNAME_PREFIX = "/news/"
@@ -270,7 +271,7 @@ class Constants:
     # Report.
 
     # The title.
     # Report.
 
     # The title.
-    REPORT_TITLE = "PER RELEASE PERFORMANCE RESULTS"
+    REPORT_TITLE = "Per Release Performance"
 
     # The pathname prefix for the application.
     REPORT_ROUTES_PATHNAME_PREFIX = "/report/"
 
     # The pathname prefix for the application.
     REPORT_ROUTES_PATHNAME_PREFIX = "/report/"
@@ -285,7 +286,7 @@ class Constants:
     # Statistics.
 
     # The title.
     # Statistics.
 
     # The title.
-    STATS_TITLE = "CI JOB EXECUTION STATISTICS"
+    STATS_TITLE = "Test Job Statistics"
 
     # The pathname prefix for the application.
     STATS_ROUTES_PATHNAME_PREFIX = "/stats/"
 
     # The pathname prefix for the application.
     STATS_ROUTES_PATHNAME_PREFIX = "/stats/"
@@ -303,7 +304,7 @@ class Constants:
     # Trending.
 
     # The title.
     # Trending.
 
     # The title.
-    TREND_TITLE = "PERFORMANCE TRENDING (DAILY, WEEKLY)"
+    TREND_TITLE = "Performance Trending"
 
     # The pathname prefix for the application.
     TREND_ROUTES_PATHNAME_PREFIX = "/trending/"
 
     # The pathname prefix for the application.
     TREND_ROUTES_PATHNAME_PREFIX = "/trending/"
diff --git a/csit.infra.dash/app/cdash/utils/control_panel.py b/csit.infra.dash/app/cdash/utils/control_panel.py
new file mode 100644 (file)
index 0000000..d892dfa
--- /dev/null
@@ -0,0 +1,87 @@
+# 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.
+
+"""A module implementing the control panel data structure.
+"""
+
+from copy import deepcopy
+
+
+class ControlPanel:
+    """A class representing the control panel.
+    """
+
+    def __init__(self, params: dict, panel: dict) -> None:
+        """Initialisation of the control pannel by default values. If
+        particular values are provided (parameter "panel") they are set
+        afterwards.
+
+        :param params: Default values to be set to the control panel. This
+            dictionary also defines the full set of the control panel's
+            parameters and their order.
+        :param panel: Custom values to be set to the control panel.
+        :type params: dict
+        :type panel: dict
+        """
+
+        if not params:
+            raise ValueError("The params must be defined.")
+        self._panel = deepcopy(params)
+        if panel:
+            for key in panel:
+                if key in self._panel:
+                    self._panel[key] = panel[key]
+                else:
+                    raise AttributeError(
+                        f"The parameter {key} is not defined in the list of "
+                        f"parameters."
+                    )
+
+    @property
+    def panel(self) -> dict:
+        return self._panel
+
+    @property
+    def values(self) -> tuple:
+        """Returns the values from the Control panel as a tuple.
+
+        :returns: The values from the Control panel.
+        :rtype: tuple
+        """
+        return tuple(self._panel.values())
+
+    def set(self, kwargs: dict=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.")
+
+    def get(self, key: str) -> any:
+        """Returns the value of a key from the Control panel.
+
+        :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]
diff --git a/csit.infra.dash/app/cdash/utils/trigger.py b/csit.infra.dash/app/cdash/utils/trigger.py
new file mode 100644 (file)
index 0000000..53f7a20
--- /dev/null
@@ -0,0 +1,63 @@
+# 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.
+
+"""A module implementing the processing of a trigger.
+"""
+
+from json import loads, JSONDecodeError
+
+
+class Trigger:
+    """
+    """
+    def __init__(self, trigger) -> None:
+        """
+        """
+        self._id = trigger[0]["prop_id"].split(".")
+        self._param = self._id[1]
+        try:
+            self._id = loads(self._id[0])
+        except (JSONDecodeError, TypeError):
+            # It is a string
+            self._id = {"type": self._id[0], "index": None}
+        self._val = trigger[0]["value"]
+
+    def __str__(self) -> str:
+        return (
+            f"\nTrigger:\n"
+            f"  ID:        {self._id}\n"
+            f"  Type:      {self._id['type']}\n"
+            f"  Index:     {self._id['index']}\n"
+            f"  Parameter: {self._param}\n"
+            f"  Value:     {self._val}\n"
+        )
+
+    @property
+    def id(self) -> dict:
+        return self._id
+
+    @property
+    def type(self) -> str:
+        return self._id["type"]
+
+    @property
+    def idx(self) -> any:
+        return self._id["index"]
+
+    @property
+    def parameter(self) -> str:
+        return self._param
+
+    @property
+    def value(self) -> any:
+        return self._val
index 9e4eeeb..461821d 100644 (file)
@@ -303,7 +303,7 @@ def get_job(df: pd.DataFrame, dut, ttype, cadence, testbed):
     )]["job"].item()
 
 
     )]["job"].item()
 
 
-def generate_options(opts: list) -> list:
+def generate_options(opts: list, sort: bool=True) -> list:
     """Return list of options for radio items in control panel. The items in
     the list are dictionaries with keys "label" and "value".
 
     """Return list of options for radio items in control panel. The items in
     the list are dictionaries with keys "label" and "value".
 
@@ -312,6 +312,8 @@ def generate_options(opts: list) -> list:
     :returns: List of options (dict).
     :rtype: list
     """
     :returns: List of options (dict).
     :rtype: list
     """
+    if sort:
+        opts = sorted(opts)
     return [{"label": i, "value": i} for i in opts]
 
 
     return [{"label": i, "value": i} for i in opts]
 
 
@@ -342,3 +344,31 @@ def set_job_params(df: pd.DataFrame, job: str) -> dict:
         "tbeds": generate_options(
             get_test_beds(df, l_job[1], l_job[3], l_job[4]))
     }
         "tbeds": generate_options(
             get_test_beds(df, l_job[1], l_job[3], l_job[4]))
     }
+
+
+def get_list_group_items(tests: list) -> list:
+    """Generate list of ListGroupItems with checkboxes with selected tests.
+
+    :param tests: List of tests to be displayed in the ListGroup.
+    :type tests: list
+    :returns: List of ListGroupItems with checkboxes with selected tests.
+    :rtype: list
+    """
+    return [
+        dbc.ListGroupItem(
+            children=[
+                dbc.Checkbox(
+                    id={"type": "sel-cl", "index": i},
+                    label=l["id"],
+                    value=False,
+                    label_class_name="m-0 p-0",
+                    label_style={
+                        "font-size": ".875em",
+                        "color": get_color(i)
+                    },
+                    input_class_name="border-danger bg-danger"
+                )
+            ],
+            class_name="p-0"
+        ) for i, l in enumerate(tests)
+    ]