PEP8 models.py

This commit is contained in:
Damien Elmes 2021-06-27 13:49:58 +10:00
parent 62c23c6816
commit 17533e6a78
25 changed files with 318 additions and 280 deletions

View file

@ -45,4 +45,7 @@ disable=
arguments-differ, arguments-differ,
[BASIC] [BASIC]
good-names = id good-names =
id,
tr,
db,

View file

@ -28,7 +28,7 @@ def partial_path(full_path: str, components: int) -> str:
def print_deprecation_warning(msg: str, frame: int = 2) -> None: def print_deprecation_warning(msg: str, frame: int = 2) -> None:
path, linenum, fn, y = traceback.extract_stack(limit=5)[frame] path, linenum, _, _ = traceback.extract_stack(limit=5)[frame]
path = partial_path(path, components=3) path = partial_path(path, components=3)
print(f"{path}:{linenum}:{msg}") print(f"{path}:{linenum}:{msg}")

View file

@ -1,6 +1,8 @@
# 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
# pylint: disable=invalid-name
import json import json
import os import os
import re import re

View file

@ -1,6 +1,8 @@
# 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
# pylint: disable=invalid-name
import os import os
import unicodedata import unicodedata
from typing import Any, Dict, List, Optional, Tuple from typing import Any, Dict, List, Optional, Tuple

View file

@ -1,6 +1,8 @@
# 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
# pylint: disable=invalid-name
import json import json
import os import os
import unicodedata import unicodedata

View file

@ -1,6 +1,8 @@
# 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
# pylint: disable=invalid-name
import re import re
import time import time
from typing import cast from typing import cast
@ -134,7 +136,7 @@ acq_reps+ret_reps, lapses, card_type_id from cards"""
model["name"] = "Mnemosyne-FrontOnly" model["name"] = "Mnemosyne-FrontOnly"
mm = self.col.models mm = self.col.models
mm.save(model) mm.save(model)
mm.setCurrent(model) mm.set_current(model)
self.model = model self.model = model
self._fields = len(model["flds"]) self._fields = len(model["flds"])
self.initMapping() self.initMapping()
@ -145,34 +147,34 @@ acq_reps+ret_reps, lapses, card_type_id from cards"""
m = addBasicModel(self.col) m = addBasicModel(self.col)
m["name"] = "Mnemosyne-FrontBack" m["name"] = "Mnemosyne-FrontBack"
mm = self.col.models mm = self.col.models
t = mm.newTemplate("Back") t = mm.new_template("Back")
t["qfmt"] = "{{Back}}" t["qfmt"] = "{{Back}}"
t["afmt"] = t["qfmt"] + "\n\n<hr id=answer>\n\n{{Front}}" # type: ignore t["afmt"] = t["qfmt"] + "\n\n<hr id=answer>\n\n{{Front}}" # type: ignore
mm.addTemplate(m, t) mm.add_template(m, t)
self._addFronts(notes, m) self._addFronts(notes, m)
def _addVocabulary(self, notes): def _addVocabulary(self, notes):
mm = self.col.models mm = self.col.models
m = mm.new("Mnemosyne-Vocabulary") m = mm.new("Mnemosyne-Vocabulary")
for f in "Expression", "Pronunciation", "Meaning", "Notes": for f in "Expression", "Pronunciation", "Meaning", "Notes":
fm = mm.newField(f) fm = mm.new_field(f)
mm.addField(m, fm) mm.addField(m, fm)
t = mm.newTemplate("Recognition") t = mm.new_template("Recognition")
t["qfmt"] = "{{Expression}}" t["qfmt"] = "{{Expression}}"
t["afmt"] = ( t["afmt"] = (
cast(str, t["qfmt"]) cast(str, t["qfmt"])
+ """\n\n<hr id=answer>\n\n\ + """\n\n<hr id=answer>\n\n\
{{Pronunciation}}<br>\n{{Meaning}}<br>\n{{Notes}}""" {{Pronunciation}}<br>\n{{Meaning}}<br>\n{{Notes}}"""
) )
mm.addTemplate(m, t) mm.add_template(m, t)
t = mm.newTemplate("Production") t = mm.new_template("Production")
t["qfmt"] = "{{Meaning}}" t["qfmt"] = "{{Meaning}}"
t["afmt"] = ( t["afmt"] = (
cast(str, t["qfmt"]) cast(str, t["qfmt"])
+ """\n\n<hr id=answer>\n\n\ + """\n\n<hr id=answer>\n\n\
{{Expression}}<br>\n{{Pronunciation}}<br>\n{{Notes}}""" {{Expression}}<br>\n{{Pronunciation}}<br>\n{{Notes}}"""
) )
mm.addTemplate(m, t) mm.add_template(m, t)
mm.add(m) mm.add(m)
self._addFronts(notes, m, fields=("f", "p_1", "m_1", "n")) self._addFronts(notes, m, fields=("f", "p_1", "m_1", "n"))
@ -206,7 +208,7 @@ acq_reps+ret_reps, lapses, card_type_id from cards"""
model["name"] = "Mnemosyne-Cloze" model["name"] = "Mnemosyne-Cloze"
mm = self.col.models mm = self.col.models
mm.save(model) mm.save(model)
mm.setCurrent(model) mm.set_current(model)
self.model = model self.model = model
self._fields = len(model["flds"]) self._fields = len(model["flds"])
self.initMapping() self.initMapping()

View file

@ -1,6 +1,8 @@
# 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
# pylint: disable=invalid-name
import html import html
import unicodedata import unicodedata
from typing import Dict, List, Optional, Tuple, Union from typing import Dict, List, Optional, Tuple, Union
@ -130,7 +132,7 @@ class NoteImporter(Importer):
csums[csum] = [id] csums[csum] = [id]
firsts: Dict[str, bool] = {} firsts: Dict[str, bool] = {}
fld0idx = self.mapping.index(self.model["flds"][0]["name"]) fld0idx = self.mapping.index(self.model["flds"][0]["name"])
self._fmap = self.col.models.fieldMap(self.model) self._fmap = self.col.models.field_map(self.model)
self._nextID = NoteId(timestampID(self.col.db, "notes")) self._nextID = NoteId(timestampID(self.col.db, "notes"))
# loop through the notes # loop through the notes
updates: List[Updates] = [] updates: List[Updates] = []

View file

@ -24,7 +24,7 @@ class PaukerImporter(NoteImporter):
model = addForwardReverse(self.col) model = addForwardReverse(self.col)
model["name"] = "Pauker" model["name"] = "Pauker"
self.col.models.save(model, updateReqs=False) self.col.models.save(model, updateReqs=False)
self.col.models.setCurrent(model) self.col.models.set_current(model)
self.model = model self.model = model
self.initMapping() self.initMapping()
NoteImporter.run(self) NoteImporter.run(self)

View file

@ -1,17 +1,19 @@
# 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
# pylint: enable=invalid-name
from __future__ import annotations from __future__ import annotations
import copy import copy
import pprint import pprint
import sys import sys
import time import time
import traceback
from typing import Any, Dict, List, NewType, Optional, Sequence, Tuple, Union from typing import Any, Dict, List, NewType, Optional, Sequence, Tuple, Union
import anki # pylint: disable=unused-import import anki # pylint: disable=unused-import
import anki._backend.backend_pb2 as _pb import anki._backend.backend_pb2 as _pb
from anki._legacy import DeprecatedNamesMixin, deprecated, print_deprecation_warning
from anki.collection import OpChanges, OpChangesWithId from anki.collection import OpChanges, OpChangesWithId
from anki.consts import * from anki.consts import *
from anki.errors import NotFoundError from anki.errors import NotFoundError
@ -40,8 +42,9 @@ class ModelsDictProxy:
self._col = col.weakref() self._col = col.weakref()
def _warn(self) -> None: def _warn(self) -> None:
traceback.print_stack(file=sys.stdout) print_deprecation_warning(
print("add-on should use methods on col.models, not col.models.models dict") "add-on should use methods on col.decks, not col.decks.decks dict"
)
def __getitem__(self, item: Any) -> Any: def __getitem__(self, item: Any) -> Any:
self._warn() self._warn()
@ -72,7 +75,7 @@ class ModelsDictProxy:
return self._col.models.have(item) return self._col.models.have(item)
class ModelManager: class ModelManager(DeprecatedNamesMixin):
# Saving/loading registry # Saving/loading registry
############################################################# #############################################################
@ -83,27 +86,9 @@ class ModelManager:
self._cache = {} self._cache = {}
def __repr__(self) -> str: def __repr__(self) -> str:
d = dict(self.__dict__) attrs = dict(self.__dict__)
del d["col"] del attrs["col"]
return f"{super().__repr__()} {pprint.pformat(d, width=300)}" return f"{super().__repr__()} {pprint.pformat(attrs, width=300)}"
def save(
self,
m: NotetypeDict = None,
# no longer used
templates: bool = False,
updateReqs: bool = True,
) -> None:
"Save changes made to provided note type."
if not m:
print("col.models.save() should be passed the changed notetype")
return
self.update(m, preserve_usn=False)
# legacy
def flush(self) -> None:
pass
# Caching # Caching
############################################################# #############################################################
@ -114,8 +99,8 @@ class ModelManager:
_cache: Dict[NotetypeId, NotetypeDict] = {} _cache: Dict[NotetypeId, NotetypeDict] = {}
def _update_cache(self, nt: NotetypeDict) -> None: def _update_cache(self, notetype: NotetypeDict) -> None:
self._cache[nt["id"]] = nt self._cache[notetype["id"]] = notetype
def _remove_from_cache(self, ntid: NotetypeId) -> None: def _remove_from_cache(self, ntid: NotetypeId) -> None:
if ntid in self._cache: if ntid in self._cache:
@ -136,14 +121,6 @@ class ModelManager:
def all_use_counts(self) -> Sequence[NotetypeNameIdUseCount]: def all_use_counts(self) -> Sequence[NotetypeNameIdUseCount]:
return self.col._backend.get_notetype_names_and_counts() return self.col._backend.get_notetype_names_and_counts()
# legacy
def allNames(self) -> List[str]:
return [n.name for n in self.all_names_and_ids()]
def ids(self) -> List[NotetypeId]:
return [NotetypeId(n.id) for n in self.all_names_and_ids()]
# only used by importing code # only used by importing code
def have(self, id: NotetypeId) -> bool: def have(self, id: NotetypeId) -> bool:
if isinstance(id, str): if isinstance(id, str):
@ -153,19 +130,15 @@ class ModelManager:
# Current note type # Current note type
############################################################# #############################################################
def current(self, forDeck: bool = True) -> NotetypeDict: def current(self, for_deck: bool = True) -> NotetypeDict:
"Get current model." "Get current model. In new code, prefer col.defaults_for_adding()"
m = self.get(self.col.decks.current().get("mid")) notetype = self.get(self.col.decks.current().get("mid"))
if not forDeck or not m: if not for_deck or not notetype:
m = self.get(self.col.conf["curModel"]) notetype = self.get(self.col.conf["curModel"])
if m: if notetype:
return m return notetype
return self.get(NotetypeId(self.all_names_and_ids()[0].id)) return self.get(NotetypeId(self.all_names_and_ids()[0].id))
def setCurrent(self, m: NotetypeDict) -> None:
"""Legacy. The current notetype is now updated on note add."""
self.col.set_config("curModel", m["id"])
# Retrieving and creating models # Retrieving and creating models
############################################################# #############################################################
@ -183,20 +156,20 @@ class ModelManager:
elif isinstance(id, str): elif isinstance(id, str):
id = int(id) id = int(id)
nt = self._get_cached(id) notetype = self._get_cached(id)
if not nt: if not notetype:
try: try:
nt = from_json_bytes(self.col._backend.get_notetype_legacy(id)) notetype = from_json_bytes(self.col._backend.get_notetype_legacy(id))
self._update_cache(nt) self._update_cache(notetype)
except NotFoundError: except NotFoundError:
return None return None
return nt return notetype
def all(self) -> List[NotetypeDict]: def all(self) -> List[NotetypeDict]:
"Get all models." "Get all models."
return [self.get(NotetypeId(nt.id)) for nt in self.all_names_and_ids()] return [self.get(NotetypeId(nt.id)) for nt in self.all_names_and_ids()]
def byName(self, name: str) -> Optional[NotetypeDict]: def by_name(self, name: str) -> Optional[NotetypeDict]:
"Get model with NAME." "Get model with NAME."
id = self.id_for_name(name) id = self.id_for_name(name)
if id: if id:
@ -207,67 +180,53 @@ class ModelManager:
def new(self, name: str) -> NotetypeDict: def new(self, name: str) -> NotetypeDict:
"Create a new model, and return it." "Create a new model, and return it."
# caller should call save() after modifying # caller should call save() after modifying
nt = from_json_bytes( notetype = from_json_bytes(
self.col._backend.get_stock_notetype_legacy(StockNotetypeKind.BASIC) self.col._backend.get_stock_notetype_legacy(StockNotetypeKind.BASIC)
) )
nt["flds"] = [] notetype["flds"] = []
nt["tmpls"] = [] notetype["tmpls"] = []
nt["name"] = name notetype["name"] = name
return nt return notetype
def rem(self, m: NotetypeDict) -> None:
"Delete model, and all its cards/notes."
self.remove(m["id"])
def remove_all_notetypes(self) -> None: def remove_all_notetypes(self) -> None:
for nt in self.all_names_and_ids(): for notetype in self.all_names_and_ids():
self._remove_from_cache(NotetypeId(nt.id)) self._remove_from_cache(NotetypeId(notetype.id))
self.col._backend.remove_notetype(nt.id) self.col._backend.remove_notetype(notetype.id)
def remove(self, id: NotetypeId) -> OpChanges: def remove(self, id: NotetypeId) -> OpChanges:
"Modifies schema." "Modifies schema."
self._remove_from_cache(id) self._remove_from_cache(id)
return self.col._backend.remove_notetype(id) return self.col._backend.remove_notetype(id)
def add(self, m: NotetypeDict) -> OpChangesWithId: def add(self, notetype: NotetypeDict) -> OpChangesWithId:
"Replaced with add_dict()" "Replaced with add_dict()"
self.ensureNameUnique(m) self.ensure_name_unique(notetype)
out = self.col._backend.add_notetype_legacy(to_json_bytes(m)) out = self.col._backend.add_notetype_legacy(to_json_bytes(notetype))
m["id"] = out.id notetype["id"] = out.id
self._mutate_after_write(m) self._mutate_after_write(notetype)
return out return out
def add_dict(self, m: NotetypeDict) -> OpChangesWithId: def add_dict(self, notetype: NotetypeDict) -> OpChangesWithId:
"Notetype needs to be fetched from DB after adding." "Notetype needs to be fetched from DB after adding."
self.ensureNameUnique(m) self.ensure_name_unique(notetype)
return self.col._backend.add_notetype_legacy(to_json_bytes(m)) return self.col._backend.add_notetype_legacy(to_json_bytes(notetype))
def ensureNameUnique(self, m: NotetypeDict) -> None: def ensure_name_unique(self, notetype: NotetypeDict) -> None:
existing_id = self.id_for_name(m["name"]) existing_id = self.id_for_name(notetype["name"])
if existing_id is not None and existing_id != m["id"]: if existing_id is not None and existing_id != notetype["id"]:
m["name"] += "-" + checksum(str(time.time()))[:5] notetype["name"] += "-" + checksum(str(time.time()))[:5]
def update(self, m: NotetypeDict, preserve_usn: bool = True) -> None: def update_dict(self, notetype: NotetypeDict) -> OpChanges:
"Add or update an existing model. Use .update_dict() instead."
self._remove_from_cache(m["id"])
self.ensureNameUnique(m)
m["id"] = self.col._backend.add_or_update_notetype(
json=to_json_bytes(m), preserve_usn_and_mtime=preserve_usn
)
self.setCurrent(m)
self._mutate_after_write(m)
def update_dict(self, m: NotetypeDict) -> OpChanges:
"Update a NotetypeDict. Caller will need to re-load notetype if new fields/cards added." "Update a NotetypeDict. Caller will need to re-load notetype if new fields/cards added."
self._remove_from_cache(m["id"]) self._remove_from_cache(notetype["id"])
self.ensureNameUnique(m) self.ensure_name_unique(notetype)
return self.col._backend.update_notetype_legacy(to_json_bytes(m)) return self.col._backend.update_notetype_legacy(to_json_bytes(notetype))
def _mutate_after_write(self, nt: NotetypeDict) -> None: def _mutate_after_write(self, notetype: NotetypeDict) -> None:
# existing code expects the note type to be mutated to reflect # existing code expects the note type to be mutated to reflect
# the changes made when adding, such as ordinal assignment :-( # the changes made when adding, such as ordinal assignment :-(
updated = self.get(nt["id"]) updated = self.get(notetype["id"])
nt.update(updated) notetype.update(updated)
# Tools # Tools
################################################## ##################################################
@ -279,147 +238,115 @@ class ModelManager:
ntid = ntid["id"] ntid = ntid["id"]
return self.col.db.list("select id from notes where mid = ?", ntid) return self.col.db.list("select id from notes where mid = ?", ntid)
def useCount(self, m: NotetypeDict) -> int: def use_count(self, notetype: NotetypeDict) -> int:
"Number of note using M." "Number of note using M."
return self.col.db.scalar("select count() from notes where mid = ?", m["id"]) return self.col.db.scalar(
"select count() from notes where mid = ?", notetype["id"]
)
# Copying # Copying
################################################## ##################################################
def copy(self, m: NotetypeDict, add: bool = True) -> NotetypeDict: def copy(self, notetype: NotetypeDict, add: bool = True) -> NotetypeDict:
"Copy, save and return." "Copy, save and return."
m2 = copy.deepcopy(m) cloned = copy.deepcopy(notetype)
m2["name"] = without_unicode_isolation( cloned["name"] = without_unicode_isolation(
self.col.tr.notetypes_copy(val=m2["name"]) self.col.tr.notetypes_copy(val=cloned["name"])
) )
m2["id"] = 0 cloned["id"] = 0
if add: if add:
self.add(m2) self.add(cloned)
return m2 return cloned
# Fields # Fields
################################################## ##################################################
def fieldMap(self, m: NotetypeDict) -> Dict[str, Tuple[int, FieldDict]]: def field_map(self, notetype: NotetypeDict) -> Dict[str, Tuple[int, FieldDict]]:
"Mapping of field name -> (ord, field)." "Mapping of field name -> (ord, field)."
return {f["name"]: (f["ord"], f) for f in m["flds"]} return {f["name"]: (f["ord"], f) for f in notetype["flds"]}
def fieldNames(self, m: NotetypeDict) -> List[str]: def field_names(self, notetype: NotetypeDict) -> List[str]:
return [f["name"] for f in m["flds"]] return [f["name"] for f in notetype["flds"]]
def sortIdx(self, m: NotetypeDict) -> int: def sort_idx(self, notetype: NotetypeDict) -> int:
return m["sortf"] return notetype["sortf"]
# Adding & changing fields # Adding & changing fields
################################################## ##################################################
def new_field(self, name: str) -> FieldDict: def new_field(self, name: str) -> FieldDict:
assert isinstance(name, str) assert isinstance(name, str)
nt = from_json_bytes( notetype = from_json_bytes(
self.col._backend.get_stock_notetype_legacy(StockNotetypeKind.BASIC) self.col._backend.get_stock_notetype_legacy(StockNotetypeKind.BASIC)
) )
field = nt["flds"][0] field = notetype["flds"][0]
field["name"] = name field["name"] = name
field["ord"] = None field["ord"] = None
return field return field
def add_field(self, m: NotetypeDict, field: FieldDict) -> None: def add_field(self, notetype: NotetypeDict, field: FieldDict) -> None:
"Modifies schema." "Modifies schema."
m["flds"].append(field) notetype["flds"].append(field)
def remove_field(self, m: NotetypeDict, field: FieldDict) -> None: def remove_field(self, notetype: NotetypeDict, field: FieldDict) -> None:
"Modifies schema." "Modifies schema."
m["flds"].remove(field) notetype["flds"].remove(field)
def reposition_field(self, m: NotetypeDict, field: FieldDict, idx: int) -> None: def reposition_field(
self, notetype: NotetypeDict, field: FieldDict, idx: int
) -> None:
"Modifies schema." "Modifies schema."
oldidx = m["flds"].index(field) oldidx = notetype["flds"].index(field)
if oldidx == idx: if oldidx == idx:
return return
m["flds"].remove(field) notetype["flds"].remove(field)
m["flds"].insert(idx, field) notetype["flds"].insert(idx, field)
def rename_field(self, m: NotetypeDict, field: FieldDict, new_name: str) -> None: def rename_field(
assert field in m["flds"] self, notetype: NotetypeDict, field: FieldDict, new_name: str
) -> None:
assert field in notetype["flds"]
field["name"] = new_name field["name"] = new_name
def set_sort_index(self, nt: NotetypeDict, idx: int) -> None: def set_sort_index(self, notetype: NotetypeDict, idx: int) -> None:
"Modifies schema." "Modifies schema."
assert 0 <= idx < len(nt["flds"]) assert 0 <= idx < len(notetype["flds"])
nt["sortf"] = idx notetype["sortf"] = idx
# legacy
newField = new_field
def addField(self, m: NotetypeDict, field: FieldDict) -> None:
self.add_field(m, field)
if m["id"]:
self.save(m)
def remField(self, m: NotetypeDict, field: FieldDict) -> None:
self.remove_field(m, field)
self.save(m)
def moveField(self, m: NotetypeDict, field: FieldDict, idx: int) -> None:
self.reposition_field(m, field, idx)
self.save(m)
def renameField(self, m: NotetypeDict, field: FieldDict, newName: str) -> None:
self.rename_field(m, field, newName)
self.save(m)
# Adding & changing templates # Adding & changing templates
################################################## ##################################################
def new_template(self, name: str) -> TemplateDict: def new_template(self, name: str) -> TemplateDict:
nt = from_json_bytes( notetype = from_json_bytes(
self.col._backend.get_stock_notetype_legacy(StockNotetypeKind.BASIC) self.col._backend.get_stock_notetype_legacy(StockNotetypeKind.BASIC)
) )
template = nt["tmpls"][0] template = notetype["tmpls"][0]
template["name"] = name template["name"] = name
template["qfmt"] = "" template["qfmt"] = ""
template["afmt"] = "" template["afmt"] = ""
template["ord"] = None template["ord"] = None
return template return template
def add_template(self, m: NotetypeDict, template: TemplateDict) -> None: def add_template(self, notetype: NotetypeDict, template: TemplateDict) -> None:
"Modifies schema." "Modifies schema."
m["tmpls"].append(template) notetype["tmpls"].append(template)
def remove_template(self, m: NotetypeDict, template: TemplateDict) -> None: def remove_template(self, notetype: NotetypeDict, template: TemplateDict) -> None:
"Modifies schema." "Modifies schema."
assert len(m["tmpls"]) > 1 assert len(notetype["tmpls"]) > 1
m["tmpls"].remove(template) notetype["tmpls"].remove(template)
def reposition_template( def reposition_template(
self, m: NotetypeDict, template: TemplateDict, idx: int self, notetype: NotetypeDict, template: TemplateDict, idx: int
) -> None: ) -> None:
"Modifies schema." "Modifies schema."
oldidx = m["tmpls"].index(template) oldidx = notetype["tmpls"].index(template)
if oldidx == idx: if oldidx == idx:
return return
m["tmpls"].remove(template) notetype["tmpls"].remove(template)
m["tmpls"].insert(idx, template) notetype["tmpls"].insert(idx, template)
# legacy
newTemplate = new_template
def addTemplate(self, m: NotetypeDict, template: TemplateDict) -> None:
self.add_template(m, template)
if m["id"]:
self.save(m)
def remTemplate(self, m: NotetypeDict, template: TemplateDict) -> None:
self.remove_template(m, template)
self.save(m)
def moveTemplate(self, m: NotetypeDict, template: TemplateDict, idx: int) -> None:
self.reposition_template(m, template, idx)
self.save(m)
def template_use_count(self, ntid: NotetypeId, ord: int) -> int: def template_use_count(self, ntid: NotetypeId, ord: int) -> int:
return self.col.db.scalar( return self.col.db.scalar(
@ -464,9 +391,9 @@ and notes.mid = ? and cards.ord = ?""",
# legacy API - used by unit tests and add-ons # legacy API - used by unit tests and add-ons
def change( def change( # pylint: disable=invalid-name
self, self,
m: NotetypeDict, notetype: NotetypeDict,
nids: List[anki.notes.NoteId], nids: List[anki.notes.NoteId],
newModel: NotetypeDict, newModel: NotetypeDict,
fmap: Dict[int, Optional[int]], fmap: Dict[int, Optional[int]],
@ -476,7 +403,11 @@ and notes.mid = ? and cards.ord = ?""",
self.col.modSchema(check=True) self.col.modSchema(check=True)
assert fmap assert fmap
field_map = self._convert_legacy_map(fmap, len(newModel["flds"])) field_map = self._convert_legacy_map(fmap, len(newModel["flds"]))
if not cmap or newModel["type"] == MODEL_CLOZE or m["type"] == MODEL_CLOZE: if (
not cmap
or newModel["type"] == MODEL_CLOZE
or notetype["type"] == MODEL_CLOZE
):
template_map = [] template_map = []
else: else:
template_map = self._convert_legacy_map(cmap, len(newModel["tmpls"])) template_map = self._convert_legacy_map(cmap, len(newModel["tmpls"]))
@ -486,7 +417,7 @@ and notes.mid = ? and cards.ord = ?""",
note_ids=nids, note_ids=nids,
new_fields=field_map, new_fields=field_map,
new_templates=template_map, new_templates=template_map,
old_notetype_id=m["id"], old_notetype_id=notetype["id"],
new_notetype_id=newModel["id"], new_notetype_id=newModel["id"],
current_schema=self.col.db.scalar("select scm from col"), current_schema=self.col.db.scalar("select scm from col"),
) )
@ -510,21 +441,107 @@ and notes.mid = ? and cards.ord = ?""",
# Schema hash # Schema hash
########################################################################## ##########################################################################
def scmhash(self, m: NotetypeDict) -> str: def scmhash(self, notetype: NotetypeDict) -> str:
"Return a hash of the schema, to see if models are compatible." "Return a hash of the schema, to see if models are compatible."
s = "" buf = ""
for f in m["flds"]: for field in notetype["flds"]:
s += f["name"] buf += field["name"]
for t in m["tmpls"]: for template in notetype["tmpls"]:
s += t["name"] buf += template["name"]
return checksum(s) return checksum(buf)
# Cloze # Legacy
########################################################################## ##########################################################################
# pylint: disable=invalid-name
@deprecated(info="use note.cloze_numbers_in_fields()")
def _availClozeOrds( def _availClozeOrds(
self, m: NotetypeDict, flds: str, allowEmpty: bool = True self, notetype: NotetypeDict, flds: str, allow_empty: bool = True
) -> List[int]: ) -> List[int]:
print("_availClozeOrds() is deprecated; use note.cloze_numbers_in_fields()")
note = _pb.Note(fields=[flds]) note = _pb.Note(fields=[flds])
return list(self.col._backend.cloze_numbers_in_note(note)) return list(self.col._backend.cloze_numbers_in_note(note))
# @deprecated(replaced_by=add_template)
def addTemplate(self, notetype: NotetypeDict, template: TemplateDict) -> None:
self.add_template(notetype, template)
if notetype["id"]:
self.update(notetype)
# @deprecated(replaced_by=remove_template)
def remTemplate(self, notetype: NotetypeDict, template: TemplateDict) -> None:
self.remove_template(notetype, template)
self.update(notetype)
# @deprecated(replaced_by=reposition_template)
def move_template(
self, notetype: NotetypeDict, template: TemplateDict, idx: int
) -> None:
self.reposition_template(notetype, template, idx)
self.update(notetype)
# @deprecated(replaced_by=add_field)
def addField(self, notetype: NotetypeDict, field: FieldDict) -> None:
self.add_field(notetype, field)
if notetype["id"]:
self.update(notetype)
# @deprecated(replaced_by=remove_field)
def remField(self, notetype: NotetypeDict, field: FieldDict) -> None:
self.remove_field(notetype, field)
self.update(notetype)
# @deprecated(replaced_by=reposition_field)
def moveField(self, notetype: NotetypeDict, field: FieldDict, idx: int) -> None:
self.reposition_field(notetype, field, idx)
self.update(notetype)
# @deprecated(replaced_by=rename_field)
def renameField(
self, notetype: NotetypeDict, field: FieldDict, new_name: str
) -> None:
self.rename_field(notetype, field, new_name)
self.update(notetype)
@deprecated(replaced_by=remove)
def rem(self, m: NotetypeDict) -> None:
"Delete model, and all its cards/notes."
self.remove(m["id"])
# @deprecated(info="not needed; is updated on note add")
def set_current(self, m: NotetypeDict) -> None:
self.col.set_config("curModel", m["id"])
@deprecated(replaced_by=all_names_and_ids)
def all_names(self) -> List[str]:
return [n.name for n in self.all_names_and_ids()]
@deprecated(replaced_by=all_names_and_ids)
def ids(self) -> List[NotetypeId]:
return [NotetypeId(n.id) for n in self.all_names_and_ids()]
@deprecated(info="no longer required")
def flush(self) -> None:
pass
# @deprecated(replaced_by=update_dict)
def update(self, notetype: NotetypeDict, preserve_usn: bool = True) -> None:
"Add or update an existing model. Use .update_dict() instead."
self._remove_from_cache(notetype["id"])
self.ensure_name_unique(notetype)
notetype["id"] = self.col._backend.add_or_update_notetype(
json=to_json_bytes(notetype), preserve_usn_and_mtime=preserve_usn
)
self.set_current(notetype)
self._mutate_after_write(notetype)
# @deprecated(replaced_by=update_dict)
def save(self, notetype: NotetypeDict = None, **legacy_kwargs: bool) -> None:
"Save changes made to provided note type."
if not notetype:
print_deprecation_warning(
"col.models.save() should be passed the changed notetype"
)
return
self.update(notetype, preserve_usn=False)

View file

@ -61,7 +61,7 @@ class Note(DeprecatedNamesMixin):
self.usn = note.usn self.usn = note.usn
self.tags = list(note.tags) self.tags = list(note.tags)
self.fields = list(note.fields) self.fields = list(note.fields)
self._fmap = self.col.models.fieldMap(self.note_type()) self._fmap = self.col.models.field_map(self.note_type())
def _to_backend_note(self) -> _pb.Note: def _to_backend_note(self) -> _pb.Note:
hooks.note_will_flush(self) hooks.note_will_flush(self)

View file

@ -1,6 +1,8 @@
# 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
# pylint: disable=invalid-name
from typing import List, Optional, Tuple from typing import List, Optional, Tuple
from anki.cards import Card, CardId from anki.cards import Card, CardId

View file

@ -1,6 +1,8 @@
# 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
# pylint: disable=invalid-name
from __future__ import annotations from __future__ import annotations
import random import random

View file

@ -1,6 +1,8 @@
# 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
# pylint: disable=invalid-name
from __future__ import annotations from __future__ import annotations
import random import random

View file

@ -44,10 +44,10 @@ def test_genrem():
m = col.models.current() m = col.models.current()
mm = col.models mm = col.models
# adding a new template should automatically create cards # adding a new template should automatically create cards
t = mm.newTemplate("rev") t = mm.new_template("rev")
t["qfmt"] = "{{Front}}2" t["qfmt"] = "{{Front}}2"
t["afmt"] = "" t["afmt"] = ""
mm.addTemplate(m, t) mm.add_template(m, t)
mm.save(m, templates=True) mm.save(m, templates=True)
assert len(note.cards()) == 2 assert len(note.cards()) == 2
# if the template is changed to remove cards, they'll be removed # if the template is changed to remove cards, they'll be removed
@ -68,9 +68,8 @@ def test_genrem():
def test_gendeck(): def test_gendeck():
col = getEmptyCol() col = getEmptyCol()
cloze = col.models.byName("Cloze") cloze = col.models.by_name("Cloze")
col.models.setCurrent(cloze) note = col.new_note(cloze)
note = col.newNote()
note["Text"] = "{{c1::one}}" note["Text"] = "{{c1::one}}"
col.addNote(note) col.addNote(note)
assert col.cardCount() == 1 assert col.cardCount() == 1

View file

@ -57,10 +57,10 @@ def test_noteAddDelete():
# test multiple cards - add another template # test multiple cards - add another template
m = col.models.current() m = col.models.current()
mm = col.models mm = col.models
t = mm.newTemplate("Reverse") t = mm.new_template("Reverse")
t["qfmt"] = "{{Back}}" t["qfmt"] = "{{Back}}"
t["afmt"] = "{{Front}}" t["afmt"] = "{{Front}}"
mm.addTemplate(m, t) mm.add_template(m, t)
mm.save(m) mm.save(m)
assert col.cardCount() == 2 assert col.cardCount() == 2
# creating new notes should use both cards # creating new notes should use both cards

View file

@ -37,10 +37,10 @@ def test_findCards():
m = col.models.current() m = col.models.current()
m = col.models.copy(m) m = col.models.copy(m)
mm = col.models mm = col.models
t = mm.newTemplate("Reverse") t = mm.new_template("Reverse")
t["qfmt"] = "{{Back}}" t["qfmt"] = "{{Back}}"
t["afmt"] = "{{Front}}" t["afmt"] = "{{Front}}"
mm.addTemplate(m, t) mm.add_template(m, t)
mm.save(m) mm.save(m)
note = col.newNote() note = col.newNote()
note["Front"] = "test" note["Front"] = "test"

View file

@ -189,7 +189,7 @@ def test_csv2():
col = getEmptyCol() col = getEmptyCol()
mm = col.models mm = col.models
m = mm.current() m = mm.current()
note = mm.newField("Three") note = mm.new_field("Three")
mm.addField(m, note) mm.addField(m, note)
mm.save(m) mm.save(m)
n = col.newNote() n = col.newNote()
@ -213,7 +213,7 @@ def test_tsv_tag_modified():
col = getEmptyCol() col = getEmptyCol()
mm = col.models mm = col.models
m = mm.current() m = mm.current()
note = mm.newField("Top") note = mm.new_field("Top")
mm.addField(m, note) mm.addField(m, note)
mm.save(m) mm.save(m)
n = col.newNote() n = col.newNote()
@ -249,7 +249,7 @@ def test_tsv_tag_multiple_tags():
col = getEmptyCol() col = getEmptyCol()
mm = col.models mm = col.models
m = mm.current() m = mm.current()
note = mm.newField("Top") note = mm.new_field("Top")
mm.addField(m, note) mm.addField(m, note)
mm.save(m) mm.save(m)
n = col.newNote() n = col.newNote()
@ -283,7 +283,7 @@ def test_csv_tag_only_if_modified():
col = getEmptyCol() col = getEmptyCol()
mm = col.models mm = col.models
m = mm.current() m = mm.current()
note = mm.newField("Left") note = mm.new_field("Left")
mm.addField(m, note) mm.addField(m, note)
mm.save(m) mm.save(m)
n = col.newNote() n = col.newNote()

View file

@ -17,7 +17,7 @@ def test_modelDelete():
note["Back"] = "2" note["Back"] = "2"
col.addNote(note) col.addNote(note)
assert col.cardCount() == 1 assert col.cardCount() == 1
col.models.rem(col.models.current()) col.models.remove(col.models.current()["id"])
assert col.cardCount() == 0 assert col.cardCount() == 0
@ -47,7 +47,7 @@ def test_fields():
assert "{{NewFront}}" in m["tmpls"][0]["qfmt"] assert "{{NewFront}}" in m["tmpls"][0]["qfmt"]
h = col.models.scmhash(m) h = col.models.scmhash(m)
# add a field # add a field
field = col.models.newField("foo") field = col.models.new_field("foo")
col.models.addField(m, field) col.models.addField(m, field)
assert col.get_note(col.models.nids(m)[0]).fields == ["1", "2", ""] assert col.get_note(col.models.nids(m)[0]).fields == ["1", "2", ""]
assert col.models.scmhash(m) != h assert col.models.scmhash(m) != h
@ -65,7 +65,7 @@ def test_fields():
col.models.moveField(m, m["flds"][1], 0) col.models.moveField(m, m["flds"][1], 0)
assert col.get_note(col.models.nids(m)[0]).fields == ["1", ""] assert col.get_note(col.models.nids(m)[0]).fields == ["1", ""]
# add another and put in middle # add another and put in middle
field = col.models.newField("baz") field = col.models.new_field("baz")
col.models.addField(m, field) col.models.addField(m, field)
note = col.get_note(col.models.nids(m)[0]) note = col.get_note(col.models.nids(m)[0])
note["baz"] = "2" note["baz"] = "2"
@ -86,10 +86,10 @@ def test_templates():
col = getEmptyCol() col = getEmptyCol()
m = col.models.current() m = col.models.current()
mm = col.models mm = col.models
t = mm.newTemplate("Reverse") t = mm.new_template("Reverse")
t["qfmt"] = "{{Back}}" t["qfmt"] = "{{Back}}"
t["afmt"] = "{{Front}}" t["afmt"] = "{{Front}}"
mm.addTemplate(m, t) mm.add_template(m, t)
mm.save(m) mm.save(m)
note = col.newNote() note = col.newNote()
note["Front"] = "1" note["Front"] = "1"
@ -101,23 +101,26 @@ def test_templates():
assert c.ord == 0 assert c.ord == 0
assert c2.ord == 1 assert c2.ord == 1
# switch templates # switch templates
col.models.moveTemplate(m, c.template(), 1) col.models.reposition_template(m, c.template(), 1)
col.models.update(m)
c.load() c.load()
c2.load() c2.load()
assert c.ord == 1 assert c.ord == 1
assert c2.ord == 0 assert c2.ord == 0
# removing a template should delete its cards # removing a template should delete its cards
col.models.remTemplate(m, m["tmpls"][0]) col.models.remove_template(m, m["tmpls"][0])
col.models.update(m)
assert col.cardCount() == 1 assert col.cardCount() == 1
# and should have updated the other cards' ordinals # and should have updated the other cards' ordinals
c = note.cards()[0] c = note.cards()[0]
assert c.ord == 0 assert c.ord == 0
assert stripHTML(c.question()) == "1" assert stripHTML(c.question()) == "1"
# it shouldn't be possible to orphan notes by removing templates # it shouldn't be possible to orphan notes by removing templates
t = mm.newTemplate("template name") t = mm.new_template("template name")
t["qfmt"] = "{{Front}}2" t["qfmt"] = "{{Front}}2"
mm.addTemplate(m, t) mm.add_template(m, t)
col.models.remTemplate(m, m["tmpls"][0]) col.models.remove_template(m, m["tmpls"][0])
col.models.update(m)
assert ( assert (
col.db.scalar( col.db.scalar(
"select count() from cards where nid not in (select id from notes)" "select count() from cards where nid not in (select id from notes)"
@ -128,17 +131,17 @@ def test_templates():
def test_cloze_ordinals(): def test_cloze_ordinals():
col = getEmptyCol() col = getEmptyCol()
col.models.setCurrent(col.models.byName("Cloze")) m = col.models.by_name("Cloze")
m = col.models.current()
mm = col.models mm = col.models
# We replace the default Cloze template # We replace the default Cloze template
t = mm.newTemplate("ChainedCloze") t = mm.new_template("ChainedCloze")
t["qfmt"] = "{{text:cloze:Text}}" t["qfmt"] = "{{text:cloze:Text}}"
t["afmt"] = "{{text:cloze:Text}}" t["afmt"] = "{{text:cloze:Text}}"
mm.addTemplate(m, t) mm.add_template(m, t)
mm.save(m) mm.save(m)
col.models.remTemplate(m, m["tmpls"][0]) col.models.remove_template(m, m["tmpls"][0])
col.models.update(m)
note = col.newNote() note = col.newNote()
note["Text"] = "{{c1::firstQ::firstA}}{{c2::secondQ::secondA}}" note["Text"] = "{{c1::firstQ::firstA}}{{c2::secondQ::secondA}}"
@ -163,26 +166,26 @@ def test_text():
def test_cloze(): def test_cloze():
col = getEmptyCol() col = getEmptyCol()
col.models.setCurrent(col.models.byName("Cloze")) m = col.models.by_name("Cloze")
note = col.newNote() note = col.new_note(m)
assert note.note_type()["name"] == "Cloze" assert note.note_type()["name"] == "Cloze"
# a cloze model with no clozes is not empty # a cloze model with no clozes is not empty
note["Text"] = "nothing" note["Text"] = "nothing"
assert col.addNote(note) assert col.addNote(note)
# try with one cloze # try with one cloze
note = col.newNote() note = col.new_note(m)
note["Text"] = "hello {{c1::world}}" note["Text"] = "hello {{c1::world}}"
assert col.addNote(note) == 1 assert col.addNote(note) == 1
assert "hello <span class=cloze>[...]</span>" in note.cards()[0].question() assert "hello <span class=cloze>[...]</span>" in note.cards()[0].question()
assert "hello <span class=cloze>world</span>" in note.cards()[0].answer() assert "hello <span class=cloze>world</span>" in note.cards()[0].answer()
# and with a comment # and with a comment
note = col.newNote() note = col.new_note(m)
note["Text"] = "hello {{c1::world::typical}}" note["Text"] = "hello {{c1::world::typical}}"
assert col.addNote(note) == 1 assert col.addNote(note) == 1
assert "<span class=cloze>[typical]</span>" in note.cards()[0].question() assert "<span class=cloze>[typical]</span>" in note.cards()[0].question()
assert "<span class=cloze>world</span>" in note.cards()[0].answer() assert "<span class=cloze>world</span>" in note.cards()[0].answer()
# and with 2 clozes # and with 2 clozes
note = col.newNote() note = col.new_note(m)
note["Text"] = "hello {{c1::world}} {{c2::bar}}" note["Text"] = "hello {{c1::world}} {{c2::bar}}"
assert col.addNote(note) == 2 assert col.addNote(note) == 2
(c1, c2) = note.cards() (c1, c2) = note.cards()
@ -192,7 +195,7 @@ def test_cloze():
assert "world <span class=cloze>bar</span>" in c2.answer() assert "world <span class=cloze>bar</span>" in c2.answer()
# if there are multiple answers for a single cloze, they are given in a # if there are multiple answers for a single cloze, they are given in a
# list # list
note = col.newNote() note = col.new_note(m)
note["Text"] = "a {{c1::b}} {{c1::c}}" note["Text"] = "a {{c1::b}} {{c1::c}}"
assert col.addNote(note) == 1 assert col.addNote(note) == 1
assert "<span class=cloze>b</span> <span class=cloze>c</span>" in ( assert "<span class=cloze>b</span> <span class=cloze>c</span>" in (
@ -211,8 +214,8 @@ def test_cloze():
def test_cloze_mathjax(): def test_cloze_mathjax():
col = getEmptyCol() col = getEmptyCol()
col.models.setCurrent(col.models.byName("Cloze")) m = col.models.by_name("Cloze")
note = col.newNote() note = col.new_note(m)
note[ note[
"Text" "Text"
] = r"{{c1::ok}} \(2^2\) {{c2::not ok}} \(2^{{c3::2}}\) \(x^3\) {{c4::blah}} {{c5::text with \(x^2\) jax}}" ] = r"{{c1::ok}} \(2^2\) {{c2::not ok}} \(2^{{c3::2}}\) \(x^3\) {{c4::blah}} {{c5::text with \(x^2\) jax}}"
@ -224,7 +227,7 @@ def test_cloze_mathjax():
assert "class=cloze" in note.cards()[3].question() assert "class=cloze" in note.cards()[3].question()
assert "class=cloze" in note.cards()[4].question() assert "class=cloze" in note.cards()[4].question()
note = col.newNote() note = col.new_note(m)
note["Text"] = r"\(a\) {{c1::b}} \[ {{c1::c}} \]" note["Text"] = r"\(a\) {{c1::b}} \[ {{c1::c}} \]"
assert col.addNote(note) assert col.addNote(note)
assert len(note.cards()) == 1 assert len(note.cards()) == 1
@ -237,11 +240,10 @@ def test_cloze_mathjax():
def test_typecloze(): def test_typecloze():
col = getEmptyCol() col = getEmptyCol()
m = col.models.byName("Cloze") m = col.models.by_name("Cloze")
col.models.setCurrent(m)
m["tmpls"][0]["qfmt"] = "{{cloze:Text}}{{type:cloze:Text}}" m["tmpls"][0]["qfmt"] = "{{cloze:Text}}{{type:cloze:Text}}"
col.models.save(m) col.models.save(m)
note = col.newNote() note = col.new_note(m)
note["Text"] = "hello {{c1::world}}" note["Text"] = "hello {{c1::world}}"
col.addNote(note) col.addNote(note)
assert "[[type:cloze:Text]]" in note.cards()[0].question() assert "[[type:cloze:Text]]" in note.cards()[0].question()
@ -249,17 +251,17 @@ def test_typecloze():
def test_chained_mods(): def test_chained_mods():
col = getEmptyCol() col = getEmptyCol()
col.models.setCurrent(col.models.byName("Cloze")) m = col.models.by_name("Cloze")
m = col.models.current()
mm = col.models mm = col.models
# We replace the default Cloze template # We replace the default Cloze template
t = mm.newTemplate("ChainedCloze") t = mm.new_template("ChainedCloze")
t["qfmt"] = "{{cloze:text:Text}}" t["qfmt"] = "{{cloze:text:Text}}"
t["afmt"] = "{{cloze:text:Text}}" t["afmt"] = "{{cloze:text:Text}}"
mm.addTemplate(m, t) mm.add_template(m, t)
mm.save(m) mm.save(m)
col.models.remTemplate(m, m["tmpls"][0]) col.models.remove_template(m, m["tmpls"][0])
col.models.update(m)
note = col.newNote() note = col.newNote()
q1 = '<span style="color:red">phrase</span>' q1 = '<span style="color:red">phrase</span>'
@ -285,14 +287,14 @@ def test_chained_mods():
def test_modelChange(): def test_modelChange():
col = getEmptyCol() col = getEmptyCol()
cloze = col.models.byName("Cloze") cloze = col.models.by_name("Cloze")
# enable second template and add a note # enable second template and add a note
m = col.models.current() m = col.models.current()
mm = col.models mm = col.models
t = mm.newTemplate("Reverse") t = mm.new_template("Reverse")
t["qfmt"] = "{{Back}}" t["qfmt"] = "{{Back}}"
t["afmt"] = "{{Front}}" t["afmt"] = "{{Front}}"
mm.addTemplate(m, t) mm.add_template(m, t)
mm.save(m) mm.save(m)
basic = m basic = m
note = col.newNote() note = col.newNote()
@ -360,7 +362,8 @@ def test_modelChange():
assert note["Text"] == "f2" assert note["Text"] == "f2"
assert len(note.cards()) == 2 assert len(note.cards()) == 2
# back the other way, with deletion of second ord # back the other way, with deletion of second ord
col.models.remTemplate(basic, basic["tmpls"][1]) col.models.remove_template(basic, basic["tmpls"][1])
col.models.update(basic)
assert col.db.scalar("select count() from cards where nid = ?", note.id) == 2 assert col.db.scalar("select count() from cards where nid = ?", note.id) == 2
map = {0: 0} map = {0: 0}
col.models.change(cloze, [note.id], basic, map, map) col.models.change(cloze, [note.id], basic, map, map)
@ -375,14 +378,14 @@ def test_req():
col = getEmptyCol() col = getEmptyCol()
mm = col.models mm = col.models
basic = mm.byName("Basic") basic = mm.by_name("Basic")
assert "req" in basic assert "req" in basic
reqSize(basic) reqSize(basic)
r = basic["req"][0] r = basic["req"][0]
assert r[0] == 0 assert r[0] == 0
assert r[1] in ("any", "all") assert r[1] in ("any", "all")
assert r[2] == [0] assert r[2] == [0]
opt = mm.byName("Basic (optional reversed card)") opt = mm.by_name("Basic (optional reversed card)")
reqSize(opt) reqSize(opt)
r = opt["req"][0] r = opt["req"][0]
assert r[1] in ("any", "all") assert r[1] in ("any", "all")
@ -397,7 +400,7 @@ def test_req():
mm.save(opt, templates=True) mm.save(opt, templates=True)
assert opt["req"][1] == [1, "none", []] assert opt["req"][1] == [1, "none", []]
opt = mm.byName("Basic (type in the answer)") opt = mm.by_name("Basic (type in the answer)")
reqSize(opt) reqSize(opt)
r = opt["req"][0] r = opt["req"][0]
assert r[1] in ("any", "all") assert r[1] in ("any", "all")

View file

@ -63,10 +63,10 @@ def test_new():
# # the default order should ensure siblings are not seen together, and # # the default order should ensure siblings are not seen together, and
# # should show all cards # # should show all cards
# m = col.models.current(); mm = col.models # m = col.models.current(); mm = col.models
# t = mm.newTemplate("Reverse") # t = mm.new_template("Reverse")
# t['qfmt'] = "{{Back}}" # t['qfmt'] = "{{Back}}"
# t['afmt'] = "{{Front}}" # t['afmt'] = "{{Front}}"
# mm.addTemplate(m, t) # mm.add_template(m, t)
# mm.save(m) # mm.save(m)
# note = col.newNote() # note = col.newNote()
# note['Front'] = u"2"; note['Back'] = u"2" # note['Front'] = u"2"; note['Back'] = u"2"
@ -562,8 +562,8 @@ def test_suspend():
def test_cram(): def test_cram():
col = getEmptyCol() col = getEmptyCol()
opt = col.models.byName("Basic (and reversed card)") opt = col.models.by_name("Basic (and reversed card)")
col.models.setCurrent(opt) col.models.set_current(opt)
note = col.newNote() note = col.newNote()
note["Front"] = "one" note["Front"] = "one"
col.addNote(note) col.addNote(note)
@ -806,14 +806,14 @@ def test_ordcycle():
# add two more templates and set second active # add two more templates and set second active
m = col.models.current() m = col.models.current()
mm = col.models mm = col.models
t = mm.newTemplate("Reverse") t = mm.new_template("Reverse")
t["qfmt"] = "{{Back}}" t["qfmt"] = "{{Back}}"
t["afmt"] = "{{Front}}" t["afmt"] = "{{Front}}"
mm.addTemplate(m, t) mm.add_template(m, t)
t = mm.newTemplate("f2") t = mm.new_template("f2")
t["qfmt"] = "{{Front}}2" t["qfmt"] = "{{Front}}2"
t["afmt"] = "{{Back}}" t["afmt"] = "{{Back}}"
mm.addTemplate(m, t) mm.add_template(m, t)
mm.save(m) mm.save(m)
# create a new note; it should have 3 cards # create a new note; it should have 3 cards
note = col.newNote() note = col.newNote()

View file

@ -75,10 +75,10 @@ def test_new():
# # the default order should ensure siblings are not seen together, and # # the default order should ensure siblings are not seen together, and
# # should show all cards # # should show all cards
# m = col.models.current(); mm = col.models # m = col.models.current(); mm = col.models
# t = mm.newTemplate("Reverse") # t = mm.new_template("Reverse")
# t['qfmt'] = "{{Back}}" # t['qfmt'] = "{{Back}}"
# t['afmt'] = "{{Front}}" # t['afmt'] = "{{Front}}"
# mm.addTemplate(m, t) # mm.add_template(m, t)
# mm.save(m) # mm.save(m)
# note = col.newNote() # note = col.newNote()
# note['Front'] = u"2"; note['Back'] = u"2" # note['Front'] = u"2"; note['Back'] = u"2"
@ -880,14 +880,14 @@ def test_ordcycle():
# add two more templates and set second active # add two more templates and set second active
m = col.models.current() m = col.models.current()
mm = col.models mm = col.models
t = mm.newTemplate("Reverse") t = mm.new_template("Reverse")
t["qfmt"] = "{{Back}}" t["qfmt"] = "{{Back}}"
t["afmt"] = "{{Front}}" t["afmt"] = "{{Front}}"
mm.addTemplate(m, t) mm.add_template(m, t)
t = mm.newTemplate("f2") t = mm.new_template("f2")
t["qfmt"] = "{{Front}}2" t["qfmt"] = "{{Front}}2"
t["afmt"] = "{{Back}}" t["afmt"] = "{{Back}}"
mm.addTemplate(m, t) mm.add_template(m, t)
mm.save(m) mm.save(m)
# create a new note; it should have 3 cards # create a new note; it should have 3 cards
note = col.newNote() note = col.newNote()

View file

@ -77,13 +77,13 @@ class Hook:
class {self.classname()}: class {self.classname()}:
{classdoc}{self.list_code()} {classdoc}{self.list_code()}
def append(self, cb: {self.callable()}) -> None: def append(self, callback: {self.callable()}) -> None:
'''{appenddoc}''' '''{appenddoc}'''
self._hooks.append(cb) self._hooks.append(callback)
def remove(self, cb: {self.callable()}) -> None: def remove(self, callback: {self.callable()}) -> None:
if cb in self._hooks: if callback in self._hooks:
self._hooks.remove(cb) self._hooks.remove(callback)
def count(self) -> int: def count(self) -> int:
return len(self._hooks) return len(self._hooks)

View file

@ -226,7 +226,7 @@ class CardLayout(QDialog):
tform.style_button.setText(tr.card_templates_template_styling()) tform.style_button.setText(tr.card_templates_template_styling())
tform.groupBox.setTitle(tr.card_templates_template_box()) tform.groupBox.setTitle(tr.card_templates_template_box())
cnt = self.mw.col.models.useCount(self.model) cnt = self.mw.col.models.use_count(self.model)
self.tform.changes_affect_label.setText( self.tform.changes_affect_label.setText(
self.col.tr.card_templates_changes_will_affect_notes(count=cnt) self.col.tr.card_templates_changes_will_affect_notes(count=cnt)
) )
@ -633,14 +633,14 @@ class CardLayout(QDialog):
return name return name
def onAddCard(self) -> None: def onAddCard(self) -> None:
cnt = self.mw.col.models.useCount(self.model) cnt = self.mw.col.models.use_count(self.model)
txt = tr.card_templates_this_will_create_card_proceed(count=cnt) txt = tr.card_templates_this_will_create_card_proceed(count=cnt)
if not askUser(txt): if not askUser(txt):
return return
if not self.change_tracker.mark_schema(): if not self.change_tracker.mark_schema():
return return
name = self._newCardName() name = self._newCardName()
t = self.mm.newTemplate(name) t = self.mm.new_template(name)
old = self.current_template() old = self.current_template()
t["qfmt"] = old["qfmt"] t["qfmt"] = old["qfmt"]
t["afmt"] = old["afmt"] t["afmt"] = old["afmt"]

View file

@ -136,7 +136,7 @@ class FieldDialog(QDialog):
if not self.change_tracker.mark_schema(): if not self.change_tracker.mark_schema():
return return
self.saveField() self.saveField()
f = self.mm.newField(name) f = self.mm.new_field(name)
self.mm.add_field(self.model, f) self.mm.add_field(self.model, f)
self.fillFields() self.fillFields()
self.form.fieldList.setCurrentRow(len(self.model["flds"]) - 1) self.form.fieldList.setCurrentRow(len(self.model["flds"]) - 1)
@ -145,7 +145,7 @@ class FieldDialog(QDialog):
if len(self.model["flds"]) < 2: if len(self.model["flds"]) < 2:
showWarning(tr.fields_notes_require_at_least_one_field()) showWarning(tr.fields_notes_require_at_least_one_field())
return return
count = self.mm.useCount(self.model) count = self.mm.use_count(self.model)
c = tr.browsing_note_count(count=count) c = tr.browsing_note_count(count=count)
if not askUser(tr.fields_delete_field_from(val=c)): if not askUser(tr.fields_delete_field_from(val=c)):
return return

View file

@ -76,7 +76,7 @@ class ModelChooser(QHBoxLayout):
edit = QPushButton(tr.qt_misc_manage(), clicked=self.onEdit) # type: ignore edit = QPushButton(tr.qt_misc_manage(), clicked=self.onEdit) # type: ignore
def nameFunc() -> List[str]: def nameFunc() -> List[str]:
return sorted(self.deck.models.allNames()) return sorted(self.deck.models.all_names())
ret = StudyDeck( ret = StudyDeck(
self.mw, self.mw,
@ -92,7 +92,7 @@ class ModelChooser(QHBoxLayout):
) )
if not ret.name: if not ret.name:
return return
m = self.deck.models.byName(ret.name) m = self.deck.models.by_name(ret.name)
self.deck.conf["curModel"] = m["id"] self.deck.conf["curModel"] = m["id"]
cdeck = self.deck.decks.current() cdeck = self.deck.decks.current()
cdeck["mid"] = m["id"] cdeck["mid"] = m["id"]

View file

@ -99,7 +99,7 @@ class NotetypeChooser(QHBoxLayout):
qconnect(edit.clicked, self.onEdit) qconnect(edit.clicked, self.onEdit)
def nameFunc() -> List[str]: def nameFunc() -> List[str]:
return sorted(self.mw.col.models.allNames()) return sorted(self.mw.col.models.all_names())
ret = StudyDeck( ret = StudyDeck(
self.mw, self.mw,
@ -116,7 +116,7 @@ class NotetypeChooser(QHBoxLayout):
if not ret.name: if not ret.name:
return return
notetype = self.mw.col.models.byName(ret.name) notetype = self.mw.col.models.by_name(ret.name)
if (id := notetype["id"]) != self._selected_notetype_id: if (id := notetype["id"]) != self._selected_notetype_id:
self.selected_notetype_id = id self.selected_notetype_id = id