diff --git a/__tests__/reschedule.test.ts b/__tests__/reschedule.test.ts index 8a47658..b824a46 100644 --- a/__tests__/reschedule.test.ts +++ b/__tests__/reschedule.test.ts @@ -127,10 +127,10 @@ function experiment( return output } -function testReschedule( +function testReschedule( scheduler: FSRS, tests: number[][], - options: Partial> = {} + options: Partial = {} ) { for (const test of tests) { const reviews = test.map((rating, index) => ({ @@ -140,7 +140,11 @@ function testReschedule( ), state: rating === Rating.Manual ? State.New : undefined, })) - const control = scheduler.reschedule(reviews, options) + const { collections: control } = scheduler.reschedule( + createEmptyCard(), + reviews, + options + ) const experimentResult = experiment( scheduler, reviews, @@ -230,7 +234,7 @@ describe('FSRS reschedule', () => { ), })) expect(() => { - scheduler.reschedule(reviews, { skipManual: false }) + scheduler.reschedule(createEmptyCard(), reviews, { skipManual: false }) }).toThrow('reschedule: state is required for manual rating') }) @@ -244,7 +248,7 @@ describe('FSRS reschedule', () => { state: rating === Rating.Manual ? State.Review : undefined, })) expect(() => { - scheduler.reschedule(reviews, { skipManual: false }) + scheduler.reschedule(createEmptyCard(), reviews, { skipManual: false }) }).toThrow('reschedule: due is required for manual rating') }) @@ -313,7 +317,13 @@ describe('FSRS reschedule', () => { }, } - const control = scheduler.reschedule(reviews, { skipManual: false }) + const { collections: control } = scheduler.reschedule( + createEmptyCard(), + reviews, + { + skipManual: false, + } + ) expect(control[2]).toEqual(expected) expect(control[3]).toEqual(nextItemExpected) }) @@ -356,15 +366,40 @@ describe('FSRS reschedule', () => { }, } - const control = scheduler.reschedule(reviews, { skipManual: false }) + const current_card = { + due: new Date(1725584400000 /**'2024-09-06T01:00:00.000Z'*/), + stability: 21.79806877, + difficulty: 3.2828565, + elapsed_days: 1, + scheduled_days: 22, + reps: 4, + lapses: 0, + state: State.Review, + last_review: new Date(1723683600000 /**'2024-08-15T01:00:00.000Z'*/), + } + + const { collections: control, reschedule_item } = scheduler.reschedule( + current_card, + reviews, + { + skipManual: false, + } + ) expect(control[2]).toEqual(expected) + expect(reschedule_item).toBeNull() }) it('Handling the case of an empty set.', () => { - const control = scheduler.reschedule([]) - expect(control).toEqual([]) + const control = scheduler.reschedule(createEmptyCard(), []) + expect(control).toEqual({ + collections: [], + reschedule_item: null, + }) - const control2 = scheduler.reschedule() - expect(control2).toEqual([]) + const control2 = scheduler.reschedule(createEmptyCard()) + expect(control2).toEqual({ + collections: [], + reschedule_item: null, + }) }) }) diff --git a/src/fsrs/fsrs.ts b/src/fsrs/fsrs.ts index f4035f8..be55160 100644 --- a/src/fsrs/fsrs.ts +++ b/src/fsrs/fsrs.ts @@ -11,7 +11,7 @@ import { ReviewLogInput, State, } from './models' -import type { IPreview, RescheduleOptions } from './types' +import type { IPreview, IReschedule, RescheduleOptions } from './types' import { FSRSAlgorithm } from './algorithm' import { TypeConvert } from './convert' import BasicScheduler from './impl/basic_scheduler' @@ -398,13 +398,15 @@ export class FSRS extends FSRSAlgorithm { * */ reschedule( + current_card: CardInput | Card, reviews: FSRSHistory[] = [], options: Partial> = {} - ): Array { + ): IReschedule { const { recordLogHandler, reviewsOrderBy, skipManual: skipManual = true, + update_memory_state: updateMemoryState = false, } = options if (reviewsOrderBy && typeof reviewsOrderBy === 'function') { reviews.sort(reviewsOrderBy) @@ -415,13 +417,27 @@ export class FSRS extends FSRSAlgorithm { const rescheduleSvc = new Reschedule(this) const collections = rescheduleSvc.reschedule( - options.card || createEmptyCard(), + options.first_card || createEmptyCard(), reviews ) + const len = collections.length + const cur_card = TypeConvert.card(current_card) + const manual_item = rescheduleSvc.calculateManualRecord( + cur_card, + len ? collections[len - 1] : undefined, + updateMemoryState + ) + if (recordLogHandler && typeof recordLogHandler === 'function') { - return collections.map(recordLogHandler) + return { + collections: collections.map(recordLogHandler), + reschedule_item: manual_item ? recordLogHandler(manual_item) : null, + } } - return collections as T[] + return { + collections, + reschedule_item: manual_item, + } as IReschedule } } diff --git a/src/fsrs/types.ts b/src/fsrs/types.ts index 718feba..5fdb121 100644 --- a/src/fsrs/types.ts +++ b/src/fsrs/types.ts @@ -21,9 +21,15 @@ export interface IScheduler { review(state: Grade): RecordLogItem } -export type RescheduleOptions = { +export type RescheduleOptions = { recordLogHandler: (recordLog: RecordLogItem) => T reviewsOrderBy: (a: FSRSHistory, b: FSRSHistory) => number skipManual: boolean - card?: CardInput + update_memory_state: boolean + first_card?: CardInput +} + +export type IReschedule = { + collections: T[] + reschedule_item: T | null }