Graduately/tools/extract/deutsch-u2.mjs
FBukovina f7470d333c Add the Next.js web app and shared lesson content.
Move lessons out of the C sources into JSON so the web client can use the same material as the desktop app, and point release builds at desktop/.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-17 16:06:15 +02:00

159 lines
5 KiB
JavaScript

import { readFileSync } from 'node:fs';
import { parseArray, parseString } from '../lib/cparse.mjs';
import {
choiceItem,
freeItem,
opt,
typedItem,
verbFill,
vocabSections,
} from './deutsch-helpers.mjs';
/**
* Deutsch Unit 2 ("Familie und Freunde").
*
* Task order, titles and instruction keys come from the `u2exN` builders at the
* bottom of exercise_u2.c; each one names the `const` arrays holding its data.
*/
export function extractDeutschUnit2(desktopDir) {
const src = readFileSync(`${desktopDir}/exercise_u2.c`, 'utf8');
const tasks = [];
/** `build_verb_ex`: one pool feeding single-gap sentences. */
const verbTask = (number, title, subtitleKey, prefix, { gloss = false } = {}) => {
const meanings = parseArray(src, `${prefix}_mean`);
const glosses = gloss ? parseArray(src, `${prefix}_gloss`) : null;
return {
number,
title,
subtitleKey,
pool: parseArray(src, `${prefix}_pool`).map(String),
items: parseArray(src, `${prefix}_rows`).map((row, i) =>
verbFill(row, meanings[i], glosses && opt(glosses[i])
? { glosses: [String(glosses[i])] }
: {})),
};
};
const choiceTask = (number, title, subtitleKey, prefix) => {
const meanings = parseArray(src, `${prefix}_mean`);
return {
number,
title,
subtitleKey,
items: parseArray(src, `${prefix}_rows`).map((row, i) => choiceItem(row, meanings[i])),
};
};
const typedTask = (number, title, subtitleKey, rowsName, bankName) => ({
number,
title,
subtitleKey,
...(bankName ? { note: parseString(src, bankName) } : {}),
items: parseArray(src, rowsName).map(typedItem),
});
/* 1 — Verben konjugieren: two people, each a headed group over one pool. */
{
const pool = parseArray(src, 'g01_pool').map(String);
const groups = [
['Peter Fritsch:', 'g01_peter', 'g01_peter_mean'],
['Jana Nová und Pavol Korčák:', 'g01_jana', 'g01_jana_mean'],
];
const items = groups.flatMap(([heading, rowsName, meanName]) => {
const meanings = parseArray(src, meanName);
return parseArray(src, rowsName).map((row, i) =>
verbFill(row, meanings[i], i === 0 ? { heading } : {}));
});
tasks.push({
number: 1,
title: 'Verben konjugieren',
subtitleKey: 'sub_verben',
pool,
items,
});
}
tasks.push(choiceTask(2, 'aus oder in', 'sub_ausin', 'g02'));
tasks.push(choiceTask(3, 'Fragewörter', 'sub_wer', 'g03'));
tasks.push(verbTask(4, 'sprechen', 'sub_sprich', 'g04'));
tasks.push(typedTask(5, 'Nationalitäten', 'sub_nation', 'g05_rows', 'g05_bank'));
tasks.push(typedTask(6, 'Woher?', 'sub_woher', 'g06_rows'));
tasks.push(typedTask(7, 'Länder schreiben', 'sub_laender', 'g07_rows'));
tasks.push(verbTask(8, 'Verben einsetzen', 'sub_verb2', 'g08'));
/* 9 — Freie Antwort */
tasks.push({
number: 9,
title: 'Freie Antwort',
subtitleKey: 'sub_free',
items: parseArray(src, 'ex9_free_qs').map(freeItem),
});
tasks.push(verbTask(10, 'Euro', 'sub_euro', 'g10'));
tasks.push(typedTask(11, 'Wörter suchen', 'sub_wortsuchen', 'g11_rows'));
tasks.push(choiceTask(12, 'Was ist richtig?', 'sub_verb2', 'g12'));
/* 13 — Ordne zu: a Fragewort dropdown and an answer dropdown per row. */
{
const meanings = parseArray(src, 'g13_mean');
tasks.push({
number: 13,
title: 'Ordne zu',
subtitleKey: 'sub_ordne',
pool: parseArray(src, 'g13_fw_pool').map(String),
altPool: parseArray(src, 'g13_ans_pool').map(String),
items: parseArray(src, 'g13_rows').map(([mid, fw, ans], i) => ({
kind: 'fill',
segments: ['', ` ${mid} → `, ''],
answers: [String(fw), String(ans)],
input: 'select',
gapPool: ['main', 'alt'],
label: `${i + 1}.`,
...(opt(meanings[i]) ? { meaning: String(meanings[i]) } : {}),
})),
});
}
tasks.push(verbTask(14, 'Lückentext', 'sub_luecke', 'g14', { gloss: true }));
tasks.push(verbTask(15, 'Verbinde', 'sub_verbinde', 'g15', { gloss: true }));
tasks.push(verbTask(16, 'Zahlen', 'sub_zahlpaar', 'g16'));
/* 17 — Steckbrief */
tasks.push({
number: 17,
title: 'Steckbrief',
subtitleKey: 'sub_steckbrief',
items: parseArray(src, 's01_rows').map(([stem, sample]) => ({
kind: 'profile',
stem: String(stem),
sample: String(sample),
})),
});
/* 18 — Berufe: hangman, one round per profession. */
{
const words = parseArray(src, 'hm_words').map(String);
const tips = parseArray(src, 'hm_tips').map(String);
tasks.push({
number: 18,
title: 'Berufe',
subtitleKey: 'sub_berufe',
items: words.map((word, i) => ({ kind: 'hangman', word, hint: tips[i] })),
});
}
tasks.push(typedTask(19, 'Nationalität', 'sub_bistdu', 's03_rows'));
return {
id: 'unit-02',
subject: 'deutsch',
number: 2,
title: { cs: 'Aus aller Welt', en: 'Aus aller Welt' },
slides: [],
tasks,
vocab: vocabSections(src, 'u2_trans_sections'),
};
}