Historico de quizzes e inicio de atualização da IA para leitura de pdfs de matemática (incompleto)

This commit is contained in:
2026-05-20 01:32:37 +01:00
parent 80ed2b1346
commit 98dcd621c7
12 changed files with 1539 additions and 271 deletions

View File

@@ -164,15 +164,67 @@ class _TeacherQuizPageState extends State<TeacherQuizPage>
}
}
/// Detect if a material is mathematics-based
bool _isMathematicsSubject(Map<String, String> material) {
final matName = (material['name'] ?? '').toLowerCase();
final classId = material['classId'];
String className = '';
// Get class name if classId is available
if (classId != null && _classNamesMap.containsKey(classId)) {
className = _classNamesMap[classId]!.toLowerCase();
}
// Keywords for mathematics
final mathKeywords = [
'matemática',
'math',
'álgebra',
'geometria',
'cálculo',
'estatística',
'trigonometria',
'função',
'equação',
'fração',
'raiz',
'potência',
'derivada',
'integral',
'número',
'gráfico',
'fórmula',
'matriz',
'vetor',
'probabilidade',
'percentagem',
'ângulo',
'triângulo',
'quadrado',
'círculo',
'volume',
'área',
'perímetro',
];
// Check if material name or class name contains math keywords
final combinedText = '$matName $className';
return mathKeywords.any((keyword) => combinedText.contains(keyword));
}
Future<void> _generateQuiz(Map<String, String> material) async {
setState(() => _generatingForId = material['id']);
try {
final matId = material['id']!;
final matName = material['name'] ?? 'Material';
final isMathematics = _isMathematicsSubject(material);
final pdfContext = await MaterialsRAGService.getRelevantChunks(
userQuery: 'conteúdo geral resumo tópicos principais',
userQuery: 'todos os exercícios todos os tópicos completo',
selectedMaterialIds: [matId],
maxChunks: 20, // Aumentar para cobrir todo o documento
filterTableData:
isMathematics, // Filtrar dados de tabela para matemática
);
if (pdfContext.isEmpty) {
@@ -180,7 +232,10 @@ class _TeacherQuizPageState extends State<TeacherQuizPage>
return;
}
final numQuestions = 5 + Random().nextInt(16); // 5..20
final numQuestions = isMathematics
? 10 +
Random().nextInt(11) // 10..20 para matemática
: 5 + Random().nextInt(16); // 5..20 para outras matérias
final prompt =
'Usa APENAS o seguinte contexto para criar um quiz. Não uses conhecimento externo.\n\n'
'$pdfContext\n\n'
@@ -190,7 +245,10 @@ class _TeacherQuizPageState extends State<TeacherQuizPage>
'[{"q":"Pergunta aqui","opts":["A) opção","B) opção","C) opção","D) opção"],"ans":0,"exp":"Explicação breve da resposta correcta"},...}]\n'
'ans é o índice (0-3) da opção correcta.';
final raw = await RAGAIService.generateQuiz(prompt);
final raw = await RAGAIService.generateQuiz(
prompt,
isMathematics: isMathematics,
);
final questions = _parseQuizJson(raw);
if (questions.isEmpty) {
@@ -358,6 +416,73 @@ class _TeacherQuizPageState extends State<TeacherQuizPage>
return groups;
}
Future<void> _deleteQuiz(String quizId, String quizTitle) async {
final confirmed = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text('Eliminar Quiz'),
content: Text('Tem certeza que deseja eliminar o quiz "$quizTitle"?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: const Text('Cancelar'),
),
TextButton(
onPressed: () => Navigator.pop(context, true),
style: TextButton.styleFrom(foregroundColor: Colors.red),
child: const Text('Eliminar'),
),
],
),
);
if (confirmed != true) return;
try {
final user = FirebaseAuth.instance.currentUser;
if (user == null) return;
// Delete from teacherQuizzes
await FirebaseFirestore.instance
.collection('teacherQuizzes')
.doc(quizId)
.delete();
// Also delete from student history
final historySnapshot = await FirebaseFirestore.instance
.collection('quizHistory')
.where('quizId', isEqualTo: quizId)
.get();
for (final doc in historySnapshot.docs) {
await doc.reference.delete();
}
setState(() {
_history.removeWhere((item) => item['id'] == quizId);
});
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Quiz eliminado com sucesso!'),
backgroundColor: Colors.green,
),
);
}
} catch (e) {
Logger.error('Error deleting quiz: $e');
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Erro ao eliminar: $e'),
backgroundColor: Colors.red,
),
);
}
}
}
Widget _buildHistoryQuizTile(Map<String, dynamic> item, ColorScheme cs) {
final name = (item['materialName'] as String? ?? 'Material')
.replaceAll('.pdf', '')
@@ -409,7 +534,19 @@ class _TeacherQuizPageState extends State<TeacherQuizPage>
style: TextStyle(fontSize: 12, color: cs.onSurfaceVariant),
)
: null,
trailing: Icon(Icons.bar_chart, color: cs.onSurfaceVariant),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.bar_chart, color: cs.onSurfaceVariant),
const SizedBox(width: 8),
IconButton(
icon: Icon(Icons.delete_outline, color: Colors.red),
onPressed: () =>
_deleteQuiz(item['id'], item['materialName'] ?? 'Quiz'),
tooltip: 'Eliminar Quiz',
),
],
),
onTap: () => _showResultsPopup(item),
),
);