blob: 80cc78f9e51bc7ba1225ad3be67263ab6acfbd77 [file] [log] [blame]
kjellander@webrtc.org689cb302011-11-07 15:25:47 +00001#!/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
12class 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.org5d3713932013-03-07 09:59:43 +000020
kjellander@webrtc.org689cb302011-11-07 15:25:47 +000021 Args:
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +000022 data_list: List of one or more data lists in the format that the
kjellander@webrtc.org689cb302011-11-07 15:25:47 +000023 Google Visualization Python API expects (list of dictionaries, one
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +000024 per row of data). See the gviz_api.DataTable documentation for more
kjellander@webrtc.org689cb302011-11-07 15:25:47 +000025 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.org5d3713932013-03-07 09:59:43 +000030 columns after. Usually different runs of data collection.
kjellander@webrtc.org689cb302011-11-07 15:25:47 +000031 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.org5d3713932013-03-07 09:59:43 +000039
kjellander@webrtc.org689cb302011-11-07 15:25:47 +000040 def CreateData(self, field_name, start_frame=0, end_frame=0):
41 """ Creates a data structure for a specified data field.
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +000042
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.org689cb302011-11-07 15:25:47 +000045 API. The frame_number column is always present and one column per data
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +000046 set is added and its field name is suffixed by _N where N is the number
kjellander@webrtc.org689cb302011-11-07 15:25:47 +000047 of the data set (0, 1, 2...)
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +000048
kjellander@webrtc.org689cb302011-11-07 15:25:47 +000049 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.org5d3713932013-03-07 09:59:43 +000053 end_frame: Frame number to be the last frame. If zero all frames
kjellander@webrtc.org689cb302011-11-07 15:25:47 +000054 will be included. Default: 0.
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +000055
kjellander@webrtc.org689cb302011-11-07 15:25:47 +000056 Returns:
57 A tuple containing:
58 - a dictionary describing the columns in the data result_data_table below.
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +000059 This description uses the name for each data set specified by
60 names_list.
61
kjellander@webrtc.org689cb302011-11-07 15:25:47 +000062 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.org5d3713932013-03-07 09:59:43 +000069 column and one column of the specified field_name column per data
70 set.
71
kjellander@webrtc.org689cb302011-11-07 15:25:47 +000072 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.org5d3713932013-03-07 09:59:43 +000078
kjellander@webrtc.org689cb302011-11-07 15:25:47 +000079 # Build dictionary that describes the data types
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +000080 result_table_description = {'frame_number': ('string', 'Frame number')}
kjellander@webrtc.org689cb302011-11-07 15:25:47 +000081 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.org5d3713932013-03-07 09:59:43 +000087 # Build data table of all the data
kjellander@webrtc.org689cb302011-11-07 15:25:47 +000088 result_data_table = []
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +000089 # We're going to have one dictionary per row.
kjellander@webrtc.org689cb302011-11-07 15:25:47 +000090 # 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.org5d3713932013-03-07 09:59:43 +000094
kjellander@webrtc.org689cb302011-11-07 15:25:47 +000095 # 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.org5d3713932013-03-07 09:59:43 +000098
kjellander@webrtc.org689cb302011-11-07 15:25:47 +000099 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.org5d3713932013-03-07 09:59:43 +0000108 "for %s" % (row_number, self.names_list[dataset_index]))
kjellander@webrtc.org689cb302011-11-07 15:25:47 +0000109 break
kjellander@webrtc.org418bce52011-12-05 16:29:21 +0000110 return result_table_description, result_data_table
kjellander@webrtc.org689cb302011-11-07 15:25:47 +0000111
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +0000112 def GetOrdering(self, table_description): # pylint: disable=R0201
kjellander@webrtc.org689cb302011-11-07 15:25:47 +0000113 """ Creates a list of column names, ordered alphabetically except for the
114 frame_number column which always will be the first column.
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +0000115
kjellander@webrtc.org689cb302011-11-07 15:25:47 +0000116 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.org5d3713932013-03-07 09:59:43 +0000124 # must have frame_number as its first column in order for the chart to
kjellander@webrtc.org689cb302011-11-07 15:25:47 +0000125 # use it as it's X-axis value series.
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +0000126 # gviz_api.DataTable orders the columns by name by default, which will
kjellander@webrtc.org689cb302011-11-07 15:25:47 +0000127 # 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.org418bce52011-12-05 16:29:21 +0000134 return columns_ordering
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +0000135
136 def CreateConfigurationTable(self, configurations): # pylint: disable=R0201
kjellander@webrtc.org418bce52011-12-05 16:29:21 +0000137 """ 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.org5d3713932013-03-07 09:59:43 +0000178 for values in configuration:
179 name = values['name']
180 value = values['value']
kjellander@webrtc.org418bce52011-12-05 16:29:21 +0000181 result_description[name] = 'string'
182 data[name] = value
183 return result_description, result_data