Flag de qualidade de rede e navegador nos pings do Avo
TLDR: Enriquece as listas “Room pings” e “Wait pings” no show de
MeetingParticipantno Avo para exibir, em cada linha, uma flag de qualidade de rede (boa/média/ruim/desconhecida, baseada emnetwork.rtt) e o navegador do participante, com um detalhe expansível mostrando o payloaddiagnosticscompleto.
Contexto
O PR #697 (meeting_ping_diagnostics) introduziu o modelo MeetingParticipantPing (kind: room/wait, pinged_at, diagnostics jsonb), criado a cada heartbeat via MeetingParticipants::CreatePingFlow, associado a MeetingParticipant via has_many :meeting_participant_pings.
No show do MeetingParticipant no Avo (app/avo/resources/meeting_participant.rb), as seções “Room pings” e “Wait pings” exibiam apenas uma lista de horários formatados (MeetingParticipants::FormatPingDates), lida a partir dos arrays legados room_pings/wait_pings (timestamps Unix). Essa lista não usava o MeetingParticipantPing nem o diagnostics associado — não havia nenhuma informação de qualidade de conexão ou dispositivo visível sem entrar no console.
Esta spec foi implementada em duas etapas na mesma branch/PR (#699): a primeira etapa cobriu apenas room_pings, com a decisão explícita de deixar wait_pings de fora do escopo inicial. A atualização de 04/08 estendeu o mesmo tratamento para wait_pings, já que o MeetingParticipants::CreatePing grava diagnostics para os dois kinds — o dado já existia no banco, só não era exibido para wait.
O payload diagnostics real (confirmado em produção e na factory spec/factories/meeting_participant_pings.rb) contém:
json
{
"device": { "user_agent": "Mozilla/5.0 ... Chrome/151.0.0.0 Safari/537.36" },
"network": { "rtt": 200, "downlink": 1.75, "effective_type": "4g", "save_data": false, "online": true },
"app_state": { "visibility_state": "visible" },
"media_devices": { "audioinput_count": 2, "videoinput_count": 1, "audiooutput_count": 2 },
"timestamp": "2026-08-03T14:50:32.112Z"
}
Além disso, network.rtt, network.downlink e network.effective_type podem vir null — a Network Information API do navegador não é suportada em Safari/Firefox.
Objetivos
- Classificar a qualidade de rede de cada ping em
good/medium/bad/unknown, com base emdiagnostics.network.rtt:rtt < 100→good100 <= rtt <= 300→mediumrtt > 300→badrttausente/null→unknown
- Extrair navegador e versão (ex.:
"Chrome 151") a partir dediagnostics.device.user_agent, via parser próprio baseado em regex (sem nova dependência de gem) - Atualizar as seções “Room pings” e “Wait pings” no show de
MeetingParticipantpara mostrar, em cada linha: horário, flag de qualidade (badge colorido) e navegador - Adicionar um
<details>/”ver detalhes” expansível por linha, mostrando odiagnosticscompleto formatado (JSON legível) - Trocar a fonte de dados dos arrays legados para
meeting_participant.meeting_participant_pings.room.order(:pinged_at)e.wait.order(:pinged_at), já que é isso que carrega odiagnostics
Fora de escopo
- Remover ou alterar as colunas/arrays legados
room_pings/wait_pingsemMeetingParticipant— continuam sendo escritos normalmente peloCreatePingFlowe usados empresence_status/serializer - Criar um
Avo::Resources::MeetingParticipantPingdedicado ou tela de listagem própria — a exibição continua embutida no show doMeetingParticipant - Adicionar gem de parsing de user-agent — usa regex própria, cobrindo os navegadores que aparecem nos dados reais (Chrome, Safari, Firefox, Edge); navegadores não reconhecidos exibem “Desconhecido”
- Diferenciar a classificação de qualidade ou os rótulos entre
roomewait— mesmo formatter, mesmos thresholds, aplicado às duas relações
Mudanças
| Arquivo | Ação |
|---|---|
app/use_cases/meeting_participant_pings/classify_network_quality.rb |
criado — recebe rtt (nullable), retorna :good/:medium/:bad/:unknown |
app/use_cases/meeting_participant_pings/parse_browser.rb |
criado — recebe user_agent (nullable), retorna string tipo "Chrome 151" ou "Desconhecido" via regex |
app/use_cases/meeting_participants/format_pings.rb |
criado (renomeado de format_room_pings.rb, sem dependência de kind) — recebe uma relação de MeetingParticipantPing de qualquer kind e monta a lista HTML: horário + qualidade + navegador + downlink |
app/avo/resources/meeting_participant.rb |
atualizado — os campos room_pings e wait_pings passam a usar MeetingParticipants::FormatPings.call(@record.meeting_participant_pings.<kind>.order(:pinged_at)) |
spec/use_cases/meeting_participant_pings/classify_network_quality_spec.rb |
criado — casos: rtt < 100, entre 100-300, > 300, nil |
spec/use_cases/meeting_participant_pings/parse_browser_spec.rb |
criado — casos: Chrome, Safari, Firefox, Edge, user_agent nil/vazio, não reconhecido |
spec/use_cases/meeting_participants/format_pings_spec.rb |
criado (renomeado de format_room_pings_spec.rb), com um caso cobrindo ping kind: wait para provar que o formatter é agnóstico a kind |
app/use_cases/meeting_participants/format_ping_dates.rb |
removido — ficou órfão depois que wait_pings passou a usar FormatPings (nunca teve spec própria) |
config/locales/pt-BR/models/meeting_participant_ping.yml |
atualizado — escopo de tradução renomeado de format_room_pings para format_pings |
Plano de implementação
- test + feat:
MeetingParticipantPings::ClassifyNetworkQuality— thresholds dertt - test + feat:
MeetingParticipantPings::ParseBrowser— regex para Chrome/Safari/Firefox/Edge; fallback “Desconhecido” - test + feat:
MeetingParticipants::FormatPings(antesFormatRoomPings) — monta a lista HTML por ping, usando os dois use cases acima - feat: trocar a fonte do campo
room_pingsno resource (array → relação) e usar o novo formatter - feat: renomear
FormatRoomPingsparaFormatPingse atualizar o campowait_pingsno mesmo resource - refactor: remover
MeetingParticipants::FormatPingDates, sem referência após o passo 5
Como verificar
```bash make run.test path=spec/use_cases/meeting_participant_pings/classify_network_quality_spec.rb make run.test path=spec/use_cases/meeting_participant_pings/parse_browser_spec.rb make run.test path=spec/use_cases/meeting_participants/format_pings_spec.rb
console
MeetingParticipantPings::ClassifyNetworkQuality.call(rtt: 50) # => :good MeetingParticipantPings::ClassifyNetworkQuality.call(rtt: 200) # => :medium MeetingParticipantPings::ClassifyNetworkQuality.call(rtt: 400) # => :bad MeetingParticipantPings::ClassifyNetworkQuality.call(rtt: nil) # => :unknown
MeetingParticipantPings::ParseBrowser.call(user_agent: “Mozilla/5.0 … Chrome/151.0.0.0 Safari/537.36”) # => “Chrome 151” ```
Verificação visual: abrir no Avo um MeetingParticipant com pings room e wait reais e confirmar que cada linha, nas duas seções, mostra horário + badge de qualidade + navegador, e que o “ver detalhes” expande o JSON completo do diagnostics.
Documentação
O comportamento resultante está em room_ping_quality — thresholds de rtt, motivo de downlink/effective_type ficarem fora do cálculo, e a lista de navegadores reconhecidos.