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 {