diff --git a/pylib/anki/decks.py b/pylib/anki/decks.py index c2c704de4..1f93e38b1 100644 --- a/pylib/anki/decks.py +++ b/pylib/anki/decks.py @@ -24,20 +24,9 @@ DeckNameID = _pb.DeckNameID defaultDeck = 0 defaultDynamicDeck = 1 -NonFilteredDeck = Dict[str, Any] -FilteredDeck = Dict[str, Any] - -"""Any kind of deck """ -Deck = Union[NonFilteredDeck, FilteredDeck] - -"""Configuration of standard deck, as seen from the deck picker's gear.""" -Config = Dict[str, Any] - -"""Configurationf of some deck, filtered deck for filtered deck, config for standard deck""" -DeckConfig = Union[FilteredDeck, Config] - -""" New/lrn/rev conf, from deck config""" -QueueConfig = Dict[str, Any] +# type aliases until we can move away from dicts +DeckDict = Dict[str, Any] +DeckConfigDict = Dict[str, Any] DeckID = NewType("DeckID", int) @@ -87,7 +76,7 @@ class DeckManager: self.col = col.weakref() self.decks = DecksDictProxy(col) - def save(self, g: Union[Deck, Config] = None) -> None: + def save(self, g: Union[DeckDict, DeckConfigDict] = None) -> None: "Can be called with either a deck or a deck configuration." if not g: print("col.decks.save() should be passed the changed deck") @@ -121,7 +110,7 @@ class DeckManager: deck["name"] = name return self.add_deck_legacy(deck) - def add_deck_legacy(self, deck: Deck) -> OpChangesWithID: + def add_deck_legacy(self, deck: DeckDict) -> OpChangesWithID: "Add a deck created with new_deck_legacy(). Must have id of 0." assert deck["id"] == 0 return self.col._backend.add_deck_legacy(to_json_bytes(deck)) @@ -169,7 +158,7 @@ class DeckManager: except NotFoundError: return None - def get_legacy(self, did: int) -> Optional[Deck]: + def get_legacy(self, did: int) -> Optional[DeckDict]: try: return from_json_bytes(self.col._backend.get_deck_legacy(did)) except NotFoundError: @@ -178,10 +167,10 @@ class DeckManager: def have(self, id: int) -> bool: return not self.get_legacy(int(id)) - def get_all_legacy(self) -> List[Deck]: + def get_all_legacy(self) -> List[DeckDict]: return list(from_json_bytes(self.col._backend.get_all_decks_legacy()).values()) - def new_deck_legacy(self, filtered: bool) -> Deck: + def new_deck_legacy(self, filtered: bool) -> DeckDict: return from_json_bytes(self.col._backend.new_deck_legacy(filtered)) def deck_tree(self) -> DeckTreeNode: @@ -199,7 +188,7 @@ class DeckManager: return match return None - def all(self) -> List[Deck]: + def all(self) -> List[DeckDict]: "All decks. Expensive; prefer all_names_and_ids()" return self.get_all_legacy() @@ -245,7 +234,7 @@ class DeckManager: ) return count - def get(self, did: Union[int, str], default: bool = True) -> Optional[Deck]: + def get(self, did: Union[int, str], default: bool = True) -> Optional[DeckDict]: if not did: if default: return self.get_legacy(1) @@ -260,20 +249,20 @@ class DeckManager: else: return None - def byName(self, name: str) -> Optional[Deck]: + def byName(self, name: str) -> Optional[DeckDict]: """Get deck with NAME, ignoring case.""" id = self.id_for_name(name) if id: return self.get_legacy(id) return None - def update(self, g: Deck, preserve_usn: bool = True) -> None: + def update(self, g: DeckDict, preserve_usn: bool = True) -> None: "Add or update an existing deck. Used for syncing and merging." g["id"] = self.col._backend.add_or_update_deck_legacy( deck=to_json_bytes(g), preserve_usn_and_mtime=preserve_usn ) - def rename(self, deck: Union[Deck, DeckID], new_name: str) -> OpChanges: + def rename(self, deck: Union[DeckDict, DeckID], new_name: str) -> OpChanges: "Rename deck prefix to NAME if not exists. Updates children." if isinstance(deck, int): deck_id = deck @@ -306,11 +295,11 @@ class DeckManager: # Deck configurations ############################################################# - def all_config(self) -> List[Config]: + def all_config(self) -> List[DeckConfigDict]: "A list of all deck config." return list(from_json_bytes(self.col._backend.all_deck_config_legacy())) - def confForDid(self, did: int) -> DeckConfig: + def confForDid(self, did: int) -> DeckConfigDict: deck = self.get(did, default=False) assert deck if "conf" in deck: @@ -324,20 +313,20 @@ class DeckManager: # dynamic decks have embedded conf return deck - def get_config(self, conf_id: int) -> Optional[DeckConfig]: + def get_config(self, conf_id: int) -> Optional[DeckConfigDict]: try: return from_json_bytes(self.col._backend.get_deck_config_legacy(conf_id)) except NotFoundError: return None - def update_config(self, conf: DeckConfig, preserve_usn: bool = False) -> None: + def update_config(self, conf: DeckConfigDict, preserve_usn: bool = False) -> None: conf["id"] = self.col._backend.add_or_update_deck_config_legacy( config=to_json_bytes(conf), preserve_usn_and_mtime=preserve_usn ) def add_config( - self, name: str, clone_from: Optional[DeckConfig] = None - ) -> DeckConfig: + self, name: str, clone_from: Optional[DeckConfigDict] = None + ) -> DeckConfigDict: if clone_from is not None: conf = copy.deepcopy(clone_from) conf["id"] = 0 @@ -348,7 +337,7 @@ class DeckManager: return conf def add_config_returning_id( - self, name: str, clone_from: Optional[DeckConfig] = None + self, name: str, clone_from: Optional[DeckConfigDict] = None ) -> int: return self.add_config(name, clone_from)["id"] @@ -364,18 +353,18 @@ class DeckManager: self.save(g) self.col._backend.remove_deck_config(id) - def setConf(self, grp: DeckConfig, id: int) -> None: + def setConf(self, grp: DeckConfigDict, id: int) -> None: grp["conf"] = id self.save(grp) - def didsForConf(self, conf: DeckConfig) -> List[int]: + def didsForConf(self, conf: DeckConfigDict) -> List[int]: dids = [] for deck in self.all(): if "conf" in deck and deck["conf"] == conf["id"]: dids.append(deck["id"]) return dids - def restoreToDefault(self, conf: DeckConfig) -> None: + def restoreToDefault(self, conf: DeckConfigDict) -> None: oldOrder = conf["new"]["order"] new = from_json_bytes(self.col._backend.new_deck_config_legacy()) new["id"] = conf["id"] @@ -437,7 +426,7 @@ class DeckManager: "The currently selected did." return DeckID(int(self.col.conf["curDeck"])) - def current(self) -> Deck: + def current(self) -> DeckDict: return self.get(self.selected()) def select(self, did: int) -> None: @@ -481,7 +470,7 @@ class DeckManager: return None @classmethod - def key(cls, deck: Deck) -> List[str]: + def key(cls, deck: DeckDict) -> List[str]: return cls.path(deck["name"]) def children(self, did: int) -> List[Tuple[str, int]]: @@ -534,8 +523,8 @@ class DeckManager: return childMap def parents( - self, did: int, nameMap: Optional[Dict[str, Deck]] = None - ) -> List[Deck]: + self, did: int, nameMap: Optional[Dict[str, DeckDict]] = None + ) -> List[DeckDict]: "All parents of did." # get parent and grandparent names parents_names: List[str] = [] @@ -544,7 +533,7 @@ class DeckManager: parents_names.append(part) else: parents_names.append(f"{parents_names[-1]}::{part}") - parents: List[Deck] = [] + parents: List[DeckDict] = [] # convert to objects for parent_name in parents_names: if nameMap: @@ -554,13 +543,13 @@ class DeckManager: parents.append(deck) return parents - def parentsByName(self, name: str) -> List[Deck]: + def parentsByName(self, name: str) -> List[DeckDict]: "All existing parents of name" if "::" not in name: return [] names = self.immediate_parent_path(name) head = [] - parents: List[Deck] = [] + parents: List[DeckDict] = [] while names: head.append(names.pop(0)) @@ -570,7 +559,7 @@ class DeckManager: return parents - def nameMap(self) -> Dict[str, Deck]: + def nameMap(self) -> Dict[str, DeckDict]: return {d["name"]: d for d in self.all()} # Dynamic decks diff --git a/pylib/anki/scheduler/base.py b/pylib/anki/scheduler/base.py index 82201c7ad..8177f7d1d 100644 --- a/pylib/anki/scheduler/base.py +++ b/pylib/anki/scheduler/base.py @@ -14,7 +14,7 @@ SchedTimingToday = _pb.SchedTimingTodayOut from typing import List, Optional, Sequence from anki.consts import CARD_TYPE_NEW, NEW_CARDS_RANDOM, QUEUE_TYPE_NEW, QUEUE_TYPE_REV -from anki.decks import DeckConfig, DeckTreeNode +from anki.decks import DeckConfigDict, DeckTreeNode from anki.notes import Note from anki.utils import ids2str, intTime @@ -195,7 +195,7 @@ select id from cards where did in %s and queue = {QUEUE_TYPE_REV} and due <= ? l def orderCards(self, did: int) -> None: self.col._backend.sort_deck(deck_id=did, randomize=False) - def resortConf(self, conf: DeckConfig) -> None: + def resortConf(self, conf: DeckConfigDict) -> None: for did in self.col.decks.didsForConf(conf): if conf["new"]["order"] == 0: self.randomizeCards(did) diff --git a/pylib/anki/scheduler/legacy.py b/pylib/anki/scheduler/legacy.py index a00ad1d0b..db13bd715 100644 --- a/pylib/anki/scheduler/legacy.py +++ b/pylib/anki/scheduler/legacy.py @@ -5,7 +5,7 @@ from typing import List, Optional, Tuple from anki.cards import Card from anki.consts import CARD_TYPE_RELEARNING, QUEUE_TYPE_DAY_LEARN_RELEARN -from anki.decks import DeckConfig +from anki.decks import DeckConfigDict from anki.scheduler.base import SchedulerBase, UnburyCurrentDeck from anki.utils import from_json_bytes, ids2str @@ -115,7 +115,7 @@ due = (case when odue>0 then odue else due end), odue = 0, odid = 0, usn = ? whe # legacy in v3 but used by unit tests; redefined in v2/v1 - def _cardConf(self, card: Card) -> DeckConfig: + def _cardConf(self, card: Card) -> DeckConfigDict: return self.col.decks.confForDid(card.did) def _fuzzIvlRange(self, ivl: int) -> Tuple[int, int]: diff --git a/pylib/anki/scheduler/v1.py b/pylib/anki/scheduler/v1.py index ba8ed282d..6aec92fab 100644 --- a/pylib/anki/scheduler/v1.py +++ b/pylib/anki/scheduler/v1.py @@ -12,9 +12,9 @@ import anki from anki import hooks from anki.cards import Card from anki.consts import * -from anki.decks import QueueConfig from anki.utils import ids2str, intTime +from .v2 import QueueConfig from .v2 import Scheduler as V2 # queue types: 0=new/cram, 1=lrn, 2=rev, 3=day lrn, -1=suspended, -2=buried diff --git a/pylib/anki/scheduler/v2.py b/pylib/anki/scheduler/v2.py index 2c124add6..b0ec0467c 100644 --- a/pylib/anki/scheduler/v2.py +++ b/pylib/anki/scheduler/v2.py @@ -13,7 +13,7 @@ import anki._backend.backend_pb2 as _pb from anki import hooks from anki.cards import Card from anki.consts import * -from anki.decks import Deck, DeckConfig, QueueConfig +from anki.decks import DeckConfigDict, DeckDict from anki.lang import FormatTimeSpan from anki.scheduler.legacy import SchedulerBaseWithLegacy from anki.utils import ids2str, intTime @@ -21,6 +21,9 @@ from anki.utils import ids2str, intTime CountsForDeckToday = _pb.CountsForDeckTodayOut SchedTimingToday = _pb.SchedTimingTodayOut +# legacy type alias +QueueConfig = Dict[str, Any] + # card types: 0=new, 1=lrn, 2=rev, 3=relrn # queue types: 0=new, 1=(re)lrn, 2=rev, 3=day (re)lrn, # 4=preview, -1=suspended, -2=sibling buried, -3=manually buried @@ -207,7 +210,7 @@ class Scheduler(SchedulerBaseWithLegacy): return None def _deckNewLimit( - self, did: int, fn: Optional[Callable[[Deck], int]] = None + self, did: int, fn: Optional[Callable[[DeckDict], int]] = None ) -> int: if not fn: fn = self._deckNewLimitSingle @@ -235,7 +238,7 @@ select count() from lim, ) - def _deckNewLimitSingle(self, g: DeckConfig) -> int: + def _deckNewLimitSingle(self, g: DeckConfigDict) -> int: "Limit for deck without parent limits." if g["dyn"]: return self.dynReportLimit @@ -490,7 +493,7 @@ limit ?""" if card.odue: card.odue = 0 - def _cardConf(self, card: Card) -> DeckConfig: + def _cardConf(self, card: Card) -> DeckConfigDict: return self.col.decks.confForDid(card.did) def _deckLimit(self) -> str: diff --git a/pylib/tools/genhooks.py b/pylib/tools/genhooks.py index 2997c0af2..a4d32c2ec 100644 --- a/pylib/tools/genhooks.py +++ b/pylib/tools/genhooks.py @@ -76,21 +76,23 @@ hooks = [ ), Hook( name="scheduler_new_limit_for_single_deck", - args=["count: int", "deck: anki.decks.Deck"], + args=["count: int", "deck: anki.decks.DeckDict"], return_type="int", doc="""Allows changing the number of new card for this deck (without considering descendants).""", ), Hook( name="scheduler_review_limit_for_single_deck", - args=["count: int", "deck: anki.decks.Deck"], + args=["count: int", "deck: anki.decks.DeckDict"], return_type="int", doc="""Allows changing the number of rev card for this deck (without considering descendants).""", ), # obsolete Hook( - name="deck_added", args=["deck: anki.decks.Deck"], doc="Obsolete, do not use." + name="deck_added", + args=["deck: anki.decks.DeckDict"], + doc="Obsolete, do not use.", ), Hook( name="note_type_added", diff --git a/qt/aqt/deckconf.py b/qt/aqt/deckconf.py index 449886017..428bb6a98 100644 --- a/qt/aqt/deckconf.py +++ b/qt/aqt/deckconf.py @@ -7,7 +7,7 @@ from PyQt5.QtWidgets import QLineEdit import aqt from anki.consts import NEW_CARDS_RANDOM -from anki.decks import DeckConfig +from anki.decks import DeckConfigDict from anki.lang import without_unicode_isolation from aqt import gui_hooks from aqt.qt import * @@ -72,7 +72,7 @@ class DeckConf(QDialog): def setupConfs(self) -> None: qconnect(self.form.dconf.currentIndexChanged, self.onConfChange) - self.conf: Optional[DeckConfig] = None + self.conf: Optional[DeckConfigDict] = None self.loadConfs() def loadConfs(self) -> None: diff --git a/qt/aqt/filtered_deck.py b/qt/aqt/filtered_deck.py index 58b01c04f..dc6b815f4 100644 --- a/qt/aqt/filtered_deck.py +++ b/qt/aqt/filtered_deck.py @@ -4,7 +4,7 @@ from typing import Callable, List, Optional, Tuple import aqt from anki.collection import SearchNode -from anki.decks import Deck +from anki.decks import DeckDict from anki.errors import DeckIsFilteredError, InvalidInput from anki.lang import without_unicode_isolation from aqt import AnkiQt, colors, gui_hooks @@ -32,7 +32,7 @@ class FilteredDeckConfigDialog(QDialog): mw: AnkiQt, search: Optional[str] = None, search_2: Optional[str] = None, - deck: Optional[Deck] = None, + deck: Optional[DeckDict] = None, ) -> None: """If 'deck' is an existing filtered deck, load and modify its settings. Otherwise, build a new one and derive settings from the current deck. @@ -99,7 +99,7 @@ class FilteredDeckConfigDialog(QDialog): _mw: AnkiQt, search: Optional[str] = None, search_2: Optional[str] = None, - _deck: Optional[Deck] = None, + _deck: Optional[DeckDict] = None, ) -> None: self.set_custom_searches(search, search_2) @@ -222,7 +222,7 @@ class FilteredDeckConfigDialog(QDialog): not self.form.resched.isChecked() and self.col.schedVer() > 1 ) - def loadConf(self, deck: Optional[Deck] = None) -> None: + def loadConf(self, deck: Optional[DeckDict] = None) -> None: f = self.form d = deck or self.deck diff --git a/qt/aqt/main.py b/qt/aqt/main.py index fd64fb22b..7de881ce7 100644 --- a/qt/aqt/main.py +++ b/qt/aqt/main.py @@ -52,7 +52,7 @@ from anki.collection import ( UndoResult, UndoStatus, ) -from anki.decks import Deck +from anki.decks import DeckDict from anki.hooks import runHook from anki.sound import AVTag, SoundOrVideoTag from anki.types import assert_exhaustive @@ -695,7 +695,7 @@ class AnkiQt(QMainWindow): self.maybe_check_for_addon_updates() self.deckBrowser.show() - def _selectedDeck(self) -> Optional[Deck]: + def _selectedDeck(self) -> Optional[DeckDict]: did = self.col.decks.selected() if not self.col.decks.name_if_exists(did): showInfo(tr(TR.QT_MISC_PLEASE_SELECT_A_DECK)) @@ -1319,7 +1319,7 @@ title="%s" %s>%s""" % ( def onEditCurrent(self) -> None: aqt.dialogs.open("EditCurrent", self) - def onDeckConf(self, deck: Optional[Deck] = None) -> None: + def onDeckConf(self, deck: Optional[DeckDict] = None) -> None: import aqt.deckconf if not deck: diff --git a/qt/tools/genhooks_gui.py b/qt/tools/genhooks_gui.py index 8487e8a2e..0bd26f060 100644 --- a/qt/tools/genhooks_gui.py +++ b/qt/tools/genhooks_gui.py @@ -21,7 +21,7 @@ from typing import Any, Callable, List, Sequence, Tuple, Optional, Union import anki import aqt from anki.cards import Card -from anki.decks import Deck, DeckConfig +from anki.decks import DeckDict, DeckConfigDict from anki.hooks import runFilter, runHook from anki.models import NoteType from aqt.qt import QDialog, QEvent, QMenu, QWidget @@ -229,20 +229,28 @@ hooks = [ ), Hook( name="deck_conf_did_load_config", - args=["deck_conf: aqt.deckconf.DeckConf", "deck: Deck", "config: DeckConfig"], + args=[ + "deck_conf: aqt.deckconf.DeckConf", + "deck: DeckDict", + "config: DeckConfigDict", + ], doc="Called once widget state has been set from deck config", ), Hook( name="deck_conf_will_save_config", - args=["deck_conf: aqt.deckconf.DeckConf", "deck: Deck", "config: DeckConfig"], + args=[ + "deck_conf: aqt.deckconf.DeckConf", + "deck: DeckDict", + "config: DeckConfigDict", + ], doc="Called before widget state is saved to config", ), Hook( name="deck_conf_did_add_config", args=[ "deck_conf: aqt.deckconf.DeckConf", - "deck: Deck", - "config: DeckConfig", + "deck: DeckDict", + "config: DeckConfigDict", "new_name: str", "new_conf_id: int", ], @@ -259,15 +267,19 @@ hooks = [ ), Hook( name="deck_conf_will_remove_config", - args=["deck_conf: aqt.deckconf.DeckConf", "deck: Deck", "config: DeckConfig"], + args=[ + "deck_conf: aqt.deckconf.DeckConf", + "deck: DeckDict", + "config: DeckConfigDict", + ], doc="Called before current config group is removed", ), Hook( name="deck_conf_will_rename_config", args=[ "deck_conf: aqt.deckconf.DeckConf", - "deck: Deck", - "config: DeckConfig", + "deck: DeckDict", + "config: DeckConfigDict", "new_name: str", ], doc="Called before config group is renamed",