Spaces:
Sleeping
Sleeping
File size: 6,197 Bytes
3bafd90 4d50a3e 3bafd90 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | """
PDF Report Generator for Audio Analysis results.
"""
from fpdf import FPDF
from datetime import datetime
def generate_pdf_report(result) -> bytes:
"""Generate a PDF report from AnalysisResult."""
pdf = FPDF()
pdf.set_auto_page_break(auto=True, margin=15)
pdf.add_page()
# Header
pdf.set_font("Helvetica", "B", 18)
pdf.cell(0, 12, "Audio Analysis Report", new_x="LMARGIN", new_y="NEXT", align="C")
pdf.set_font("Helvetica", "", 10)
pdf.cell(0, 6, f"Test ID: {result.test_id}", new_x="LMARGIN", new_y="NEXT", align="C")
pdf.cell(0, 6, f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M')}", new_x="LMARGIN", new_y="NEXT", align="C")
pdf.ln(8)
# Risk Score
score = result.risk_score
label = result.risk_label
r, g, b = _risk_rgb(score)
pdf.set_fill_color(r, g, b)
pdf.set_text_color(255, 255, 255)
pdf.set_font("Helvetica", "B", 28)
pdf.cell(40, 18, str(score), fill=True, align="C")
pdf.set_font("Helvetica", "B", 14)
pdf.cell(50, 18, f" {label} RISK", new_x="LMARGIN", new_y="NEXT")
pdf.set_text_color(0, 0, 0)
pdf.ln(6)
# Audio Summary
_section(pdf, "Audio Summary")
pdf.set_font("Helvetica", "", 10)
pdf.cell(0, 6, f"File: {result.filename}", new_x="LMARGIN", new_y="NEXT")
pdf.cell(0, 6, f"Duration: {result.duration_seconds:.1f}s", new_x="LMARGIN", new_y="NEXT")
pdf.cell(0, 6, f"Analyzed: {result.analyzed_at}", new_x="LMARGIN", new_y="NEXT")
pdf.ln(4)
# Speaker Summary
_section(pdf, "Speaker Summary")
if result.main_speaker:
m = result.main_speaker
pdf.set_font("Helvetica", "", 10)
pdf.cell(0, 6,
f"Main Speaker: {m.voiceprint_id} | Quality: {m.quality} | "
f"Synthetic: {m.synthetic_score:.0%} | Seen: {m.times_seen}x",
new_x="LMARGIN", new_y="NEXT")
if result.additional_speakers:
for i, s in enumerate(result.additional_speakers):
pdf.cell(0, 6,
f"Speaker {chr(66+i)}: {s.voiceprint_id} | "
f"{s.total_seconds:.1f}s | Synthetic: {s.synthetic_score:.0%}",
new_x="LMARGIN", new_y="NEXT")
else:
pdf.cell(0, 6, "No additional speakers detected.", new_x="LMARGIN", new_y="NEXT")
pdf.ln(4)
# Detection Flags Table
_section(pdf, "Detection Flags")
_row(pdf, ["Flag", "Detected", "Score / Detail"], bold=True)
synth = result.main_speaker.is_synthetic if result.main_speaker else False
synth_s = f"{result.main_speaker.synthetic_score:.0%}" if result.main_speaker else "N/A"
_row(pdf, ["Synthetic Voice", "Yes" if synth else "No", synth_s])
_row(pdf, ["Playback", "Yes" if result.playback_detected else "No", f"{result.playback_score:.0%}"])
_row(pdf, ["Reading Pattern", "Yes" if result.reading_pattern_detected else "No", f"{result.reading_confidence:.0%}"])
_row(pdf, ["Whispers", "Yes" if result.whisper_detected else "No", f"{len(result.whisper_instances or [])} instances"])
_row(pdf, ["Suspicious Pauses", "Yes" if result.suspicious_pauses_detected else "No",
f"{len(result.suspicious_pauses or [])} (max {result.longest_pause:.1f}s)"])
_row(pdf, ["Wake Words", str(len(result.wake_words or [])),
", ".join(w.get('word', '') for w in (result.wake_words or [])) or "None"])
pdf.ln(4)
# Alert Details
has_alerts = (result.wake_words or
(result.whisper_instances and len(result.whisper_instances) > 0) or
(result.suspicious_pauses and len(result.suspicious_pauses) > 0))
if has_alerts:
_section(pdf, "Alert Details")
pdf.set_font("Helvetica", "", 9)
for ww in (result.wake_words or []):
pdf.cell(0, 5,
f" Wake Word: \"{ww.get('word', '')}\" at {ww.get('time', 0):.1f}s "
f"(confidence: {ww.get('confidence', 0):.0%})",
new_x="LMARGIN", new_y="NEXT")
for w in (result.whisper_instances or []):
pdf.cell(0, 5,
f" Whisper: {w.get('start', 0):.1f}s - {w.get('end', 0):.1f}s "
f"(confidence: {w.get('confidence', 0):.0%})",
new_x="LMARGIN", new_y="NEXT")
for p in (result.suspicious_pauses or []):
pdf.cell(0, 5,
f" Pause: {p.get('start', 0):.1f}s - {p.get('end', 0):.1f}s "
f"({p.get('duration', 0):.1f}s)",
new_x="LMARGIN", new_y="NEXT")
pdf.ln(4)
# Risk Score Breakdown
_section(pdf, "Risk Score Breakdown")
pdf.set_font("Helvetica", "", 10)
if result.main_speaker:
pdf.cell(0, 6, f"Synthetic voice: {result.main_speaker.synthetic_score*25:.0f} / 25", new_x="LMARGIN", new_y="NEXT")
pdf.cell(0, 6, f"Playback: {result.playback_score*15:.0f} / 15", new_x="LMARGIN", new_y="NEXT")
pdf.cell(0, 6, f"Reading pattern: {result.reading_confidence*20:.0f} / 20", new_x="LMARGIN", new_y="NEXT")
wc = len(result.whisper_instances or [])
pdf.cell(0, 6, f"Whispers: {min(wc,3)/3*15:.0f} / 15", new_x="LMARGIN", new_y="NEXT")
pc = len(result.suspicious_pauses or [])
pdf.cell(0, 6, f"Suspicious pauses: {min(pc,3)/3*10:.0f} / 10", new_x="LMARGIN", new_y="NEXT")
wkc = len(result.wake_words or [])
pdf.cell(0, 6, f"Wake words: {min(wkc,2)/2*10:.0f} / 10", new_x="LMARGIN", new_y="NEXT")
pdf.ln(6)
# Footer
pdf.set_font("Helvetica", "I", 8)
pdf.cell(0, 5, "Generated by Audio Analyzer PoC", align="C")
return bytes(pdf.output())
def _section(pdf, title):
pdf.set_font("Helvetica", "B", 12)
pdf.set_fill_color(240, 240, 240)
pdf.cell(0, 8, f" {title}", fill=True, new_x="LMARGIN", new_y="NEXT")
pdf.ln(2)
def _row(pdf, cols, bold=False):
pdf.set_font("Helvetica", "B" if bold else "", 9)
widths = [50, 25, 115]
for i, col in enumerate(cols):
pdf.cell(widths[i], 6, str(col), border=1)
pdf.ln()
def _risk_rgb(score):
if score <= 30:
return (34, 197, 94)
elif score <= 60:
return (234, 179, 8)
else:
return (239, 68, 68)
|