Files
renato97 a3360ec6e4 feat: fase 2+3 — expansion masiva multi-perfil + bug fixes + hardening
Fase 2 (expansion):
- Francesca: +6 etapas matematicas (5-10), +7 etapas lectura (3-9), +2 ortografia
- Sebastian: +8 etapas matematicas (4-11), +6 lectura, +3 historia, +3 geografia, +1 banderas
- Nuevas paginas: ortografia, lectura, geografia, matematicas
- ~900 ejercicios nuevos, paridad ~500 por perfil

Fase 3 (bug fixes + hardening):
- Fix: SessionLog auto-create en grade route
- Fix: engine cache (no borrar antes de revisar)
- Fix: FSRS cards actualizadas en flujo curriculum
- Fix: responseMs real (no hardcodeado 3000)
- Fix: options dentro de content en sebastian etapa-1
- Fix: trazo-letra consonantes crash
- Security: execFileSync anti-command-injection en TTS
- DB: 18 cascade deletes + indices + unique constraints
- APIs debug: reset, state, seed
- Docker: espeak-ng + prisma + user/group
- E2E: 23 tests nuevos (curriculum-flow.spec.ts)
- Code review: tailwind colors, middleware dev mode, eslint config

Verificado: tsc exit 0, build exit 0, 61 E2E tests pass
2026-07-25 22:07:28 -03:00

212 lines
5.6 KiB
TypeScript

import type { Stage } from "../types"
const exercises: any[] = []
let idx = 0
// Part 1: Estructura de carta (partes)
const cartas = [
{
instruction: "¿Qué parte de la carta es 'Querido amigo'?",
correct: "Saludo",
wrong1: "Despedida",
wrong2: "Cuerpo",
emoji: "✉️",
},
{
instruction: "¿Qué parte de la carta es 'Atentamente, Juan'?",
correct: "Despedida",
wrong1: "Saludo",
wrong2: "Fecha",
emoji: "✍️",
},
{
instruction: "¿Qué parte de la carta es 'Buenos Aires, 15 de marzo'?",
correct: "Fecha y lugar",
wrong1: "Saludo",
wrong2: "Firma",
emoji: "📅",
},
{
instruction: "¿Qué parte de la carta va al final con tu nombre?",
correct: "Firma",
wrong1: "Saludo",
wrong2: "Destinatario",
emoji: "🖊️",
},
{
instruction: "¿Cómo se llama la persona que escribe la carta?",
correct: "Remitente",
wrong1: "Destinatario",
wrong2: "Cartero",
emoji: "📝",
},
{
instruction: "¿Cómo se llama la persona que recibe la carta?",
correct: "Destinatario",
wrong1: "Remitente",
wrong2: "Cartero",
emoji: "📬",
},
]
for (const c of cartas) {
exercises.push({
id: `seb-lect6-carta-${idx}`,
stage: 6,
skillCode: `carta-parte-${idx}`,
type: "eleccion",
prerequisites: idx === 0 ? [`seb-lect5-completar-24`] : [exercises[idx - 1].id],
content: {
instruction: c.instruction,
text: c.instruction,
audioText: c.instruction,
emoji: c.emoji,
options: [
{ label: c.correct, correct: true, id: `c-${idx}` },
{ label: c.wrong1, correct: false, id: `w1-${idx}` },
{ label: c.wrong2, correct: false, id: `w2-${idx}` },
].sort(() => Math.random() - 0.5),
},
functionalContext: "Correspondencia interescolar Freinet: conocer las partes de una carta.",
errorType: "omision-letra",
})
idx++
}
// Part 2: Ordenar la carta
const cartasOrdenar = [
{
instruction: "Ordená las partes de una carta:",
parts: ["Fecha y lugar", "Saludo", "Cuerpo de la carta", "Despedida", "Firma"],
emoji: "✉️",
},
]
for (const co of cartasOrdenar) {
const shuffled = [...co.parts].sort(() => Math.random() - 0.5)
exercises.push({
id: `seb-lect6-orden-${idx}`,
stage: 6,
skillCode: `carta-orden-${idx}`,
type: "secuencia",
prerequisites: [exercises[idx - 1].id],
content: {
instruction: co.instruction,
text: "Ordená las partes de una carta correctamente",
audioText: "Ordená las partes de una carta",
emoji: co.emoji,
sequence: shuffled,
},
functionalContext: "Correspondencia interescolar Freinet: ordenar las partes de una carta.",
errorType: "omision-letra",
})
idx++
}
// Part 3: Preguntas sobre correspondencia
const corresp = [
{
q: "¿Con qué frase podés empezar una carta a un amigo?",
correct: "Querido amigo",
wrong1: "Señor juez",
wrong2: "A quien corresponda",
emoji: "👋",
},
{
q: "¿Cómo podés despedirte en una carta a tu abuela?",
correct: "Con cariño, tu nieto",
wrong1: "Atentamente",
wrong2: "Sinceramente",
emoji: "💖",
},
{
q: "Además de cartas, ¿qué otra forma de correspondencia existe?",
correct: "Correo electrónico",
wrong1: "Teléfono",
wrong2: "Radio",
emoji: "📧",
},
{
q: "¿Qué dato debe estar en el sobre para que llegue la carta?",
correct: "La dirección del destinatario",
wrong1: "El teléfono del remitente",
wrong2: "La edad del cartero",
emoji: "🏠",
},
{
q: "¿Qué se necesita para enviar una carta por correo?",
correct: "Un sello / estampilla",
wrong1: "Una foto",
wrong2: "Un código de barras",
emoji: "💌",
},
{
q: "¿Qué escribís en el cuerpo de la carta?",
correct: "Lo que querés contar",
wrong1: "Solo el saludo",
wrong2: "Solo la fecha",
emoji: "📄",
},
{
q: "¿Por qué es importante escribir la fecha en una carta?",
correct: "Para saber cuándo se escribió",
wrong1: "Para decorar la hoja",
wrong2: "Para que llegue más rápido",
emoji: "📅",
},
{
q: "Si le escribís una carta a tu maestra, ¿qué saludo usás?",
correct: "Querida maestra",
wrong1: "Hola, che",
wrong2: "Señora, le comunico",
emoji: "👩‍🏫",
},
{
q: "¿Qué información va en el remitente?",
correct: "Tu nombre y dirección",
wrong1: "Solo tu nombre",
wrong2: "La dirección del destinatario",
emoji: "📤",
},
{
q: "¿Qué significa 'correspondencia'?",
correct: "Intercambio de cartas y mensajes",
wrong1: "Un tipo de baile",
wrong2: "Un juego de mesa",
emoji: "📬",
},
]
for (const cor of corresp) {
exercises.push({
id: `seb-lect6-corresp-${idx}`,
stage: 6,
skillCode: `correspondencia-${idx}`,
type: "eleccion",
prerequisites: [exercises[idx - 1].id],
content: {
instruction: cor.q,
text: cor.q,
audioText: cor.q,
emoji: cor.emoji,
options: [
{ label: cor.correct, correct: true, id: `c-${idx}` },
{ label: cor.wrong1, correct: false, id: `w1-${idx}` },
{ label: cor.wrong2, correct: false, id: `w2-${idx}` },
].sort(() => Math.random() - 0.5),
},
functionalContext: "Correspondencia interescolar Freinet: conocer el formato y propósito de las cartas.",
errorType: "omision-letra",
})
idx++
}
const stage: Stage = {
id: 6,
name: "Correspondencia interescolar (Freinet)",
description: "Aprender la estructura de una carta: saludo, cuerpo, despedida, firma, y cómo escribir correspondencia.",
exercises,
}
export default stage