feat: words-Tabelle – Brysbaert-Import + hierarchische Kategorien + Batch-Anreicherung

- categories: parent_id (self-referential) + 49 Unterkategorien geseedet
- words: neue Spalten conc_m, dom_pos, level, themenfeld_id + unique index titel_en
- enrich_batches + word_generative Tabellen
- src/lib/enrichWords.js: Batch-Anreicherung (DE/SV-Übersetzung, Wortart, CEFR, Themenfeld)
- src/routes/wordGenerative.js: CRUD für KI-Bild-Pipeline
- src/routes/words.js: Filter dom_pos/level/themenfeld_id/has_conc_m + picture_count
- scripts/import-brysbaert.js: CSV-Import-Skript (lokal gegen Prod-DB)
- POST /api/words/enrich-batch als manueller Trigger

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-18 20:41:52 +02:00
parent 1605d2cdd1
commit 7ba6b7120b
6 changed files with 535 additions and 14 deletions

View File

@@ -44,6 +44,7 @@ app.use('/api/audios', auth, require('./routes/audios'));
app.use('/api/tts-settings', auth, require('./routes/tts-settings'));
app.use('/api/claude', auth, require('./routes/claude'));
app.use('/api/pipeline', auth, require('./routes/pipeline'));
app.use('/api/word-generative', auth, require('./routes/wordGenerative'));
// 404
app.use((req, res) => {
@@ -66,9 +67,27 @@ migrate()
// Automatische Wort-Kategorisierung (Message Batches API): kurz nach Boot + stündlich.
// Submit/Collect-Ticks, entkoppelt von generate-words und Publish.
const { runCategorizationTick } = require('./lib/classifyWords');
const { runEnrichTick, enrichWordsSync } = require('./lib/enrichWords');
const HOUR = 60 * 60 * 1000;
const tick = () => runCategorizationTick().catch(err => console.error('Auto-Kategorisierung:', err.message));
const enrichTick = () => runEnrichTick().catch(err => console.error('Auto-Anreicherung:', err.message));
setTimeout(tick, 30_000);
setTimeout(enrichTick, 60_000);
setInterval(tick, HOUR);
setInterval(enrichTick, HOUR);
// Manueller Trigger: POST /api/words/enrich-batch
app.post('/api/words/enrich-batch', auth, async (req, res, next) => {
try {
const sync = req.query.sync === 'true';
if (sync) {
const max = parseInt(req.query.max) || 500;
const result = await enrichWordsSync({ max });
return res.json(result);
}
const result = await runEnrichTick();
res.json(result);
} catch (err) { next(err); }
});
})
.catch(err => { console.error('Migration failed:', err); process.exit(1); });