Add SPS 1.10 related-article validations with i18n support#1140
Add SPS 1.10 related-article validations with i18n support#1140
Conversation
…hensive tests Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
Rossi-Luciano
left a comment
There was a problem hiding this comment.
Evidência de revisão — related_articles.py
Data: 2026-04-28
Revisor: Luciano
Artefatos analisados:
related_articles.py(validation) — módulo de validação
(XMLRelatedArticlesValidation,FulltextRelatedArticlesValidation,
RelatedArticleValidation)related_articles.py(models/, não-v2) — modelo atualmente importadorelated_articles.py(models/v2/) — modelo correto e atualrelated_article_rules.json— parâmetros de configuraçãorelated_articles.xml— XML artificial gerado para cobertura de testesrelated_articles-2026-28-04-131429-errors.csv— relatório de erros
Metodologia
Análise cruzada entre o módulo de validação, os dois módulos de model de mesmo nome (não-v2 e v2), o JSON de regras, o XML artificial construído com o princípio do defeito isolado e o relatório CSV. O XML usa article-type="correction", para o qual required_related_article_types=["corrected-article"] e acceptable_related_article_types=[], permitindo testar tanto a regra de presença obrigatória (R0) quanto a regra de compatibilidade de tipo (R8).
O subject das entradas do módulo no CSV é "related-article".
Regras validadas (11 métodos em RelatedArticleValidation + 1 de nível artigo)
| # | Método | Nível configurado | Comportamento |
|---|---|---|---|
| R0 | validate_presence_of_required_related_articles |
CRITICAL | retorna None se válido |
| R1 | validate_related_article_type_presence |
CRITICAL | retorna None se válido |
| R2 | validate_ext_link_type_presence |
CRITICAL | retorna None se válido |
| R3 | validate_related_article_type_value |
ERROR | skipa se ausente |
| R4 | validate_ext_link_type_value |
ERROR | skipa se ausente |
| R5 | validate_xlink_href_presence |
ERROR | retorna None se válido |
| R6 | validate_doi_preference |
WARNING | skipa se ext-link-type != "uri" |
| R7 | validate_attrib_order |
INFO | skipa se attribs ausente |
| R8 | validate_type |
CRITICAL | sempre retorna resultado |
| R9 | validate_ext_link_type |
CRITICAL | sempre retorna resultado |
| R10 | validate_doi() or validate_uri() |
CRITICAL | condicional por ext-link-type |
| R11 | validate_id_presence |
CRITICAL | retorna None se válido |
Casos de teste no XML artificial (11 elementos <related-article>)
| Caso | id | Defeito introduzido | Entradas esperadas |
|---|---|---|---|
| P1 | ra01 | Nenhum — caso ouro completo | nenhuma |
| C-R1 | ra02 | sem @related-article-type |
R1 CRITICAL + R7 INFO + R8 CRITICAL |
| C-R2 | ra03 | sem @ext-link-type |
R2 CRITICAL + R7 INFO + R9 CRITICAL |
| C-R3 | ra04 | @related-article-type="invalid-type" |
R3 ERROR + R8 CRITICAL |
| C-R4+R9 | ra05 | @ext-link-type="ftp" |
R4 ERROR + R9 CRITICAL |
| C-R5+R10 | ra06 | sem @xlink:href (ext-link-type="doi") |
R5 ERROR + R7 INFO + R10 CRITICAL |
| C-R6 | ra07 | ext-link-type="uri" para corrected-article |
R6 WARNING |
| C-R7 | ra08 | atributos fora de ordem no XML | R7 INFO |
| C-R8 | ra09 | related-article-type="commentary" para correction |
R8 CRITICAL |
| C-R10 | ra10 | xlink:href="nao-e-um-doi" |
R10 CRITICAL |
| C-R11 | ra11 | sem @id |
R7 INFO + R11 CRITICAL |
Cascatas de R7 INFO em C-R1, C-R2, C-R5 e C-R11 são estruturais: quando um atributo está ausente, related.attrib.keys() retorna um conjunto diferente da ordem esperada, fazendo R7 disparar como efeito colateral.
Não testável neste XML: C-R0 (requer artigo correction sem nenhum <related-article related-article-type="corrected-article">; incompatível com a presença de P1).
Verificação cruzada com o CSV
Todas as 19 entradas esperadas aparecem no CSV com os níveis de severidade
corretos. Nenhum falso positivo nem falso negativo originado do módulo.
| Subject | Caso | Nível | Verificação |
|---|---|---|---|
| related-article | C-R1 | CRITICAL | PASS — Add @related-article-type attribute |
| related-article | C-R1 cascata R8 | CRITICAL | PASS — correction does not match "" |
| related-article | C-R1 cascata R7 | INFO | PASS — ordem de atributos |
| related-article | C-R2 | CRITICAL | PASS — Add @ext-link-type attribute |
| related-article | C-R2 cascata R9 | CRITICAL | PASS — ext-link-type should be doi/uri for ra03 |
| related-article | C-R2 cascata R7 | INFO | PASS — ordem de atributos |
| related-article | C-R3 | ERROR | PASS — invalid-type not allowed |
| related-article | C-R3 cascata R8 | CRITICAL | PASS — correction does not match invalid-type |
| related-article | C-R4 | ERROR | PASS — ftp not allowed for @ext-link-type |
| related-article | C-R4 cascata R9 | CRITICAL | PASS — ext-link-type should be doi/uri for ra05 |
| related-article | C-R5 | ERROR | PASS — Add @xlink:href attribute |
| related-article | C-R5 cascata R10 | CRITICAL | PASS — Provide a valid DOI for ra06 |
| related-article | C-R5 cascata R7 | INFO | PASS — ordem de atributos |
| related-article | C-R6 | WARNING | PASS — use doi for corrected-article |
| related-article | C-R7 | INFO | PASS — ordem de atributos |
| related-article | C-R8 | CRITICAL | PASS — correction does not match commentary |
| related-article | C-R10 | CRITICAL | PASS — Invalid DOI format: nao-e-um-doi |
| related-article | C-R11 | CRITICAL | PASS — Add @id attribute |
| related-article | C-R11 cascata R7 | INFO | PASS — ordem de atributos |
R0 passou corretamente (elemento corrected-article presente em P1).
P1 não gerou nenhuma entrada.
Problema identificado: uso do modelo de dados incorreto
Situação atual
O módulo de validação importa:
from packtools.sps.models.related_articles import FulltextRelatedArticlesEste é o modelo não-v2, localizado em packtools/sps/models/related_articles.py.
O modelo correto e atual é packtools/sps/models/v2/related_articles.py.
Diferenças entre os modelos
| Aspecto | Modelo não-v2 (atual) | Modelo v2 (correto) |
|---|---|---|
| Classe principal | FulltextRelatedArticles |
RelatedArticles / RelatedArticlesByNode |
XPath para <related-article> |
./front | ./front-stub | ./body | ./back + .//related-article (amplo) |
article-meta//related-article (article) e front-stub//related-article (sub-article) |
Campo attribs |
related.attrib.keys() (chaves brutas com namespace) |
não exposto — campos individuais tipados |
| Estrutura do dado | dict com chaves brutas + attribs |
dataclass com propriedades tipadas |
| Estado de manutenção | deprecated | atual |
Impacto funcional observado neste teste
Neste cenário de teste, o modelo não-v2 produziu resultados funcionalmente corretos: todos os <related-article> foram encontrados e o campo original_article_type foi populado adequadamente via attribs_parent_prefixed. A busca ampla por XPath cobriu os elementos em article-meta sem falhas.
No entanto, o uso do modelo não-v2 representa um risco arquitetural:
- Busca excessivamente ampla: o XPath
./front | ./body | ./backcom
.//related-articlepode encontrar elementos em posições não previstas pela
especificação SPS, gerando falsos positivos em XMLs com estrutura não padrão. - Campo
attribscom namespace bruto:{http://www.w3.org/1999/xlink}href
exposto diretamente emattribs, exigindo que a validação conheça detalhes de
implementação do modelo. - Manutenção desacoplada: correções e evoluções aplicadas ao modelo v2 não
refletem na validação enquanto o import apontar para o não-v2.
Correção necessária
Substituir o import no módulo de validação:
# remover
from packtools.sps.models.related_articles import FulltextRelatedArticles
# adicionar
from packtools.sps.models.v2.related_articles import RelatedArticlesE adaptar FulltextRelatedArticlesValidation para usar a interface do modelo v2 (RelatedArticles, RelatedArticlesByNode, RelatedArticle).
Documentação de deprecação solicitada
O arquivo packtools/sps/models/related_articles.py deve receber docstring indicando que está deprecated e apontando para o substituto:
"""
.. deprecated::
Este módulo está deprecated. Use `packtools.sps.models.v2.related_articles`
(classes `RelatedArticles`, `RelatedArticlesByNode`, `RelatedArticle`).
Este arquivo é mantido apenas por compatibilidade com aplicações legadas.
"""Ruído identificado (outros módulos) — 15 entradas
Ruído estrutural (XML artificial mínimo): disp-formula, inline-formula,
table-wrap, app ausentes; subj-group heading ausente (2 entradas);
open science sem @specific-use válido (2 entradas).
Ruído de ambiente: rendition PDF ausente, DOI não registrado no Crossref,
issue não verificada no Core.
Ruído de referência (id and rid, 1 entrada): <ref id="B01"> sem
<xref ref-type="bibr" rid="B01"> no corpo.
Ruído de article dates (1 entrada): pub-date collection sem
@publication-format — padrão conhecido dos XMLs artificiais anteriores ao
uso de date-type="collection" sem esse atributo.
Conclusão
O módulo related_articles.py (validation) está funcionalmente correto: todas as 19 entradas esperadas dispararam com os níveis corretos, sem falsos positivos nem falsos negativos. No entanto, o módulo utiliza um modelo de dados deprecated (models/related_articles.py não-v2) em vez do modelo atual (models/v2/related_articles.py). A correção do import e a adaptação da interface para o modelo v2 são necessárias, assim como a inserção de docstring de deprecação no arquivo não-v2.
|
@copilot ## Evidência de revisão — Data: 2026-04-28
MetodologiaAnálise cruzada entre o módulo de validação, os dois módulos de model de mesmo nome (não-v2 e v2), o JSON de regras, o XML artificial construído com o princípio do defeito isolado e o relatório CSV. O XML usa O subject das entradas do módulo no CSV é Regras validadas (11 métodos em
|
| # | Método | Nível configurado | Comportamento |
|---|---|---|---|
| R0 | validate_presence_of_required_related_articles |
CRITICAL | retorna None se válido |
| R1 | validate_related_article_type_presence |
CRITICAL | retorna None se válido |
| R2 | validate_ext_link_type_presence |
CRITICAL | retorna None se válido |
| R3 | validate_related_article_type_value |
ERROR | skipa se ausente |
| R4 | validate_ext_link_type_value |
ERROR | skipa se ausente |
| R5 | validate_xlink_href_presence |
ERROR | retorna None se válido |
| R6 | validate_doi_preference |
WARNING | skipa se ext-link-type != "uri" |
| R7 | validate_attrib_order |
INFO | skipa se attribs ausente |
| R8 | validate_type |
CRITICAL | sempre retorna resultado |
| R9 | validate_ext_link_type |
CRITICAL | sempre retorna resultado |
| R10 | validate_doi() or validate_uri() |
CRITICAL | condicional por ext-link-type |
| R11 | validate_id_presence |
CRITICAL | retorna None se válido |
Casos de teste no XML artificial (11 elementos <related-article>)
| Caso | id | Defeito introduzido | Entradas esperadas |
|---|---|---|---|
| P1 | ra01 | Nenhum — caso ouro completo | nenhuma |
| C-R1 | ra02 | sem @related-article-type |
R1 CRITICAL + R7 INFO + R8 CRITICAL |
| C-R2 | ra03 | sem @ext-link-type |
R2 CRITICAL + R7 INFO + R9 CRITICAL |
| C-R3 | ra04 | @related-article-type="invalid-type" |
R3 ERROR + R8 CRITICAL |
| C-R4+R9 | ra05 | @ext-link-type="ftp" |
R4 ERROR + R9 CRITICAL |
| C-R5+R10 | ra06 | sem @xlink:href (ext-link-type="doi") |
R5 ERROR + R7 INFO + R10 CRITICAL |
| C-R6 | ra07 | ext-link-type="uri" para corrected-article |
R6 WARNING |
| C-R7 | ra08 | atributos fora de ordem no XML | R7 INFO |
| C-R8 | ra09 | related-article-type="commentary" para correction |
R8 CRITICAL |
| C-R10 | ra10 | xlink:href="nao-e-um-doi" |
R10 CRITICAL |
| C-R11 | ra11 | sem @id |
R7 INFO + R11 CRITICAL |
Cascatas de R7 INFO em C-R1, C-R2, C-R5 e C-R11 são estruturais: quando um atributo está ausente, related.attrib.keys() retorna um conjunto diferente da ordem esperada, fazendo R7 disparar como efeito colateral.
Não testável neste XML: C-R0 (requer artigo correction sem nenhum <related-article related-article-type="corrected-article">; incompatível com a presença de P1).
Verificação cruzada com o CSV
Todas as 19 entradas esperadas aparecem no CSV com os níveis de severidade
corretos. Nenhum falso positivo nem falso negativo originado do módulo.
| Subject | Caso | Nível | Verificação |
|---|---|---|---|
| related-article | C-R1 | CRITICAL | PASS — Add @related-article-type attribute |
| related-article | C-R1 cascata R8 | CRITICAL | PASS — correction does not match "" |
| related-article | C-R1 cascata R7 | INFO | PASS — ordem de atributos |
| related-article | C-R2 | CRITICAL | PASS — Add @ext-link-type attribute |
| related-article | C-R2 cascata R9 | CRITICAL | PASS — ext-link-type should be doi/uri for ra03 |
| related-article | C-R2 cascata R7 | INFO | PASS — ordem de atributos |
| related-article | C-R3 | ERROR | PASS — invalid-type not allowed |
| related-article | C-R3 cascata R8 | CRITICAL | PASS — correction does not match invalid-type |
| related-article | C-R4 | ERROR | PASS — ftp not allowed for @ext-link-type |
| related-article | C-R4 cascata R9 | CRITICAL | PASS — ext-link-type should be doi/uri for ra05 |
| related-article | C-R5 | ERROR | PASS — Add @xlink:href attribute |
| related-article | C-R5 cascata R10 | CRITICAL | PASS — Provide a valid DOI for ra06 |
| related-article | C-R5 cascata R7 | INFO | PASS — ordem de atributos |
| related-article | C-R6 | WARNING | PASS — use doi for corrected-article |
| related-article | C-R7 | INFO | PASS — ordem de atributos |
| related-article | C-R8 | CRITICAL | PASS — correction does not match commentary |
| related-article | C-R10 | CRITICAL | PASS — Invalid DOI format: nao-e-um-doi |
| related-article | C-R11 | CRITICAL | PASS — Add @id attribute |
| related-article | C-R11 cascata R7 | INFO | PASS — ordem de atributos |
R0 passou corretamente (elemento corrected-article presente em P1).
P1 não gerou nenhuma entrada.
Problema identificado: uso do modelo de dados incorreto
Situação atual
O módulo de validação importa:
from packtools.sps.models.related_articles import FulltextRelatedArticlesEste é o modelo não-v2, localizado em packtools/sps/models/related_articles.py.
O modelo correto e atual é packtools/sps/models/v2/related_articles.py.
Diferenças entre os modelos
| Aspecto | Modelo não-v2 (atual) | Modelo v2 (correto) |
|---|---|---|
| Classe principal | FulltextRelatedArticles |
RelatedArticles / RelatedArticlesByNode |
XPath para <related-article> |
./front | ./front-stub | ./body | ./back + .//related-article (amplo) |
article-meta//related-article (article) e front-stub//related-article (sub-article) |
Campo attribs |
related.attrib.keys() (chaves brutas com namespace) |
não exposto — campos individuais tipados |
| Estrutura do dado | dict com chaves brutas + attribs |
dataclass com propriedades tipadas |
| Estado de manutenção | deprecated | atual |
Impacto funcional observado neste teste
Neste cenário de teste, o modelo não-v2 produziu resultados funcionalmente corretos: todos os <related-article> foram encontrados e o campo original_article_type foi populado adequadamente via attribs_parent_prefixed. A busca ampla por XPath cobriu os elementos em article-meta sem falhas.
No entanto, o uso do modelo não-v2 representa um risco arquitetural:
- Busca excessivamente ampla: o XPath
./front | ./body | ./backcom
.//related-articlepode encontrar elementos em posições não previstas pela
especificação SPS, gerando falsos positivos em XMLs com estrutura não padrão. - Campo
attribscom namespace bruto:{http://www.w3.org/1999/xlink}href
exposto diretamente emattribs, exigindo que a validação conheça detalhes de
implementação do modelo. - Manutenção desacoplada: correções e evoluções aplicadas ao modelo v2 não
refletem na validação enquanto o import apontar para o não-v2.
Correção necessária
Substituir o import no módulo de validação:
# remover
from packtools.sps.models.related_articles import FulltextRelatedArticles
# adicionar
from packtools.sps.models.v2.related_articles import RelatedArticlesE adaptar FulltextRelatedArticlesValidation para usar a interface do modelo v2 (RelatedArticles, RelatedArticlesByNode, RelatedArticle).
Documentação de deprecação solicitada
O arquivo packtools/sps/models/related_articles.py deve receber docstring indicando que está deprecated e apontando para o substituto:
"""
.. deprecated::
Este módulo está deprecated. Use `packtools.sps.models.v2.related_articles`
(classes `RelatedArticles`, `RelatedArticlesByNode`, `RelatedArticle`).
Este arquivo é mantido apenas por compatibilidade com aplicações legadas.
"""Ruído identificado (outros módulos) — 15 entradas
Ruído estrutural (XML artificial mínimo): disp-formula, inline-formula,
table-wrap, app ausentes; subj-group heading ausente (2 entradas);
open science sem @specific-use válido (2 entradas).
Ruído de ambiente: rendition PDF ausente, DOI não registrado no Crossref,
issue não verificada no Core.
Ruído de referência (id and rid, 1 entrada): <ref id="B01"> sem
<xref ref-type="bibr" rid="B01"> no corpo.
Ruído de article dates (1 entrada): pub-date collection sem
@publication-format — padrão conhecido dos XMLs artificiais anteriores ao
uso de date-type="collection" sem esse atributo.
Conclusão
O módulo related_articles.py (validation) está funcionalmente correto: todas as 19 entradas esperadas dispararam com os níveis corretos, sem falsos positivos nem falsos negativos. No entanto, o módulo utiliza um modelo de dados deprecated (models/related_articles.py não-v2) em vez do modelo atual (models/v2/related_articles.py). A correção do import e a adaptação da interface para o modelo v2 são necessárias, assim como a inserção de docstring de deprecação no arquivo não-v2.
O que esse PR faz?
Implementa 8 das 12 regras de validação para
<related-article>conforme SPS 1.10, elevando a conformidade para ~70%.Novas validações (P0):
validate_related_article_type_presence()— CRITICALvalidate_ext_link_type_presence()— CRITICALvalidate_related_article_type_value()— ERROR (17 valores permitidos, case-sensitive)validate_ext_link_type_value()— ERROR (doi|uri)validate_xlink_href_presence()— ERRORvalidate_doi_preference()— WARNING (exceções:reviewer-report,preprint)Melhorias em validações existentes (P1):
validate_attrib_order()— rebaixado para INFO, corrigido FIXME, tratamento gracioso de attribs ausentesadvice_text/advice_params) adicionado a todas as mensagens existentes e novasConfiguração:
related_article_rules.jsonatualizado com 6 novos error level keys.peer-reviewed-materialadicionado à lista de tipos permitidos (necessário para contexto de peer review).Onde a revisão poderia começar?
packtools/sps/validation/related_articles.py— constantes no topo, depois as novasvalidate_*methods na classeRelatedArticleValidation.Como este poderia ser testado manualmente?
python -m pytest tests/sps/validation/test_related_articles.py -v # 119 testes (32 originais + 87 novos)Exemplo de uso direto:
Algum cenário de contexto que queira dar?
Validações parciais já existiam (
validate_type,validate_ext_link_type,validate_id_presence,validate_doi,validate_uri,validate_attrib_order). Este PR complementa com verificações de presença de atributos obrigatórios, validação contra lista global de valores permitidos, e preferência DOI vs URI.Testes de peer review (
test_peer_review.py) tinham 8 falhas pré-existentes. Este PR corrige 2 delas (testes de related-article); as 6 restantes são problemas não relacionados.Screenshots
N/A
Quais são tickets relevantes?
Criar validações para o elemento
<related-article>— SPS 1.10Referências
<related-article><related-article>Original prompt
This section details on the original issue you should resolve
<issue_title>Criar validações para o elemento </issue_title>
<issue_description>## Objetivo
Implementar validações para o elemento
<related-article>conforme a especificação SPS 1.10, aumentando a conformidade de X% para 70% (8 de 12 regras).Nota: Algumas validações para
<related-article>podem já estar parcialmente implementadas no repositório. Este Issue visa reavaliar, complementar e garantir cobertura completa das regras SPS 1.10.Contexto
O elemento
<related-article>indica documentos relacionados ao que está sendo publicado (erratas, retratações, adendos, comentários, cartas, pareceres, preprints). Quando o documento relacionado também está publicado em SciELO, deve-se adicionar o<related-article>em ambos os documentos. Validações corretas garantem presença de atributos obrigatórios, valores corretos, e uso adequado de DOI vs URI.Conformidade atual: X de 12 regras implementadas (X%)
Meta após implementação: 8 de 12 regras (70%)
Documentação SPS
Referência oficial: https://docs.google.com/document/d/1GTv4Inc2LS_AXY-ToHT3HmO66UT0VAHWJNOIqzBNSgA/edit?tab=t.0#heading=h.relatedarticle
Regras principais conforme SPS 1.10:
Ocorrência:
<related-article>pode aparecer zero ou mais vezes em<article-meta>e<front-stub>Atributos obrigatórios:
@related-article-type(obrigatório)@id(obrigatório)@ext-link-type(obrigatório, valores:doiouuri)Valores permitidos para
@related-article-type:corrected-article- Erratacorrection-forward- Documento corrigido pela errataretracted-article- Retratação (total ou parcial)retraction-forward- Documento retratado totalmentepartial-retraction- Documento retratado parcialmenteaddended-article- Adendoaddendum- Documento objeto do adendoexpression-of-concern- Manifestação de preocupaçãoobject-of-concern- Documento objeto de manifestação de preocupaçãocommentary-article- Comentário ou cartacommentary- Documento comentado ou respostareply- Resposta (para comentário ou carta)letter- Carta ou resposta para cartareviewed-article- Parecer (revisão por pares)reviewer-report- Documento com parecerpreprint- Manuscrito em servidor de preprintsRegras de
@ext-link-type:@ext-link-type="doi"uri:@related-article-type="reviewer-report"(parecer com link externo)@related-article-type="preprint"(preprint)doise existir, mesmo para exceçõesOrdem obrigatória de atributos:
@related-article-type@id@xlink:href@ext-link-typePosicionamento:
<permissions>ou acima de<counts>Tradução (
<sub-article>):<related-article>também no idioma traduzido@idde<article>em<sub-article>Regras a Implementar
P0 – Críticas (implementar obrigatoriamente)
@related-article-type@related-article-typeé obrigatório em<related-article>@id@idé obrigatório em<related-article>@ext-link-type@ext-link-typeé obrigatório em<related-article>@related-article-type@related-article-typedeve estar na lista de valores permitidos@ext-link-type@ext-link-typedeve ser"doi"ou"uri"doicomo padrão@ext-link-typedeve ser"doi"(exceto parareviewer-reportepreprintque podem usar"uri")P1 – Importantes (implementar se possível)
@xlink:href@xlink:hrefé obrigatório em<related-article>@related-article-type,@id,@xlink:href,@ext-link-typeP2 – Futuras (fora do escopo deste Issue)
<permissions>ou antes de<counts><sub-article>quando há no artigo principal@identre<article>e<sub-article>💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.