Skip to content

Conversation

@robertatakenaka
Copy link
Member

Pull Request: Implementa processamento de author-notes para elementos corresp

📋 Descrição

Implementa novo pipe para processar elementos <corresp> localizados em <back> ou <body>, movendo-os para dentro de <author-notes> na seção <article-meta>, conforme especificação SPS para estruturação de metadados de correspondência de autores.

🎯 Motivação

Elementos de correspondência (<corresp>) frequentemente aparecem em seções inadequadas do XML (back/body), mas segundo o padrão SPS devem estar organizados dentro de <author-notes> em <article-meta> para melhor estruturação semântica e processamento padronizado.

✨ Alterações Implementadas

Nova funcionalidade

  • XMLArticleMetaAuthorNotesPipe: Novo pipe responsável por:
    • Identificar elementos <corresp> em <back> ou <body>
    • Criar elemento <author-notes>
    • Mover elementos <corresp> para dentro de <author-notes>
    • Posicionar <author-notes> como irmão imediato após <contrib-group>

Integração ao pipeline

  • Pipe adicionado ao pipeline de processamento após XMLArticleMetaContribGroupPipe
  • Mantém ordem lógica de processamento dos metadados

🔄 Exemplo de Transformação

Antes:

<article>
  <front>
    <article-meta>
      <contrib-group>...</contrib-group>
    </article-meta>
  </front>
  <back>
    <corresp id="cor1">
      <email>autor@example.com</email>
    </corresp>
  </back>
</article>

Depois:

<article>
  <front>
    <article-meta>
      <contrib-group>...</contrib-group>
      <author-notes>
        <corresp id="cor1">
          <email>autor@example.com</email>
        </corresp>
      </author-notes>
    </article-meta>
  </front>
  <back>
    <!-- corresp removido daqui -->
  </back>
</article>

✅ Checklist

  • Código implementado seguindo padrões do projeto
  • Pipe com precondição para executar apenas quando há <corresp>
  • Tratamento de casos onde não existe <contrib-group>
  • Preservação de atributos e conteúdo dos elementos movidos
  • Testes unitários adicionados
  • Testes de integração validados
  • Documentação atualizada

🧪 Como Testar

  1. Execute o pipeline com XMLs contendo <corresp> em <back> ou <body>
  2. Verifique se os elementos foram movidos para <author-notes> em <article-meta>
  3. Confirme que a ordem dos elementos em <article-meta> está correta
  4. Valide XMLs processados contra o schema SPS

📝 Notas Adicionais

  • O pipe só executa quando encontra elementos <corresp> (precondição implementada)
  • Compatível com XMLs que já possuem estrutura correta (não altera)
  • Segue a especificação SPS para posicionamento de metadados de correspondência

🔗 Issues Relacionadas

Adicionar referências a issues se aplicável

- Cria nova classe XMLArticleMetaAuthorNotesPipe
- Move elementos corresp de back/body para author-notes
- Posiciona author-notes como irmão de contrib-group
- Implementa precondição para verificar existência de corresp
- Importa XMLArticleMetaAuthorNotesPipe
- Adiciona pipe após XMLArticleMetaContribGroupPipe no processo
- Mantém ordem lógica de processamento dos metadados do artigo
Copilot AI review requested due to automatic review settings December 3, 2025 02:01
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements a new pipeline component (XMLArticleMetaAuthorNotesPipe) to restructure XML documents according to SPS (SciELO Publishing Schema) specifications. The pipe automatically moves <corresp> elements that appear in incorrect locations (<back> or <body>) to their proper semantic location within an <author-notes> element inside <article-meta>.

Key Changes:

  • New XMLArticleMetaAuthorNotesPipe class to process and relocate correspondence elements
  • Integration into the main processing pipeline after XMLArticleMetaContribGroupPipe
  • Precondition logic to execute only when <corresp> elements are present

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 8 comments.

File Description
scielo_classic_website/spsxml/sps_xml_pipes.py Adds import and instantiation of XMLArticleMetaAuthorNotesPipe in the processing pipeline
scielo_classic_website/spsxml/sps_xml_article_meta.py Implements the new XMLArticleMetaAuthorNotesPipe class with logic to identify, move, and reposition <corresp> elements

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

"""
Pipe para criar author-notes como irmão de contrib-group.
Move elementos corresp de back para dentro de author-notes.
Executa após XMLArticleMetacontribGroupPipe.
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Typo in docstring: "XMLArticleMetacontribGroupPipe" should be "XMLArticleMetaContribGroupPipe" (capital C in "Contrib").

Suggested change
Executa após XMLArticleMetacontribGroupPipe.
Executa após XMLArticleMetaContribGroupPipe.

Copilot uses AI. Check for mistakes.
Comment on lines +314 to +323
contrib_group = article_meta.find("contrib-group")

if contrib_group is not None:
# Insere author-notes após contrib-group
contrib_group_index = list(article_meta).index(contrib_group)
article_meta.insert(contrib_group_index + 1, author_notes)
else:
# Se não há contrib-group, adiciona ao final de article-meta
article_meta.append(author_notes)

Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Potential None reference error. If article_meta is None (when ./front/article-meta doesn't exist), calling article_meta.find("contrib-group") on line 314 will raise an AttributeError. Consider adding a null check: if article_meta is not None: before line 314, or handle the case when article-meta doesn't exist.

Suggested change
contrib_group = article_meta.find("contrib-group")
if contrib_group is not None:
# Insere author-notes após contrib-group
contrib_group_index = list(article_meta).index(contrib_group)
article_meta.insert(contrib_group_index + 1, author_notes)
else:
# Se não há contrib-group, adiciona ao final de article-meta
article_meta.append(author_notes)
if article_meta is not None:
contrib_group = article_meta.find("contrib-group")
if contrib_group is not None:
# Insere author-notes após contrib-group
contrib_group_index = list(article_meta).index(contrib_group)
article_meta.insert(contrib_group_index + 1, author_notes)
else:
# Se não há contrib-group, adiciona ao final de article-meta
article_meta.append(author_notes)
else:
logging.warning("article-meta element not found; cannot insert author-notes.")

Copilot uses AI. Check for mistakes.
Comment on lines +298 to +323
if corresp_elements:
# Cria author-notes
author_notes = ET.Element("author-notes")

# Move cada corresp para dentro de author-notes
for corresp in corresp_elements:
# Remove corresp de sua localização atual
parent = corresp.getparent()
if parent is not None:
parent.remove(corresp)

# Adiciona corresp ao author-notes
author_notes.append(corresp)

# Insere author-notes como irmão de contrib-group
article_meta = xml.find("./front/article-meta")
contrib_group = article_meta.find("contrib-group")

if contrib_group is not None:
# Insere author-notes após contrib-group
contrib_group_index = list(article_meta).index(contrib_group)
article_meta.insert(contrib_group_index + 1, author_notes)
else:
# Se não há contrib-group, adiciona ao final de article-meta
article_meta.append(author_notes)

Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Redundant condition check. The if corresp_elements: check on line 298 is redundant since the precondition on lines 283-289 already ensures that corresp_elements is not empty. This check can be removed, and the code inside can be dedented.

Suggested change
if corresp_elements:
# Cria author-notes
author_notes = ET.Element("author-notes")
# Move cada corresp para dentro de author-notes
for corresp in corresp_elements:
# Remove corresp de sua localização atual
parent = corresp.getparent()
if parent is not None:
parent.remove(corresp)
# Adiciona corresp ao author-notes
author_notes.append(corresp)
# Insere author-notes como irmão de contrib-group
article_meta = xml.find("./front/article-meta")
contrib_group = article_meta.find("contrib-group")
if contrib_group is not None:
# Insere author-notes após contrib-group
contrib_group_index = list(article_meta).index(contrib_group)
article_meta.insert(contrib_group_index + 1, author_notes)
else:
# Se não há contrib-group, adiciona ao final de article-meta
article_meta.append(author_notes)
# Cria author-notes
author_notes = ET.Element("author-notes")
# Move cada corresp para dentro de author-notes
for corresp in corresp_elements:
# Remove corresp de sua localização atual
parent = corresp.getparent()
if parent is not None:
parent.remove(corresp)
# Adiciona corresp ao author-notes
author_notes.append(corresp)
# Insere author-notes como irmão de contrib-group
article_meta = xml.find("./front/article-meta")
contrib_group = article_meta.find("contrib-group")
if contrib_group is not None:
# Insere author-notes após contrib-group
contrib_group_index = list(article_meta).index(contrib_group)
article_meta.insert(contrib_group_index + 1, author_notes)
else:
# Se não há contrib-group, adiciona ao final de article-meta
article_meta.append(author_notes)

Copilot uses AI. Check for mistakes.
Comment on lines +276 to +324
class XMLArticleMetaAuthorNotesPipe(plumber.Pipe):
"""
Pipe para criar author-notes como irmão de contrib-group.
Move elementos corresp de back para dentro de author-notes.
Executa após XMLArticleMetacontribGroupPipe.
"""

def precond(data):
raw, xml = data

# Verifica se existe corresp em back
corresp_elements = xml.xpath(".//back//corresp|.//body//corresp")
if not corresp_elements:
raise plumber.UnmetPrecondition()

@plumber.precondition(precond)
def transform(self, data):
raw, xml = data

# Encontra elementos corresp em back
corresp_elements = xml.xpath(".//back//corresp|.//body//corresp")

if corresp_elements:
# Cria author-notes
author_notes = ET.Element("author-notes")

# Move cada corresp para dentro de author-notes
for corresp in corresp_elements:
# Remove corresp de sua localização atual
parent = corresp.getparent()
if parent is not None:
parent.remove(corresp)

# Adiciona corresp ao author-notes
author_notes.append(corresp)

# Insere author-notes como irmão de contrib-group
article_meta = xml.find("./front/article-meta")
contrib_group = article_meta.find("contrib-group")

if contrib_group is not None:
# Insere author-notes após contrib-group
contrib_group_index = list(article_meta).index(contrib_group)
article_meta.insert(contrib_group_index + 1, author_notes)
else:
# Se não há contrib-group, adiciona ao final de article-meta
article_meta.append(author_notes)

return data
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Missing test coverage for XMLArticleMetaAuthorNotesPipe. The repository has comprehensive test coverage for similar pipes (e.g., XMLArticleMetaAffiliationPipe, XMLArticleMetaPublicationDatesPipe) in tests/test_sps_xml_article_meta.py. Consider adding test cases that cover:

  1. Moving <corresp> elements from <back> to <author-notes>
  2. Moving <corresp> elements from <body> to <author-notes>
  3. Correct positioning after <contrib-group>
  4. Fallback behavior when no <contrib-group> exists
  5. Precondition behavior (pipe should not execute when no <corresp> elements exist)

Copilot uses AI. Check for mistakes.
def precond(data):
raw, xml = data

# Verifica se existe corresp em back
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Inaccurate comment. The comment says "Verifica se existe corresp em back" (Checks if corresp exists in back), but the XPath also checks for corresp in body (".//back//corresp|.//body//corresp"). Update the comment to: "Verifica se existe corresp em back ou body".

Suggested change
# Verifica se existe corresp em back
# Verifica se existe corresp em back ou body

Copilot uses AI. Check for mistakes.
def transform(self, data):
raw, xml = data

# Encontra elementos corresp em back
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Inaccurate comment. The comment says "Encontra elementos corresp em back" (Finds corresp elements in back), but the XPath also finds corresp in body (".//back//corresp|.//body//corresp"). Update the comment to: "Encontra elementos corresp em back ou body".

Suggested change
# Encontra elementos corresp em back
# Encontra elementos corresp em back ou body

Copilot uses AI. Check for mistakes.
Comment on lines +299 to +300
# Cria author-notes
author_notes = ET.Element("author-notes")
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Missing handling for existing <author-notes> element. The code always creates a new <author-notes> element (line 300), but if an <author-notes> element already exists in <article-meta>, this would create a duplicate. Consider checking if <author-notes> already exists and either:

  1. Append <corresp> elements to the existing <author-notes>, or
  2. Document that this pipe assumes no pre-existing <author-notes> element.

Example check:

author_notes = article_meta.find("author-notes")
if author_notes is None:
    author_notes = ET.Element("author-notes")
    # ... positioning logic

Copilot uses AI. Check for mistakes.
Executa após XMLArticleMetacontribGroupPipe.
"""

def precond(data):
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Normal methods should have 'self', rather than 'data', as their first parameter.

Suggested change
def precond(data):
def precond(self, data):

Copilot uses AI. Check for mistakes.
@robertatakenaka robertatakenaka merged commit ee79eaf into scieloorg:main Dec 3, 2025
7 checks passed
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.

1 participant