NF: DeckID type

This commit is contained in:
Arthur Milchior 2021-03-23 10:53:47 +01:00 committed by Damien Elmes
parent 08e13013b2
commit 6ac1e6477e
18 changed files with 112 additions and 95 deletions

View File

@ -37,6 +37,8 @@ class Card:
ord: int ord: int
nid: anki.notes.NoteID nid: anki.notes.NoteID
id: CardID id: CardID
did: anki.decks.DeckID
odid: anki.decks.DeckID
def __init__( def __init__(
self, col: anki.collection.Collection, id: Optional[CardID] = None self, col: anki.collection.Collection, id: Optional[CardID] = None
@ -62,7 +64,7 @@ class Card:
self._note = None self._note = None
self.id = CardID(c.id) self.id = CardID(c.id)
self.nid = anki.notes.NoteID(c.note_id) self.nid = anki.notes.NoteID(c.note_id)
self.did = c.deck_id self.did = anki.decks.DeckID(c.deck_id)
self.ord = c.template_idx self.ord = c.template_idx
self.mod = c.mtime_secs self.mod = c.mtime_secs
self.usn = c.usn self.usn = c.usn
@ -75,7 +77,7 @@ class Card:
self.lapses = c.lapses self.lapses = c.lapses
self.left = c.remaining_steps self.left = c.remaining_steps
self.odue = c.original_due self.odue = c.original_due
self.odid = c.original_deck_id self.odid = anki.decks.DeckID(c.original_deck_id)
self.flags = c.flags self.flags = c.flags
self.data = c.data self.data = c.data
@ -163,7 +165,7 @@ class Card:
def startTimer(self) -> None: def startTimer(self) -> None:
self.timerStarted = time.time() self.timerStarted = time.time()
def currentDeckID(self) -> int: def currentDeckID(self) -> anki.decks.DeckID:
return anki.decks.DeckID(self.odid or self.did) return anki.decks.DeckID(self.odid or self.did)
def timeLimit(self) -> int: def timeLimit(self) -> int:

View File

@ -38,7 +38,7 @@ from anki.cards import Card, CardID
from anki.config import Config, ConfigManager from anki.config import Config, ConfigManager
from anki.consts import * from anki.consts import *
from anki.dbproxy import DBProxy from anki.dbproxy import DBProxy
from anki.decks import DeckManager from anki.decks import DeckID, DeckManager
from anki.errors import AnkiError, DBError from anki.errors import AnkiError, DBError
from anki.lang import TR, FormatTimeSpan from anki.lang import TR, FormatTimeSpan
from anki.media import MediaManager, media_paths_from_col_path from anki.media import MediaManager, media_paths_from_col_path
@ -370,7 +370,7 @@ class Collection:
def new_note(self, notetype: NoteType) -> Note: def new_note(self, notetype: NoteType) -> Note:
return Note(self, notetype) return Note(self, notetype)
def add_note(self, note: Note, deck_id: int) -> OpChanges: def add_note(self, note: Note, deck_id: DeckID) -> OpChanges:
out = self._backend.add_note(note=note._to_backend_note(), deck_id=deck_id) out = self._backend.add_note(note=note._to_backend_note(), deck_id=deck_id)
note.id = NoteID(out.note_id) note.id = NoteID(out.note_id)
return out.changes return out.changes
@ -400,21 +400,23 @@ class Collection:
if card := current_review_card: if card := current_review_card:
home_deck = card.currentDeckID() home_deck = card.currentDeckID()
else: else:
home_deck = 0 home_deck = DeckID(0)
return self._backend.defaults_for_adding( return self._backend.defaults_for_adding(
home_deck_of_current_review_card=home_deck, home_deck_of_current_review_card=home_deck,
) )
def default_deck_for_notetype(self, notetype_id: NoteID) -> Optional[int]: def default_deck_for_notetype(self, notetype_id: NoteID) -> Optional[DeckID]:
"""If 'change deck depending on notetype' is enabled in the preferences, """If 'change deck depending on notetype' is enabled in the preferences,
return the last deck used with the provided notetype, if any..""" return the last deck used with the provided notetype, if any.."""
if self.get_config_bool(Config.Bool.ADDING_DEFAULTS_TO_CURRENT_DECK): if self.get_config_bool(Config.Bool.ADDING_DEFAULTS_TO_CURRENT_DECK):
return None return None
return ( return (
self._backend.default_deck_for_notetype( DeckID(
ntid=notetype_id, self._backend.default_deck_for_notetype(
ntid=notetype_id,
)
) )
or None or None
) )

View File

@ -31,7 +31,7 @@ DeckDict = Dict[str, Any]
DeckConfigDict = Dict[str, Any] DeckConfigDict = Dict[str, Any]
DeckID = NewType("DeckID", int) DeckID = NewType("DeckID", int)
default_deck_id = 1 default_deck_id = DeckID(1)
default_deck_conf_id = 1 default_deck_conf_id = 1
@ -45,7 +45,7 @@ class DecksDictProxy:
def __getitem__(self, item: Any) -> Any: def __getitem__(self, item: Any) -> Any:
self._warn() self._warn()
return self._col.decks.get(int(item)) return self._col.decks.get(DeckID(int(item)))
def __setitem__(self, key: Any, val: Any) -> None: def __setitem__(self, key: Any, val: Any) -> None:
self._warn() self._warn()
@ -124,7 +124,7 @@ class DeckManager:
name: str, name: str,
create: bool = True, create: bool = True,
type: int = 0, type: int = 0,
) -> Optional[int]: ) -> Optional[DeckID]:
"Add a deck with NAME. Reuse deck if already exists. Return id as int." "Add a deck with NAME. Reuse deck if already exists. Return id as int."
id = self.id_for_name(name) id = self.id_for_name(name)
if id: if id:
@ -135,17 +135,17 @@ class DeckManager:
deck = self.new_deck_legacy(bool(type)) deck = self.new_deck_legacy(bool(type))
deck["name"] = name deck["name"] = name
out = self.add_deck_legacy(deck) out = self.add_deck_legacy(deck)
return out.id return DeckID(out.id)
@legacy_func(sub="remove") @legacy_func(sub="remove")
def rem(self, did: int, cardsToo: bool = True, childrenToo: bool = True) -> None: def rem(self, did: DeckID, cardsToo: bool = True, childrenToo: bool = True) -> None:
"Remove the deck. If cardsToo, delete any cards inside." "Remove the deck. If cardsToo, delete any cards inside."
if isinstance(did, str): if isinstance(did, str):
did = int(did) did = int(did)
assert cardsToo and childrenToo assert cardsToo and childrenToo
self.remove([did]) self.remove([did])
def remove(self, dids: Sequence[int]) -> OpChangesWithCount: def remove(self, dids: Sequence[DeckID]) -> OpChangesWithCount:
return self.col._backend.remove_decks(dids) return self.col._backend.remove_decks(dids)
def all_names_and_ids( def all_names_and_ids(
@ -156,20 +156,20 @@ class DeckManager:
skip_empty_default=skip_empty_default, include_filtered=include_filtered skip_empty_default=skip_empty_default, include_filtered=include_filtered
) )
def id_for_name(self, name: str) -> Optional[int]: def id_for_name(self, name: str) -> Optional[DeckID]:
try: try:
return self.col._backend.get_deck_id_by_name(name) return DeckID(self.col._backend.get_deck_id_by_name(name))
except NotFoundError: except NotFoundError:
return None return None
def get_legacy(self, did: int) -> Optional[DeckDict]: def get_legacy(self, did: DeckID) -> Optional[DeckDict]:
try: try:
return from_json_bytes(self.col._backend.get_deck_legacy(did)) return from_json_bytes(self.col._backend.get_deck_legacy(did))
except NotFoundError: except NotFoundError:
return None return None
def have(self, id: int) -> bool: def have(self, id: DeckID) -> bool:
return not self.get_legacy(int(id)) return not self.get_legacy(id)
def get_all_legacy(self) -> List[DeckDict]: def get_all_legacy(self) -> List[DeckDict]:
return list(from_json_bytes(self.col._backend.get_all_decks_legacy()).values()) return list(from_json_bytes(self.col._backend.get_all_decks_legacy()).values())
@ -191,7 +191,7 @@ class DeckManager:
@classmethod @classmethod
def find_deck_in_tree( def find_deck_in_tree(
cls, node: DeckTreeNode, deck_id: int cls, node: DeckTreeNode, deck_id: DeckID
) -> Optional[DeckTreeNode]: ) -> Optional[DeckTreeNode]:
if node.deck_id == deck_id: if node.deck_id == deck_id:
return node return node
@ -218,12 +218,12 @@ class DeckManager:
) )
] ]
def collapse(self, did: int) -> None: def collapse(self, did: DeckID) -> None:
deck = self.get(did) deck = self.get(did)
deck["collapsed"] = not deck["collapsed"] deck["collapsed"] = not deck["collapsed"]
self.save(deck) self.save(deck)
def collapseBrowser(self, did: int) -> None: def collapseBrowser(self, did: DeckID) -> None:
deck = self.get(did) deck = self.get(did)
collapsed = deck.get("browserCollapsed", False) collapsed = deck.get("browserCollapsed", False)
deck["browserCollapsed"] = not collapsed deck["browserCollapsed"] = not collapsed
@ -233,7 +233,7 @@ class DeckManager:
return len(self.all_names_and_ids()) return len(self.all_names_and_ids())
def card_count( def card_count(
self, dids: Union[int, Iterable[int]], include_subdecks: bool self, dids: Union[DeckID, Iterable[DeckID]], include_subdecks: bool
) -> Any: ) -> Any:
if isinstance(dids, int): if isinstance(dids, int):
dids = {dids} dids = {dids}
@ -247,13 +247,13 @@ class DeckManager:
) )
return count return count
def get(self, did: Union[int, str], default: bool = True) -> Optional[DeckDict]: def get(self, did: Union[DeckID, str], default: bool = True) -> Optional[DeckDict]:
if not did: if not did:
if default: if default:
return self.get_legacy(default_deck_id) return self.get_legacy(default_deck_id)
else: else:
return None return None
id = int(did) id = DeckID(int(did))
deck = self.get_legacy(id) deck = self.get_legacy(id)
if deck: if deck:
return deck return deck
@ -297,7 +297,9 @@ class DeckManager:
# legacy # legacy
def renameForDragAndDrop( def renameForDragAndDrop(
self, draggedDeckDid: Union[int, str], ontoDeckDid: Optional[Union[int, str]] self,
draggedDeckDid: Union[DeckID, str],
ontoDeckDid: Optional[Union[DeckID, str]],
) -> None: ) -> None:
if not ontoDeckDid: if not ontoDeckDid:
onto = 0 onto = 0
@ -312,7 +314,7 @@ class DeckManager:
"A list of all deck config." "A list of all deck config."
return list(from_json_bytes(self.col._backend.all_deck_config_legacy())) return list(from_json_bytes(self.col._backend.all_deck_config_legacy()))
def confForDid(self, did: int) -> DeckConfigDict: def confForDid(self, did: DeckID) -> DeckConfigDict:
deck = self.get(did, default=False) deck = self.get(did, default=False)
assert deck assert deck
if "conf" in deck: if "conf" in deck:
@ -370,7 +372,7 @@ class DeckManager:
grp["conf"] = id grp["conf"] = id
self.save(grp) self.save(grp)
def didsForConf(self, conf: DeckConfigDict) -> List[int]: def didsForConf(self, conf: DeckConfigDict) -> List[DeckID]:
dids = [] dids = []
for deck in self.all(): for deck in self.all():
if "conf" in deck and deck["conf"] == conf["id"]: if "conf" in deck and deck["conf"] == conf["id"]:
@ -397,19 +399,19 @@ class DeckManager:
# Deck utils # Deck utils
############################################################# #############################################################
def name(self, did: int, default: bool = False) -> str: def name(self, did: DeckID, default: bool = False) -> str:
deck = self.get(did, default=default) deck = self.get(did, default=default)
if deck: if deck:
return deck["name"] return deck["name"]
return self.col.tr(TR.DECKS_NO_DECK) return self.col.tr(TR.DECKS_NO_DECK)
def name_if_exists(self, did: int) -> Optional[str]: def name_if_exists(self, did: DeckID) -> Optional[str]:
deck = self.get(did, default=False) deck = self.get(did, default=False)
if deck: if deck:
return deck["name"] return deck["name"]
return None return None
def setDeck(self, cids: List[CardID], did: int) -> None: def setDeck(self, cids: List[CardID], did: DeckID) -> None:
self.col.db.execute( self.col.db.execute(
f"update cards set did=?,usn=?,mod=? where id in {ids2str(cids)}", f"update cards set did=?,usn=?,mod=? where id in {ids2str(cids)}",
did, did,
@ -417,7 +419,7 @@ class DeckManager:
intTime(), intTime(),
) )
def cids(self, did: int, children: bool = False) -> List[CardID]: def cids(self, did: DeckID, children: bool = False) -> List[CardID]:
if not children: if not children:
return self.col.db.list("select id from cards where did=?", did) return self.col.db.list("select id from cards where did=?", did)
dids = [did] dids = [did]
@ -425,13 +427,13 @@ class DeckManager:
dids.append(id) dids.append(id)
return self.col.db.list(f"select id from cards where did in {ids2str(dids)}") return self.col.db.list(f"select id from cards where did in {ids2str(dids)}")
def for_card_ids(self, cids: List[CardID]) -> List[int]: def for_card_ids(self, cids: List[CardID]) -> List[DeckID]:
return self.col.db.list(f"select did from cards where id in {ids2str(cids)}") return self.col.db.list(f"select did from cards where id in {ids2str(cids)}")
# Deck selection # Deck selection
############################################################# #############################################################
def active(self) -> List[int]: def active(self) -> List[DeckID]:
"The currrently active dids." "The currrently active dids."
return self.col.get_config("activeDecks", [1]) return self.col.get_config("activeDecks", [1])
@ -442,10 +444,9 @@ class DeckManager:
def current(self) -> DeckDict: def current(self) -> DeckDict:
return self.get(self.selected()) return self.get(self.selected())
def select(self, did: int) -> None: def select(self, did: DeckID) -> None:
"Select a new branch." "Select a new branch."
# make sure arg is an int # make sure arg is an int
did = int(did)
current = self.selected() current = self.selected()
active = self.deck_and_child_ids(did) active = self.deck_and_child_ids(did)
if current != did or active != self.active(): if current != did or active != self.active():
@ -486,29 +487,31 @@ class DeckManager:
def key(cls, deck: DeckDict) -> List[str]: def key(cls, deck: DeckDict) -> List[str]:
return cls.path(deck["name"]) return cls.path(deck["name"])
def children(self, did: int) -> List[Tuple[str, int]]: def children(self, did: DeckID) -> List[Tuple[str, DeckID]]:
"All children of did, as (name, id)." "All children of did, as (name, id)."
name = self.get(did)["name"] name = self.get(did)["name"]
actv = [] actv = []
for g in self.all_names_and_ids(): for g in self.all_names_and_ids():
if g.name.startswith(f"{name}::"): if g.name.startswith(f"{name}::"):
actv.append((g.name, g.id)) actv.append((g.name, DeckID(g.id)))
return actv return actv
def child_ids(self, parent_name: str) -> Iterable[int]: def child_ids(self, parent_name: str) -> Iterable[DeckID]:
prefix = f"{parent_name}::" prefix = f"{parent_name}::"
return (d.id for d in self.all_names_and_ids() if d.name.startswith(prefix)) return (
DeckID(d.id) for d in self.all_names_and_ids() if d.name.startswith(prefix)
)
def deck_and_child_ids(self, deck_id: int) -> List[int]: def deck_and_child_ids(self, deck_id: DeckID) -> List[DeckID]:
parent_name = self.get_legacy(deck_id)["name"] parent_name = self.get_legacy(deck_id)["name"]
out = [deck_id] out = [deck_id]
out.extend(self.child_ids(parent_name)) out.extend(self.child_ids(parent_name))
return out return out
childMapNode = Dict[int, Any] childMapNode = Dict[DeckID, Any]
# Change to Dict[int, "DeckManager.childMapNode"] when MyPy allow recursive type # Change to Dict[int, "DeckManager.childMapNode"] when MyPy allow recursive type
def childDids(self, did: int, childMap: DeckManager.childMapNode) -> List: def childDids(self, did: DeckID, childMap: DeckManager.childMapNode) -> List:
def gather(node: DeckManager.childMapNode, arr: List) -> None: def gather(node: DeckManager.childMapNode, arr: List) -> None:
for did, child in node.items(): for did, child in node.items():
arr.append(did) arr.append(did)
@ -536,7 +539,7 @@ class DeckManager:
return childMap return childMap
def parents( def parents(
self, did: int, nameMap: Optional[Dict[str, DeckDict]] = None self, did: DeckID, nameMap: Optional[Dict[str, DeckDict]] = None
) -> List[DeckDict]: ) -> List[DeckDict]:
"All parents of did." "All parents of did."
# get parent and grandparent names # get parent and grandparent names
@ -578,14 +581,14 @@ class DeckManager:
# Dynamic decks # Dynamic decks
########################################################################## ##########################################################################
def new_filtered(self, name: str) -> int: def new_filtered(self, name: str) -> DeckID:
"Return a new dynamic deck and set it as the current deck." "Return a new dynamic deck and set it as the current deck."
did = self.id(name, type=default_deck_conf_id) did = self.id(name, type=default_deck_conf_id)
self.select(did) self.select(did)
return did return did
# 1 for dyn, 0 for standard # 1 for dyn, 0 for standard
def isDyn(self, did: Union[int, str]) -> int: def isDyn(self, did: Union[DeckID, str]) -> int:
return self.get(did)["dyn"] return self.get(did)["dyn"]
# legacy # legacy

View File

@ -14,6 +14,7 @@ from zipfile import ZipFile
from anki import hooks from anki import hooks
from anki.cards import CardID from anki.cards import CardID
from anki.collection import Collection from anki.collection import Collection
from anki.decks import DeckID
from anki.lang import TR from anki.lang import TR
from anki.utils import ids2str, namedtmp, splitFields, stripHTML from anki.utils import ids2str, namedtmp, splitFields, stripHTML
@ -28,7 +29,7 @@ class Exporter:
def __init__( def __init__(
self, self,
col: Collection, col: Collection,
did: Optional[int] = None, did: Optional[DeckID] = None,
cids: Optional[List[CardID]] = None, cids: Optional[List[CardID]] = None,
) -> None: ) -> None:
self.col = col.weakref() self.col = col.weakref()
@ -185,7 +186,7 @@ class AnkiExporter(Exporter):
def key(col: Collection) -> str: def key(col: Collection) -> str:
return col.tr(TR.EXPORTING_ANKI_20_DECK) return col.tr(TR.EXPORTING_ANKI_20_DECK)
def deckIds(self) -> List[int]: def deckIds(self) -> List[DeckID]:
if self.cids: if self.cids:
return self.col.decks.for_card_ids(self.cids) return self.col.decks.for_card_ids(self.cids)
elif self.did: elif self.did:

View File

@ -8,7 +8,7 @@ from typing import Any, Dict, List, Optional, Tuple
from anki.cards import CardID from anki.cards import CardID
from anki.collection import Collection from anki.collection import Collection
from anki.consts import * from anki.consts import *
from anki.decks import DeckManager from anki.decks import DeckID, DeckManager
from anki.importing.base import Importer from anki.importing.base import Importer
from anki.lang import TR from anki.lang import TR
from anki.notes import NoteID from anki.notes import NoteID
@ -31,7 +31,7 @@ class Anki2Importer(Importer):
super().__init__(col, file) super().__init__(col, file)
# set later, defined here for typechecking # set later, defined here for typechecking
self._decks: Dict[int, int] = {} self._decks: Dict[DeckID, DeckID] = {}
self.source_needs_upgrade = False self.source_needs_upgrade = False
def run(self, media: None = None) -> None: def run(self, media: None = None) -> None:
@ -256,7 +256,7 @@ class Anki2Importer(Importer):
# Decks # Decks
###################################################################### ######################################################################
def _did(self, did: int) -> Any: def _did(self, did: DeckID) -> Any:
"Given did in src col, return local id." "Given did in src col, return local id."
# already converted? # already converted?
if did in self._decks: if did in self._decks:

View File

@ -91,10 +91,10 @@ select id from cards where did in %s and queue = {QUEUE_TYPE_REV} and due <= ? l
# Filtered deck handling # Filtered deck handling
########################################################################## ##########################################################################
def rebuild_filtered_deck(self, deck_id: int) -> OpChangesWithCount: def rebuild_filtered_deck(self, deck_id: DeckID) -> OpChangesWithCount:
return self.col._backend.rebuild_filtered_deck(deck_id) return self.col._backend.rebuild_filtered_deck(deck_id)
def empty_filtered_deck(self, deck_id: int) -> OpChanges: def empty_filtered_deck(self, deck_id: DeckID) -> OpChanges:
return self.col._backend.empty_filtered_deck(deck_id) return self.col._backend.empty_filtered_deck(deck_id)
def get_or_create_filtered_deck(self, deck_id: DeckID) -> FilteredDeckForUpdate: def get_or_create_filtered_deck(self, deck_id: DeckID) -> FilteredDeckForUpdate:
@ -199,10 +199,10 @@ select id from cards where did in %s and queue = {QUEUE_TYPE_REV} and due <= ? l
shift_existing=shift_existing, shift_existing=shift_existing,
) )
def randomizeCards(self, did: int) -> None: def randomizeCards(self, did: DeckID) -> None:
self.col._backend.sort_deck(deck_id=did, randomize=True) self.col._backend.sort_deck(deck_id=did, randomize=True)
def orderCards(self, did: int) -> None: def orderCards(self, did: DeckID) -> None:
self.col._backend.sort_deck(deck_id=did, randomize=False) self.col._backend.sort_deck(deck_id=did, randomize=False)
def resortConf(self, conf: DeckConfigDict) -> None: def resortConf(self, conf: DeckConfigDict) -> None:
@ -213,7 +213,7 @@ select id from cards where did in %s and queue = {QUEUE_TYPE_REV} and due <= ? l
self.orderCards(did) self.orderCards(did)
# for post-import # for post-import
def maybeRandomizeDeck(self, did: Optional[int] = None) -> None: def maybeRandomizeDeck(self, did: Optional[DeckID] = None) -> None:
if not did: if not did:
did = self.col.decks.selected() did = self.col.decks.selected()
conf = self.col.decks.confForDid(did) conf = self.col.decks.confForDid(did)

View File

@ -5,7 +5,7 @@ from typing import List, Optional, Tuple
from anki.cards import Card, CardID from anki.cards import Card, CardID
from anki.consts import CARD_TYPE_RELEARNING, QUEUE_TYPE_DAY_LEARN_RELEARN from anki.consts import CARD_TYPE_RELEARNING, QUEUE_TYPE_DAY_LEARN_RELEARN
from anki.decks import DeckConfigDict from anki.decks import DeckConfigDict, DeckID
from anki.notes import NoteID from anki.notes import NoteID
from anki.scheduler.base import SchedulerBase, UnburyCurrentDeck from anki.scheduler.base import SchedulerBase, UnburyCurrentDeck
from anki.utils import from_json_bytes, ids2str from anki.utils import from_json_bytes, ids2str
@ -49,7 +49,7 @@ class SchedulerBaseWithLegacy(SchedulerBase):
print("_nextDueMsg() is obsolete") print("_nextDueMsg() is obsolete")
return "" return ""
def rebuildDyn(self, did: Optional[int] = None) -> Optional[int]: def rebuildDyn(self, did: Optional[DeckID] = None) -> Optional[int]:
did = did or self.col.decks.selected() did = did or self.col.decks.selected()
count = self.rebuild_filtered_deck(did).count or None count = self.rebuild_filtered_deck(did).count or None
if not count: if not count:
@ -58,7 +58,7 @@ class SchedulerBaseWithLegacy(SchedulerBase):
self.col.decks.select(did) self.col.decks.select(did)
return count return count
def emptyDyn(self, did: Optional[int], lim: Optional[str] = None) -> None: def emptyDyn(self, did: Optional[DeckID], lim: Optional[str] = None) -> None:
if lim is None: if lim is None:
self.empty_filtered_deck(did) self.empty_filtered_deck(did)
return return
@ -86,7 +86,7 @@ due = (case when odue>0 then odue else due end), odue = 0, odid = 0, usn = ? whe
# used by v2 scheduler and some add-ons # used by v2 scheduler and some add-ons
def update_stats( def update_stats(
self, self,
deck_id: int, deck_id: DeckID,
new_delta: int = 0, new_delta: int = 0,
review_delta: int = 0, review_delta: int = 0,
milliseconds_delta: int = 0, milliseconds_delta: int = 0,

View File

@ -12,6 +12,7 @@ import anki
from anki import hooks from anki import hooks
from anki.cards import Card from anki.cards import Card
from anki.consts import * from anki.consts import *
from anki.decks import DeckID
from anki.utils import ids2str, intTime from anki.utils import ids2str, intTime
from .v2 import QueueConfig from .v2 import QueueConfig
@ -299,7 +300,7 @@ limit %d"""
if card.odid: if card.odid:
card.did = card.odid card.did = card.odid
card.odue = 0 card.odue = 0
card.odid = 0 card.odid = DeckID(0)
# if rescheduling is off, it needs to be set back to a new card # if rescheduling is off, it needs to be set back to a new card
if not resched and not lapse: if not resched and not lapse:
card.queue = card.type = CARD_TYPE_NEW card.queue = card.type = CARD_TYPE_NEW
@ -401,7 +402,7 @@ where queue in ({QUEUE_TYPE_LRN},{QUEUE_TYPE_DAY_LEARN_RELEARN}) and type = {CAR
) )
) )
def _lrnForDeck(self, did: int) -> int: def _lrnForDeck(self, did: DeckID) -> int:
cnt = ( cnt = (
self.col.db.scalar( self.col.db.scalar(
f""" f"""
@ -426,7 +427,7 @@ and due <= ? limit ?)""",
# Reviews # Reviews
########################################################################## ##########################################################################
def _deckRevLimit(self, did: int) -> int: def _deckRevLimit(self, did: DeckID) -> int:
return self._deckNewLimit(did, self._deckRevLimitSingle) return self._deckNewLimit(did, self._deckRevLimitSingle)
def _resetRev(self) -> None: def _resetRev(self) -> None:
@ -541,7 +542,7 @@ did = ? and queue = {QUEUE_TYPE_REV} and due <= ? limit ?""",
card.due = card.odue card.due = card.odue
if card.odid: if card.odid:
card.did = card.odid card.did = card.odid
card.odid = 0 card.odid = DeckID(0)
card.odue = 0 card.odue = 0
# Interval management # Interval management
@ -617,7 +618,8 @@ did = ? and queue = {QUEUE_TYPE_REV} and due <= ? limit ?""",
card.due = card.odue card.due = card.odue
if card.odid: if card.odid:
card.did = card.odid card.did = card.odid
card.odue = card.odid = 0 card.odue = 0
card.odid = DeckID(0)
card.queue = QUEUE_TYPE_SUSPENDED card.queue = QUEUE_TYPE_SUSPENDED
# notify UI # notify UI
hooks.card_did_leech(card) hooks.card_did_leech(card)

View File

@ -13,7 +13,7 @@ import anki._backend.backend_pb2 as _pb
from anki import hooks from anki import hooks
from anki.cards import Card, CardID from anki.cards import Card, CardID
from anki.consts import * from anki.consts import *
from anki.decks import DeckConfigDict, DeckDict from anki.decks import DeckConfigDict, DeckDict, DeckID
from anki.lang import FormatTimeSpan from anki.lang import FormatTimeSpan
from anki.scheduler.legacy import SchedulerBaseWithLegacy from anki.scheduler.legacy import SchedulerBaseWithLegacy
from anki.utils import ids2str, intTime from anki.utils import ids2str, intTime
@ -75,7 +75,9 @@ class Scheduler(SchedulerBaseWithLegacy):
def _reset_counts(self) -> None: def _reset_counts(self) -> None:
tree = self.deck_due_tree(self.col.decks.selected()) tree = self.deck_due_tree(self.col.decks.selected())
node = self.col.decks.find_deck_in_tree(tree, int(self.col.conf["curDeck"])) node = self.col.decks.find_deck_in_tree(
tree, DeckID(int(self.col.conf["curDeck"]))
)
if not node: if not node:
# current deck points to a missing deck # current deck points to a missing deck
self.newCount = 0 self.newCount = 0
@ -210,7 +212,7 @@ class Scheduler(SchedulerBaseWithLegacy):
return None return None
def _deckNewLimit( def _deckNewLimit(
self, did: int, fn: Optional[Callable[[DeckDict], int]] = None self, did: DeckID, fn: Optional[Callable[[DeckDict], int]] = None
) -> int: ) -> int:
if not fn: if not fn:
fn = self._deckNewLimitSingle fn = self._deckNewLimitSingle
@ -225,7 +227,7 @@ class Scheduler(SchedulerBaseWithLegacy):
lim = min(rem, lim) lim = min(rem, lim)
return lim return lim
def _newForDeck(self, did: int, lim: int) -> int: def _newForDeck(self, did: DeckID, lim: int) -> int:
"New count for a single deck." "New count for a single deck."
if not lim: if not lim:
return 0 return 0
@ -788,7 +790,7 @@ limit ?"""
if card.odid: if card.odid:
card.did = card.odid card.did = card.odid
card.odue = 0 card.odue = 0
card.odid = 0 card.odid = DeckID(0)
def _restorePreviewCard(self, card: Card) -> None: def _restorePreviewCard(self, card: Card) -> None:
assert card.odid assert card.odid

View File

@ -19,6 +19,7 @@ import anki # pylint: disable=unused-import
import anki._backend.backend_pb2 as _pb import anki._backend.backend_pb2 as _pb
import anki.collection import anki.collection
from anki.collection import OpChangesWithCount from anki.collection import OpChangesWithCount
from anki.decks import DeckID
from anki.notes import NoteID from anki.notes import NoteID
from anki.utils import ids2str from anki.utils import ids2str
@ -49,7 +50,7 @@ class TagManager:
def clear_unused_tags(self) -> OpChangesWithCount: def clear_unused_tags(self) -> OpChangesWithCount:
return self.col._backend.clear_unused_tags() return self.col._backend.clear_unused_tags()
def byDeck(self, did: int, children: bool = False) -> List[str]: def byDeck(self, did: DeckID, children: bool = False) -> List[str]:
basequery = "select n.tags from cards c, notes n WHERE c.nid = n.id" basequery = "select n.tags from cards c, notes n WHERE c.nid = n.id"
if not children: if not children:
query = f"{basequery} AND c.did=?" query = f"{basequery} AND c.did=?"

View File

@ -8,6 +8,7 @@ import aqt.editor
import aqt.forms import aqt.forms
from anki.collection import OpChanges, SearchNode from anki.collection import OpChanges, SearchNode
from anki.consts import MODEL_CLOZE from anki.consts import MODEL_CLOZE
from anki.decks import DeckID
from anki.notes import DuplicateOrEmptyResult, Note, NoteID from anki.notes import DuplicateOrEmptyResult, Note, NoteID
from anki.utils import htmlToTextLine, isMac from anki.utils import htmlToTextLine, isMac
from aqt import AnkiQt, gui_hooks from aqt import AnkiQt, gui_hooks
@ -69,7 +70,7 @@ class AddCards(QDialog):
on_notetype_changed=self.on_notetype_change, on_notetype_changed=self.on_notetype_change,
) )
self.deck_chooser = aqt.deckchooser.DeckChooser( self.deck_chooser = aqt.deckchooser.DeckChooser(
self.mw, self.form.deckArea, starting_deck_id=defaults.deck_id self.mw, self.form.deckArea, starting_deck_id=DeckID(defaults.deck_id)
) )
def helpRequested(self) -> None: def helpRequested(self) -> None:

View File

@ -40,7 +40,7 @@ class DeckBrowserContent:
@dataclass @dataclass
class RenderDeckNodeContext: class RenderDeckNodeContext:
current_deck_id: int current_deck_id: DeckID
class DeckBrowser: class DeckBrowser:
@ -101,7 +101,7 @@ class DeckBrowser:
source, target = arg.split(",") source, target = arg.split(",")
self._handle_drag_and_drop(DeckID(int(source)), DeckID(int(target or 0))) self._handle_drag_and_drop(DeckID(int(source)), DeckID(int(target or 0)))
elif cmd == "collapse": elif cmd == "collapse":
self._collapse(int(arg)) self._collapse(DeckID(int(arg)))
elif cmd == "v2upgrade": elif cmd == "v2upgrade":
self._confirm_upgrade() self._confirm_upgrade()
elif cmd == "v2upgradeinfo": elif cmd == "v2upgradeinfo":
@ -112,7 +112,7 @@ class DeckBrowser:
return False return False
def _selDeck(self, did: str) -> None: def _selDeck(self, did: str) -> None:
self.mw.col.decks.select(int(did)) self.mw.col.decks.select(DeckID(int(did)))
self.mw.onOverview() self.mw.onOverview()
# HTML generation # HTML generation
@ -255,13 +255,13 @@ class DeckBrowser:
a = m.addAction(tr(TR.ACTIONS_OPTIONS)) a = m.addAction(tr(TR.ACTIONS_OPTIONS))
qconnect(a.triggered, lambda b, did=did: self._options(DeckID(int(did)))) qconnect(a.triggered, lambda b, did=did: self._options(DeckID(int(did))))
a = m.addAction(tr(TR.ACTIONS_EXPORT)) a = m.addAction(tr(TR.ACTIONS_EXPORT))
qconnect(a.triggered, lambda b, did=did: self._export(int(did))) qconnect(a.triggered, lambda b, did=did: self._export(DeckID(int(did))))
a = m.addAction(tr(TR.ACTIONS_DELETE)) a = m.addAction(tr(TR.ACTIONS_DELETE))
qconnect(a.triggered, lambda b, did=did: self._delete(DeckID(int(did)))) qconnect(a.triggered, lambda b, did=did: self._delete(DeckID(int(did))))
gui_hooks.deck_browser_will_show_options_menu(m, int(did)) gui_hooks.deck_browser_will_show_options_menu(m, int(did))
m.exec_(QCursor.pos()) m.exec_(QCursor.pos())
def _export(self, did: int) -> None: def _export(self, did: DeckID) -> None:
self.mw.onExport(did=did) self.mw.onExport(did=did)
def _rename(self, did: DeckID) -> None: def _rename(self, did: DeckID) -> None:
@ -279,7 +279,7 @@ class DeckBrowser:
self.mw.col.decks.select(did) self.mw.col.decks.select(did)
self.mw.onDeckConf() self.mw.onDeckConf()
def _collapse(self, did: int) -> None: def _collapse(self, did: DeckID) -> None:
self.mw.col.decks.collapse(did) self.mw.col.decks.collapse(did)
node = self.mw.col.decks.find_deck_in_tree(self._dueTree, did) node = self.mw.col.decks.find_deck_in_tree(self._dueTree, did)
if node: if node:

View File

@ -3,7 +3,7 @@
from typing import Optional from typing import Optional
from anki.decks import default_deck_id from anki.decks import DeckID, default_deck_id
from aqt import AnkiQt from aqt import AnkiQt
from aqt.qt import * from aqt.qt import *
from aqt.utils import TR, HelpPage, shortcut, tr from aqt.utils import TR, HelpPage, shortcut, tr
@ -15,17 +15,17 @@ class DeckChooser(QHBoxLayout):
mw: AnkiQt, mw: AnkiQt,
widget: QWidget, widget: QWidget,
label: bool = True, label: bool = True,
starting_deck_id: Optional[int] = None, starting_deck_id: Optional[DeckID] = None,
) -> None: ) -> None:
QHBoxLayout.__init__(self) QHBoxLayout.__init__(self)
self._widget = widget # type: ignore self._widget = widget # type: ignore
self.mw = mw self.mw = mw
self._setup_ui(show_label=label) self._setup_ui(show_label=label)
self._selected_deck_id = 0 self._selected_deck_id = DeckID(0)
# default to current deck if starting id not provided # default to current deck if starting id not provided
if starting_deck_id is None: if starting_deck_id is None:
starting_deck_id = self.mw.col.get_config("curDeck", default=1) or 1 starting_deck_id = DeckID(self.mw.col.get_config("curDeck", default=1) or 1)
self.selected_deck_id = starting_deck_id self.selected_deck_id = starting_deck_id
def _setup_ui(self, show_label: bool) -> None: def _setup_ui(self, show_label: bool) -> None:
@ -57,13 +57,13 @@ class DeckChooser(QHBoxLayout):
) )
@property @property
def selected_deck_id(self) -> int: def selected_deck_id(self) -> DeckID:
self._ensure_selected_deck_valid() self._ensure_selected_deck_valid()
return self._selected_deck_id return self._selected_deck_id
@selected_deck_id.setter @selected_deck_id.setter
def selected_deck_id(self, id: int) -> None: def selected_deck_id(self, id: DeckID) -> None:
if id != self._selected_deck_id: if id != self._selected_deck_id:
self._selected_deck_id = id self._selected_deck_id = id
self._ensure_selected_deck_valid() self._ensure_selected_deck_valid()
@ -104,7 +104,7 @@ class DeckChooser(QHBoxLayout):
onDeckChange = choose_deck onDeckChange = choose_deck
deckName = selected_deck_name deckName = selected_deck_name
def selectedId(self) -> int: def selectedId(self) -> DeckID:
return self.selected_deck_id return self.selected_deck_id
def cleanup(self) -> None: def cleanup(self) -> None:

View File

@ -12,6 +12,7 @@ from typing import List, Optional
import aqt import aqt
from anki import hooks from anki import hooks
from anki.cards import CardID from anki.cards import CardID
from anki.decks import DeckID
from anki.exporting import Exporter, exporters from anki.exporting import Exporter, exporters
from aqt.qt import * from aqt.qt import *
from aqt.utils import ( from aqt.utils import (
@ -29,7 +30,7 @@ class ExportDialog(QDialog):
def __init__( def __init__(
self, self,
mw: aqt.main.AnkiQt, mw: aqt.main.AnkiQt,
did: Optional[int] = None, did: Optional[DeckID] = None,
cids: Optional[List[CardID]] = None, cids: Optional[List[CardID]] = None,
): ):
QDialog.__init__(self, mw, Qt.Window) QDialog.__init__(self, mw, Qt.Window)
@ -43,7 +44,7 @@ class ExportDialog(QDialog):
self.setup(did) self.setup(did)
self.exec_() self.exec_()
def setup(self, did: Optional[int]) -> None: def setup(self, did: Optional[DeckID]) -> None:
self.exporters = exporters(self.col) self.exporters = exporters(self.col)
# if a deck specified, start with .apkg type selected # if a deck specified, start with .apkg type selected
idx = 0 idx = 0

View File

@ -52,7 +52,7 @@ from anki.collection import (
UndoResult, UndoResult,
UndoStatus, UndoStatus,
) )
from anki.decks import DeckDict from anki.decks import DeckDict, DeckID
from anki.hooks import runHook from anki.hooks import runHook
from anki.notes import NoteID from anki.notes import NoteID
from anki.sound import AVTag, SoundOrVideoTag from anki.sound import AVTag, SoundOrVideoTag
@ -518,7 +518,7 @@ class AnkiQt(QMainWindow):
except Exception as e: except Exception as e:
if "FileTooNew" in str(e): if "FileTooNew" in str(e):
showWarning( showWarning(
"This profile requires a newer version of Anki to open. Did you forget to use the Downgrade button prior to switching Anki versions?" "This profile requires a newer version of Anki to open. DeckID you forget to use the Downgrade button prior to switching Anki versions?"
) )
else: else:
showWarning( showWarning(
@ -1382,7 +1382,7 @@ title="%s" %s>%s</button>""" % (
aqt.importing.onImport(self) aqt.importing.onImport(self)
def onExport(self, did: Optional[int] = None) -> None: def onExport(self, did: Optional[DeckID] = None) -> None:
import aqt.exporting import aqt.exporting
aqt.exporting.ExportDialog(self, did=did) aqt.exporting.ExportDialog(self, did=did)

View File

@ -5,6 +5,7 @@ from __future__ import annotations
from typing import Callable, Sequence from typing import Callable, Sequence
from anki.decks import DeckID
from anki.notes import Note, NoteID from anki.notes import Note, NoteID
from aqt import AnkiQt from aqt import AnkiQt
from aqt.main import PerformOpOptionalSuccessCallback from aqt.main import PerformOpOptionalSuccessCallback
@ -14,7 +15,7 @@ def add_note(
*, *,
mw: AnkiQt, mw: AnkiQt,
note: Note, note: Note,
target_deck_id: int, target_deck_id: DeckID,
success: PerformOpOptionalSuccessCallback = None, success: PerformOpOptionalSuccessCallback = None,
) -> None: ) -> None:
mw.perform_op(lambda: mw.col.add_note(note, target_deck_id), success=success) mw.perform_op(lambda: mw.col.add_note(note, target_deck_id), success=success)

View File

@ -976,7 +976,7 @@ class SidebarTreeView(QTreeView):
for node in nodes: for node in nodes:
def toggle_expand() -> Callable[[bool], None]: def toggle_expand() -> Callable[[bool], None]:
did = node.deck_id # pylint: disable=cell-var-from-loop did = DeckID(node.deck_id) # pylint: disable=cell-var-from-loop
return lambda _: self.mw.col.decks.collapseBrowser(did) return lambda _: self.mw.col.decks.collapseBrowser(did)
item = SidebarItem( item = SidebarItem(
@ -1158,7 +1158,7 @@ class SidebarTreeView(QTreeView):
########################### ###########################
def rename_deck(self, item: SidebarItem, new_name: str) -> None: def rename_deck(self, item: SidebarItem, new_name: str) -> None:
deck = self.mw.col.decks.get(item.id) deck = self.mw.col.decks.get(DeckID(item.id))
if not new_name: if not new_name:
return return
new_name = item.name_prefix + new_name new_name = item.name_prefix + new_name

View File

@ -5,6 +5,7 @@ from typing import List, Optional
import aqt import aqt
from anki.collection import OpChangesWithID from anki.collection import OpChangesWithID
from anki.decks import DeckID
from aqt import gui_hooks from aqt import gui_hooks
from aqt.deck_ops import add_deck_dialog from aqt.deck_ops import add_deck_dialog
from aqt.qt import * from aqt.qt import *
@ -167,7 +168,7 @@ class StudyDeck(QDialog):
default = self.names[self.form.list.currentRow()] default = self.names[self.form.list.currentRow()]
def success(out: OpChangesWithID) -> None: def success(out: OpChangesWithID) -> None:
deck = self.mw.col.decks.get(out.id) deck = self.mw.col.decks.get(DeckID(out.id))
self.name = deck["name"] self.name = deck["name"]
# make sure we clean up reset hook when manually exiting # make sure we clean up reset hook when manually exiting