PsicoScan ML está compuesto por dos servicios independientes que se comunican via HTTP:
- Frontend Next.js — interfaz de usuario, autenticación, lógica de negocio y acceso a base de datos.
- API de clasificación FastAPI — servicio Python que recibe las escalas SENA y devuelve el tipo de caso y semáforo de riesgo.
┌─────────────────────────────────┐ ┌───────────────────────────────┐
│ Frontend Next.js │ │ ML API (FastAPI) │
│ localhost:3000 │──────▶ │ localhost:8000 │
│ │ HTTP │ │
│ App Router + NextAuth + Prisma │ │ POST /api/v1/clasificar │
└──────────────┬──────────────────┘ └───────────────────────────────┘
│
▼
┌───────────────────────┐
│ PostgreSQL │
│ (Supabase) │
└───────────────────────┘
Stack tecnológico
Frontend
ML API
Infraestructura
| Tecnología | Versión | Uso |
|---|
| Next.js | 16.1.6 | Framework full-stack (App Router) |
| React | 19.2.3 | Interfaz de usuario |
| TypeScript | 5 | Tipado estático |
| Tailwind CSS | 4 | Estilos |
| Recharts | 3.7 | Gráficas interactivas del dashboard |
| NextAuth | 4.24 | Autenticación JWT con sesiones |
| Prisma | 7.4 | ORM para PostgreSQL |
| bcryptjs | 3.0 | Hash de contraseñas |
| ExcelJS | 4.4 | Exportación de reportes |
| Tecnología | Versión | Uso |
|---|
| FastAPI | 0.115 | Framework de API REST |
| Uvicorn | 0.32 | Servidor ASGI |
| Scikit-learn | 1.6 | Utilidades de modelos ML |
| XGBoost | 2.1 | Clasificador principal (en entrenamiento) |
| Pandas / NumPy | 2.x | Procesamiento de datos |
| pdfplumber | 0.11 | Extracción de PDFs del software TEA |
| Pydantic | 2.10 | Validación de esquemas de entrada/salida |
| Componente | Servicio |
|---|
| Base de datos | PostgreSQL en Supabase |
| Frontend (producción) | Vercel |
| ML API (producción) | Railway o Render |
Estructura del proyecto
Frontend (app/)
El frontend sigue la convención App Router de Next.js con grupos de rutas:
app/
├── (auth)/
│ └── login/ # Página de inicio de sesión
├── (dashboard)/ # Rutas protegidas (requieren sesión activa)
│ ├── layout.tsx # Sidebar + shell compartido
│ ├── dashboard/ # Panel principal con gráficas de riesgo
│ ├── estudiantes/ # Tabla de todos los estudiantes
│ ├── estudiantes/[id]/ # Expediente individual del estudiante
│ ├── cuestionario/ # Aplicación digital del SENA (en desarrollo)
│ ├── citas/ # Gestión de citas
│ ├── respuestas/ # Respuestas crudas del cuestionario
│ ├── expediente/ # Vista de expediente clínico
│ ├── historico/ # Histórico de tamizajes
│ └── usuarios/ # Gestión de usuarios (solo ADMIN)
└── api/
└── auth/ # Endpoints NextAuth (signIn, session, CSRF)
Componentes (components/)
components/
├── dashboard/ # Gráficas y tarjetas del panel principal
├── semaforo/ # Badge de nivel de riesgo (VERDE/AMARILLO/ROJO/ROJO_URGENTE)
└── ui/ # Sidebar, botones y componentes base
Capa lib (lib/)
lib/
├── auth.ts # Configuración de NextAuth (providers, callbacks, rol en JWT)
├── db.ts # Singleton del cliente Prisma
├── types.ts # Tipos TypeScript, colores y labels del semáforo
├── enums.ts # Enums Rol, Semaforo, TipoCaso (seguros para Client Components)
└── data/mock.ts # 5 casos tipo para desarrollo sin base de datos
ML API (ml-api/)
ml-api/
├── main.py # App FastAPI, CORS, router mounting
├── routers/
│ └── clasificacion.py # POST /api/v1/clasificar, GET /api/v1/modelo/info
├── schemas/
│ └── tamizaje.py # Modelos Pydantic TamizajeInput / TamizajeOutput
├── services/
│ └── clasificador.py # Motor de clasificación (reglas + XGBoost)
└── requirements.txt
Esquema de base de datos
El esquema completo está definido en prisma/schema.prisma. Los modelos principales son:
| Modelo | Descripción |
|---|
Usuario | Cuenta de acceso con rol (ESTUDIANTE, PSICOLOGO, ORIENTADOR, DIRECTOR, ADMIN) |
Estudiante | Perfil del alumno con CURP, grado, grupo y token de encuesta único |
Tamizaje | Resultado de una aplicación SENA: 31 escalas en puntuación T + tipoCaso + semaforo + ítems críticos |
Cita | Cita agendada para un estudiante con estado PENDIENTE/CONFIRMADA/COMPLETADA/CANCELADA |
ExpedienteClinico | Expediente clínico con motivo de consulta, antecedentes, diagnóstico y plan de intervención |
Sesion | Registro de sesión clínica vinculada a una cita y al expediente del estudiante |
Canalizacion | Derivación a institución externa con estado de seguimiento |
HistoricoSENA | Corpus de tamizajes históricos usado para entrenamiento del modelo XGBoost |
EntrenamientoML | Registro de cada ciclo de entrenamiento con métricas (accuracy, CV, feature importance) |
Motor de clasificación
El servicio ClasificadorML en services/clasificador.py aplica la siguiente lógica al recibir un TamizajeInput:
- Evalúa primero las escalas de control (INC, NEG, POS) para detectar inconsistencias o sesgos de respuesta.
- Si las escalas de control son válidas, evalúa el índice global GLO y los ítems críticos para determinar el nivel de riesgo.
- Devuelve un
TamizajeOutput con tipo_caso, semaforo, confianza y observaciones.
Motor de reglas vs. modelo XGBoost. En la versión actual (0.1.0-reglas), el endpoint GET /api/v1/modelo/info reporta "modelo_cargado": false. Esto indica que el sistema está operando con el motor de reglas heurísticas como fallback. El modelo XGBoost se activará automáticamente una vez entrenado con el dataset real de ~2,000 tamizajes históricos y guardado como artefacto. No es necesario ningún cambio en el frontend ni en los endpoints.
Flujo de datos de un tamizaje
Estudiante responde SENA
│
▼
Frontend captura 31 escalas T
│
▼
POST /api/v1/clasificar ──▶ Motor de reglas / XGBoost
│ │
│ ◀────────────────── tipo_caso + semáforo + confianza
▼
Prisma guarda Tamizaje en PostgreSQL
│
▼
Dashboard actualiza distribución de riesgo