feat: verbessere SRS-Logik und füge serverseitige Zählung für fällige Items hinzu
Some checks failed
Deploy to production / deploy (push) Failing after 1m50s
Some checks failed
Deploy to production / deploy (push) Failing after 1m50s
This commit is contained in:
@@ -928,6 +928,16 @@ export default class VocabService {
|
|||||||
if (!reference || !learning) {
|
if (!reference || !learning) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Heuristik: Vermeide, einzelne sehr kurze Ziel-Token (z.B. "ko")
|
||||||
|
// automatisch mit mehrwortigen Glosses (z.B. "Ich arbeite") zu koppeln.
|
||||||
|
// Das verhindert fehlerhafte Glosszuweisungen für Partikeln/Pronomina.
|
||||||
|
const compactRefLen = reference.replace(/\s+/g, '').length;
|
||||||
|
const learningWordCount = this._wordCount(learning);
|
||||||
|
if (compactRefLen <= 3 && learningWordCount > 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!glossByReference.has(reference)) {
|
if (!glossByReference.has(reference)) {
|
||||||
glossByReference.set(reference, learning);
|
glossByReference.set(reference, learning);
|
||||||
}
|
}
|
||||||
@@ -1885,6 +1895,11 @@ export default class VocabService {
|
|||||||
const rows = validDueRows.slice(0, limit);
|
const rows = validDueRows.slice(0, limit);
|
||||||
const totalDueCount = validDueRows.length;
|
const totalDueCount = validDueRows.length;
|
||||||
|
|
||||||
|
// Debug: Logge Anzahl fälliger SRS-Items (nur in Entwicklung sichtbar)
|
||||||
|
try {
|
||||||
|
console.debug('[VocabService] getCourseSrsDue', { userId: user.id, courseId: course.id, totalDueCount });
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
courseId: course.id,
|
courseId: course.id,
|
||||||
dueAt: now.toISOString(),
|
dueAt: now.toISOString(),
|
||||||
@@ -1992,6 +2007,18 @@ export default class VocabService {
|
|||||||
}
|
}
|
||||||
await item.save();
|
await item.save();
|
||||||
|
|
||||||
|
// Debug: Logge SRS-Updates, damit wir sehen, ob Reviews ankommen
|
||||||
|
try {
|
||||||
|
console.debug('[VocabService] reviewSrsItem saved', {
|
||||||
|
userId: user.id,
|
||||||
|
courseId: courseId,
|
||||||
|
itemKey: item.itemKey,
|
||||||
|
correct: correct,
|
||||||
|
nextDueAt: this._normalizeIsoDate(item.nextDueAt),
|
||||||
|
stage: item.stage
|
||||||
|
});
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
itemKey: item.itemKey,
|
itemKey: item.itemKey,
|
||||||
correct,
|
correct,
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
<div v-else-if="pool.length === 0">
|
<div v-else-if="pool.length === 0">
|
||||||
{{ $t('socialnetwork.vocab.practice.noPool') }}
|
{{ $t('socialnetwork.vocab.practice.noPool') }}
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="srsMode && srsQueueIds.length === 0" class="srs-finished">
|
<div v-else-if="srsMode && srsRemainingCount === 0" class="srs-finished">
|
||||||
<div class="srs-finished__title">{{ $t('socialnetwork.vocab.practice.srsFinishedTitle') }}</div>
|
<div class="srs-finished__title">{{ $t('socialnetwork.vocab.practice.srsFinishedTitle') }}</div>
|
||||||
<div class="srs-finished__desc">{{ $t('socialnetwork.vocab.practice.srsFinishedDesc') }}</div>
|
<div class="srs-finished__desc">{{ $t('socialnetwork.vocab.practice.srsFinishedDesc') }}</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -180,6 +180,7 @@ export default {
|
|||||||
// Stored per day and course so the user can close/reopen without starting over.
|
// Stored per day and course so the user can close/reopen without starting over.
|
||||||
srsSession: null, // { version, dateKey, courseId, initialTotalDue, initialDueIds, doneIds, correctCount, wrongCount }
|
srsSession: null, // { version, dateKey, courseId, initialTotalDue, initialDueIds, doneIds, correctCount, wrongCount }
|
||||||
srsQueueIds: [], // remaining due ids for this session, in due order
|
srsQueueIds: [], // remaining due ids for this session, in due order
|
||||||
|
srsServerTotalDue: null,
|
||||||
|
|
||||||
// session stats
|
// session stats
|
||||||
correctCount: 0,
|
correctCount: 0,
|
||||||
@@ -517,7 +518,8 @@ export default {
|
|||||||
version: SRS_SESSION_STORAGE_VERSION,
|
version: SRS_SESSION_STORAGE_VERSION,
|
||||||
dateKey: this.getLocalDateKey(),
|
dateKey: this.getLocalDateKey(),
|
||||||
courseId: this.openParams.courseId,
|
courseId: this.openParams.courseId,
|
||||||
initialTotalDue: dueIds.length,
|
// Prefer server-reported total due count when available (shows full-course due count)
|
||||||
|
initialTotalDue: Number.isFinite(Number(this.srsServerTotalDue)) ? Number(this.srsServerTotalDue) : dueIds.length,
|
||||||
initialDueIds: dueIds,
|
initialDueIds: dueIds,
|
||||||
doneIds: [],
|
doneIds: [],
|
||||||
correctCount: 0,
|
correctCount: 0,
|
||||||
@@ -761,9 +763,29 @@ export default {
|
|||||||
this.next();
|
this.next();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
try {
|
try {
|
||||||
let res;
|
let res;
|
||||||
|
let courseDueRes = null;
|
||||||
|
// Wenn SRS-Modus auf Kurs-Ebene, lade kursweite fällige Items (Server liefert totalDueCount)
|
||||||
|
if (this.srsMode && this.openParams.courseId && !this.openParams.lessonId) {
|
||||||
|
try {
|
||||||
|
courseDueRes = await apiClient.get(`/api/vocab/courses/${this.openParams.courseId}/srs/due`, { params: { limit: 100 } });
|
||||||
|
} catch (e) {
|
||||||
|
// ignore, fallback to lesson/chapters/vocabs endpoints
|
||||||
|
courseDueRes = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (courseDueRes && Array.isArray(courseDueRes.data?.items) && courseDueRes.data.items.length > 0) {
|
||||||
|
// Map server items to pool shape (use itemKey as id)
|
||||||
|
this.pool = this.normalizePool((courseDueRes.data.items || []).map((it) => ({
|
||||||
|
id: it.itemKey,
|
||||||
|
learning: it.learning,
|
||||||
|
reference: it.reference,
|
||||||
|
lessonId: it.lessonId || null
|
||||||
|
})));
|
||||||
|
this.srsServerTotalDue = Number.isFinite(Number(courseDueRes.data?.totalDueCount)) ? Number(courseDueRes.data.totalDueCount) : null;
|
||||||
|
} else {
|
||||||
if (this.openParams.lessonId) {
|
if (this.openParams.lessonId) {
|
||||||
if (this.allVocabs && this.openParams.courseId) {
|
if (this.allVocabs && this.openParams.courseId) {
|
||||||
res = await apiClient.get(`/api/vocab/courses/${this.openParams.courseId}/completed-lesson-vocabs`, {
|
res = await apiClient.get(`/api/vocab/courses/${this.openParams.courseId}/completed-lesson-vocabs`, {
|
||||||
@@ -782,7 +804,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
res = await apiClient.get(`/api/vocab/chapters/${this.openParams.chapterId}/vocabs`);
|
res = await apiClient.get(`/api/vocab/chapters/${this.openParams.chapterId}/vocabs`);
|
||||||
this.pool = this.normalizePool(res.data?.vocabs || []);
|
this.pool = this.normalizePool(res.data?.vocabs || []);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Reload pool failed:', e);
|
console.error('Reload pool failed:', e);
|
||||||
this.pool = [];
|
this.pool = [];
|
||||||
|
|||||||
Reference in New Issue
Block a user