feat(VocabService): improve SRS pair validation and introduce new text analysis methods
All checks were successful
Deploy to production / deploy (push) Successful in 2m13s
All checks were successful
Deploy to production / deploy (push) Successful in 2m13s
- Refactored SRS pair validation logic by introducing the `_isTrainableSrsPair` method to enhance the criteria for acceptable learning and reference pairs. - Added `_isInstructionLikeText` method to filter out instructional texts from SRS pairs, improving the quality of vocabulary training. - Updated various methods to utilize the new validation logic, ensuring consistent handling of vocabulary entries across the service. - Enhanced the `_courseHasDueSrsItems` and `_extractTrainerVocabsFromLessonDidactics` methods to improve data retrieval and filtering based on the new criteria.
This commit is contained in:
@@ -42,7 +42,7 @@ export default class VocabService {
|
||||
.map((entry) => {
|
||||
const learning = String(entry?.learning || '').trim();
|
||||
const reference = String(entry?.reference || '').trim();
|
||||
if (!learning || !reference || this._normalizeSrsText(learning) === this._normalizeSrsText(reference)) {
|
||||
if (!this._isTrainableSrsPair({ learning, reference })) {
|
||||
return null;
|
||||
}
|
||||
const direction = String(entry?.direction || 'BOTH').toUpperCase();
|
||||
@@ -61,6 +61,30 @@ export default class VocabService {
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
_isInstructionLikeText(value) {
|
||||
const text = String(value || '').trim();
|
||||
if (!text) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const wordCount = text.split(/\s+/).filter(Boolean).length;
|
||||
if (wordCount < 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return /^(sage|sag|frage|frag|bitte|stelle|sprich|erzähle|beschreibe|bilde|wähle|ordne|übersetze|nenne|nenn|beginne|verwende|reagiere|kombiniere|spiele|löse|beantworte|ergänze|formuliere)\b/i.test(text);
|
||||
}
|
||||
|
||||
_isTrainableSrsPair(entry) {
|
||||
const learning = String(entry?.learning || '').trim();
|
||||
const reference = String(entry?.reference || '').trim();
|
||||
if (!learning || !reference || this._normalizeSrsText(learning) === this._normalizeSrsText(reference)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !this._isInstructionLikeText(learning) && !this._isInstructionLikeText(reference);
|
||||
}
|
||||
|
||||
_calculateSrsSchedule(item, { correct, rating = null } = {}) {
|
||||
const now = new Date();
|
||||
const previousStage = Math.max(0, Number(item?.stage) || 0);
|
||||
@@ -459,16 +483,17 @@ export default class VocabService {
|
||||
}
|
||||
|
||||
async _courseHasDueSrsItems(userId, courseId) {
|
||||
const due = await VocabSrsItem.count({
|
||||
const dueItems = await VocabSrsItem.findAll({
|
||||
where: {
|
||||
userId,
|
||||
courseId: Number(courseId),
|
||||
nextDueAt: {
|
||||
[Op.lte]: new Date()
|
||||
}
|
||||
}
|
||||
},
|
||||
attributes: ['learning', 'reference']
|
||||
});
|
||||
return due > 0;
|
||||
return dueItems.some((item) => this._isTrainableSrsPair(item));
|
||||
}
|
||||
|
||||
async _getUserByHashedId(hashedUserId) {
|
||||
@@ -713,7 +738,7 @@ export default class VocabService {
|
||||
const question = String(qData.question || qData.text || '');
|
||||
|
||||
let match = question.match(/Wie sagt man ['"]([^'"]+)['"]/i);
|
||||
if (match && match[1] && correctAnswer && match[1].trim() !== String(correctAnswer).trim()) {
|
||||
if (match && this._isTrainableSrsPair({ learning: match[1], reference: String(correctAnswer) })) {
|
||||
vocabMap.set(`${match[1]}-${correctAnswer}`, {
|
||||
learning: match[1],
|
||||
reference: String(correctAnswer)
|
||||
@@ -722,7 +747,7 @@ export default class VocabService {
|
||||
}
|
||||
|
||||
match = question.match(/Was bedeutet ['"]([^'"]+)['"]/i);
|
||||
if (match && match[1] && correctAnswer && match[1].trim() !== String(correctAnswer).trim()) {
|
||||
if (match && this._isTrainableSrsPair({ learning: String(correctAnswer), reference: match[1] })) {
|
||||
vocabMap.set(`${correctAnswer}-${match[1]}`, {
|
||||
learning: String(correctAnswer),
|
||||
reference: match[1]
|
||||
@@ -745,7 +770,7 @@ export default class VocabService {
|
||||
answers.forEach((answer, index) => {
|
||||
const nativeWord = nativeWords[index];
|
||||
const normalizedAnswer = String(answer || '').trim();
|
||||
if (!nativeWord || !normalizedAnswer || nativeWord === normalizedAnswer) {
|
||||
if (!this._isTrainableSrsPair({ learning: nativeWord, reference: normalizedAnswer })) {
|
||||
return;
|
||||
}
|
||||
vocabMap.set(`${nativeWord}-${normalizedAnswer}`, {
|
||||
@@ -764,31 +789,13 @@ export default class VocabService {
|
||||
|
||||
_extractTrainerVocabsFromLessonDidactics(lesson) {
|
||||
const vocabMap = new Map();
|
||||
const speakingPrompts = Array.isArray(lesson?.speakingPrompts) ? lesson.speakingPrompts : [];
|
||||
const practicalTasks = Array.isArray(lesson?.practicalTasks) ? lesson.practicalTasks : [];
|
||||
const corePatterns = Array.isArray(lesson?.corePatterns) ? lesson.corePatterns : [];
|
||||
|
||||
corePatterns.forEach((entry) => {
|
||||
const pattern = this._normalizeCorePatternEntry(entry);
|
||||
const reference = String(pattern?.target || '').trim();
|
||||
const learning = String(pattern?.gloss || '').trim();
|
||||
if (!learning || !reference || learning === reference) return;
|
||||
vocabMap.set(`${learning}-${reference}`, { learning, reference });
|
||||
});
|
||||
|
||||
speakingPrompts.forEach((prompt, index) => {
|
||||
const learning = String(prompt?.prompt || prompt?.title || '').trim();
|
||||
const refEntry = corePatterns[index] ?? corePatterns[0];
|
||||
const reference = String(prompt?.cue || this._corePatternTarget(refEntry) || '').trim();
|
||||
if (!learning || !reference || learning === reference) return;
|
||||
vocabMap.set(`${learning}-${reference}`, { learning, reference });
|
||||
});
|
||||
|
||||
practicalTasks.forEach((task, index) => {
|
||||
const learning = String(task?.text || task?.title || '').trim();
|
||||
const refEntry = corePatterns[index] ?? corePatterns[0];
|
||||
const reference = String(this._corePatternTarget(refEntry) || '').trim();
|
||||
if (!learning || !reference || learning === reference) return;
|
||||
if (!this._isTrainableSrsPair({ learning, reference })) return;
|
||||
vocabMap.set(`${learning}-${reference}`, { learning, reference });
|
||||
});
|
||||
|
||||
@@ -1808,16 +1815,17 @@ export default class VocabService {
|
||||
[Op.lte]: now
|
||||
}
|
||||
};
|
||||
const totalDueCount = await VocabSrsItem.count({ where: dueWhere });
|
||||
const rows = await VocabSrsItem.findAll({
|
||||
const dueRows = await VocabSrsItem.findAll({
|
||||
where: dueWhere,
|
||||
order: [
|
||||
['nextDueAt', 'ASC'],
|
||||
['wrongCount', 'DESC'],
|
||||
['stage', 'ASC']
|
||||
],
|
||||
limit
|
||||
]
|
||||
});
|
||||
const validDueRows = dueRows.filter((item) => this._isTrainableSrsPair(item));
|
||||
const rows = validDueRows.slice(0, limit);
|
||||
const totalDueCount = validDueRows.length;
|
||||
|
||||
return {
|
||||
courseId: course.id,
|
||||
|
||||
Reference in New Issue
Block a user