Skip to content

Commit bb58dc9

Browse files
[TTS] Updated TTS comparison report
1 parent 3998328 commit bb58dc9

6 files changed

Lines changed: 261 additions & 14 deletions

File tree

scripts/tts_comparison_report/reporting/components/boxplots.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ class BoxPlotsConfig:
5050
outlier_markersize: float = 3.0
5151
outlier_alpha: float = 0.5
5252

53+
unavailable_text_color: str = "#CD5C5C"
54+
5355

5456
def _style_boxplot(
5557
bp: dict[str, PathPatch],
@@ -98,6 +100,42 @@ def _add_mean_ci_labels(
98100
ax.text(x + x_offset, mean + y_offset, label, ha="left", va="center", fontsize=cfg.fontsize)
99101

100102

103+
def _mean_exceeds_plot_range(
104+
baseline: np.ndarray,
105+
candidate: np.ndarray,
106+
metric: DistributionMetricSpec,
107+
) -> bool:
108+
if metric.plot_range is None:
109+
return False
110+
111+
upper_limit = metric.plot_range[1]
112+
return bool(baseline.mean() > upper_limit or candidate.mean() > upper_limit)
113+
114+
115+
def _render_plot_not_shown(
116+
ax: Axes,
117+
metric: DistributionMetricSpec,
118+
cfg: BoxPlotsConfig,
119+
) -> None:
120+
if metric.plot_range is None:
121+
raise ValueError(f"Metric '{metric.report_name}' does not define a plot range.")
122+
123+
upper_limit = metric.plot_range[1]
124+
125+
ax.set_title(metric.report_name, fontsize=cfg.fontsize_title)
126+
ax.set_axis_off()
127+
ax.text(
128+
0.5,
129+
0.5,
130+
f"Plot not shown.\nMean {metric.report_name} exceeds {upper_limit:.0%} display limit.",
131+
ha="center",
132+
va="center",
133+
color=cfg.unavailable_text_color,
134+
fontsize=cfg.fontsize_title,
135+
transform=ax.transAxes,
136+
)
137+
138+
101139
def _configure_boxplot_axis(
102140
ax: Axes,
103141
metric: DistributionMetricSpec,
@@ -170,6 +208,10 @@ def prepare_boxplots(
170208
ax = axs[plot_idx]
171209
plot_idx += 1
172210

211+
if _mean_exceeds_plot_range(baseline, candidate, metric):
212+
_render_plot_not_shown(ax, metric, cfg)
213+
continue
214+
173215
bp = ax.boxplot(
174216
[baseline, candidate],
175217
positions=[1, 2],

scripts/tts_comparison_report/reporting/constants.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@
5151
# Number of decimal digits used when formatting p-values in statistical tests.
5252
P_VAL_ROUND_DIGITS: int = 4
5353

54+
# Default signature version used to sign S3 client requests.
55+
S3_SIGNATURE_VERSION: str = "s3"
56+
5457
# Default lifetime of generated S3 presigned links in seconds (one year).
5558
S3_LINK_EXPIRES_IN: int = 31536000
5659

scripts/tts_comparison_report/reporting/models.py

100644100755
Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,17 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414
import hashlib
15+
import math
1516
from dataclasses import dataclass, field
1617
from enum import Enum
1718
from io import BytesIO
1819
from pathlib import Path
1920
from typing import Any, Optional, Self
2021

21-
from scripts.tts_comparison_report.reporting.constants import TQDM_NCOLS
22+
from scripts.tts_comparison_report.reporting.constants import BENCHMARK_META, TQDM_NCOLS
2223
from scripts.tts_comparison_report.reporting.storage import BaseStorage
2324
from tqdm import tqdm
2425

25-
2626
_REQUIRED_SAMPLE_ID_KEYS: list[str] = [
2727
"pred_audio_filepath",
2828
"gt_text",
@@ -309,6 +309,8 @@ def from_storage(
309309
310310
Raises:
311311
FileNotFoundError: If the expected results directory is missing.
312+
ValueError: If a recognized benchmark directory does not use the required
313+
'<configuration>_<language>_<benchmark>' naming format.
312314
"""
313315
obj = cls(name=bucket_name, path=bucket_path)
314316
results_path = bucket_path / bucket_structure.eval_output_subdir
@@ -334,8 +336,23 @@ def from_storage(
334336
storage=storage,
335337
)
336338
if obj.configuration_str is None:
337-
suffix = f"_{name}"
338-
obj.configuration_str = dir_name[: -len(suffix)]
339+
lang = BENCHMARK_META[name]
340+
suffix = f"_{lang}_{name}"
341+
342+
if not dir_name.endswith(suffix):
343+
raise ValueError(
344+
f"Unsupported results directory name '{dir_name}' for benchmark '{name}': "
345+
f"expected '<configuration>{suffix}'."
346+
)
347+
348+
configuration_str = dir_name[: -len(suffix)]
349+
350+
if not configuration_str:
351+
raise ValueError(
352+
f"Missing configuration prefix in results directory '{dir_name}' for benchmark '{name}': "
353+
f"expected '<configuration>{suffix}'."
354+
)
355+
obj.configuration_str = configuration_str
339356

340357
return obj
341358

@@ -391,11 +408,19 @@ def get_metric_avg_value(
391408
if metric_name not in metrics:
392409
return None
393410

411+
value = metrics[metric_name]
412+
413+
if value is None:
414+
return None
415+
394416
value = _validate_numeric_metric_value(
395-
value=metrics[metric_name],
417+
value=value,
396418
metric_name=metric_name,
397419
context=f"averaged metrics for benchmark '{benchmark_name}'",
398420
)
421+
if math.isnan(value):
422+
return None
423+
399424
return value
400425

401426
def _get_metric_stats(
@@ -423,6 +448,11 @@ def _get_metric_stats(
423448
metric_name=metric_name,
424449
context=validation_context,
425450
)
451+
if math.isnan(value):
452+
raise ValueError(
453+
f"Metric '{metric_name}' in {validation_context} contains NaN; "
454+
"statistical tests and box plots require non-NaN samples."
455+
)
426456
output.append(value)
427457

428458
if not output:

scripts/tts_comparison_report/reporting/s3_client.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import boto3
1818
from botocore.config import Config
1919

20+
from scripts.tts_comparison_report.reporting.constants import S3_SIGNATURE_VERSION
21+
2022

2123
@dataclass
2224
class S3Config:
@@ -26,6 +28,7 @@ class S3Config:
2628
endpoint_url: str
2729
region_name: str
2830
connect_timeout: int = 10
31+
signature_version: str = S3_SIGNATURE_VERSION
2932

3033

3134
class S3Client:
@@ -38,13 +41,17 @@ def __init__(
3841
aws_secret_access_key: str,
3942
) -> None:
4043
self.cfg = cfg
44+
config = Config(
45+
connect_timeout=cfg.connect_timeout,
46+
signature_version=cfg.signature_version,
47+
)
4148
self.client = boto3.client(
4249
"s3",
4350
endpoint_url=cfg.endpoint_url,
4451
aws_access_key_id=aws_access_key_id,
4552
aws_secret_access_key=aws_secret_access_key,
4653
region_name=cfg.region_name,
47-
config=Config(connect_timeout=cfg.connect_timeout),
54+
config=config,
4855
)
4956

5057
def upload_fileobj(

scripts/tts_comparison_report/templates/audio_report.jinja

Lines changed: 86 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,38 @@
6565
6666
.sidebar-left {
6767
grid-column: 1;
68-
padding-right: 20px;
68+
min-width: 0;
69+
height: var(--sidebar-height);
70+
padding-right: 12px;
71+
max-height: calc(100vh - 64px);
72+
73+
display: flex;
74+
flex-direction: column;
75+
overflow: hidden;
76+
}
77+
78+
.sidebar-left > h2 {
79+
flex: 0 0 auto;
80+
align-self: flex-start;
81+
}
82+
83+
.sidebar-left > ul {
84+
flex: 1 1 auto;
85+
min-height: 0;
86+
padding-bottom: 64px;
87+
overflow-y: auto;
88+
overflow-x: hidden;
89+
overscroll-behavior: contain;
90+
91+
/* Hide scrollbar */
92+
/* Firefox and old Microsoft browsers */
93+
scrollbar-width: none;
94+
-ms-overflow-style: none;
95+
}
96+
97+
/* Chrome, Edge, and Safari */
98+
.sidebar-left > ul::-webkit-scrollbar {
99+
display: none;
69100
}
70101
71102
.sidebar-right {
@@ -112,8 +143,8 @@
112143
113144
.sidebar ul ul {
114145
margin-top: 8px;
115-
margin-left: 14px;
116-
padding-left: 12px;
146+
margin-left: 6px;
147+
padding-left: 8px;
117148
border-left: 1px solid var(--border);
118149
}
119150
@@ -137,6 +168,24 @@
137168
margin-bottom: 1.5rem;
138169
}
139170
171+
.benchmark-link {
172+
position: relative;
173+
display: block;
174+
max-width: 100%;
175+
padding-left: 14px;
176+
overflow-wrap: anywhere;
177+
word-break: break-word;
178+
}
179+
180+
.benchmark-link::before {
181+
content: "";
182+
position: absolute;
183+
left: 0;
184+
top: 0;
185+
color: currentColor;
186+
font-weight: 700;
187+
}
188+
140189
.link-comment {
141190
color: var(--highlighted);
142191
font-size: 1rem;
@@ -264,7 +313,7 @@
264313
<h2>Content</h2>
265314
<ul>
266315
{% for section_id, section_name in benchmark_section_info %}
267-
<li><a href="#{{ section_id }}">{{ section_name }}</a></li>
316+
<li><a class="benchmark-link" href="#{{ section_id }}">{{ section_name }}</a></li>
268317
{% endfor %}
269318
</ul>
270319
</aside>
@@ -322,16 +371,20 @@
322371
<script>
323372
const ACTIVE_SECTION_OFFSET = 140;
324373
const BOTTOM_THRESHOLD = 20;
374+
const SIDEBAR_STICKY_TOP = 32;
325375
326376
const sections = [...document.querySelectorAll("h2[id], h3[id]")];
327377
const navLinks = [...document.querySelectorAll('.sidebar-left a[href^="#"]')];
378+
let clickedNavLink = null;
328379
329380
const linkMap = new Map();
330381
navLinks.forEach(link => {
331382
const href = link.getAttribute("href");
332383
linkMap.set(href.slice(1), link);
333384
334385
link.addEventListener("click", () => {
386+
clickedNavLink = link;
387+
335388
navLinks.forEach(l => l.classList.remove("active"));
336389
link.classList.add("active");
337390
});
@@ -358,12 +411,41 @@
358411
const activeLink = linkMap.get(currentSection);
359412
if (activeLink) {
360413
activeLink.classList.add("active");
414+
415+
if (activeLink !== clickedNavLink) {
416+
clickedNavLink = null;
417+
activeLink.scrollIntoView({
418+
block: "nearest",
419+
inline: "nearest",
420+
behavior: "auto",
421+
});
422+
}
361423
}
362424
}
363425
}
364426
427+
const sidebar = document.querySelector(".sidebar-left");
428+
429+
function updateSidebarHeight() {
430+
if (!sidebar) return;
431+
432+
const top = Math.max(
433+
sidebar.getBoundingClientRect().top,
434+
SIDEBAR_STICKY_TOP
435+
);
436+
sidebar.style.setProperty(
437+
"--sidebar-height",
438+
`${Math.max(0, window.innerHeight - top)}px`
439+
);
440+
}
441+
365442
window.addEventListener("scroll", updateActiveLink, { passive: true });
443+
window.addEventListener("scroll", updateSidebarHeight, { passive: true });
444+
window.addEventListener("resize", updateSidebarHeight);
366445
window.addEventListener("load", updateActiveLink);
446+
window.addEventListener("load", updateSidebarHeight);
447+
448+
updateSidebarHeight();
367449
</script>
368450

369451
</body>

0 commit comments

Comments
 (0)