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_pt — Lista 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 só 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
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)
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-availabilityesupplementary-materialentram no body quando o texto enviado as contém (o SPS permite esses<sec>em<body>ou<back>).Endpoints
POST/api/v1/body/POST/api/v1/body/docx/.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) oumultipart/form-data(DOCX)POST /api/v1/body/Campos do request
bodytypejson|xmljsonlanguagept,en,es)Exemplo A — saída JSON
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
200
{ "data": "<body>...</body>" }O XML deve usar as tags do SPS 1.10 para o corpo (
<body>,<sec>com@sec-typee<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/.Resposta no mesmo formato de
/api/v1/body/.Schema do payload marcado (
dataquandotype=json)sections<body>Cada secção:
idsec+ inteiro (sec1,sec2). Transcrição:TR1.sec_type@sec-type. Omitir se o título de 1.º nível não estiver na tabela.title<title>é mandatório em<sec>)contentsections<sec>aninhado, em geral sem@sec-type)Blocos em
content(type):typeptext,xrefs[],ext_links[]<p>figid,fig_type,label,caption,href,attrib,alt_text<fig>+<graphic>fig-groupid,figs[](cada um comlanguage,label,caption,href)<fig-group>(legendas traduzidas)table-wrapid,label,caption,attrib,headers[],rows[][],footnotes[]<table-wrap>+<table>NISOlistid,list_type,title,items[]<list>(itemspodem aninhar outra lista)disp-formulaid,label,text<disp-formula>(@idprefixoe)mediaid,label,caption,mime_type,mime_subtype,href<media>supplementary-materialid,label,caption,href,mime_type,mime_subtype<supplementary-material>disp-quotetext,attrib<disp-quote>(JATS; quando o texto for citação longa)xrefs[]:{ ref_type, rid, text }. Todo@riddeve ter@idcorrespondente no XML gerado.Regras SPS 1.10 para o body
Fonte: SPS 1.10_pt — Lista de marcação e Sugestão de atribuição de
@id.Prefixo de
@id<sec>secsec1,sec2<sec sec-type="transcript">TRTR1<fig>ff1,f2<graphic>/<inline-graphic>gfgf1(noxlink:hrefdo ficheiro)<table-wrap>tt1,t2<table-wrap-foot>+<fn>TFNTFN1<disp-formula>/<inline-formula>ee1,e2<mml:math>mm1<media>/<inline-media>mdmd1<supplementary-material>supplsuppl1<ref>(só oridda citação)BB1,B2<fn>fnfn1Não usar prefixos antigos de Markup (
gf01em@idde figura,B001, etc.). Figura =f1; o ficheiro da imagem pode chamar-se…-gf1.jpg.<sec>e@sec-type<sec>obriga<title>, depois um ou mais<p>(ou outros blocos).@sec-type. Se o título não estiver na tabela, não inserir o atributo.<sec>dentro de<sec>, em geral sem@sec-type.\|(pipe), na ordem do título. Ex.:materials|methods,results|discussion.supplementary-material,transcript,data-availability.@sec-typeintromaterialsmethodsresultsdiscussionconclusionscasessubjectssupplementary-material<body>ou em<back>)transcript@idTR1; exige<title>)data-availability@specific-use)@specific-useemdata-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>@idobrigatório (f1,f2, …).<label>,<caption>+<title>,<graphic @xlink:href>,<attrib>.@fig-typesó se o<label>não for fig/figure/figura:graphic·chart·diagram·drawing·illustration·map.<fig-group>com<fig xml:lang="…">.jpg/jpeg(preferencial),png,tif/tiff;svgsó em<alternatives>.<alt-text>e/ou<long-desc>quando houver descrição.<media>, não<fig>.<table-wrap>@idobrigatório (t1, …).<label>ou<caption>+<title>(se ambos ausentes no original:<caption><title/></caption>).<table>não pode ser<tr>;<th>só em<thead>;<td>só em<tbody>.<table-wrap-foot>+<fn id="TFN1">com<label>(não usar<title>/<bold>como rótulo).<list>@list-typeobrigatório:order·bullet·alpha-lower·alpha-upper·roman-lower·roman-upper·simple.<label>nos itens (o@list-typegera o prefixo).<title>da lista só quando existir no texto.<list>dentro de<list-item>.<disp-formula>/<inline-formula>@idobrigatório (prefixoe). MathML:@idprefixomem<mml:math>.<xref>Atributos obrigatórios:
@ref-typee@rid. Todo@ridtem de ter@idno XML.Valores de
@ref-typeusados 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):<sup>não envolve<xref>quando não há caracteres extra; o<sup>fica dentro de<xref>. Intervalo com parênteses: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 sec-type="supplementary-material">última em<body>(ou em<back>), com<title>obrigatório.<supplementary-material id="supplN">por item, com<label>obrigatório;<graphic>para figura,<media>para PDF/vídeo/etc.<sec>/<fig>/<table-wrap>.Códigos HTTP
200400bodyausente/vazio;typeinválido; DOCX inválido)401/403503Comportamento esperado
/api/v1/front/e/api/v1/reference/.body/(não importaxml_manager,frontnemreference).BODY_ENABLED,BODY_URL), no mesmo padrão deFRONT_*. Não usar API de IA de terceiros.bodynão deve reenviar à LLM se já existir marcação persistida.type=json(default) devolve objeto estruturado emdata.type=xmldevolve string XML SPS 1.10 do<body>emdata.503sem persistir resultado inválido como sucesso.Critérios de aceite
POST /api/v1/body/autenticado com texto válido devolve200edatano schema acima (type=json).POST /api/v1/body/comtype=xmldevolve XML SPS 1.10 do<body>emdata.<sec>tem<title>; 1.º nível usa@sec-typeda tabela SPS (incluindo pipe em secções combinadas).@idsegue os prefixos SPS (sec,f,t,e,B,suppl,md,TR).disp-formulaaparecem após a primeira chamada no texto.<xref>com@ref-type/@rid;ridtemidcorrespondente.<xref>…<sup>e não<sup><xref>.body(ou vazio) devolve400.401/403.503.docs/wiki/api-rest.mdinclui o endpoint.Notas de implementação
front/:api/v1/views.py, serializers,marking.py,providers/http.py,data_utils.py(JSON → XML), modelo comchecksum, testes embody/tests/.config/api_router.py(basename="body").fixtures/*/xml/(<body>real SciELO SPS 1.10) — ajustar prefixos de@idao guia atual quando o corpus legado divergir.Referências