Additional rework of NSB report 95/65995/2
authorPatrice Buriez <patrice.buriez@intel.com>
Mon, 17 Dec 2018 18:22:16 +0000 (19:22 +0100)
committerPatrice Buriez <patrice.buriez@intel.com>
Fri, 21 Dec 2018 18:07:59 +0000 (19:07 +0100)
- Make format_for_jstree expect a list of metric names
- Avoid displaying timestamps twice in initial data table
- Sort metrics in initial data table
- Display testcase name
- Fix styling
- Make better use of JS and jQuery features
- Move event handler to JS file
- Avoid adding multiple tbody elements into data table
- Adjust unit tests and functional tests accordingly

JIRA: YARDSTICK-1367
Topic: report/html_table (12 of 12)

Change-Id: I85d853f8e392953cace67e94fa0af2e2492a2b86
Signed-off-by: Patrice Buriez <patrice.buriez@intel.com>
yardstick/benchmark/core/report.py
yardstick/common/nsb_report.css
yardstick/common/nsb_report.html.j2
yardstick/common/nsb_report.js
yardstick/tests/functional/benchmark/core/test_report.py
yardstick/tests/unit/benchmark/core/test_report.py

index 17a9fe4..0819cd4 100644 (file)
@@ -54,11 +54,9 @@ class JSTree(object):
     def format_for_jstree(self, data):
         """Format the data into the required format for jsTree.
 
-        The data format expected is a list of key-value pairs which represent
-        the data and label for each metric e.g.:
+        The data format expected is a list of metric names e.g.:
 
-            [{'data': [0, ], 'label': 'tg__0.DropPackets'},
-             {'data': [548, ], 'label': 'tg__0.LatencyAvg.5'},]
+            ['tg__0.DropPackets', 'tg__0.LatencyAvg.5']
 
         This data is converted into the format required for jsTree to group and
         display the metrics in a hierarchial fashion, including creating a
@@ -75,8 +73,8 @@ class JSTree(object):
         self._created_nodes = ['#']
         self.jstree_data = []
 
-        for item in data:
-            self._create_node(item["label"])
+        for metric in data:
+            self._create_node(metric)
 
         return self.jstree_data
 
@@ -230,8 +228,14 @@ class Report(object):
     @cliargs("yaml_name", type=str, help=" Yaml file Name", nargs=1)
     def generate_nsb(self, args):
         """Start NSB report generation."""
-        datasets, table_vals = self._generate_common(args)
-        jstree_data = JSTree().format_for_jstree(datasets)
+        _, report_data = self._generate_common(args)
+        report_time = report_data.pop('Timestamp')
+        report_keys = sorted(report_data, key=str.lower)
+        report_tree = JSTree().format_for_jstree(report_keys)
+        report_meta = {
+            "testcase": self.yaml_name,
+            "task_id": self.task_id,
+        }
 
         template_dir = consts.YARDSTICK_ROOT_PATH + "yardstick/common"
         template_environment = jinja2.Environment(
@@ -240,10 +244,11 @@ class Report(object):
             lstrip_blocks=True)
 
         context = {
-            "Timestamps": self.Timestamp,
-            "task_id": self.task_id,
-            "table": table_vals,
-            "jstree_nodes": jstree_data,
+            "report_meta": report_meta,
+            "report_data": report_data,
+            "report_time": report_time,
+            "report_keys": report_keys,
+            "report_tree": report_tree,
         }
 
         template_html = template_environment.get_template("nsb_report.html.j2")
index 2353214..667f865 100644 (file)
@@ -9,38 +9,26 @@
  ******************************************************************************/
 
 body {
-    font-size: 16pt;
-}
-
-table {
-    overflow-y: scroll;
-    height: 360px;
-    display: block;
-    width: 100%;
+    font-family: Frutiger, "Helvetica Neue", Helvetica, Arial, sans-serif;
 }
 
 header {
-    font-family: Frutiger, "Helvetica Neue", Helvetica, Arial, sans-serif;
-    clear: left;
+    padding-top: 5px;
     text-align: center;
-}
-
-h1 {
-    font-size: 20pt;
     font-weight: bold;
-    height: 20px;
-    margin: 0px 0px;
 }
 
-h4 {
-    font-size: 13pt;
-    height: 1px;
-    margin-bottom: 0px;
+#tblMetrics {
+    overflow-y: scroll;
+    height: 360px;
+    display: block;
 }
 
-.control-pane {
-    font-size: 10pt;
+#cnvGraph {
+    width: 100%;
+    height: 500px;
 }
 
-.data-pane {
+#divTree {
+    font-size: 10pt;
 }
index 6523fd1..aa90253 100644 (file)
     <body>
         <div class="container-fluid">
             <div class="row">
-                <header class="jumbotron">
-                    <h1>Yardstick User Interface</h1>
-                    <h4>Report of {{task_id}} Generated</h4>
+                <header>
+                    Testcase: {{report_meta.testcase}}<br>
+                    Task-ID: {{report_meta.task_id}}<br>
                 </header>
             </div>
             <div class="row">
-                <div class="col-md-2 control-pane">
-                    <div id="data_selector"></div>
+                <div class="col-md-2">
+                    <div id="divTree"></div>
                 </div>
-                <div class="col-md-10 data-pane">
-                    <canvas id="cnvGraph" style="width: 100%; height: 500px"></canvas>
+                <div class="col-md-10">
+                    <canvas id="cnvGraph"></canvas>
                 </div>
             </div>
             <div class="row">
                 <div class="col-md-12 table-responsive">
-                    <table class="table table-hover"></table>
+                    <table id="tblMetrics" class="table table-condensed table-hover"></table>
                 </div>
             </div>
         </div>
 
         <script>
-            var arr, jstree_data, timestamps;
-            arr = {{table|safe}};
-            timestamps = {{Timestamps|safe}};
-            jstree_data = {{jstree_nodes|safe}};
+            // Injected metrics, timestamps, keys and hierarchy
+            var report_data = {{report_data|safe}};
+            var report_time = {{report_time|safe}};
+            var report_keys = {{report_keys|safe}};
+            var report_tree = {{report_tree|safe}};
 
+            // Wait for DOM to be loaded
             $(function() {
-                create_table(arr, timestamps);
-                create_tree(jstree_data);
-                var objGraph = create_graph($('#cnvGraph'), timestamps);
+                var tblMetrics = $('#tblMetrics');
+                var cnvGraph = $('#cnvGraph');
+                var divTree = $('#divTree');
 
-                $('#data_selector').on('check_node.jstree uncheck_node.jstree', function(e, data) {
-                    var selected_datasets = [];
-                    var new_arr = {};
-                    deleteRows();
-                    for (var i = 0; i < data.selected.length; i++) {
-                        var node = data.instance.get_node(data.selected[i]);
-                        if (node.children.length == 0) {
-                            var dataset = {
-                                label: node.id,
-                                data: arr[node.id],
-                            };
-                            selected_datasets.push(dataset);
-                            // Create new array for table to show only subset of metrics
-                            new_arr[node.id] = arr[node.id];
-                        }
-                    }
-                    create_table(new_arr, timestamps);
-                    update_graph(objGraph, selected_datasets);
-                });
+                create_table(tblMetrics, report_data, report_time, report_keys);
+                var objGraph = create_graph(cnvGraph, report_time);
+                create_tree(divTree, report_tree);
+                handle_tree(divTree, tblMetrics, objGraph, report_data, report_time);
             });
         </script>
     </body>
index 4bff15b..4de1c8e 100644 (file)
@@ -10,9 +10,9 @@
 
 var None = null;
 
-function create_tree(jstree_data)
+function create_tree(divTree, jstree_data)
 {
-    $('#data_selector').jstree({
+    divTree.jstree({
         plugins: ['checkbox'],
         checkbox: {
             three_state: false,
@@ -29,55 +29,33 @@ function create_tree(jstree_data)
     });
 }
 
-function create_table(table_data, timestamps)
+function create_table(tblMetrics, table_data, timestamps, table_keys)
 {
-    var tab, tr, td, tn, tbody, keys, key, curr_data, val;
-    // create table
-    tab = document.getElementsByTagName('table')[0];
-    tbody = document.createElement('tbody');
+    var tbody = $('<tbody></tbody>');
+    var tr0 = $('<tr></tr>');
+    var th0 = $('<th></th>');
+    var td0 = $('<td></td>');
+    var tr;
+
     // create table headings using timestamps
-    tr = document.createElement('tr');
-    td = document.createElement('td');
-    tn = document.createTextNode('Timestamps');
-    td.appendChild(tn);
-    tr.appendChild(td);
-    for (var k = 0; k < timestamps.length; k++) {
-        td = document.createElement('td');
-        tn = document.createTextNode(timestamps[k]);
-        td.appendChild(tn);
-        tr.appendChild(td);
-    }
-    tbody.appendChild(tr);
+    tr = tr0.clone().append(th0.clone().text('Timestamp'));
+    timestamps.forEach(function(t) {
+        tr.append(th0.clone().text(t));
+    });
+    tbody.append(tr);
+
     // for each metric
-    keys = Object.keys(table_data);
-    for (var i = 0; i < keys.length; i++) {
-        key = keys[i];
-        tr = document.createElement('tr');
-        td = document.createElement('td');
-        tn = document.createTextNode(key);
-        td.appendChild(tn);
-        tr.appendChild(td);
+    table_keys.forEach(function(key) {
+        tr = tr0.clone().append(td0.clone().text(key));
         // add each piece of data as its own column
-        curr_data = table_data[key];
-        for (var j = 0; j < curr_data.length; j++) {
-            val = curr_data[j];
-            td = document.createElement('td');
-            tn = document.createTextNode(val === None ? '' : val);
-            td.appendChild(tn);
-            tr.appendChild(td);
-        }
-        tbody.appendChild(tr);
-    }
-    tab.appendChild(tbody);
-}
+        table_data[key].forEach(function(val) {
+            tr.append(td0.clone().text(val === None ? '' : val));
+        });
+        tbody.append(tr);
+    });
 
-function deleteRows()
-{
-    // delete rows of the table
-    var tab = document.getElementsByTagName('table')[0];
-    for (var i = tab.rows.length - 1; i >= 0; i--) {
-        tab.deleteRow(i);
-    }
+    // re-create table
+    tblMetrics.empty().append(tbody);
 }
 
 function create_graph(cnvGraph, timestamps)
@@ -165,3 +143,23 @@ function update_graph(objGraph, datasets)
     objGraph.data.datasets = datasets;
     objGraph.update();
 }
+
+function handle_tree(divTree, tblMetrics, objGraph, table_data, timestamps)
+{
+    divTree.on('check_node.jstree uncheck_node.jstree', function(e, data) {
+        var selected_keys = [];
+        var selected_datasets = [];
+        data.selected.forEach(function(sel) {
+            var node = data.instance.get_node(sel);
+            if (node.children.length == 0) {
+                selected_keys.push(node.id);
+                selected_datasets.push({
+                    label: node.id,
+                    data: table_data[node.id],
+                });
+            }
+        });
+        create_table(tblMetrics, table_data, timestamps, selected_keys);
+        update_graph(objGraph, selected_datasets);
+    });
+}
index 401b97d..5f060dd 100644 (file)
@@ -23,9 +23,9 @@ GOOD_YAML_NAME = 'fake_name'
 GOOD_TASK_ID = "9cbe74b6-df09-4535-8bdc-dc3a43b8a4e2"
 GOOD_DB_FIELDKEYS = [
     {u'fieldKey': u'metric1', u'fieldType': u'integer'},
+    {u'fieldKey': u'metric4', u'fieldType': u'integer'},
     {u'fieldKey': u'metric2', u'fieldType': u'integer'},
     {u'fieldKey': u'metric3', u'fieldType': u'integer'},
-    {u'fieldKey': u'metric4', u'fieldType': u'integer'},
 ]
 GOOD_DB_METRICS = [
     {u'time': u'2018-08-20T16:49:26.372662016Z',
@@ -73,28 +73,42 @@ class ReportTestCase(unittest.TestCase):
         with mock.patch.object(report.consts, 'DEFAULT_HTML_FILE', tmpfile.name):
             report.Report().generate_nsb(params)
 
+        data_act = None
+        time_act = None
+        keys_act = None
+        tree_act = None
         with open(tmpfile.name) as f:
             for l in f.readlines():
-                 if " arr = {" in l:
-                     arr_act = ast.literal_eval(l.strip()[6:-1])
-                 elif " jstree_data = [" in l:
-                     jstree_data_act = ast.literal_eval(l.strip()[14:-1])
-
-        arr_exp = {
-            'Timestamp':
-                ['16:49:26.372662', '16:49:27.374208', '16:49:28.375742',
-                 '16:49:29.377299', '16:49:30.378252', '16:49:30.379359'],
+                 if "var report_data = {" in l:
+                     data_act = ast.literal_eval(l.strip()[18:-1])
+                 elif "var report_time = [" in l:
+                     time_act = ast.literal_eval(l.strip()[18:-1])
+                 elif "var report_keys = [" in l:
+                     keys_act = ast.literal_eval(l.strip()[18:-1])
+                 elif "var report_tree = [" in l:
+                     tree_act = ast.literal_eval(l.strip()[18:-1])
+
+        data_exp = {
             'metric1': [1, 1, 2, 3, 5, 8],
             'metric2': [0, 1, 2, 3, 4, 5],
             'metric3': [8, 5, 3, 2, 1, 1],
             'metric4': [5, 4, 3, 2, 1, 0],
         }
-        jstree_data_exp = [
+        time_exp = [
+            '16:49:26.372662', '16:49:27.374208', '16:49:28.375742',
+            '16:49:29.377299', '16:49:30.378252', '16:49:30.379359',
+        ]
+        keys_exp = [
+            'metric1', 'metric2', 'metric3', 'metric4',
+        ]
+        tree_exp = [
             {'parent': '#', 'text': 'metric1', 'id': 'metric1'},
             {'parent': '#', 'text': 'metric2', 'id': 'metric2'},
             {'parent': '#', 'text': 'metric3', 'id': 'metric3'},
             {'parent': '#', 'text': 'metric4', 'id': 'metric4'},
         ]
 
-        self.assertEqual(arr_exp, arr_act)
-        self.assertEqual(jstree_data_exp, jstree_data_act)
+        self.assertEqual(data_exp, data_act)
+        self.assertEqual(time_exp, time_act)
+        self.assertEqual(keys_exp, keys_act)
+        self.assertEqual(tree_exp, tree_act)
index 41991dd..4683c26 100644 (file)
@@ -117,23 +117,15 @@ class JSTreeTestCase(unittest.TestCase):
 
     def test_format_for_jstree(self):
         data = [
-            {'data': [0, ], 'label': 'tg__0.DropPackets'},
-            {'data': [548, ], 'label': 'tg__0.LatencyAvg.5'},
-            {'data': [1172, ], 'label': 'tg__0.LatencyAvg.6'},
-            {'data': [1001, ], 'label': 'tg__0.LatencyMax.5'},
-            {'data': [1468, ], 'label': 'tg__0.LatencyMax.6'},
-            {'data': [18.11, ], 'label': 'tg__0.RxThroughput'},
-            {'data': [18.11, ], 'label': 'tg__0.TxThroughput'},
-            {'data': [0, ], 'label': 'tg__1.DropPackets'},
-            {'data': [548, ], 'label': 'tg__1.LatencyAvg.5'},
-            {'data': [1172, ], 'label': 'tg__1.LatencyAvg.6'},
-            {'data': [1001, ], 'label': 'tg__1.LatencyMax.5'},
-            {'data': [1468, ], 'label': 'tg__1.LatencyMax.6'},
-            {'data': [18.1132084505, ], 'label': 'tg__1.RxThroughput'},
-            {'data': [18.1157260383, ], 'label': 'tg__1.TxThroughput'},
-            {'data': [9057888, ], 'label': 'vnf__0.curr_packets_in'},
-            {'data': [0, ], 'label': 'vnf__0.packets_dropped'},
-            {'data': [617825443, ], 'label': 'vnf__0.packets_fwd'},
+            'tg__0.DropPackets',
+            'tg__0.LatencyAvg.5', 'tg__0.LatencyAvg.6',
+            'tg__0.LatencyMax.5', 'tg__0.LatencyMax.6',
+            'tg__0.RxThroughput', 'tg__0.TxThroughput',
+            'tg__1.DropPackets',
+            'tg__1.LatencyAvg.5', 'tg__1.LatencyAvg.6',
+            'tg__1.LatencyMax.5', 'tg__1.LatencyMax.6',
+            'tg__1.RxThroughput', 'tg__1.TxThroughput',
+            'vnf__0.curr_packets_in', 'vnf__0.packets_dropped', 'vnf__0.packets_fwd',
         ]
 
         expected_output = [