feat: automatische Wort-Kategorisierung (Batches API + Sofort-Backfill)
Feste ~20er-Taxonomie geseedet (de/en/sv, published; bestehende Kategorien werden wiederverwendet) + Tabelle category_batches. src/lib/classifyWords.js: findet in Pairs verwendete Wörter ohne Kategorie und klassifiziert sie per Haiku gegen die feste Liste. - Stundenjob über die Message Batches API (asynchron, ~50% günstiger): submit/collect-Ticks, in index.js nach Boot + stündlich. - Sofortiger synchroner One-Shot-Backfill (classifyWordsSync) für Live-Test ohne 24h-Verzug. Beides materialisiert pair_categories via derivePairCategories. POST /api/categories/auto-assign (admin): ?sync=true = Sofort-Backfill, sonst ein Batch-Tick. Entkoppelt von generate-words und Publish. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -131,6 +131,56 @@ async function migrate() {
|
||||
)
|
||||
`);
|
||||
|
||||
// Feste Alltags-Taxonomie seeden (de/en/sv, published). Basis für die automatische
|
||||
// Wort-Kategorisierung (src/lib/classifyWords.js) und die Kategorie-Punkte im Profil.
|
||||
// Idempotent: bestehende Kategorie (z. B. "Tiere") wird wiederverwendet, keine Dubletten.
|
||||
const CATEGORY_TAXONOMY = [
|
||||
['Lebensmittel', 'Food', 'Mat'],
|
||||
['Tiere', 'Animals', 'Djur'],
|
||||
['Körper', 'Body', 'Kropp'],
|
||||
['Kleidung', 'Clothing', 'Kläder'],
|
||||
['Familie & Menschen','Family & People', 'Familj & människor'],
|
||||
['Beruf & Arbeit', 'Job & Work', 'Jobb & arbete'],
|
||||
['Haushalt', 'Household', 'Hushåll'],
|
||||
['Wohnen & Möbel', 'Home & Furniture', 'Hem & möbler'],
|
||||
['Natur & Pflanzen', 'Nature & Plants', 'Natur & växter'],
|
||||
['Wetter', 'Weather', 'Väder'],
|
||||
['Verkehr & Reisen', 'Transport & Travel', 'Transport & resor'],
|
||||
['Stadt & Gebäude', 'City & Buildings', 'Stad & byggnader'],
|
||||
['Schule & Bildung', 'School & Education', 'Skola & utbildning'],
|
||||
['Technik & Geräte', 'Technology & Devices','Teknik & apparater'],
|
||||
['Sport & Freizeit', 'Sports & Leisure', 'Sport & fritid'],
|
||||
['Gefühle', 'Emotions', 'Känslor'],
|
||||
['Farben', 'Colors', 'Färger'],
|
||||
['Zahlen & Zeit', 'Numbers & Time', 'Tal & tid'],
|
||||
['Werkzeuge', 'Tools', 'Verktyg'],
|
||||
['Sonstiges', 'Other', 'Övrigt'],
|
||||
];
|
||||
for (const [de, en, sv] of CATEGORY_TAXONOMY) {
|
||||
await query(
|
||||
`INSERT INTO categories (titel_de, titel_en, titel_sv, status, requested_at, published_at)
|
||||
SELECT $1, $2, $3, 'published', NOW(), NOW()
|
||||
WHERE NOT EXISTS (SELECT 1 FROM categories WHERE lower(titel_de) = lower($1))`,
|
||||
[de, en, sv]
|
||||
).catch(() => {});
|
||||
}
|
||||
// Bestehende Treffer auf published heben (z. B. die alte "Tiere"-Kategorie)
|
||||
await query(
|
||||
`UPDATE categories
|
||||
SET status = 'published', published_at = COALESCE(published_at, NOW())
|
||||
WHERE lower(titel_de) = ANY($1) AND status <> 'published'`,
|
||||
[CATEGORY_TAXONOMY.map(([de]) => de.toLowerCase())]
|
||||
).catch(() => {});
|
||||
|
||||
// Asynchroner Kategorisierungs-Batch (Message Batches API) — Status über Boots/Redeploys merken
|
||||
await query(`
|
||||
CREATE TABLE IF NOT EXISTS category_batches (
|
||||
batch_id TEXT PRIMARY KEY,
|
||||
status TEXT NOT NULL DEFAULT 'submitted',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
)
|
||||
`);
|
||||
|
||||
await query(`
|
||||
CREATE TABLE IF NOT EXISTS questions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
Reference in New Issue
Block a user