Medición por alumno y lista de rescate
Esta es la primera capa de medición del Campus. No son gráficas de negocio ni promedios: es una capa pensada para actuar sobre una persona concreta antes de perderla. Responde preguntas como "¿quién pagó y todavía no ha entrado?", "¿quién empezó a grabar y se atascó?" o "¿a quién le respondió su tutora y ni lo ha abierto?".
La medición del Campus tiene tres capas, de la más cercana al alumno a la más técnica:
Las tres capas de medición
Capa 1 · Medición por alumnoQué hace cada alumno (entró, abrió una lección, empezó a grabar…) y la lista de rescate de quien está en riesgo.👁️ Gestor y administración
Capa 2 · Coste e infraestructuraQué cuesta el Campus cada semana y el coste estimado por alumno, con avisos si algo se dispara.👁️ Solo administración
Capa 3 · Repetición de sesión (replay)Ver, como en un vídeo, cómo un alumno usó una pantalla —con sus datos personales tapados.👁️ Solo administración · apagado por defecto
Para quién es esta página
La usan gestor y administración. El tutor no ve esta información (es una lista de gestión que cruza a todos los alumnos, no algo por tutora). Está escrita para que el equipo entienda qué se mide, cómo se calcula y dónde mirarlo.
Qué medimos y cómo llega
El Campus va anotando, de forma discreta, un puñado de cosas que hace el alumno que antes no quedaban registradas en ningún sitio: cuándo entra, cuándo abre una lección, cuánto vídeo ve, cuándo empieza a grabar, cuándo abre la respuesta de su tutora… Cada una de esas cosas es un evento.
Solo lo que no tenía ya un hogar
No se re-registra lo que ya vive en su tabla (envíos, valoraciones, mensajes leídos, lecciones completadas): eso ya se sabe. Esta capa solo captura señales que antes se perdían — los pasos intermedios que cuentan si alguien está enganchado o a punto de irse.
Los eventos que capturamos hoy:
| Evento | Qué significa |
|---|---|
| Entró al Campus | El alumno abre el Campus tras un rato fuera (se agrupa: no cuenta cada clic). |
| Primer acceso de su vida | La primerísima vez que entra. Su ausencia es la señal "nunca ha entrado". |
| Abrió una lección | Abrió el contenido de una lección. |
| Reprodujo un vídeo / avanzó / cambió la velocidad | Le dio al play, y cuánto llegó a ver (25 %, 50 %, 75 %, 100 %). |
| Empezó a grabar / descartó una grabación | Empezó a grabar un audio de feedback, o lo tiró para rehacerlo. |
| Abrió el grabador | Entró en la pantalla de grabar un envío. |
| Abrió la respuesta de su tutora | Vio la respuesta que le dejó su tutora. |
| Empezó un test | Arrancó un test de autoevaluación. |
| Error en su pantalla | Algo falló en el navegador del alumno (para poder investigarlo). |
Cómo funciona por dentro (equipo técnico)
Todos los eventos caen en una sola tabla de la base de datos (analytics_events), con una forma común: quién (el alumno), cuándo, qué pasó, y sobre qué curso/lección. Entran por dos puertas: los eventos del navegador viajan en segundo plano a un endpoint (POST /api/analytics, que responde siempre sin bloquear la página), y los del servidor se anotan directamente. El servidor nunca se fía de quién dice ser el navegador: la identidad la pone desde la sesión. El curso/tutora no se guardan en el evento — se derivan de la matrícula cuando hacen falta. La lista de eventos es un catálogo cerrado y tipado (src/lib/analytics/actions.ts): añadir uno nuevo o mandar un dato con la forma equivocada no compila.
Privacidad y retención
Cada evento está atado al alumno, así que solo él puede leer los suyos; el equipo los consulta a través de las pantallas de abajo, no de la tabla cruda. Cuando se elimina un usuario, sus eventos se borran con él. No hay caducidad automática: se guardan mientras la cuenta exista.
La lista de rescate
Es la pantalla principal de esta capa. Reúne a los alumnos en riesgo de perderse, ordenados de más urgente a menos, con el motivo y su tutora en cada fila.
Dónde se ve: en Panel Gestor → Rescate y en Panel Admin → Rescate (la misma pantalla en los dos sitios). El tutor no tiene acceso.
Arriba, una tira de conteos dice cuántos alumnos hay por cada motivo. Pinchar un motivo filtra la lista por él; el buscador acota la lista por nombre o email. (La tira siempre muestra el total global: no cambia al buscar ni al filtrar.)
Los cinco motivos de riesgo — y cómo se calcula cada uno
Un alumno puede tener varios motivos a la vez. El orden de abajo es la prioridad: cuando coinciden varios, manda el más cercano a perder al alumno.
- Nunca entró — pagó, su acceso al curso empezó hace 7 días o más y jamás ha entrado (ni primer acceso ni ninguna sesión). Es la cola de "pagó y no ha aparecido".
- Dormido — tiene matrícula activa pero lleva 14 días o más sin entrar.
- Valoración negativa — dejó una valoración baja en los últimos 30 días: una carita 1-2 (en una lección o en la respuesta de su tutora) o un NPS de 0 a 6. Es el único motivo que mira hacia atrás (una carita triste de hace medio año no es una emergencia).
- Audio abandonado — empezó a grabar, su envío sigue en borrador (nunca lo mandó) y así lleva 3 días o más.
- Respuesta sin abrir — su tutora le respondió y el alumno no ha abierto la respuesta en 7 días o más.
Los umbrales se pueden ajustar
Esos días (7 / 14 / 30 / 3 / 7) son decisiones de producto, no números mágicos, y se pueden afinar. El criterio general es precisión sobre cobertura: preferimos no marcar a alguien de más que llenar la lista de falsas alarmas para un público de 50-70 años.
Cómo funciona por dentro (equipo técnico)
El modelo vive en src/lib/analytics/risk.ts como gemelo exacto de una vista SQL (reporting.student_risk): la vista calcula los cinco booleanos y el rango, y el módulo de TypeScript los reproduce para que el test verifique que ambos dicen lo mismo. La superficie lee la vista reporting.student_rescue (añade nombre, email y tutora) y …_rescue_counts (la tira). Los umbrales están en RISK_THRESHOLDS; cambiarlos obliga a cambiar los interval de la vista en el mismo cambio.
La ficha del alumno
Al pinchar una fila se abre la ficha del alumno a página completa (no un panel lateral): arriba, por qué está en riesgo; debajo, su historia (timeline).
La historia no es un volcado de clics: está curada para leerse de un vistazo. Se agrupa por día (el más reciente primero) y dentro de cada día se colapsa el ruido:
- Varios inicios de sesión → "Se conectó N veces".
- Todos los avances de un vídeo → "Vio el 75 %" (el máximo que llegó a ver).
- Varios mensajes leídos → "Leyó N mensajes".
El resto de hitos (abrió una lección, empezó un test, envió un audio, valoró…) aparecen tal cual. La historia junta el stream de eventos con lo que ya existía (envíos, valoraciones, mensajes leídos, días activos) y está paginada con un "cargar más".
Tiempo de activación (primer acceso)
Una cosa es quién no ha entrado (eso lo marca el motivo "Nunca entró"); otra es cuánto tarda en entrar quien sí acaba entrando. A eso lo llamamos activación: el tiempo entre que se le concede el acceso a un curso y su primer acceso de su vida al Campus. Cuanto antes entra, mejor arranca.
En Rescate: la columna «Primer acceso»
Cada fila de la lista de rescate lleva una columna "Primer acceso" que resume, de un vistazo, en qué punto está ese alumno:
- Ya entró → la fecha de su primer acceso y cuánto tardó (p. ej. "15 ago · 3 días").
- Aún no entró → "— · aún no entró". Todavía no ha pisado el Campus.
- Antes de la medición → "— · antes de la medición". Entró antes de que empezáramos a medir esto, así que no sabemos cuánto tardó (no es que no haya entrado).
En Analítica: la pestaña «Activación»
Para la foto global (no alumno a alumno), administración tiene una pestaña propia en Panel Admin → Analítica → "Activación". Responde "¿de la gente que empezó, cuánta ha entrado, y cuánto tardó?". Arriba, cuatro números:
| Número | Qué dice |
|---|---|
| Tasa de activación | Qué porcentaje de la cohorte ya ha entrado alguna vez. |
| Media | Cuántos días tarda de media quien entra. |
| Mediana | El día "del medio": la mitad tarda menos, la mitad más (menos sensible a casos raros). |
| Activaron en <24 h | Cuántos entraron el primer día. |
Debajo, "Distribución del tiempo hasta activar" reparte a los que ya entraron en cuatro tramos —<24 h, 1–3 días, 4–7 días y >7 días— y una tabla por curso (alumnos, tasa, pendientes, media, mediana y cuántos en <24 h) para ver qué curso arranca peor.
La cohorte de medición
La media y la tasa se calculan sobre los alumnos cuyo acceso empezó desde que arrancó esta medición (los de antes no cuentan, porque no tenemos su primer acceso). Si aún no hay nadie en esa cohorte, la pestaña lo dice en lugar de enseñar ceros.
Posibles cuentas compartidas
Una pestaña aparte dentro de la misma superficie de rescate. Marca cuentas que probablemente comparten varias personas — una fuga de ingresos distinta del abandono (aquí el alumno recibe más de lo que pagó).
Cómo se detecta: solo por concurrencia, dos cosas que no puede hacer una sola persona a la vez, en una ventana tan corta que son "a la vez":
- Viaje imposible — dos ubicaciones (a nivel de ciudad/IP) claramente distintas separadas por 15 minutos o menos: imposible haber viajado. Es la señal fuerte.
- Dispositivos a la vez — misma zona pero aparatos distintos casi simultáneos (una pareja en la misma casa, cada uno en su móvil). Señal más débil.
Es una señal, nunca un veredicto
- Solo se marca con un patrón sostenido: 3 incidentes o más, en 3 días distintos o más, dentro de una ventana de 30 días. Un incidente suelto no marca a nadie.
- Cada fila trae su puntuación (el viaje imposible pesa el doble que los aparatos a la vez) y la evidencia (los incidentes con sus fechas y ubicaciones) para que un humano revise.
- Nunca hay bloqueo automático. Jamás se corta el acceso ni se avisa al alumno por esto: entre la señal y cualquier acción siempre hay una persona (gestor o admin).
- Un VPN puede dar un falso positivo aislado; por eso hace falta el patrón sostenido y la revisión humana. Es un coste asumido de detectar sin identificar el dispositivo.
Cómo funciona por dentro (equipo técnico)
Un trabajo programado semanal (GET /api/cron/shared-account-signals, lunes) recorre las sesiones de cada alumno, aplica la lógica pura de src/lib/analytics/shared-account.ts (detectIncidents + evaluateSharedAccount) y escribe una fila por sospechoso en analytics_shared_account_signals con score + evidence. El staff lee esa tabla, no el firehose. Umbrales en SHARED_ACCOUNT_THRESHOLDS. Se recalcula cada semana.
Resumen: dónde se ve cada cosa
| Quiero ver… | Dónde |
|---|---|
| Alumnos en riesgo, con el motivo y la tutora | Panel Gestor / Admin → Rescate |
| Si un alumno ya entró y cuánto tardó | Columna "Primer acceso" en Rescate |
| El tiempo de activación global y por curso | Admin → Analítica → "Activación" |
| La historia completa de un alumno | La ficha dentro de Rescate (pinchar una fila) |
| Posibles cuentas compartidas | Pestaña "Posibles cuentas compartidas" en Rescate |
| Cómo valoran (agregado por lección) y el NPS | Satisfacción → ver Valoraciones |
| Coste de infraestructura por alumno | Admin → Analítica → ver Infraestructura y costes |
→ Para ver a un alumno "en directo" atascarse en una pantalla, existe además la repetición de sesión.