Skip to main content
Las acciones de tamizaje coordinan el flujo completo de evaluación: scoring local de las 188 respuestas, llamada a la ML API para clasificación (con fallback automático), y persistencia en PostgreSQL a través de Prisma.
Estas funciones están marcadas con "use server" y se invocan desde componentes React. No son endpoints HTTP — Next.js las serializa en el bundle del servidor.

procesarYGuardarTamizaje

export async function procesarYGuardarTamizaje(
  estudianteId: string,
  respuestas: number[],
  { skipML = false }: { skipML?: boolean } = {}
): Promise<{ semaforo: string; tipoCaso: string }>
Núcleo del tamizaje. Realiza el scoring local, consulta la ML API, y guarda el tamizaje y las respuestas crudas en la base de datos. Es la función base utilizada por guardarTamizaje e importaciones masivas.

Parámetros

ParámetroTipoDescripción
estudianteIdstringID del estudiante (CUID de Prisma)
respuestasnumber[]Array de 188 respuestas con valores 1–5
skipMLbooleanSi es true, omite la llamada a la ML API y usa solo el scoring local. Útil para importaciones masivas. Valor por defecto: false

Retorno

{ semaforo: string; tipoCaso: string }
semaforo y tipoCaso reflejan el resultado final — del modelo ML si estuvo disponible, o del scoring local como fallback.

Flujo interno

  1. Scoring local — Calcula puntuaciones brutas por escala usando calcularResultado(). Siempre disponible, no depende de la ML API.
  2. Llamada ML (si skipML es false) — Construye el payload con las escalas calculadas y hace POST /api/v1/clasificar con un timeout de 5 segundos. Si la API no está disponible o retorna error, continúa con el resultado del scoring local sin propagar el error.
  3. Persistencia — Guarda un registro Tamizaje con todas las escalas y el resultado final.
  4. Respuestas crudas — Guarda las respuestas brutas en RespuestasCuestionario con procesado: true.

Ejemplo

import { procesarYGuardarTamizaje } from "@/lib/actions/tamizaje"

// Desde una importación masiva, omitiendo el ML
const resultado = await procesarYGuardarTamizaje(
  "cm1abc123",
  [3, 2, 1, 4, 2, /* ... 183 más */],
  { skipML: true }
)
console.log(resultado.semaforo)  // "VERDE"
console.log(resultado.tipoCaso)  // "SIN_RIESGO"

guardarTamizaje

export async function guardarTamizaje(
  estudianteId: string,
  respuestas: number[],
  esEstudiante?: boolean
): Promise<GuardarTamizajeResult>
Wrap de procesarYGuardarTamizaje con validación de entrada y redirección final. Diseñada para ser invocada desde el formulario del cuestionario.

Parámetros

ParámetroTipoDescripción
estudianteIdstringID del estudiante
respuestasnumber[]Array de respuestas
esEstudiantebooleanSi true, redirige a /cuestionario/mi-sena/completado. Si false (por defecto), redirige a /cuestionario/{estudianteId}/completado

Retorno

export type GuardarTamizajeResult = { error: string } | undefined
Retorna undefined en éxito (la función llama a redirect() antes de retornar). Retorna { error: string } si la validación falla o hay un error de base de datos.

Validaciones

  • El array respuestas debe tener exactamente TOTAL_REACTIVOS elementos (188).
  • Cada respuesta debe estar en el rango [1, 5].

Ejemplo

import { guardarTamizaje } from "@/lib/actions/tamizaje"

// Desde un componente de servidor o useActionState
const resultado = await guardarTamizaje(
  "cm1abc123",
  respuestasArray,
  false  // redirige a /cuestionario/cm1abc123/completado
)

if (resultado?.error) {
  console.error(resultado.error)
  // "Debe completar todos los reactivos antes de enviar."
  // "Respuestas fuera de rango (1–5)."
  // "Error al guardar el tamizaje. Intente de nuevo."
}

Modelo de datos: Tamizaje

Registro persistido en PostgreSQL con todas las escalas del SENA y el resultado de clasificación.
CampoTipoDescripción
idStringCUID autogenerado
fechaDateTimeFecha y hora del tamizaje (automática)
estudianteIdStringFK al estudiante
incFloatEscala Inconsistencia
negFloatImpresión negativa
posFloatImpresión positiva
glo_t … rec_tInt6 índices globales (T)
dep_t … obs_tInt6 escalas de problemas interiorizados (T)
ate_t … ant_tInt6 escalas de problemas exteriorizados (T)
sus_t, esq_t, ali_tInt3 escalas de otros problemas (T)
fam_t, esc_t, com_tInt3 escalas contextuales (T)
reg_t, bus_tInt2 vulnerabilidades (T)
aut_t, soc_t, cnc_tInt3 recursos personales (T)
tipoCasoTipoCasoResultado de clasificación
semaforoSemaforoNivel de riesgo
observacionesString?Texto descriptivo del resultado
itemsCriticosJsonLista de ítems críticos activados

Enums

TipoCaso

import type { TipoCaso } from "@/lib/enums"
ValorDescripción
INCONSISTENCIACuestionario con inconsistencias — solicitar repetición
SIN_RIESGOSin indicadores de riesgo
IMPRESION_POSITIVASesgo de presentación positiva detectado
IMPRESION_NEGATIVAImpresión negativa con índices elevados
CON_RIESGORiesgo emocional o conductual confirmado

Semaforo

import type { Semaforo } from "@/lib/enums"
ValorColorAcción recomendada
VERDEVerdeSeguimiento periódico normal
AMARILLOAmarilloRevisión preventiva con orientador o psicólogo
ROJORojoProgramar cita con psicólogo
ROJO_URGENTERojo urgenteAtención inmediata

Integración con la ML API

La acción procesarYGuardarTamizaje construye el payload para POST /api/v1/clasificar directamente desde los resultados del scoring local:
const mlUrl = process.env.ML_API_URL ?? "http://localhost:8000"
const mlRes = await fetch(`${mlUrl}/api/v1/clasificar`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    estudiante_id: estudianteId,
    inc: pb.inc ?? 0,
    neg: pb.neg ?? 0,
    pos: pb.pos ?? 0,
    // ... todas las escalas clínicas
    items_criticos_count: scoring.itemsCriticos.length,
    respuestas: respuestas.join(""),
    edad: estudiante?.edad ?? 15,
    sexo: estudiante?.sexo ?? "MASCULINO",
  }),
  signal: AbortSignal.timeout(5000), // timeout de 5 segundos
})
Si la ML API no está disponible (timeout, error de red, respuesta no-OK), la acción no falla. El resultado del scoring local ya calculado se usa directamente como semaforo y tipoCaso. Esto garantiza que los tamizajes siempre se guarden aunque el servicio ML esté caído.

Build docs developers (and LLMs) love