From 70b80cb312c45e20f65190a60ee76c410a426f29 Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 14 Apr 2026 23:05:29 +0000 Subject: [PATCH] Phase 6b: fix dashboard Untitled/WEB bug for transcripts Two bugs in the Recently Completed table: 1. Title showed "Untitled" for all transcripts because the dashboard read documents.book_title (populated by PDF metadata voting) which is NULL for transcripts. Fixed by COALESCE(book_title, filename) in the SQL query -- falls back to catalogue.filename which holds the real video title. 2. Type showed "WEB" for all transcripts because the type CASE expression only had web and pdf branches, with web matching any http% path -- and transcript paths are PeerTube watch URLs. Fixed by adding a transcript branch keyed on catalogue.source = stream.echo6.co, evaluated before the web branch. Also adds badge-transcript CSS (purple) and JS rendering case. Applied consistently to both the Recently Completed and Sources table queries. Co-Authored-By: Claude Opus 4.6 --- lib/api.py | 24 +++++++++++++++++------- static/css/recon.css | 1 + static/js/dashboard.js | 4 ++-- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/lib/api.py b/lib/api.py index 4ceab68..fa72c3f 100644 --- a/lib/api.py +++ b/lib/api.py @@ -882,7 +882,11 @@ def _build_knowledge_stats(): sources = conn.execute(""" SELECT c.source, - CASE WHEN c.path LIKE 'http%' THEN 'web' ELSE 'pdf' END as type, + CASE + WHEN c.source = 'stream.echo6.co' THEN 'transcript' + WHEN c.path LIKE 'http%' THEN 'web' + ELSE 'pdf' + END as type, COUNT(DISTINCT c.hash) as catalogued, COUNT(DISTINCT CASE WHEN d.status = 'complete' THEN d.hash END) as complete, COUNT(DISTINCT CASE WHEN d.status NOT IN ('complete', 'failed') AND d.status IS NOT NULL THEN d.hash END) as in_pipeline, @@ -935,11 +939,17 @@ def _build_knowledge_stats(): """).fetchone()[0] recent = conn.execute(""" - SELECT book_title, status, concepts_extracted, vectors_inserted, - CASE WHEN path LIKE 'http%' THEN 'web' ELSE 'pdf' END as type - FROM documents - WHERE status = 'complete' - ORDER BY embedded_at DESC + SELECT COALESCE(d.book_title, c.filename) as title, + d.status, d.concepts_extracted, d.vectors_inserted, + CASE + WHEN c.source = 'stream.echo6.co' THEN 'transcript' + WHEN d.path LIKE 'http%' THEN 'web' + ELSE 'pdf' + END as type + FROM documents d + JOIN catalogue c ON d.hash = c.hash + WHERE d.status = 'complete' + ORDER BY d.embedded_at DESC LIMIT 10 """).fetchall() @@ -979,7 +989,7 @@ def _build_knowledge_stats(): 'source_types': dict(sorted(source_type_counts.items(), key=lambda x: -x[1])), 'sample_size': sample_size, 'recent_complete': [{ - 'title': r['book_title'] or 'Untitled', + 'title': r['title'] or 'Untitled', 'concepts': r['concepts_extracted'] or 0, 'vectors': r['vectors_inserted'] or 0, 'type': r['type'], diff --git a/static/css/recon.css b/static/css/recon.css index d6752cf..95aed52 100644 --- a/static/css/recon.css +++ b/static/css/recon.css @@ -210,6 +210,7 @@ tr:hover { background: var(--bg-secondary); } } .badge-web { background: #1e3a5f; color: #60a5fa; padding: 2px 8px; border-radius: var(--radius); font-size: 11px; } .badge-pdf { background: #2d5a2d; color: #4ade80; padding: 2px 8px; border-radius: var(--radius); font-size: 11px; } +.badge-transcript { background: #3b1f5e; color: #c084fc; padding: 2px 8px; border-radius: var(--radius); font-size: 11px; } /* ── Trend indicators ── */ .trend { font-size: 11px; margin-left: 6px; } diff --git a/static/js/dashboard.js b/static/js/dashboard.js index 4e0b3d1..254d92a 100644 --- a/static/js/dashboard.js +++ b/static/js/dashboard.js @@ -88,7 +88,7 @@ var pipeCount = s.in_pipeline || 0; totalCat += catCount; totalComp += compCount; totalPipe += pipeCount; totalConcepts += s.concepts; totalVectors += s.vectors; - var badge = s.type === 'web' ? 'WEB' : 'PDF'; + var badge = s.type === 'transcript' ? 'TRANSCRIPT' : s.type === 'web' ? 'WEB' : 'PDF'; var compPct = catCount > 0 ? (compCount / catCount * 100) : 0; var pipePct = catCount > 0 ? (pipeCount / catCount * 100) : 0; var compColor = compPct >= 100 ? '#00ff41' : compPct > 0 ? '#ffa500' : '#666'; @@ -185,7 +185,7 @@ rtb.innerHTML = 'None yet'; } else { rtb.innerHTML = data.recent_complete.map(function(r) { - var badge = r.type === 'web' ? 'WEB' : 'PDF'; + var badge = r.type === 'transcript' ? 'TRANSCRIPT' : r.type === 'web' ? 'WEB' : 'PDF'; return '' + r.title + '' + badge + '' + r.concepts + '' + r.vectors + ''; }).join('');