Modelo
GradeCategory (peso, drop_lowest_n, late_penalty_pct)
└── GradeItem (atividade — quiz, assignment, manual)
└── Grade (nota por aluno)
└── Rubric (opcional — criterios)
Calculo
courseGrade = Σ (categoryAvg × categoryWeight) / totalWeight
categoryAvg = average(itemAvgs após drop_lowest)
itemAvg = Σ studentGrades / itemMaxScore × 100
Late penalty aplicado se submittedAt > dueDate (configurable por categoria).
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
Inline no gradebook ao lado de cada nota de quiz:
- 🛡️ ShieldAlert vermelho → tabSwitchCount > 5 OU avgTimePerQuestion < 2s
- 🛡️ ShieldAlert amarelo → 2-5 tab switches OU 2-5s por questao
- Tooltip detalha: "Tab switches: 8, time per question: 1.2s avg"
Professor decide acao (anular, manter, revisar).
Exportacao
GET /api/institution/courses/[id]/gradebook/export?format=csv retorna CSV com:
- Aluno
- Cada GradeItem como coluna (nota raw + nota ponderada)
- Total final
- Status (passing/failing baseado em passingScore)
Compativel com Google Sheets, Excel.