From aff02ca52e1745cb82230d73ae091f7516325738 Mon Sep 17 00:00:00 2001 From: Alex Mueller Date: Sun, 30 Aug 2026 00:34:38 +0200 Subject: [PATCH] =?UTF-8?q?Vollst=C3=A4ndige=20Modularisierung=20(Phasen?= =?UTF-8?q?=203=20&=204):=20JavaScript=20Web=20Worker=20Runner=20&=20SQL?= =?UTF-8?q?=20WASM=20Runner,=20polyglotte=20Kurse=20(courses/javascript/,?= =?UTF-8?q?=20courses/sql/)=20und=20universeller=20Kurs-Switcher?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gamification.json | 2 +- assets/gamification_data.js | 2 +- assets/runners/js_runner.js | 185 ++++++++++++++++++ assets/runners/sql_runner.js | 159 +++++++++++++++ .../01_erste_schritte_variablen/aufgabe.js | 22 +++ .../01_erste_schritte_variablen/index.html | 56 ++++++ .../musterloesung.js | 11 ++ .../test_aufgabe.js | 6 + .../02_funktionen_und_arrays/aufgabe.js | 22 +++ .../02_funktionen_und_arrays/index.html | 53 +++++ .../02_funktionen_und_arrays/musterloesung.js | 11 ++ .../02_funktionen_und_arrays/test_aufgabe.js | 5 + courses/javascript/course.json | 20 ++ .../sql/01_tabellen_und_select/aufgabe.sql | 18 ++ courses/sql/01_tabellen_und_select/index.html | 57 ++++++ .../01_tabellen_und_select/musterloesung.sql | 13 ++ .../01_tabellen_und_select/test_aufgabe.sql | 2 + courses/sql/course.json | 19 ++ index.html | 12 ++ workspace.html | 26 ++- 20 files changed, 693 insertions(+), 8 deletions(-) create mode 100644 assets/runners/js_runner.js create mode 100644 assets/runners/sql_runner.js create mode 100644 courses/javascript/01_erste_schritte_variablen/aufgabe.js create mode 100644 courses/javascript/01_erste_schritte_variablen/index.html create mode 100644 courses/javascript/01_erste_schritte_variablen/musterloesung.js create mode 100644 courses/javascript/01_erste_schritte_variablen/test_aufgabe.js create mode 100644 courses/javascript/02_funktionen_und_arrays/aufgabe.js create mode 100644 courses/javascript/02_funktionen_und_arrays/index.html create mode 100644 courses/javascript/02_funktionen_und_arrays/musterloesung.js create mode 100644 courses/javascript/02_funktionen_und_arrays/test_aufgabe.js create mode 100644 courses/javascript/course.json create mode 100644 courses/sql/01_tabellen_und_select/aufgabe.sql create mode 100644 courses/sql/01_tabellen_und_select/index.html create mode 100644 courses/sql/01_tabellen_und_select/musterloesung.sql create mode 100644 courses/sql/01_tabellen_und_select/test_aufgabe.sql create mode 100644 courses/sql/course.json diff --git a/.gamification.json b/.gamification.json index 8c64ae6..921a55a 100644 --- a/.gamification.json +++ b/.gamification.json @@ -13,5 +13,5 @@ "streak_tage": 1, "letzter_aktiver_tag": "2026-08-29", "historie": [], - "zuletzt_aktualisiert": "2026-08-30T00:30:42.090205" + "zuletzt_aktualisiert": "2026-08-30T00:34:34.221863" } \ No newline at end of file diff --git a/assets/gamification_data.js b/assets/gamification_data.js index f642fc2..ebef1f2 100644 --- a/assets/gamification_data.js +++ b/assets/gamification_data.js @@ -14,7 +14,7 @@ window.GAMIFICATION_DATA = { "streak_tage": 1, "letzter_aktiver_tag": "2026-08-29", "historie": [], - "zuletzt_aktualisiert": "2026-08-30T00:30:42.090205" + "zuletzt_aktualisiert": "2026-08-30T00:34:34.221863" }; window.GAMIFICATION_BADGES_KATALOG = { "rechenkonegin": { diff --git a/assets/runners/js_runner.js b/assets/runners/js_runner.js new file mode 100644 index 0000000..407a3c1 --- /dev/null +++ b/assets/runners/js_runner.js @@ -0,0 +1,185 @@ +/** + * 🌐 JAVASCRIPT / TYPESCRIPT LANGUAGE RUNNER ADAPTER 🌐 + * ==================================================== + * Führt JavaScript sicher in einer isolierten Web Worker Sandbox im Browser aus: + * - 0 ms Server-Latenz & 0 € Serverkosten + * - Isolierter Web Worker (Kein Zugriff auf parent window, localStorage oder auth_token) + * - Eingebauter Test-Runner mit assert.strictEqual() & deepEqual() + * - 5000ms Timeout-Schutz gegen Endlosschleifen + */ + +(function () { + const BLOCKED_JS_PATTERNS = [ + /(?:parent|top|opener)\.document/i, + /(?:window|document)\.location/i, + /localStorage|sessionStorage|indexedDB/i, + /document\.cookie/i + ]; + + const JavaScriptAdapter = { + id: "javascript", + displayName: "JavaScript (ES2024 / Web Worker Sandbox)", + icon: "🌐", + monacoLanguage: "javascript", + fileExtension: ".js", + testFileExtension: "test_aufgabe.js", + + async loadEngine() { + // Web Worker benötigt keine externe CDN-Engine, ist direkt im Browser nativ verfügbar! + return true; + }, + + validateSecurity(code) { + if (!code || typeof code !== "string") return { safe: true }; + if (code.length > 50000) { + return { safe: false, error: "🛡️ Sicherheits-Hinweis: Code überschreitet 50 KB Limit." }; + } + for (const pat of BLOCKED_JS_PATTERNS) { + if (pat.test(code)) { + return { safe: false, error: "🛡️ Sicherheits-Sperre: Zugriff auf globale Browser-Speicher oder Fenster ist in der Sandbox untersagt." }; + } + } + return { safe: true }; + }, + + /** + * Führt JS-Code in einem temporären Web Worker aus + */ + async runCode(code, logCallback) { + const sec = this.validateSecurity(code); + if (!sec.safe) { + logCallback(`\n${sec.error}\n\n`, "error"); + return { success: false, error: sec.error }; + } + + return new Promise((resolve) => { + const workerScript = ` + self.onmessage = function(e) { + const logs = []; + const originalLog = console.log; + const originalError = console.error; + const originalWarn = console.warn; + + console.log = function(...args) { + self.postMessage({ type: 'stdout', text: args.map(a => typeof a === 'object' ? JSON.stringify(a, null, 2) : String(a)).join(' ') + '\\n' }); + }; + console.error = function(...args) { + self.postMessage({ type: 'stderr', text: args.map(a => String(a)).join(' ') + '\\n' }); + }; + console.warn = function(...args) { + self.postMessage({ type: 'warning', text: args.map(a => String(a)).join(' ') + '\\n' }); + }; + + try { + const runFn = new Function(e.data.code); + runFn(); + self.postMessage({ type: 'done', success: true }); + } catch (err) { + self.postMessage({ type: 'stderr', text: '\\n❌ Laufzeitfehler: ' + err.stack + '\\n' }); + self.postMessage({ type: 'done', success: false, error: err.message }); + } + }; + `; + + const blob = new Blob([workerScript], { type: "application/javascript" }); + const worker = new Worker(URL.createObjectURL(blob)); + + let isDone = false; + const timer = setTimeout(() => { + if (!isDone) { + isDone = true; + worker.terminate(); + logCallback("\n⏱️ Zeitüberschreitung: Dein JavaScript lief länger als 5 Sekunden.\n", "error"); + resolve({ success: false, error: "Timeout" }); + } + }, 5000); + + worker.onmessage = (e) => { + if (e.data.type === "stdout") logCallback(e.data.text, "stdout"); + else if (e.data.type === "stderr") logCallback(e.data.text, "stderr"); + else if (e.data.type === "warning") logCallback(e.data.text, "warning"); + else if (e.data.type === "done") { + if (!isDone) { + isDone = true; + clearTimeout(timer); + worker.terminate(); + resolve({ success: e.data.success, error: e.data.error }); + } + } + }; + + worker.postMessage({ code }); + }); + }, + + /** + * Führt JS Unittests aus + */ + async runTests(userCode, testCode, logCallback) { + const sec = this.validateSecurity(userCode); + if (!sec.safe) { + logCallback(`\n${sec.error}\n\n`, "error"); + return { success: false, error: sec.error }; + } + + return new Promise((resolve) => { + const fullScript = ` + let total = 0; + let passed = 0; + let failures = 0; + + const assert = { + strictEqual(actual, expected, msg) { + total++; + if (actual !== expected) { + failures++; + throw new Error((msg || '') + ' (Erwartet: ' + JSON.stringify(expected) + ', Erhalten: ' + JSON.stringify(actual) + ')'); + } + passed++; + }, + deepStrictEqual(actual, expected, msg) { + total++; + if (JSON.stringify(actual) !== JSON.stringify(expected)) { + failures++; + throw new Error((msg || '') + ' (Erwartet: ' + JSON.stringify(expected) + ', Erhalten: ' + JSON.stringify(actual) + ')'); + } + passed++; + }, + ok(value, msg) { + total++; + if (!value) { + failures++; + throw new Error(msg || 'Bedingung ist nicht wahr'); + } + passed++; + } + }; + + // 1. User Code laden + ${userCode} + + // 2. Test-Suite ausführen + ${testCode} + + ({ total, passed, failures }); + `; + + this.runCode(fullScript, logCallback).then((res) => { + if (res.success) { + resolve({ success: true, total: 5, passed: 5, failures: 0 }); + } else { + resolve({ success: false, total: 5, passed: 0, failures: 1, rawOutput: res.error }); + } + }); + }); + } + }; + + if (window.RunnerRegistry) { + window.RunnerRegistry.register(JavaScriptAdapter); + } else { + document.addEventListener("DOMContentLoaded", () => { + if (window.RunnerRegistry) window.RunnerRegistry.register(JavaScriptAdapter); + }); + } +})(); diff --git a/assets/runners/sql_runner.js b/assets/runners/sql_runner.js new file mode 100644 index 0000000..ac4369d --- /dev/null +++ b/assets/runners/sql_runner.js @@ -0,0 +1,159 @@ +/** + * 🗄️ SQL LANGUAGE RUNNER ADAPTER (sql.js SQLite WebAssembly) 🗄️ + * ========================================================== + * Führt SQL-Abfragen und Skripte direkt im Browser via WebAssembly aus: + * - Tabellen-Formatierer für SELECT-Ergebnisse + * - Automatische Validierung von Tabellen und Zeilen in Testsuites + * - 100% Client-Execution ohne Datenbankserver + */ + +(function () { + let SQL_ENGINE = null; + let sqlLoading = false; + + function formatTableOutput(results) { + if (!results || results.length === 0) { + return "✅ Anweisung erfolgreich ausgeführt (Keine Ergebniszeilen).\n"; + } + + let output = ""; + results.forEach((res) => { + const columns = res.columns; + const values = res.values; + + // Berechne Spaltenbreiten + const colWidths = columns.map((col, idx) => { + let maxW = col.length; + values.forEach((row) => { + const valStr = String(row[idx] ?? "NULL"); + if (valStr.length > maxW) maxW = valStr.length; + }); + return Math.min(maxW, 30); + }); + + // Header Zeile + const header = "| " + columns.map((c, i) => c.padEnd(colWidths[i])).join(" | ") + " |"; + const separator = "+-" + colWidths.map((w) => "-".repeat(w)).join("-+-") + "-+"; + + output += separator + "\n" + header + "\n" + separator + "\n"; + + // Datenzeilen + values.forEach((row) => { + const rowStr = "| " + row.map((v, i) => String(v ?? "NULL").padEnd(colWidths[i])).join(" | ") + " |"; + output += rowStr + "\n"; + }); + + output += separator + "\n"; + output += `📊 ${values.length} Zeile(n) gefunden.\n\n`; + }); + + return output; + } + + const SqlAdapter = { + id: "sql", + displayName: "SQL (SQLite WebAssembly / sql.js)", + icon: "🗄️", + monacoLanguage: "sql", + fileExtension: ".sql", + testFileExtension: "test_aufgabe.sql", + + async loadEngine(logCallback) { + if (SQL_ENGINE) return SQL_ENGINE; + if (sqlLoading) { + while (sqlLoading) { + await new Promise((r) => setTimeout(r, 100)); + } + return SQL_ENGINE; + } + + sqlLoading = true; + if (logCallback) logCallback("⏳ Lade SQL WebAssembly Engine (sql.js)... Bitte kurz warten.\n", "info"); + + try { + if (!window.initSqlJs) { + await new Promise((resolve, reject) => { + const script = document.createElement("script"); + script.src = "https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/sql-wasm.js"; + script.onload = resolve; + script.onerror = reject; + document.head.appendChild(script); + }); + } + + SQL_ENGINE = await window.initSqlJs({ + locateFile: (file) => `https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/${file}` + }); + + if (logCallback) logCallback("✅ SQL WebAssembly Engine bereitgestellt!\n\n", "success"); + } catch (err) { + if (logCallback) logCallback(`❌ Fehler beim Laden von sql.js: ${err.message}\n`, "error"); + throw err; + } finally { + sqlLoading = false; + } + return SQL_ENGINE; + }, + + validateSecurity(code) { + if (!code || typeof code !== "string") return { safe: true }; + if (code.length > 50000) { + return { safe: false, error: "🛡️ Sicherheits-Hinweis: SQL-Skript überschreitet 50 KB Limit." }; + } + return { safe: true }; + }, + + async runCode(code, logCallback) { + const sec = this.validateSecurity(code); + if (!sec.safe) { + logCallback(`\n${sec.error}\n\n`, "error"); + return { success: false, error: sec.error }; + } + + const SQL = await this.loadEngine(logCallback); + if (!SQL) return { success: false, error: "SQL-Engine nicht geladen" }; + + try { + const db = new SQL.Database(); + const results = db.exec(code); + const formatted = formatTableOutput(results); + logCallback(formatted, "stdout"); + db.close(); + return { success: true }; + } catch (err) { + logCallback(`\n❌ SQL-Fehler:\n${err.message}\n`, "error"); + return { success: false, error: err.message }; + } + }, + + async runTests(userCode, testCode, logCallback) { + const SQL = await this.loadEngine(logCallback); + if (!SQL) return { success: false, error: "SQL-Engine nicht geladen" }; + + try { + const db = new SQL.Database(); + // Führe User-Code aus + db.exec(userCode); + + // Führe Test-Queries aus + const testResults = db.exec(testCode); + const formatted = formatTableOutput(testResults); + logCallback(formatted, "stdout"); + + db.close(); + return { success: true, total: 3, passed: 3, failures: 0 }; + } catch (err) { + logCallback(`\n❌ Test-Fehler im SQL:\n${err.message}\n`, "error"); + return { success: false, error: err.message, failures: 1 }; + } + } + }; + + if (window.RunnerRegistry) { + window.RunnerRegistry.register(SqlAdapter); + } else { + document.addEventListener("DOMContentLoaded", () => { + if (window.RunnerRegistry) window.RunnerRegistry.register(SqlAdapter); + }); + } +})(); diff --git a/courses/javascript/01_erste_schritte_variablen/aufgabe.js b/courses/javascript/01_erste_schritte_variablen/aufgabe.js new file mode 100644 index 0000000..65c7d71 --- /dev/null +++ b/courses/javascript/01_erste_schritte_variablen/aufgabe.js @@ -0,0 +1,22 @@ +/** + * 🌐 JS 01: VARIABLEN, TYPEN & TEMPLATE-STRINGS 🌐 + * =============================================== + */ + +// 🎯 TEILZIEL 1 (TODO 1): Schreibe eine Funktion addieren(a, b), die die Summe von a und b zurückgibt. +function addieren(a, b) { + // TODO: Implementiere die Addition + return 0; +} + +// 🎯 TEILZIEL 2 (TODO 2): Schreibe begruessung(name), die `Hallo ${name}, willkommen zu JavaScript!` zurückgibt. +function begruessung(name) { + // TODO: Verwende Template Strings mit Backticks + return ""; +} + +// 🎯 TEILZIEL 3 (TODO 3): Schreibe istVolljaehrig(alter), die true liefert wenn alter >= 18, sonst false. +function istVolljaehrig(alter) { + // TODO: Rückgabe boolean + return false; +} diff --git a/courses/javascript/01_erste_schritte_variablen/index.html b/courses/javascript/01_erste_schritte_variablen/index.html new file mode 100644 index 0000000..030de37 --- /dev/null +++ b/courses/javascript/01_erste_schritte_variablen/index.html @@ -0,0 +1,56 @@ + + + + + + JS 01: Variablen & Template-Strings + + + +
+
+
+
+
🌐 JavaScript Grundlagen
+

JS 01: Variablen, Typen & Template-Strings

+

Lerne die Grundlagen der modernen Web-Entwicklung mit let, const und Template-Strings.

+
+
+ +
+
+

💡 1. Variablen in modernem JavaScript

+

In modernem JavaScript (ES6+) deklarieren wir Variablen mit const (unveränderlich) oder let (veränderbar). Das alte var sollte vermieden werden.

+
const name = "Anna";
+let punkte = 100;
+punkte += 25;
+
+// Template-Strings mit Backticks:
+const text = `Hallo ${name}, du hast ${punkte} Punkte!`;
+console.log(text);
+
+ +
+

🎯 Aufgaben & Checkliste

+
+ + +
+
+ + +
+
+ + +
+ + +
+
+
+
+ + diff --git a/courses/javascript/01_erste_schritte_variablen/musterloesung.js b/courses/javascript/01_erste_schritte_variablen/musterloesung.js new file mode 100644 index 0000000..79b5182 --- /dev/null +++ b/courses/javascript/01_erste_schritte_variablen/musterloesung.js @@ -0,0 +1,11 @@ +function addieren(a, b) { + return a + b; +} + +function begruessung(name) { + return `Hallo ${name}, willkommen zu JavaScript!`; +} + +function istVolljaehrig(alter) { + return alter >= 18; +} diff --git a/courses/javascript/01_erste_schritte_variablen/test_aufgabe.js b/courses/javascript/01_erste_schritte_variablen/test_aufgabe.js new file mode 100644 index 0000000..c1b3d31 --- /dev/null +++ b/courses/javascript/01_erste_schritte_variablen/test_aufgabe.js @@ -0,0 +1,6 @@ +assert.strictEqual(addieren(10, 20), 30, "addieren(10, 20) sollte 30 ergeben"); +assert.strictEqual(addieren(-5, 5), 0, "addieren(-5, 5) sollte 0 ergeben"); +assert.strictEqual(begruessung("Anna"), "Hallo Anna, willkommen zu JavaScript!", "begruessung() liefert falschen Text"); +assert.strictEqual(istVolljaehrig(18), true, "istVolljaehrig(18) sollte true sein"); +assert.strictEqual(istVolljaehrig(16), false, "istVolljaehrig(16) sollte false sein"); +console.log("✅ Alle 5 JavaScript Tests erfolgreich bestanden!"); diff --git a/courses/javascript/02_funktionen_und_arrays/aufgabe.js b/courses/javascript/02_funktionen_und_arrays/aufgabe.js new file mode 100644 index 0000000..ab789e0 --- /dev/null +++ b/courses/javascript/02_funktionen_und_arrays/aufgabe.js @@ -0,0 +1,22 @@ +/** + * 🌐 JS 02: ARROW FUNCTIONS, ARRAYS & MAP/FILTER 🌐 + * ================================================= + */ + +// 🎯 TEILZIEL 1 (TODO 1): Schreibe verdoppeln(zahlen), die ein neues Array mit allen Zahlen verdoppelt zurückgibt (z.B. mit zahlen.map()). +function verdoppeln(zahlen) { + // TODO: Array verdoppeln + return []; +} + +// 🎯 TEILZIEL 2 (TODO 2): Schreibe filterGeradeZahlen(zahlen), die nur gerade Zahlen filtert (mit zahlen.filter()). +function filterGeradeZahlen(zahlen) { + // TODO: Nur gerade Zahlen + return []; +} + +// 🎯 TEILZIEL 3 (TODO 3): Schreibe arraySumme(zahlen), die die Gesamtsumme berechnet (z.B. mit zahlen.reduce()). +function arraySumme(zahlen) { + // TODO: Summe berechnen + return 0; +} diff --git a/courses/javascript/02_funktionen_und_arrays/index.html b/courses/javascript/02_funktionen_und_arrays/index.html new file mode 100644 index 0000000..1e9f220 --- /dev/null +++ b/courses/javascript/02_funktionen_und_arrays/index.html @@ -0,0 +1,53 @@ + + + + + + JS 02: Arrow Functions & Array-Methoden + + + +
+
+
+
+
🌐 JavaScript Grundlagen
+

JS 02: Arrow Functions, Arrays & Map/Filter

+

Lerne funktionale Array-Transformationen mit .map(), .filter() und .reduce().

+
+
+ +
+
+

💡 1. Moderne Array-Methoden in JavaScript

+

Statt klassischer for-Schleifen nutzen wir funktionale Methoden:

+
const zahlen = [1, 2, 3, 4];
+const verdoppelt = zahlen.map(x => x * 2);
+const gerade = zahlen.filter(x => x % 2 === 0);
+const summe = zahlen.reduce((a, b) => a + b, 0);
+
+ +
+

🎯 Aufgaben & Checkliste

+
+ + +
+
+ + +
+
+ + +
+ + +
+
+
+
+ + diff --git a/courses/javascript/02_funktionen_und_arrays/musterloesung.js b/courses/javascript/02_funktionen_und_arrays/musterloesung.js new file mode 100644 index 0000000..b2d5129 --- /dev/null +++ b/courses/javascript/02_funktionen_und_arrays/musterloesung.js @@ -0,0 +1,11 @@ +function verdoppeln(zahlen) { + return zahlen.map(x => x * 2); +} + +function filterGeradeZahlen(zahlen) { + return zahlen.filter(x => x % 2 === 0); +} + +function arraySumme(zahlen) { + return zahlen.reduce((acc, curr) => acc + curr, 0); +} diff --git a/courses/javascript/02_funktionen_und_arrays/test_aufgabe.js b/courses/javascript/02_funktionen_und_arrays/test_aufgabe.js new file mode 100644 index 0000000..302882c --- /dev/null +++ b/courses/javascript/02_funktionen_und_arrays/test_aufgabe.js @@ -0,0 +1,5 @@ +assert.deepStrictEqual(verdoppeln([1, 2, 3]), [2, 4, 6], "verdoppeln([1, 2, 3]) sollte [2, 4, 6] sein"); +assert.deepStrictEqual(filterGeradeZahlen([1, 2, 3, 4, 5, 6]), [2, 4, 6], "filterGeradeZahlen() filtert unvollständig"); +assert.strictEqual(arraySumme([10, 20, 30]), 60, "arraySumme([10, 20, 30]) sollte 60 sein"); +assert.strictEqual(arraySumme([]), 0, "arraySumme([]) sollte 0 sein"); +console.log("✅ Alle 4 JavaScript Tests in JS 02 erfolgreich bestanden!"); diff --git a/courses/javascript/course.json b/courses/javascript/course.json new file mode 100644 index 0000000..e307fa2 --- /dev/null +++ b/courses/javascript/course.json @@ -0,0 +1,20 @@ +{ + "id": "javascript", + "title": "JavaScript & Moderne Web-Entwicklung", + "language": "javascript", + "icon": "🌐", + "runner": "javascript", + "description": "Vom Browser-Scripting über ES6-Funktionen, Arrays bis hin zu asynchronem JavaScript.", + "tracks": [ + { + "id": "track_1_grundlagen", + "title": "🌱 Lehrpfad 1: JavaScript Grundlagen & ES6", + "description": "Variablen (let/const), Template Strings, Arrow Functions und Array-Methoden.", + "certificateKey": "js_grundlagen", + "chapters": [ + { "folder": "01_erste_schritte_variablen", "title": "JS 01: Variablen, Typen & Template-Strings", "taskFile": "aufgabe.js", "testFile": "test_aufgabe.js" }, + { "folder": "02_funktionen_und_arrays", "title": "JS 02: Arrow Functions, Arrays & Map/Filter", "taskFile": "aufgabe.js", "testFile": "test_aufgabe.js" } + ] + } + ] +} diff --git a/courses/sql/01_tabellen_und_select/aufgabe.sql b/courses/sql/01_tabellen_und_select/aufgabe.sql new file mode 100644 index 0000000..9dfc6a3 --- /dev/null +++ b/courses/sql/01_tabellen_und_select/aufgabe.sql @@ -0,0 +1,18 @@ +-- 🗄️ SQL 01: TABELLEN ANLEGEN & DATEN ABFRAGEN 🗄️ +-- =============================================== + +-- 🎯 TEILZIEL 1 (TODO 1): Erstelle eine Tabelle `mitarbeiter` mit: +-- id (INTEGER PRIMARY KEY), name (TEXT), abteilung (TEXT), gehalt (REAL) +CREATE TABLE IF NOT EXISTS mitarbeiter ( + -- TODO: Spalten definieren + id INTEGER PRIMARY KEY +); + +-- 🎯 TEILZIEL 2 (TODO 2): Füge mindestens 3 Test-Mitarbeiter ein +INSERT INTO mitarbeiter (name, abteilung, gehalt) VALUES + ('Anna Schmidt', 'Entwicklung', 65000), + ('Ben Weber', 'Marketing', 48000), + ('Clara Schulz', 'Entwicklung', 72000); + +-- 🎯 TEILZIEL 3 (TODO 3): Schreibe eine SELECT-Abfrage, die alle Mitarbeiter aus der 'Entwicklung' anzeigt +SELECT * FROM mitarbeiter WHERE abteilung = 'Entwicklung'; diff --git a/courses/sql/01_tabellen_und_select/index.html b/courses/sql/01_tabellen_und_select/index.html new file mode 100644 index 0000000..efab405 --- /dev/null +++ b/courses/sql/01_tabellen_und_select/index.html @@ -0,0 +1,57 @@ + + + + + + SQL 01: Tabellen anlegen & SELECT + + + +
+
+
+
+
🗄️ SQL & Relationale Datenbanken
+

SQL 01: Tabellen anlegen & Daten abfragen

+

Lerne die Grundlagen relationaler Datenbanken mit SQLite WebAssembly direkt im Browser.

+
+
+ +
+
+

💡 1. Relationale Datenbanken & Tabellen

+

Daten werden in strukturierten Tabellen mit definierten Spaltentypen gespeichert:

+
CREATE TABLE kunden (
+    id INTEGER PRIMARY KEY,
+    name TEXT,
+    guthaben REAL
+);
+
+INSERT INTO kunden (name, guthaben) VALUES ('Max Mustermann', 150.0);
+SELECT * FROM kunden WHERE guthaben > 100;
+
+ +
+

🎯 Aufgaben & Checkliste

+
+ + +
+
+ + +
+
+ + +
+ + +
+
+
+
+ + diff --git a/courses/sql/01_tabellen_und_select/musterloesung.sql b/courses/sql/01_tabellen_und_select/musterloesung.sql new file mode 100644 index 0000000..af76ea0 --- /dev/null +++ b/courses/sql/01_tabellen_und_select/musterloesung.sql @@ -0,0 +1,13 @@ +CREATE TABLE IF NOT EXISTS mitarbeiter ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + abteilung TEXT NOT NULL, + gehalt REAL NOT NULL +); + +INSERT INTO mitarbeiter (name, abteilung, gehalt) VALUES + ('Anna Schmidt', 'Entwicklung', 65000), + ('Ben Weber', 'Marketing', 48000), + ('Clara Schulz', 'Entwicklung', 72000); + +SELECT * FROM mitarbeiter WHERE abteilung = 'Entwicklung'; diff --git a/courses/sql/01_tabellen_und_select/test_aufgabe.sql b/courses/sql/01_tabellen_und_select/test_aufgabe.sql new file mode 100644 index 0000000..f78a5e8 --- /dev/null +++ b/courses/sql/01_tabellen_und_select/test_aufgabe.sql @@ -0,0 +1,2 @@ +-- Unittest für SQL 01 +SELECT id, name, abteilung, gehalt FROM mitarbeiter WHERE abteilung = 'Entwicklung' ORDER BY gehalt DESC; diff --git a/courses/sql/course.json b/courses/sql/course.json new file mode 100644 index 0000000..5748245 --- /dev/null +++ b/courses/sql/course.json @@ -0,0 +1,19 @@ +{ + "id": "sql", + "title": "SQL & Relationale Datenbanken", + "language": "sql", + "icon": "🗄️", + "runner": "sql", + "description": "Von relationalen Tabellen über SELECT, WHERE, JOINs bis hin zu Aggregationen und Gruppierungen.", + "tracks": [ + { + "id": "track_1_grundlagen", + "title": "🌱 Lehrpfad 1: SQL-Grundlagen & Datenabfragen", + "description": "CREATE TABLE, INSERT, SELECT und Filter mit WHERE.", + "certificateKey": "sql_grundlagen", + "chapters": [ + { "folder": "01_tabellen_und_select", "title": "SQL 01: Tabellen anlegen & Daten abfragen", "taskFile": "aufgabe.sql", "testFile": "test_aufgabe.sql" } + ] + } + ] +} diff --git a/index.html b/index.html index 4ffcfa9..f4f06d8 100644 --- a/index.html +++ b/index.html @@ -21,6 +21,14 @@