Changes include: - Updated architecture documentation - Enhanced module definitions (OQI-001 to OQI-008) - ML integration documentation updates - Trading strategies documentation - Orchestration and inventory updates - Docker configuration updates 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
418 lines
12 KiB
Markdown
418 lines
12 KiB
Markdown
---
|
|
id: "RF-EDU-004"
|
|
title: "Sistema de Quizzes"
|
|
type: "Requirement"
|
|
status: "Done"
|
|
priority: "Alta"
|
|
module: "education"
|
|
epic: "OQI-002"
|
|
version: "1.0"
|
|
created_date: "2025-12-05"
|
|
updated_date: "2026-01-04"
|
|
---
|
|
|
|
# RF-EDU-004: Sistema de Quizzes
|
|
|
|
**Versión:** 1.0.0
|
|
**Fecha:** 2025-12-05
|
|
**Épica:** OQI-002 - Módulo Educativo
|
|
**Prioridad:** P1
|
|
**Story Points:** 8
|
|
|
|
---
|
|
|
|
## Descripción
|
|
|
|
El sistema debe proporcionar un sistema completo de evaluaciones interactivas (quizzes) que permita validar el conocimiento adquirido por los usuarios, con soporte para múltiples tipos de preguntas, feedback inmediato, intentos limitados, calificaciones y análisis de resultados.
|
|
|
|
---
|
|
|
|
## Requisitos Funcionales
|
|
|
|
### RF-EDU-004.1: Tipos de Preguntas
|
|
|
|
El sistema debe soportar:
|
|
|
|
| Tipo | Descripción | Características |
|
|
|------|-------------|-----------------|
|
|
| **Multiple Choice** | Una respuesta correcta | 2-6 opciones |
|
|
| **Multiple Select** | Varias respuestas correctas | Checkbox, puntuación parcial |
|
|
| **True/False** | Verdadero o falso | 2 opciones |
|
|
| **Fill in the Blank** | Completar espacios | Input de texto, validación |
|
|
| **Matching** | Emparejar elementos | Drag & drop opcional |
|
|
| **Ordering** | Ordenar elementos | Secuencia correcta |
|
|
|
|
### RF-EDU-004.2: Estructura de Quiz
|
|
|
|
Cada quiz debe tener:
|
|
- Título y descripción
|
|
- Tiempo límite (opcional)
|
|
- Número de preguntas
|
|
- Puntuación mínima para aprobar (% o puntos)
|
|
- Número de intentos permitidos (ilimitado, 1, 2, 3...)
|
|
- Modo: Práctica (sin límite) o Evaluación (formal)
|
|
- Mostrar respuestas correctas: Inmediato, Al finalizar, Nunca
|
|
- Barajear preguntas (randomizar orden)
|
|
- Barajear opciones de respuesta
|
|
|
|
### RF-EDU-004.3: Interfaz de Quiz
|
|
|
|
El sistema debe mostrar:
|
|
- Contador de preguntas (Pregunta 1 de 10)
|
|
- Barra de progreso del quiz
|
|
- Timer countdown si hay límite de tiempo
|
|
- Pregunta actual con opciones
|
|
- Botones: "Anterior", "Siguiente", "Marcar para revisión"
|
|
- Navegador de preguntas (minimap con estado: respondida, marcada, pendiente)
|
|
- Botón "Finalizar quiz" (requiere confirmación)
|
|
- Auto-submit cuando expira el tiempo
|
|
|
|
### RF-EDU-004.4: Navegación y Estados
|
|
|
|
Estados de pregunta:
|
|
- **No respondida:** Sin respuesta seleccionada
|
|
- **Respondida:** Respuesta seleccionada
|
|
- **Marcada:** Flagged para revisión posterior
|
|
- **Correcta:** Solo visible después de submit (si configurado)
|
|
- **Incorrecta:** Solo visible después de submit (si configurado)
|
|
|
|
El usuario debe poder:
|
|
- Navegar libremente entre preguntas antes de submit
|
|
- Cambiar respuestas antes de finalizar
|
|
- Marcar preguntas para revisar después
|
|
- Ver resumen antes de enviar
|
|
|
|
### RF-EDU-004.5: Calificación y Resultados
|
|
|
|
Al finalizar el quiz, mostrar:
|
|
- Puntuación obtenida (X/Y puntos o %)
|
|
- Estado: Aprobado / Reprobado
|
|
- Tiempo invertido
|
|
- Feedback general basado en score
|
|
- Desglose por pregunta (si configurado):
|
|
- Pregunta
|
|
- Tu respuesta
|
|
- Respuesta correcta
|
|
- Explicación
|
|
- Intentos restantes
|
|
- Botón "Reintentar" si aplica
|
|
- Botón "Continuar al siguiente contenido"
|
|
|
|
### RF-EDU-004.6: Historial de Intentos
|
|
|
|
El sistema debe:
|
|
- Guardar todos los intentos del usuario
|
|
- Mostrar tabla con: fecha, puntuación, tiempo, estado
|
|
- Permitir ver detalle de intento anterior
|
|
- Mostrar mejor intento destacado
|
|
- Calcular promedio de intentos
|
|
- Guardar última puntuación como oficial
|
|
|
|
### RF-EDU-004.7: Feedback y Explicaciones
|
|
|
|
El sistema debe permitir:
|
|
- Explicación de respuesta correcta (markdown)
|
|
- Explicación de por qué otras opciones son incorrectas
|
|
- Links a recursos relacionados
|
|
- Video explicativo opcional
|
|
- Sugerencias de lecciones para repasar
|
|
|
|
### RF-EDU-004.8: Analítica de Quiz
|
|
|
|
Para cada pregunta, rastrear:
|
|
- Número de veces respondida
|
|
- Número de respuestas correctas
|
|
- Número de respuestas incorrectas
|
|
- Tasa de éxito global (%)
|
|
- Tiempo promedio de respuesta
|
|
- Opción más elegida (para detectar confusión)
|
|
|
|
Para cada quiz, rastrear:
|
|
- Número de intentos totales
|
|
- Tasa de aprobación (%)
|
|
- Puntuación promedio
|
|
- Tiempo promedio de completitud
|
|
- Pregunta más difícil (menor % acierto)
|
|
- Pregunta más fácil (mayor % acierto)
|
|
|
|
---
|
|
|
|
## Datos de Entrada
|
|
|
|
| Campo | Tipo | Descripción |
|
|
|-------|------|-------------|
|
|
| quizId | string | UUID del quiz |
|
|
| answers | object | Mapa de questionId -> respuesta |
|
|
|
|
---
|
|
|
|
## Datos de Salida
|
|
|
|
```typescript
|
|
interface Quiz {
|
|
id: string;
|
|
title: string;
|
|
description: string;
|
|
lessonId?: string;
|
|
courseId: string;
|
|
timeLimit?: number; // minutos
|
|
passingScore: number; // 0-100
|
|
maxAttempts: number; // 0 = ilimitado
|
|
questionCount: number;
|
|
totalPoints: number;
|
|
shuffleQuestions: boolean;
|
|
shuffleOptions: boolean;
|
|
showAnswers: 'immediate' | 'after_submit' | 'never';
|
|
mode: 'practice' | 'assessment';
|
|
}
|
|
|
|
interface Question {
|
|
id: string;
|
|
quizId: string;
|
|
type: 'multiple_choice' | 'multiple_select' | 'true_false' | 'fill_blank' | 'matching' | 'ordering';
|
|
question: string; // Markdown
|
|
points: number;
|
|
order: number;
|
|
|
|
// Para multiple choice/select
|
|
options?: {
|
|
id: string;
|
|
text: string;
|
|
isCorrect: boolean;
|
|
explanation?: string;
|
|
}[];
|
|
|
|
// Para fill in the blank
|
|
correctAnswers?: string[];
|
|
caseSensitive?: boolean;
|
|
|
|
// Para matching
|
|
pairs?: {
|
|
left: string;
|
|
right: string;
|
|
}[];
|
|
|
|
// Para ordering
|
|
correctOrder?: string[];
|
|
|
|
explanation?: string; // Explicación general
|
|
hint?: string;
|
|
relatedResources?: {
|
|
type: 'lesson' | 'article' | 'video';
|
|
id: string;
|
|
title: string;
|
|
}[];
|
|
}
|
|
|
|
interface QuizAttempt {
|
|
id: string;
|
|
quizId: string;
|
|
userId: string;
|
|
attemptNumber: number;
|
|
startedAt: string;
|
|
submittedAt?: string;
|
|
timeSpent: number; // segundos
|
|
|
|
answers: {
|
|
questionId: string;
|
|
userAnswer: any;
|
|
isCorrect: boolean;
|
|
pointsEarned: number;
|
|
}[];
|
|
|
|
score: number; // 0-100
|
|
pointsEarned: number;
|
|
totalPoints: number;
|
|
passed: boolean;
|
|
|
|
analytics: {
|
|
questionsCorrect: number;
|
|
questionsIncorrect: number;
|
|
questionsSkipped: number;
|
|
avgTimePerQuestion: number;
|
|
};
|
|
}
|
|
|
|
interface QuizResults {
|
|
attempt: QuizAttempt;
|
|
quiz: Quiz;
|
|
questions: (Question & {
|
|
userAnswer: any;
|
|
isCorrect: boolean;
|
|
pointsEarned: number;
|
|
})[];
|
|
feedback: {
|
|
title: string;
|
|
message: string;
|
|
suggestions?: string[];
|
|
};
|
|
attemptsRemaining: number;
|
|
canRetake: boolean;
|
|
nextContent?: {
|
|
type: 'lesson' | 'quiz' | 'module';
|
|
id: string;
|
|
title: string;
|
|
};
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Reglas de Negocio
|
|
|
|
1. **Puntuación mínima:** Default 70% para aprobar
|
|
2. **Intentos:** Si se agotan intentos, usuario debe esperar 24h o contactar soporte
|
|
3. **Tiempo límite:** Si expira, se auto-submit con respuestas actuales
|
|
4. **Preguntas obligatorias:** No se puede submit sin responder todas (modo evaluación)
|
|
5. **Modo práctica:** Sin límite de intentos, sin guardar en historial oficial
|
|
6. **Partial credit:** Multiple select otorga puntos parciales (50% si elige 2/4 correctas)
|
|
7. **Shuffle:** Si está activado, orden diferente en cada intento
|
|
8. **Feedback inmediato:** Solo en modo práctica
|
|
9. **Certificación:** Quiz final de curso debe aprobarse para certificado
|
|
|
|
---
|
|
|
|
## Criterios de Aceptación
|
|
|
|
```gherkin
|
|
Escenario: Usuario inicia quiz
|
|
DADO que el usuario está en una lección con quiz
|
|
CUANDO hace click en "Iniciar quiz"
|
|
ENTONCES se muestra pantalla de introducción del quiz
|
|
Y se muestra título, descripción, número de preguntas
|
|
Y se muestra tiempo límite si aplica
|
|
Y se muestra intentos disponibles
|
|
Y se muestra puntuación requerida para aprobar
|
|
Y se muestra botón "Comenzar"
|
|
|
|
Escenario: Usuario responde preguntas
|
|
DADO que el usuario comenzó el quiz
|
|
CUANDO selecciona una respuesta
|
|
ENTONCES la opción se marca como seleccionada
|
|
Y la pregunta se marca como "respondida"
|
|
Y puede navegar a siguiente pregunta
|
|
Y puede volver a preguntas anteriores
|
|
Y puede cambiar respuesta antes de submit
|
|
|
|
Escenario: Usuario finaliza quiz exitosamente
|
|
DADO que el usuario respondió todas las preguntas
|
|
CUANDO hace click en "Finalizar quiz"
|
|
Y confirma en el modal
|
|
ENTONCES se calcula la puntuación
|
|
Y se muestra pantalla de resultados
|
|
Y se muestra "Aprobado" si score >= passing score
|
|
Y se desbloquea siguiente contenido
|
|
Y se otorga XP por aprobar
|
|
|
|
Escenario: Usuario reprueba quiz
|
|
DADO que el usuario envió el quiz
|
|
Y la puntuación es < passing score
|
|
ENTONCES se muestra pantalla de resultados
|
|
Y se muestra "Reprobado"
|
|
Y se muestra feedback con áreas a mejorar
|
|
Y se muestra "Intentos restantes: X"
|
|
Y se muestra botón "Reintentar"
|
|
Y siguiente contenido permanece bloqueado
|
|
|
|
Escenario: Quiz con tiempo límite expira
|
|
DADO que el quiz tiene tiempo límite de 30 minutos
|
|
Y el usuario está en la pregunta 5 de 10
|
|
CUANDO el tiempo llega a 0
|
|
ENTONCES el quiz se envía automáticamente
|
|
Y se califica con respuestas hasta el momento
|
|
Y preguntas sin responder cuentan como incorrectas
|
|
|
|
Escenario: Ver explicación de respuestas
|
|
DADO que el quiz permite ver respuestas
|
|
Y el usuario envió el quiz
|
|
CUANDO ve los resultados
|
|
ENTONCES se muestran todas las preguntas
|
|
Y se destacan respuestas correctas en verde
|
|
Y se destacan respuestas incorrectas en rojo
|
|
Y se muestra explicación de cada respuesta
|
|
Y se muestran recursos relacionados
|
|
|
|
Escenario: Reintentar quiz
|
|
DADO que el usuario reprobó un quiz
|
|
Y tiene intentos disponibles
|
|
CUANDO hace click en "Reintentar"
|
|
ENTONCES se inicia nuevo intento
|
|
Y preguntas pueden estar en diferente orden
|
|
Y respuestas anteriores no están pre-seleccionadas
|
|
Y contador de intentos se decrementa
|
|
```
|
|
|
|
---
|
|
|
|
## Dependencias
|
|
|
|
- PostgreSQL para quizzes y resultados
|
|
- Redis para cachear quizzes activos
|
|
- WebSocket para timer en tiempo real (opcional)
|
|
|
|
---
|
|
|
|
## Notas Técnicas
|
|
|
|
- Implementar auto-save cada 30s para evitar pérdida de progreso
|
|
- Usar WebSockets para sincronizar timer entre tabs
|
|
- Encriptar respuestas correctas en frontend
|
|
- Validar respuestas en backend (nunca confiar en frontend)
|
|
- Implementar rate limiting para prevenir brute force
|
|
- Usar optimistic updates para mejor UX
|
|
- Considerar adaptive quizzes (ajustar dificultad según respuestas)
|
|
|
|
---
|
|
|
|
## Referencias
|
|
|
|
- Schema: `/backend/src/database/schemas/education.sql`
|
|
- API: `/backend/src/modules/courses/quizzes.routes.ts`
|
|
- Frontend: `/frontend/src/pages/QuizPlayer.tsx`
|
|
|
|
---
|
|
|
|
## Tareas Técnicas
|
|
|
|
**Database:**
|
|
- [ ] Tabla education.quizzes
|
|
- [ ] Tabla education.questions con FK a quiz
|
|
- [ ] Tabla education.question_options
|
|
- [ ] Tabla education.quiz_attempts
|
|
- [ ] Tabla education.quiz_answers
|
|
- [ ] Índices para queries por usuario y quiz
|
|
|
|
**Backend:**
|
|
- [ ] Endpoint GET /education/quizzes/:id (sin respuestas correctas)
|
|
- [ ] Endpoint POST /education/quizzes/:id/start
|
|
- [ ] Endpoint POST /education/quizzes/:id/submit
|
|
- [ ] Endpoint GET /education/quizzes/:id/attempts (historial)
|
|
- [ ] Endpoint GET /education/quizzes/:id/results/:attemptId
|
|
- [ ] Implementar QuizService.gradeAttempt()
|
|
- [ ] Implementar shuffle de preguntas y opciones
|
|
- [ ] Rate limiting en submit
|
|
|
|
**Frontend:**
|
|
- [ ] Crear QuizIntroPage.tsx
|
|
- [ ] Crear QuizPlayerPage.tsx
|
|
- [ ] Crear componente QuestionRenderer.tsx (soporta todos los tipos)
|
|
- [ ] Crear componente QuizNavigator.tsx
|
|
- [ ] Crear componente QuizTimer.tsx
|
|
- [ ] Crear QuizResultsPage.tsx
|
|
- [ ] Crear componente QuestionExplanation.tsx
|
|
- [ ] Auto-save de respuestas cada 30s
|
|
- [ ] Implementar quizStore
|
|
- [ ] Confirmación antes de salir (window.onbeforeunload)
|
|
|
|
**Tests:**
|
|
- [ ] Test calificación de quiz con diferentes tipos de preguntas
|
|
- [ ] Test partial credit en multiple select
|
|
- [ ] Test expiración de tiempo
|
|
- [ ] Test E2E completar quiz y aprobar
|
|
|
|
---
|
|
|
|
**Creado por:** Requirements-Analyst
|
|
**Fecha:** 2025-12-05
|
|
**Última actualización:** 2025-12-05
|