Skip to content

fix: extrai parágrafos de resumo estruturado (sec por subseção) - #1333

Merged
Rossi-Luciano merged 2 commits into
scieloorg:masterfrom
Rossi-Luciano:fix/pdf-structured-abstract-empty
Sep 9, 2026
Merged

fix: extrai parágrafos de resumo estruturado (sec por subseção)#1333
Rossi-Luciano merged 2 commits into
scieloorg:masterfrom
Rossi-Luciano:fix/pdf-structured-abstract-empty

Conversation

@Rossi-Luciano

@Rossi-Luciano Rossi-Luciano commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

O que esse PR faz?

Corrige a issue #1332: quando um artigo tem resumo estruturado (subseções tipo Introduction/Methods/Results, cada uma dentro de sua própria <sec>), o resumo saía vazio no PDF gerado — só o cabeçalho "ABSTRACT"/"RESUMO" aparecia, indo direto para "Keywords".

extract_abstract_data e extract_trans_abstract_data (packtools/sps/formats/pdf/pipeline/xml.py) usavam node.findall('p'), que só localiza <p> filho direto do elemento <abstract>/<trans-abstract>. Num resumo estruturado, os <p> ficam um nível mais fundo (dentro de <sec>), então a extração retornava lista vazia.

Adiciona _extract_abstract_paragraphs(node), um helper recursivo que percorre <p> e <sec> em qualquer profundidade, incluindo o <title> de cada <sec> como rótulo (o texto fonte já vem com o :, ex. "Methods:") — preserva a estrutura do resumo no texto final em vez de apagar a divisão entre subseções. Reaproveitado nas duas funções, que tinham exatamente o mesmo bug.

Onde a revisão poderia começar?

packtools/sps/formats/pdf/pipeline/xml.py_extract_abstract_paragraphs (helpers privados) e os dois pontos que passaram a chamá-la: extract_abstract_data e extract_trans_abstract_data.

Como este poderia ser testado manualmente?

python -m packtools.sps.formats.pdf_generator \
  -i <artigo-com-resumo-estruturado>.xml \
  -l layout.docx \
  -o saida.pdf \
  --libreoffice-binary libreoffice

Conferir o RESUMO/ABSTRACT na página 1.

Algum cenário de contexto que queira dar?

Achado por acaso revisando visualmente o corpus de teste de 26 artigos reais (layout_examples_rafael/corpus/), enquanto validava outro fix (afiliação duplicada). Confirmei a causa raiz e a abrangência antes de abrir a issue #1332: 6 de 26 artigos (23%) tinham resumo vazio por esse motivo — a10.xml (RESUMO em português também vazio), a11.xml, a14.xml, a17.xml, a20.xml, a28.xml. Não é um caso raro nem exclusivo de uma área (saúde e administração/negócios ambos afetados).

Screenshots

issue1332_before_after

Quais são os tickets relevantes?

Corrige #1332.

Referências

N/A


Segurança da informação (NSI.04)

Seção obrigatória. Marque as opções aplicáveis e justifique quando necessário. Referência: NSI.04 - Norma de Desenvolvimento Seguro.

Este PR manipula dados sensíveis ou pessoais (LGPD)?

  • Sim — descreva os controles de proteção aplicados (criptografia, mascaramento, anonimização, etc.):
  • Não

Este PR altera autenticação, autorização, controle de acesso ou gerenciamento de sessão?

  • Sim — descreva o que mudou e por quê:
  • Não

Este PR introduz, atualiza ou remove dependências de terceiros?

  • Sim — as novas dependências foram verificadas no SBOM/Trivy sem vulnerabilidades críticas/altas em aberto?
    • Verificado e aprovado
    • Pendente / vulnerabilidade aceita com justificativa:
  • Não

Este PR foi validado pelo pipeline de segurança (SonarQube / Trivy)?

  • Sim — link do job:
  • Não aplicável a este PR (justifique): mudança isolada de extração de texto a partir do próprio XML do artigo, sem I/O externo ou entrada não confiável

Este PR concatena, monta ou executa comandos SQL, HTML ou JavaScript a partir de entrada externa?

  • Sim — confirme que há sanitização/parametrização (prepared statements, escaping, etc.):
  • Não

Este PR expõe novos endpoints, telas ou serviços?

  • Sim — HTTPS obrigatório está garantido e o acesso segue o princípio de menor privilégio?
  • Não

Algum segredo, senha, chave ou token está sendo adicionado ao código-fonte?

  • Não, nenhum segredo foi commitado
  • Sim (bloquear merge e corrigir antes de prosseguir)

🤖 Generated with Claude Code

https://claude.ai/code/session_01X8r2LRJ3PGTT9vLaPtb373

Corrige a issue scieloorg#1332: extract_abstract_data e extract_trans_abstract_data
usavam node.findall('p'), que so acha <p> filho DIRETO de <abstract>/
<trans-abstract>. Um resumo estruturado (subsecoes tipo Introduction/
Methods/Results, cada uma sua propria <sec><title>...</title><p>...</p>
</sec>) tem os <p> um nivel mais fundo, entao a extracao retornava
content vazio - o PDF mostrava so o cabecalho "ABSTRACT"/"RESUMO" e ia
direto pra "Keywords", sem nenhum texto de resumo.

Adiciona _extract_abstract_paragraphs(node), helper recursivo que
percorre <p> e <sec> em qualquer profundidade, incluindo o <title> de
cada <sec> como rotulo (ja vem com o ":" da propria fonte, ex.:
"Methods:"), preservando a estrutura do resumo em vez de so concatenar
tudo. Reaproveitado nas duas funcoes, que tinham o mesmo bug.

Confirmado contra o corpus real de 26 artigos: 6 (23%) tinham resumo
vazio por esse motivo (a10, a11, a14, a17, a20, a28) - a10 tinha o
RESUMO em portugues vazio tambem. Todos os 6 corrigidos; os outros 20
(resumo simples, sem <sec>) continuam identicos.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X8r2LRJ3PGTT9vLaPtb373
# Private helpers
# -----------------

def _extract_abstract_paragraphs(node):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Boa.

if p is not None:
abstract.append(''.join(p.itertext()).strip())
data['content'] = ' '.join(abstract)
data['content'] = ' '.join(_extract_abstract_paragraphs(node_abstract))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A extração agora preenche corretamente o resumo estruturado, mas no a10, por exemplo, esse mesmo conteúdo também entra novamente pelo extract_body_data(), que percorre xml_tree.findall('.//sec') e, portanto, inclui as de e . No resultado, “Background: A staggering 99%…” e “Contexto: Um número impressionante…” aparecem no resumo e outra vez como corpo. Precisamos restringir a extração do corpo às seções do principal. Veja como ficou:

Página 1:

Image

Página 2:

Image

Página 3:

Image

@pitangainnovare pitangainnovare left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A correção preenche os resumos estruturados que antes ficavam vazios, mas encontrei dois problemas na saída final que precisam ser tratados antes da aprovação.

No a10.xml, o conteúdo do ABSTRACT e do RESUMO passa a aparecer duas vezes. O PR agora extrai corretamente as <sec> internas do <abstract> e do <trans-abstract>, mas extract_body_data() continua usando xml_tree.findall('.//sec'). Com isso, ela também considera como corpo as seções pertencentes aos resumos.

No DOCX gerado pelo HEAD deste PR, por exemplo, “Background: A staggering 99%…” aparece uma vez no ABSTRACT e novamente como seção do corpo. O mesmo ocorre com “Contexto: Um número impressionante…” no RESUMO, além das demais seções Objective/Objetivo, Methods/Métodos, Results/Resultados e Conclusion/Conclusão.

Acredito que extract_body_data() deva percorrer somente as <sec> do <body> principal.

Também há um problema dos títulos de seções sem pontuação. A implementação pressupõe que todo <sec><title> já possui dois-pontos, mas isso não ocorre em parte do corpus. Em a11.xml, por exemplo, o XML contém <title>Objetivo</title>, e a saída fica “Objetivo descrever...”, enquanto o PDF publicado apresenta “Objetivo: descrever...”. O mesmo padrão aparece também em a17.xml e a20.xml. A formatação poderia, como solução, acrescentar um separador quando o título não trouxer pontuação final, sem duplicá-lo quando o XML já tiver :.

…tulo sem pontuacao

extract_body_data() usava './/sec' sobre a arvore inteira, incluindo as
<sec> internas do resumo estruturado (abstract/trans-abstract), o que
duplicava o mesmo conteudo no resumo e no corpo (ex: a10.xml).

_extract_abstract_paragraphs() assumia que o <title> de cada subsecao
ja trazia ':' (ex: "Methods:"), mas parte do corpus nao tem essa
pontuacao (ex: a11/a17/a20.xml com "Objetivo"), gerando saida como
"Objetivo descrever..." em vez de "Objetivo: descrever...".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MwX5yYrVbsuZ9dB1VrMUCZ
@Rossi-Luciano

Copy link
Copy Markdown
Contributor Author

Obrigado pela revisão, @pitangainnovare. Os dois pontos procedem, corrigidos em 9c6cb95:

  1. Duplicação abstract/body: extract_body_data() agora usa .//sec[not(ancestor::abstract) and not(ancestor::trans-abstract)], excluindo as <sec> internas do resumo estruturado. Verificado no a10.xml: antes retornava 25 seções (15 do body + 10 vazadas do abstract/trans-abstract), agora retorna as 15 corretas.

  2. Título sem pontuação: _extract_abstract_paragraphs() agora acrescenta ':' quando o título da subseção não termina em pontuação própria (:.!?;), evitando "Objetivo descrever..." e produzindo "Objetivo: descrever...". Confirmado em a11.xml, a17.xml e a20.xml.

Testado contra os 26 XMLs do corpus, sem regressão. Testes de regressão adicionados para os dois casos.

Comment on lines +367 to +369
body_sections = xml_tree.xpath(
'.//sec[not(ancestor::abstract) and not(ancestor::trans-abstract)]'
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Boa solução.

@Rossi-Luciano
Rossi-Luciano merged commit 9fad113 into scieloorg:master Sep 9, 2026
2 checks passed
@Rossi-Luciano
Rossi-Luciano deleted the fix/pdf-structured-abstract-empty branch September 9, 2026 14:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants