diff --git a/proto/backend.proto b/proto/backend.proto
index 9170f237c..649fbea93 100644
--- a/proto/backend.proto
+++ b/proto/backend.proto
@@ -71,7 +71,7 @@ message BackendInput {
AddOrUpdateNotetypeIn add_or_update_notetype = 57;
Empty get_all_decks = 58;
Empty check_database = 59;
- Empty all_stock_notetypes = 60;
+ StockNoteType get_stock_notetype_legacy = 60;
int64 get_notetype_legacy = 61;
Empty get_notetype_names = 62;
Empty get_notetype_names_and_counts = 63;
@@ -114,7 +114,6 @@ message BackendOutput {
string studied_today = 32;
string congrats_learn_msg = 33;
Empty abort_media_sync = 46;
- AllStockNotetypesOut all_stock_notetypes = 60;
ClozeNumbersInNoteOut cloze_numbers_in_note = 87;
// fallible commands
@@ -177,6 +176,7 @@ message BackendOutput {
Preferences get_preferences = 84;
Empty set_preferences = 85;
RenderCardOut render_existing_card = 86;
+ bytes get_stock_notetype_legacy = 60;
BackendError error = 2047;
}
@@ -587,10 +587,6 @@ enum StockNoteType {
StockNoteTypeCloze = 4;
}
-message AllStockNotetypesOut {
- repeated NoteType notetypes = 1;
-}
-
message NoteTypeNames {
repeated NoteTypeNameID entries = 1;
}
diff --git a/pylib/anki/rsbackend.py b/pylib/anki/rsbackend.py
index abaaf9ca9..1ef8b2ee3 100644
--- a/pylib/anki/rsbackend.py
+++ b/pylib/anki/rsbackend.py
@@ -50,6 +50,7 @@ BackendNote = pb.Note
TagUsnTuple = pb.TagUsnTuple
NoteType = pb.NoteType
DeckTreeNode = pb.DeckTreeNode
+StockNoteType = pb.StockNoteType
try:
import orjson
@@ -602,12 +603,11 @@ class RustBackend:
).get_all_decks
return orjson.loads(jstr)
- def all_stock_notetypes(self) -> List[NoteType]:
- return list(
- self._run_command(
- pb.BackendInput(all_stock_notetypes=pb.Empty())
- ).all_stock_notetypes.notetypes
- )
+ def get_stock_notetype_legacy(self, kind: StockNoteType) -> Dict[str, Any]:
+ bytes = self._run_command(
+ pb.BackendInput(get_stock_notetype_legacy=kind)
+ ).get_stock_notetype_legacy
+ return orjson.loads(bytes)
def get_notetype_names_and_ids(self) -> List[pb.NoteTypeNameID]:
return list(
diff --git a/pylib/anki/stdmodels.py b/pylib/anki/stdmodels.py
index 0e6985744..4dd3dcb48 100644
--- a/pylib/anki/stdmodels.py
+++ b/pylib/anki/stdmodels.py
@@ -1,125 +1,62 @@
# Copyright: Ankitects Pty Ltd and contributors
# 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.consts import MODEL_CLOZE
-from anki.lang import _
from anki.models import NoteType
+from anki.rsbackend import StockNoteType
-models: List[Tuple[Union[Callable[[], str], str], Callable[[Any], NoteType]]] = []
-
-# Basic
-##########################################################################
+# 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
+models: List[Tuple] = []
-def _newBasicModel(col: _Collection, name: Optional[str] = None) -> NoteType:
- mm = col.models
- m = mm.new(name or _("Basic"))
- 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
\n\n" + "{{" + _("Back") + "}}"
- mm.addTemplate(m, t)
+def add_stock_notetype(col: _Collection, kind: StockNoteType) -> NoteType:
+ m = col.backend.get_stock_notetype_legacy(kind)
+ col.models.add(m)
return m
def addBasicModel(col: _Collection) -> NoteType:
- m = _newBasicModel(col)
- col.models.add(m)
- return m
-
-
-models.append((lambda: _("Basic"), addBasicModel))
-
-# Basic w/ typing
-##########################################################################
+ return add_stock_notetype(col, StockNoteType.StockNoteTypeBasic)
def addBasicTypingModel(col: _Collection) -> NoteType:
- mm = col.models
- m = _newBasicModel(col, _("Basic (type in the answer)"))
- t = m["tmpls"][0]
- t["qfmt"] = "{{" + _("Front") + "}}\n\n{{type:" + _("Back") + "}}"
- t["afmt"] = "{{" + _("Front") + "}}\n\n
\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
\n\n" + "{{" + _("Front") + "}}"
- mm.addTemplate(m, t)
- return m
+ return add_stock_notetype(col, StockNoteType.StockNoteTypeBasicTyping)
def addForwardReverse(col: _Collection) -> NoteType:
- m = _newForwardReverse(col)
- col.models.add(m)
- return m
-
-
-models.append((lambda: _("Basic (and reversed card)"), addForwardReverse))
-
-# Forward & Optional Reverse
-##########################################################################
+ return add_stock_notetype(col, StockNoteType.StockNoteTypeBasicAndReversed)
def addForwardOptionalReverse(col: _Collection) -> NoteType:
- mm = col.models
- 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
-##########################################################################
+ return add_stock_notetype(col, StockNoteType.StockNoteTypeBasicOptionalReversed)
def addClozeModel(col: _Collection) -> NoteType:
- mm = col.models
- 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
+ return add_stock_notetype(col, StockNoteType.StockNoteTypeCloze)
-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
diff --git a/pylib/tests/test_collection.py b/pylib/tests/test_collection.py
index b98ca7b50..e0915db1e 100644
--- a/pylib/tests/test_collection.py
+++ b/pylib/tests/test_collection.py
@@ -7,7 +7,7 @@ from anki import Collection as aopen
from anki.dbproxy import emulate_named_args
from anki.lang import without_unicode_isolation
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 tests.shared import assertException, getEmptyCol
@@ -119,10 +119,10 @@ def test_addDelTags():
def test_timestamps():
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):
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():
diff --git a/qt/aqt/models.py b/qt/aqt/models.py
index f901da8f0..4a903d816 100644
--- a/qt/aqt/models.py
+++ b/qt/aqt/models.py
@@ -1,7 +1,6 @@
# Copyright: Ankitects Pty Ltd and contributors
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-import collections
from operator import itemgetter
from typing import List, Optional
@@ -196,9 +195,7 @@ class AddModel(QDialog):
self.dialog.setupUi(self)
# standard models
self.models = []
- for (name, func) in stdmodels.models:
- if isinstance(name, collections.Callable): # type: ignore
- name = name() # type: ignore
+ for (name, func) in stdmodels.get_stock_notetypes(self.col):
item = QListWidgetItem(_("Add: %s") % name)
self.dialog.models.addItem(item)
self.models.append((True, func))
diff --git a/rslib/src/backend/mod.rs b/rslib/src/backend/mod.rs
index 8103148ae..e53e8cd31 100644
--- a/rslib/src/backend/mod.rs
+++ b/rslib/src/backend/mod.rs
@@ -308,12 +308,9 @@ impl Backend {
OValue::GetChangedNotetypes(self.get_changed_notetypes()?)
}
Value::GetAllDecks(_) => OValue::GetAllDecks(self.get_all_decks()?),
- Value::AllStockNotetypes(_) => OValue::AllStockNotetypes(pb::AllStockNotetypesOut {
- notetypes: all_stock_notetypes(&self.i18n)
- .into_iter()
- .map(Into::into)
- .collect(),
- }),
+ Value::GetStockNotetypeLegacy(kind) => {
+ OValue::GetStockNotetypeLegacy(self.get_stock_notetype_legacy(kind)?)
+ }
Value::GetNotetypeLegacy(id) => {
OValue::GetNotetypeLegacy(self.get_notetype_legacy(id)?)
}
@@ -1151,6 +1148,15 @@ impl Backend {
numbers: set.into_iter().map(|n| n as u32).collect(),
}
}
+
+ fn get_stock_notetype_legacy(&self, kind: i32) -> Result> {
+ // 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) -> Vec {