Skip to content

Adicionar app docx_parser (extração DOCX isolada e reutilizável) #23

Description

@gitnnolabs

Descrição da tarefa

Adicionar o app Django docx_parser ao repositório scielo-tools.

O núcleo de extração DOCX (parser.py, omml2mml.xsl) será implementado como pacote Python dentro do app, consumível por manuscrito e sps, mas sem acoplamento directo ao Wagtail (get_image_model() no parser) e com persistência própria. O objetivo desta issue é uma aplicação Django auto-suficiente e reutilizável:

  • Banco de dados próprio com migrations em docx_parser/migrations/ (layouts DOCX, jobs de parsing, imagens extraídas, configuração).
  • Telas Wagtail próprias para gerir layouts, testar extração e consultar histórico de jobs.
  • APIs REST (DRF) para parsing síncrono/assíncrono e consulta de layouts — consumíveis por manuscrito, integrações editoriais ou outros projetos.
  • Sem dependência do app manuscrito, rotulagem, sps, ia ou referencia.
  • Contrato Python estável (DocxParser, serviços de extração) para uso interno pelos demais apps.

Âmbito:

Módulo Responsabilidade
docx_parser/parser.py Classe DocxParser: extração de parágrafos, listas, tabelas, fórmulas OMML→MathML, imagens, detecção de secções e front matter
docx_parser/omml2mml.xsl Transformação XSLT de fórmulas Office Math para MathML
docx_parser/models.py DocxLayout, DocxParseJob, DocxExtractedImage, DocxParserConfig
docx_parser/services.py Orquestração de parsing, persistência de resultados, resolução de imagens
docx_parser/tasks.py Parsing assíncrono via Celery (jobs grandes)
docx_parser/wagtail_hooks.py Snippets / painéis Wagtail (layouts, config, preview de extração)
docx_parser/api/v1/ Endpoints REST (upload, parse, jobs, layouts)
docx_parser/forms.py Formulários admin Wagtail com metadados de auditoria próprios
docx_parser/apps.py Registo Django (DocxParserConfig)

Fora do âmbito desta issue:

  • Adição do app manuscrito, rotulagem ou sps (são consumidores futuros).
  • Segmentação semântica front/body/back (rotulagem / labeling) — recebe a saída bruta de extract_content.
  • Geração de XML SPS, HTML ou PDF (sps).
  • Pipeline editorial completo (upload → artigo → publicação).

Pré-requisitos (blockers)

Pré-requisito Consumido por Notas
python-docx==1.1.2 docx_parser/parser.py Leitura e manipulação DOCX
lxml==4.9.4 parser.py, XSLT Parsing XML, OMML→MathML
djangorestframework docx_parser/api/ API REST
Celery + Redis docx_parser/tasks.py Jobs assíncronos (opcional no smoke inicial)
Wagtail wagtail_hooks.py, storage opcional de imagens UI admin; não obrigatório para API pura

Ordem recomendada na fragmentação: docx_parserrotulagemspsmanuscrito.

Decisões de design para reusabilidade

  1. Imagens extraídas desacopladas do Wagtail:

    • DocxParser.extract_content não deve criar registos wagtail.images.Image directamente.
    • Introduzir modelo DocxExtractedImage (FileField, checksum, nome original) como armazenamento padrão.
    • Expor setting DOCX_PARSER_IMAGE_BACKEND (docx_parser | wagtail) e/ou callback image_store(blob, name) -> id injetável em extract_content, para projetos que já usam Wagtail media.
  2. Layouts DOCX versionados em base de dados:

    • sps/utils.generate_pdf_for_xml_document referencia docx_parser/layouts/two_cols.docx no filesystem.
    • Modelo DocxLayout com FileField, slug (two_cols), is_default; seed migration ou fixture com two_cols.docx.
    • Helper get_default_layout_path() resolve layout activo (DB → fallback filesystem em docx_parser/layouts/).
  3. Jobs de parsing auditáveis:

    • Modelo DocxParseJob: ficheiro de entrada, estado (pending, running, done, failed), resultado JSON (sections, content), erro, timestamps, utilizador opcional.
    • Permite Wagtail (histórico) e API (polling) sem depender de manuscrito.
  4. Configuração editorial:

    • Modelo DocxParserConfig (singleton ou por tenant): merge_front default, marcadores de secção (introdução, abstract, etc.), regras extensíveis via JSON.
    • Wagtail snippet para editar sem deploy.
  5. Metadados de auditoria próprios:

    • Classe abstrata DocxParserMetadataModel + DocxParserAdminModelForm em docx_parser/forms.py — sem core.models ou core.forms.

Subtarefas

  • Criar app docx_parser/ na raiz do projeto com parser.py e omml2mml.xsl.
  • Refactorizar parser.py: remover import directo de wagtail.images.get_image_model; usar services.store_extracted_image ou callback injectável.
  • Implementar modelos DocxLayout, DocxExtractedImage, DocxParseJob, DocxParserConfig com migrations próprias.
  • Implementar docx_parser/services.py (parse_docx_file, parse_docx_document, get_layout_path).
  • Adicionar layout seed two_cols.docx em docx_parser/layouts/ e registo inicial em DocxLayout.
  • Implementar docx_parser/tasks.py com task_parse_docx(job_id).
  • Criar docx_parser/wagtail_hooks.py: grupo "DOCX Parser" com snippets para layouts, config e listagem de jobs.
  • Criar docx_parser/api/v1/serializers.py e views.py com viewsets/endpoints documentados abaixo.
  • Registar "docx_parser" em LOCAL_APPS (config/settings/base.py).
  • Adicionar settings: DOCX_PARSER_IMAGE_BACKEND, paths default de layouts.
  • Adicionar dependências em requirements/base.txt: python-docx==1.1.2, lxml==4.9.4.
  • Registar rotas REST em config/urls.py ou router DRF global.
  • Criar testes em docx_parser/tests/ (extração de parágrafo, tabela, fórmula, imagem mock, API upload, resolução de layout).
  • Smoke test no shell:
    python manage.py shell
    from docx_parser.parser import DocxParser
    from docx_parser.services import get_default_layout_path
    path = get_default_layout_path()
    print(path)

API pública a preservar / expor

Serviços Python (docx_parser.parser / docx_parser.services)

Função / método Uso
DocxParser.open_docx(filename) Abre python-docx.Document
DocxParser().extract_content(doc, doc_path, merge_front=True, image_store=None) Extrai secções tipográficas + lista de blocos (text, table, list, image, formula, first_block, …)
parse_docx_file(path, **options) -> dict Wrapper de alto nível com persistência opcional em DocxParseJob
get_default_layout_path() -> str Caminho do layout DOCX default (consumido por sps na geração PDF)
get_layout_path(slug="two_cols") -> str Resolve layout por slug

Formato de saída de extract_content (contrato estável)

sections: list[dict]   # perfis tipográficos detectados (size, bold, isupper, count)
content: list[dict]    # blocos sequenciais com keys: type, text, table, list, image, formula, paraph, ...

Endpoints REST

Método Endpoint Descrição
POST /api/v1/docx_parser/parse/ Upload DOCX; parsing síncrono; devolve {sections, content}
POST /api/v1/docx_parser/jobs/ Enfileira job Celery; devolve {id, status}
GET /api/v1/docx_parser/jobs/{id}/ Estado e resultado do job
GET /api/v1/docx_parser/layouts/ Lista layouts registados
GET /api/v1/docx_parser/layouts/{slug}/ Metadados + URL do ficheiro layout
GET /api/v1/docx_parser/config/ Configuração activa do parser (autenticado)

Autenticação: IsAuthenticated para escrita e jobs; leitura de layouts pode ser IsAuthenticated ou IsAdminUser conforme política do projeto.

Wagtail

  • Grupo DOCX Parser: layouts, configuração, jobs recentes.
  • Acção opcional: upload de DOCX de teste com preview JSON na admin.

Critérios de aceite

  • python manage.py check sem erros com docx_parser em LOCAL_APPS.
  • Nenhum import de manuscrito, rotulagem, sps, ia, referencia ou core dentro de docx_parser/.
  • Tabelas docx_parser_* criadas via migrations (DocxLayout, DocxParseJob, DocxExtractedImage, DocxParserConfig).
  • DocxParser().extract_content funciona sem Wagtail quando DOCX_PARSER_IMAGE_BACKEND=docx_parser.
  • Layout two_cols disponível via modelo ou filesystem; get_default_layout_path() não levanta FileNotFoundError.
  • POST /api/v1/docx_parser/parse/ com DOCX de exemplo devolve JSON com sections e content.
  • Menu Wagtail exibe grupo DOCX Parser com layouts e config.
  • Pelo menos três testes automatizados em docx_parser/tests/ passam no CI.
  • App instalável em projeto Django+Wagtail+DRF sem os demais apps SciELO Tools.

Como testar manualmente

  1. Subir ambiente: make build && make up && make django_migrate.
  2. Wagtail admin → DOCX Parser → confirmar layout two_cols e config default.
  3. Upload de DOCX de teste via API:
    curl -X POST -H "Authorization: Bearer <token>" \
      -F "file=@/caminho/artigo.docx" \
      http://localhost:8009/api/v1/docx_parser/parse/
  4. No shell:
    from docx import Document
    from docx_parser.parser import DocxParser
    doc = Document("/caminho/artigo.docx")
    sections, content = DocxParser().extract_content(doc, "/caminho/artigo.docx")
    print(len(sections), len(content), content[0].keys())
  5. Confirmar imagem extraída em DocxExtractedImage (ou Wagtail, se backend configurado).
  6. (Opcional) Enfileirar job:
    from docx_parser.models import DocxParseJob
    from docx_parser.tasks import task_parse_docx
    job = DocxParseJob.objects.create(...)
    task_parse_docx.delay(job.pk)

Considerações e notas

  • Evolução biblioteca → app: o plano de fragmentação (docs/plans/2026-06-28-fragmentar-manuscrito.md) descrevia docx_parser como biblioteca sem DB; esta issue eleva o pacote a app Django com persistência e API, alinhada ao pedido de auto-suficiência e reuso.
  • Consumidores downstream: rotulagem segmenta a saída de extract_content em front/body/back; manuscrito orquestra parser + rotulagem + xref; sps consome o caminho do layout two_cols.docx. Manter contrato de saída {sections, content} estável.
  • Fórmulas: omml2mml.xsl deve ser empacotado como package-data do app (docx_parser/omml2mml.xsl ao lado de parser.py).
  • Layout two_cols.docx: necessário para geração PDF via sps — incluir fixture ou gerar documento mínimo de duas colunas para testes.
  • RCT vs código: o RCT menciona docx_layouts; nesta issue o nome do app permanece docx_parser (singular), alinhado às issues sps / rotulagem.
  • Performance: parsing de DOCX grandes pode exceder timeout HTTP — usar DocxParseJob + Celery na API para ficheiros acima de limiar configurável.
  • Plano de fragmentação: docx_parser é pré-requisito de rotulagem, sps e manuscrito — ver docs/plans/2026-06-28-fragmentar-manuscrito.md.

Referências

  • RCT SciELO Tools v4.0 — pipeline DOCX → estrutura editorial

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions