mirror of
https://github.com/ankitects/anki.git
synced 2025-09-21 15:32:23 -04:00
support card state mutator in test scheduler
Documentation to come
This commit is contained in:
parent
1f16ce2096
commit
9edac805ad
7 changed files with 145 additions and 2 deletions
|
@ -37,12 +37,21 @@ copy_files_into_group(
|
||||||
package = "//ts/editor",
|
package = "//ts/editor",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
copy_files_into_group(
|
||||||
|
name = "reviewer_extras",
|
||||||
|
srcs = [
|
||||||
|
"reviewer_extras.js",
|
||||||
|
],
|
||||||
|
package = "//ts/reviewer",
|
||||||
|
)
|
||||||
|
|
||||||
filegroup(
|
filegroup(
|
||||||
name = "js",
|
name = "js",
|
||||||
srcs = [
|
srcs = [
|
||||||
"aqt_es5",
|
"aqt_es5",
|
||||||
"editor",
|
"editor",
|
||||||
"mathjax.js",
|
"mathjax.js",
|
||||||
|
"reviewer_extras",
|
||||||
"//qt/aqt/data/web/js/vendor",
|
"//qt/aqt/data/web/js/vendor",
|
||||||
],
|
],
|
||||||
visibility = ["//qt:__subpackages__"],
|
visibility = ["//qt:__subpackages__"],
|
||||||
|
|
|
@ -22,6 +22,7 @@ import aqt
|
||||||
from anki import hooks
|
from anki import hooks
|
||||||
from anki.collection import GraphPreferences, OpChanges
|
from anki.collection import GraphPreferences, OpChanges
|
||||||
from anki.decks import UpdateDeckConfigs
|
from anki.decks import UpdateDeckConfigs
|
||||||
|
from anki.scheduler.v3 import NextStates
|
||||||
from anki.utils import devMode, from_json_bytes
|
from anki.utils import devMode, from_json_bytes
|
||||||
from aqt.deckoptions import DeckOptionsDialog
|
from aqt.deckoptions import DeckOptionsDialog
|
||||||
from aqt.operations.deck import update_deck_configs
|
from aqt.operations.deck import update_deck_configs
|
||||||
|
@ -307,12 +308,29 @@ def update_deck_configs_request() -> bytes:
|
||||||
return b""
|
return b""
|
||||||
|
|
||||||
|
|
||||||
|
def next_card_states() -> bytes:
|
||||||
|
if states := aqt.mw.reviewer.get_next_states():
|
||||||
|
return states.SerializeToString()
|
||||||
|
else:
|
||||||
|
return b""
|
||||||
|
|
||||||
|
|
||||||
|
def set_next_card_states() -> bytes:
|
||||||
|
key = request.headers.get("key", "")
|
||||||
|
input = NextStates()
|
||||||
|
input.ParseFromString(request.data)
|
||||||
|
aqt.mw.reviewer.set_next_states(key, input)
|
||||||
|
return b""
|
||||||
|
|
||||||
|
|
||||||
post_handlers = {
|
post_handlers = {
|
||||||
"graphData": graph_data,
|
"graphData": graph_data,
|
||||||
"graphPreferences": graph_preferences,
|
"graphPreferences": graph_preferences,
|
||||||
"setGraphPreferences": set_graph_preferences,
|
"setGraphPreferences": set_graph_preferences,
|
||||||
"deckConfigsForUpdate": deck_configs_for_update,
|
"deckConfigsForUpdate": deck_configs_for_update,
|
||||||
"updateDeckConfigs": update_deck_configs_request,
|
"updateDeckConfigs": update_deck_configs_request,
|
||||||
|
"nextCardStates": next_card_states,
|
||||||
|
"setNextCardStates": set_next_card_states,
|
||||||
# pylint: disable=unnecessary-lambda
|
# pylint: disable=unnecessary-lambda
|
||||||
"i18nResources": i18n_resources,
|
"i18nResources": i18n_resources,
|
||||||
"congratsInfo": congrats_info,
|
"congratsInfo": congrats_info,
|
||||||
|
|
|
@ -6,6 +6,7 @@ from __future__ import annotations
|
||||||
import difflib
|
import difflib
|
||||||
import html
|
import html
|
||||||
import json
|
import json
|
||||||
|
import random
|
||||||
import re
|
import re
|
||||||
import unicodedata as ucd
|
import unicodedata as ucd
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
@ -124,6 +125,7 @@ class Reviewer:
|
||||||
self.state: Optional[str] = None
|
self.state: Optional[str] = None
|
||||||
self._refresh_needed: Optional[RefreshNeeded] = None
|
self._refresh_needed: Optional[RefreshNeeded] = None
|
||||||
self._v3: Optional[V3CardInfo] = None
|
self._v3: Optional[V3CardInfo] = None
|
||||||
|
self._state_mutation_key = str(random.randint(0, 2 ** 64 - 1))
|
||||||
self.bottom = BottomBar(mw, mw.bottomWeb)
|
self.bottom = BottomBar(mw, mw.bottomWeb)
|
||||||
hooks.card_did_leech.append(self.onLeech)
|
hooks.card_did_leech.append(self.onLeech)
|
||||||
|
|
||||||
|
@ -131,6 +133,7 @@ class Reviewer:
|
||||||
self.mw.setStateShortcuts(self._shortcutKeys()) # type: ignore
|
self.mw.setStateShortcuts(self._shortcutKeys()) # type: ignore
|
||||||
self.web.set_bridge_command(self._linkHandler, self)
|
self.web.set_bridge_command(self._linkHandler, self)
|
||||||
self.bottom.web.set_bridge_command(self._linkHandler, ReviewerBottomBar(self))
|
self.bottom.web.set_bridge_command(self._linkHandler, ReviewerBottomBar(self))
|
||||||
|
self._state_mutation_js = self.mw.col.get_config("cardStateCustomizer")
|
||||||
self._reps: int = None
|
self._reps: int = None
|
||||||
self._refresh_needed = RefreshNeeded.QUEUES
|
self._refresh_needed = RefreshNeeded.QUEUES
|
||||||
self.refresh_if_needed()
|
self.refresh_if_needed()
|
||||||
|
@ -231,6 +234,25 @@ class Reviewer:
|
||||||
self.card = Card(self.mw.col, backend_card=self._v3.top_card().card)
|
self.card = Card(self.mw.col, backend_card=self._v3.top_card().card)
|
||||||
self.card.startTimer()
|
self.card.startTimer()
|
||||||
|
|
||||||
|
def get_next_states(self) -> Optional[NextStates]:
|
||||||
|
if v3 := self._v3:
|
||||||
|
return v3.next_states
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def set_next_states(self, key: str, states: NextStates) -> None:
|
||||||
|
if key != self._state_mutation_key:
|
||||||
|
return
|
||||||
|
|
||||||
|
if v3 := self._v3:
|
||||||
|
v3.next_states = states
|
||||||
|
|
||||||
|
def _run_state_mutation_hook(self) -> None:
|
||||||
|
if self._v3 and (js := self._state_mutation_js):
|
||||||
|
self.web.eval(
|
||||||
|
f"anki.mutateNextCardStates('{self._state_mutation_key}', (states) => {{ {js} }})"
|
||||||
|
)
|
||||||
|
|
||||||
# Audio
|
# Audio
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
@ -268,6 +290,8 @@ class Reviewer:
|
||||||
"js/mathjax.js",
|
"js/mathjax.js",
|
||||||
"js/vendor/mathjax/tex-chtml.js",
|
"js/vendor/mathjax/tex-chtml.js",
|
||||||
"js/reviewer.js",
|
"js/reviewer.js",
|
||||||
|
"js/vendor/protobuf.min.js",
|
||||||
|
"js/reviewer_extras.js",
|
||||||
],
|
],
|
||||||
context=self,
|
context=self,
|
||||||
)
|
)
|
||||||
|
@ -308,6 +332,7 @@ class Reviewer:
|
||||||
# render & update bottom
|
# render & update bottom
|
||||||
q = self._mungeQA(q)
|
q = self._mungeQA(q)
|
||||||
q = gui_hooks.card_will_show(q, c, "reviewQuestion")
|
q = gui_hooks.card_will_show(q, c, "reviewQuestion")
|
||||||
|
self._run_state_mutation_hook()
|
||||||
|
|
||||||
bodyclass = theme_manager.body_classes_for_card_ord(c.ord)
|
bodyclass = theme_manager.body_classes_for_card_ord(c.ord)
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
|
|
||||||
export async function postRequest(
|
export async function postRequest(
|
||||||
path: string,
|
path: string,
|
||||||
body: string | Uint8Array
|
body: string | Uint8Array,
|
||||||
|
headers: Record<string, string> = {}
|
||||||
): Promise<Uint8Array> {
|
): Promise<Uint8Array> {
|
||||||
const headers = {};
|
|
||||||
if (body instanceof Uint8Array) {
|
if (body instanceof Uint8Array) {
|
||||||
headers["Content-type"] = "application/octet-stream";
|
headers["Content-type"] = "application/octet-stream";
|
||||||
}
|
}
|
||||||
|
|
54
ts/reviewer/BUILD.bazel
Normal file
54
ts/reviewer/BUILD.bazel
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
load("@npm//@bazel/typescript:index.bzl", "ts_library")
|
||||||
|
load("//ts:prettier.bzl", "prettier_test")
|
||||||
|
load("//ts:eslint.bzl", "eslint_test")
|
||||||
|
load("//ts/svelte:svelte.bzl", "svelte", "svelte_check")
|
||||||
|
load("//ts:esbuild.bzl", "esbuild")
|
||||||
|
load("//ts:compile_sass.bzl", "compile_sass")
|
||||||
|
|
||||||
|
ts_library(
|
||||||
|
name = "lib",
|
||||||
|
srcs = glob(["*.ts"]),
|
||||||
|
deps = [
|
||||||
|
"//ts/lib",
|
||||||
|
"//ts/lib:backend_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
esbuild(
|
||||||
|
name = "reviewer_extras",
|
||||||
|
srcs = [
|
||||||
|
"//ts:protobuf-shim.js",
|
||||||
|
],
|
||||||
|
args = [
|
||||||
|
"--inject:$(location //ts:protobuf-shim.js)",
|
||||||
|
"--resolve-extensions=.mjs,.js",
|
||||||
|
"--log-level=warning",
|
||||||
|
],
|
||||||
|
entry_point = "index.ts",
|
||||||
|
external = [
|
||||||
|
"protobufjs/light",
|
||||||
|
],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
":lib",
|
||||||
|
"//ts/lib",
|
||||||
|
"//ts/lib:backend_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
################
|
||||||
|
|
||||||
|
prettier_test(
|
||||||
|
name = "format_check",
|
||||||
|
srcs = glob([
|
||||||
|
"*.ts",
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
|
||||||
|
eslint_test(
|
||||||
|
name = "eslint",
|
||||||
|
srcs = glob([
|
||||||
|
"*.ts",
|
||||||
|
]),
|
||||||
|
)
|
28
ts/reviewer/answering.ts
Normal file
28
ts/reviewer/answering.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
import * as pb from "lib/backend_proto";
|
||||||
|
import { postRequest } from "lib/postrequest";
|
||||||
|
|
||||||
|
async function getNextStates(): Promise<pb.BackendProto.NextCardStates> {
|
||||||
|
return pb.BackendProto.NextCardStates.decode(
|
||||||
|
await postRequest("/_anki/nextCardStates", "")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setNextStates(
|
||||||
|
key: string,
|
||||||
|
states: pb.BackendProto.NextCardStates
|
||||||
|
): Promise<void> {
|
||||||
|
const data: Uint8Array = pb.BackendProto.NextCardStates.encode(states).finish();
|
||||||
|
await postRequest("/_anki/setNextCardStates", data, { key });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function mutateNextCardStates(
|
||||||
|
key: string,
|
||||||
|
mutator: (states: pb.BackendProto.NextCardStates) => void
|
||||||
|
): Promise<void> {
|
||||||
|
const states = await getNextStates();
|
||||||
|
mutator(states);
|
||||||
|
await setNextStates(key, states);
|
||||||
|
}
|
9
ts/reviewer/index.ts
Normal file
9
ts/reviewer/index.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
// This is a temporary extra file we load separately from reviewer.ts. Once
|
||||||
|
// reviewer.ts has been migrated into ts/, the code here can be merged into
|
||||||
|
// it.
|
||||||
|
|
||||||
|
import { mutateNextCardStates } from "./answering";
|
||||||
|
globalThis.anki = { ...globalThis.anki, mutateNextCardStates };
|
Loading…
Reference in a new issue