Add parser to support jsTree in report 61/60061/14
authorPatrice Buriez <patrice.buriez@intel.com>
Fri, 9 Nov 2018 09:45:31 +0000 (10:45 +0100)
committerPatrice Buriez <patrice.buriez@intel.com>
Thu, 22 Nov 2018 19:18:12 +0000 (20:18 +0100)
Allow the user to select what data to show in the report using
jsTree to navigate a hierarchical metrics list.

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

Change-Id: I86d782a0a70b80a1cdfaab2f41afb7668066cbf7
Signed-off-by: Emma Foley <emma.l.foley@intel.com>
Signed-off-by: Patrice Buriez <patrice.buriez@intel.com>
yardstick/benchmark/core/report.py
yardstick/tests/unit/benchmark/core/test_report.py

index a43efec..a484a49 100644 (file)
@@ -1,7 +1,7 @@
-#############################################################################
-# Copyright (c) 2017 Rajesh Kudaka
+##############################################################################
+# Copyright (c) 2017 Rajesh Kudaka <4k.rajesh@gmail.com>
+# Copyright (c) 2018 Intel Corporation.
 #
-# Author: Rajesh Kudaka 4k.rajesh@gmail.com
 # All rights reserved. This program and the accompanying materials
 # are made available under the terms of the Apache License, Version 2.0
 # which accompanies this distribution, and is available at
@@ -22,6 +22,66 @@ from yardstick.common import constants as consts
 from yardstick.common.utils import cliargs
 
 
+class JSTree(object):
+    """Data structure to parse data for use with the JS library jsTree"""
+    def __init__(self):
+        self._created_nodes = ['#']
+        self.jstree_data = []
+
+    def _create_node(self, _id):
+        """Helper method for format_for_jstree to create each node.
+
+        Creates the node (and any required parents) and keeps track
+        of the created nodes.
+
+        :param _id: (string) id of the node to be created
+        :return: None
+        """
+        components = _id.split(".")
+
+        if len(components) == 1:
+            text = components[0]
+            parent_id = "#"
+        else:
+            text = components[-1]
+            parent_id = ".".join(components[:-1])
+            # make sure the parent has been created
+            if not parent_id in self._created_nodes:
+                self._create_node(parent_id)
+
+        self.jstree_data.append({"id": _id, "text": text, "parent": parent_id})
+        self._created_nodes.append(_id)
+
+    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 name for each metric e.g.:
+
+            [{'data': [0, ], 'name': 'tg__0.DropPackets'},
+             {'data': [548, ], 'name': '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
+        number of parent nodes e.g.::
+
+            [{"id": "tg__0", "text": "tg__0", "parent": "#"},
+             {"id": "tg__0.DropPackets", "text": "DropPackets", "parent": "tg__0"},
+             {"id": "tg__0.LatencyAvg", "text": "LatencyAvg", "parent": "tg__0"},
+             {"id": "tg__0.LatencyAvg.5", "text": "5", "parent": "tg__0.LatencyAvg"},]
+
+        :param data: (list) data to be converted
+        :return: list
+        """
+        self._created_nodes = ['#']
+        self.jstree_data = []
+
+        for item in data:
+            self._create_node(item["name"])
+
+        return self.jstree_data
+
+
 class Report(object):
     """Report commands.
 
index 2827f8e..3e80dcc 100644 (file)
@@ -1,5 +1,6 @@
 ##############################################################################
 # Copyright (c) 2017 Rajesh Kudaka.
+# Copyright (c) 2018 Intel Corporation.
 #
 # All rights reserved. This program and the accompanying materials
 # are made available under the terms of the Apache License, Version 2.0
@@ -27,6 +28,75 @@ BAD_YAML_NAME = 'F@KE_NAME'
 BAD_TASK_ID = 'aaaaaa-aaaaaaaa-aaaaaaaaaa-aaaaaa'
 
 
+class JSTreeTestCase(unittest.TestCase):
+
+    def setUp(self):
+        self.jstree = report.JSTree()
+
+    def test__create_node(self):
+        _id = "tg__0.DropPackets"
+
+        expected_data = [
+            {"id": "tg__0", "text": "tg__0", "parent": "#"},
+            {"id": "tg__0.DropPackets", "text": "DropPackets", "parent": "tg__0"}
+        ]
+        self.jstree._create_node(_id)
+
+        self.assertEqual(self.jstree._created_nodes, ['#', 'tg__0', 'tg__0.DropPackets'])
+        self.assertEqual(self.jstree.jstree_data, expected_data)
+
+    def test_format_for_jstree(self):
+        data = [
+            {'data': [0, ], 'name': 'tg__0.DropPackets'},
+            {'data': [548, ], 'name': 'tg__0.LatencyAvg.5'},
+            {'data': [1172, ], 'name': 'tg__0.LatencyAvg.6'},
+            {'data': [1001, ], 'name': 'tg__0.LatencyMax.5'},
+            {'data': [1468, ], 'name': 'tg__0.LatencyMax.6'},
+            {'data': [18.11, ], 'name': 'tg__0.RxThroughput'},
+            {'data': [18.11, ], 'name': 'tg__0.TxThroughput'},
+            {'data': [0, ], 'name': 'tg__1.DropPackets'},
+            {'data': [548, ], 'name': 'tg__1.LatencyAvg.5'},
+            {'data': [1172, ], 'name': 'tg__1.LatencyAvg.6'},
+            {'data': [1001, ], 'name': 'tg__1.LatencyMax.5'},
+            {'data': [1468, ], 'name': 'tg__1.LatencyMax.6'},
+            {'data': [18.1132084505, ], 'name': 'tg__1.RxThroughput'},
+            {'data': [18.1157260383, ], 'name': 'tg__1.TxThroughput'},
+            {'data': [9057888, ], 'name': 'vnf__0.curr_packets_in'},
+            {'data': [0, ], 'name': 'vnf__0.packets_dropped'},
+            {'data': [617825443, ], 'name': 'vnf__0.packets_fwd'},
+        ]
+
+        expected_output = [
+            {"id": "tg__0", "text": "tg__0", "parent": "#"},
+                {"id": "tg__0.DropPackets", "text": "DropPackets", "parent": "tg__0"},
+                {"id": "tg__0.LatencyAvg", "text": "LatencyAvg", "parent": "tg__0"},
+                    {"id": "tg__0.LatencyAvg.5", "text": "5", "parent": "tg__0.LatencyAvg"},
+                    {"id": "tg__0.LatencyAvg.6", "text": "6", "parent": "tg__0.LatencyAvg"},
+                {"id": "tg__0.LatencyMax", "text": "LatencyMax", "parent": "tg__0"},
+                    {"id": "tg__0.LatencyMax.5", "text": "5", "parent": "tg__0.LatencyMax"},
+                    {"id": "tg__0.LatencyMax.6", "text": "6", "parent": "tg__0.LatencyMax"},
+                {"id": "tg__0.RxThroughput", "text": "RxThroughput", "parent": "tg__0"},
+                {"id": "tg__0.TxThroughput", "text": "TxThroughput", "parent": "tg__0"},
+            {"id": "tg__1", "text": "tg__1", "parent": "#"},
+                {"id": "tg__1.DropPackets", "text": "DropPackets", "parent": "tg__1"},
+                {"id": "tg__1.LatencyAvg", "text": "LatencyAvg", "parent": "tg__1"},
+                    {"id": "tg__1.LatencyAvg.5", "text": "5", "parent": "tg__1.LatencyAvg"},
+                    {"id": "tg__1.LatencyAvg.6", "text": "6", "parent": "tg__1.LatencyAvg"},
+                {"id": "tg__1.LatencyMax", "text": "LatencyMax", "parent": "tg__1"},
+                    {"id": "tg__1.LatencyMax.5", "text": "5", "parent": "tg__1.LatencyMax"},
+                    {"id": "tg__1.LatencyMax.6", "text": "6", "parent": "tg__1.LatencyMax"},
+                {"id": "tg__1.RxThroughput", "text": "RxThroughput", "parent": "tg__1"},
+                {"id": "tg__1.TxThroughput", "text": "TxThroughput", "parent": "tg__1"},
+            {"id": "vnf__0", "text": "vnf__0", "parent": "#"},
+                {"id": "vnf__0.curr_packets_in", "text": "curr_packets_in", "parent": "vnf__0"},
+                {"id": "vnf__0.packets_dropped", "text": "packets_dropped", "parent": "vnf__0"},
+                {"id": "vnf__0.packets_fwd", "text": "packets_fwd", "parent": "vnf__0"},
+        ]
+
+        result = self.jstree.format_for_jstree(data)
+        self.assertEqual(expected_output, result)
+
+
 class ReportTestCase(unittest.TestCase):
 
     def setUp(self):