Campos comuns a todos os tipos de questão (* = obrigatório):
type * — "true_false" | "multiple_choice" | "essay"
concurso * — sigla ou nome do concurso (deve existir)
ano * — número
fase * — nome da fase cadastrada em Concursos → fases do ano (ex: "Prova Objetiva"). Deve bater exato (case-insensitive) com o nome cadastrado.
materia * — sigla ou nome da matéria (deve existir)
numero * — número da questão no caderno
enunciado * — HTML permitido
description — texto livre (resumo curto, aparece no card de listagem)
temas * — array de nomes ou códigos item ex: "1.2.3" (devem existir). Aceita também o formato exportado "1.2.3 Independência do Brasil" (item + nome).
difficulty — "facil" | "media" | "dificil" (default "media", aceita variações com acento ou maiúsculas: "Fácil", "MÉDIA", etc.)
anulada, desatualizada — booleanos (default false)
questao_simulada — boolean (default false). true = questão inventada/simulada (não saiu de prova real). false = questão de prova real.
tags — array de nomes. Tags inexistentes serão criadas automaticamente.
image_url — URL pública da imagem da questão (opcional)
image_title, image_slug, image_alt — metadados da imagem (opcionais; persistidos em public.images apenas se image_url apontar pro bucket question-images; do contrário, só image_url é guardado)
textos_referencia — array de objetos (ver seção abaixo)
Imagens — todos os campos de imagem (questão, item C/E, alternativa MC, texto de referência) seguem o mesmo padrão:
• image_url obrigatório quando há imagem.
• image_title / image_slug / image_alt opcionais — só viram image_id em public.images se a URL estiver no bucket question-images. URLs externas mantém apenas image_url (você pode editar a imagem pelo modal depois pra adicionar título/slug/alt e mover pro bucket).
Textos de referência — array textos_referencia
"textos_referencia": [
{
"label": "TEXTO I", // opcional — rótulo do texto na prova
"title": "Declaração do Rio...", // opcional — título identificador
"subtitle": "Conferência de 1992", // opcional
"author": "ONU", // opcional
"source": "Rio-92", // opcional
"image_url": "https://...", // opcional — URL da imagem
"image_title": "Capa da Declaração", // opcional — title em public.images
"image_slug": "capa-declaracao-rio", // opcional — slug em public.images
"image_alt": "Capa do documento", // opcional — alt text em public.images
"ordem": 1, // opcional — ordem de apresentação
"text": "<p>Os seres humanos...</p>" // obrigatório para criar novo
}
]
Estratégia de resolução (exam + year vêm automaticamente da questão):
- Se
title for informado: busca texto existente com mesmo título (case-insensitive). 1 match → vincula. 2+ matches → falha (ambíguo).
- Se não achou por título e há
text: compara por similaridade (shingles 4 palavras, normalização sem HTML/pontuação/caixa). ≥90% em 1 único → vincula. ≥90% em 2+ → falha. nenhum → cria novo.
- Sem
title nem text: erro.
- Com
title mas título não encontrado e sem text: erro (não tem como criar).
No lote, textos novos idênticos entre questões são deduplicados automaticamente.
TRUE_FALSE: adicionar items[] (* = obrigatório)
{
"type": "true_false",
// ... campos comuns ...
"items": [
{
"numero": 1, // opcional — auto-incrementa se omitido
"texto": "Afirmação...", // * obrigatório
"key": true, // * obrigatório (boolean)
"difficulty": "Fácil", // opcional, default "media"
"comment": "Justificativa...", // opcional
"anulada": false, // opcional, default false
"desatualizada": false, // opcional, default false
"temas": ["1.2.3"], // opcional
"tags": ["tag1"], // opcional (criadas se inexistentes)
"image_url": "https://...", // opcional — imagem por item
"image_title": "Mapa 1", // opcional — metadado da imagem
"image_slug": "mapa-1", // opcional
"image_alt": "Mapa do litoral" // opcional
}
]
}
MULTIPLE_CHOICE: adicionar alternatives[] (* = obrigatório)
{
"type": "multiple_choice",
// ... campos comuns ...
"alternatives": [
{
"letra": "A", // opcional — auto-gerada (A, B, C, D, E...) se omitida
"texto": "Enunciado...", // * obrigatório
"correct": false, // * obrigatório (exatamente 1 correta no array)
"comment": "Justifica...", // opcional
"image_url": "https://...", // opcional — imagem por alternativa
"image_title": "Diagrama A", // opcional — metadado da imagem
"image_slug": "diagrama-a", // opcional
"image_alt": "Esquema A" // opcional
}
]
}
Precisa de pelo menos 2 alternativas e exatamente 1 correta.
ESSAY: adicionar command, criteria[], answer_models[] (* = obrigatório)
{
"type": "essay",
// ... campos comuns ...
"command": "Aborde (a)...(b)...", // opcional — anexado ao enunciado
"criteria": [
{
"title": "Nome do critério", // * obrigatório (aceita "criterio")
"description": "Resumo curto", // opcional (aceita "descricao")
"resposta_esperada": "O que espera", // opcional
"peso": 3.0, // opcional (aceita "max_score")
"ordem": 1, // opcional — ordem de exibição (1..n, default = posição no array)
"numero": 1 // opcional — rótulo do critério (default = idx+1)
}
],
"answer_models": [
{
"content": "<p>Texto modelo...</p>", // * obrigatório (aceita "modelo")
"author": "Prof. Silva", // opcional
"grade": "9.5", // opcional
"description": "Nota explicativa" // opcional
}
]
}
Campos de essay no banco:
• criteria[] → colunas criterio (aceita title), descricao (aceita description), resposta_esperada, peso, ordem (1..n), numero
• answer_models[] → coluna modelo (aceita content), author, grade, description
• command não tem coluna própria — é anexado ao final do enunciado como parágrafo
IMPORTAÇÃO EM LOTE
Para importar múltiplas questões de uma vez, envolva o JSON em um array: [ {quest1}, {quest2}, ... ]. Cada questão é validada separadamente; as válidas são importadas e as com erro são puladas. Textos de referência novos idênticos entre questões do lote são deduplicados automaticamente — é criado apenas um registro e vinculado a todas as questões que o usam.
POLÍTICA DE VALIDAÇÃO
Devem existir: matéria, concurso, temas.
Criados automaticamente: tags novas, textos de referência sem correspondência.
Falha com erro: matéria/concurso/tema não encontrado; texto ambíguo (múltiplos textos existentes batem com o novo).