Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ WANDBOX_COMPILER_OPTIONS=""
WANDBOX_CSHARP_COMPILER="" # default mono-6.12.0.199
WANDBOX_CSHARP_OPTIONS="" # vacío: el subconjunto del curso no necesita flags

# Perfil sql-sqlite3-wandbox — SIN default. Verifica el compiler SQLite/SQLite3
# estable (no *-head) contra https://wandbox.org/api/list.json y ponlo aquí.
# Sin esta variable, el perfil SQL queda registrado pero NO disponible para
# ejecutar (mismo patrón que JUDGE0_CSHARP_LANGUAGE_ID / PISTON_CSHARP_VERSION).
WANDBOX_SQL_COMPILER=""
WANDBOX_SQL_OPTIONS="" # vacío: el subconjunto SQL del curso no necesita flags

# piston / piston-selfhosted
PISTON_URL="" # obligatorio con piston-selfhosted (ej http://127.0.0.1:2000)
PISTON_CPP_VERSION=""
Expand Down
55 changes: 55 additions & 0 deletions prisma/content/courses/bases-de-datos/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { defineCourse } from "../../authoring";

import { bd1Fundamentos } from "./units/01-fundamentos-sgbd";
import { bd1Requerimientos } from "./units/02-requerimientos-informacion";
import { bd1ModeloEr } from "./units/03-modelo-er";
import { bd1ModeloRelacional } from "./units/04-modelo-relacional";
import { bd1Normalizacion } from "./units/05-normalizacion";
import { bd1AlgebraRelacional } from "./units/06-algebra-relacional";
import { bd1Ddl } from "./units/07-ddl";
import { bd1Dml } from "./units/08-dml";
import { bd1ConsultasReportes } from "./units/09-consultas-reportes";
import { bd1RespaldoIntegrador } from "./units/10-respaldo-integrador";

// =====================================================================
// Paquete de curso: Bases de datos (Base de Datos I, S4).
//
// Curso nuevo y lenguaje nuevo de plataforma: `sql`, perfil
// `sql-sqlite3-wandbox` (ver TECHNICAL_CONTRACT del paquete de contenido
// `bd1-ceti-content-pack`). El SQL didáctico usa el subconjunto relacional
// portable compatible con SQLite; backup/restore real y la GUI de
// escritorio que pide el programa oficial quedan como laboratorio local.
//
// 10 unidades, una sola CurriculumSection semestral, las 10 nacen
// `published: true` — sin release gate para esta entrega.
// =====================================================================

export const basesDeDatos = defineCourse({
slug: "bases-de-datos",
title: "Bases de datos",
description:
"Diseña bases relacionales desde necesidades reales, normalízalas y consulta información con SQL.",
subjectName: "Bases de datos",
academicContext: "CETI · Tecnólogo en Desarrollo de Software",
language: "sql",
executionProfile: "sql-sqlite3-wandbox",
curriculum: [
{
key: "s4-base-de-datos-1",
semester: 4,
subjectName: "Base de Datos I",
units: [
bd1Fundamentos,
bd1Requerimientos,
bd1ModeloEr,
bd1ModeloRelacional,
bd1Normalizacion,
bd1AlgebraRelacional,
bd1Ddl,
bd1Dml,
bd1ConsultasReportes,
bd1RespaldoIntegrador,
],
},
],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { defineUnit } from "../../../../authoring";

import { leccion01 } from "./lessons/01-dato-informacion";
import { leccion02 } from "./lessons/02-evolucion-sistemas-datos";
import { leccion03 } from "./lessons/03-componentes-sgbd";
import { leccion04 } from "./lessons/04-relacional-en-una-frase";

import { practice } from "./practice";

export const bd1Fundamentos = defineUnit({
slug: "bd1-01-fundamentos-sgbd",
title: "Información, bases de datos y SGBD",
description:
"Distingue dato de información, entiende para qué sirve un SGBD y adopta el vocabulario relacional básico.",
icon: "🗄️",
published: true,
lessons: [leccion01, leccion02, leccion03, leccion04],
practice,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { defineLesson } from "../../../../../authoring";

export const leccion01 = defineLesson({
slug: "dato-informacion",
title: "Dato, información y necesidad",
description: "Distingue dato almacenado de información útil para una decisión.",
estimatedMinutes: 15,
xpReward: 45,
steps: [
{
type: "theory",
markdown: `Un **dato** es una representación registrada: \`13\`, \`CERRADO\`, \`2026-09-02\`. Se vuelve información cuando se interpreta dentro de un contexto para responder una pregunta.

Una base de datos no es valiosa por guardar mucho, sino por conservar datos con estructura suficiente para recuperarlos, relacionarlos y producir información confiable.`,
},
{
type: "matching",
pairs: [
{ left: "13", right: "Dato sin contexto" },
{ left: "13 tickets abiertos", right: "Información contextualizada" },
{ left: "¿Qué técnico tiene más carga?", right: "Necesidad de información" },
{ left: "Conteo por técnico", right: "Resultado que ayuda a decidir" },
],
explanation: "La pregunta de negocio determina qué datos hay que conservar.",
},
{
type: "quiz",
question: "¿Cuál es la mejor razón para usar una base de datos?",
options: [
"Guardar cualquier cosa",
"Conservar y consultar datos relacionados de forma controlada",
"Evitar modelar",
"Sustituir la aplicación",
],
correctIndex: 1,
explanation: "El SGBD ayuda a organizar, proteger y consultar información.",
},
{
type: "fill_blank",
prompt: "Completa los espacios.",
template: "datos + {{0}} + contexto → {{1}}",
blanks: [
{ answer: "estructura" },
{ answer: "información" },
],
explanation: "La estructura permite interpretar y relacionar datos.",
},
],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { defineLesson } from "../../../../../authoring";

export const leccion02 = defineLesson({
slug: "evolucion-sistemas-datos",
title: "De archivos aislados a SGBD",
description: "Explica qué problemas resuelve un gestor frente a archivos dispersos.",
estimatedMinutes: 15,
xpReward: 45,
steps: [
{
type: "theory",
markdown: `Archivos sueltos pueden funcionar al inicio, pero duplican datos, dificultan búsquedas simultáneas y vuelven frágiles las reglas de integridad.

Un SGBD centraliza mecanismos para definir estructura, consultar, modificar, controlar acceso, respaldar y recuperar información. No elimina la necesidad de buen diseño.`,
},
{
type: "matching",
pairs: [
{ left: "Duplicación de cliente en muchos archivos", right: "Redundancia" },
{ left: "Dos nombres distintos para el mismo cliente", right: "Inconsistencia" },
{ left: "No poder relacionar ticket con técnico", right: "Falta de modelo" },
{ left: "Recuperar tras pérdida", right: "Necesidad de respaldo" },
],
explanation: "Los problemas de datos motivan mecanismos del SGBD.",
},
{
type: "quiz",
question: "¿Qué afirmación es correcta?",
options: [
"Un SGBD corrige automáticamente un mal modelo",
"Un SGBD ofrece herramientas, pero el diseño sigue siendo responsabilidad del equipo",
"SQL reemplaza requisitos",
"Una tabla siempre equivale a un archivo",
],
correctIndex: 1,
explanation: "Herramienta y modelo son responsabilidades diferentes.",
},
{
type: "code_completion",
prompt: "Ordena correctamente.",
lines: [
"Necesidad de información",
"Modelo de datos",
"Estructura en SGBD",
"Datos",
"Consultas/reportes",
],
explanation: "La tecnología aparece después de entender qué información se necesita.",
},
],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { defineLesson } from "../../../../../authoring";

export const leccion03 = defineLesson({
slug: "componentes-sgbd",
title: "Componentes y responsabilidades de un SGBD",
description: "Reconoce esquema, motor, almacenamiento, consultas y administración.",
estimatedMinutes: 15,
xpReward: 45,
steps: [
{
type: "theory",
markdown: `En este curso usamos "SGBD" como sistema que interpreta definiciones y consultas sobre datos persistentes.

Conceptualmente distinguimos: **esquema** (estructura declarada), **datos**, **motor** que ejecuta operaciones, lenguaje de definición/manipulación y herramientas de administración como respaldo/restauración.`,
},
{
type: "matching",
pairs: [
{ left: "Esquema", right: "Definición de tablas y restricciones" },
{ left: "Motor", right: "Ejecuta operaciones" },
{ left: "DDL", right: "Define estructura" },
{ left: "DML", right: "Modifica datos" },
{ left: "Backup", right: "Copia recuperable" },
],
explanation: "Cada pieza responde a una responsabilidad distinta.",
},
{
type: "quiz",
question: "¿Dónde pertenece `CREATE TABLE`?",
options: ["DML", "DDL", "Reporte", "Backup"],
correctIndex: 1,
explanation: "CREATE define estructura.",
},
{
type: "fill_blank",
prompt: "Completa los espacios.",
template: "{{0}} define la estructura; {{1}} modifica datos.",
blanks: [
{ answer: "DDL" },
{ answer: "DML" },
],
explanation: "La separación reaparece durante todo el curso.",
},
],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { defineLesson } from "../../../../../authoring";

export const leccion04 = defineLesson({
slug: "relacional-en-una-frase",
title: "Qué significa relacional",
description:
"Entiende tablas, filas, atributos y relaciones sin confundir \"relación\" con sólo una llave foránea.",
estimatedMinutes: 15,
xpReward: 45,
steps: [
{
type: "theory",
markdown: `El modelo relacional representa información mediante **relaciones** que solemos visualizar como tablas. Cada fila representa una tupla y cada columna un atributo con significado definido.

Relacionar información exige atributos que permitan identificar y vincular hechos sin duplicarlos innecesariamente.`,
},
{
type: "matching",
pairs: [
{ left: "tabla/relation", right: "Conjunto estructurado de filas" },
{ left: "fila/tupla", right: "Una ocurrencia" },
{ left: "columna/atributo", right: "Propiedad definida" },
{ left: "clave", right: "Atributo(s) que identifican o vinculan" },
],
explanation: "Este vocabulario será la base para normalización y SQL.",
},
{
type: "quiz",
question: "En una tabla `cliente(id,nombre)`, ¿qué representa una fila?",
options: ["Una columna", "Un cliente registrado", "Todo el esquema", "El SGBD"],
correctIndex: 1,
explanation: "La fila es una ocurrencia del tipo de hecho modelado.",
},
{
type: "code_example",
code: `CREATE TABLE cliente(id INTEGER PRIMARY KEY, nombre TEXT NOT NULL);
INSERT INTO cliente VALUES (1,'Ana'),(2,'Luis');
SELECT id,nombre FROM cliente ORDER BY id;`,
explanation: "El ejemplo sólo anticipa la forma final: estructura + datos + consulta.",
runnable: true,
expectedOutput: `1|Ana
2|Luis`,
},
],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import type { PracticeExerciseDefinition } from "../../../../exercises/types";

export const practice = [
{
slug: "bd1-vocabulario-tabla",
title: "Leer una tabla",
description: "Consulta todos los clientes ordenados por id.",
prompt: "Consulta todos los clientes ordenados por id.",
starterCode: `-- escribe tu SQL aquí
`,
solutionCode: `SELECT id,nombre FROM cliente ORDER BY id;`,
difficulty: "easy",
xpReward: 20,
testCases: [
{
description: "dataset visible",
stdin: `CREATE TABLE cliente(id INTEGER PRIMARY KEY, nombre TEXT NOT NULL, ciudad TEXT NOT NULL);
INSERT INTO cliente VALUES (1,'Ana','GDL'),(2,'Luis','Zapopan'),(3,'Mara','GDL');`,
expectedStdout: `1|Ana
2|Luis
3|Mara`,
visible: true,
},
{
description: "orden diferente",
stdin: `CREATE TABLE cliente(id INTEGER PRIMARY KEY,nombre TEXT,ciudad TEXT); INSERT INTO cliente VALUES(8,'Eva','Tlaquepaque'),(4,'Noe','GDL');`,
expectedStdout: `4|Noe
8|Eva`,
visible: false,
},
],
},
{
slug: "bd1-contar-registros",
title: "Contar filas",
description: "Cuenta cuántos clientes hay.",
prompt: "Cuenta cuántos clientes hay.",
starterCode: `-- escribe tu SQL aquí
`,
solutionCode: `SELECT COUNT(*) FROM cliente;`,
difficulty: "easy",
xpReward: 20,
testCases: [
{
description: "tres clientes",
stdin: `CREATE TABLE cliente(id INTEGER PRIMARY KEY, nombre TEXT NOT NULL, ciudad TEXT NOT NULL);
INSERT INTO cliente VALUES (1,'Ana','GDL'),(2,'Luis','Zapopan'),(3,'Mara','GDL');`,
expectedStdout: `3`,
visible: true,
},
{
description: "un cliente",
stdin: `CREATE TABLE cliente(id INTEGER PRIMARY KEY,nombre TEXT,ciudad TEXT); INSERT INTO cliente VALUES(1,'A','X');`,
expectedStdout: `1`,
visible: false,
},
{
description: "tabla vacía",
stdin: `CREATE TABLE cliente(id INTEGER PRIMARY KEY,nombre TEXT,ciudad TEXT);`,
expectedStdout: `0`,
visible: false,
},
],
},
] satisfies PracticeExerciseDefinition[];
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { defineUnit } from "../../../../authoring";

import { leccion01 } from "./lessons/01-preguntas-antes-tablas";
import { leccion02 } from "./lessons/02-tecnicas-recoleccion";
import { leccion03 } from "./lessons/03-abstraer-clasificar-filtrar";
import { leccion04 } from "./lessons/04-requerimientos-documentados";

import { practice } from "./practice";

export const bd1Requerimientos = defineUnit({
slug: "bd1-02-requerimientos-informacion",
title: "Requerimientos de información",
description:
"Recolecta y documenta requerimientos de información antes de diseñar cualquier tabla.",
icon: "🔎",
published: true,
lessons: [leccion01, leccion02, leccion03, leccion04],
practice,
});
Loading
Loading