Identificação e inserção automática de xref no pipeline DOCX → SPS XML#109
Merged
Conversation
Contributor
|
Revisando.... |
…OCX → SPS XML) Adiciona módulo xref.py com detecção de estilo de citação (ABNT narrativo, ABNT parentético, Vancouver bracket, Vancouver superscript), marcação de bookmarks e hyperlinks no DOCX, validação de consistência e extração do mapeamento citação→rid. Integra o pipeline em tasks.py: o DOCX é marcado antes do processamento principal, o xref_map é aplicado em stream_data_body e repassado a get_xml(). Ranges Vancouver são expandidos para rid multi-valor (ex: "[26-27]" → "B26 B27"). Em xml.py, substitui o guard coarse `if 'xref' not in paragraph` por processamento por segmento via _apply_to_segments(), eliminando risco de double-wrapping e garantindo que citações em parágrafos parcialmente marcados não sejam ignoradas. O LLM (proccess_labeled_text) passa a atuar como fallback. Adiciona campos marked_file e xref_status ao modelo ArticleDocxMarkup, com widgets visuais, proxy model ProcessedDocx e views download_marked_docx e reprocess para suporte à revisão humana via interface Wagtail. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
labeling_utils.py: - Regex de detecção da seção de referências mais precisa (refer[eê]nci|references?) - resp_json inicializado antes do bloco condicional (evitava UnboundLocalError) - Escapa '<' literal em append_fragment para não quebrar o parser XML - Guarda None em proccess_special_content para search_special_id retornando None function_docx.py: - is_numPr inicializado antes do loop (tabelas após listas eram descartadas silenciosamente por flag herdada da iteração anterior) - Parágrafos adicionados a content independente de tabelas adjacentes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Insere botão na barra de ações do cabeçalho nas views de edição de ProcessedDocx e MarkupXML, com mensagem de confirmação contextual. Usa MutationObserver como fallback para injeção no DOM quando o cabeçalho ainda não está renderizado. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2df66d3 to
4ca261d
Compare
Remoção de guarda duplicada `if vals2 and vals2[0]:` que ficou sem corpo após resolução de conflito de rebase, causando IndentationError. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Dois acessos diretos a values["reftype"] sobreviveram à resolução de
conflito do rebase. Substituídos por values.get("reftype") para não
lançar KeyError quando o LLM não parseia o tipo da referência.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
O passe de xref_map em stream_data_body usava para.replace() cru, sem segmentação. Para Vancouver superscript, chaves curtas como "2" substituíam o dígito dentro de rid="B2" já criado, corrompendo o atributo: rid="B<xref ref-type="bibr" rid="B2">2</xref>". Substituído por _apply_xref_map_safe() com a mesma lógica segmentada de xml.py: divide nos limites de <xref> existentes e aplica o replace apenas nos segmentos de texto puro. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
O bug: _apply_xref_map acumulava replacements dentro do mesmo segmento. Após substituir "20" → <xref rid="B20">20</xref>, a iteração seguinte substituía "2" dentro do próprio rid="B20" já criado, produzindo rid="B<xref rid="B2">2</xref>0" — XML inválido. Fix: um _apply_to_segments por citação (não um for-loop dentro do segmento). A cada iteração, os <xref> criados tornam-se fronteiras que protegem as iterações seguintes. Aplicado em tasks.py (_apply_xref_map_safe) e xml.py (_apply_xref_map). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…l.py Artigos com figuras sem figlabel levantavam KeyError. Substituídos por .get() com fallback vazio para todos os campos opcionais de elementos especiais (fig, table, formula). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Figuras e tabelas sem rótulo levantavam KeyError em search_special_id. Substituídos por .get() com fallback vazio e guards de comprimento antes de indexar figid/tabid. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-type Fallback para "bullet" quando o parágrafo não contém o padrão esperado. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fallback para string vazia quando o regex de content_list não encontra o padrão [list ...][/list] no parágrafo. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Contexto
O padrão SPS/JATS exige que toda citação bibliográfica no corpo do artigo esteja marcada com
<xref ref-type="bibr" rid="Bn">, ligando-a à entrada correspondente na lista de referências. Anteriormente, a geração dessas tags dependia exclusivamente de um LLM para parsear as referências e identificar citações — mecanismo com cobertura baixa e imprevisível: sem o modelo disponível, nenhum<xref>era gerado.Esta branch implementa um pipeline determinístico e independente de LLM para essa tarefa, usando o próprio DOCX como fonte de verdade. O LLM passa de mecanismo primário a camada de fallback.
Fluxo do pipeline
Os passes ⑤ e ⑥ operam por segmento: o parágrafo é dividido nas fronteiras de
<xref>já existentes e cada camada processa apenas o texto puro, eliminando risco de double-wrapping e garantindo que citações num parágrafo parcialmente marcado não sejam ignoradas.Cobertura por estilo de citação
*B27 e B30 cobertos via expansão de range
[26-27]/[29-30]→ sem hyperlink direto no DOCX, mas presentes noriddo XML.Por que não é 100%:
Comparação com a versão anterior (apenas LLM via
proccess_labeled_text):O que foi implementado
markup_doc/xref.py— novo móduloCore da feature. Contém toda a lógica de identificação de citações no DOCX:
mark_references(),validate_marks(),read_marks(),build_text_xref_replacer()Heading Nmarkup_doc/tasks.pyIntegração do pipeline xref antes do processamento principal. Expansão de ranges Vancouver no
xref_map. Pré-processamento destream_data_bodycom os dois primeiros passes.markup_doc/xml.pySubstituição do guard coarse
if 'xref' not in paragraphpor processamento por segmento via_apply_to_segments(),_apply_xref_map()e_apply_proccess_labeled_text(). Todos os parágrafos recebem os três passes; xrefs pré-existentes são preservados.markup_doc/models.pyCampos
marked_file(FileField) exref_status(JSONField) emArticleDocxMarkup. WidgetsDownloadMarkedFileWidget,XrefStatusWidgete painelReprocessButtonPanel. Proxy modelProcessedDocxcom view dedicada no admin.markup_doc/views.py+markup_doc/wagtail_hooks.pyViews
download_marked_docxereprocess.ProcessedDocxViewSetregistrado no grupo de marcação editorial.markup_doc/static/js/xref-button.jsBotão "Reprocessar" injetado na barra de ações do cabeçalho Wagtail nas views de
ProcessedDocxeMarkupXML, com mensagem de confirmação contextual.Correções de bugs colaterais
markup_doc/labeling_utils.py:resp_json = {}inicializado antes do bloco condicional (preveniaUnboundLocalError)<literal escapado emappend_fragment(prevenia quebra do parser XML)Noneemproccess_special_contentmarkuplib/function_docx.py:is_numPrinicializado antes do loop (tabelas após listas eram descartadas silenciosamente)contentindependente de tabelas adjacentesMigrações
0003_articledocxmarkup_marked_file0004_articledocxmarkup_xref_status🤖 Generated with Claude Code