OLD | NEW |
| (Empty) |
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. | |
20 | |
21 Args: | |
22 data_list: List of one or more data lists in the format that the | |
23 Google Visualization Python API expects (list of dictionaries, one | |
24 per row of data). See the gviz_api.DataTable documentation for more | |
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 | |
30 columns after. Usually different runs of data collection. | |
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]) | |
39 | |
40 def CreateData(self, field_name, start_frame=0, end_frame=0): | |
41 """ Creates a data structure for a specified data field. | |
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 | |
45 API. The frame_number column is always present and one column per data | |
46 set is added and its field name is suffixed by _N where N is the number | |
47 of the data set (0, 1, 2...) | |
48 | |
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. | |
53 end_frame: Frame number to be the last frame. If zero all frames | |
54 will be included. Default: 0. | |
55 | |
56 Returns: | |
57 A tuple containing: | |
58 - a dictionary describing the columns in the data result_data_table below. | |
59 This description uses the name for each data set specified by | |
60 names_list. | |
61 | |
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 | |
69 column and one column of the specified field_name column per data | |
70 set. | |
71 | |
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 """ | |
78 | |
79 # Build dictionary that describes the data types | |
80 result_table_description = {'frame_number': ('string', 'Frame number')} | |
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 | |
87 # Build data table of all the data | |
88 result_data_table = [] | |
89 # We're going to have one dictionary per row. | |
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) | |
94 | |
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 | |
98 | |
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 " | |
108 "for %s" % (row_number, self.names_list[dataset_index])) | |
109 break | |
110 return result_table_description, result_data_table | |
111 | |
112 def GetOrdering(self, table_description): # pylint: disable=R0201 | |
113 """ Creates a list of column names, ordered alphabetically except for the | |
114 frame_number column which always will be the first column. | |
115 | |
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() | |
124 # must have frame_number as its first column in order for the chart to | |
125 # use it as it's X-axis value series. | |
126 # gviz_api.DataTable orders the columns by name by default, which will | |
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) | |
134 return columns_ordering | |
135 | |
136 def CreateConfigurationTable(self, configurations): # pylint: disable=R0201 | |
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) | |
178 for values in configuration: | |
179 name = values['name'] | |
180 value = values['value'] | |
181 result_description[name] = 'string' | |
182 data[name] = value | |
183 return result_description, result_data | |
OLD | NEW |