Some checks are pending
CI Pipeline / changes (push) Waiting to run
CI Pipeline / core (push) Blocked by required conditions
CI Pipeline / trading-backend (push) Blocked by required conditions
CI Pipeline / trading-data-service (push) Blocked by required conditions
CI Pipeline / trading-frontend (push) Blocked by required conditions
CI Pipeline / erp-core (push) Blocked by required conditions
CI Pipeline / erp-mecanicas (push) Blocked by required conditions
CI Pipeline / gamilit-backend (push) Blocked by required conditions
CI Pipeline / gamilit-frontend (push) Blocked by required conditions
Backend: - Fix email verification and password recovery services - Fix exercise submission and student progress services Frontend: - Update missions, password, and profile API services - Fix ExerciseContentRenderer component Docs & Scripts: - Add SSL/Certbot deployment guide - Add quick deployment guide - Database scripts for testing and validations - Migration and homologation reports - Functions inventory documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
293 lines
6.1 KiB
Markdown
293 lines
6.1 KiB
Markdown
# Funcion: validate_rueda_inferencias
|
|
|
|
**Version:** 1.0.0
|
|
**Fecha:** 2025-12-18
|
|
**Schema:** educational_content
|
|
**Ubicacion:** `apps/database/ddl/schemas/educational_content/functions/14-validate_rueda_inferencias.sql`
|
|
|
|
---
|
|
|
|
## PROPOSITO
|
|
|
|
Validar respuestas abiertas para ejercicios de tipo "Rueda de Inferencias", soportando multiples estructuras de datos y proporcionando retroalimentacion granular.
|
|
|
|
---
|
|
|
|
## FIRMA
|
|
|
|
```sql
|
|
CREATE OR REPLACE FUNCTION educational_content.validate_rueda_inferencias(
|
|
p_student_response JSONB,
|
|
p_correct_answer JSONB,
|
|
p_exercise_config JSONB DEFAULT '{}'::JSONB
|
|
) RETURNS RECORD AS $$
|
|
```
|
|
|
|
---
|
|
|
|
## PARAMETROS
|
|
|
|
| Parametro | Tipo | Descripcion |
|
|
|-----------|------|-------------|
|
|
| p_student_response | JSONB | Respuesta del estudiante |
|
|
| p_correct_answer | JSONB | Respuesta correcta esperada |
|
|
| p_exercise_config | JSONB | Configuracion adicional (opcional) |
|
|
|
|
---
|
|
|
|
## RETORNO
|
|
|
|
```sql
|
|
RECORD (
|
|
is_correct BOOLEAN, -- Si la respuesta es correcta
|
|
score INTEGER, -- Puntaje obtenido (0-100)
|
|
feedback TEXT, -- Retroalimentacion para el estudiante
|
|
details JSONB -- Detalles de evaluacion por categoria
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
## ESTRUCTURAS SOPORTADAS
|
|
|
|
### Estructura Nueva: categoryExpectations
|
|
|
|
```json
|
|
{
|
|
"categoryExpectations": {
|
|
"category_id_1": {
|
|
"keywords": ["palabra1", "palabra2"],
|
|
"minLength": 10,
|
|
"maxLength": 500
|
|
},
|
|
"category_id_2": {
|
|
"keywords": ["palabra3", "palabra4"],
|
|
"minLength": 20
|
|
}
|
|
},
|
|
"fragmentStates": {
|
|
"fragment_id_1": {
|
|
"categoryId": "category_id_1"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Estructura Legacy: flat
|
|
|
|
```json
|
|
{
|
|
"keywords": ["palabra1", "palabra2", "palabra3"],
|
|
"minLength": 10,
|
|
"maxLength": 500,
|
|
"minKeywords": 2
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## LOGICA DE VALIDACION
|
|
|
|
### 1. Deteccion de Estructura
|
|
|
|
```sql
|
|
IF p_correct_answer ? 'categoryExpectations' THEN
|
|
-- Usar estructura nueva
|
|
ELSE
|
|
-- Usar estructura legacy (flat)
|
|
END IF;
|
|
```
|
|
|
|
### 2. Normalizacion de Texto
|
|
|
|
```sql
|
|
v_normalized_text := lower(
|
|
translate(
|
|
p_student_response->>'text',
|
|
'áéíóúÁÉÍÓÚñÑ',
|
|
'aeiouAEIOUnN'
|
|
)
|
|
);
|
|
```
|
|
|
|
### 3. Validacion de Longitud
|
|
|
|
```sql
|
|
v_text_length := length(p_student_response->>'text');
|
|
|
|
IF v_text_length < v_min_length THEN
|
|
v_feedback := 'Respuesta muy corta. Minimo ' || v_min_length || ' caracteres.';
|
|
v_is_correct := false;
|
|
END IF;
|
|
|
|
IF v_max_length IS NOT NULL AND v_text_length > v_max_length THEN
|
|
v_feedback := 'Respuesta muy larga. Maximo ' || v_max_length || ' caracteres.';
|
|
v_is_correct := false;
|
|
END IF;
|
|
```
|
|
|
|
### 4. Conteo de Keywords
|
|
|
|
```sql
|
|
v_keyword_count := 0;
|
|
|
|
FOR v_keyword IN SELECT jsonb_array_elements_text(v_keywords) LOOP
|
|
IF v_normalized_text LIKE '%' || lower(v_keyword) || '%' THEN
|
|
v_keyword_count := v_keyword_count + 1;
|
|
END IF;
|
|
END LOOP;
|
|
```
|
|
|
|
### 5. Calculo de Score
|
|
|
|
```sql
|
|
-- Puntuacion parcial basada en keywords encontradas
|
|
v_keyword_ratio := v_keyword_count::FLOAT / v_total_keywords::FLOAT;
|
|
v_score := LEAST(100, ROUND(v_keyword_ratio * 100));
|
|
|
|
-- Bonus por longitud adecuada
|
|
IF v_text_length >= v_ideal_length THEN
|
|
v_score := v_score + 10;
|
|
END IF;
|
|
```
|
|
|
|
---
|
|
|
|
## EJEMPLOS DE USO
|
|
|
|
### Estructura categoryExpectations
|
|
|
|
```sql
|
|
SELECT educational_content.validate_rueda_inferencias(
|
|
'{"text": "El texto habla sobre la importancia de la lectura critica"}'::JSONB,
|
|
'{
|
|
"categoryExpectations": {
|
|
"cat-inferencias": {
|
|
"keywords": ["lectura", "critica", "importancia", "texto"],
|
|
"minLength": 20
|
|
}
|
|
}
|
|
}'::JSONB
|
|
);
|
|
-- Retorna: (true, 75, 'Respuesta aceptable', {"keywords_found": 3, "keywords_total": 4})
|
|
```
|
|
|
|
### Estructura Legacy
|
|
|
|
```sql
|
|
SELECT educational_content.validate_rueda_inferencias(
|
|
'{"text": "Pienso que el autor quiere transmitir un mensaje sobre la sociedad"}'::JSONB,
|
|
'{
|
|
"keywords": ["autor", "mensaje", "sociedad", "transmitir"],
|
|
"minLength": 30,
|
|
"minKeywords": 2
|
|
}'::JSONB
|
|
);
|
|
-- Retorna: (true, 100, 'Excelente respuesta', {"keywords_found": 4, "keywords_total": 4})
|
|
```
|
|
|
|
---
|
|
|
|
## MANEJO DE ERRORES
|
|
|
|
### Categoria No Encontrada
|
|
|
|
Si un fragmentId no tiene categoryId mapeado, se usa fallback:
|
|
|
|
```sql
|
|
v_category_id := COALESCE(
|
|
p_correct_answer->'fragmentStates'->v_fragment_id->>'categoryId',
|
|
'cat-literal' -- Fallback
|
|
);
|
|
```
|
|
|
|
### Respuesta Vacia
|
|
|
|
```sql
|
|
IF p_student_response IS NULL OR p_student_response->>'text' = '' THEN
|
|
RETURN (false, 0, 'No se proporciono respuesta', '{}'::JSONB);
|
|
END IF;
|
|
```
|
|
|
|
---
|
|
|
|
## RETROALIMENTACION
|
|
|
|
### Mensajes Predefinidos
|
|
|
|
| Condicion | Mensaje |
|
|
|-----------|---------|
|
|
| Score >= 90 | "Excelente respuesta" |
|
|
| Score >= 70 | "Buena respuesta" |
|
|
| Score >= 50 | "Respuesta aceptable, considera agregar mas detalles" |
|
|
| Score < 50 | "Respuesta insuficiente, revisa los conceptos clave" |
|
|
| Muy corta | "Respuesta muy corta. Minimo X caracteres" |
|
|
| Muy larga | "Respuesta muy larga. Maximo X caracteres" |
|
|
|
|
---
|
|
|
|
## DETALLES DE RETORNO
|
|
|
|
### Estructura de details
|
|
|
|
```json
|
|
{
|
|
"keywords_found": 3,
|
|
"keywords_total": 5,
|
|
"text_length": 85,
|
|
"min_length": 20,
|
|
"max_length": 500,
|
|
"categories_evaluated": [
|
|
{
|
|
"category_id": "cat-inferencias",
|
|
"keywords_found": 2,
|
|
"keywords_total": 3,
|
|
"passed": true
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## INTEGRACION
|
|
|
|
### Trigger de Validacion
|
|
|
|
```sql
|
|
CREATE TRIGGER trg_validate_rueda_response
|
|
BEFORE INSERT ON educational_content.exercise_attempts
|
|
FOR EACH ROW
|
|
WHEN (NEW.exercise_type = 'rueda_inferencias')
|
|
EXECUTE FUNCTION educational_content.validate_rueda_response_trigger();
|
|
```
|
|
|
|
### Uso desde Backend
|
|
|
|
```typescript
|
|
const result = await db.query(`
|
|
SELECT * FROM educational_content.validate_rueda_inferencias($1, $2)
|
|
`, [studentResponse, correctAnswer]);
|
|
|
|
const { is_correct, score, feedback, details } = result.rows[0];
|
|
```
|
|
|
|
---
|
|
|
|
## HISTORIAL DE CAMBIOS
|
|
|
|
| Fecha | Version | Cambio |
|
|
|-------|---------|--------|
|
|
| 2025-12-15 | 1.0.0 | Version inicial con soporte dual |
|
|
|
|
---
|
|
|
|
## REFERENCIAS
|
|
|
|
- [04-FUNCTIONS-INVENTORY.md](../../90-transversal/inventarios-database/inventarios/04-FUNCTIONS-INVENTORY.md)
|
|
- Ejercicios de Rueda de Inferencias en Modulo 2
|
|
|
|
---
|
|
|
|
**Ultima actualizacion:** 2025-12-18
|