feat: fase 2 - detecção de crianças, tags, filtros e histórico
- GPT retorna JSON estruturado: criancas, turma, tags, relatorio - Frontmatter agora inclui criancas[], turma, tags[] no .md - Índice JSON estruturado com metadados completos - Prévia do relatório mostra crianças detectadas + tags + turma - Modal 'Minhas Observações' na sidebar - Filtros por mês, tag e busca por criança - Lista de observações com preview e chips - Modal de detalhe com copiar texto - CSS para btn-observacoes, obs-modal, obs-items, tags/chips
This commit is contained in:
@@ -252,24 +252,32 @@ app.post('/api/observacao/transcrever', requireAuth, multerUpload.single('audio'
|
||||
}
|
||||
|
||||
// Gera relatório estruturado via GPT
|
||||
// Envia transcrição + pedido de extrair: crianças, turma, tags, relatório
|
||||
const reportPrompt = `Você é a Kemily, assistente pedagógica da professora Camila Martella Gasparini Reifonas.
|
||||
|
||||
Ela acabou de gravar uma observação em voz sobre seus alunos da educação infantil. Sua tarefa é analisar a transcrição e gerar um RELATÓRIO ESTRUTURADO de observação pedagógica.
|
||||
Ela acabou de gravar uma observação em voz sobre seus alunos da educação infantil. Sua tarefa é ANALISAR a transcrição e retornar UM OBJETO JSON (sem markdown, sem código, apenas o JSON puro) com:
|
||||
|
||||
REGRAS DO RELATÓRIO:
|
||||
- Use linguagem formal-pedagógica com calor humano
|
||||
- Estruture em seções claras com_MARKDOWN
|
||||
- Identifique crianças mencionadas, comportamentos, interações, conquistas, dificuldades
|
||||
- Inclua sugestões de seguimento para o planejamento
|
||||
- Foque em evidências observáveis, não interpretções subjetivas
|
||||
- Seja objetiva mas detalhista
|
||||
{
|
||||
"criancas": ["lista de nomes de crianças mencionadas ou 'null' se não identificar nenhuma"],
|
||||
"turma": "turma mencionada ou 'não informada'",
|
||||
"tags": ["tag1", "tag2"] (social, cognitivo, motor, linguagem, emocional,轮的哪个),
|
||||
"relatorio": "...markdown estruturado do relatório pedagógico..."
|
||||
}
|
||||
|
||||
TRANSCRIÇÃO DA OBSERVAÇÃO:
|
||||
REGRAS:
|
||||
- Identifique crianças pelo nome próprio mencionado na fala
|
||||
- Se não conseguir identificar nomes, use "criancas": null
|
||||
- Tags: escolha entre social, cognitivo, motor, linguagem, emocional, colaborativo, autónomo
|
||||
- Relatório em português, linguagem formal-pedagógica, seções claras em **markdown**
|
||||
- Foque em evidências observáveis
|
||||
- Máximo 120 palavras no relatório
|
||||
|
||||
TRANSCRIÇÃO:
|
||||
"""
|
||||
${transcribedText}
|
||||
"""
|
||||
|
||||
Gerando o relatório...`;
|
||||
Retorne APENAS o JSON válido:`;
|
||||
|
||||
const reportRes = await fetch('https://openrouter.ai/api/v1/chat/completions', {
|
||||
method: 'POST',
|
||||
@@ -290,11 +298,29 @@ Gerando o relatório...`;
|
||||
}
|
||||
|
||||
const reportData = await reportRes.json();
|
||||
const reportText = reportData.choices?.[0]?.message?.content || 'Relatório não pôde ser gerado.';
|
||||
const rawContent = reportData.choices?.[0]?.message?.content || '';
|
||||
|
||||
// Parse JSON da resposta
|
||||
let parsedData = { criancas: null, turma: 'não informada', tags: [], relatorio: rawContent };
|
||||
try {
|
||||
// Limpa markdown code blocks se houver
|
||||
const cleanJson = rawContent.replace(/```json\s*/g, '').replace(/```\s*/g, '').trim();
|
||||
const parsed = JSON.parse(cleanJson);
|
||||
parsedData = {
|
||||
criancas: parsed.criancas || null,
|
||||
turma: parsed.turma || 'não informada',
|
||||
tags: parsed.tags || [],
|
||||
relatorio: parsed.relatorio || rawContent
|
||||
};
|
||||
} catch (parseErr) {
|
||||
// Se não conseguir parsear JSON, retorna o texto como relatório
|
||||
console.warn('Não foi possível parsear JSON do GPT, usando texto bruto');
|
||||
parsedData.relatorio = rawContent;
|
||||
}
|
||||
|
||||
res.json({
|
||||
transcribedText,
|
||||
report: reportText
|
||||
...parsedData
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
@@ -306,7 +332,7 @@ Gerando o relatório...`;
|
||||
|
||||
// Rota: POST /api/observacao/salvar — salva relatório em .md
|
||||
app.post('/api/observacao/salvar', requireAuth, (req, res) => {
|
||||
const { report } = req.body;
|
||||
const { report, criancas, turma, tags } = req.body;
|
||||
if (!report) return res.status(400).json({ error: 'Relatório vazio' });
|
||||
|
||||
const now = new Date();
|
||||
@@ -316,13 +342,7 @@ app.post('/api/observacao/salvar', requireAuth, (req, res) => {
|
||||
const timestamp = now.toISOString();
|
||||
|
||||
// Monta markdown com frontmatter
|
||||
const mdContent = `---
|
||||
data: "${dateStr}"
|
||||
hora: "${timeStr}"
|
||||
timestamp: "${timestamp}"
|
||||
---
|
||||
|
||||
${report}
|
||||
const mdContent = `---\ndata: "${dateStr}"\nhora: "${timeStr}"\ntimestamp: "${timestamp}"\ncriancas: ${JSON.stringify(criancas)}\nturma: "${turma || 'não informada'}"\ntags: ${JSON.stringify(tags)}\n---\n\n${report}
|
||||
`;
|
||||
|
||||
const filename = `${dateStr}_${timeStr}.md`;
|
||||
@@ -332,7 +352,7 @@ ${report}
|
||||
const filePath = path.join(monthDir, filename);
|
||||
fs.writeFileSync(filePath, mdContent, 'utf8');
|
||||
|
||||
// Atualiza índice
|
||||
// Atualiza índice com metadados estruturados
|
||||
const idx = readObsIndex();
|
||||
idx.observations.unshift({
|
||||
id: Date.now(),
|
||||
@@ -340,6 +360,9 @@ ${report}
|
||||
date: dateStr,
|
||||
time: timeStr,
|
||||
timestamp,
|
||||
criancas: criancas || null,
|
||||
turma: turma || 'não informada',
|
||||
tags: tags || [],
|
||||
preview: report.slice(0, 120) + '...'
|
||||
});
|
||||
writeObsIndex(idx);
|
||||
|
||||
Reference in New Issue
Block a user