refactor: Move the SM2 implementation to the backend

This commit is contained in:
Alexander Polynomdivision
2018-10-17 14:40:21 +02:00
parent 6e80ed79d2
commit 7de3cedb15
2 changed files with 16 additions and 17 deletions

View File

@@ -0,0 +1,35 @@
import { dayInNDays } from "../../utils/date";
export interface ISchedulingData {
easiness: number;
consecutiveCorrectAnswers: number;
nextDueDate: number;
};
export enum AnswerType {
CORRECT,
WRONG,
};
function performanceRating(answer: AnswerType): number {
switch (answer) {
case AnswerType.WRONG:
return 1;
case AnswerType.CORRECT:
return 4;
}
}
export function updateSchedulingData(data: ISchedulingData, answer: AnswerType): ISchedulingData {
const perfRating = performanceRating(answer);
data.easiness += -0.8 + 0.28 * perfRating + 0.02 * Math.pow(perfRating, 2);
data.consecutiveCorrectAnswers = answer === AnswerType.CORRECT ? (
data.consecutiveCorrectAnswers + 1
) : 0;
data.nextDueDate = answer === AnswerType.CORRECT ? (
dayInNDays(6 * Math.pow(data.easiness, data.consecutiveCorrectAnswers - 1))
) : dayInNDays(1);
return data;
}

View File

@@ -8,7 +8,9 @@ import { authRoute } from "../security/token";
import { userFromSession } from "../utils/user";
import { IUser, IUserDBModel } from "../models/user";
import { ISM2Metadata } from "../models/review";
import {
ISchedulingData, AnswerType, updateSchedulingData
} from "../algorithms/sm2";
import { LRequest } from "../types/express";
const userRouter = express.Router();
@@ -190,34 +192,31 @@ userRouter.post("/lastReview", async (req: LRequest, res) => {
return;
}
// NOTE: Oh oh
// Update the user's score based on what they sent us
user.score += parseInt(req.body.delta);
// Itterate over all of the vocabulary that needs to have its
// metadata updated.
// Use our SM2 implementation to update the vocabulary and modify our
// copy of the user object, so that we can write the copied (and modified)
// user object back into the database.
Object.keys(req.body.sm2).forEach((id: string) => {
const vocabId = parseInt(id);
const correct: boolean = req.body.sm2[id];
let vocab_sm2: ISM2Metadata = Object.assign({},
let vocab_sm2: ISchedulingData = Object.assign({},
user.vocabMetadata[vocabId]);
// TODO: Tuning?
// TODO: Move into another module
const perf = correct ? 3 : 1;
vocab_sm2.easiness += -0.8 + 0.28 * perf + 0.02 * Math.pow(perf, 2);
// Update the consecutive correct answers and the due date
if (correct) {
vocab_sm2.consecutiveCorrectAnswers += 1;
vocab_sm2.nextDueDate = dateInNDays(6 * Math.pow(vocab_sm2.easiness, vocab_sm2.consecutiveCorrectAnswers - 1));
} else {
vocab_sm2.consecutiveCorrectAnswers = 0;
vocab_sm2.nextDueDate = dateInNDays(1);
}
user.vocabMetadata[vocabId] = vocab_sm2;
const answer = correct ? AnswerType.CORRECT : AnswerType.WRONG;
user.vocabMetadata[vocabId] = updateSchedulingData(vocab_sm2, answer);
});
// Update the last review
user.lastReview = req.body.meta;
// TODO: Error handling
// NOTE: Error handling could be implemented here, but we can ensure that
// the user exists, as we just got his username (and entire data)
// from `userFromSession`.
await db.collection("users").updateOne({
username: user.username,
}, {