kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. |
| 3 | # |
| 4 | # Use of this source code is governed by a BSD-style license |
| 5 | # that can be found in the LICENSE file in the root of the source |
| 6 | # tree. An additional intellectual property rights grant can be found |
| 7 | # in the file PATENTS. All contributing project authors may |
| 8 | # be found in the AUTHORS file in the root of the source tree. |
| 9 | |
| 10 | __author__ = 'kjellander@webrtc.org (Henrik Kjellander)' |
| 11 | |
| 12 | class DataHelper(object): |
| 13 | """ |
| 14 | Helper class for managing table data. |
| 15 | This class does not verify the consistency of the data tables sent into it. |
| 16 | """ |
| 17 | |
| 18 | def __init__(self, data_list, table_description, names_list, messages): |
| 19 | """ Initializes the DataHelper with data. |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 20 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 21 | Args: |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 22 | data_list: List of one or more data lists in the format that the |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 23 | Google Visualization Python API expects (list of dictionaries, one |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 24 | per row of data). See the gviz_api.DataTable documentation for more |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 25 | info. |
| 26 | table_description: dictionary describing the data types of all |
| 27 | columns in the data lists, as defined in the gviz_api.DataTable |
| 28 | documentation. |
| 29 | names_list: List of strings of what we're going to name the data |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 30 | columns after. Usually different runs of data collection. |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 31 | messages: List of strings we might append error messages to. |
| 32 | """ |
| 33 | self.data_list = data_list |
| 34 | self.table_description = table_description |
| 35 | self.names_list = names_list |
| 36 | self.messages = messages |
| 37 | self.number_of_datasets = len(data_list) |
| 38 | self.number_of_frames = len(data_list[0]) |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 39 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 40 | def CreateData(self, field_name, start_frame=0, end_frame=0): |
| 41 | """ Creates a data structure for a specified data field. |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 42 | |
| 43 | Creates a data structure (data type description dictionary and a list |
| 44 | of data dictionaries) to be used with the Google Visualization Python |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 45 | API. The frame_number column is always present and one column per data |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 46 | set is added and its field name is suffixed by _N where N is the number |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 47 | of the data set (0, 1, 2...) |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 48 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 49 | Args: |
| 50 | field_name: String name of the field, must be present in the data |
| 51 | structure this DataHelper was created with. |
| 52 | start_frame: Frame number to start at (zero indexed). Default: 0. |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 53 | end_frame: Frame number to be the last frame. If zero all frames |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 54 | will be included. Default: 0. |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 55 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 56 | Returns: |
| 57 | A tuple containing: |
| 58 | - a dictionary describing the columns in the data result_data_table below. |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 59 | This description uses the name for each data set specified by |
| 60 | names_list. |
| 61 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 62 | Example with two data sets named 'Foreman' and 'Crew': |
| 63 | { |
| 64 | 'frame_number': ('number', 'Frame number'), |
| 65 | 'ssim_0': ('number', 'Foreman'), |
| 66 | 'ssim_1': ('number', 'Crew'), |
| 67 | } |
| 68 | - a list containing dictionaries (one per row) with the frame_number |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 69 | column and one column of the specified field_name column per data |
| 70 | set. |
| 71 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 72 | Example with two data sets named 'Foreman' and 'Crew': |
| 73 | [ |
| 74 | {'frame_number': 0, 'ssim_0': 0.98, 'ssim_1': 0.77 }, |
| 75 | {'frame_number': 1, 'ssim_0': 0.81, 'ssim_1': 0.53 }, |
| 76 | ] |
| 77 | """ |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 78 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 79 | # Build dictionary that describes the data types |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 80 | result_table_description = {'frame_number': ('string', 'Frame number')} |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 81 | for dataset_index in range(self.number_of_datasets): |
| 82 | column_name = '%s_%s' % (field_name, dataset_index) |
| 83 | column_type = self.table_description[field_name][0] |
| 84 | column_description = self.names_list[dataset_index] |
| 85 | result_table_description[column_name] = (column_type, column_description) |
| 86 | |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 87 | # Build data table of all the data |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 88 | result_data_table = [] |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 89 | # We're going to have one dictionary per row. |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 90 | # Create that and copy frame_number values from the first data set |
| 91 | for source_row in self.data_list[0]: |
| 92 | row_dict = { 'frame_number': source_row['frame_number'] } |
| 93 | result_data_table.append(row_dict) |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 94 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 95 | # Pick target field data points from the all data tables |
| 96 | if end_frame == 0: # Default to all frames |
| 97 | end_frame = self.number_of_frames |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 98 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 99 | for dataset_index in range(self.number_of_datasets): |
| 100 | for row_number in range(start_frame, end_frame): |
| 101 | column_name = '%s_%s' % (field_name, dataset_index) |
| 102 | # Stop if any of the data sets are missing the frame |
| 103 | try: |
| 104 | result_data_table[row_number][column_name] = \ |
| 105 | self.data_list[dataset_index][row_number][field_name] |
| 106 | except IndexError: |
| 107 | self.messages.append("Couldn't find frame data for row %d " |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 108 | "for %s" % (row_number, self.names_list[dataset_index])) |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 109 | break |
kjellander@webrtc.org | 418bce5 | 2011-12-05 16:29:21 +0000 | [diff] [blame] | 110 | return result_table_description, result_data_table |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 111 | |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 112 | def GetOrdering(self, table_description): # pylint: disable=R0201 |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 113 | """ Creates a list of column names, ordered alphabetically except for the |
| 114 | frame_number column which always will be the first column. |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 115 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 116 | Args: |
| 117 | table_description: A dictionary of column definitions as defined by the |
| 118 | gviz_api.DataTable documentation. |
| 119 | Returns: |
| 120 | A list of column names, where frame_number is the first and the |
| 121 | remaining columns are sorted alphabetically. |
| 122 | """ |
| 123 | # The JSON data representation generated from gviz_api.DataTable.ToJSon() |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 124 | # must have frame_number as its first column in order for the chart to |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 125 | # use it as it's X-axis value series. |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 126 | # gviz_api.DataTable orders the columns by name by default, which will |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 127 | # be incorrect if we have column names that are sorted before frame_number |
| 128 | # in our data table. |
| 129 | columns_ordering = ['frame_number'] |
| 130 | # add all other columns: |
| 131 | for column in sorted(table_description.keys()): |
| 132 | if column != 'frame_number': |
| 133 | columns_ordering.append(column) |
kjellander@webrtc.org | 418bce5 | 2011-12-05 16:29:21 +0000 | [diff] [blame] | 134 | return columns_ordering |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 135 | |
| 136 | def CreateConfigurationTable(self, configurations): # pylint: disable=R0201 |
kjellander@webrtc.org | 418bce5 | 2011-12-05 16:29:21 +0000 | [diff] [blame] | 137 | """ Combines multiple test data configurations for display. |
| 138 | |
| 139 | Args: |
| 140 | configurations: List of one ore more configurations. Each configuration |
| 141 | is required to be a list of dictionaries with two keys: 'name' and |
| 142 | 'value'. |
| 143 | Example of a single configuration: |
| 144 | [ |
| 145 | {'name': 'name', 'value': 'VP8 software'}, |
| 146 | {'name': 'test_number', 'value': '0'}, |
| 147 | {'name': 'input_filename', 'value': 'foreman_cif.yuv'}, |
| 148 | ] |
| 149 | Returns: |
| 150 | A tuple containing: |
| 151 | - a dictionary describing the columns in the configuration table to be |
| 152 | displayed. All columns will have string as data type. |
| 153 | Example: |
| 154 | { |
| 155 | 'name': 'string', |
| 156 | 'test_number': 'string', |
| 157 | 'input_filename': 'string', |
| 158 | } |
| 159 | - a list containing dictionaries (one per configuration) with the |
| 160 | configuration column names mapped to the value for each test run: |
| 161 | |
| 162 | Example matching the columns above: |
| 163 | [ |
| 164 | {'name': 'VP8 software', |
| 165 | 'test_number': '12', |
| 166 | 'input_filename': 'foreman_cif.yuv' }, |
| 167 | {'name': 'VP8 hardware', |
| 168 | 'test_number': '5', |
| 169 | 'input_filename': 'foreman_cif.yuv' }, |
| 170 | ] |
| 171 | """ |
| 172 | result_description = {} |
| 173 | result_data = [] |
| 174 | |
| 175 | for configuration in configurations: |
| 176 | data = {} |
| 177 | result_data.append(data) |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame^] | 178 | for values in configuration: |
| 179 | name = values['name'] |
| 180 | value = values['value'] |
kjellander@webrtc.org | 418bce5 | 2011-12-05 16:29:21 +0000 | [diff] [blame] | 181 | result_description[name] = 'string' |
| 182 | data[name] = value |
| 183 | return result_description, result_data |