105 lines
3.2 KiB
JavaScript
105 lines
3.2 KiB
JavaScript
import { ReviewLog } from '../models/ReviewLog.js';
|
|
import { StudyItem } from '../models/StudyItem.js';
|
|
import { getDaysDiff, getSRSDate } from '../utils/dateUtils.js';
|
|
|
|
export const processReview = async (user, subjectId, success) => {
|
|
if (!user.stats) user.stats = { totalReviews: 0, correctReviews: 0, currentStreak: 0, maxStreak: 0 };
|
|
user.stats.totalReviews += 1;
|
|
if (success) user.stats.correctReviews += 1;
|
|
|
|
const todayStr = new Date().toISOString().split('T')[0];
|
|
const lastStudyStr = user.stats.lastStudyDate;
|
|
|
|
if (lastStudyStr !== todayStr) {
|
|
if (!lastStudyStr) {
|
|
user.stats.currentStreak = 1;
|
|
} else {
|
|
const diff = getDaysDiff(lastStudyStr, todayStr);
|
|
if (diff === 1) {
|
|
user.stats.currentStreak += 1;
|
|
} else if (diff > 1) {
|
|
const lastFreeze = user.stats.lastFreezeDate ? new Date(user.stats.lastFreezeDate) : null;
|
|
let daysSinceFreeze = 999;
|
|
if (lastFreeze) daysSinceFreeze = getDaysDiff(lastFreeze.toISOString().split('T')[0], todayStr);
|
|
|
|
const canUseShield = daysSinceFreeze >= 7;
|
|
|
|
if (canUseShield && diff === 2) {
|
|
console.log(`User ${user._id} saved by Zen Shield!`);
|
|
user.stats.lastFreezeDate = new Date();
|
|
user.stats.currentStreak += 1;
|
|
} else {
|
|
user.stats.currentStreak = 1;
|
|
}
|
|
}
|
|
}
|
|
user.stats.lastStudyDate = todayStr;
|
|
if (user.stats.currentStreak > user.stats.maxStreak) user.stats.maxStreak = user.stats.currentStreak;
|
|
}
|
|
await user.save();
|
|
|
|
await ReviewLog.findOneAndUpdate(
|
|
{ userId: user._id, date: todayStr },
|
|
{ $inc: { count: 1 } },
|
|
{ upsert: true, new: true }
|
|
);
|
|
|
|
const item = await StudyItem.findOne({ userId: user._id, wkSubjectId: subjectId });
|
|
if (!item) throw new Error('Item not found');
|
|
|
|
if (!item.stats) item.stats = { correct: 0, total: 0 };
|
|
item.stats.total += 1;
|
|
if (success) item.stats.correct += 1;
|
|
|
|
if (success) {
|
|
const nextLevel = Math.min(item.srsLevel + 1, 10);
|
|
item.srsLevel = nextLevel;
|
|
item.nextReview = getSRSDate(nextLevel);
|
|
} else {
|
|
item.srsLevel = Math.max(1, item.srsLevel - 1);
|
|
item.nextReview = Date.now();
|
|
}
|
|
|
|
await item.save();
|
|
return { nextReview: item.nextReview, srsLevel: item.srsLevel };
|
|
};
|
|
|
|
export const getQueue = async (user, limit = 100, sortMode) => {
|
|
const query = {
|
|
userId: user._id,
|
|
srsLevel: { $lt: 10, $gt: 0 },
|
|
nextReview: { $lte: new Date() }
|
|
};
|
|
|
|
let dueItems;
|
|
if (sortMode === 'priority') {
|
|
dueItems = await StudyItem.find(query).sort({ srsLevel: 1, level: 1 }).limit(limit);
|
|
} else {
|
|
dueItems = await StudyItem.find(query).limit(limit);
|
|
for (let i = dueItems.length - 1; i > 0; i--) {
|
|
const j = Math.floor(Math.random() * (i + 1));
|
|
[dueItems[i], dueItems[j]] = [dueItems[j], dueItems[i]];
|
|
}
|
|
}
|
|
return dueItems;
|
|
};
|
|
|
|
export const getLessonQueue = async (user, limit = 100) => {
|
|
const query = {
|
|
userId: user._id,
|
|
srsLevel: 0
|
|
};
|
|
return await StudyItem.find(query).sort({ level: 1, wkSubjectId: 1 }).limit(limit);
|
|
};
|
|
|
|
export const processLesson = async (user, subjectId) => {
|
|
const item = await StudyItem.findOne({ userId: user._id, wkSubjectId: subjectId });
|
|
if (!item) throw new Error('Item not found');
|
|
|
|
item.srsLevel = 1;
|
|
item.nextReview = getSRSDate(1);
|
|
|
|
await item.save();
|
|
return { success: true, item };
|
|
};
|