PWA educativa para profesores y alumnos de Formación Profesional (FP) en España. Gestiona y consulta los Resultados de Aprendizaje (RAs) y Criterios de Evaluación (CEs) de los módulos de la familia de Informática, con un asistente IA basado en Claude Opus 4.7 para resolver dudas en tiempo real.
Demo en vivo: https://appra.netlify.app/
La normativa educativa de FP define cada módulo mediante un conjunto de Resultados de Aprendizaje (RAs) y Criterios de Evaluación (CEs) publicados en el BOE y los boletines autonómicos (BOCM en Madrid). Profesores, alumnos y familias necesitan acceder a esta información para:
- Profesores: estructurar la programación didáctica, llevar el seguimiento del avance del curso, evaluar.
- Alumnos: entender qué van a aprender, en qué se les va a evaluar, qué nivel se les exige.
- Familias: comprender los contenidos del ciclo y el progreso de sus hijos.
APPRA centraliza esta información, la hace accesible desde cualquier dispositivo (PWA offline) y añade un asistente IA que responde dudas adaptando la respuesta al rol del usuario.
Proyecto presentado al hackathon Built with Opus 4.7 organizado por Cerebral Valley en abril de 2026.
- Modelo: Claude Opus 4.7 (
claude-opus-4-7) - Repositorio: github.com/joanh/APPRA — Open Source (MIT)
- Demo: appra.netlify.app
APPRA es un caso de uso real de Claude aplicado a la educación pública española: extrae datos de documentos oficiales (PDFs del BOE/BOCM), los estructura como JSON consultable, y ofrece un asistente conversacional que adapta sus respuestas al rol del usuario (profesor, alumno, familia). El propio proceso de extracción de los datos curriculares se realiza con Claude Opus 4.7, así que el ciclo es coherente: Claude genera los datos, Claude los consume.
- Página de portada (landing): logo, intro y selector de módulo. La app no carga ningún módulo hasta que el usuario lo elige; "Página Inicio" siempre vuelve aquí.
- Selector dinámico de módulo: la web se auto-personaliza según el módulo elegido (título, imagen, RAs, CEs, pesos, descripciones).
- Datos auténticos del BOE: los RAs y CEs se extraen de los Reales Decretos oficiales del BOE con Claude Opus 4.7 (
scripts/extraer-modulos.js), no se redactan manualmente. El proceso es reproducible y auditable. - Asistente IA multi-rol (en desarrollo): chatbot con Claude Opus 4.7 que adapta sus respuestas según el rol (profesor, alumno, familia).
- Seguimiento de progreso: estados visuales (No iniciado / En progreso / Completado) con barra de progreso global ponderada. El estado de cada módulo se guarda independientemente en
localStorage(ceStates_<moduleId>), así cambiar de módulo no pisa el progreso. - Búsqueda y filtrado: búsqueda por texto en CEs, filtro por RA, sugerencias dinámicas (los términos más frecuentes se recalculan por módulo).
- Exportación múltiple: JSON y CSV con etiquetas en español (Pendiente / En Progreso / Completado). PDF y DOCX en el roadmap.
- Modo profesor — estado oficial del curso: el admin (
joanh) puede guardar el estado en el repo de GitHub vía Netlify Function. Lectura pública para el resto. - Diálogos unificados: todos los avisos, confirmaciones y prompts usan SweetAlert2 con tema oscuro coherente.
- PWA (parcial): manifest + iconos. Service worker en el roadmap.
- Accesibilidad y responsive: dark mode, navegable por teclado, adaptado a móvil y escritorio.
- Abre https://appra.netlify.app/. Aterrizas en la portada: logo APPRA, intro y un único selector de módulo en el centro.
- Elige un módulo del desplegable. La página se transforma: aparece el título del módulo, su imagen propia y todas las tarjetas de RAs con sus CEs literales del BOE.
- Despliega un RA haciendo click en su cabecera para ver la tabla de CEs (descripción, peso e icono FCT cuando aplique).
- Marca tu progreso clicando en el botón No iniciado → cicla a En progreso → Completado. La barra global se actualiza ponderada por pesos. Cada estado se guarda en
localStoragenamespaceado por módulo (ceStates_<id>), así que cambiar de módulo y volver mantiene tu progreso. - Busca CEs desde el cuadro de búsqueda. Al hacer click en el input aparecen chips con los términos más frecuentes — específicos del módulo activo (los términos de DWEC son distintos de los de SAD).
- Carga el Estado Oficial desde el panel del profesor para ver el avance publicado por el docente del curso.
- Cambia de módulo desde el selector cuando quieras, o vuelve a la portada con Página Inicio en el menú lateral / móvil.
- Exporta tu progreso a JSON o CSV (etiquetas en español: Pendiente / En Progreso / Completado).
Para publicar el estado oficial del curso necesitas la contraseña que el admin configuró como ADMIN_PASSWORD en Netlify:
- Sobre un módulo, marca el avance real del curso (qué CEs se han trabajado, en qué punto están).
- Click en Guardar Estado Oficial → introduce la contraseña en el SweetAlert.
- Si es correcta, aparece un commit nuevo en
joanh/APPRAbajoJSON/oficiales/<moduleId>.json. - La contraseña se cachea en
sessionStorage— no se te volverá a pedir mientras la pestaña esté abierta. - Si la contraseña ha rotado entre sesiones, la siguiente acción de guardado devolverá 401 y limpiará la caché automáticamente.
| Acción | Dónde |
|---|---|
| Volver a la portada | Página Inicio en el sidebar / Inicio en navbar móvil |
| Resetear el progreso del módulo actual | Panel del profesor → Reset (solo borra localStorage, no toca el estado oficial) |
| Expandir / colapsar todos los RAs | Botón Expandir todos sobre el listado |
| Limpiar la búsqueda | Click en la X dentro del input |
- "Sin estado oficial" al cargar → ese módulo aún no tiene
JSON/oficiales/<id>.jsonpublicado por el profesor. Es informativo, no un fallo. - "Selecciona un módulo" al guardar/cargar → estás en la portada; elige un módulo desde el selector primero.
- "Sesión expirada" al guardar → la contraseña ha cambiado en Netlify; pulsa Guardar otra vez para introducir la nueva.
| Capa | Tecnología |
|---|---|
| Frontend | HTML5, CSS3, JavaScript vanilla (sin frameworks) |
| Backend serverless | Netlify Functions (Node.js) |
| IA | Anthropic Claude API — claude-opus-4-7 |
| Almacenamiento | localStorage (cliente) + GitHub API (estado oficial del curso) |
| Despliegue | Netlify |
| PWA | Manifest + Service Worker (en desarrollo) |
| Animación | particles.js |
| UI utilitaria | W3.CSS, Font Awesome 6, SweetAlert2 |
Decisión de diseño: sin frameworks frontend. El proyecto debe ser auditable, ligero, y aprendible por estudiantes de FP que están justamente aprendiendo HTML, CSS y JavaScript vanilla. Es coherente con su misión educativa.
┌───────────────────────┐ ┌────────────────────────┐
│ Cliente PWA │ │ Netlify Functions │
│ │ │ │
│ index.html │ ◄─────► │ validateAdmin.js │
│ js/raManager.js │ HTTPS │ saveState.js │
│ js/chatWidget.js │ │ chatbot.mjs │
│ JSON/modulos/*.json │ │ │
│ localStorage │ │ │
└───────────────────────┘ └────────────────────────┘
│
▼
┌────────────────────────┐
│ Servicios externos │
│ │
│ Anthropic Claude API │
│ GitHub API │
└────────────────────────┘
Los datos curriculares (JSON/modulos/*.json) son la fuente de verdad: se cargan dinámicamente al cambiar de módulo y se sincronizan con el estado del usuario (localStorage). El estado oficial del profesor se persiste vía GitHub API (commits sobre official-state.json).
APPRA/
├── index.html Página principal (landing + vista módulo)
├── site.webmanifest Manifest PWA + iconos
├── netlify.toml Configuración Netlify + headers de seguridad
│
├── .Docs/ PDFs oficiales fuente (BOE / BOCM)
│
├── css/
│ ├── styles.css Estilos (dark mode, landing/módulo)
│ └── search-suggestions.css Sugerencias del buscador
│
├── js/
│ ├── script.js Bootstrap + admin + UI global
│ ├── constants.js Configuración (partículas, etc.)
│ ├── search-suggestions.js Chips de términos frecuentes (dinámicos)
│ └── modules/
│ ├── raManager.js Render de RAs/CEs + estado por módulo
│ └── moduleLoader.js Selector + carga JSON + landing/módulo
│
├── netlify/functions/
│ ├── validateAdmin.js Valida la contraseña admin contra ADMIN_PASSWORD
│ ├── saveState.js Guarda estado oficial por módulo (commit a GitHub)
│ └── chatbot.mjs Chatbot multi-rol con Claude Opus 4.7 (streaming SSE)
│
├── scripts/
│ ├── extraer-modulos.js Extrae RAs/CEs de los PDFs con Claude
│ ├── verificar-pdfs.js Sanity check: módulos por PDF
│ └── og-card.html Plantilla de la social card OG (1200×630)
│
├── JSON/
│ ├── modulos/
│ │ ├── modulos.json Índice de módulos (id, nombre, imagen, ...)
│ │ └── <id>.json RAs y CEs de cada módulo
│ └── oficiales/
│ └── <id>.json Estado oficial publicado por el profesor
│
├── icon/, img/ Recursos gráficos
├── lib/ Librerías externas (particles.js)
└── prompts/ Logs de sesiones con Claude (gitignored)
Los datos de cada módulo se extraen de fuentes oficiales:
- BOE (Real Decreto que establece el título y enseñanzas mínimas)
- BOCM (Decreto autonómico de Madrid que desarrolla el currículo)
- Documentación de la Consejería de Educación de la Comunidad de Madrid
El proceso de extracción se realiza con Claude Opus 4.7, parseando los PDFs oficiales y generando los JSONs estructurados.
| ID | Módulo | Curso | RAs | CEs | Fuente |
|---|---|---|---|---|---|
2daw-dwec |
Desarrollo Web en Entorno Cliente | 2º DAW | 7 | 56 | RD 686/2010 (BOE) |
1daw-ed |
Entornos de Desarrollo | 1º DAW | 6 | 43 | RD 686/2010 (BOE) |
2asir-sad |
Seguridad y Alta Disponibilidad | 2º ASIR | 7 | 58 | RD 1629/2009 (BOE) |
Nota sobre fuentes: los Reales Decretos del BOE definen los RAs y CEs (válidos para todo el Estado). El Decreto autonómico (BOCM en Madrid) solo amplía las horas y los contenidos didácticos, sin redefinir RAs/CEs. Por eso APPRA toma los RAs/CEs del BOE y, en una iteración futura, podrá superponer las horas autonómicas correspondientes.
{
"id": "2daw-dwec",
"nombre": "Desarrollo Web en Entorno Cliente",
"abreviatura": "DWEC",
"curso": "2º DAW",
"ciclo": "Desarrollo de Aplicaciones Web",
"familia": "Informática y Comunicaciones",
"grado": "Superior",
"horas": 80,
"descripcionBreve": "...",
"resultadosAprendizaje": [
{
"id": "ra1",
"numero": 1,
"peso": 16,
"descripcion": "...",
"criteriosEvaluacion": [
{
"id": "1a",
"descripcion": "...",
"peso": 20,
"ffe": false
}
]
}
]
}El índice (JSON/modulos/modulos.json) es ligero, solo lo necesario para el selector e identificar la imagen de cada módulo:
[
{ "id": "2daw-dwec", "nombre": "...", "abreviatura": "DWEC",
"curso": "2º DAW", "ciclo": "...", "imagen": "img/portada.png" }
]Convenciones del esquema:
pesode RAs suma 100 dentro del módulo (distribución equitativa por defecto, ajustable).pesode CEs suma 100 dentro del RA.ffe: truemarca criterios vinculados a la Formación en Centro de Trabajo (FCT) — se renderizan con un icono distintivo.- El
iddel CE ("1a","7i"...) es la clave que se usa enlocalStorage(ceStates_<moduleId>[ceId]) y enJSON/oficiales/<id>.json.
- Node.js 18+
- (Para el extractor) cuenta en Anthropic con clave API
- (Para servir las Netlify Functions en local) Netlify CLI (
npm install -g netlify-cli)
# Para el chatbot (servidor) y para regenerar los JSONs con scripts/extraer-modulos.js
ANTHROPIC_API_KEY=sk-ant-...
# Solo si quieres probar el modo profesor en local (Netlify Functions)
GITHUB_TOKEN=ghp_...
ADMIN_PASSWORD=lo-que-elijasEn producción, estas variables viven como Environment variables en Netlify. Nunca commitees el
.env— está gitignored.
git clone https://github.com/joanh/APPRA.git
cd APPRA
npm install # solo si vas a usar el extractor
netlify dev # http://localhost:8888 con Netlify Functions
# alternativamente, si solo quieres ver el frontend:
python -m http.server 8000 # http://localhost:8000 (sin functions)npm run extraer-modulosEsto lee los PDFs de .Docs/, llama a Claude Opus 4.7 con el output_config.format: json_schema para forzar respuestas estructuradas, y escribe los JSONs en JSON/modulos/. Hace caché del PDF (cache_control: ephemeral) cuando se extraen varios módulos del mismo documento, para abaratar coste.
El profesor puede publicar un estado oficial (qué CEs ha cubierto el curso, qué nivel se ha alcanzado, etc.) que el resto de usuarios puede cargar. Por debajo, esto se persiste como un commit en el repositorio (JSON/oficiales/<moduleId>.json).
- El PAT de GitHub vive como variable de entorno en Netlify (
GITHUB_TOKEN). El usuario nunca lo ve. - El admin se autentica introduciendo una contraseña corta que se compara contra
ADMIN_PASSWORD(otra env var). - La Netlify Function actúa como relé: si la contraseña coincide, hace el commit a GitHub usando el token del servidor.
- La contraseña se cachea en
sessionStoragedurante la sesión de la pestaña — el admin la introduce una vez y se reutiliza para sucesivos guardados. - Cualquier usuario puede cargar el estado oficial sin contraseña (es lectura pública del JSON).
✅ Ventajas: UX cómoda (contraseña corta, una vez por sesión) · token GitHub nunca expuesto al cliente · ideal para demos en hackathon.
ADMIN_PASSWORD). Si se filtra, hay que rotarlo y redeploy.
-
Crear un PAT classic en GitHub → Settings → Developer settings → Personal access tokens (classic):
- Note:
APPRA - Netlify State Manager - Expiration: 90 days
- Scopes:
repo(basta para escribir) - Generate token → copiar el
ghp_...a un gestor de contraseñas
- Note:
-
Añadir variables de entorno en Netlify (Site settings → Environment variables):
GITHUB_TOKEN= elghp_...que acabas de generarADMIN_PASSWORD= una contraseña memorable (única, mínimo 8 caracteres)
-
Redeploy del sitio (basta con un
git pusho un Deploy → Trigger deploy) para que las nuevas env vars cojan efecto.
| Acción | Quién | Auth |
|---|---|---|
| Cargar estado oficial | Cualquier usuario | Pública (lectura del JSON) |
| Guardar estado oficial | Admin | Contraseña → cachée en sessionStorage |
- PAT GitHub: desde tu lista de tokens. Tras rotar, actualiza
GITHUB_TOKENen Netlify y redeploy. - Contraseña admin: cambia
ADMIN_PASSWORDen Netlify y redeploy. Las pestañas con sessionStorage cacheada empezarán a recibir 401 y se limpiarán solas pidiendo la nueva.
- Maquetación completa con dark mode
- Página de portada (landing) — logo, intro, selector centrado, banner del hackathon
- Selector dinámico de módulo — el frontend se auto-personaliza al elegir
- 3 módulos extraídos del BOE con Claude Opus 4.7: DWEC (2º DAW), ED (1º DAW), SAD (2º ASIR)
- Hero por módulo — título + curso/ciclo + imagen propia (
portada.png,portada2.png,portada3.png) - Seguimiento de estados con
localStoragenamespaceado por módulo (ceStates_<id>) - Búsqueda en CEs + chips de términos frecuentes recalculados por módulo
- Exportación a JSON y CSV con etiquetas en español
- Diálogos unificados con SweetAlert2 (alerts, confirms y prompts)
- Modo profesor — token GitHub en servidor + contraseña admin corta (Opción B)
- Asistente IA con Claude Opus 4.7 + system prompts diferenciados por rol (profesor / alumno / familia)
- Estado oficial por módulo (
saveState.js→JSON/oficiales/<id>.json) - Manifest PWA + iconos
- Headers de seguridad
- Service Worker (PWA offline real)
- Más módulos (resto de DAW, ASIR completo, DAM)
- Override de horas por comunidad autónoma (Madrid usa BOCM)
- Exportación a PDF
- Exportación a DOCX
- Auditoría WCAG completa
- Soporte para módulos de otras familias profesionales
- Soporte para ESO y Bachillerato
- Autor: Jose Angel Heras (@joanh) — joseaheras@pm.me
- IA: Anthropic Claude Opus 4.7 — usado tanto para construir el proyecto como para alimentar el chatbot.
- Hackathon: Built with Opus 4.7 · Cerebral Valley
- Datos curriculares: BOE, BOCM y Consejería de Educación de la Comunidad de Madrid.
MIT. Libre uso, modificación y redistribución del código y de los JSON de los módulos. Los Reales Decretos del BOE incluidos en .Docs/ son legislación española y por tanto dominio público.
