Skip to content

fix: campo CITE AS seleciona nota de rodapé errada e quebra espaçamento sem volume - #1350

Open
Rossi-Luciano wants to merge 3 commits into
scieloorg:masterfrom
Rossi-Luciano:fix/pdf-cite-as-wrong-footnote-selection
Open

fix: campo CITE AS seleciona nota de rodapé errada e quebra espaçamento sem volume#1350
Rossi-Luciano wants to merge 3 commits into
scieloorg:masterfrom
Rossi-Luciano:fix/pdf-cite-as-wrong-footnote-selection

Conversation

@Rossi-Luciano

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

Copy link
Copy Markdown
Contributor

O que esse PR faz?

Corrige a issue #1349: o campo CITE AS do rodapé da página 1 tinha dois problemas.

extract_cite_as_part_one pegava a primeira <fn fn-type="other"> do documento, sem checar o <label>. fn-type="other" é uma categoria genérica do JATS usada para qualquer tipo de nota (institucional, declaração de uso de IA, códigos JEL, política de plágio, registro ZooBank), então o CITE AS - que deveria ser uma citação bibliográfica - imprimia conteúdo errado em 7 de 26 artigos do corpus de teste, incluindo um caso (a29.xml) em que a nota correta existia no mesmo fn-group mas não era a primeira.

docx_cite_as_pipe montava cite_as_part_two como f'{volume}: {location}' sem checar se volume existia, deixando um : solto quando o XML não tem <volume> (ex: "Cadernos Pagu : e236720.").

Atualização pós-revisão: a revisão apontou três pontos - duplicação da nota completa, heurística de detecção frágil, e ausência de referência completa quando não há nota editorial. Todos corrigidos - ver seções "Revisão, rodada 2" e "Revisão, rodada 3" abaixo.

Onde a revisão poderia começar?

packtools/sps/formats/pdf/pipeline/xml.py, função extract_cite_as_part_one, e packtools/sps/formats/pdf/pipeline/docx.py, docx_cite_as_pipe e o helper _format_cite_as_part_two.

Como este poderia ser testado manualmente?

  1. Gerar o PDF de a11.xml do corpus de teste, cuja primeira <fn fn-type="other"> é uma nota institucional.
  2. Antes: CITE AS: Trabalho realizado na Universidade Federal de Pernambuco - UFPE - Recife (PE), Brasil.Audiology - Communication Research 28: e2725.
  3. Depois: CITE AS: Audiology - Communication Research 28: e2725.
  4. Gerar o PDF de a18.xml (sem <volume>).
  5. Antes: CITE AS: Cadernos Pagu : e236720. Depois: CITE AS: Cadernos Pagu e236720.
  6. Gerar o PDF de a8.xml ou a29.xml (nota "como citar" já é uma citação completa).
  7. Antes: duplicava periódico/volume/localização depois da nota. Depois: a nota é usada sozinha, ex. CITE AS: Sousa VR, Gomes MM, Couri MS (2024) On Cerodontha...Zoologia 41: e23038. https://doi.org/10.1590/S1984-4689.v41.e23038.
  8. Gerar o PDF do fixture tests/fixtures/pdf/a3.xml (nota sem <label> próprio, frase "Como citar:" embutida no <p>).
  9. Antes: CITE AS: Como citar: Souza CM, Iser BM... Depois: CITE AS: Souza CM, Iser BM... (prefixo removido).
  10. Gerar o PDF de a7.xml (sem nenhuma nota "como citar").
  11. Antes: CITE AS: Acta Botanica Brasilica 40: e20250035. (sem autores/título/DOI). Depois: CITE AS: Oliveira BPS, Leite KRB, Amorim VO, Roque N. Leaf morphoanatomy supporting evolutionary relationships in a recent clade of Asteraceae. Acta Bot. Bras. 2026;40:e20250035. https://doi.org/10.1590/1677-941X-ABB-2025-0035.
  12. pytest tests/sps/formats/pdf/pipeline/test_xml.py tests/sps/formats/pdf/pipeline/test_docx.py cobre todos os casos com testes de regressão.

Algum cenário de contexto que queira dar?

Achado revisando o PR #1326: o @pitangainnovare notou o espaçamento quebrado sem volume no CITE AS e sugeriu abrir uma issue separada, já que "o problema não é só esse". Investigando a fundo, a causa maior é a seleção da nota errada (fn-type="other" sendo tratado como sinônimo de "nota de citação", o que não é garantido pelo JATS).

Validado contra as 26 amostras do corpus de teste: 6 casos que vazavam conteúdo errado (a10, a11, a12, a20, a24, a28) agora retornam vazio corretamente; a29 passou a capturar a nota real de citação em vez de uma URL do ZooBank; os 3 casos que já funcionavam (a8, a17, a30) continuam corretos e ganharam texto completo (antes truncado por usar .text em vez de itertext).

Revisão, rodada 2 (2026-09-10)

Dois pontos corrigidos:

  1. Duplicação da nota completa (a3, a8, a29): docx_cite_as_pipe imprimia journal_title/volume/localização depois de cite_as_part_one incondicionalmente. Quando a nota "como citar" já é uma citação completa (o caso normal), isso duplicava conteúdo ou colava sem separador. Agora, quando a nota existe, ela é usada sozinha; o fallback journal/volume/localização só entra quando não há nota (a7, a28 - ver ponto pendente abaixo).
  2. Heurística de detecção frágil: troquei 'cit' in signal.lower() por uma lista de frases-âncora (pt/en: "como citar", "cite as", "how to cite this article", "citação sugerida"...) via regex, e passei a remover o prefixo da própria frase quando ela vem embutida no <p> por falta de <label> próprio (caso do fixture a3.xml, sem essa remoção ficaria "CITE AS: Como citar: ...").

Suíte completa de tests/sps/formats/pdf/ agora com 230 testes, todos passando.

Revisão, rodada 3 (2026-09-10)

Ponto 3 da revisão implementado: referência completa quando não há nota editorial (a7, a28, e na prática a maioria do corpus - a maior parte dos artigos simplesmente não carrega uma nota "como citar este artigo").

Nova função build_full_citation monta a citação a partir da própria metadata do artigo (autores com iniciais estilo Vancouver, título, periódico abreviado, ano/volume/número/localização, DOI), truncando em 6 autores + "et al." (regra ICMJE). docx.py agora usa extract_cite_as_part_one(...) or build_full_citation(...) - a nota editorial continua tendo prioridade quando existe.

O formato é Vancouver por decisão do mantenedor, já antecipando um plano de configuração por periódico: o parâmetro style de build_full_citation (só 'vancouver' implementado, via dict de despacho) segue o mesmo padrão já usado em layout_config.load_page_attributes - não lê um arquivo de configuração ainda, mas deixa o ponto de extensão pronto para um JSON de configuração escolher o estilo por periódico no futuro, sem mudar a assinatura da função.

Validado contra as 26 amostras do corpus: a maioria não tinha nota e antes mostrava só "CITE AS: Periódico Vol: local." incompleto - agora sai uma citação completa, ex. a7: Oliveira BPS, Leite KRB, Amorim VO, Roque N. Leaf morphoanatomy... Acta Bot. Bras. 2026;40:e20250035. https://doi.org/10.1590/1677-941X-ABB-2025-0035. Os que já tinham nota real (a8, a17, a29, a30) continuam usando a nota, intocados.

Corrigido de quebra: extract_doi lança AttributeError de propósito quando não há DOI (contrato já testado em test_extract_doi_missing_doi); build_full_citation checa o nó diretamente em vez de arriscar propagar essa exceção para artigos sem DOI.

Suíte completa de tests/sps/formats/pdf/ agora com 239 testes, todos passando.

Screenshots

a11.xml (nota institucional sendo usada como citação):

a11_cite_as_comparison

a18.xml (sem volume, espaçamento quebrado):

a18_cite_as_comparison

Quais são os tickets relevantes?

Issue #1349

Referências

Comentário original do @pitangainnovare: #1326 (review)


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 pontual de extração/formatação de texto, sem alteração de infraestrutura, dependências ou pipeline.

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_01V829P3TiVHWnnde6tumABq

…to sem volume

extract_cite_as_part_one pegava a primeira <fn fn-type="other"> do
documento, sem checar o <label>. fn-type="other" e uma categoria
generica do JATS usada para qualquer tipo de nota (institucional,
declaracao de uso de IA, codigos JEL, politica de plagio, registro
ZooBank), entao o campo CITE AS do PDF - que deveria ser uma citacao
bibliografica - imprimia conteudo errado em 7 de 26 artigos do corpus
de teste, incluindo um caso (a29.xml) em que a nota correta existia no
mesmo fn-group mas nao era a primeira.

Passa a percorrer todas as <fn fn-type="other"> do documento e so usa
a que tiver label (ou, na ausencia dele, inicio do texto do <p>)
sinalizando citacao ("Como citar:", "CITE AS:", "How to cite this
article"); nada e retornado quando nenhuma nota corresponde. Tambem
troca part_one.text por extracao de texto completa (itertext), que
antes cortava a citacao no primeiro elemento filho (ex: um DOI dentro
de <ext-link> logo apos o texto).

Corrige tambem docx_cite_as_pipe: cite_as_part_two montava
"{volume}: {location}" sem checar se volume existia, deixando um ':'
solto quando o XML nao tem <volume> (ex: "Cadernos Pagu : e236720.").
Extrai _format_cite_as_part_two, que omite o segmento de volume e seu
separador nesse caso.

Issue: scieloorg#1349
@Rossi-Luciano Rossi-Luciano added the PDF generator Funcionalidade de geração de PDF label Sep 9, 2026

@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 resolve a seleção da primeira fn-type="other", mas o resultado ainda não produz uma referência bibliográfica cientificamente correta.

Quando existe uma nota editorial de “Como citar”, ela já contém a referência completa e deve ser usada integralmente, sem acrescentar novamente periódico, volume e localização. No estado atual, casos como a8 e a29 ficam duplicados ou concatenados sem espaço; em a3, o resultado começa com CITE AS: Como citar:.

Quando a nota não existe, como em a7 e a28, os PDFs originais mostram que o comportamento esperado não é deixar o campo vazio nem gerar apenas Periódico volume: localização. Deve ser construída uma referência completa a partir dos metadados — autores, ano, título, periódico, volume/número, páginas ou e-location e DOI — segundo um formato explicitamente definido.

Também considero frágil identificar a nota apenas por 'cit' in signal.lower(), pois isso admite falsos positivos e pode ignorar uma citação cujo label seja genérico, mas cujo p comece com “Como citar”.

Solicito ajustar o PR e a issue #1349 para definir e testar estes dois caminhos:

  1. nota editorial explícita: utilizar a citação completa uma única vez;
  2. ausência da nota: gerar uma referência bibliográfica completa e determinística a partir dos metadados.

Os testes devem validar o texto final do DOCX, incluindo pelo menos a3, a7, a8, a28 e a29, e não apenas extrator e renderer isoladamente.

Dois ajustes pedidos na revisao do PR scieloorg#1350:

- docx_cite_as_pipe imprimia journal_title/volume/localizacao depois de
  cite_as_part_one incondicionalmente. Quando a nota "como citar" ja e
  uma citacao completa (o caso normal), isso duplicava conteudo (a8,
  a29) ou colava sem separador (a3). Agora, quando a nota existe, ela e
  usada sozinha; o fallback journal/volume/localizacao so entra quando
  nao ha nota (a7, a28 - ainda incompletos, tratamento fica para uma
  proposta de formato separada).
- extract_cite_as_part_one trocou o teste 'cit' in signal.lower() por
  uma lista de frases-ancora (como citar, cite as, how to cite...), e
  passa a remover o prefixo da propria frase quando ela vem embutida no
  <p> por falta de <label> proprio (caso a3: "Como citar: Autor...") -
  senao duplicava com o "CITE AS: " que o chamador ja imprime.

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

Copy link
Copy Markdown
Contributor Author

Oi @pitangainnovare, avancei em duas partes dos três pontos levantados.

Corrigido: duplicação da nota completa (a3, a8, a29)

docx_cite_as_pipe imprimia journal_title/volume/localização depois de cite_as_part_one incondicionalmente. Quando a nota "como citar" já é uma citação completa (o caso normal), isso duplicava conteúdo ou colava sem separador. Agora, quando a nota existe, ela é usada sozinha. Testei os três casos apontados:

  • a3 (tests/fixtures/pdf/a3.xml): CITE AS: Souza CM, Iser BM, Malta DC. Diabetes gestacional autorreferido... (sem "Como citar:" duplicado)
  • a8: CITE AS: Colorado Z., G. J.; Valencia-C, G. 2024. Avifauna...Acta Amazonica 55: e55bc24197. (sem repetir periódico/volume)
  • a29: CITE AS: Sousa VR, Gomes MM, Couri MS (2024) On Cerodontha...Zoologia 41: e23038. https://doi.org/10.1590/S1984-4689.v41.e23038. (sem duplicar, período adicionado só quando a nota não termina com pontuação própria)

Corrigido: heurística de detecção frágil

Troquei 'cit' in signal.lower() por uma lista de frases-âncora (pt/en: "como citar", "cite as", "how to cite this article", "citação sugerida"...) via regex. Aproveitei para também remover o prefixo da própria frase quando ela vem embutida no <p> por falta de <label> próprio (é o caso do a3 - sem essa remoção, ficaria "CITE AS: Como citar: Souza CM...").

Pendente de confirmação: referência completa quando não há nota (a7, a28)

Toda a metadata necessária já está disponível e extraível (autores, ano, título, periódico abreviado, volume/número, páginas/e-location, DOI) - não é isso que falta. O que falta é uma decisão de formato: os exemplos reais mostram estilos diferentes por periódico (a8 usa "Sobrenome I., N. 2024. Título. Periódico Vol: loc.", a29 usa "Sobrenome IN (2024) Título. Periódico Vol: loc. DOI"), então construir uma referência "correta" sem seguir uma nota editorial existente significa escolher um formato que o packtools nunca definiu antes.

Proposta (estilo Vancouver, comum em citação de artigo):

Sobrenome IN, Sobrenome IN, Sobrenome IN. Título do artigo. Periódico abrev. Ano;Vol(Número):páginas. DOI: 10.x/y

Exemplo com os dados reais do a7 (hoje sai "CITE AS: Acta Botanica Brasilica 40: e20250035.", incompleto):

CITE AS: Oliveira BPS, Leite KRB, Amorim VO. Leaf morphoanatomy supporting evolutionary relationships in a recent clade of Asteraceae. Acta Bot. Bras. 2026;40:e20250035. DOI: 10.1590/1677-941X-ABB-2025-0035

Antes de implementar (formatação de nomes com iniciais, truncamento com "et al." se muitos autores, etc.), gostaria de confirmar se esse formato serve ou se há um padrão SciELO já definido que eu não encontrei. Posso abrir isso como issue separada de #1349/#1350 se preferir tratar como funcionalidade nova em vez de bugfix.

Suíte completa de tests/sps/formats/pdf/ (230 testes) passa com as duas correções acima.

…itorial

Ponto 3 da revisao do PR scieloorg#1350: a maioria dos artigos do corpus nao tem
uma nota "como citar este artigo" (fn-type=other so cobre o caso
minoritario onde ela existe). Sem uma, o CITE AS ficava incompleto
("Periodico Volume: localizacao", sem autores/titulo/DOI).

build_full_citation monta a citacao a partir da propria metadata do
artigo (autores com iniciais estilo Vancouver, titulo, periodico
abreviado, ano/volume/numero/localizacao, DOI), truncando em 6 autores
+ "et al." (regra ICMJE). docx.py usa extract_cite_as_part_one(...) or
build_full_citation(...) - a nota editorial continua tendo prioridade
quando existe.

O parametro `style` (so 'vancouver' implementado) segue o mesmo padrao
de layout_config.load_page_attributes: nao le config ainda, mas deixa
o ponto de extensao pronto para um JSON de configuracao por periodico
escolher o formato no futuro sem mudar a assinatura da funcao.

Corrigido de quebra: extract_doi lanca AttributeError de proposito
quando nao ha DOI (contrato ja testado); build_full_citation checa o
no diretamente em vez de arriscar propagar essa excecao para artigos
sem DOI.

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

Rossi-Luciano commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

Implementei o ponto 3 (referência completa quando não há nota editorial) formato Vancouver como padrão, com o parâmetro style de build_full_citation já preparado (dict de despacho, mesmo padrão de layout_config.load_page_attributes) para um JSON de configuração por periódico escolher outro formato no futuro, sem mudar a assinatura da função.

Toda a metadata é extraída da própria XML (autores com iniciais estilo Vancouver - excluindo partículas minúsculas como "da"/"de"/"dos" - título, periódico abreviado, ano/volume/número/localização, DOI), truncando em 6 autores + "et al." (regra ICMJE).

Exemplo real, a7.xml (antes incompleto):

Antes: CITE AS: Acta Botanica Brasilica 40: e20250035.

Depois: CITE AS: Oliveira BPS, Leite KRB, Amorim VO, Roque N. Leaf morphoanatomy supporting evolutionary relationships in a recent clade of Asteraceae. Acta Bot. Bras. 2026;40:e20250035. https://doi.org/10.1590/1677-941X-ABB-2025-0035.

Validado contra as 26 amostras do corpus: a maioria não tinha nota editorial e ganhou uma citação completa; os que já tinham nota real (a8, a17, a29, a30) continuam intocados, já que a nota tem prioridade sobre o formato construído.

De quebra corrigi um bug real que apareceu no caminho: extract_doi lança AttributeError de propósito quando não há DOI (contrato já coberto por teste existente), então build_full_citation checa o nó diretamente em vez de arriscar propagar essa exceção para um artigo sem DOI.

Suíte completa de tests/sps/formats/pdf/ agora com 239 testes, todos passando.

Comment on lines +389 to +530
# Only 'vancouver' exists today. Kept as a plain function parameter/dict
# dispatch (instead of hardcoding the format inline) so a future per-journal
# JSON config can plug in a different style by name without changing
# build_full_citation's contract - the same not-wired-yet-to-config pattern
# as layout_config.load_page_attributes.
CITATION_STYLE_VANCOUVER = 'vancouver'
_MAX_CITATION_AUTHORS_BEFORE_ET_AL = 6


def _extract_citation_authors(xml_tree):
"""
Returns the article's own contributors as Vancouver-style "Surname IN"
strings (surname, then the initials of given names - lowercase
particles like "da"/"de"/"dos" excluded from initials, e.g. "Bárbara
Passos da Silva" -> "BPS").

Args:
xml_tree (ElementTree): The XML tree to extract authors from.

Returns:
list: Author strings in document order; empty if there's no
<contrib-group> or no <contrib> has both <surname> and text.
"""
article_meta = xml_tree.find('./front/article-meta')
metadata_scope = article_meta if article_meta is not None else xml_tree
contrib_group = metadata_scope.find('.//contrib-group')
if contrib_group is None:
return []

authors = []
for contrib in contrib_group.findall('.//contrib'):
name = contrib.find('name')
if name is None:
continue
surname = name.find('surname')
if surname is None or not (surname.text or '').strip():
continue

given_names = name.find('given-names')
initials = ''
if given_names is not None and given_names.text:
initials = ''.join(
word[0].upper() for word in given_names.text.split() if word[:1].isupper()
)

author = surname.text.strip()
if initials:
author = f'{author} {initials}'
authors.append(author)

return authors


def _sentence(text):
"""Appends a period unless text already ends with terminal punctuation."""
return text if text.endswith(('.', '!', '?')) else f'{text}.'


def _format_vancouver_citation(xml_tree, footer_data):
"""
Builds a Vancouver-style citation: "Surname IN, Surname IN. Article
title. Journal abbrev. Year;Volume(Issue):location. https://doi.org/...".
Missing pieces (issue, DOI...) are simply omitted rather than leaving a
stray separator; returns '' when there are no authors to start from.
"""
authors = _extract_citation_authors(xml_tree)
if not authors:
return ''
if len(authors) > _MAX_CITATION_AUTHORS_BEFORE_ET_AL:
authors = authors[:_MAX_CITATION_AUTHORS_BEFORE_ET_AL] + ['et al']
segments = [f"{', '.join(authors)}."]

title = extract_article_title(xml_tree)
if title:
segments.append(_sentence(title))

abbrev_journal = xml_tree.find('.//abbrev-journal-title')
journal = (
''.join(abbrev_journal.itertext()).strip()
if abbrev_journal is not None
else extract_journal_title(xml_tree)
)
if journal:
segments.append(_sentence(journal))

year = footer_data.get('year') or ''
volume = footer_data.get('volume') or ''
issue = footer_data.get('issue') or ''
location = footer_data.get('location_label') or ''

vol_issue = f'{volume}({issue})' if volume and issue else volume
date_and_location = year
if vol_issue:
date_and_location = f'{date_and_location};{vol_issue}' if date_and_location else vol_issue
if location:
date_and_location = f'{date_and_location}:{location}' if date_and_location else location
if date_and_location:
segments.append(_sentence(date_and_location))

# Not extract_doi(): that function raises AttributeError on a missing
# DOI by design (see test_extract_doi_missing_doi) - a DOI is just
# another optional piece of a citation, not something to blow up over.
doi_node = xml_tree.find('.//article-id[@pub-id-type="doi"]')
doi = doi_node.text if doi_node is not None else None
if doi:
segments.append(f'https://doi.org/{doi}')

return ' '.join(segments)


_CITATION_STYLE_FORMATTERS = {
CITATION_STYLE_VANCOUVER: _format_vancouver_citation,
}


def build_full_citation(xml_tree, footer_data, style=CITATION_STYLE_VANCOUVER):
"""
Builds a complete "how to cite this article" citation from the
article's own metadata (authors, title, journal, volume/issue/location,
DOI), for use as a fallback when `extract_cite_as_part_one` finds no
explicit editorial note (see issue #1349's review: some articles simply
don't carry one).

`style` selects the citation format. Only CITATION_STYLE_VANCOUVER
exists today; it's a plain parameter (not read from a config file) so a
future per-journal JSON config can choose it by name later without
changing this function's contract - not wired to the CLI/API yet.

Args:
xml_tree (ElementTree): The XML tree to build the citation from.
footer_data (dict): Output of `extract_footer_data` (year/volume/issue/location_label).
style (str, optional): Citation format identifier. Defaults to CITATION_STYLE_VANCOUVER.

Returns:
str: The complete citation, or '' when the style is unknown or the
article has no authors to build one from.
"""
formatter = _CITATION_STYLE_FORMATTERS.get(style)
if formatter is None:
return ''
return formatter(xml_tree, footer_data)

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.

Em lugar de implementar uma lógica para montar a citação, podemos usar uma biblioteca chamada citeproc-py e citeproc-py-styles. Essas libs tem uma estrutura que permite receber os metadados e devolver a citação completa, no formato que bem desejar. Então bastaria nós usarmos os métodos para extrairmos autor, revista, volume, ano, etc, e passar nessa libs. Elas seriam as responsáveis por montar a full citation. Veja um exemplo:

pip install citeproc-py citeproc-py-styles bibtexparser
In [1]: from citeproc import (
   ...:     Citation,
   ...:     CitationItem,
   ...:     CitationStylesBibliography,
   ...:     CitationStylesStyle,
   ...:     formatter,
   ...: )
   ...: from citeproc.source.json import CiteProcJSON
   ...: from citeproc_styles import get_style_filepath
   ...: 
   ...: references = [{
   ...:     "id": "ref1",
   ...:     "type": "article-journal",
   ...:     "title": "An example paper",
   ...:     "author": [
   ...:         {"family": "Damaceno", "given": "Rafael"}
   ...:     ],
   ...:     "issued": {"date-parts": [[2026]]},
   ...:     "container-title": "Journal of Examples",
   ...:     "volume": "10",
   ...:     "page": "1-10"
   ...: }]
   ...: 
   ...: source = CiteProcJSON(references)
   ...: 
   ...: style = CitationStylesStyle(
   ...:     get_style_filepath("apa")
   ...: )
   ...: 
   ...: bibliography = CitationStylesBibliography(
   ...:     style,
   ...:     source,
   ...:     formatter.plain
   ...: )
   ...: 
   ...: citation = Citation([CitationItem("ref1")])
   ...: bibliography.register(citation)
   ...: 
   ...: print(bibliography.cite(citation, lambda x: None))
   ...: 
   ...: for ref in bibliography.bibliography():
   ...:     print(str(ref))
   ...: 
(Damaceno, 2026)
Damaceno, R. (2026). An example paper. Journal of Examples, 10, 110.

@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.

Acredito que o ideal é usar uma lib externa e já deixar pronta a estrutura para gerar citações em quaisquer estilos. Veja nos comentários como é mais tranquilo. Bastaria montar o objeto Reference com os metadados extraídos do XML.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

PDF generator Funcionalidade de geração de PDF

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants