- Configure workspace Git repository with comprehensive .gitignore - Add Odoo as submodule for ERP reference code - Include documentation: SETUP.md, GIT-STRUCTURE.md - Add gitignore templates for projects (backend, frontend, database) - Structure supports independent repos per project/subproject level Workspace includes: - core/ - Reusable patterns, modules, orchestration system - projects/ - Active projects (erp-suite, gamilit, trading-platform, etc.) - knowledge-base/ - Reference code and patterns (includes Odoo submodule) - devtools/ - Development tools and templates - customers/ - Client implementations template 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
390 lines
11 KiB
Markdown
390 lines
11 KiB
Markdown
# Arquitectura Dual: Sistema de Ejercicios
|
|
|
|
**Fecha:** 2025-11-24
|
|
**Versión:** 2.5.3
|
|
**Tipo:** Arquitectura + Bug Fix Crítico
|
|
**Prioridad:** P0 - CRÍTICO
|
|
|
|
---
|
|
|
|
## 📋 Resumen Ejecutivo
|
|
|
|
**Problema:** Sistema bloqueaba reenvíos de ejercicios después del primer intento exitoso, impidiendo práctica ilimitada.
|
|
|
|
**Solución:** Arquitectura dual que separa ejercicios autocorregibles (práctica) de ejercicios de revisión manual (evaluación formal).
|
|
|
|
**Resultado:**
|
|
- ✅ Reenvíos ilimitados permitidos
|
|
- ✅ Anti-farming implementado (XP solo en primer acierto)
|
|
- ✅ Sin duplicación de datos ni XP
|
|
|
|
---
|
|
|
|
## 🏗️ Arquitectura Implementada
|
|
|
|
```
|
|
┌─────────────────────────────────────────┐
|
|
│ EJERCICIO COMPLETADO POR ESTUDIANTE │
|
|
└─────────────────┬───────────────────────┘
|
|
│
|
|
▼
|
|
¿requires_manual_grading?
|
|
│
|
|
┌─────────┴─────────┐
|
|
│ │
|
|
FALSE TRUE
|
|
(práctica) (evaluación)
|
|
│ │
|
|
▼ ▼
|
|
┌─────────────────┐ ┌──────────────────┐
|
|
│exercise_attempts│ │exercise_ │
|
|
│ │ │submissions │
|
|
├─────────────────┤ ├──────────────────┤
|
|
│• Reintentos ∞ │ │• 1 entrega │
|
|
│• XP 1ra vez │ │• Espera revisión │
|
|
│• Trigger auto │ │• XP al calificar │
|
|
└─────────────────┘ └──────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 📂 Archivos Modificados
|
|
|
|
### Database
|
|
|
|
**Migraciones:**
|
|
- `migrations/2025-11-24-add-requires-manual-grading.sql`
|
|
- Agrega columna `requires_manual_grading` a `educational_content.exercises`
|
|
- Clasifica 15 ejercicios existentes como autocorregibles
|
|
- Crea índice de performance
|
|
|
|
- `migrations/2025-11-24-cleanup-incorrect-submissions.sql`
|
|
- Elimina 8 registros legacy incorrectos
|
|
- Limpia `exercise_submissions` de ejercicios autocorregibles
|
|
|
|
**Ubicación histórica:**
|
|
- `docs/historical-migrations/2025-11-24-add-requires-manual-grading.sql`
|
|
- `docs/historical-migrations/2025-11-24-cleanup-incorrect-submissions.sql`
|
|
|
|
### Backend
|
|
|
|
**Entity:**
|
|
- `apps/backend/src/modules/educational/entities/exercise.entity.ts`
|
|
- Line 202: Campo `requires_manual_grading: boolean`
|
|
|
|
**Service:**
|
|
- `apps/backend/src/modules/progress/services/exercise-submission.service.ts`
|
|
- Lines 199-236: Validación de tipo + single submission enforcement
|
|
|
|
**Controller:**
|
|
- `apps/backend/src/modules/educational/controllers/exercises.controller.ts`
|
|
- Lines 16-17: Imports (Connection, InjectConnection)
|
|
- Lines 45-46: Constructor injection
|
|
- Lines 840-938: Arquitectura dual completa con anti-farming
|
|
|
|
---
|
|
|
|
## 🔄 Flujo de Ejercicios Autocorregibles
|
|
|
|
### 1. Usuario envía respuesta
|
|
```http
|
|
POST /api/v1/educational/exercises/:id/complete
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"answers": { ... },
|
|
"startedAt": 1234567890,
|
|
"hintsUsed": 0,
|
|
"powerupsUsed": []
|
|
}
|
|
```
|
|
|
|
### 2. Backend valida tipo
|
|
```typescript
|
|
const exercise = await this.exercisesService.findById(exerciseId);
|
|
|
|
if (exercise.requires_manual_grading) {
|
|
// Ruta de revisión manual
|
|
return await this.exerciseSubmissionService.submitExercise(...);
|
|
}
|
|
|
|
// Continúa con flujo autocorregible...
|
|
```
|
|
|
|
### 3. Validación con PostgreSQL
|
|
```typescript
|
|
const validationResult = await this.connection.query(`
|
|
SELECT * FROM educational_content.validate_and_audit(
|
|
$1::UUID, $2::UUID, $3::JSONB
|
|
)
|
|
`, [exerciseId, profileId, JSON.stringify(submittedAnswers)]);
|
|
|
|
const { score, is_correct, feedback } = validationResult[0];
|
|
```
|
|
|
|
### 4. Anti-Farming: Verificar primer acierto
|
|
```typescript
|
|
const previousAttempts = await this.exerciseAttemptService
|
|
.findByUserAndExercise(profileId, exerciseId);
|
|
|
|
const hasCorrectAttemptBefore = previousAttempts
|
|
.some((attempt: any) => attempt.is_correct);
|
|
|
|
const isFirstCorrectAttempt = !hasCorrectAttemptBefore && isCorrect;
|
|
|
|
// XP solo en primer acierto
|
|
let xpEarned = isFirstCorrectAttempt ? exercise.xp_reward : 0;
|
|
let mlCoinsEarned = isFirstCorrectAttempt ? exercise.ml_coins_reward : 0;
|
|
```
|
|
|
|
### 5. Crear attempt
|
|
```typescript
|
|
await this.exerciseAttemptService.create({
|
|
user_id: profileId,
|
|
exercise_id: exerciseId,
|
|
submitted_answers: submittedAnswers,
|
|
is_correct: isCorrect,
|
|
score: score,
|
|
xp_earned: xpEarned,
|
|
ml_coins_earned: mlCoinsEarned,
|
|
time_spent_seconds: timeSpent,
|
|
hints_used: hintsUsed,
|
|
comodines_used: powerupsUsed,
|
|
});
|
|
|
|
// Trigger automático: trg_update_user_stats_on_exercise
|
|
// Actualiza: gamification_system.user_stats
|
|
```
|
|
|
|
### 6. Respuesta al Frontend
|
|
```json
|
|
{
|
|
"score": 100,
|
|
"isPerfect": true,
|
|
"rewards": {
|
|
"xp": 100,
|
|
"mlCoins": 20,
|
|
"bonuses": []
|
|
},
|
|
"feedback": "Excelente trabajo...",
|
|
"isFirstCorrectAttempt": true,
|
|
"rankUp": null
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 Datos Actuales
|
|
|
|
### Ejercicios por Módulo
|
|
|
|
| Módulo | Ejercicios | XP Total | Tipo |
|
|
|--------|------------|----------|------|
|
|
| Módulo 1 | 5 | 500 XP | Autocorregible |
|
|
| Módulo 2 | 5 | 500 XP | Autocorregible |
|
|
| Módulo 3 | 5 | 500 XP | Autocorregible |
|
|
| **Total** | **15** | **1,500 XP** | **100% autocorregibles** |
|
|
|
|
### Estado de Tablas
|
|
|
|
```sql
|
|
-- exercise_attempts (práctica)
|
|
SELECT COUNT(*) FROM progress_tracking.exercise_attempts;
|
|
-- Result: 7 registros (usuarios reales practicando)
|
|
|
|
-- exercise_submissions (evaluación formal)
|
|
SELECT COUNT(*) FROM progress_tracking.exercise_submissions;
|
|
-- Result: 0 registros (ningún ejercicio de revisión manual aún)
|
|
|
|
-- Validación: NO debe haber autocorregibles en submissions
|
|
SELECT COUNT(*)
|
|
FROM progress_tracking.exercise_submissions es
|
|
JOIN educational_content.exercises e ON e.id = es.exercise_id
|
|
WHERE e.requires_manual_grading = false;
|
|
-- Result: 0 ✅ (Limpieza exitosa)
|
|
```
|
|
|
|
---
|
|
|
|
## 🧪 Testing
|
|
|
|
### Script de Validación
|
|
```bash
|
|
cd /home/isem/workspace/workspace-gamilit/gamilit/projects/gamilit/apps/database
|
|
./test-exercise-resubmission.sh
|
|
```
|
|
|
|
### Tests Automatizados
|
|
|
|
| # | Test | Estado |
|
|
|---|------|--------|
|
|
| 1 | Columna `requires_manual_grading` existe | ✅ |
|
|
| 2 | 15 ejercicios clasificados correctamente | ✅ |
|
|
| 3 | Usuario de prueba configurado | ✅ |
|
|
| 4 | 10 ejercicios disponibles (M2 y M3) | ✅ |
|
|
| 5 | Historial limpio | ✅ |
|
|
| 6 | 0 registros incorrectos en submissions | ✅ |
|
|
|
|
### Testing Manual Pendiente
|
|
|
|
**Usuario de prueba:**
|
|
- ID: `bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb`
|
|
- Email: `teacher@gamilit.com`
|
|
- Estado: Level 1, Rank Ajaw, 0 XP, 100 ML Coins
|
|
|
|
**Ejercicio sugerido:**
|
|
- Módulo 2, Ejercicio 1: "Detective Textual: El Misterio de la Radiación"
|
|
- ID: `b842db8f-08c5-4413-8920-6a8b5f61fc22`
|
|
- Recompensas: 100 XP, 20 ML Coins
|
|
|
|
**Pasos:**
|
|
1. ✅ Completar ejercicio → Verificar +100 XP
|
|
2. ✅ Reintentar mismo ejercicio → Verificar reenvío permitido
|
|
3. ✅ Segunda respuesta correcta → Verificar +0 XP (anti-farming)
|
|
4. ✅ Verificar en DB: solo tabla `exercise_attempts` usada
|
|
|
|
**Validación SQL:**
|
|
```sql
|
|
-- Ver intentos del estudiante
|
|
SELECT
|
|
e.title,
|
|
ea.is_correct,
|
|
ea.score,
|
|
ea.xp_earned,
|
|
ea.submitted_at
|
|
FROM progress_tracking.exercise_attempts ea
|
|
JOIN educational_content.exercises e ON e.id = ea.exercise_id
|
|
WHERE ea.user_id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'
|
|
ORDER BY ea.submitted_at DESC;
|
|
|
|
-- Ver XP total del estudiante
|
|
SELECT total_xp, level, current_rank, ml_coins
|
|
FROM gamification_system.user_stats
|
|
WHERE user_id = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb';
|
|
```
|
|
|
|
---
|
|
|
|
## 📈 Métricas de Impacto
|
|
|
|
### Antes del Fix
|
|
|
|
| Métrica | Valor | Estado |
|
|
|---------|-------|--------|
|
|
| Reenvíos permitidos | ❌ No | Bloqueante |
|
|
| Duplicación de registros | ✅ Sí | Bug crítico |
|
|
| XP duplicado | ✅ Sí | Bug crítico |
|
|
| XP farming | ✅ Posible | Explotable |
|
|
|
|
### Después del Fix
|
|
|
|
| Métrica | Valor | Estado |
|
|
|---------|-------|--------|
|
|
| Reenvíos permitidos | ✅ Ilimitados | Funcional |
|
|
| Duplicación de registros | ❌ No | Corregido |
|
|
| XP duplicado | ❌ No | Corregido |
|
|
| XP farming | ❌ Bloqueado | Corregido |
|
|
|
|
---
|
|
|
|
## 🔗 Referencias
|
|
|
|
### Documentación Completa
|
|
|
|
**Ubicación:** `/orchestration/agentes/architecture-analyst/analisis-sistema-xp-rangos-2025-11-24/`
|
|
|
|
**Documentos (27,000+ palabras):**
|
|
|
|
1. **MATRIZ-IMPACTO-Y-DEPENDENCIAS.md** (9,000+ palabras)
|
|
- Análisis de dependencias backend/frontend
|
|
- 5 conflictos críticos identificados
|
|
- Planes de mitigación por conflicto
|
|
|
|
2. **SOLUCION-DEFINITIVA-EJERCICIOS-REENVIOS.md** (13,000+ palabras)
|
|
- Especificación técnica completa
|
|
- Diagramas de flujo detallados
|
|
- Casos de uso exhaustivos
|
|
|
|
3. **RESUMEN-IMPLEMENTACION-2025-11-24.md**
|
|
- Comparación código antes/después
|
|
- Scripts de testing documentados
|
|
- Métricas de validación
|
|
|
|
4. **STATUS-FINAL-2025-11-24.md**
|
|
- Estado post-implementación
|
|
- Checklist completo (15 items)
|
|
- Plan de testing manual paso a paso
|
|
|
|
### Código Relacionado
|
|
|
|
**Database:**
|
|
- DDL: `apps/database/ddl/schemas/educational_content/tables/02-exercises.sql`
|
|
- Función: `apps/database/ddl/schemas/educational_content/functions/validate_and_audit.sql`
|
|
- Trigger: `apps/database/ddl/schemas/gamification_system/triggers/trg_update_user_stats_on_exercise.sql`
|
|
|
|
**Backend:**
|
|
- Entity: `apps/backend/src/modules/educational/entities/exercise.entity.ts:202`
|
|
- Service Submission: `apps/backend/src/modules/progress/services/exercise-submission.service.ts:199-236`
|
|
- Service Attempt: `apps/backend/src/modules/progress/services/exercise-attempt.service.ts`
|
|
- Controller: `apps/backend/src/modules/educational/controllers/exercises.controller.ts:840-938`
|
|
|
|
---
|
|
|
|
## 🚀 Próximos Pasos
|
|
|
|
### Fase 1: Testing Manual (CRÍTICO)
|
|
- [ ] Validar reenvíos ilimitados en frontend
|
|
- [ ] Validar anti-farming (XP solo en primer acierto)
|
|
- [ ] Validar integridad de datos en DB
|
|
|
|
### Fase 2: Completar Módulos 2 y 3
|
|
- [ ] Avanzar 500 XP adicionales (Módulo 2 completo)
|
|
- [ ] Verificar ascenso a rango **Nacom** (500 XP)
|
|
- [ ] Avanzar 500 XP adicionales (Módulo 3 completo)
|
|
- [ ] Verificar permanencia en **Nacom** hasta 1,500 XP
|
|
|
|
### Fase 3: Deployment
|
|
- [ ] Commit de cambios a Git
|
|
- [ ] Push a staging
|
|
- [ ] Testing en staging
|
|
- [ ] Deployment a producción
|
|
|
|
---
|
|
|
|
## 🔄 Rollback Plan
|
|
|
|
### Revertir Database
|
|
```sql
|
|
BEGIN;
|
|
DROP INDEX IF EXISTS educational_content.idx_exercises_requires_manual_grading;
|
|
ALTER TABLE educational_content.exercises
|
|
DROP COLUMN IF EXISTS requires_manual_grading;
|
|
COMMIT;
|
|
```
|
|
|
|
### Revertir Backend
|
|
```bash
|
|
git revert <commit-hash>
|
|
git push
|
|
npm run build
|
|
pm2 restart gamilit-backend
|
|
```
|
|
|
|
---
|
|
|
|
## 📞 Soporte
|
|
|
|
**Implementado por:** Architecture-Analyst Agent
|
|
**Fecha:** 2025-11-24
|
|
**Duración:** 4 horas (análisis + implementación + testing)
|
|
|
|
**Para issues:**
|
|
- Logs backend: `tail -f /tmp/backend-test-fix.log`
|
|
- Testing script: `apps/database/test-exercise-resubmission.sh`
|
|
- CHANGELOG completo: `apps/database/docs/database/CHANGELOG.md`
|
|
|
|
---
|
|
|
|
**Última actualización:** 2025-11-24 10:30 AM
|
|
**Versión documento:** 1.0
|