Spaces:
Sleeping
Sleeping
Download scripts/plot_debug_bundle.py from jasonmdong/notepilot: direct link, hf CLI and curl.
- Browser
- Download file 3.42 kB
-
https://huggingface.co/spaces/jasonmdong/notepilot/resolve/main/scripts/plot_debug_bundle.py
- Command line
-
hf download hf://spaces/jasonmdong/notepilot/scripts/plot_debug_bundle.py
-
curl -L -o plot_debug_bundle.py https://huggingface.co/spaces/jasonmdong/notepilot/resolve/main/scripts/plot_debug_bundle.py
3.42 kB
| #!/usr/bin/env python3 | |
| """Create four forensic plots using only one NotePilot debug bundle.""" | |
| import json | |
| import sys | |
| from pathlib import Path | |
| def main(): | |
| if len(sys.argv) not in (2, 3): | |
| print('usage: plot_debug_bundle.py bundle.json [output-directory]', file=sys.stderr); return 2 | |
| bundle = json.loads(Path(sys.argv[1]).read_text()) | |
| out = Path(sys.argv[2]) if len(sys.argv) == 3 else Path(sys.argv[1]).with_suffix('') | |
| out.mkdir(parents=True, exist_ok=True) | |
| rows = bundle['diagnostics']['timeline'] | |
| t = [r['tMs'] / 1000 for r in rows] | |
| def vals(name): return [r.get(name) for r in rows] | |
| events = bundle['events'] | |
| def marks(kind): return [e['sessionTimeMs'] / 1000 for e in events if e.get('type') == kind] | |
| # Self-contained SVG avoids a plotting-package dependency in a fresh shell. | |
| def graph(name, title, series, vertical=()): | |
| width, height, pad = 1200, 460, 55 | |
| numeric = [v for _, values, _ in series for v in values if isinstance(v, (int, float))] | |
| lo, hi = (min(numeric), max(numeric)) if numeric else (0, 1) | |
| if lo == hi: lo -= 1; hi += 1 | |
| end = max(t) if t else 1 | |
| def xy(i, value): return (pad + (t[i] / end) * (width - 2 * pad), height - pad - ((value - lo) / (hi - lo)) * (height - 2 * pad)) | |
| paths = [] | |
| for label, values, color in series: | |
| points = [f'{x:.2f},{y:.2f}' for i, v in enumerate(values) if isinstance(v, (int, float)) for x, y in [xy(i, v)]] | |
| paths.append(f'<polyline fill="none" stroke="{color}" stroke-width="1.5" points="{" ".join(points)}"/><text x="{pad}" y="{22 + 16*len(paths)}" fill="{color}">{label}</text>') | |
| lines = ''.join(f'<line x1="{pad+(x/end)*(width-2*pad):.2f}" x2="{pad+(x/end)*(width-2*pad):.2f}" y1="{pad}" y2="{height-pad}" stroke="#c44" opacity=".25"/>' for x in vertical) | |
| svg = f'<svg xmlns="http://www.w3.org/2000/svg" width="{width}" height="{height}" viewBox="0 0 {width} {height}"><rect width="100%" height="100%" fill="white"/><text x="{pad}" y="20" font-size="16">{title}</text><line x1="{pad}" x2="{pad}" y1="{pad}" y2="{height-pad}" stroke="black"/><line x1="{pad}" x2="{width-pad}" y1="{height-pad}" y2="{height-pad}" stroke="black"/>{lines}{"".join(paths)}<text x="{pad}" y="{height-15}">session seconds (range 0–{end:.2f}); value range {lo:.3g}–{hi:.3g}</text></svg>' | |
| (out / name).write_text(svg) | |
| attempts = [e['sessionTimeMs']/1000 for e in events if e.get('physicalAttemptId') is not None] | |
| graph('01-mic-level.svg', 'Microphone level / noise floor / attempts', [('mic dB', vals('db'), '#1769aa'), ('noise floor dB', vals('noiseFloorDb'), '#777')], attempts + marks('mic.gate')) | |
| graph('02-pitch.svg', 'Blind YIN pitch and expected score', [('blind MIDI', vals('blindMidi'), '#1769aa'), ('expected MIDI', vals('expectedMidi'), '#e68613')], attempts) | |
| graph('03-onset.svg', 'Onset feature and attack flags', [('onset ratio', vals('onsetValue'), '#6a3d9a')], [r['tMs']/1000 for r in rows if r.get('attack')]) | |
| graph('04-follower.svg', 'Follower posterior / WRONG / evidence', [('posterior', vals('posterior'), '#1769aa'), ('committed posterior', vals('committedPosterior'), '#e68613'), ('WRONG', vals('wrongProbability'), '#c44'), ('evidence nats', vals('evidenceNats'), '#6a3d9a')]) | |
| print(f'Wrote four plots to {out}'); return 0 | |
| if __name__ == '__main__': raise SystemExit(main()) | |