Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(15)

Side by Side Diff: webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/export.py

Issue 2723703002: Evaluation scores export library and CSS file for the exported HTML file (Closed)
Patch Set: merge Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. 1 # Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
2 # 2 #
3 # Use of this source code is governed by a BSD-style license 3 # Use of this source code is governed by a BSD-style license
4 # that can be found in the LICENSE file in the root of the source 4 # that can be found in the LICENSE file in the root of the source
5 # tree. An additional intellectual property rights grant can be found 5 # tree. An additional intellectual property rights grant can be found
6 # in the file PATENTS. All contributing project authors may 6 # in the file PATENTS. All contributing project authors may
7 # be found in the AUTHORS file in the root of the source tree. 7 # be found in the AUTHORS file in the root of the source tree.
8 8
9 import logging 9 import logging
10 import os 10 import os
11 import re
12
11 13
12 class HtmlExport(object): 14 class HtmlExport(object):
13 15
14 def __init__(self, output_path, output_filename): 16 # Path to CSS and JS files.
15 self._output_path = output_path 17 _PATH = os.path.dirname(os.path.realpath(__file__))
16 self._output_filename = output_filename 18
19 # CSS file parameters.
20 _CSS_FILEPATH = os.path.join(_PATH, 'results.css')
21 _INLINE_CSS = True
22
23 _NEW_LINE = '\n'
24
25 def __init__(self, output_filepath):
26 self._noise_names = None
27 self._noise_params = None
28 self._output_filepath = output_filepath
17 29
18 def export(self, scores): 30 def export(self, scores):
19 logging.debug('%d score names found', len(scores)) 31 """
20 output_filepath = os.path.join(self._output_path, self._output_filename) 32 Export the scores into an HTML file.
21 33
22 # TODO(alessio): remove once implemented 34 Args:
35 scores: nested dictionary containing the scores.
36 """
37 # Generate one table for each evaluation score.
38 tables = []
39 for score_name in sorted(scores.keys()):
40 tables.append(self._build_score_table(score_name, scores[score_name]))
41
42 # Create the html file.
43 html = (
44 '<html>' +
45 self._build_header() +
46 '<body>' +
47 '<h1>Results from {}</h1>'.format(self._output_filepath) +
48 self._NEW_LINE.join(tables) +
49 '</body>' +
50 '</html>')
51
52 self._save(self._output_filepath, html)
53
54 def _build_header(self):
55 """
56 HTML file header with page title and either embedded or linked CSS and JS
57 files.
58 """
59 html = ['<head>', '<title>Results</title>']
60
61 # CSS.
62 if self._INLINE_CSS:
63 # Embed.
64 html.append('<style>')
65 with open(self._CSS_FILEPATH) as f:
66 for l in f:
67 html.append(l.strip())
68 html.append('</style>')
69 else:
70 # Link.
71 html.append('<link rel="stylesheet" type="text/css" '
72 'href="file://{}?">'.format(self._CSS_FILEPATH))
73
74 html.append('</head>')
75
76 return self._NEW_LINE.join(html)
77
78 def _build_score_table(self, score_name, scores):
79 """
80 Generate a table for a specific evaluation score (e.g., POLQA).
81 """
82 config_names = sorted(scores.keys())
83 input_names = sorted(scores[config_names[0]].keys())
84 rows = [self._table_row(
85 score_name, config_name, scores[config_name], input_names) for (
86 config_name) in config_names]
87
88 html = (
89 '<table celpadding="0" cellspacing="0">' +
90 '<thead><tr>{}</tr></thead>'.format(
91 self._table_header(score_name, input_names)) +
92 '<tbody>' +
93 '<tr>' + '</tr><tr>'.join(rows) + '</tr>' +
94 '</tbody>' +
95 '</table>' + self._legend())
96
97 return html
98
99 def _table_header(self, score_name, input_names):
100 """
101 Generate a table header with the name of the evaluation score in the first
102 column and then one column for each probing signal.
103 """
104 html = (
105 '<th>{}</th>'.format(self._format_name(score_name)) +
106 '<th>' + '</th><th>'.join(
107 [self._format_name(name) for name in input_names]) + '</th>')
108 return html
109
110 def _table_row(self, score_name, config_name, scores, input_names):
111 """
112 Generate a table body row with the name of the APM configuration file in the
113 first column and then one column for each probing singal.
114 """
115 cells = [self._table_cell(
116 scores[input_name], score_name, config_name, input_name) for (
117 input_name) in input_names]
118 html = ('<td>{}</td>'.format(self._format_name(config_name)) +
119 '<td>' + '</td><td>'.join(cells) + '</td>')
120 return html
121
122 def _table_cell(self, scores, score_name, config_name, input_name):
123 """
124 Generate a table cell content with all the scores for the current evaluation
125 score, APM configuration, and probing signal.
126 """
127 # Init noise generator names and noise parameters cache (if not done).
128 if self._noise_names is None:
129 self._noise_names = sorted(scores.keys())
130 self._noise_params = {noise_name: sorted(scores[noise_name].keys()) for (
131 noise_name) in self._noise_names}
132
133 # For each noisy input (that is a pair of noise generator name and noise
134 # generator parameters), add an item with the score and its metadata.
135 items = []
136 for name_index, noise_name in enumerate(self._noise_names):
137 for params_index, noise_params in enumerate(
138 self._noise_params[noise_name]):
139
140 # Init.
141 score_value = '?'
142 metadata = ''
143
144 # Extract score value and its metadata.
145 try:
146 data = scores[noise_name][noise_params]
147 score_value = '{0:f}'.format(data['score'])
148 metadata = (
149 '<input type="hidden" name="noise_name" value="{}"/>'
150 '<input type="hidden" name="noise_params" value="{}"/>'
151 '<input type="hidden" name="audio_in" value="file://{}"/>'
152 '<input type="hidden" name="audio_out" value="file://{}"/>'
153 '<input type="hidden" name="audio_ref" value="file://{}"/>'
154 ).format(
155 noise_name,
156 noise_params,
157 data['audio_in_filepath'],
158 data['audio_out_filepath'],
159 data['audio_ref_filepath'])
160 except TypeError:
161 logging.warning(
162 'missing score found: <score:%s> <config:%s> <input:%s> '
163 '<noise:%s> <params:%s>', score_name, config_name, input_name,
164 noise_name, noise_params)
165
166 # Add the score.
167 items.append(
168 '<div class="noise-desc">[{0:d}, {1:d}]{2}</div>'
169 '<div class="value">{3}</div>'.format(
170 name_index, params_index, metadata, score_value))
171
172 html = (
173 '<div class="score">' +
174 '</div><div class="score">'.join(items) +
175 '</div>')
176
177 return html
178
179 def _legend(self):
180 """
181 Generate the legend for each noise generator name and parameters pair.
182 """
183 items = []
184 for name_index, noise_name in enumerate(self._noise_names):
185 for params_index, noise_params in enumerate(
186 self._noise_params[noise_name]):
187 items.append('<div class="noise-desc">[{0:d}, {1:d}]</div>: {2} noise, '
188 '{3}'.format(name_index, params_index, noise_name,
189 noise_params))
190 html = (
191 '<div class="legend"><div>' +
192 '</div><div>'.join(items) + '</div></div>')
193
194 return html
195
196 @classmethod
197 def _save(cls, output_filepath, html):
23 with open(output_filepath, 'w') as f: 198 with open(output_filepath, 'w') as f:
24 f.write('APM Quality Assessment scores\n') 199 f.write(html)
25 200
26 return output_filepath 201 @classmethod
202 def _format_name(cls, name):
203 return re.sub(r'[_\-]', ' ', name)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698