feat(pubmed): adicionar Suffix e CollectiveName em AuthorList#1245
feat(pubmed): adicionar Suffix e CollectiveName em AuthorList#1245Rossi-Luciano wants to merge 2 commits into
Conversation
Implementa add_suffix e add_collective_name em
packtools/sps/formats/pubmed.py, usando dados já expostos pelo model
article_contribs.Contrib (contrib_name["suffix"] e collab).
A DTD do PubMed exige que <Author> tenha nome pessoal
(FirstName/MiddleName/LastName/Suffix) OU CollectiveName, nunca os
dois: (((FirstName, MiddleName?, LastName, Suffix?, Initials?) |
CollectiveName), affiliation, Identifier*). xml_pubmed_author_list
agora respeita essa exclusividade: contribs com <collab> (sem <name>)
geram só CollectiveName, os demais geram FirstName/LastName/Suffix
como antes.
Corrige também um bug em get_affiliations: contribs sem afiliação
vinculada (comum no caso de CollectiveName, cujo único xref costuma
ser para author-notes) não tinham a chave "affs" no dict, causando
TypeError ao iterar author_reg.get("affs"). Agora usa
`or []` como fallback.
Refs scieloorg#1226, scieloorg#1236
|
Validado localmente contra a DTD oficial do PubMed (
(O validador linkado na issue-mãe #1226 não serve para este formato — ver comentário em #1226.) |
A DTD do PubMed exige FirstName e LastName sempre juntos em Author (nenhum dos dois é opcional isoladamente): Author (((FirstName, MiddleName?, LastName, Suffix?, Initials?) | CollectiveName), ...) Encontrado ao validar a saída da CLI em lote (scieloorg#1241) contra a DTD real usando tests/samples/example.xml: um contrib tem <given-names> mas não tem <surname>, gerando <Author><FirstName>...</FirstName>...</Author> sem <LastName> — inválido segundo a DTD (confirmado empiricamente: Author só com FirstName ou só com LastName sempre falha a validação). add_first_name e add_last_name agora caem para o outro campo quando o esperado está ausente, duplicando o único nome disponível em ambos — preserva a identificação do autor em vez de descartá-lo ou gerar XML inválido. Refs scieloorg#1226, scieloorg#1236
|
Adicionado mais um commit a este PR. Achado ao validar a #1241 (CLI em lote) contra a DTD real usando `tests/samples/example.xml`: a DTD do PubMed exige `FirstName` e `LastName` sempre juntos em `Author` (nenhum dos dois é opcional isoladamente), mas há um contrib nesse artigo real com `` e sem ``, gerando `...` sem `LastName` — inválido segundo a DTD (confirmei empiricamente: `Author` só com `FirstName` ou só com `LastName` sempre falha). `add_first_name`/`add_last_name` agora caem para o outro campo quando o esperado está ausente, duplicando o único nome disponível em ambos. Teste de regressão adicionado. Suíte: 49/49 passando. |
…perdida Encontrados ao validar as 26 amostras reais de tests/samples/ contra a DTD real do PubMed em vez de só 1-2 arquivos escolhidos a dedo. 1. get_date() só olhava article_date (epub), ignorando collection_date (pub-type="epub-ppub"/date-type="collection") -- o padrão mais comum em artigos SciELO mais antigos. Isso descartava 21 de 26 (81%) das amostras reais do lote inteiro por "falta de PubDate", quando na verdade tinham data. references.py e oai_dc.py já fazem esse fallback manualmente; só pubmed.py não fazia. 2. xml_pubmed_citations quebrava com AttributeError quando <ref-list> não tem <title> (opcional no JATS), pois presumia que xml_pubmed_title_reference_list já tinha criado <ReferenceList>. Agora cria o elemento se necessário -- válido pela DTD (ReferenceList (Title?, Reference*, ReferenceList*)). 3. get_first_page só olhava elocation-id, nunca fpage; LastPage não existia. Artigos com paginação tradicional (fpage/lpage) perdiam essa informação por completo. Agora FirstPage prefere fpage e cai para elocation-id só quando não há paginação (fluxo de Publicação Contínua); LastPage foi implementado. Validado: 24/26 amostras reais passam na DTD real (as 2 restantes dependem do fix de Author FirstName/LastName de scieloorg#1236/scieloorg#1245, ainda não mesclado nesta branch -- não são regressão destes fixes).
…ra a DTD Diferente de test_pubmed.py (uma pipe por vez, XML sintético mínimo), este teste roda o pipeline completo (build_pubmed_article + build_article_set_xml) em cada amostra real de tests/samples/*.xml e valida o resultado contra a DTD real do PubMed. A DTD foi vendorizada em tests/fixtures/pubmed/PubMed.dtd para o teste não depender de rede (CI sem acesso à internet, ou instabilidade do servidor da NLM não devem quebrar a suíte). Essa é exatamente a lacuna que deixou passar despercebidos os 3 bugs corrigidos no commit anterior (PubDate/collection_date, crash em citations, FirstPage/LastPage) por 8 PRs já abertos. Duas amostras (example.xml, 0034-8910-rsp-48-2-0249.xml) ainda dependem de fixes que vivem em scieloorg#1236/PR scieloorg#1245 (Author FirstName/ LastName pairing e fallback `or []` em get_affiliations), ainda não mesclados nesta branch -- marcadas em KNOWN_FAILURES com asserção inversa, para que a suíte avise (ao invés de silenciosamente continuar verde) quando alguém puder remover a entrada.
O que esse PR faz?
Implementa
<Suffix>e<CollectiveName>em<AuthorList>no conversor SPS → PubMed XML (packtools/sps/formats/pubmed.py), fechando 2 dos# TODOmarcados no código original. Os dados já estavam disponíveis no modelarticle_contribs.Contrib(contrib_name["suffix"]ecollab), só faltava conectar.A DTD do PubMed exige que
<Author>tenha nome pessoal OUCollectiveName, nunca os dois juntos:xml_pubmed_author_listagora respeita essa exclusividade: contribs que só têm<collab>(sem<name>) — o padrão SPS 1.10 para autoria em grupo — geram<Author><CollectiveName>...</CollectiveName></Author>; os demais continuam gerandoFirstName/LastName/Suffixcomo antes.Também corrige um bug em
get_affiliations: contribs sem afiliação vinculada (comum justamente no caso deCollectiveName, cujo únicoxrefcostuma apontar paraauthor-notes, não paraaff) não tinham a chave"affs"no dicionário, efor item in author_reg.get("affs")lançavaTypeError: 'NoneType' object is not iterable. Sem esse fix, qualquer artigo com autoria em grupo quebraria o pipeline inteiro ao tentar gerarCollectiveName.Onde a revisão poderia começar?
packtools/sps/formats/pubmed.py:add_suffix,add_collective_name(funções novas),get_affiliations(fix de uma linha), exml_pubmed_author_list(lógica de exclusividade FirstName/LastName vs. CollectiveName).Como este poderia ser testado manualmente?
source .venv/bin/activate && python -m pytest tests/sps/formats/test_pubmed.py -v(48 passed: 46 originais + 2 novos).get_affiliationsantes do fix (reverter só essa linha) e rodar:TypeError: 'NoneType' object is not iterable. Com o fix:<Author><CollectiveName>The SciELO Group</CollectiveName></Author>, semFirstName/LastNamevazios.tests/samples/0034-7094-rba-69-03-0227.xml, sem autoria em grupo) para confirmar que o comportamento de autores individuais não mudou.Algum cenário de contexto que queira dar?
Parte da quebra da #1226 em sub-issues. O padrão SPS 1.10 para autoria em grupo (documentado no guia interno da SciELO) é:
Este PR cobre apenas o
<collab>do primeirocontrib-group(o "nome do grupo"). Os membros individuais docontent-type="collab-list"continuam sendo listados comoAuthorcomuns emAuthorList(comportamento herdado, sem mudança) — mover esses membros paraGroupList/IndividualNameé escopo da #1237, que também depende deste PR (mesma funçãoxml_pubmed_author_list).Quais são tickets relevantes?
Closes #1236. Relacionado a #1226 (issue-mãe), #1237 (depende deste PR).
Referências
Author)<contrib>:<name>e<collab>— exemplos de autoria em grupo