fetch stock notetypes from backend

This commit is contained in:
Damien Elmes 2020-05-15 16:05:58 +10:00
parent f650e5557f
commit 2ac33500eb
6 changed files with 59 additions and 123 deletions

View file

@ -71,7 +71,7 @@ message BackendInput {
AddOrUpdateNotetypeIn add_or_update_notetype = 57; AddOrUpdateNotetypeIn add_or_update_notetype = 57;
Empty get_all_decks = 58; Empty get_all_decks = 58;
Empty check_database = 59; Empty check_database = 59;
Empty all_stock_notetypes = 60; StockNoteType get_stock_notetype_legacy = 60;
int64 get_notetype_legacy = 61; int64 get_notetype_legacy = 61;
Empty get_notetype_names = 62; Empty get_notetype_names = 62;
Empty get_notetype_names_and_counts = 63; Empty get_notetype_names_and_counts = 63;
@ -114,7 +114,6 @@ message BackendOutput {
string studied_today = 32; string studied_today = 32;
string congrats_learn_msg = 33; string congrats_learn_msg = 33;
Empty abort_media_sync = 46; Empty abort_media_sync = 46;
AllStockNotetypesOut all_stock_notetypes = 60;
ClozeNumbersInNoteOut cloze_numbers_in_note = 87; ClozeNumbersInNoteOut cloze_numbers_in_note = 87;
// fallible commands // fallible commands
@ -177,6 +176,7 @@ message BackendOutput {
Preferences get_preferences = 84; Preferences get_preferences = 84;
Empty set_preferences = 85; Empty set_preferences = 85;
RenderCardOut render_existing_card = 86; RenderCardOut render_existing_card = 86;
bytes get_stock_notetype_legacy = 60;
BackendError error = 2047; BackendError error = 2047;
} }
@ -587,10 +587,6 @@ enum StockNoteType {
StockNoteTypeCloze = 4; StockNoteTypeCloze = 4;
} }
message AllStockNotetypesOut {
repeated NoteType notetypes = 1;
}
message NoteTypeNames { message NoteTypeNames {
repeated NoteTypeNameID entries = 1; repeated NoteTypeNameID entries = 1;
} }

View file

@ -50,6 +50,7 @@ BackendNote = pb.Note
TagUsnTuple = pb.TagUsnTuple TagUsnTuple = pb.TagUsnTuple
NoteType = pb.NoteType NoteType = pb.NoteType
DeckTreeNode = pb.DeckTreeNode DeckTreeNode = pb.DeckTreeNode
StockNoteType = pb.StockNoteType
try: try:
import orjson import orjson
@ -602,12 +603,11 @@ class RustBackend:
).get_all_decks ).get_all_decks
return orjson.loads(jstr) return orjson.loads(jstr)
def all_stock_notetypes(self) -> List[NoteType]: def get_stock_notetype_legacy(self, kind: StockNoteType) -> Dict[str, Any]:
return list( bytes = self._run_command(
self._run_command( pb.BackendInput(get_stock_notetype_legacy=kind)
pb.BackendInput(all_stock_notetypes=pb.Empty()) ).get_stock_notetype_legacy
).all_stock_notetypes.notetypes return orjson.loads(bytes)
)
def get_notetype_names_and_ids(self) -> List[pb.NoteTypeNameID]: def get_notetype_names_and_ids(self) -> List[pb.NoteTypeNameID]:
return list( return list(

View file

@ -1,125 +1,62 @@
# Copyright: Ankitects Pty Ltd and contributors # Copyright: Ankitects Pty Ltd and contributors
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
from typing import Any, Callable, List, Optional, Tuple, Union from typing import Callable, List, Tuple
from anki.collection import _Collection from anki.collection import _Collection
from anki.consts import MODEL_CLOZE
from anki.lang import _
from anki.models import NoteType from anki.models import NoteType
from anki.rsbackend import StockNoteType
models: List[Tuple[Union[Callable[[], str], str], Callable[[Any], NoteType]]] = [] # add-on authors can add ("note type name", function_like_addBasicModel)
# to this list to have it shown in the add/clone note type screen
# Basic models: List[Tuple] = []
##########################################################################
def _newBasicModel(col: _Collection, name: Optional[str] = None) -> NoteType: def add_stock_notetype(col: _Collection, kind: StockNoteType) -> NoteType:
mm = col.models m = col.backend.get_stock_notetype_legacy(kind)
m = mm.new(name or _("Basic")) col.models.add(m)
fm = mm.newField(_("Front"))
mm.addField(m, fm)
fm = mm.newField(_("Back"))
mm.addField(m, fm)
t = mm.newTemplate(_("Card 1"))
t["qfmt"] = "{{" + _("Front") + "}}"
t["afmt"] = "{{FrontSide}}\n\n<hr id=answer>\n\n" + "{{" + _("Back") + "}}"
mm.addTemplate(m, t)
return m return m
def addBasicModel(col: _Collection) -> NoteType: def addBasicModel(col: _Collection) -> NoteType:
m = _newBasicModel(col) return add_stock_notetype(col, StockNoteType.StockNoteTypeBasic)
col.models.add(m)
return m
models.append((lambda: _("Basic"), addBasicModel))
# Basic w/ typing
##########################################################################
def addBasicTypingModel(col: _Collection) -> NoteType: def addBasicTypingModel(col: _Collection) -> NoteType:
mm = col.models return add_stock_notetype(col, StockNoteType.StockNoteTypeBasicTyping)
m = _newBasicModel(col, _("Basic (type in the answer)"))
t = m["tmpls"][0]
t["qfmt"] = "{{" + _("Front") + "}}\n\n{{type:" + _("Back") + "}}"
t["afmt"] = "{{" + _("Front") + "}}\n\n<hr id=answer>\n\n{{type:" + _("Back") + "}}"
mm.add(m)
return m
models.append((lambda: _("Basic (type in the answer)"), addBasicTypingModel))
# Forward & Reverse
##########################################################################
def _newForwardReverse(col: _Collection, name: Optional[str] = None) -> NoteType:
mm = col.models
m = _newBasicModel(col, name or _("Basic (and reversed card)"))
t = mm.newTemplate(_("Card 2"))
t["qfmt"] = "{{" + _("Back") + "}}"
t["afmt"] = "{{FrontSide}}\n\n<hr id=answer>\n\n" + "{{" + _("Front") + "}}"
mm.addTemplate(m, t)
return m
def addForwardReverse(col: _Collection) -> NoteType: def addForwardReverse(col: _Collection) -> NoteType:
m = _newForwardReverse(col) return add_stock_notetype(col, StockNoteType.StockNoteTypeBasicAndReversed)
col.models.add(m)
return m
models.append((lambda: _("Basic (and reversed card)"), addForwardReverse))
# Forward & Optional Reverse
##########################################################################
def addForwardOptionalReverse(col: _Collection) -> NoteType: def addForwardOptionalReverse(col: _Collection) -> NoteType:
mm = col.models return add_stock_notetype(col, StockNoteType.StockNoteTypeBasicOptionalReversed)
m = _newForwardReverse(col, _("Basic (optional reversed card)"))
av = _("Add Reverse")
fm = mm.newField(av)
mm.addField(m, fm)
t = m["tmpls"][1]
t["qfmt"] = "{{#%s}}%s{{/%s}}" % (av, t["qfmt"], av)
mm.add(m)
return m
models.append((lambda: _("Basic (optional reversed card)"), addForwardOptionalReverse))
# Cloze
##########################################################################
def addClozeModel(col: _Collection) -> NoteType: def addClozeModel(col: _Collection) -> NoteType:
mm = col.models return add_stock_notetype(col, StockNoteType.StockNoteTypeCloze)
m = mm.new(_("Cloze"))
m["type"] = MODEL_CLOZE
txt = _("Text")
fm = mm.newField(txt)
mm.addField(m, fm)
t = mm.newTemplate(_("Cloze"))
fmt = "{{cloze:%s}}" % txt
m[
"css"
] += """
.cloze {
font-weight: bold;
color: blue;
}
.nightMode .cloze {
color: lightblue;
}"""
t["qfmt"] = fmt
t["afmt"] = fmt
mm.addTemplate(m, t)
mm.add(m)
return m
models.append((lambda: _("Cloze"), addClozeModel)) def get_stock_notetypes(
col: _Collection,
) -> List[Tuple[str, Callable[[_Collection], NoteType]]]:
out: List[Tuple[str, Callable[[_Collection], NoteType]]] = []
# add standard
for (kind, func) in [
(StockNoteType.StockNoteTypeBasic, addBasicModel),
(StockNoteType.StockNoteTypeBasicTyping, addBasicTypingModel),
(StockNoteType.StockNoteTypeBasicAndReversed, addForwardReverse),
(StockNoteType.StockNoteTypeBasicOptionalReversed, addForwardOptionalReverse),
(StockNoteType.StockNoteTypeCloze, addClozeModel),
]:
m = col.backend.get_stock_notetype_legacy(kind)
out.append((m["name"], func))
# add extras from add-ons
for (name_or_func, func) in models:
if not isinstance(name_or_func, str):
name = name_or_func()
else:
name = name_or_func
out.append((name, func))
return out

View file

@ -7,7 +7,7 @@ from anki import Collection as aopen
from anki.dbproxy import emulate_named_args from anki.dbproxy import emulate_named_args
from anki.lang import without_unicode_isolation from anki.lang import without_unicode_isolation
from anki.rsbackend import TR from anki.rsbackend import TR
from anki.stdmodels import addBasicModel, models from anki.stdmodels import addBasicModel, get_stock_notetypes
from anki.utils import isWin from anki.utils import isWin
from tests.shared import assertException, getEmptyCol from tests.shared import assertException, getEmptyCol
@ -119,10 +119,10 @@ def test_addDelTags():
def test_timestamps(): def test_timestamps():
deck = getEmptyCol() deck = getEmptyCol()
assert len(deck.models.all_names_and_ids()) == len(models) assert len(deck.models.all_names_and_ids()) == len(get_stock_notetypes(deck))
for i in range(100): for i in range(100):
addBasicModel(deck) addBasicModel(deck)
assert len(deck.models.all_names_and_ids()) == 100 + len(models) assert len(deck.models.all_names_and_ids()) == 100 + len(get_stock_notetypes(deck))
def test_furigana(): def test_furigana():

View file

@ -1,7 +1,6 @@
# Copyright: Ankitects Pty Ltd and contributors # Copyright: Ankitects Pty Ltd and contributors
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import collections
from operator import itemgetter from operator import itemgetter
from typing import List, Optional from typing import List, Optional
@ -196,9 +195,7 @@ class AddModel(QDialog):
self.dialog.setupUi(self) self.dialog.setupUi(self)
# standard models # standard models
self.models = [] self.models = []
for (name, func) in stdmodels.models: for (name, func) in stdmodels.get_stock_notetypes(self.col):
if isinstance(name, collections.Callable): # type: ignore
name = name() # type: ignore
item = QListWidgetItem(_("Add: %s") % name) item = QListWidgetItem(_("Add: %s") % name)
self.dialog.models.addItem(item) self.dialog.models.addItem(item)
self.models.append((True, func)) self.models.append((True, func))

View file

@ -308,12 +308,9 @@ impl Backend {
OValue::GetChangedNotetypes(self.get_changed_notetypes()?) OValue::GetChangedNotetypes(self.get_changed_notetypes()?)
} }
Value::GetAllDecks(_) => OValue::GetAllDecks(self.get_all_decks()?), Value::GetAllDecks(_) => OValue::GetAllDecks(self.get_all_decks()?),
Value::AllStockNotetypes(_) => OValue::AllStockNotetypes(pb::AllStockNotetypesOut { Value::GetStockNotetypeLegacy(kind) => {
notetypes: all_stock_notetypes(&self.i18n) OValue::GetStockNotetypeLegacy(self.get_stock_notetype_legacy(kind)?)
.into_iter() }
.map(Into::into)
.collect(),
}),
Value::GetNotetypeLegacy(id) => { Value::GetNotetypeLegacy(id) => {
OValue::GetNotetypeLegacy(self.get_notetype_legacy(id)?) OValue::GetNotetypeLegacy(self.get_notetype_legacy(id)?)
} }
@ -1151,6 +1148,15 @@ impl Backend {
numbers: set.into_iter().map(|n| n as u32).collect(), numbers: set.into_iter().map(|n| n as u32).collect(),
} }
} }
fn get_stock_notetype_legacy(&self, kind: i32) -> Result<Vec<u8>> {
// fixme: use individual functions instead of full vec
let mut all = all_stock_notetypes(&self.i18n);
let idx = (kind as usize).min(all.len() - 1);
let nt = all.swap_remove(idx);
let schema11: NoteTypeSchema11 = nt.into();
serde_json::to_vec(&schema11).map_err(Into::into)
}
} }
fn to_nids(ids: Vec<i64>) -> Vec<NoteID> { fn to_nids(ids: Vec<i64>) -> Vec<NoteID> {