From 392cfa51d498832a6b9fbdfcf93f5f5721fc4e65 Mon Sep 17 00:00:00 2001 From: Francisco Solla Date: Tue, 15 Sep 2026 09:44:04 -0300 Subject: [PATCH] =?UTF-8?q?feat(C163):=20v=C3=ADdeo=20e=20transcri=C3=A7?= =?UTF-8?q?=C3=A3o=20no=20acervo=20de=20falas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/skills/relatorio-cidade/SKILL.md | 6 ++++-- .../2026-09-15-c163-falas-video-transcricao.md | 1 + scripts/cityReportSnapshot.mjs | 4 ++++ scripts/lib/cityReportBlocks.mjs | 16 +++++++++------- tests/unit/cityReportBlocks.unit.spec.ts | 14 +++++++++++--- 5 files changed, 29 insertions(+), 12 deletions(-) create mode 100644 docs/changelog/2026-09-15-c163-falas-video-transcricao.md diff --git a/.agents/skills/relatorio-cidade/SKILL.md b/.agents/skills/relatorio-cidade/SKILL.md index 7401ce2e..2e837fed 100644 --- a/.agents/skills/relatorio-cidade/SKILL.md +++ b/.agents/skills/relatorio-cidade/SKILL.md @@ -178,8 +178,10 @@ cacheado em `data/relatorios-cidade/.emendas.json` para replay. econômica (pesquisa)` · `9. Transporte e conexões (pesquisa)` · `10. Acervo de falas` — cada fala com "O que é" (sumário oficial) e "Menção ao município" (passagem que cita a cidade ou, se o nome não aparece nos trechos, a marcação - do acervo com o nº de municípios) · `11. Notícias e imprensa` · `12. Panorama - regional` · `13. Abordagem sugerida (personas)` · `14. Fontes e limites`. + do acervo com o nº de municípios), mais **Vídeo** (YouTube/VOD da Câmara) e + **Transcrição** (PDF do Diário) quando existirem · `11. Notícias e imprensa` · + `12. Panorama regional` · `13. Abordagem sugerida (personas)` · `14. Fontes e + limites`. - **Links clicáveis:** todo URL no PDF é um link (``): células de tabela, fontes por linha e a seção de fontes. diff --git a/docs/changelog/2026-09-15-c163-falas-video-transcricao.md b/docs/changelog/2026-09-15-c163-falas-video-transcricao.md new file mode 100644 index 00000000..764686d9 --- /dev/null +++ b/docs/changelog/2026-09-15-c163-falas-video-transcricao.md @@ -0,0 +1 @@ +**C163 — vídeo e transcrição no acervo de falas (2026-09-15):** a fala de Itamaraju **tem vídeo** (YouTube `2cX_gKkJH7Q` e VOD da Câmara) — o relatório é que só projetava o texto oficial, por isso o link ia para o PDF. O extrator passou a selecionar `youtubeUrl`/`vodPlaybackUrl` e a tabela de falas ganhou as colunas **Vídeo** (YouTube, com fallback para o VOD da Câmara) e **Transcrição** (PDF do Diário), ambas clicáveis quando existem e "—" quando não. No acervo (997 falas): 802 com YouTube, 924 com VOD e 965 com texto oficial. Larguras da tabela ajustadas (data em uma linha só). Unit 3118/3118, `tsc`/eslint verdes; snapshot de Itamaraju reextraído e PDF regerado (9 páginas). diff --git a/scripts/cityReportSnapshot.mjs b/scripts/cityReportSnapshot.mjs index 853634b0..598be0ed 100644 --- a/scripts/cityReportSnapshot.mjs +++ b/scripts/cityReportSnapshot.mjs @@ -63,6 +63,8 @@ const loadSpeeches = async (payload, actor, municipalityID, municipalityName) => phase: true, summary: true, officialTextUrl: true, + youtubeUrl: true, + vodPlaybackUrl: true, mentionedMunicipalities: true, }, user: actor, @@ -88,6 +90,8 @@ const loadSpeeches = async (payload, actor, municipalityID, municipalityName) => phase: doc.phase ?? null, summary: doc.summary ?? null, officialTextUrl: doc.officialTextUrl ?? null, + youtubeUrl: doc.youtubeUrl ?? null, + vodPlaybackUrl: doc.vodPlaybackUrl ?? null, mentionExcerpt: mentionSegment?.text ?? null, mentionedMunicipalityCount: Array.isArray(doc.mentionedMunicipalities) ? doc.mentionedMunicipalities.length diff --git a/scripts/lib/cityReportBlocks.mjs b/scripts/lib/cityReportBlocks.mjs index b693d145..2eb6bff0 100644 --- a/scripts/lib/cityReportBlocks.mjs +++ b/scripts/lib/cityReportBlocks.mjs @@ -873,11 +873,12 @@ const buildSpeechesSection = ({ snapshot }) => { kind: 'table', title: 'Acervo de falas', columns: [ - { key: 'date', label: 'Data', width: 8 }, - { key: 'phase', label: 'Fase', width: 11 }, - { key: 'description', label: 'O que é', width: 37 }, - { key: 'mention', label: `Menção a ${cityLabel}`, width: 32 }, - { key: 'link', label: 'Link', width: 12 }, + { key: 'date', label: 'Data', width: 9 }, + { key: 'phase', label: 'Fase', width: 10 }, + { key: 'description', label: 'O que é', width: 32 }, + { key: 'mention', label: `Menção a ${cityLabel}`, width: 27 }, + { key: 'video', label: 'Vídeo', width: 11 }, + { key: 'transcript', label: 'Transcrição', width: 11 }, ], rows: speeches.rows.map((row) => ({ date: formatDateBr(row.speechAt), @@ -888,10 +889,11 @@ const buildSpeechesSection = ({ snapshot }) => { : row.mentionedMunicipalityCount ? `Marcada no acervo — nome não localizado nos trechos (a fala cita ${formatInteger(row.mentionedMunicipalityCount)} municípios).` : 'Marcada no acervo — trecho não localizado.', - link: row.officialTextUrl ?? '—', + video: row.youtubeUrl ?? row.vodPlaybackUrl ?? '—', + transcript: row.officialTextUrl ?? '—', })), note: [ - '“O que é” = sumário oficial da Câmara; “Menção” = passagem que cita o município ou, quando o nome não aparece nos trechos, a marcação do acervo (C153/C155) com o nº de municípios da fala.', + '“O que é” = sumário oficial da Câmara; “Menção” = passagem que cita o município ou, quando o nome não aparece nos trechos, a marcação do acervo (C153/C155) com o nº de municípios da fala; “Vídeo” e “Transcrição” = links oficiais da Câmara quando existem.', speeches.totalCount > speeches.rows.length ? `Mostrando ${speeches.rows.length} de ${speeches.totalCount} falas.` : null, diff --git a/tests/unit/cityReportBlocks.unit.spec.ts b/tests/unit/cityReportBlocks.unit.spec.ts index 016c127d..903cbc73 100644 --- a/tests/unit/cityReportBlocks.unit.spec.ts +++ b/tests/unit/cityReportBlocks.unit.spec.ts @@ -276,6 +276,8 @@ const snapshot = { phase: 'Discurso em plenário', summary: 'Resumo da fala', officialTextUrl: 'https://camara.test/discurso', + youtubeUrl: 'https://youtube.test/video', + vodPlaybackUrl: 'https://vod.test/video.mp4', mentionExcerpt: 'Trecho da fala que cita Feira de Santana', mentionedMunicipalityCount: 3, }, @@ -286,6 +288,8 @@ const snapshot = { phase: 'Pequeno Expediente', summary: 'Saúde indígena na Bahia', officialTextUrl: 'https://camara.test/discurso-2', + youtubeUrl: null, + vodPlaybackUrl: null, mentionExcerpt: null, mentionedMunicipalityCount: 25, }, @@ -433,15 +437,19 @@ describe('buildCityReport', () => { 'Fase', 'O que é', `Menção a ${snapshot.municipality.name}`, - 'Link', + 'Vídeo', + 'Transcrição', ]) - expect(falas.columns!.find((column) => column.key === 'description')!.width).toBe(37) - expect(falas.columns!.find((column) => column.key === 'mention')!.width).toBe(32) + expect(falas.columns!.find((column) => column.key === 'description')!.width).toBe(32) + expect(falas.columns!.find((column) => column.key === 'mention')!.width).toBe(27) expect(rows[0].description).toBe('Resumo da fala') expect(rows[0].mention).toContain('cita Feira de Santana') + expect(rows[0].video).toBe('https://youtube.test/video') + expect(rows[0].transcript).toBe('https://camara.test/discurso') expect(rows[1].description).toBe('Saúde indígena na Bahia') expect(rows[1].mention).toContain('nome não localizado nos trechos') expect(rows[1].mention).toContain('25 municípios') + expect(rows[1].video).toBe('—') }) it('renders the persona approach section and its sources', () => {