feat: add word-type pairs + difficulty_level to auto create

- Claude now generates 40 pairs: +10 × word type (positive_words / negative_words)
- difficulty easy→1, medium→2 stored as difficulty_level on each pair
- findOrCreateWord() looks up or creates words by title before linking
- savePairsForObject handles all 4 pair types (text/yes_no/question/word)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-28 21:31:08 +02:00
parent b39a3cca9f
commit acc15849f3

View File

@@ -890,9 +890,21 @@ async function callClaudeForPairs(picture, allObjects, selectedObjectId) {
return res.pairs; return res.pairs;
} }
async function findOrCreateWord(title, lang) {
try {
const results = await apiFetch(`/words?search=${encodeURIComponent(title.trim())}&limit=5`);
const exact = Array.isArray(results)
? results.find(w => (w[`titel_${lang}`] || w.titel_de || '').toLowerCase() === title.trim().toLowerCase())
: null;
if (exact) return exact;
return await apiPost('/words', { [`titel_${lang}`]: title.trim() });
} catch { return null; }
}
async function savePairsForObject(pairs, objectId, lang, onPairSaved, onProgress) { async function savePairsForObject(pairs, objectId, lang, onPairSaved, onProgress) {
for (let i = 0; i < pairs.length; i++) { for (let i = 0; i < pairs.length; i++) {
const p = pairs[i]; const p = pairs[i];
const difficultyLevel = p.difficulty === 'easy' ? 1 : p.difficulty === 'medium' ? 2 : null;
let questionId = null; let questionId = null;
if (p.type !== 'text' && p.question?.trim()) { if (p.type !== 'text' && p.question?.trim()) {
@@ -901,22 +913,41 @@ async function savePairsForObject(pairs, objectId, lang, onPairSaved, onProgress
} }
let posStmtId = null; let posStmtId = null;
if ((p.type === 'text' || p.type === 'question') && p.positive?.trim()) { let negStmtId = null;
if (p.type === 'text' && p.positive?.trim()) {
const s = await apiPost('/statements', { status: 'draft', [`positive_sentence_${lang}`]: p.positive.trim() }); const s = await apiPost('/statements', { status: 'draft', [`positive_sentence_${lang}`]: p.positive.trim() });
posStmtId = s.id; posStmtId = s.id;
} else if (p.type === 'yes_no') { } else if (p.type === 'yes_no') {
const s = await apiPost('/statements', { status: 'draft', answer: p.answer ?? null }); const s = await apiPost('/statements', { status: 'draft', answer: p.answer ?? null });
posStmtId = s.id; posStmtId = s.id;
} else if (p.type === 'question') {
if (p.positive?.trim()) {
const s = await apiPost('/statements', { status: 'draft', [`positive_sentence_${lang}`]: p.positive.trim() });
posStmtId = s.id;
} }
if (p.negative?.trim()) {
let negStmtId = null;
if (p.type === 'question' && p.negative?.trim()) {
const s = await apiPost('/statements', { status: 'draft', [`negative_sentence_${lang}`]: p.negative.trim() }); const s = await apiPost('/statements', { status: 'draft', [`negative_sentence_${lang}`]: p.negative.trim() });
negStmtId = s.id; negStmtId = s.id;
} }
} else if (p.type === 'word') {
if (p.positive_words?.length) {
const s = await apiPost('/statements', { status: 'draft' });
posStmtId = s.id;
const words = await Promise.all(p.positive_words.map(w => findOrCreateWord(w, lang)));
await Promise.all(words.filter(Boolean).map(w => apiLink(`/statements/${posStmtId}/positive-words/${w.id}`)));
}
if (p.negative_words?.length) {
const s = await apiPost('/statements', { status: 'draft' });
negStmtId = s.id;
const words = await Promise.all(p.negative_words.map(w => findOrCreateWord(w, lang)));
await Promise.all(words.filter(Boolean).map(w => apiLink(`/statements/${negStmtId}/negative-words/${w.id}`)));
}
}
const created = await apiPost('/pairs', { const created = await apiPost('/pairs', {
answer_type: p.type, answer_type: p.type,
difficulty_level: difficultyLevel,
question_id: questionId, question_id: questionId,
positive_statement_id: posStmtId, positive_statement_id: posStmtId,
negative_statement_id: negStmtId, negative_statement_id: negStmtId,