NF: NoteTypeID type

This commit is contained in:
Arthur Milchior 2021-03-23 12:41:24 +01:00 committed by Damien Elmes
parent b54410200e
commit 7ea862931c
10 changed files with 59 additions and 43 deletions

View File

@ -42,7 +42,7 @@ from anki.decks import DeckID, DeckManager
from anki.errors import AnkiError, DBError
from anki.lang import TR, FormatTimeSpan
from anki.media import MediaManager, media_paths_from_col_path
from anki.models import ModelManager, NoteType
from anki.models import ModelManager, NoteType, NoteTypeID
from anki.notes import Note, NoteID
from anki.scheduler.v1 import Scheduler as V1Scheduler
from anki.scheduler.v2 import Scheduler as V2Scheduler
@ -406,7 +406,7 @@ class Collection:
home_deck_of_current_review_card=home_deck,
)
def default_deck_for_notetype(self, notetype_id: NoteID) -> Optional[DeckID]:
def default_deck_for_notetype(self, notetype_id: NoteTypeID) -> Optional[DeckID]:
"""If 'change deck depending on notetype' is enabled in the preferences,
return the last deck used with the provided notetype, if any.."""
if self.get_config_bool(Config.Bool.ADDING_DEFAULTS_TO_CURRENT_DECK):
@ -578,7 +578,7 @@ class Collection:
dupes = []
fields: Dict[int, int] = {}
def ordForMid(mid: int) -> int:
def ordForMid(mid: NoteTypeID) -> int:
if mid not in fields:
model = self.models.get(mid)
for c, f in enumerate(model["flds"]):

View File

@ -11,6 +11,7 @@ from anki.consts import *
from anki.decks import DeckID, DeckManager
from anki.importing.base import Importer
from anki.lang import TR
from anki.models import NoteTypeID
from anki.notes import NoteID
from anki.utils import intTime, joinFields, splitFields, stripHTMLMedia
@ -81,7 +82,7 @@ class Anki2Importer(Importer):
def _importNotes(self) -> None:
# build guid -> (id,mod,mid) hash & map of existing note ids
self._notes: Dict[str, Tuple[NoteID, int, int]] = {}
self._notes: Dict[str, Tuple[NoteID, int, NoteTypeID]] = {}
existing = {}
for id, guid, mod, mid in self.dst.db.execute(
"select id, guid, mod, mid from notes"
@ -217,9 +218,9 @@ class Anki2Importer(Importer):
def _prepareModels(self) -> None:
"Prepare index of schema hashes."
self._modelMap: Dict[int, int] = {}
self._modelMap: Dict[NoteTypeID, NoteTypeID] = {}
def _mid(self, srcMid: int) -> Any:
def _mid(self, srcMid: NoteTypeID) -> Any:
"Return local id for remote MID."
# already processed this mid?
if srcMid in self._modelMap:
@ -248,7 +249,7 @@ class Anki2Importer(Importer):
self.dst.models.update(model)
break
# as they don't match, try next id
mid += 1
mid = NoteTypeID(mid + 1)
# save map and return new mid
self._modelMap[srcMid] = mid
return mid
@ -432,7 +433,7 @@ insert or ignore into revlog values (?,?,?,?,?,?,?,?,?)""",
# the user likely used subdirectories
pass
def _mungeMedia(self, mid: int, fieldsStr: str) -> str:
def _mungeMedia(self, mid: NoteTypeID, fieldsStr: str) -> str:
fields = splitFields(fieldsStr)
def repl(match):

View File

@ -10,6 +10,7 @@ from anki.config import Config
from anki.consts import NEW_CARDS_RANDOM, STARTING_FACTOR
from anki.importing.base import Importer
from anki.lang import TR
from anki.models import NoteTypeID
from anki.notes import NoteID
from anki.utils import (
fieldChecksum,
@ -243,7 +244,7 @@ class NoteImporter(Importer):
def newData(
self, n: ForeignNote
) -> Tuple[NoteID, str, int, int, int, str, str, str, int, int, str]:
) -> Tuple[NoteID, str, NoteTypeID, int, int, str, str, str, int, int, str]:
id = self._nextID
self._nextID = NoteID(self._nextID + 1)
self._ids.append(id)
@ -267,7 +268,9 @@ class NoteImporter(Importer):
def addNew(
self,
rows: List[Tuple[NoteID, str, int, int, int, str, str, str, int, int, str]],
rows: List[
Tuple[NoteID, str, NoteTypeID, int, int, str, str, str, int, int, str]
],
) -> None:
self.col.db.executemany(
"insert or replace into notes values (?,?,?,?,?,?,?,?,?,?,?)", rows

View File

@ -17,6 +17,7 @@ import anki
import anki._backend.backend_pb2 as _pb
from anki.consts import *
from anki.latex import render_latex, render_latex_returning_errors
from anki.models import NoteTypeID
from anki.sound import SoundOrVideoTag
from anki.template import av_tags_to_native
from anki.utils import intTime
@ -159,7 +160,7 @@ class MediaManager:
##########################################################################
def filesInStr(
self, mid: int, string: str, includeRemote: bool = False
self, mid: NoteTypeID, string: str, includeRemote: bool = False
) -> List[str]:
l = []
model = self.col.models.get(mid)

View File

@ -8,7 +8,7 @@ import pprint
import sys
import time
import traceback
from typing import Any, Dict, List, Optional, Sequence, Tuple, Union
from typing import Any, Dict, List, NewType, Optional, Sequence, Tuple, Union
import anki # pylint: disable=unused-import
import anki._backend.backend_pb2 as _pb
@ -35,6 +35,7 @@ NoteTypeNameIDUseCount = _pb.NoteTypeNameIDUseCount
NoteType = Dict[str, Any]
Field = Dict[str, Any]
Template = Dict[str, Union[str, int, None]]
NoteTypeID = NewType("NoteTypeID", int)
class ModelsDictProxy:
@ -47,7 +48,7 @@ class ModelsDictProxy:
def __getitem__(self, item: Any) -> Any:
self._warn()
return self._col.models.get(int(item))
return self._col.models.get(NoteTypeID(int(item)))
def __setitem__(self, key: str, val: Any) -> None:
self._warn()
@ -114,16 +115,16 @@ class ModelManager:
# need to cache responses from the backend. Please do not
# access the cache directly!
_cache: Dict[int, NoteType] = {}
_cache: Dict[NoteTypeID, NoteType] = {}
def _update_cache(self, nt: NoteType) -> None:
self._cache[nt["id"]] = nt
def _remove_from_cache(self, ntid: int) -> None:
def _remove_from_cache(self, ntid: NoteTypeID) -> None:
if ntid in self._cache:
del self._cache[ntid]
def _get_cached(self, ntid: int) -> Optional[NoteType]:
def _get_cached(self, ntid: NoteTypeID) -> Optional[NoteType]:
return self._cache.get(ntid)
def _clear_cache(self) -> None:
@ -143,11 +144,11 @@ class ModelManager:
def allNames(self) -> List[str]:
return [n.name for n in self.all_names_and_ids()]
def ids(self) -> List[int]:
return [n.id 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
def have(self, id: int) -> bool:
def have(self, id: NoteTypeID) -> bool:
if isinstance(id, str):
id = int(id)
return any(True for e in self.all_names_and_ids() if e.id == id)
@ -162,7 +163,7 @@ class ModelManager:
m = self.get(self.col.conf["curModel"])
if m:
return m
return self.get(self.all_names_and_ids()[0].id)
return self.get(NoteTypeID(self.all_names_and_ids()[0].id))
def setCurrent(self, m: NoteType) -> None:
self.col.conf["curModel"] = m["id"]
@ -170,13 +171,13 @@ class ModelManager:
# Retrieving and creating models
#############################################################
def id_for_name(self, name: str) -> Optional[int]:
def id_for_name(self, name: str) -> Optional[NoteTypeID]:
try:
return self.col._backend.get_notetype_id_by_name(name)
return NoteTypeID(self.col._backend.get_notetype_id_by_name(name))
except NotFoundError:
return None
def get(self, id: int) -> Optional[NoteType]:
def get(self, id: NoteTypeID) -> Optional[NoteType]:
"Get model with ID, or None."
# deal with various legacy input types
if id is None:
@ -195,7 +196,7 @@ class ModelManager:
def all(self) -> List[NoteType]:
"Get all models."
return [self.get(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[NoteType]:
"Get model with NAME."
@ -222,10 +223,10 @@ class ModelManager:
def remove_all_notetypes(self) -> None:
for nt in self.all_names_and_ids():
self._remove_from_cache(nt.id)
self._remove_from_cache(NoteTypeID(nt.id))
self.col._backend.remove_notetype(nt.id)
def remove(self, id: int) -> None:
def remove(self, id: NoteTypeID) -> None:
"Modifies schema."
self._remove_from_cache(id)
self.col._backend.remove_notetype(id)
@ -257,7 +258,7 @@ class ModelManager:
# Tools
##################################################
def nids(self, ntid: int) -> List[anki.notes.NoteID]:
def nids(self, ntid: NoteTypeID) -> List[anki.notes.NoteID]:
"Note ids for M."
if isinstance(ntid, dict):
# legacy callers passed in note type
@ -403,7 +404,7 @@ class ModelManager:
self.reposition_template(m, template, idx)
self.save(m)
def template_use_count(self, ntid: int, ord: int) -> int:
def template_use_count(self, ntid: NoteTypeID, ord: int) -> int:
return self.col.db.scalar(
"""
select count() from cards, notes where cards.nid = notes.id

View File

@ -11,7 +11,7 @@ import anki # pylint: disable=unused-import
import anki._backend.backend_pb2 as _pb
from anki import hooks
from anki.consts import MODEL_STD
from anki.models import NoteType, Template
from anki.models import NoteType, NoteTypeID, Template
from anki.utils import joinFields
DuplicateOrEmptyResult = _pb.NoteIsDuplicateOrEmptyOut.State
@ -25,6 +25,7 @@ class Note:
flags = 0
data = ""
id: NoteID
mid: NoteTypeID
def __init__(
self,
@ -52,7 +53,7 @@ class Note:
def _load_from_backend_note(self, n: _pb.Note) -> None:
self.id = NoteID(n.id)
self.guid = n.guid
self.mid = n.notetype_id
self.mid = NoteTypeID(n.notetype_id)
self.mod = n.mtime_secs
self.usn = n.usn
self.tags = list(n.tags)

View File

@ -9,6 +9,7 @@ import aqt.forms
from anki.collection import OpChanges, SearchNode
from anki.consts import MODEL_CLOZE
from anki.decks import DeckID
from anki.models import NoteTypeID
from anki.notes import DuplicateOrEmptyResult, Note, NoteID
from anki.utils import htmlToTextLine, isMac
from aqt import AnkiQt, gui_hooks
@ -65,7 +66,7 @@ class AddCards(QDialog):
self.notetype_chooser = NoteTypeChooser(
mw=self.mw,
widget=self.form.modelArea,
starting_notetype_id=defaults.notetype_id,
starting_notetype_id=NoteTypeID(defaults.notetype_id),
on_button_activated=self.show_notetype_selector,
on_notetype_changed=self.on_notetype_change,
)
@ -110,7 +111,7 @@ class AddCards(QDialog):
def show_notetype_selector(self) -> None:
self.editor.call_after_note_saved(self.notetype_chooser.choose_notetype)
def on_notetype_change(self, notetype_id: NoteID) -> None:
def on_notetype_change(self, notetype_id: NoteTypeID) -> None:
# need to adjust current deck?
if deck_id := self.mw.col.default_deck_for_notetype(notetype_id):
self.deck_chooser.selected_deck_id = deck_id

View File

@ -8,7 +8,7 @@ from typing import Any, List, Optional, Sequence
import aqt.clayout
from anki import stdmodels
from anki.lang import without_unicode_isolation
from anki.models import NoteType, NoteTypeNameIDUseCount
from anki.models import NoteType, NoteTypeID, NoteTypeNameIDUseCount
from anki.notes import Note
from aqt import AnkiQt, gui_hooks
from aqt.qt import *
@ -33,7 +33,7 @@ class Models(QDialog):
mw: AnkiQt,
parent: Optional[QWidget] = None,
fromMain: bool = False,
selected_notetype_id: Optional[int] = None,
selected_notetype_id: Optional[NoteTypeID] = None,
):
self.mw = mw
parent = parent or mw

View File

@ -2,7 +2,7 @@
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
from typing import List, Optional
from anki.notes import NoteID
from anki.models import NoteTypeID
from aqt import AnkiQt, gui_hooks
from aqt.qt import *
from aqt.utils import TR, HelpPage, shortcut, tr
@ -23,14 +23,16 @@ class NoteTypeChooser(QHBoxLayout):
deleted.
"""
_selected_notetype_id: NoteTypeID
def __init__(
self,
*,
mw: AnkiQt,
widget: QWidget,
starting_notetype_id: int,
starting_notetype_id: NoteTypeID,
on_button_activated: Optional[Callable[[], None]] = None,
on_notetype_changed: Optional[Callable[[NoteID], None]] = None,
on_notetype_changed: Optional[Callable[[NoteTypeID], None]] = None,
show_prefix_label: bool = True,
) -> None:
QHBoxLayout.__init__(self)
@ -42,7 +44,7 @@ class NoteTypeChooser(QHBoxLayout):
self.on_button_activated = self.choose_notetype
self._setup_ui(show_label=show_prefix_label)
gui_hooks.state_did_reset.append(self.reset_state)
self._selected_notetype_id = 0
self._selected_notetype_id = NoteTypeID(0)
# triggers UI update; avoid firing changed hook on startup
self.on_notetype_changed = None
self.selected_notetype_id = starting_notetype_id
@ -119,7 +121,7 @@ class NoteTypeChooser(QHBoxLayout):
self.selected_notetype_id = id
@property
def selected_notetype_id(self) -> int:
def selected_notetype_id(self) -> NoteTypeID:
# theoretically this should not be necessary, as we're listening to
# resets
self._ensure_selected_notetype_valid()
@ -127,7 +129,7 @@ class NoteTypeChooser(QHBoxLayout):
return self._selected_notetype_id
@selected_notetype_id.setter
def selected_notetype_id(self, id: int) -> None:
def selected_notetype_id(self, id: NoteTypeID) -> None:
if id != self._selected_notetype_id:
self._selected_notetype_id = id
self._ensure_selected_notetype_valid()
@ -140,7 +142,9 @@ class NoteTypeChooser(QHBoxLayout):
def _ensure_selected_notetype_valid(self) -> None:
if not self.mw.col.models.get(self._selected_notetype_id):
self.selected_notetype_id = self.mw.col.models.all_names_and_ids()[0].id
self.selected_notetype_id = NoteTypeID(
self.mw.col.models.all_names_and_ids()[0].id
)
def _update_button_label(self) -> None:
self.button.setText(self.selected_notetype_name().replace("&", "&&"))

View File

@ -9,6 +9,7 @@ from typing import Dict, Iterable, List, Optional, Tuple, cast
import aqt
from anki.collection import Config, OpChanges, SearchJoiner, SearchNode
from anki.decks import DeckID, DeckTreeNode
from anki.models import NoteTypeID
from anki.notes import Note
from anki.tags import TagTreeNode
from anki.types import assert_exhaustive
@ -1291,11 +1292,14 @@ class SidebarTreeView(QTreeView):
def manage_notetype(self, item: SidebarItem) -> None:
Models(
self.mw, parent=self.browser, fromMain=True, selected_notetype_id=item.id
self.mw,
parent=self.browser,
fromMain=True,
selected_notetype_id=NoteTypeID(item.id),
)
def manage_template(self, item: SidebarItem) -> None:
note = Note(self.col, self.col.models.get(item._parent_item.id))
note = Note(self.col, self.col.models.get(NoteTypeID(item._parent_item.id)))
CardLayout(self.mw, note, ord=item.id, parent=self, fill_empty=True)
# Helpers