-
Notifications
You must be signed in to change notification settings - Fork 3
Corrige e melhora conversao html para xml #137
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Corrige e melhora conversao html para xml #137
Conversation
- Remove xpath complexo .//back//corresp|.//body//corresp - Usa xpath simples .//corresp para buscar em todo documento - Evita duplicação de lógica de busca em precondition() e transform() - Mantém funcionalidade de mover corresp para author-notes
- Usa xpath com indexação [0] ao invés de find() para ref-list - Simplifica lógica de merge entre mixed-citation e element-citation - Remove criação desnecessária de novo elemento ref-list - Melhora tratamento de exceções salvando traceback completo - Adiciona exception info em raw.exceptions ao invés de comentários XML - Mantém integridade das referências existentes no ref-list
…ferências - Refatora XMLSupToXrefPipe para criar xref dentro de sup mantendo hierarquia - Adiciona complete_xref_reftype() para xrefs com ref-type=number - Melhora detecção de referências bibliográficas e footnotes numéricas - Preserva estrutura original do documento ao converter sup em xref - Adiciona verificação de body não-nulo antes de adicionar ao XML - Otimiza reutilização de lógica entre sup e xref numéricas
… com nova arquitetura modular - Reorganiza as etapas / grupos de pipelines de conversão progressiva HTML para XML - Adiciona detecção inteligente de nome de elementos e/ou respectivos atributos (sec, fn, ref-list, ack) - Refatora a organização dos elementos em hierarquia (sec, p, break) - Detecta ausência de <p> e cria <p> - Refatora a identificação de title e identifica seu elemento pai e/ou tipo do elemento (*-type) - Agrupa fn em fn-group - Remove texto anterior a sec-type=intro e, na sua ausência, remove texto anterior a kwd-group e/ou abstract - Corrige processamento de mixed-citation / element-citation
There was a problem hiding this 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 significant refactoring of the HTML to XML conversion pipeline for processing scientific articles in the SciELO Classic Website. The changes aim to optimize XPath queries, improve error handling, and restructure the conversion architecture for better modularity.
Key Changes:
- Simplified XPath queries for corresp elements and ref-list processing
- Refactored XMLSupToXrefPipe to preserve document hierarchy and add xref completion logic
- Extensive restructuring of body_pipes with new modular pipeline stages for detecting and organizing document elements (sections, footnotes, references)
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 30 comments.
| File | Description |
|---|---|
| sps_xml_refs.py | Changed from find() to xpath()[0] for ref-list access; improved exception tracking with traceback |
| sps_xml_pipes.py | Refactored sup to xref conversion with new complete_xref_reftype method; added null check for body element |
| sps_xml_body_pipes.py | Major restructuring with new pipes for element detection, wrapping, and organization; changed imports from scielo_migration to scielo_classic_website |
| sps_xml_article_meta.py | Simplified XPath from './/back//corresp|.//body//corresp' to './/corresp' |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| while True: | ||
| sibling = item.getnext() | ||
| if sibling is None: | ||
| return | ||
| if sibling.tag in ("graphic", "table"): | ||
| return sibling | ||
| graphic = sibling.find(".//graphic|.//table") | ||
| if graphic is not None: | ||
| return graphic | ||
| item = sibling |
Copilot
AI
Dec 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method find_graphic_in_siblings uses 'while True:' loop but only has 'return' statements to exit, no break statement. If neither sibling.tag matches (line 1416-1417) nor graphic is found in subtree (line 1418-1420), the loop continues with the current sibling as the new item. However, if all siblings are exhausted and none contain a graphic, the loop will never terminate naturally - it relies on the return statement at line 1415 when sibling is None. Consider adding a comment to clarify this termination condition or restructure for clarity.
| def find_graphic_in_upper_siblings(self, item): | ||
| if item is None: | ||
| return None | ||
| node = item | ||
| while True: | ||
| node = self.go_up(node) | ||
| if node is None: | ||
| return None | ||
| graphic = self.find_graphic_in_siblings(node) | ||
| if graphic is not None: | ||
| return graphic |
Copilot
AI
Dec 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to the fig version, this method uses 'while True:' loop for finding graphic in upper siblings. The loop relies on return statements to exit and could be clearer. Consider restructuring or adding clarifying comments about the termination conditions.
| while True: | ||
| for item in list(xml.xpath(".//*[not(*)]")): | ||
| self.mark_tag_to_strip(item) | ||
| if not xml.xpath(".//TOSTRIP"): | ||
| break | ||
| ET.strip_tags(xml, "TOSTRIP") |
Copilot
AI
Dec 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The remove_empty_tags method uses nested loops with xpath searches, where the outer loop continues until no TOSTRIP tags are found. However, the inner loop on line 1468 creates a list of all elements without children, which could be very large for complex documents. This O(n²) behavior could cause performance issues. Consider optimizing by processing elements in a single pass or using a more efficient traversal strategy.
| while True: | |
| for item in list(xml.xpath(".//*[not(*)]")): | |
| self.mark_tag_to_strip(item) | |
| if not xml.xpath(".//TOSTRIP"): | |
| break | |
| ET.strip_tags(xml, "TOSTRIP") | |
| # Processa os elementos em ordem inversa (de baixo para cima) para | |
| # garantir que filhos vazios sejam removidos antes de seus pais. | |
| elements = list(xml.iter()) | |
| for elem in reversed(elements): | |
| # Não tenta remover o elemento raiz. | |
| if elem is xml: | |
| continue | |
| # Aplica os mesmos critérios de seleção de tag que mark_tag_to_strip. | |
| if elem.tag not in ("div", "p", "span", "bold", "italic", "sup", "sub"): | |
| continue | |
| # Verifica se existe algum filho no elemento. | |
| if elem.getchildren(): | |
| continue | |
| # Verifica se o texto (incluindo descendentes remanescentes) tem conteúdo. | |
| if "".join(elem.itertext()).strip(): | |
| continue | |
| parent = elem.getparent() | |
| if parent is not None: | |
| parent.remove(elem) |
| # Cria um novo elemento <xref> e insere em <sup> | ||
| xref = ET.Element("xref") | ||
| xref.set("ref-type", ref_type) | ||
| xref.set("rid", rid) | ||
| if sup.text: | ||
| xref.text = sup.text | ||
| sup.text = None | ||
| for child in list(sup): | ||
| xref.append(child) | ||
| sup.text = None | ||
| sup.clear() | ||
| sup.append(xref) | ||
| total += 1 |
Copilot
AI
Dec 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic for wrapping sup content in xref has changed significantly. The new implementation clears the sup element and moves children to xref, then appends xref to sup. However, lines 770-771 set sup.text to None twice (once on line 767 and again on line 770). This is redundant. Additionally, the new approach of modifying sup in place and keeping it as a wrapper differs from the old approach where sup was replaced by xref. This could affect styling or processing expectations downstream. Verify this behavior change is intentional and won't break existing functionality.
| def join_bold_and_bold(self, xml): | ||
| for node in xml.xpath(".//bold[following-sibling::node()[1][self::bold]]"): | ||
| bold = node.getnext() | ||
| if bold.text: | ||
| node.tail = (node.tail or "") + bold.text | ||
| bold.text = "" | ||
| for child in bold.getchildren(): | ||
| node.append(child) | ||
| bold.tag = "TAGTOSTRIP" | ||
| ET.strip_tags(xml, "TAGTOSTRIP") |
Copilot
AI
Dec 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method join_bold_and_bold uses node.tail which could be None. On line 1891, it concatenates (node.tail or "") with bold.text, which is safe. However, the logic removes the following bold's text (line 1892) and moves children, but doesn't handle the following bold's tail. This tail content will be lost when the bold tag is stripped. Consider preserving the tail by appending it to the last child or to the parent node's text.
| return | ||
| graphic = self.find_graphic(item) | ||
| if graphic is None: | ||
| logging.error(f"InsertGraphicInFigPipe - no graphic found for fig {ET.tostring(item)}") |
Copilot
AI
Dec 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error logging at line 1324 will be triggered every time a fig element doesn't have a graphic sibling, which might be a valid scenario (e.g., fig with only a caption). This could lead to excessive error logs for normal cases. Consider using logging.warning or logging.debug instead of logging.error, or add logic to distinguish between expected and unexpected missing graphics.
| logging.error(f"InsertGraphicInFigPipe - no graphic found for fig {ET.tostring(item)}") | |
| logging.warning(f"InsertGraphicInFigPipe - no graphic found for fig {ET.tostring(item)}") |
| if graphic is None: | ||
| return | ||
| tablewrap.append(graphic) | ||
| logging.info(f"InsertGraphicInTablewrapPipe - feito {ET.tostring(tablewrap)}") |
Copilot
AI
Dec 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logging statement at line 1399 uses f-string to include ET.tostring(tablewrap) which could produce very large log entries if the tablewrap element contains substantial content. This could lead to excessive log file sizes. Consider truncating the output or logging just the id attribute or a summary instead of the full element.
| logging.info(f"InsertGraphicInTablewrapPipe - feito {ET.tostring(tablewrap)}") | |
| tablewrap_str = ET.tostring(tablewrap, encoding="unicode") | |
| max_len = 500 | |
| if len(tablewrap_str) > max_len: | |
| tablewrap_str = tablewrap_str[:max_len] + " ... [truncated]" | |
| logging.info("InsertGraphicInTablewrapPipe - feito %s", tablewrap_str) |
|
|
||
| def exclude_p_title_children(self, node): | ||
| for p_title in node.xpath(".//p[title]|.//p[bold]"): | ||
| parent = p_title.getparent() |
Copilot
AI
Dec 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Variable parent is not used.
| parent = p_title.getparent() |
| if not deleted: | ||
| deleted = self.strip_front_text_by_other_element_name_sublevel(body, "kwd-group") | ||
| if not deleted: | ||
| deleted = self.strip_front_text_by_other_element_name_sublevel(body, "abstract") |
Copilot
AI
Dec 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This assignment to 'deleted' is unnecessary as it is redefined before this value is used.
| deleted = self.strip_front_text_by_other_element_name_sublevel(body, "abstract") | |
| self.strip_front_text_by_other_element_name_sublevel(body, "abstract") |
| if parent is not None: | ||
| parent.remove(item) | ||
| deleted.append(bool(todelete)) | ||
| return any(deleted) |
Copilot
AI
Dec 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This statement is unreachable.
Pull Request: Otimização do Pipeline de Processamento XML
📋 Resumo
Esta PR implementa melhorias significativas no pipeline de conversão HTML para XML, otimizando o processamento de artigos científicos no SciELO Classic Website.
🎯 Objetivos
📝 Alterações Detalhadas
1. sps_xml_article_meta.py - Simplificação de busca de elementos corresp
.//back//corresp|.//body//corresp.//corresppara busca em todo documentoprecondition()etransform()2. sps_xml_refs.py - Otimização de processamento de citações
find()por XPath com indexação[0]para ref-listraw.exceptions3. sps_xml_pipes.py - Aprimoramento de conversão sup→xref
complete_xref_reftype()para xrefs com ref-type=number4. sps_xml_body_pipes.py - Reestruturação completa do pipeline HTML→XML
<p>quando ausente🔧 Tipo de Mudança
✅ Checklist
🧪 Como Testar
Execute os testes unitários:
Teste conversão de artigo exemplo:
Valide XML gerado:
📊 Impacto de Performance
🔍 Observações
📚 Referências