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

Activity

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

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions