Modelo
GradeCategory (peso, drop_lowest)
└── GradeItem (atividade — quiz, assignment, manual; vinculo opcional a aula, dueDate, isPublished)
└── Grade (nota por aluno)
└── Rubric (opcional — criterios)
Calculo (exportacao CSV)
A exportacao CSV calcula a nota final ponderada assim:
courseGrade = Σ (categoryAvg × categoryWeight) / Σ pesosUsados
categoryAvg = average(itemPercents após descartar o drop_lowest da categoria)
itemPercent = studentScore / itemMaxScore × 100
Itens sem categoria sao ponderados pelo proprio GradeItem.weight. Apenas itens publicados contam.
Roadmap: penalidade automatica por atraso (
submittedAt > dueDate) e release agendado por data ainda nao sao aplicados.dueDatee armazenado e exibido;isPublishedcontrola a visibilidade.
Rubricas
{
"name": "Redacao ENEM",
"criteria": [
{
"name": "Dominio da norma culta",
"weight": 0.2,
"levels": [
{ "score": 200, "label": "Excelente", "description": "..." },
{ "score": 160, "label": "Bom", "description": "..." },
{ "score": 120, "label": "Regular", "description": "..." }
]
},
{
"name": "Compreensao do tema",
"weight": 0.2,
"levels": [...]
}
]
}
Aluno ve breakdown expansivel: nota final + cada criterio + comentario.
Integridade flags
O QuizAttempt persiste tabSwitchCount, copyPasteCount, blurCount, timeSpentSec total, IP e user-agent. Um 🛡️ ShieldAlert aparece ao lado da nota do quiz no gradebook quando a tentativa esta flagged — ex.: tab switches acima do maximo configurado, tempo acima do limite, ou tentativas de copy/paste enquanto copy/paste esta bloqueado. O tempo medio por questao e derivado de timeSpentSec ÷ nº de questoes (nao e coluna armazenada). O tooltip mostra o motivo do flag. Professor decide acao (anular, manter, revisar).
Exportacao
GET /api/institution/gradebook/[courseId]/export retorna CSV com:
- Aluno
- Cada GradeItem como coluna
- Media ponderada (por categoria + drop-lowest)
Compativel com Google Sheets, Excel.