Communitygithub.com

constancetanahrefs/webinar-analyzer

Uma especificação portátil de skill para construir um dashboard de Análise de Webinars neutro em plataforma: porta de anonimização, mapeamento de colunas e análises por webinar e entre webinars.

O que é webinar-analyzer?

O Webinar Analyzer é uma skill projetada como um especificação portátil e neutra em tecnologia, permitindo que desenvolvedores construam um dashboard de análise de webinars funcional em qualquer stack (Flask, Django, FastAPI, Streamlit, notebooks, etc.). Ela transforma exportações brutas de webinars em duas categorias de respostas: análises individuais de cada sessão (quem participou, tempo de permanência, momento de saída) e análises transversais entre todos os webinars (crescimento de audiência, formatos que funcionam, retenção de participantes e temas recorrentes nos feedbacks). Um dos seus diferenciais é o rigoroso processo de privacidade: todo arquivo enviado passa por uma verificação obrigatória de dados pessoais antes de ser analisado, rejeitando automaticamente qualquer conteúdo que contenha e-mails, telefones, endereços ou IDs governamentais. Além disso, a skill insiste em um mapeamento explícito de colunas entre o formato de exportação do usuário (Zoom, Teams, GoToWebinar, etc.) e um schema canônico, garantindo que nenhuma suposição sobre a plataforma de origem seja codificada. Esse mapeamento determina quais gráficos e análises estarão disponíveis, e o dashboard oculta automaticamente funcionalidades para as quais os dados não são suficientes. Para análises transversais, é exigido um ID pseudônimo estável (como um hash SHA-256 do e-mail), permitindo reconhecer o mesmo participante em diferentes webinars sem violar a privacidade. A skill também suporta a ingestão de feedbacks de pesquisas pós-webinar (de formulários internos ou externos como Typeform) e análises de replays de sessões gravadas, com três caminhos de entrada possíveis: API, exportação CSV nativa ou template CSV manual.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add constancetanahrefs/webinar-analyzer

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Webinar Analyzer

Uma ferramenta que transforma exportações brutas de webinars em dois tipos de respostas:

  1. Por webinar — quem veio, quanto tempo ficaram, quando saíram, quem eram, o que disseram.
  2. Entre todos os webinars — a audiência está crescendo, qual horário e formato funciona, quem continua voltando, e quais elogios e reclamações se repetem.

Esta skill é neutra em tecnologia. Ela define contratos de dados, regras de validação, computações e semânticas de gráficos. Implemente-a em qualquer stack web que você tiver (Flask/Django/FastAPI + Postgres + qualquer biblioteca de gráficos; Streamlit; um notebook; uma ferramenta de BI). A implementação de referência da qual estas instruções foram extraídas é um aplicativo Flask + PostgreSQL + ApexCharts.


Antes de construir qualquer coisa: a conversa de admissão

Não presuma Zoom. Não presuma YouTube. O maior modo de falha desta ferramenta é codificar o formato de exportação de uma única plataforma. O usuário pode realizar webinars no Teams, GoToWebinar, Livestorm, Demio, Webex, Hopin, Riverside, StreamYard, Crowdcast, BigMarker ou uma stack personalizada, e pode hospedar replays no Vimeo, Wistia, seu próprio CDN, ou em lugar nenhum.

Execute esta sequência antes de escrever código:

Passo 1 — Pergunte quais plataformas estão em jogo

Pergunte, como uma pergunta de múltipla escolha curta:

  • Em qual plataforma você realiza webinars ao vivo? (Zoom Webinars / Zoom Meetings / Microsoft Teams / GoToWebinar / Livestorm / Demio / Webex / Hopin / BigMarker / Crowdcast / outro)
  • Onde as pesquisas pós-webinar ficam? (integrada à plataforma de webinar / Typeform / Google Forms / SurveyMonkey / Hubspot / nenhuma)
  • Onde os replays ficam, se houver? (YouTube / Vimeo / Wistia / auto-hospedado / página de destino com acesso restrito / sem replays)
  • Você tem dados de registro separados dos dados de participação? (uma exportação combinada / duas exportações separadas / apenas registro / apenas participação)

Passo 2 — Peça uma exportação real, antes de projetar qualquer coisa

Diga claramente: "Faça upload de uma exportação real de cada fonte — participantes, pesquisa, replay — com os dados pessoais já removidos. Vou ler os cabeçalhos reais das colunas em vez de adivinhar, e direi quais partes do dashboard seus dados podem ou não suportar."

Insista nisso. Um único arquivo real elimina todas as suposições. Se o usuário não puder compartilhar dados reais, peça que cole a linha do cabeçalho mais 2–3 linhas totalmente fabricadas.

Passo 3 — Execute a verificação de privacidade no arquivo enviado

Veja Verificação de privacidade. Rejeite o arquivo se ele contiver dados pessoais não anonimizados. Não prossiga para o mapeamento até que um arquivo limpo seja fornecido.

Passo 4 — Mapeie as colunas deles para o schema canônico

Mostre ao usuário uma tabela de mapeamento explícita (coluna deles → campo canônico → o que ele desbloqueia) e nomeie o que está faltando. Veja Requisitos de campo e references/platform-exports.md.

Passo 5 — Diga a eles quais gráficos eles terão

Imprima um relatório de disponibilidade de funcionalidades antes de construir:

✅ Disponível com seus dados
   Distribuição do tempo de exibição, linha do tempo de audiência simultânea, detecção de abandono,
   funil de participação, crescimento mês a mês, novo vs. recorrente.

⚠️  Parcialmente disponível
   Demografia — você tem País, mas não tem Cargo, então o gráfico de rosquinha de senioridade
   ficará oculto.

❌ Não disponível
   Análises de replay — sem exportação de replay. A aba Replays ficará oculta
   até que você adicione uma (ou você pode inserir os valores manualmente através do template CSV).

Nunca construa um gráfico cujos dados de entrada não existam. Oculte todo o painel e mostre uma explicação de uma linha sobre quais dados o ativariam.


Verificação de privacidade (OBRIGATÓRIA)

Cada arquivo enviado passa por uma verificação de anonimização antes da análise, antes do armazenamento, antes da análise. Se dados pessoais forem encontrados, rejeite o arquivo inteiro, nomeie as colunas ofensivas e os números das linhas, e explique como anonimizá-lo. Nunca importe parcialmente. Nunca anonimize automaticamente silenciosamente.

O que conta como dados pessoais

CategoriaDetecção
Endereços de e-mail[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,} em qualquer célula
Números de telefone(?:\+?\d[\s\-\(\)\.]{0,2}){7,15} com ≥7 dígitos, excluindo timestamps/IDs puros
Endereços postaisCélula que corresponde a palavras-chave de tipo de rua (`street
CEPs / ZIPs\b\d{5}(-\d{4})?\b ou [A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2} quando o nome da coluna sugere localização
Endereços IPLiteral IPv4/IPv6
IDs governamentais / de pagamento\b\d{3}-\d{2}-\d{4}\b (formato de SSN), \b(?:\d[ -]*?){13,16}\b passando na verificação de Luhn
Nomes de coluna suspeitosCabeçalho correspondente a `email

Regras

  1. Varredura de cabeçalho primeiro. Qualquer coluna cujo nome corresponda à lista suspeita é sinalizada mesmo que as células amostradas pareçam vazias — uma coluna vazia hoje pode ser preenchida amanhã.
  2. Varra cada célula de cada linha. Não apenas uma amostra. Os arquivos são pequenos (milhares de linhas); uma varredura completa é barata e uma varredura amostrada dá uma falsa sensação de segurança.
  3. Respostas de pesquisa em texto livre também são varridas. As pessoas digitam "envie-me um e-mail para [email protected]" em caixas de feedback. Este é o vazamento mais comumente negligenciado.
  4. Rejeite na primeira categoria encontrada, mas relate todas elas. Complete a varredura e apresente cada problema de uma vez para que o usuário corrija o arquivo em uma única passagem.
  5. Nunca escreva um arquivo rejeitado em disco ou banco de dados. Mantenha-o na memória, rejeite, descarte.
  6. Registre a rejeição sem os valores ofensivos. Registre apenas os nomes das colunas e números das linhas — nunca o e-mail/telefone detectado em si.

Identificadores permitidos (estes NÃO são rejeições)

  • Um ID de pessoa pseudônimo estável — um hash, UUID ou inteiro sequencial que seja consistente entre arquivos. Isso é obrigatório para análises entre webinars (veja abaixo).
  • Nomes de país/região, nomes de organização, cargos, faixas de senioridade.
  • Timestamps, durações, IDs de webinar, strings de tópico.

Modelo de mensagem de rejeição

❌ Upload rejeitado — dados pessoais detectados em attendees_march.csv

  Coluna "Email"           → 412 endereços de e-mail (linhas 2–413)
  Coluna "Contact Number"  → 88 números de telefone  (linhas 2, 5, 9, … )
  Coluna "What can we improve?" → 3 endereços de e-mail dentro de texto livre
                                  (linhas 47, 112, 288)

Nada foi importado e o arquivo foi descartado.

Para corrigir:
  1. Substitua a coluna Email por um ID pseudônimo estável — por exemplo, um
     hash SHA-256 do e-mail em minúsculas, truncado para 16 caracteres.
     Use o MESMO método de hash para cada arquivo para que a mesma pessoa seja
     reconhecível entre webinars.
  2. Exclua a coluna Contact Number inteiramente — ela nunca é usada.
  3. Remova os endereços de e-mail dentro das respostas de texto livre, mantendo
     o restante do comentário.

Re-envie após a anonimização. Consulte references/anonymisation.md para um script
 de anonimização pronto.

Por que um ID pseudônimo estável é inegociável

Metade do valor desta ferramenta é entre webinars: novos vs. recorrentes, coortes de retenção, repetidores de registro, taxa de comparecimento por pessoa. Tudo isso precisa reconhecer a mesma pessoa entre arquivos sem saber quem ela é. Diga isso explicitamente ao usuário na admissão — se eles anonimizarem e-mails para valores em branco ou aleatórios por arquivo, todo gráfico entre webinars se torna silenciosamente sem sentido.

Recomendado: person_id = sha256(lower(strip(email)) + fixed_salt)[:16], com o salt armazenado fora do conjunto de dados. references/anonymisation.md contém um script pronto.


Requisitos de campo

O dashboard é executado em um schema canônico. As colunas de exportação do usuário são mapeadas para ele. Qualquer coisa que o mapeamento não puder preencher é uma funcionalidade que fica oculta.

Entidade canônica: webinar (uma linha por sessão)

CampoObrigatório?TipoDesbloqueiaColuna de origem típica
external_idObrigatóriotexto, únicoTudo — a chave de junção entre arquivos de participantes/pesquisa/replayID do Webinar, ID da Reunião, ID do Evento, ID da Sessão
topicObrigatóriotextoToda rotulagem; agrupamento de campanha de reprisesTópico, Título, Nome do evento
start_timeObrigatóriotimestampTodo gráfico baseado em tempo; mês a mês; hora do dia; a linha do tempo de simultaneidadeHora de Início Real, Início, Data
duration_minutesFortemente recomendadointEixo x da linha do tempo de simultaneidade; % da sessão assistidaDuração Real, Duração
num_registrantsRecomendadointFunil, taxa de comparecimento (pode ser derivada contando linhas)# Registrantes, Registrados
unique_viewersRecomendadointGráfico de hora do dia; alcance principal (derivável)Visualizadores Únicos, Participantes
max_concurrentOpcionalintEstatística de pico de audiênciaVisualizações Simultâneas Máximas

Regra de fuso horário: decida uma vez se os timestamps armazenados são UTC ou um fuso local fixo, escreva-o no comentário do schema e nunca converta ad hoc. O agrupamento mensal lê diretamente deste campo. Forneça um mecanismo manual de substituição de mês (veja Substituições de mês) para sessões que acontecem logo após a meia-noite local para uma audiência em outro fuso horário.

Entidade canônica: attendee_session (uma linha por pessoa por entrada)

Uma pessoa que sai e retorna produz múltiplas linhas. Nunca deduplicar na ingestão — deduplique no momento da análise, deliberadamente.

CampoObrigatório?TipoDesbloqueiaColuna de origem típica
webinar_idObrigatórioFKTudo—
person_idObrigatório (pseudônimo)textoNovo vs. recorrente, retenção, repetidores de registro, deduplicação por pessoaEmail com Hash, ID do Participante
attendedObrigatórioboolDivisão participou/não compareceu, funil, taxa de comparecimentoParticipou (Sim/Não)
is_registrantRecomendadoboolCoorte de não comparecimento; funilderivado da presença da Hora de Registro
join_timeRecomendadotimestampLinha do tempo de simultaneidade, detecção de abandonoHora de Entrada
leave_timeRecomendadotimestampLinha do tempo de simultaneidade, detecção de abandonoHora de Saída
minutesRecomendadointHistograma de tempo de exibição, tempo médio de exibiçãoTempo na Sessão (minutos)
country / regionOpcionaltextoGráfico de rosquinha geográfico, análise de horário regionalPaís/Região
organizationOpcionaltextoVisão por contaOrganização, Empresa
job_titleOpcionaltextoGráfico de rosquinha de senioridade (derivado)Cargo, Título, Função
source_nameOpcionaltextoGráfico de rosquinha de canal de aquisiçãoNome da Fonte, UTM source, Referrer
registration_timeOpcionaltimestampAnálise de curva de inscriçãoHora de Registro
custom_answersOpcionalJSONUm gráfico de rosquinha por pergunta de registroQualquer coluna não reconhecida

Regra de coluna não reconhecida: qualquer coluna no arquivo de participantes que não seja um dos campos conhecidos acima é tratada como uma pergunta de pesquisa de registro e armazenada em custom_answers como {pergunta: resposta}. Cada uma dessas perguntas renderiza automaticamente seu próprio gráfico de rosquinha. Isso é o que faz a ferramenta funcionar em plataformas que você nunca viu.

Campo derivado — seniority: agrupe job_title por palavra-chave em C-Level / Fundador (ceo, cto, cfo, cmo, coo, chief, founder, owner, president), VP / Diretor (vp, vice president, head of, director), Gerente / Líder (manager, lead, principal), IC Sênior (senior, sr.), Júnior (junior, jr., intern, associate, assistant), senão Contribuidor Individual. Oculte o gráfico de senioridade inteiramente quando nenhuma linha tiver um cargo.

Entidade canônica: feedback_response (uma linha por envio de pesquisa)

CampoObrigatório?TipoDesbloqueiaColuna de origem típica
webinar_idObrigatórioFKJuntar feedback à sessãoID da Reunião/Webinar
person_idOpcional (pseudônimo)textoVincular feedback ao comportamento de participaçãoEndereço de Email com Hash
submitted_atOpcionaltimestampOrdenação, recênciaData e Hora Enviadas
scoreRecomendadoint 1–5Distribuição de satisfação + médias"Quão útil você achou…"
liked_textRecomendadotextoAgrupamento de temas de elogio"O que você gostou…"
improve_textRecomendadotextoAgrupamento de temas de preocupação"O que podemos fazer para melhorar…"
other_answersOpcionalJSONTodo o resto, textualcolunas restantes de perguntas

Detecção de coluna: encontre as colunas de score / liked / improve por correspondência de palavra-chave no cabeçalho (useful/rate/score; like; improve), insensível a maiúsculas, primeira correspondência vence. Mostre ao usuário o mapeamento detectado e deixe-o corrigi-lo — o texto das perguntas da pesquisa difere em cada empresa.

Se a pesquisa estiver fora da plataforma de webinar (Typeform, Google Forms), ela não trará um ID de webinar. Então exija que o usuário adicione um campo oculto carregando o ID do webinar, ou escolha o webinar de destino na interface do usuário no momento do upload.

Entidade canônica: replay_stats (uma linha por webinar)

Replays são os dados mais variáveis de plataforma de todos. Suporte três caminhos de ingestão, nesta ordem de preferência:

  1. Conector de API para o host de replay (YouTube Data/Analytics API, Vimeo, Wistia) — apenas se o usuário tiver um e conceder credenciais.
  2. Exportação CSV de analytics nativa do host de replay, mapeada para os campos canônicos.
  3. Template CSV manual — sempre disponível, sempre o fallback. Envie um template para download com uma linha de cabeçalho, um bloco de comentário explicando cada métrica e uma linha de exemplo.
CampoObrigatório?TipoDesbloqueia
webinar_idObrigatóriochave de junçãoTudo relacionado a replay
video_id / video_urlOpcionaltextoLink direto para o vídeo hospedado; sincronização de API
views_day1OpcionalintGráfico de demanda inicial
views_day7OpcionalintGráfico de demanda sustentada
total_view_hoursOpcionalnuméricoGráfico de consumo total de replay
avg_view_duration_secOpcionalintGráfico de engajamento no replay
avg_view_percentageOpcionalnuméricoGráfico de retenção no replay
likes, comments, shares, subscribers_gainedOpcionalintEstatísticas de reação

Todo campo de replay é opcional. Armazene NULL para espaços em branco, nunca 0 — uma métrica não medida e uma métrica medida como zero são semanticamente diferentes.

Habilidades Relacionadas