Skip to content

Commit

Permalink
Feat/add reschedule item
Browse files Browse the repository at this point in the history
  • Loading branch information
ishiko732 committed Sep 30, 2024
1 parent 52f0183 commit 66c0b79
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 18 deletions.
57 changes: 46 additions & 11 deletions __tests__/reschedule.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,10 @@ function experiment(
return output
}

function testReschedule<T = RecordLogItem>(
function testReschedule(
scheduler: FSRS,
tests: number[][],
options: Partial<RescheduleOptions<T>> = {}
options: Partial<RescheduleOptions> = {}
) {
for (const test of tests) {
const reviews = test.map((rating, index) => ({
Expand All @@ -140,7 +140,11 @@ function testReschedule<T = RecordLogItem>(
),
state: rating === Rating.Manual ? State.New : undefined,
}))
const control = <RecordLogItem[]>scheduler.reschedule(reviews, options)
const { collections: control } = scheduler.reschedule(
createEmptyCard(),
reviews,
options
)
const experimentResult = experiment(
scheduler,
reviews,
Expand Down Expand Up @@ -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')
})

Expand All @@ -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')
})

Expand Down Expand Up @@ -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)
})
Expand Down Expand Up @@ -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,
})
})
})
26 changes: 21 additions & 5 deletions src/fsrs/fsrs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -398,13 +398,15 @@ export class FSRS extends FSRSAlgorithm {
*
*/
reschedule<T = RecordLogItem>(
current_card: CardInput | Card,
reviews: FSRSHistory[] = [],
options: Partial<RescheduleOptions<T>> = {}
): Array<T> {
): IReschedule<T> {
const {
recordLogHandler,
reviewsOrderBy,
skipManual: skipManual = true,
update_memory_state: updateMemoryState = false,
} = options
if (reviewsOrderBy && typeof reviewsOrderBy === 'function') {
reviews.sort(reviewsOrderBy)
Expand All @@ -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<T>
}
}

Expand Down
10 changes: 8 additions & 2 deletions src/fsrs/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,15 @@ export interface IScheduler {
review(state: Grade): RecordLogItem
}

export type RescheduleOptions<T> = {
export type RescheduleOptions<T = RecordLogItem> = {
recordLogHandler: (recordLog: RecordLogItem) => T
reviewsOrderBy: (a: FSRSHistory, b: FSRSHistory) => number
skipManual: boolean
card?: CardInput
update_memory_state: boolean
first_card?: CardInput
}

export type IReschedule<T = RecordLogItem> = {
collections: T[]
reschedule_item: T | null
}

0 comments on commit 66c0b79

Please sign in to comment.