Skip to content

Adicionar app body #41

Description

@gitnnolabs

Descrição da tarefa

Criar um endpoint REST para marcar o body do artigo, no mesmo padrão dos endpoints de front (POST /api/v1/front/) e de referências (POST /api/v1/reference/).

O cliente envia o texto do corpo do artigo. A API usa LLM local para extrair a estrutura JATS / SciELO PS 1.10 e devolve JSON ou XML.

O XML gerado deve seguir as regras do guia SPS 1.10 para os elementos que ocorrem em <body>: <sec>, <p>, <fig>, <fig-group>, <graphic>, <table-wrap>, <list>, <disp-formula>, <inline-formula>, <media>, <supplementary-material>, <xref>, <ext-link>, e formatação inline (italic, bold, sup, sub).

Fora do âmbito desta API (já cobertos por front / reference / back):

  • <front>: título, autores, afiliação, resumo, palavras-chave, DOI, journal-meta, permissions, history
  • <back>: <ref-list>, <ack>, <app-group>, <fn-group> (notas de documento / financiamento / conflito)

data-availability e supplementary-material entram no body quando o texto enviado as contém (o SPS permite esses <sec> em <body> ou <back>).


Endpoints

Método Caminho Auth Descrição
POST /api/v1/body/ Sim (JWT) Marcar body a partir de texto
POST /api/v1/body/docx/ Sim (JWT) Upload .docx, extrair o texto do corpo e marcar (opcional nesta issue)

Prefixo: /api/v1/
Auth: Authorization: Bearer <access_token> (IsAuthenticated)
Content-Type: application/json (texto) ou multipart/form-data (DOCX)


POST /api/v1/body/

Campos do request

Campo Tipo Default Obrigatório Descrição
body string sim Texto do corpo do artigo a marcar
type json | xml json não Formato de saída
language string (ex.: pt, en, es) não Idioma fallback quando a IA não devolver idioma

Exemplo A — saída JSON

curl -s -X POST "${BASE_URL}/api/v1/body/" \
  -H "Authorization: Bearer ${ACCESS}" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "INTRODUÇÃO\nO empreendedorismo social promove o bem-estar coletivo.1-3 O modelo está na Figura 1.\n\nMÉTODO\nEstudo teórico-reflexivo.\n\nRESULTADOS E DISCUSSÃO\nA reflexão deu origem a dois eixos.\nFigura 1. Sistemas inter-relacionados da teoria.\nFonte: Elaboração própria.",
    "type": "json",
    "language": "pt"
  }'

200

{
  "data": {
    "sections": [
      {
        "id": "sec1",
        "sec_type": "intro",
        "title": "INTRODUÇÃO",
        "content": [
          {
            "type": "p",
            "text": "O empreendedorismo social promove o bem-estar coletivo. O modelo está na Figura 1.",
            "xrefs": [
              {"ref_type": "bibr", "rid": "B1", "text": "1"},
              {"ref_type": "bibr", "rid": "B3", "text": "3"},
              {"ref_type": "fig", "rid": "f1", "text": "Figura 1"}
            ]
          }
        ],
        "sections": []
      },
      {
        "id": "sec2",
        "sec_type": "methods",
        "title": "MÉTODO",
        "content": [
          {
            "type": "p",
            "text": "Estudo teórico-reflexivo."
          }
        ],
        "sections": []
      },
      {
        "id": "sec3",
        "sec_type": "results|discussion",
        "title": "RESULTADOS E DISCUSSÃO",
        "content": [
          {
            "type": "p",
            "text": "A reflexão deu origem a dois eixos."
          },
          {
            "type": "fig",
            "id": "f1",
            "fig_type": null,
            "label": "Figura 1",
            "caption": "Sistemas inter-relacionados da teoria.",
            "href": "",
            "attrib": "Elaboração própria."
          }
        ],
        "sections": []
      }
    ]
  }
}

Exemplo B — saída XML

curl -s -X POST "${BASE_URL}/api/v1/body/" \
  -H "Authorization: Bearer ${ACCESS}" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "INTRODUÇÃO\nO empreendedorismo social promove o bem-estar coletivo.\n\nMÉTODO\nEstudo teórico-reflexivo.",
    "type": "xml",
    "language": "pt"
  }'

200

{
  "data": "<body>...</body>"
}

O XML deve usar as tags do SPS 1.10 para o corpo (<body>, <sec> com @sec-type e <title> obrigatório, <p>, <fig id="f1">, <table-wrap id="t1">, <list list-type="…">, <disp-formula id="e1">, <xref ref-type="…" rid="…">, etc.).


POST /api/v1/body/docx/ (opcional)

Upload de .docx; a API extrai o texto do corpo do documento (excluindo front e lista de referências, quando identificáveis) e aplica a mesma marcação de /api/v1/body/.

curl -s -X POST "${BASE_URL}/api/v1/body/docx/" \
  -H "Authorization: Bearer ${ACCESS}" \
  -F "file=@/caminho/artigo.docx" \
  -F "type=json" \
  -F "language=pt"

Resposta no mesmo formato de /api/v1/body/.


Schema do payload marcado (data quando type=json)

Campo Tipo Descrição
sections array Secções de primeiro nível do <body>

Cada secção:

Campo Tipo Descrição
id string Prefixo SPS sec + inteiro (sec1, sec2). Transcrição: TR1.
sec_type string | null Ver tabela de @sec-type. Omitir se o título de 1.º nível não estiver na tabela.
title string Obrigatório (<title> é mandatório em <sec>)
content array Blocos na ordem do texto
sections array Subsecções (<sec> aninhado, em geral sem @sec-type)

Blocos em content (type):

type Campos SPS
p text, xrefs[], ext_links[] <p>
fig id, fig_type, label, caption, href, attrib, alt_text <fig> + <graphic>
fig-group id, figs[] (cada um com language, label, caption, href) <fig-group> (legendas traduzidas)
table-wrap id, label, caption, attrib, headers[], rows[][], footnotes[] <table-wrap> + <table> NISO
list id, list_type, title, items[] <list> (items podem aninhar outra lista)
disp-formula id, label, text <disp-formula> (@id prefixo e)
media id, label, caption, mime_type, mime_subtype, href <media>
supplementary-material id, label, caption, href, mime_type, mime_subtype <supplementary-material>
disp-quote text, attrib <disp-quote> (JATS; quando o texto for citação longa)

xrefs[]: { ref_type, rid, text }. Todo @rid deve ter @id correspondente no XML gerado.


Regras SPS 1.10 para o body

Fonte: SPS 1.10_ptLista de marcação e Sugestão de atribuição de @id.

Prefixo de @id

Elemento Prefixo Exemplo
<sec> sec sec1, sec2
<sec sec-type="transcript"> TR TR1
<fig> f f1, f2
<graphic> / <inline-graphic> gf gf1 (no xlink:href do ficheiro)
<table-wrap> t t1, t2
<table-wrap-foot> + <fn> TFN TFN1
<disp-formula> / <inline-formula> e e1, e2
<mml:math> m m1
<media> / <inline-media> md md1
<supplementary-material> suppl suppl1
<ref> (só o rid da citação) B B1, B2
<fn> fn fn1

Não usar prefixos antigos de Markup (gf01 em @id de figura, B001, etc.). Figura = f1; o ficheiro da imagem pode chamar-se …-gf1.jpg.

<sec> e @sec-type

  • Cada <sec> obriga <title>, depois um ou mais <p> (ou outros blocos).
  • Secção de primeiro nível cujo título corresponde à tabela deve ter @sec-type. Se o título não estiver na tabela, não inserir o atributo.
  • Subsecções: <sec> dentro de <sec>, em geral sem @sec-type.
  • Secções combinadas: valores unidos por \| (pipe), na ordem do título. Ex.: materials|methods, results|discussion.
  • Não combinar com pipe: supplementary-material, transcript, data-availability.
@sec-type Quando
intro Introdução / sinopse
materials Materiais
methods Metodologia / método / procedimentos
results Resultados / descobertas
discussion Discussão / interpretações
conclusions Conclusões / considerações finais / comentários
cases Relatos / casos / estudos de caso
subjects Participantes / pacientes
supplementary-material Material suplementar (última secção de <body> ou em <back>)
transcript Transcrição de vídeo/áudio (@id TR1; exige <title>)
data-availability Declaração de disponibilidade de dados (+ @specific-use)

@specific-use em data-availability (quando o texto o permitir): data-available · data-available-upon-request · uninformed · data-not-available · data-in-article.

Figuras, tabelas e fórmulas — posição

Figuras, tabelas e <disp-formula> fora de <app-group> e <supplementary-material> devem ser inseridas no XML logo abaixo da primeira chamada no texto, independentemente da posição no PDF/DOCX.

<fig>

  • @id obrigatório (f1, f2, …).
  • Pode ter <label>, <caption> + <title>, <graphic @xlink:href>, <attrib>.
  • @fig-type se o <label> não for fig/figure/figura: graphic · chart · diagram · drawing · illustration · map.
  • Legendas traduzidas: <fig-group> com <fig xml:lang="…">.
  • Extensões de imagem: jpg/jpeg (preferencial), png, tif/tiff; svg só em <alternatives>.
  • Acessibilidade: <alt-text> e/ou <long-desc> quando houver descrição.
  • Vídeo/áudio/PDF: <media>, não <fig>.

<table-wrap>

  • @id obrigatório (t1, …).
  • Obrigatório pelo menos <label> ou <caption> + <title> (se ambos ausentes no original: <caption><title/></caption>).
  • Tabela codificada no modelo NISO JATS: primeiro nível de <table> não pode ser <tr>; <th> só em <thead>; <td> só em <tbody>.
  • Notas: <table-wrap-foot> + <fn id="TFN1"> com <label> (não usar <title>/<bold> como rótulo).

<list>

  • @list-type obrigatório: order · bullet · alpha-lower · alpha-upper · roman-lower · roman-upper · simple.
  • Não usar <label> nos itens (o @list-type gera o prefixo).
  • <title> da lista só quando existir no texto.
  • Sublistas: <list> dentro de <list-item>.

<disp-formula> / <inline-formula>

  • @id obrigatório (prefixo e). MathML: @id prefixo m em <mml:math>.
  • Fórmulas devem ser codificadas (MathML preferencial; TeX/LaTeX aceitável). Não deixar só imagem sem código, salvo se o texto de entrada não permitir extração.

<xref>

Atributos obrigatórios: @ref-type e @rid. Todo @rid tem de ter @id no XML.

Valores de @ref-type usados no body: bibr · fig · table · disp-formula · sec · fn · list · supplementary-material · boxed-text · app · table-fn.

Citações bibliográficas (SciELO Brasil: pelo menos uma @ref-type="bibr" no documento indexável):

<xref ref-type="bibr" rid="B1"><sup>1</sup></xref>

<sup> não envolve <xref> quando não há caracteres extra; o <sup> fica dentro de <xref>. Intervalo com parênteses:

<sup>(<xref ref-type="bibr" rid="B1">1</xref> - <xref ref-type="bibr" rid="B7">7</xref>)</sup>

Figura: <xref ref-type="fig" rid="f1">Figura 1</xref>.

Transcrição: se existir <sec sec-type="transcript" id="TR1">, o <media> deve ter <xref ref-type="sec" rid="TR1"/>.

Material suplementar

  • Secção <sec sec-type="supplementary-material"> última em <body> (ou em <back>), com <title> obrigatório.
  • Um <supplementary-material id="supplN"> por item, com <label> obrigatório; <graphic> para figura, <media> para PDF/vídeo/etc.
  • Conteúdo integral no PDF/texto do body não é suplementar: marcar no sítio com <sec> / <fig> / <table-wrap>.
  • URL de dataset → declaração de disponibilidade de dados, não suplementar.

Códigos HTTP

Código Quando
200 Marcação concluída
400 Validação (ex.: body ausente/vazio; type inválido; DOCX inválido)
401 / 403 Sem autenticação ou token inválido
503 LLM indisponível / desligado / mal configurado

Comportamento esperado

  • Autenticação JWT igual à de /api/v1/front/ e /api/v1/reference/.
  • App Django modular body/ (não importa xml_manager, front nem reference).
  • LLM local (settings BODY_ENABLED, BODY_URL), no mesmo padrão de FRONT_*. Não usar API de IA de terceiros.
  • Cache por checksum do texto normalizado: o mesmo body não deve reenviar à LLM se já existir marcação persistida.
  • type=json (default) devolve objeto estruturado em data.
  • type=xml devolve string XML SPS 1.10 do <body> em data.
  • Preservar ordem dos blocos e hierarquia de secções.
  • Figuras/tabelas/fórmulas imediatamente após a primeira menção.
  • Em falha de LLM, responder 503 sem persistir resultado inválido como sucesso.

Critérios de aceite

  • POST /api/v1/body/ autenticado com texto válido devolve 200 e data no schema acima (type=json).
  • POST /api/v1/body/ com type=xml devolve XML SPS 1.10 do <body> em data.
  • Cada <sec> tem <title>; 1.º nível usa @sec-type da tabela SPS (incluindo pipe em secções combinadas).
  • @id segue os prefixos SPS (sec, f, t, e, B, suppl, md, TR).
  • Figuras/tabelas/disp-formula aparecem após a primeira chamada no texto.
  • Citações e menções a figura/tabela são <xref> com @ref-type/@rid; rid tem id correspondente.
  • Citações numéricas sobrescritas usam <xref>…<sup> e não <sup><xref>.
  • Request sem body (ou vazio) devolve 400.
  • Request sem token devolve 401/403.
  • LLM indisponível devolve 503.
  • Mesmo texto reenviado reutiliza cache (checksum) sem nova chamada à LLM.
  • Documentação em docs/wiki/api-rest.md inclui o endpoint.

Notas de implementação

  • Espelhar a estrutura do app front/: api/v1/views.py, serializers, marking.py, providers/http.py, data_utils.py (JSON → XML), modelo com checksum, testes em body/tests/.
  • Registrar o viewset em config/api_router.py (basename="body").
  • Corpus de referência: XMLs em fixtures/*/xml/ (<body> real SciELO SPS 1.10) — ajustar prefixos de @id ao guia atual quando o corpus legado divergir.
  • Revisão humana continua possível a jusante; esta issue é só a API de marcação.

Referências

  • SPS 1.10_pt (guia SciELO Publishing Schema, 22/05/2025)

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