Relatório de validação agrupado por gravidade/categoria + CSV objetivo (#34)#35
Open
Rossi-Luciano wants to merge 11 commits into
Open
Relatório de validação agrupado por gravidade/categoria + CSV objetivo (#34)#35Rossi-Luciano wants to merge 11 commits into
Rossi-Luciano wants to merge 11 commits into
Conversation
…lidacao - Adiciona o campo "advise" as rows, preenchido com o resultado bruto "advice" do packtools (a acao de correcao) - antes esse texto ia, sem renomear, para o campo "message". - Passa a preencher "message" com o campo "message" que o proprio packtools ja calcula (o diagnostico automatico "Got X, expected Y"), antes descartado dentro do dicionario bruto "data". - Fixa "subject" como "Renditions" e "Assets" nas duas rows que o spsvalidator monta manualmente (PDF de idioma ausente, asset referenciado no XML mas ausente do pacote), no lugar do valor por instancia (idioma ou nome de arquivo) usado antes. - Escreve manualmente o texto de "advise" para essas duas rows, ja que elas nao passam por build_response() do packtools e nao tinham uma acao de correcao calculada separadamente do diagnostico. Porque: primeira etapa da issue scieloorg#34, aberta a partir de feedback de usabilidade da equipe de Publicacao. O CSV/relatorio de validacao misturava, numa coluna so chamada "message", o texto de acao corretiva do packtools - sem nunca expor o diagnostico separado. Alem disso, cada asset ausente virava uma categoria propria (o nome do arquivo), fragmentando o agrupamento por categoria em vez de juntar ocorrencias do mesmo tipo de problema. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LZA68J1nQ47EsvTN63wgwt
- Troca as duas asserções que checavam "subject == fig1.jpg" (nome do arquivo) por "subject == 'Assets'", validando o nome do arquivo dentro do texto de "message" em vez de na categoria. Porque: consequência direta da mudança em domain/validation.py (commit anterior) - "subject" das rows de asset ausente deixou de ser o nome do arquivo e passou a ser a categoria fixa "Assets". Sem este ajuste os dois testes quebrariam. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LZA68J1nQ47EsvTN63wgwt
…s customizados - Troca "data" (dump bruto de depuracao do resultado de validacao) por "advise" (acao de correcao) em VALIDATION_CSV_COLUMNS. - build_validation_csv() ganha o parametro opcional "headers", com fallback para os nomes tecnicos das colunas (DEFAULT_CSV_HEADERS) quando nenhum cabecalho customizado e informado. Porque: segunda etapa da issue scieloorg#34. O feedback da equipe de Publicacao apontava que o CSV reunia informacao tecnica e de depuracao numa coluna so, dificultando localizar a acao necessaria. O parametro "headers" evita acoplar esta camada de dominio (sem dependencia do Flask) ao flask_babel - a traducao dos cabecalhos fica por conta de quem chama a funcao (ex.: a rota web, que ja tem acesso a gettext). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LZA68J1nQ47EsvTN63wgwt
… atual - Substitui o teste antigo, que ja verificava colunas (group, response, advice) que nao existem mais em VALIDATION_CSV_COLUMNS ha tempos - o teste falhava mesmo antes desta sessao, sem relacao com a mudanca atual (confirmado rodando a suite completa antes de qualquer edicao). - Novo teste cobre o formato de row usado hoje (package, status, subject, message, advise) e confirma que "data" nao aparece no CSV. - Adiciona teste para o parametro "headers" customizado introduzido no commit anterior. Porque: aproveitado o commit em domain/export.py para corrigir um teste ja desatualizado no mesmo arquivo, e cobrir a funcionalidade nova (headers) que ele nao testava. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LZA68J1nQ47EsvTN63wgwt
…desktop - Troca a asserção "group,title" (ja desatualizada antes desta sessao, nao batia com nenhuma versao recente de VALIDATION_CSV_COLUMNS) por "package,status,subject,message,advise", refletindo as colunas atuais. Porque: consequência da mudanca em domain/export.py. A API desktop (save_validation_csv) chama build_validation_csv() sem parametro de "headers" customizado, entao usa o fallback tecnico em ingles - o que e esperado, ja que essa chamada roda fora do contexto de requisicao do Flask e nao tem acesso a gettext. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LZA68J1nQ47EsvTN63wgwt
…a e mensagem
- build_grouped_report(rows): agrupa por gravidade (ordem fixa
CRITICAL, ERROR, WARNING), depois por categoria (ordem alfabetica),
depois por mensagem de problema identica dentro da categoria (mais
frequente primeiro) - em vez de listar cada ocorrencia solta.
- Cada ocorrencia recebe um subconjunto legivel de detalhes tecnicos
(item, sub_item, validation_type, expected_value, got_value),
descartando os campos de infraestrutura de i18n do packtools
(msg_text, msg_params, adv_text, adv_params) e os campos internos de
posicao (parent*, title), que nao agregam valor pro usuario final.
- Cada ocorrencia recebe uma chave estavel ("key"), gerada por hash de
gravidade+categoria+mensagem+acao+indice - usada pelo relatorio HTML
(proximo commit) para lembrar, no navegador, quais itens ja foram
marcados como corrigidos.
Porque: nucleo da issue scieloorg#34. Testando com um pacote real (254
ocorrencias, 24 categorias), a categoria "contrib" sozinha tinha 59
ocorrencias mas so 11 mensagens de problema distintas - sem esse
agrupamento por mensagem, o relatorio recriaria o mesmo problema do
CSV bruto (parede de texto repetido), so que em HTML.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LZA68J1nQ47EsvTN63wgwt
- Ordenacao de gravidade (CRITICAL/ERROR/WARNING primeiro, extras
depois) e de categoria (alfabetica) dentro de cada gravidade.
- Contagem de ocorrencias por gravidade e por categoria.
- Agrupamento de mensagens repetidas dentro de uma categoria,
incluindo ordenacao por frequencia (mais comum primeiro) e
desempate alfabetico.
- Selecao dos campos tecnicos expostos (e omissao dos vazios/None).
- Estabilidade e unicidade das chaves ("key") entre duas execucoes com
o mesmo input, e caso de lista vazia.
Porque: cobertura da logica pura de domain/report.py, testavel isolada
de Flask/banco/packtools.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LZA68J1nQ47EsvTN63wgwt
- download_csv(): monta os cabecalhos do CSV via gettext (Pacote, Gravidade, Categoria, Problema, Acao de correcao), seguindo o idioma da UI (pt/en/es), e passa para build_validation_csv(headers=...). - Nova rota GET /validation/<history_id>/report.html (view_report): busca os detalhes da validacao, agrupa via domain.report.build_grouped_report() e renderiza um template novo. Porque: liga a camada de dominio (domain/export.py, domain/report.py) a interface web. Os cabecalhos localizados mantem o CSV consistente com o resto do app, que ja suporta pt/en/es; a nova rota e a etapa central da issue scieloorg#34, expondo o agrupamento por gravidade/categoria como uma pagina navegavel. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LZA68J1nQ47EsvTN63wgwt
… de correcao - Pagina standalone com secoes por gravidade (cor de destaque na borda), categorias e mensagens agrupadas em <details> fechados por padrao (nada expandido ao carregar a pagina). - Checkbox "Corrigido" por ocorrencia e "Marcar todas como corrigidas" por grupo de mensagem repetida, com estado guardado no localStorage do navegador (chave por history_id) e restaurado ao recarregar a pagina; ocorrencias marcadas ganham estilo visual (opacidade reduzida, texto riscado). - Contadores de ocorrencias corrigidas em tres niveis (total da pagina, por gravidade, por categoria), recalculados ao vivo a cada marcacao. - Botao "Limpar marcacoes", com confirmacao, que zera o localStorage e a interface. - Detalhes tecnicos de cada ocorrencia escondidos atras de uma segunda expansao opcional, separados do texto de problema/acao de correcao. Porque: entrega visual da issue scieloorg#34. O padrao fechado por default e o agrupamento de mensagens repetidas foram ajustados depois de testar com um pacote real - categorias abertas por padrao e ocorrencias repetidas sem agrupar recriavam o problema original (parede de texto, maior esforco pra localizar do que pra analisar). O checklist de correcao foi pedido em seguida, pensado como controle momentaneo de quem esta corrigindo o XML durante uma sessao de trabalho, sem persistir no banco - por isso localStorage, nao uma tabela nova. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LZA68J1nQ47EsvTN63wgwt
- Nova coluna "Relatório", com link para a rota /report.html do pacote (nova aba). - Renomeia a coluna que baixa o CSV, de "Validação" (cabecalho) / "Relatório" (texto do link) para "CSV" nos dois lugares. Porque: da acesso ao novo relatorio HTML a partir do historico (ultima etapa da issue scieloorg#34). A renomeacao evita a colisao de nomes entre os dois links (antes "Relatório" era o texto do botao que baixava o CSV) e segue o espirito da issue de tornar o CSV um artefato tecnico secundario, nao mais a interface principal de consulta dos resultados. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LZA68J1nQ47EsvTN63wgwt
…nativa - Move "icon" de window_kwargs (repassado a webview.create_window()) para webview.start(icon=...), unico lugar onde esse parametro existe na versao instalada do pywebview (6.2.1) - create_window() nao aceita mais "icon", o que fazia a janela desktop falhar ao abrir com TypeError. - webview.start() passa a receber private_mode=False e storage_path apontando para ~/.spsvalidator/webview_data, ligando cookies e localStorage na janela desktop nativa. Antes, o padrao do pywebview (private_mode=True) desligava o localStorage por completo no backend GTK/WebKit2GTK usado no Linux (nao so "nao persiste entre aberturas" - a propria API ficava indisponivel). Porque: o bug do "icon" e pre-existente (nao introduzido nesta sessao) e so apareceu ao tentar abrir a janela desktop de verdade pela primeira vez. O ajuste de private_mode foi pedido para que o checklist de correcao do relatorio (commit anterior) se comporte de forma consistente entre "--browser" e a janela nativa - ainda que, na pratica, o relatorio sempre abra no navegador do sistema em ambos os casos (o pywebview abre links com target="_blank" externamente por padrao), esse ajuste evita que qualquer outra parte do app que dependa de armazenamento local dentro da propria janela fique inconsistente entre os dois modos. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LZA68J1nQ47EsvTN63wgwt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
O que esse PR faz?
Resolve a issue #34: substitui o CSV bruto (que misturava texto de diagnóstico técnico com a ação de correção numa coluna só, chamada
data) por um relatório HTML navegável, agrupado por gravidade e categoria, com um checklist de progresso.message(diagnóstico) deadvise(ação de correção) nas rows de validação; remove a coluna técnicadatado CSV e localiza seus cabeçalhos (pt/en/es)./validation/<id>/report.html: ocorrências agrupadas por gravidade (CRITICAL/ERROR/WARNING) → categoria (alfabética) → mensagem de problema idêntica (evita repetir o mesmo texto uma vez por ocorrência — ex.: uma categoria com 59 ocorrências e só 11 mensagens distintas, testado com pacote real).localStoragedo navegador — pensado como controle momentâneo de quem está corrigindo o XML, sem persistir no banco.iconque opywebviewnão aceita mais emcreate_window()) e ligalocalStorage/cookies nela.Onde a revisão poderia começar?
spsvalidator/src/spsvalidator/domain/report.py(lógica pura de agrupamento, sem dependência de Flask) →spsvalidator/src/spsvalidator/web/routes.py(rotaview_report) →spsvalidator/src/spsvalidator/web/templates/report.html(template + checklist).Como este poderia ser testado manualmente?
pip install -e .espsvalidator --browser(ou a janela nativa)..zipSPS.Pacote, Gravidade, Categoria, Problema, Ação de correção), sem a colunadata.Algum cenário de contexto que queira dar?
Motivado por feedback de usabilidade da equipe de Publicação sobre a versão anterior do SPS Validator: o CSV era a única forma de ver o detalhamento de uma validação, misturava informação de depuração com o que realmente importa, e exigia mais esforço para localizar um problema do que para de fato analisá-lo. O desenho do agrupamento (fechado por padrão, mensagens repetidas consolidadas) foi ajustado depois de testar com pacotes reais, não só dados sintéticos — os primeiros testes revelaram, por exemplo, que categorias abertas por padrão recriavam a mesma "parede de texto" do problema original.
Screenshots
Interface modificada com relatório em HTML:
Relatório HTML com agrupamento:
Visão geral do relatório HTML:
Relatório CSV com colunar modificadas:
HTML do artigo:
PDF do pacote:
Quais são os tickets relevantes?
Closes #34
Referências
N/A
Segurança da informação (NSI.04)
Este PR manipula dados sensíveis ou pessoais (LGPD)?
Este PR altera autenticação, autorização, controle de acesso ou gerenciamento de sessão?
Este PR introduz, atualiza ou remove dependências de terceiros?
domain/report.pyusa só a biblioteca padrão (hashlib).Este PR foi validado pelo pipeline de segurança (SonarQube / Trivy)?
Este PR concatena, monta ou executa comandos SQL, HTML ou JavaScript a partir de entrada externa?
{{ }}), com autoescape padrão do Flask ativo e sem nenhum uso de|safeem nenhum template do repositório (conferido). Testado na prática: texto de validação contendo<role>foi renderizado como entidade escapada (<role>), não como HTML. Os dois valores injetados em<script>inline usam o filtro|tojson, forma segura de serializar para JS. Nenhuma consulta SQL é concatenada nesta mudança.Este PR expõe novos endpoints, telas ou serviços?
GET /validation/<history_id>/report.html. HTTPS não se aplica: o app é uma ferramenta desktop/local, o servidor Flask escuta só em127.0.0.1(loopback), sem exposição de rede — mesmo padrão dos endpoints já existentes (/validation/<id>/report.csv, previews HTML/PDF), sem modelo de autenticação porque é uso local de um único usuário por vez.Algum segredo, senha, chave ou token está sendo adicionado ao código-fonte?