Refactor lesson types and update review handling
All checks were successful
Deploy to production / deploy (push) Successful in 2m22s

- Changed lesson type from 'review' to 'weekly_review' in multiple lesson definitions across phase 3 and phase 4 extensions.
- Updated the logic in the vocabService to accommodate 'weekly_review' in various methods, ensuring proper handling of weekly review lessons.
- Modified the VocabLessonView component to recognize 'weekly_review' as a valid lesson type for calculations and access checks.
- Enhanced the didactics update script to include 'weekly_review' in the lesson type checks.
- Adjusted the create scripts to reflect the new lesson type for weekly reviews.
This commit is contained in:
Torsten Schulz (local)
2026-05-27 11:34:03 +02:00
parent 664a7b3530
commit d441b4fa31
9 changed files with 131 additions and 67 deletions

View File

@@ -421,7 +421,7 @@ export default class VocabService {
_supportsScheduledReview(lessonData = null) {
const lessonType = String(lessonData?.lessonType || '').toLowerCase();
const didacticMode = String(lessonData?.didacticMode || '').toLowerCase();
if (lessonType === 'culture' || lessonType === 'review' || lessonType === 'vocab_review') {
if (lessonType === 'culture' || lessonType === 'review' || lessonType === 'vocab_review' || lessonType === 'weekly_review') {
return false;
}
if (didacticMode === 'intensive_review' || didacticMode === 'checkpoint') {
@@ -1051,7 +1051,7 @@ export default class VocabService {
if (title.includes('abschluss') || title.includes('prüfung') || title.includes('test')) {
return 'checkpoint';
}
if (plainLesson.isIntensiveReview || lessonType === 'review' || lessonType === 'vocab_review' || title.includes('wiederholung')) {
if (plainLesson.isIntensiveReview || lessonType === 'review' || lessonType === 'vocab_review' || lessonType === 'weekly_review' || title.includes('wiederholung')) {
return 'intensive_review';
}
if (lessonType === 'grammar') {
@@ -2663,7 +2663,7 @@ export default class VocabService {
if (!plainLesson?.chapterId) {
return list;
}
if (plainLesson.lessonType === 'review' || plainLesson.lessonType === 'vocab_review') {
if (plainLesson.lessonType === 'review' || plainLesson.lessonType === 'vocab_review' || plainLesson.lessonType === 'weekly_review') {
return list;
}
let rows = [];
@@ -2899,13 +2899,47 @@ export default class VocabService {
}
});
const isWeeklyReview = plainLesson.lessonType === 'weekly_review';
// Lade Vokabeln aus vorherigen Lektionen (für Wiederholung UND für gemischten Vokabeltrainer)
if (plainLesson.lessonNumber > 1) {
if (plainLesson.lessonNumber > 1 && !isWeeklyReview) {
plainLesson.previousLessonExercises = await this._getReviewVocabExercises(plainLesson.courseId, plainLesson.lessonNumber);
}
// Bei Wiederholungslektionen: Auch Lektions-Liste für Anzeige
if (plainLesson.lessonType === 'review' || plainLesson.lessonType === 'vocab_review') {
plainLesson.reviewLessons = await this._getReviewLessons(plainLesson.courseId, plainLesson.lessonNumber);
if (isWeeklyReview) {
const weeklyLessons = await this._getReviewLessons(
plainLesson.courseId,
plainLesson.lessonNumber,
plainLesson.weekNumber
);
const weeklyExercises = await this._getReviewVocabExercises(
plainLesson.courseId,
plainLesson.lessonNumber,
plainLesson.weekNumber
);
plainLesson.reviewLessons = weeklyLessons;
plainLesson.previousLessonExercises = [];
plainLesson.weeklyReviewTrainingExercises = weeklyExercises;
plainLesson.reviewVocabExercises = this._selectWeeklyReviewExamExercises(weeklyExercises, plainLesson.id);
plainLesson.weeklyReviewExamCount = plainLesson.reviewVocabExercises.length;
plainLesson.weeklyReviewTrainingCount = weeklyExercises.length;
plainLesson.corePatterns = weeklyLessons.flatMap((entry) => {
if (Array.isArray(entry.corePatterns)) return entry.corePatterns;
if (typeof entry.corePatterns === 'string') {
try {
const parsed = JSON.parse(entry.corePatterns);
return Array.isArray(parsed) ? parsed : [];
} catch (error) {
return [];
}
}
return [];
});
} else if (plainLesson.lessonType === 'review' || plainLesson.lessonType === 'vocab_review') {
// Kursweite/gezielt kuratierte Reviews behalten das bisherige Verhalten.
plainLesson.reviewLessons = await this._getReviewLessons(
plainLesson.courseId,
plainLesson.lessonNumber
);
plainLesson.reviewVocabExercises = plainLesson.previousLessonExercises || [];
}
@@ -3091,19 +3125,24 @@ export default class VocabService {
/**
* Sammelt alle Lektionen, die in einer Wiederholungslektion wiederholt werden sollen
*/
async _getReviewLessons(courseId, currentLessonNumber) {
const lessons = await VocabCourseLesson.findAll({
where: {
courseId: courseId,
lessonNumber: {
[Op.lt]: currentLessonNumber // Nur Lektionen mit kleinerer Nummer
},
lessonType: {
[Op.notIn]: ['review', 'vocab_review'] // Keine anderen Wiederholungslektionen
}
async _getReviewLessons(courseId, currentLessonNumber, weekNumber = null) {
const where = {
courseId: courseId,
lessonNumber: {
[Op.lt]: currentLessonNumber // Nur Lektionen mit kleinerer Nummer
},
lessonType: {
[Op.notIn]: ['review', 'vocab_review', 'weekly_review'] // Keine anderen Wiederholungslektionen
}
};
if (weekNumber != null) {
where.weekNumber = weekNumber;
}
const lessons = await VocabCourseLesson.findAll({
where,
order: [['lessonNumber', 'ASC']],
attributes: ['id', 'lessonNumber', 'title']
attributes: ['id', 'lessonNumber', 'title', 'corePatterns']
});
return lessons.map(l => l.get({ plain: true }));
}
@@ -3111,17 +3150,22 @@ export default class VocabService {
/**
* Sammelt alle Grammatik-Übungen aus vorherigen Lektionen für Wiederholungslektionen
*/
async _getReviewVocabExercises(courseId, currentLessonNumber) {
const previousLessons = await VocabCourseLesson.findAll({
where: {
courseId: courseId,
lessonNumber: {
[Op.lt]: currentLessonNumber
},
lessonType: {
[Op.notIn]: ['review', 'vocab_review']
}
async _getReviewVocabExercises(courseId, currentLessonNumber, weekNumber = null) {
const where = {
courseId: courseId,
lessonNumber: {
[Op.lt]: currentLessonNumber
},
lessonType: {
[Op.notIn]: ['review', 'vocab_review', 'weekly_review']
}
};
if (weekNumber != null) {
where.weekNumber = weekNumber;
}
const previousLessons = await VocabCourseLesson.findAll({
where,
attributes: ['id']
});
@@ -3156,6 +3200,16 @@ export default class VocabService {
return exercises.map(e => e.get({ plain: true }));
}
_selectWeeklyReviewExamExercises(exercises = [], lessonId) {
const list = Array.isArray(exercises) ? exercises : [];
if (list.length === 0) return [];
const seed = (Number(lessonId) * 100003) >>> 0;
const percentage = 40 + (seed % 21);
const targetCount = Math.max(1, Math.ceil((list.length * percentage) / 100));
return this._seededShuffle(list.slice(), seed).slice(0, targetCount);
}
/**
* Sammelt GrammatikÜbungen aus vorherigen Lektionen derselben Woche
*/
@@ -3166,7 +3220,7 @@ export default class VocabService {
courseId: courseId,
weekNumber: weekNumber,
lessonNumber: { [Op.lt]: currentLessonNumber },
lessonType: { [Op.notIn]: ['review', 'vocab_review'] }
lessonType: { [Op.notIn]: ['review', 'vocab_review', 'weekly_review'] }
},
attributes: ['id']
});