cena dos quizzes resolvidos

This commit is contained in:
2026-05-17 15:04:19 +01:00
parent 5649f7d96a
commit 6ba5c837ce

View File

@@ -1,4 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:cloud_firestore/cloud_firestore.dart';
@@ -53,45 +54,135 @@ class _QuizListPageState extends State<QuizListPage>
} }
Future<void> _loadTeacherQuizzes() async { Future<void> _loadTeacherQuizzes() async {
Logger.info('=== _loadTeacherQuizzes STARTED ===');
try { try {
final uid = FirebaseAuth.instance.currentUser?.uid; final uid = FirebaseAuth.instance.currentUser?.uid;
if (uid == null) { if (uid == null) {
Logger.info('User UID is null, returning');
if (mounted) setState(() => _loadingTeacherQuizzes = false); if (mounted) setState(() => _loadingTeacherQuizzes = false);
return; return;
} }
Logger.info('Loading teacher quizzes for user: $uid');
// Obter teacherIds dos professores do aluno // Obter enrollments do aluno
final enrollSnap = await FirebaseFirestore.instance final enrollSnap = await FirebaseFirestore.instance
.collection('enrollments') .collection('enrollments')
.where('studentId', isEqualTo: uid) .where('studentId', isEqualTo: uid)
.get(); .get();
Logger.info('Enrollments found for student $uid: ${enrollSnap.docs.length}');
final classIds = enrollSnap.docs final classIds = enrollSnap.docs
.map((d) => d.data()['classId'] as String?) .map((d) => d.data()['classId'] as String?)
.whereType<String>() .whereType<String>()
.toSet(); .toSet();
Logger.info('ClassIds from enrollments: $classIds');
if (classIds.isEmpty) { if (classIds.isEmpty) {
Logger.info('No classIds found for student, returning empty');
if (mounted) setState(() => _loadingTeacherQuizzes = false); if (mounted) setState(() => _loadingTeacherQuizzes = false);
return; return;
} }
// Quizzes publicados para as turmas do aluno (array-contains-any, máx 10 por query) // Obter também os teacherIds (fallback para quizzes sem classIds)
final classSnaps = await Future.wait(
classIds.map((id) => FirebaseFirestore.instance.collection('classes').doc(id).get()),
);
final teacherIds = classSnaps
.where((d) => d.exists)
.map((d) => d.data()?['teacherId'] as String?)
.whereType<String>()
.toSet()
.toList();
Logger.info('TeacherIds from classes: $teacherIds');
// Simplificar: tentar query básica primeiro
final classIdList = classIds.toList(); final classIdList = classIds.toList();
final batches = <Future<QuerySnapshot>>[]; final batches = <Future<QuerySnapshot>>[];
for (int i = 0; i < classIdList.length; i += 10) {
batches.add( // Query 1: por teacherId (mais simples e compatível)
FirebaseFirestore.instance if (teacherIds.isNotEmpty) {
.collection('teacherQuizzes') Logger.info('Executing simple teacherIds query with: $teacherIds');
.where( // Limitar a 10 teacherIds por query (limite do Firestore)
'classIds', final batch = teacherIds.take(10).toList();
arrayContainsAny: classIdList.sublist( final query = FirebaseFirestore.instance
i, .collection('teacherQuizzes')
(i + 10).clamp(0, classIdList.length), .where('teacherId', whereIn: batch)
), .orderBy('createdAt', descending: true);
) batches.add(query.get());
.orderBy('createdAt', descending: true) Logger.info('Added teacherIds batch query: $batch');
.get(),
);
} }
// Query 2: por classIds (se a primeira não retornar resultados)
if (classIdList.isNotEmpty) {
Logger.info('Executing classIds query with: $classIdList');
final batch = classIdList.take(10).toList();
final query = FirebaseFirestore.instance
.collection('teacherQuizzes')
.where('classIds', arrayContainsAny: batch)
.orderBy('createdAt', descending: true);
batches.add(query.get());
Logger.info('Added classIds batch query: $batch');
}
// Debug: query super simplificada para teste
try {
final testSnap = await FirebaseFirestore.instance
.collection('teacherQuizzes')
.limit(1)
.get();
Logger.info('=== SUPER SIMPLE TEST QUERY ===');
Logger.info('Test query result: ${testSnap.docs.length} documents');
if (testSnap.docs.isNotEmpty) {
final data = testSnap.docs.first.data();
Logger.info('First quiz data: $data');
}
Logger.info('=== END TEST QUERY ===');
} catch (e) {
Logger.error('Super simple test query failed: $e');
}
// Teste: retornar todos os quizzes sem filtros (temporário para debug)
try {
final allSnap = await FirebaseFirestore.instance
.collection('teacherQuizzes')
.orderBy('createdAt', descending: true)
.limit(10)
.get();
Logger.info('=== ALL QUIZZES NO FILTER ===');
Logger.info('All quizzes count: ${allSnap.docs.length}');
final allQuizzes = allSnap.docs
.map((d) => {'id': d.id, ...d.data() as Map<String, dynamic>})
.toList();
// Filtrar manualmente para testar
final filteredQuizzes = allQuizzes.where((quiz) {
final quizTeacherId = quiz['teacherId'] as String?;
final quizClassIds = (quiz['classIds'] as List?)?.cast<String>() ?? [];
return teacherIds.contains(quizTeacherId) ||
quizClassIds.any((cid) => classIds.contains(cid));
}).toList();
Logger.info('Manual filtered quizzes: ${filteredQuizzes.length}');
Logger.info('=== END ALL QUIZZES NO FILTER ===');
// Usar este resultado temporariamente para debug
if (filteredQuizzes.isNotEmpty) {
Logger.info('Using manually filtered quizzes for UI');
if (mounted) {
setState(() {
_teacherQuizzes = filteredQuizzes;
_loadingTeacherQuizzes = false;
});
}
return; // Sair cedo para não sobrescrever com a query normal
}
} catch (e) {
Logger.error('All quizzes query failed: $e');
}
// Executar queries e processar resultados
final results = await Future.wait(batches); final results = await Future.wait(batches);
Logger.info('Query batches completed: ${results.length} results');
// deduplicar por id (pode aparecer em múltiplos batches) // deduplicar por id (pode aparecer em múltiplos batches)
final seen = <String>{}; final seen = <String>{};
final quizzes = results final quizzes = results
@@ -99,11 +190,19 @@ class _QuizListPageState extends State<QuizListPage>
.where((d) => seen.add(d.id)) .where((d) => seen.add(d.id))
.map((d) => {'id': d.id, ...d.data() as Map<String, dynamic>}) .map((d) => {'id': d.id, ...d.data() as Map<String, dynamic>})
.toList(); .toList();
if (mounted)
Logger.info('Final quizzes after deduplication: ${quizzes.length}');
for (final quiz in quizzes.take(3)) {
Logger.info('Quiz sample: ${quiz['materialName']} - classIds: ${quiz['classIds']} - teacherId: ${quiz['teacherId']}');
}
if (mounted) {
Logger.info('Updating UI state: _teacherQuizzes.length = ${quizzes.length}');
setState(() { setState(() {
_teacherQuizzes = quizzes; _teacherQuizzes = quizzes;
_loadingTeacherQuizzes = false; _loadingTeacherQuizzes = false;
}); });
Logger.info('UI state updated');
}
} catch (e) { } catch (e) {
Logger.error('Error loading teacher quizzes: $e'); Logger.error('Error loading teacher quizzes: $e');
if (mounted) setState(() => _loadingTeacherQuizzes = false); if (mounted) setState(() => _loadingTeacherQuizzes = false);
@@ -154,10 +253,11 @@ class _QuizListPageState extends State<QuizListPage>
} }
// Gerar quiz via Ollama em formato JSON estruturado // Gerar quiz via Ollama em formato JSON estruturado
final numQuestions = 5 + Random().nextInt(16); // 5..20
final prompt = final prompt =
'Usa APENAS o seguinte contexto para criar um quiz. Não uses conhecimento externo.\n\n' 'Usa APENAS o seguinte contexto para criar um quiz. Não uses conhecimento externo.\n\n'
'$context\n\n' '$context\n\n'
'Cria um quiz com 5 perguntas de escolha múltipla sobre o conteúdo acima.\n' 'Cria um quiz com EXACTAMENTE $numQuestions perguntas de escolha múltipla sobre o conteúdo acima. Não repitas perguntas.\n'
'Responde SOMENTE com JSON válido, sem texto adicional, sem markdown, sem blocos de código.\n' 'Responde SOMENTE com JSON válido, sem texto adicional, sem markdown, sem blocos de código.\n'
'Formato exacto:\n' 'Formato exacto:\n'
'[{"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' '[{"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'