From 1ad0c062073e5ecec5e7b048ff4d0e85b295110a Mon Sep 17 00:00:00 2001 From: ishiko Date: Thu, 26 Sep 2024 23:57:42 +0800 Subject: [PATCH] pref/separate the code --- src/fsrs/fsrs.ts | 84 ++++--------------------------- src/fsrs/reschedule.ts | 110 +++++++++++++++++++++++++++++++++++++++++ src/fsrs/types.ts | 4 +- 3 files changed, 123 insertions(+), 75 deletions(-) create mode 100644 src/fsrs/reschedule.ts diff --git a/src/fsrs/fsrs.ts b/src/fsrs/fsrs.ts index 4dea638..f4035f8 100644 --- a/src/fsrs/fsrs.ts +++ b/src/fsrs/fsrs.ts @@ -17,6 +17,7 @@ import { TypeConvert } from './convert' import BasicScheduler from './impl/basic_scheduler' import LongTermScheduler from './impl/long_term_scheduler' import { createEmptyCard } from './default' +import { Reschedule } from './reschedule' export class FSRS extends FSRSAlgorithm { private Scheduler @@ -411,81 +412,16 @@ export class FSRS extends FSRSAlgorithm { if (skipManual) { reviews = reviews.filter((review) => review.rating !== Rating.Manual) } - const datum: T[] = [] - let card: Card | undefined = undefined - for (const [index, review] of reviews.entries()) { - card = (card || createEmptyCard(review.review)) - let log: ReviewLog - if (!skipManual && review.rating === Rating.Manual) { - if (typeof review.state === 'undefined') { - throw new Error('reschedule: state is required for manual rating') - } - if (review.state === State.New) { - log = { - rating: Rating.Manual, - state: review.state, - due: review.due, - stability: card.stability, - difficulty: card.difficulty, - elapsed_days: review.elapsed_days, - last_elapsed_days: card.elapsed_days, - scheduled_days: card.scheduled_days, - review: review.review, - } - card = createEmptyCard(review.review) - card.last_review = review.review - } else { - if (typeof review.due === 'undefined') { - throw new Error('reschedule: due is required for manual rating') - } - const scheduled_days = review.due.diff(review.review as Date, 'days') - const elapsed_days = - review.elapsed_days || - review.review.diff(card.last_review as Date, 'days') - log = { - rating: Rating.Manual, - state: review.state, - due: card.last_review, - stability: card.stability, - difficulty: card.difficulty, - elapsed_days: elapsed_days, - last_elapsed_days: card.elapsed_days, - scheduled_days: card.scheduled_days, - review: review.review, - } - card = { - ...card, - state: review.state, - due: review.due, - last_review: review.review, - stability: review.stability || card.stability, - difficulty: review.difficulty || card.difficulty, - elapsed_days: elapsed_days, - scheduled_days: scheduled_days, - reps: index + 1, - } - } - datum.push( - ( - (recordLogHandler && typeof recordLogHandler === 'function' - ? recordLogHandler({ card, log }) - : { card, log }) - ) - ) - } else { - const item = this.next(card, review.review, review.rating) - card = item.card - datum.push( - ( - (recordLogHandler && typeof recordLogHandler === 'function' - ? recordLogHandler(item) - : item) - ) - ) - } - } + const rescheduleSvc = new Reschedule(this) - return datum + const collections = rescheduleSvc.reschedule( + options.card || createEmptyCard(), + reviews + ) + if (recordLogHandler && typeof recordLogHandler === 'function') { + return collections.map(recordLogHandler) + } + return collections as T[] } } diff --git a/src/fsrs/reschedule.ts b/src/fsrs/reschedule.ts new file mode 100644 index 0000000..d125077 --- /dev/null +++ b/src/fsrs/reschedule.ts @@ -0,0 +1,110 @@ +import { TypeConvert } from './convert' +import { createEmptyCard } from './default' +import type { FSRS } from './fsrs' +import { + Card, + CardInput, + FSRSHistory, + Grade, + Rating, + RecordLogItem, + ReviewLog, + State, +} from './models' + +export class Reschedule { + private fsrs: FSRS + constructor(fsrs: FSRS) { + this.fsrs = fsrs + } + + replay(card: Card, reviewed: Date, rating: Grade): RecordLogItem { + return this.fsrs.next(card, reviewed, rating) + } + + processManual( + card: Card, + state: State, + reviewed: Date, + elapsed_days?: number, + stability?: number, + difficulty?: number, + due?: Date + ): RecordLogItem { + if (typeof state === 'undefined') { + throw new Error('reschedule: state is required for manual rating') + } + let log: ReviewLog + let next_card: Card + if (state === State.New) { + log = { + rating: Rating.Manual, + state: state, + due: due ?? reviewed, + stability: card.stability, + difficulty: card.difficulty, + elapsed_days: elapsed_days || 0, + last_elapsed_days: card.elapsed_days, + scheduled_days: card.scheduled_days, + review: reviewed, + } satisfies ReviewLog + next_card = createEmptyCard(reviewed) + next_card.last_review = reviewed + } else { + if (typeof due === 'undefined') { + throw new Error('reschedule: due is required for manual rating') + } + const scheduled_days = due.diff(reviewed as Date, 'days') + elapsed_days = + elapsed_days || reviewed.diff(card.last_review as Date, 'days') + log = { + rating: Rating.Manual, + state: state, + due: card.last_review, + stability: card.stability, + difficulty: card.difficulty, + elapsed_days: elapsed_days, + last_elapsed_days: card.elapsed_days, + scheduled_days: card.scheduled_days, + review: reviewed, + } satisfies ReviewLog + next_card = { + ...card, + state: state, + due: due, + last_review: reviewed, + stability: stability || card.stability, + difficulty: difficulty || card.difficulty, + elapsed_days: elapsed_days, + scheduled_days: scheduled_days, + reps: card.reps + 1, + } satisfies Card + } + + return { card: next_card, log } + } + + reschedule(card: CardInput, reviews: FSRSHistory[]) { + const collections: RecordLogItem[] = [] + let _card = TypeConvert.card(card) + for (const review of reviews) { + let item: RecordLogItem + if (review.rating === Rating.Manual) { + item = this.processManual( + _card, + review.state, + review.review, + review.elapsed_days, + review.stability, + review.difficulty, + review.due + ) + } else { + item = this.replay(_card, review.review, review.rating) + } + collections.push(item) + _card = item.card + } + return collections + } +} diff --git a/src/fsrs/types.ts b/src/fsrs/types.ts index d21adfc..718feba 100644 --- a/src/fsrs/types.ts +++ b/src/fsrs/types.ts @@ -1,5 +1,6 @@ import type { Card, + CardInput, FSRSHistory, Grade, RecordLog, @@ -21,7 +22,8 @@ export interface IScheduler { } export type RescheduleOptions = { - recordLogHandler:(recordLog: RecordLogItem) => T + recordLogHandler: (recordLog: RecordLogItem) => T reviewsOrderBy: (a: FSRSHistory, b: FSRSHistory) => number skipManual: boolean + card?: CardInput }