feat: füge neue Funktionen zur Analyse und Zählung von Lektionen hinzu, einschließlich der Generierung von Lernkontrollpunkten
All checks were successful
Deploy to production / deploy (push) Successful in 2m3s
All checks were successful
Deploy to production / deploy (push) Successful in 2m3s
This commit is contained in:
92
backend/scripts/analyze-bisaya-courses.cjs
Normal file
92
backend/scripts/analyze-bisaya-courses.cjs
Normal file
@@ -0,0 +1,92 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
function extractBlocks(content) {
|
||||
const re = /['"]([^'"]+)['"]\s*:\s*\{([\s\S]*?)\n\s*\}/g;
|
||||
const blocks = {};
|
||||
let m;
|
||||
while ((m = re.exec(content))) {
|
||||
blocks[m[1]] = m[2];
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
|
||||
function countCorePatternsInBlock(blockText) {
|
||||
const cpRe = /corePatterns\s*:\s*\[([\s\S]*?)\]/g;
|
||||
let m = cpRe.exec(blockText);
|
||||
if (!m) return 0;
|
||||
const arrText = m[1];
|
||||
const targetRe = /\btarget\s*:/g;
|
||||
return (arrText.match(targetRe) || []).length;
|
||||
}
|
||||
|
||||
function parseDidacticsFile(filePath) {
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
const blocks = extractBlocks(content);
|
||||
const counts = {};
|
||||
let total = 0;
|
||||
for (const [k, v] of Object.entries(blocks)) {
|
||||
const c = countCorePatternsInBlock(v);
|
||||
if (c > 0) {
|
||||
counts[k] = c;
|
||||
total += c;
|
||||
}
|
||||
}
|
||||
return { counts, total };
|
||||
}
|
||||
|
||||
function extractLessonArray(content, varName) {
|
||||
const re = new RegExp(varName.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') + "\\s*=\\s*\\[([\\s\\S]*?)\\];", 'm');
|
||||
const m = re.exec(content);
|
||||
if (!m) return null;
|
||||
return m[1];
|
||||
}
|
||||
|
||||
function countExamLessonsFromArrayText(arrayText) {
|
||||
const objRe = /\{([\s\S]*?)\}(?=\s*,|$)/g;
|
||||
let m;
|
||||
let examCount = 0;
|
||||
let total = 0;
|
||||
while ((m = objRe.exec(arrayText))) {
|
||||
total++;
|
||||
const obj = m[1];
|
||||
const typeMatch = /type\s*:\s*['"](\w+)['"]/.exec(obj);
|
||||
const titleMatch = /title\s*:\s*['"]([\s\S]*?)['"]/.exec(obj);
|
||||
const type = typeMatch ? typeMatch[1] : '';
|
||||
const title = titleMatch ? titleMatch[1].toLowerCase() : '';
|
||||
if (type === 'review' || /checkpoint|test|abschluss|prüfung|check/i.test(title)) {
|
||||
examCount++;
|
||||
}
|
||||
}
|
||||
return { examCount, total };
|
||||
}
|
||||
|
||||
function analyze() {
|
||||
const repoRoot = path.join(__dirname);
|
||||
const file1 = path.join(repoRoot, 'bisaya-course-plan-24-43.js');
|
||||
const file2 = path.join(repoRoot, 'bisaya-course-phase3-extension.js');
|
||||
|
||||
const res1 = parseDidacticsFile(file1);
|
||||
const res2 = parseDidacticsFile(file2);
|
||||
|
||||
const f1 = fs.readFileSync(file1, 'utf8');
|
||||
const f2 = fs.readFileSync(file2, 'utf8');
|
||||
|
||||
const arr1Text = extractLessonArray(f1, 'const BISAYA_LESSONS_24_43_BASE') || extractLessonArray(f1, 'BISAYA_LESSONS_24_43') || extractLessonArray(f1, 'BISAYA_LESSONS_24_43_BASE =');
|
||||
const arr2Text = extractLessonArray(f2, 'export const BISAYA_PHASE3_LESSONS') || extractLessonArray(f2, 'BISAYA_PHASE3_LESSONS');
|
||||
|
||||
const a1 = arr1Text ? countExamLessonsFromArrayText(arr1Text) : { examCount: 0, total: 0 };
|
||||
const a2 = arr2Text ? countExamLessonsFromArrayText(arr2Text) : { examCount: 0, total: 0 };
|
||||
|
||||
console.log('--- BISAYA 24-43 didactics corePatterns per key ---');
|
||||
Object.entries(res1.counts).sort((a,b)=>b[1]-a[1]).forEach(([k,v])=>console.log(`${k}: ${v}`));
|
||||
console.log(`Total corePatterns (24-43 files): ${res1.total}`);
|
||||
console.log(`Lessons parsed in array (approx): ${a1.total}, exam-like lessons (type=review or title contains checkpoint/test/abschluss): ${a1.examCount}`);
|
||||
|
||||
console.log('\n--- BISAYA Phase3 didactics corePatterns per key ---');
|
||||
Object.entries(res2.counts).sort((a,b)=>b[1]-a[1]).forEach(([k,v])=>console.log(`${k}: ${v}`));
|
||||
console.log(`Total corePatterns (phase3 file): ${res2.total}`);
|
||||
console.log(`Lessons parsed in phase3 array (approx): ${a2.total}, exam-like lessons: ${a2.examCount}`);
|
||||
}
|
||||
|
||||
analyze();
|
||||
95
backend/scripts/analyze-bisaya-courses.js
Normal file
95
backend/scripts/analyze-bisaya-courses.js
Normal file
@@ -0,0 +1,95 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
function extractBlocks(content) {
|
||||
// Find occurrences like 'Key': { ... corePatterns: [ ... ] ... }
|
||||
const re = /['"]([^'"]+)['"]\s*:\s*\{([\s\S]*?)\n\s*\}/g;
|
||||
const blocks = {};
|
||||
let m;
|
||||
while ((m = re.exec(content))) {
|
||||
blocks[m[1]] = m[2];
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
|
||||
function countCorePatternsInBlock(blockText) {
|
||||
const cpRe = /corePatterns\s*:\s*\[([\s\S]*?)\]/g;
|
||||
let m = cpRe.exec(blockText);
|
||||
if (!m) return 0;
|
||||
const arrText = m[1];
|
||||
const targetRe = /\btarget\s*:/g;
|
||||
return (arrText.match(targetRe) || []).length;
|
||||
}
|
||||
|
||||
function parseDidacticsFile(filePath) {
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
const blocks = extractBlocks(content);
|
||||
const counts = {};
|
||||
let total = 0;
|
||||
for (const [k, v] of Object.entries(blocks)) {
|
||||
const c = countCorePatternsInBlock(v);
|
||||
if (c > 0) {
|
||||
counts[k] = c;
|
||||
total += c;
|
||||
}
|
||||
}
|
||||
return { counts, total };
|
||||
}
|
||||
|
||||
function extractLessonArray(content, varName) {
|
||||
// crude: find 'const VAR = [' up to closing '];'
|
||||
const re = new RegExp(varName.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') + "\\s*=\\s*\\[([\\s\\S]*?)\\];", 'm');
|
||||
const m = re.exec(content);
|
||||
if (!m) return null;
|
||||
return m[1];
|
||||
}
|
||||
|
||||
function countExamLessonsFromArrayText(arrayText) {
|
||||
// Count objects with type:'review' or title containing checkpoint/test/abschluss (case-insensitive)
|
||||
const objRe = /\{([\s\S]*?)\}(?=\s*,|$)/g;
|
||||
let m;
|
||||
let examCount = 0;
|
||||
let total = 0;
|
||||
while ((m = objRe.exec(arrayText))) {
|
||||
total++;
|
||||
const obj = m[1];
|
||||
const typeMatch = /type\s*:\s*['"](\w+)['"]/.exec(obj);
|
||||
const titleMatch = /title\s*:\s*['"]([\s\S]*?)['"]/.exec(obj);
|
||||
const type = typeMatch ? typeMatch[1] : '';
|
||||
const title = titleMatch ? titleMatch[1].toLowerCase() : '';
|
||||
if (type === 'review' || /checkpoint|test|abschluss|prüfung|check/i.test(title)) {
|
||||
examCount++;
|
||||
}
|
||||
}
|
||||
return { examCount, total };
|
||||
}
|
||||
|
||||
function analyze() {
|
||||
const repoRoot = path.join(__dirname);
|
||||
const file1 = path.join(repoRoot, 'bisaya-course-plan-24-43.js');
|
||||
const file2 = path.join(repoRoot, 'bisaya-course-phase3-extension.js');
|
||||
|
||||
const res1 = parseDidacticsFile(file1);
|
||||
const res2 = parseDidacticsFile(file2);
|
||||
|
||||
const f1 = fs.readFileSync(file1, 'utf8');
|
||||
const f2 = fs.readFileSync(file2, 'utf8');
|
||||
|
||||
const arr1Text = extractLessonArray(f1, 'const BISAYA_LESSONS_24_43_BASE') || extractLessonArray(f1, 'BISAYA_LESSONS_24_43') || extractLessonArray(f1, 'BISAYA_LESSONS_24_43_BASE =');
|
||||
const arr2Text = extractLessonArray(f2, 'export const BISAYA_PHASE3_LESSONS') || extractLessonArray(f2, 'BISAYA_PHASE3_LESSONS');
|
||||
|
||||
const a1 = arr1Text ? countExamLessonsFromArrayText(arr1Text) : { examCount: 0, total: 0 };
|
||||
const a2 = arr2Text ? countExamLessonsFromArrayText(arr2Text) : { examCount: 0, total: 0 };
|
||||
|
||||
console.log('--- BISAYA 24-43 didactics corePatterns per key ---');
|
||||
Object.entries(res1.counts).sort((a,b)=>b[1]-a[1]).forEach(([k,v])=>console.log(`${k}: ${v}`));
|
||||
console.log(`Total corePatterns (24-43 files): ${res1.total}`);
|
||||
console.log(`Lessons parsed in array (approx): ${a1.total}, exam-like lessons (type=review or title contains checkpoint/test/abschluss): ${a1.examCount}`);
|
||||
|
||||
console.log('\n--- BISAYA Phase3 didactics corePatterns per key ---');
|
||||
Object.entries(res2.counts).sort((a,b)=>b[1]-a[1]).forEach(([k,v])=>console.log(`${k}: ${v}`));
|
||||
console.log(`Total corePatterns (phase3 file): ${res2.total}`);
|
||||
console.log(`Lessons parsed in phase3 array (approx): ${a2.total}, exam-like lessons: ${a2.examCount}`);
|
||||
}
|
||||
|
||||
analyze();
|
||||
@@ -186,6 +186,7 @@ const BISAYA_DIDACTICS_24_43_ENRICHMENTS = {
|
||||
corePatterns: [
|
||||
{ target: 'Ayaw kalimot.', gloss: 'Vergiss es nicht.' },
|
||||
{ target: 'Hugas sa kamot.', gloss: 'Wasch dir die Hände.' },
|
||||
{ target: 'Kumusta imong tulog?', gloss: 'Wie hast du geschlafen?' },
|
||||
{ target: 'Patya ang suga.', gloss: 'Mach das Licht aus.' }
|
||||
],
|
||||
grammarFocus: [
|
||||
@@ -216,6 +217,7 @@ const BISAYA_DIDACTICS_24_43_ENRICHMENTS = {
|
||||
corePatterns: [
|
||||
{ target: 'Hugas sa nawong.', gloss: 'Wasch dein Gesicht.' },
|
||||
{ target: 'Kuhaa imong bag.', gloss: 'Hol deine Tasche.' },
|
||||
{ target: 'Kumusta imong tulog?', gloss: 'Wie hast du geschlafen?' },
|
||||
{ target: 'Mogawas na ta.', gloss: 'Wir gehen jetzt raus.' }
|
||||
],
|
||||
speakingPrompts: [
|
||||
|
||||
25
backend/scripts/count-per-lesson-24-43.mjs
Normal file
25
backend/scripts/count-per-lesson-24-43.mjs
Normal file
@@ -0,0 +1,25 @@
|
||||
import { BISAYA_LESSONS_24_43_BY_NUMBER, BISAYA_DIDACTICS_24_43 } from './bisaya-course-plan-24-43.js';
|
||||
|
||||
const out = [];
|
||||
for (let n = 24; n <= 43; n++) {
|
||||
const lesson = BISAYA_LESSONS_24_43_BY_NUMBER[n];
|
||||
if (!lesson) {
|
||||
out.push({ lesson: n, title: null, corePatterns: 0 });
|
||||
continue;
|
||||
}
|
||||
const title = lesson.title;
|
||||
const didactic = BISAYA_DIDACTICS_24_43[title];
|
||||
let count = 0;
|
||||
if (didactic && didactic.corePatterns) {
|
||||
count += didactic.corePatterns.length;
|
||||
}
|
||||
out.push({ lesson: n, title, corePatterns: count });
|
||||
}
|
||||
|
||||
// Print nicely
|
||||
for (const r of out) {
|
||||
console.log(`Lektion ${r.lesson}: ${r.title || '[nicht definiert]'} — ${r.corePatterns} corePatterns`);
|
||||
}
|
||||
|
||||
const total = out.reduce((s,r)=>s+r.corePatterns,0);
|
||||
console.log(`\nGesamt corePatterns (Lektionen 24–43 Summe): ${total}`);
|
||||
56
backend/scripts/generate-lesson-checkpoints.cjs
Normal file
56
backend/scripts/generate-lesson-checkpoints.cjs
Normal file
@@ -0,0 +1,56 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const vm = require('vm');
|
||||
|
||||
function extractLessonArray(content, varNameCandidates) {
|
||||
for (const varName of varNameCandidates) {
|
||||
const re = new RegExp(varName.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') + "\\s*=\\s*\\[([\\s\\S]*?)\\];", 'm');
|
||||
const m = re.exec(content);
|
||||
if (m) return m[1];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function evalArrayText(arrayText) {
|
||||
const wrapped = '[' + arrayText + ']';
|
||||
const script = new vm.Script(wrapped, { filename: 'array.js' });
|
||||
const context = vm.createContext({});
|
||||
return script.runInContext(context);
|
||||
}
|
||||
|
||||
function loadLessons(filePath, varCandidates) {
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
const arrText = extractLessonArray(content, varCandidates);
|
||||
if (!arrText) return [];
|
||||
const arr = evalArrayText(arrText);
|
||||
return arr;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const repo = path.join(__dirname);
|
||||
const file1 = path.join(repo, 'bisaya-course-plan-24-43.js');
|
||||
const file2 = path.join(repo, 'bisaya-course-phase3-extension.js');
|
||||
|
||||
const lessons1 = loadLessons(file1, ['const BISAYA_LESSONS_24_43_BASE', 'BISAYA_LESSONS_24_43_BASE', 'BISAYA_LESSONS_24_43']);
|
||||
const lessons2 = loadLessons(file2, ['export const BISAYA_PHASE3_LESSONS', 'BISAYA_PHASE3_LESSONS']);
|
||||
|
||||
const mapping = {};
|
||||
for (const l of lessons1) {
|
||||
if (l && typeof l.num === 'number') mapping[l.num] = true;
|
||||
}
|
||||
for (const l of lessons2) {
|
||||
if (l && typeof l.num === 'number') mapping[l.num] = true;
|
||||
}
|
||||
|
||||
// Also include any missing range 24..63
|
||||
for (let n = 24; n <= 63; n++) mapping[n] = true;
|
||||
|
||||
const outDir = path.join(__dirname, '..', 'data');
|
||||
if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
|
||||
const outFile = path.join(outDir, 'lesson-checkpoints.json');
|
||||
fs.writeFileSync(outFile, JSON.stringify(mapping, null, 2));
|
||||
console.log('Wrote', outFile);
|
||||
console.log('Marked lessons:', Object.keys(mapping).length);
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user