Graduately/tools/extract/cetba.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

114 lines
3.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { readFileSync } from 'node:fs';
import { parseArray, parseArrayInFunction, trimNulls } from '../lib/cparse.mjs';
/**
* Maturita reading list ("Četba"): one lesson per book.
*
* Each book page shows four reference cards followed by two exercises, so the
* cards become the lesson's theory slides and the quiz and plot ordering
* become its two tasks.
*/
/**
* Lesson numbers continue after Mluvnice, so every Czech lesson has a distinct
* number for the statistics page; the reading path numbers its own nodes.
*/
const BOOKS = [
{
id: 'orwell-1984',
number: 2,
title: 'George Orwell – 1984',
page: 'build_cetba1984_page',
quiz: 'lit_qs',
plot: 'lit_plot_items',
plotMeaning: 'lit_plot_meaning',
cards: [
['about', 'O knize'],
['world', 'Svět a strana'],
['people', 'Postavy'],
['terms', 'Klíčové pojmy'],
],
},
{
id: 'fuks-spalovac-mrtvol',
number: 3,
title: 'Ladislav Fuks – Spalovač mrtvol',
page: 'build_cetba_fuks_page',
quiz: 'fuks_qs',
plot: 'fuks_plot_items',
plotMeaning: 'fuks_plot_meaning',
cards: [
['about', 'O knize'],
['world', 'Doba a svět'],
['people', 'Postavy'],
['terms', 'Klíčové motivy'],
],
},
];
/** `LitQ`: question, opts[4], correct, explanation. */
function litChoice(row, label) {
const [prompt, options, correct, expl] = row;
const list = trimNulls(options ?? []).map(String);
if (typeof correct !== 'number' || correct < 0 || correct >= list.length) {
throw new Error(`${label}: correct index ${correct} out of range for ${list.length} options`);
}
return {
kind: 'choice',
prompt: String(prompt),
options: list,
correct,
...(expl ? { hint: String(expl) } : {}),
};
}
export function extractCetba(desktopDir) {
const src = readFileSync(`${desktopDir}/cetba.c`, 'utf8');
return BOOKS.map((book) => {
const slides = book.cards.map(([arrayName, cardTitle]) => ({
kicker: book.title,
title: cardTitle,
tip: null,
lines: trimNulls(parseArrayInFunction(src, book.page, arrayName)).map(String),
}));
const plotMeanings = parseArray(src, book.plotMeaning).map(String);
return {
id: book.id,
subject: 'czech',
number: book.number,
title: { cs: book.title, en: book.title },
subtitle: {
cs: 'Projděte si přehled a pak si vyzkoušejte kvíz a řazení děje.',
en: 'Read the overview, then try the quiz and the plot ordering.',
},
slides,
tasks: [
{
number: 1,
title: 'Kvíz',
heading: 'Kvíz ke knize',
subtitleKey: 'lit_quiz_sub',
items: parseArray(src, book.quiz)
.map((row, i) => litChoice(row, `${book.id} q${i + 1}`)),
},
{
number: 2,
title: 'Děj',
heading: 'Seřaďte děj',
subtitleKey: 'lit_plot_sub',
items: parseArray(src, book.plot).map(([prompt, words], i) => ({
kind: 'assembly',
prompt: prompt ? String(prompt) : '',
words: trimNulls(words).map(String),
...(plotMeanings[i] ? { meaning: plotMeanings[i] } : {}),
})),
},
],
};
});
}