extended admin tool for finished lessons
All checks were successful
Deploy to production / deploy (push) Successful in 2m54s
All checks were successful
Deploy to production / deploy (push) Successful in 2m54s
This commit is contained in:
@@ -1974,6 +1974,28 @@ class AdminService {
|
||||
}
|
||||
}
|
||||
|
||||
async adminMarkUserVocabLessonsCompleteThrough(requesterHashedId, targetHashedId, courseId, throughLessonNumber) {
|
||||
if (!(await this.hasUserAccess(requesterHashedId, 'useradministration'))) {
|
||||
throw new Error('noaccess');
|
||||
}
|
||||
const vocab = new VocabService();
|
||||
try {
|
||||
return await vocab.adminMarkLessonsCompleteThrough(
|
||||
targetHashedId,
|
||||
Number(courseId),
|
||||
Number(throughLessonNumber)
|
||||
);
|
||||
} catch (e) {
|
||||
if (e.status === 403) {
|
||||
throw new Error('notenrolled');
|
||||
}
|
||||
if (e.status === 400) {
|
||||
throw new Error('badrequest');
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async adminListUserEnrolledVocabCourses(requesterHashedId, targetHashedId) {
|
||||
if (!(await this.hasUserAccess(requesterHashedId, 'useradministration'))) {
|
||||
throw new Error('noaccess');
|
||||
|
||||
@@ -2609,6 +2609,101 @@ export default class VocabService {
|
||||
return this._purgeLessonProgressForUser(user.id, lesson.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin: Alle Lektionen eines Kurses bis einschließlich lesson_number als abgeschlossen setzen
|
||||
* (nur Zeilen, die noch nicht completed sind). Nur bei eingeschriebenem Nutzer.
|
||||
*/
|
||||
async adminMarkLessonsCompleteThrough(targetHashedUserId, courseId, throughLessonNumber) {
|
||||
const user = await this._getUserByHashedId(targetHashedUserId);
|
||||
const cid = Number(courseId);
|
||||
const maxNum = Number(throughLessonNumber);
|
||||
if (!Number.isFinite(cid) || cid < 1) {
|
||||
const err = new Error('Invalid courseId');
|
||||
err.status = 400;
|
||||
throw err;
|
||||
}
|
||||
if (!Number.isFinite(maxNum) || maxNum < 1) {
|
||||
const err = new Error('Invalid throughLessonNumber');
|
||||
err.status = 400;
|
||||
throw err;
|
||||
}
|
||||
|
||||
const enrollment = await VocabCourseEnrollment.findOne({
|
||||
where: { userId: user.id, courseId: cid }
|
||||
});
|
||||
if (!enrollment) {
|
||||
const err = new Error('Not enrolled in this course');
|
||||
err.status = 403;
|
||||
throw err;
|
||||
}
|
||||
|
||||
const lessons = await VocabCourseLesson.findAll({
|
||||
where: { courseId: cid, lessonNumber: { [Op.lte]: maxNum } },
|
||||
order: [['lessonNumber', 'ASC']]
|
||||
});
|
||||
|
||||
const now = new Date();
|
||||
const details = [];
|
||||
|
||||
for (const lesson of lessons) {
|
||||
const lessonData = lesson.get({ plain: true });
|
||||
const targetScore = lessonData.targetScorePercent || 80;
|
||||
|
||||
const [progress] = await VocabCourseProgress.findOrCreate({
|
||||
where: { userId: user.id, lessonId: lesson.id },
|
||||
defaults: {
|
||||
userId: user.id,
|
||||
courseId: cid,
|
||||
lessonId: lesson.id,
|
||||
completed: false,
|
||||
score: 0,
|
||||
lessonState: {}
|
||||
}
|
||||
});
|
||||
|
||||
if (progress.completed) {
|
||||
details.push({
|
||||
lessonNumber: lesson.lessonNumber,
|
||||
lessonId: lesson.id,
|
||||
status: 'unchanged'
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
const mergedState = this._applyScheduledReviewState(
|
||||
this._sanitizeLessonState(progress.lessonState),
|
||||
{
|
||||
previousCompleted: false,
|
||||
nextCompleted: true,
|
||||
shouldAdvanceReview: true,
|
||||
lessonData,
|
||||
now
|
||||
}
|
||||
);
|
||||
|
||||
await progress.update({
|
||||
completed: true,
|
||||
completedAt: now,
|
||||
score: Math.max(Number(progress.score) || 0, targetScore),
|
||||
lastAccessedAt: now,
|
||||
lessonState: mergedState
|
||||
});
|
||||
|
||||
details.push({
|
||||
lessonNumber: lesson.lessonNumber,
|
||||
lessonId: lesson.id,
|
||||
status: 'marked_complete'
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
courseId: cid,
|
||||
throughLessonNumber: maxNum,
|
||||
lessonsConsidered: lessons.length,
|
||||
details
|
||||
};
|
||||
}
|
||||
|
||||
// ========== GRAMMAR EXERCISE METHODS ==========
|
||||
|
||||
async getExerciseTypes() {
|
||||
|
||||
Reference in New Issue
Block a user