From b1958a626751398357ca28850af0666cb0d6d5a9 Mon Sep 17 00:00:00 2001 From: Alex Errant <109672176+AlexErrant@users.noreply.github.com> Date: Sun, 28 Apr 2024 08:08:58 -0500 Subject: [PATCH] add `minute_offset` and update to fsrs 0.6.2 (#25) --- Cargo.lock | 2 +- fsrs-rs | 2 +- sandbox/src/train.ts | 20 +++++++++++++++++++- sandbox/tests/prod.spec.ts | 12 ++++++------ src/lib.rs | 8 ++++++-- 5 files changed, 33 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fc63c76..38b60eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -726,7 +726,7 @@ checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" [[package]] name = "fsrs" -version = "0.6.1" +version = "0.6.2" dependencies = [ "burn", "chrono", diff --git a/fsrs-rs b/fsrs-rs index 2170a29..14a6284 160000 --- a/fsrs-rs +++ b/fsrs-rs @@ -1 +1 @@ -Subproject commit 2170a2903699ccb99d913def9f5f6e485e1681ca +Subproject commit 14a62849af95a0df0f97cc1251390d03a1dda19c diff --git a/sandbox/src/train.ts b/sandbox/src/train.ts index d1010f9..1c68fea 100644 --- a/sandbox/src/train.ts +++ b/sandbox/src/train.ts @@ -135,7 +135,25 @@ function computeParameters( // Do not read from memory after `Stop` is posted! pointer: progress.pointer(), } satisfies ProgressMessage) - let parameters = fsrs.computeParametersAnki(cids, eases, ids, types, progress) + // MDN states: + // The number of minutes returned by getTimezoneOffset() is positive if the + // local time zone is behind UTC, and negative if the local time zone is ahead of UTC. + // This is useful when going from our timezone to UTC; our local time + offset = UTC. + // However, we want to go from UTC to our timezone. (Anki revlog id is UTC, and we want to convert it to local time.) + // So we flip the sign of `getTimezoneOffset()`. + let timeZoneMinutesOffset = new Date().getTimezoneOffset() * -1 + let ankiNextDayStartsAtMinutes = 4 * 60 + let parameters = fsrs.computeParametersAnki( + // We subtract ankiNextDayStartsAtMinutes because + // we want a review done at, say, 1am to be done for the *prior* day. + // Adding would move it into the future. + timeZoneMinutesOffset - ankiNextDayStartsAtMinutes, + cids, + eases, + ids, + types, + progress, + ) self.postMessage({ tag: 'Stop', parameters, diff --git a/sandbox/tests/prod.spec.ts b/sandbox/tests/prod.spec.ts index cf1e465..54e9a80 100644 --- a/sandbox/tests/prod.spec.ts +++ b/sandbox/tests/prod.spec.ts @@ -37,8 +37,8 @@ test('check progress and parameters', async ({ page }) => { let [n, d] = progress.split('/').map((x) => Math.round(parseInt(x))) return { numeratorLargerThan0: n > 0, - numeratorLessThanMax: n < 54500, - denominatorIsMax: d === 54500, + numeratorLessThanDenominator: n < d, + denominatorIsLarge: d > 10000, } }, { @@ -49,19 +49,19 @@ test('check progress and parameters', async ({ page }) => { ) .toEqual({ numeratorLargerThan0: true, - numeratorLessThanMax: true, - denominatorIsMax: true, + numeratorLessThanDenominator: true, + denominatorIsLarge: true, }) await expect .poll( async () => { let parameters = await page.locator('#parametersResult').innerText() - return parameters.split(',').map((x) => Math.round(parseFloat(x))) + return parameters.split(',').length }, { timeout: 20_000, intervals: [200], }, ) - .toEqual([1, 2, 4, 11, 5, 1, 1, 0, 2, 0, 1, 2, 0, 0, 2, 0, 3]) + .toEqual(17) }) diff --git a/src/lib.rs b/src/lib.rs index 3d5b7ef..0e6d659 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,9 +29,11 @@ impl FSRSwasm { Self { model } } + /// `minute_offset` should be the `user's timezone offset from UTC` minus `Anki's "next day starts at"`, in minutes. #[wasm_bindgen(js_name = computeParametersAnki)] pub fn compute_parameters_anki( &mut self, + minute_offset: i32, cids: &[i64], eases: &[u8], ids: &[i64], @@ -39,7 +41,7 @@ impl FSRSwasm { progress: Option, ) -> Vec { let revlog_entries = to_revlog_entry(cids, eases, ids, types); - let items = anki_to_fsrs(revlog_entries); + let items = anki_to_fsrs(revlog_entries, minute_offset); self.train_and_set_parameters(items, progress) } @@ -98,9 +100,11 @@ impl FSRSwasm { } #[wasm_bindgen(js_name = memoryStateAnki)] + /// `minute_offset` should be the `user's timezone offset from UTC` minus `Anki's "next day starts at"`, in minutes. /// Returns an array of 2 elements: `[stability, difficulty]` pub fn memory_state_anki( &self, + minute_offset: i32, cids: &mut [i64], eases: &[u8], ids: &[i64], @@ -116,7 +120,7 @@ impl FSRSwasm { assert_eq!(len, 1, "Expected 1 card, but was given {}", len); let revlog_entries = to_revlog_entry(cids, eases, ids, types); - anki_to_fsrs(revlog_entries) + anki_to_fsrs(revlog_entries, minute_offset) .pop() .map(|item| self._memory_state(item)) }