best-man-speech-practice / evals /extract_transcript_stats.py
pjc43's picture
Sync from GitHub via hub-sync
0b79533 verified
Raw
History Blame Contribute Delete
4.27 kB
from __future__ import annotations
import argparse
import csv
import json
import sys
from pathlib import Path
from typing import Any
REPO_ROOT = Path(__file__).resolve().parents[1]
EVALS_DIR = Path(__file__).resolve().parent
DEFAULT_INPUT = EVALS_DIR / "data" / "master_transcripts.csv"
DEFAULT_OUTPUT = EVALS_DIR / "private" / "master_transcript_stats.csv"
TEXT_FIELDS = {"transcript", "text", "gold_feedback"}
STAT_FIELDS = (
"computed_word_count",
"computed_filler_count",
"computed_duration_seconds",
"computed_duration_mmss",
"computed_wpm",
"computed_wpm_band",
"computed_filler_per_min",
"computed_filler_band",
"computed_notable_fillers",
"computed_filler_counts_json",
)
def main() -> None:
args = parse_args()
sys.path.insert(0, str(REPO_ROOT))
sys.path.insert(0, str(EVALS_DIR))
from eval_data import build_stats
rows = read_csv(args.input)
if not rows:
raise SystemExit(f"No rows found in {args.input}")
output_rows = []
for index, row in enumerate(rows, start=1):
transcript = str(row.get("transcript") or row.get("text") or "").strip()
if not transcript:
raise ValueError(f"Row {index} has no transcript/text value")
stats = build_stats(row, transcript)
output_row = metadata_for_output(row, include_text=args.include_text)
output_row.update(flatten_stats(stats))
output_rows.append(output_row)
args.output.parent.mkdir(parents=True, exist_ok=True)
write_csv(args.output, output_rows)
print(f"Wrote {len(output_rows)} rows to {args.output}")
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Extract deterministic transcript stats to CSV.")
parser.add_argument("--input", type=Path, default=DEFAULT_INPUT, help="Input transcript CSV.")
parser.add_argument("--output", type=Path, default=DEFAULT_OUTPUT, help="Output stats CSV.")
parser.add_argument(
"--include-text",
action="store_true",
help="Include raw transcript text in the output. Keep the output private if enabled.",
)
return parser.parse_args()
def read_csv(path: Path) -> list[dict[str, Any]]:
with path.open("r", encoding="utf-8", newline="") as input_file:
return list(csv.DictReader(input_file))
def metadata_for_output(row: dict[str, Any], *, include_text: bool) -> dict[str, Any]:
output = {}
for key, value in row.items():
if not include_text and key in TEXT_FIELDS:
continue
output[key] = value
return output
def flatten_stats(stats: dict[str, Any]) -> dict[str, Any]:
notable_fillers = stats.get("notable_fillers") or []
return {
"computed_word_count": stats.get("word_count", ""),
"computed_filler_count": stats.get("filler_count", ""),
"computed_duration_seconds": stats.get("duration_seconds", ""),
"computed_duration_mmss": stats.get("duration_mmss", ""),
"computed_wpm": stats.get("wpm", ""),
"computed_wpm_band": stats.get("wpm_band", ""),
"computed_filler_per_min": stats.get("filler_per_min", ""),
"computed_filler_band": stats.get("filler_band", ""),
"computed_notable_fillers": format_notable_fillers(notable_fillers),
"computed_filler_counts_json": json.dumps(stats.get("filler_counts", {}), sort_keys=True),
}
def format_notable_fillers(notable_fillers: Any) -> str:
if not isinstance(notable_fillers, list):
return ""
formatted = []
for item in notable_fillers:
if not isinstance(item, dict):
continue
filler = item.get("filler")
count = item.get("count")
if filler and count:
formatted.append(f"{filler}:{count}")
return ", ".join(formatted)
def write_csv(path: Path, rows: list[dict[str, Any]]) -> None:
fieldnames = list(rows[0].keys())
for field in STAT_FIELDS:
if field not in fieldnames:
fieldnames.append(field)
with path.open("w", encoding="utf-8", newline="") as output_file:
writer = csv.DictWriter(output_file, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(rows)
if __name__ == "__main__":
main()