NF: NoteID type
This commit is contained in:
parent
23083c3eb4
commit
6ac540927a
@ -32,6 +32,7 @@ class Card:
|
|||||||
timerStarted: Optional[float]
|
timerStarted: Optional[float]
|
||||||
lastIvl: int
|
lastIvl: int
|
||||||
ord: int
|
ord: int
|
||||||
|
nid: anki.notes.NoteID
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, col: anki.collection.Collection, id: Optional[int] = None
|
self, col: anki.collection.Collection, id: Optional[int] = None
|
||||||
@ -56,7 +57,7 @@ class Card:
|
|||||||
self._render_output = None
|
self._render_output = None
|
||||||
self._note = None
|
self._note = None
|
||||||
self.id = c.id
|
self.id = c.id
|
||||||
self.nid = c.note_id
|
self.nid = anki.notes.NoteID(c.note_id)
|
||||||
self.did = c.deck_id
|
self.did = c.deck_id
|
||||||
self.ord = c.template_idx
|
self.ord = c.template_idx
|
||||||
self.mod = c.mtime_secs
|
self.mod = c.mtime_secs
|
||||||
|
@ -43,7 +43,7 @@ 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
|
||||||
from anki.models import ModelManager, NoteType
|
from anki.models import ModelManager, NoteType
|
||||||
from anki.notes import Note
|
from anki.notes import Note, NoteID
|
||||||
from anki.scheduler.v1 import Scheduler as V1Scheduler
|
from anki.scheduler.v1 import Scheduler as V1Scheduler
|
||||||
from anki.scheduler.v2 import Scheduler as V2Scheduler
|
from anki.scheduler.v2 import Scheduler as V2Scheduler
|
||||||
from anki.scheduler.v3 import Scheduler as V3TestScheduler
|
from anki.scheduler.v3 import Scheduler as V3TestScheduler
|
||||||
@ -327,7 +327,7 @@ class Collection:
|
|||||||
Unlike card.flush(), this will invalidate any current checkpoint."""
|
Unlike card.flush(), this will invalidate any current checkpoint."""
|
||||||
self._backend.update_card(card=card._to_backend_card(), skip_undo_entry=False)
|
self._backend.update_card(card=card._to_backend_card(), skip_undo_entry=False)
|
||||||
|
|
||||||
def get_note(self, id: int) -> Note:
|
def get_note(self, id: NoteID) -> Note:
|
||||||
return Note(self, id=id)
|
return Note(self, id=id)
|
||||||
|
|
||||||
def update_note(self, note: Note) -> OpChanges:
|
def update_note(self, note: Note) -> OpChanges:
|
||||||
@ -358,7 +358,7 @@ class Collection:
|
|||||||
# Deletion logging
|
# Deletion logging
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _logRem(self, ids: List[int], type: int) -> None:
|
def _logRem(self, ids: List[Union[int, NoteID]], type: int) -> None:
|
||||||
self.db.executemany(
|
self.db.executemany(
|
||||||
"insert into graves values (%d, ?, %d)" % (self.usn(), type),
|
"insert into graves values (%d, ?, %d)" % (self.usn(), type),
|
||||||
([x] for x in ids),
|
([x] for x in ids),
|
||||||
@ -372,10 +372,10 @@ class Collection:
|
|||||||
|
|
||||||
def add_note(self, note: Note, deck_id: int) -> OpChanges:
|
def add_note(self, note: Note, deck_id: int) -> 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 = out.note_id
|
note.id = NoteID(out.note_id)
|
||||||
return out.changes
|
return out.changes
|
||||||
|
|
||||||
def remove_notes(self, note_ids: Sequence[int]) -> OpChanges:
|
def remove_notes(self, note_ids: Sequence[NoteID]) -> OpChanges:
|
||||||
hooks.notes_will_be_deleted(self, note_ids)
|
hooks.notes_will_be_deleted(self, note_ids)
|
||||||
return self._backend.remove_notes(note_ids=note_ids, card_ids=[])
|
return self._backend.remove_notes(note_ids=note_ids, card_ids=[])
|
||||||
|
|
||||||
@ -387,7 +387,7 @@ class Collection:
|
|||||||
hooks.notes_will_be_deleted(self, nids)
|
hooks.notes_will_be_deleted(self, nids)
|
||||||
self._backend.remove_notes(note_ids=[], card_ids=card_ids)
|
self._backend.remove_notes(note_ids=[], card_ids=card_ids)
|
||||||
|
|
||||||
def card_ids_of_note(self, note_id: int) -> Sequence[int]:
|
def card_ids_of_note(self, note_id: NoteID) -> Sequence[int]:
|
||||||
return self._backend.cards_of_note(note_id)
|
return self._backend.cards_of_note(note_id)
|
||||||
|
|
||||||
def defaults_for_adding(
|
def defaults_for_adding(
|
||||||
@ -406,7 +406,7 @@ class Collection:
|
|||||||
home_deck_of_current_review_card=home_deck,
|
home_deck_of_current_review_card=home_deck,
|
||||||
)
|
)
|
||||||
|
|
||||||
def default_deck_for_notetype(self, notetype_id: int) -> Optional[int]:
|
def default_deck_for_notetype(self, notetype_id: NoteID) -> Optional[int]:
|
||||||
"""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):
|
||||||
@ -432,10 +432,10 @@ class Collection:
|
|||||||
self.add_note(note, note.model()["did"])
|
self.add_note(note, note.model()["did"])
|
||||||
return len(note.cards())
|
return len(note.cards())
|
||||||
|
|
||||||
def remNotes(self, ids: Sequence[int]) -> None:
|
def remNotes(self, ids: Sequence[NoteID]) -> None:
|
||||||
self.remove_notes(ids)
|
self.remove_notes(ids)
|
||||||
|
|
||||||
def _remNotes(self, ids: List[int]) -> None:
|
def _remNotes(self, ids: List[NoteID]) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Cards
|
# Cards
|
||||||
@ -470,7 +470,7 @@ class Collection:
|
|||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def after_note_updates(
|
def after_note_updates(
|
||||||
self, nids: List[int], mark_modified: bool, generate_cards: bool = True
|
self, nids: List[NoteID], mark_modified: bool, generate_cards: bool = True
|
||||||
) -> None:
|
) -> None:
|
||||||
self._backend.after_note_updates(
|
self._backend.after_note_updates(
|
||||||
nids=nids, generate_cards=generate_cards, mark_notes_modified=mark_modified
|
nids=nids, generate_cards=generate_cards, mark_notes_modified=mark_modified
|
||||||
@ -478,11 +478,11 @@ class Collection:
|
|||||||
|
|
||||||
# legacy
|
# legacy
|
||||||
|
|
||||||
def updateFieldCache(self, nids: List[int]) -> None:
|
def updateFieldCache(self, nids: List[NoteID]) -> None:
|
||||||
self.after_note_updates(nids, mark_modified=False, generate_cards=False)
|
self.after_note_updates(nids, mark_modified=False, generate_cards=False)
|
||||||
|
|
||||||
# this also updates field cache
|
# this also updates field cache
|
||||||
def genCards(self, nids: List[int]) -> List[int]:
|
def genCards(self, nids: List[NoteID]) -> List[int]:
|
||||||
self.after_note_updates(nids, mark_modified=False, generate_cards=True)
|
self.after_note_updates(nids, mark_modified=False, generate_cards=True)
|
||||||
# previously returned empty cards, no longer does
|
# previously returned empty cards, no longer does
|
||||||
return []
|
return []
|
||||||
@ -527,7 +527,7 @@ class Collection:
|
|||||||
)
|
)
|
||||||
return self._backend.search_cards(search=query, order=mode)
|
return self._backend.search_cards(search=query, order=mode)
|
||||||
|
|
||||||
def find_notes(self, *terms: Union[str, SearchNode]) -> Sequence[int]:
|
def find_notes(self, *terms: Union[str, SearchNode]) -> Sequence[NoteID]:
|
||||||
"""Return note ids matching the provided search or searches.
|
"""Return note ids matching the provided search or searches.
|
||||||
|
|
||||||
If more than one search is provided, they will be ANDed together.
|
If more than one search is provided, they will be ANDed together.
|
||||||
@ -538,12 +538,15 @@ class Collection:
|
|||||||
Eg: col.find_notes(SearchNode(deck="test"), "foo") will return notes
|
Eg: col.find_notes(SearchNode(deck="test"), "foo") will return notes
|
||||||
that have a card in deck called "test", and have the text "foo".
|
that have a card in deck called "test", and have the text "foo".
|
||||||
"""
|
"""
|
||||||
return self._backend.search_notes(self.build_search_string(*terms))
|
return [
|
||||||
|
NoteID(did)
|
||||||
|
for did in self._backend.search_notes(self.build_search_string(*terms))
|
||||||
|
]
|
||||||
|
|
||||||
def find_and_replace(
|
def find_and_replace(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
note_ids: Sequence[int],
|
note_ids: Sequence[NoteID],
|
||||||
search: str,
|
search: str,
|
||||||
replacement: str,
|
replacement: str,
|
||||||
regex: bool = False,
|
regex: bool = False,
|
||||||
|
@ -6,6 +6,7 @@ from __future__ import annotations
|
|||||||
from typing import TYPE_CHECKING, Optional, Set
|
from typing import TYPE_CHECKING, Optional, Set
|
||||||
|
|
||||||
from anki.hooks import *
|
from anki.hooks import *
|
||||||
|
from anki.notes import NoteID
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from anki.collection import Collection
|
from anki.collection import Collection
|
||||||
@ -29,7 +30,7 @@ class Finder:
|
|||||||
|
|
||||||
def findReplace(
|
def findReplace(
|
||||||
col: Collection,
|
col: Collection,
|
||||||
nids: List[int],
|
nids: List[NoteID],
|
||||||
src: str,
|
src: str,
|
||||||
dst: str,
|
dst: str,
|
||||||
regex: bool = False,
|
regex: bool = False,
|
||||||
@ -48,7 +49,7 @@ def findReplace(
|
|||||||
).count
|
).count
|
||||||
|
|
||||||
|
|
||||||
def fieldNamesForNotes(col: Collection, nids: List[int]) -> List[str]:
|
def fieldNamesForNotes(col: Collection, nids: List[NoteID]) -> List[str]:
|
||||||
return list(col.field_names_for_note_ids(nids))
|
return list(col.field_names_for_note_ids(nids))
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ from anki.consts import *
|
|||||||
from anki.decks import DeckManager
|
from anki.decks import 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.utils import intTime, joinFields, splitFields, stripHTMLMedia
|
from anki.utils import intTime, joinFields, splitFields, stripHTMLMedia
|
||||||
|
|
||||||
GUID = 1
|
GUID = 1
|
||||||
@ -79,7 +80,7 @@ class Anki2Importer(Importer):
|
|||||||
|
|
||||||
def _importNotes(self) -> None:
|
def _importNotes(self) -> None:
|
||||||
# build guid -> (id,mod,mid) hash & map of existing note ids
|
# build guid -> (id,mod,mid) hash & map of existing note ids
|
||||||
self._notes: Dict[str, Tuple[int, int, int]] = {}
|
self._notes: Dict[str, Tuple[NoteID, int, int]] = {}
|
||||||
existing = {}
|
existing = {}
|
||||||
for id, guid, mod, mid in self.dst.db.execute(
|
for id, guid, mod, mid in self.dst.db.execute(
|
||||||
"select id, guid, mod, mid from notes"
|
"select id, guid, mod, mid from notes"
|
||||||
|
@ -10,6 +10,7 @@ from anki.config import Config
|
|||||||
from anki.consts import NEW_CARDS_RANDOM, STARTING_FACTOR
|
from anki.consts import NEW_CARDS_RANDOM, STARTING_FACTOR
|
||||||
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.utils import (
|
from anki.utils import (
|
||||||
fieldChecksum,
|
fieldChecksum,
|
||||||
guid64,
|
guid64,
|
||||||
@ -19,9 +20,9 @@ from anki.utils import (
|
|||||||
timestampID,
|
timestampID,
|
||||||
)
|
)
|
||||||
|
|
||||||
type_tagsMapped = Tuple[int, int, str, str, int, str, str]
|
type_tagsMapped = Tuple[int, int, str, str, NoteID, str, str]
|
||||||
type_tagsModified = Tuple[int, int, str, str, int, str]
|
type_tagsModified = Tuple[int, int, str, str, NoteID, str]
|
||||||
type_tagsElse = Tuple[int, int, str, int, str]
|
type_tagsElse = Tuple[int, int, str, NoteID, str]
|
||||||
type_udpates = Union[type_tagsMapped, type_tagsModified, type_tagsElse]
|
type_udpates = Union[type_tagsMapped, type_tagsModified, type_tagsElse]
|
||||||
|
|
||||||
# Stores a list of fields, tags and deck
|
# Stores a list of fields, tags and deck
|
||||||
@ -127,7 +128,7 @@ class NoteImporter(Importer):
|
|||||||
if f == "_tags":
|
if f == "_tags":
|
||||||
self._tagsMapped = True
|
self._tagsMapped = True
|
||||||
# gather checks for duplicate comparison
|
# gather checks for duplicate comparison
|
||||||
csums: Dict[str, List[int]] = {}
|
csums: Dict[str, List[NoteID]] = {}
|
||||||
for csum, id in self.col.db.execute(
|
for csum, id in self.col.db.execute(
|
||||||
"select csum, id from notes where mid = ?", self.model["id"]
|
"select csum, id from notes where mid = ?", self.model["id"]
|
||||||
):
|
):
|
||||||
@ -138,12 +139,12 @@ class NoteImporter(Importer):
|
|||||||
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.fieldMap(self.model)
|
||||||
self._nextID = timestampID(self.col.db, "notes")
|
self._nextID = NoteID(timestampID(self.col.db, "notes"))
|
||||||
# loop through the notes
|
# loop through the notes
|
||||||
updates: List[type_udpates] = []
|
updates: List[type_udpates] = []
|
||||||
updateLog = []
|
updateLog = []
|
||||||
new = []
|
new = []
|
||||||
self._ids: List[int] = []
|
self._ids: List[NoteID] = []
|
||||||
self._cards: List[Tuple] = []
|
self._cards: List[Tuple] = []
|
||||||
dupeCount = 0
|
dupeCount = 0
|
||||||
dupes: List[str] = []
|
dupes: List[str] = []
|
||||||
@ -242,9 +243,9 @@ class NoteImporter(Importer):
|
|||||||
|
|
||||||
def newData(
|
def newData(
|
||||||
self, n: ForeignNote
|
self, n: ForeignNote
|
||||||
) -> Tuple[int, str, int, int, int, str, str, str, int, int, str]:
|
) -> Tuple[NoteID, str, int, int, int, str, str, str, int, int, str]:
|
||||||
id = self._nextID
|
id = self._nextID
|
||||||
self._nextID += 1
|
self._nextID = NoteID(self._nextID + 1)
|
||||||
self._ids.append(id)
|
self._ids.append(id)
|
||||||
self.processFields(n)
|
self.processFields(n)
|
||||||
# note id for card updates later
|
# note id for card updates later
|
||||||
@ -266,14 +267,14 @@ class NoteImporter(Importer):
|
|||||||
|
|
||||||
def addNew(
|
def addNew(
|
||||||
self,
|
self,
|
||||||
rows: List[Tuple[int, str, int, int, int, str, str, str, int, int, str]],
|
rows: List[Tuple[NoteID, str, int, int, int, str, str, str, int, int, str]],
|
||||||
) -> None:
|
) -> None:
|
||||||
self.col.db.executemany(
|
self.col.db.executemany(
|
||||||
"insert or replace into notes values (?,?,?,?,?,?,?,?,?,?,?)", rows
|
"insert or replace into notes values (?,?,?,?,?,?,?,?,?,?,?)", rows
|
||||||
)
|
)
|
||||||
|
|
||||||
def updateData(
|
def updateData(
|
||||||
self, n: ForeignNote, id: int, sflds: List[str]
|
self, n: ForeignNote, id: NoteID, sflds: List[str]
|
||||||
) -> Optional[type_udpates]:
|
) -> Optional[type_udpates]:
|
||||||
self._ids.append(id)
|
self._ids.append(id)
|
||||||
self.processFields(n, sflds)
|
self.processFields(n, sflds)
|
||||||
|
@ -257,7 +257,7 @@ class ModelManager:
|
|||||||
# Tools
|
# Tools
|
||||||
##################################################
|
##################################################
|
||||||
|
|
||||||
def nids(self, ntid: int) -> List[int]:
|
def nids(self, ntid: int) -> List[anki.notes.NoteID]:
|
||||||
"Note ids for M."
|
"Note ids for M."
|
||||||
if isinstance(ntid, dict):
|
if isinstance(ntid, dict):
|
||||||
# legacy callers passed in note type
|
# legacy callers passed in note type
|
||||||
@ -420,7 +420,7 @@ and notes.mid = ? and cards.ord = ?""",
|
|||||||
def change(
|
def change(
|
||||||
self,
|
self,
|
||||||
m: NoteType,
|
m: NoteType,
|
||||||
nids: List[int],
|
nids: List[anki.notes.NoteID],
|
||||||
newModel: NoteType,
|
newModel: NoteType,
|
||||||
fmap: Optional[Dict[int, Union[None, int]]],
|
fmap: Optional[Dict[int, Union[None, int]]],
|
||||||
cmap: Optional[Dict[int, Union[None, int]]],
|
cmap: Optional[Dict[int, Union[None, int]]],
|
||||||
@ -434,7 +434,10 @@ and notes.mid = ? and cards.ord = ?""",
|
|||||||
self.col.after_note_updates(nids, mark_modified=True)
|
self.col.after_note_updates(nids, mark_modified=True)
|
||||||
|
|
||||||
def _changeNotes(
|
def _changeNotes(
|
||||||
self, nids: List[int], newModel: NoteType, map: Dict[int, Union[None, int]]
|
self,
|
||||||
|
nids: List[anki.notes.NoteID],
|
||||||
|
newModel: NoteType,
|
||||||
|
map: Dict[int, Union[None, int]],
|
||||||
) -> None:
|
) -> None:
|
||||||
d = []
|
d = []
|
||||||
nfields = len(newModel["flds"])
|
nfields = len(newModel["flds"])
|
||||||
@ -464,7 +467,7 @@ and notes.mid = ? and cards.ord = ?""",
|
|||||||
|
|
||||||
def _changeCards(
|
def _changeCards(
|
||||||
self,
|
self,
|
||||||
nids: List[int],
|
nids: List[anki.notes.NoteID],
|
||||||
oldModel: NoteType,
|
oldModel: NoteType,
|
||||||
newModel: NoteType,
|
newModel: NoteType,
|
||||||
map: Dict[int, Union[None, int]],
|
map: Dict[int, Union[None, int]],
|
||||||
|
@ -5,7 +5,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import copy
|
import copy
|
||||||
import pprint
|
import pprint
|
||||||
from typing import Any, List, Optional, Sequence, Tuple
|
from typing import Any, List, NewType, Optional, Sequence, Tuple
|
||||||
|
|
||||||
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
|
||||||
@ -16,6 +16,9 @@ from anki.utils import joinFields
|
|||||||
|
|
||||||
DuplicateOrEmptyResult = _pb.NoteIsDuplicateOrEmptyOut.State
|
DuplicateOrEmptyResult = _pb.NoteIsDuplicateOrEmptyOut.State
|
||||||
|
|
||||||
|
# types
|
||||||
|
NoteID = NewType("NoteID", int)
|
||||||
|
|
||||||
|
|
||||||
class Note:
|
class Note:
|
||||||
# not currently exposed
|
# not currently exposed
|
||||||
@ -26,7 +29,7 @@ class Note:
|
|||||||
self,
|
self,
|
||||||
col: anki.collection.Collection,
|
col: anki.collection.Collection,
|
||||||
model: Optional[NoteType] = None,
|
model: Optional[NoteType] = None,
|
||||||
id: Optional[int] = None,
|
id: Optional[NoteID] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
assert not (model and id)
|
assert not (model and id)
|
||||||
self.col = col.weakref()
|
self.col = col.weakref()
|
||||||
@ -46,7 +49,7 @@ class Note:
|
|||||||
self._load_from_backend_note(n)
|
self._load_from_backend_note(n)
|
||||||
|
|
||||||
def _load_from_backend_note(self, n: _pb.Note) -> None:
|
def _load_from_backend_note(self, n: _pb.Note) -> None:
|
||||||
self.id = n.id
|
self.id = NoteID(n.id)
|
||||||
self.guid = n.guid
|
self.guid = n.guid
|
||||||
self.mid = n.notetype_id
|
self.mid = n.notetype_id
|
||||||
self.mod = n.mtime_secs
|
self.mod = n.mtime_secs
|
||||||
|
@ -6,6 +6,7 @@ from typing import List, Optional, Tuple
|
|||||||
from anki.cards import Card
|
from anki.cards import Card
|
||||||
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
|
||||||
|
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
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ class SchedulerBaseWithLegacy(SchedulerBase):
|
|||||||
) -> None:
|
) -> None:
|
||||||
self.set_due_date(card_ids, f"{min_interval}-{max_interval}!")
|
self.set_due_date(card_ids, f"{min_interval}-{max_interval}!")
|
||||||
|
|
||||||
def buryNote(self, nid: int) -> None:
|
def buryNote(self, nid: NoteID) -> None:
|
||||||
note = self.col.get_note(nid)
|
note = self.col.get_note(nid)
|
||||||
self.bury_cards(note.card_ids())
|
self.bury_cards(note.card_ids())
|
||||||
|
|
||||||
|
@ -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.notes import NoteID
|
||||||
from anki.utils import ids2str
|
from anki.utils import ids2str
|
||||||
|
|
||||||
# public exports
|
# public exports
|
||||||
@ -68,11 +69,11 @@ class TagManager:
|
|||||||
# Bulk addition/removal from specific notes
|
# Bulk addition/removal from specific notes
|
||||||
#############################################################
|
#############################################################
|
||||||
|
|
||||||
def bulk_add(self, note_ids: Sequence[int], tags: str) -> OpChangesWithCount:
|
def bulk_add(self, note_ids: Sequence[NoteID], tags: str) -> OpChangesWithCount:
|
||||||
"""Add space-separate tags to provided notes, returning changed count."""
|
"""Add space-separate tags to provided notes, returning changed count."""
|
||||||
return self.col._backend.add_note_tags(note_ids=note_ids, tags=tags)
|
return self.col._backend.add_note_tags(note_ids=note_ids, tags=tags)
|
||||||
|
|
||||||
def bulk_remove(self, note_ids: Sequence[int], tags: str) -> OpChangesWithCount:
|
def bulk_remove(self, note_ids: Sequence[NoteID], tags: str) -> OpChangesWithCount:
|
||||||
return self.col._backend.remove_note_tags(note_ids=note_ids, tags=tags)
|
return self.col._backend.remove_note_tags(note_ids=note_ids, tags=tags)
|
||||||
|
|
||||||
# Find&replace
|
# Find&replace
|
||||||
@ -175,12 +176,12 @@ class TagManager:
|
|||||||
) -> None:
|
) -> None:
|
||||||
print("tags.register() is deprecated and no longer works")
|
print("tags.register() is deprecated and no longer works")
|
||||||
|
|
||||||
def bulkAdd(self, ids: List[int], tags: str, add: bool = True) -> None:
|
def bulkAdd(self, ids: List[NoteID], tags: str, add: bool = True) -> None:
|
||||||
"Add tags in bulk. TAGS is space-separated."
|
"Add tags in bulk. TAGS is space-separated."
|
||||||
if add:
|
if add:
|
||||||
self.bulk_add(ids, tags)
|
self.bulk_add(ids, tags)
|
||||||
else:
|
else:
|
||||||
self.bulk_remove(ids, tags)
|
self.bulk_remove(ids, tags)
|
||||||
|
|
||||||
def bulkRem(self, ids: List[int], tags: str) -> None:
|
def bulkRem(self, ids: List[NoteID], tags: str) -> None:
|
||||||
self.bulkAdd(ids, tags, False)
|
self.bulkAdd(ids, tags, False)
|
||||||
|
@ -23,7 +23,7 @@ hooks = [
|
|||||||
Hook(name="schema_will_change", args=["proceed: bool"], return_type="bool"),
|
Hook(name="schema_will_change", args=["proceed: bool"], return_type="bool"),
|
||||||
Hook(
|
Hook(
|
||||||
name="notes_will_be_deleted",
|
name="notes_will_be_deleted",
|
||||||
args=["col: anki.collection.Collection", "ids: Sequence[int]"],
|
args=["col: anki.collection.Collection", "ids: Sequence[anki.notes.NoteID]"],
|
||||||
legacy_hook="remNotes",
|
legacy_hook="remNotes",
|
||||||
),
|
),
|
||||||
Hook(name="media_files_did_export", args=["count: int"]),
|
Hook(name="media_files_did_export", args=["count: int"]),
|
||||||
|
@ -8,7 +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.notes import DuplicateOrEmptyResult, Note
|
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
|
||||||
from aqt.note_ops import add_note
|
from aqt.note_ops import add_note
|
||||||
@ -47,7 +47,7 @@ class AddCards(QDialog):
|
|||||||
self.setupEditor()
|
self.setupEditor()
|
||||||
self.setupButtons()
|
self.setupButtons()
|
||||||
self._load_new_note()
|
self._load_new_note()
|
||||||
self.history: List[int] = []
|
self.history: List[NoteID] = []
|
||||||
self._last_added_note: Optional[Note] = None
|
self._last_added_note: Optional[Note] = None
|
||||||
restoreGeom(self, "add")
|
restoreGeom(self, "add")
|
||||||
addCloseShortcut(self)
|
addCloseShortcut(self)
|
||||||
@ -109,7 +109,7 @@ class AddCards(QDialog):
|
|||||||
def show_notetype_selector(self) -> None:
|
def show_notetype_selector(self) -> None:
|
||||||
self.editor.call_after_note_saved(self.notetype_chooser.choose_notetype)
|
self.editor.call_after_note_saved(self.notetype_chooser.choose_notetype)
|
||||||
|
|
||||||
def on_notetype_change(self, notetype_id: int) -> None:
|
def on_notetype_change(self, notetype_id: NoteID) -> None:
|
||||||
# need to adjust current deck?
|
# need to adjust current deck?
|
||||||
if deck_id := self.mw.col.default_deck_for_notetype(notetype_id):
|
if deck_id := self.mw.col.default_deck_for_notetype(notetype_id):
|
||||||
self.deck_chooser.selected_deck_id = deck_id
|
self.deck_chooser.selected_deck_id = deck_id
|
||||||
@ -178,7 +178,7 @@ class AddCards(QDialog):
|
|||||||
gui_hooks.add_cards_will_show_history_menu(self, m)
|
gui_hooks.add_cards_will_show_history_menu(self, m)
|
||||||
m.exec_(self.historyButton.mapToGlobal(QPoint(0, 0)))
|
m.exec_(self.historyButton.mapToGlobal(QPoint(0, 0)))
|
||||||
|
|
||||||
def editHistory(self, nid: int) -> None:
|
def editHistory(self, nid: NoteID) -> None:
|
||||||
aqt.dialogs.open("Browser", self.mw, search=(SearchNode(nid=nid),))
|
aqt.dialogs.open("Browser", self.mw, search=(SearchNode(nid=nid),))
|
||||||
|
|
||||||
def add_current_note(self) -> None:
|
def add_current_note(self) -> None:
|
||||||
|
@ -28,6 +28,7 @@ from anki.consts import *
|
|||||||
from anki.errors import NotFoundError
|
from anki.errors import NotFoundError
|
||||||
from anki.lang import without_unicode_isolation
|
from anki.lang import without_unicode_isolation
|
||||||
from anki.models import NoteType
|
from anki.models import NoteType
|
||||||
|
from anki.notes import NoteID
|
||||||
from anki.stats import CardStats
|
from anki.stats import CardStats
|
||||||
from anki.tags import MARKED_TAG
|
from anki.tags import MARKED_TAG
|
||||||
from anki.utils import ids2str, isMac, isWin
|
from anki.utils import ids2str, isMac, isWin
|
||||||
@ -1054,7 +1055,7 @@ QTableView {{ gridline-color: {grid} }}
|
|||||||
for idx in self.form.tableView.selectionModel().selectedRows()
|
for idx in self.form.tableView.selectionModel().selectedRows()
|
||||||
]
|
]
|
||||||
|
|
||||||
def selected_notes(self) -> List[int]:
|
def selected_notes(self) -> List[NoteID]:
|
||||||
return self.col.db.list(
|
return self.col.db.list(
|
||||||
"""
|
"""
|
||||||
select distinct nid from cards
|
select distinct nid from cards
|
||||||
@ -1073,7 +1074,7 @@ where id in %s"""
|
|||||||
% ",".join([str(s) for s in self.selected_notes()])
|
% ",".join([str(s) for s in self.selected_notes()])
|
||||||
)
|
)
|
||||||
|
|
||||||
def oneModelNotes(self) -> List[int]:
|
def oneModelNotes(self) -> List[NoteID]:
|
||||||
sf = self.selected_notes()
|
sf = self.selected_notes()
|
||||||
if not sf:
|
if not sf:
|
||||||
return []
|
return []
|
||||||
@ -1603,7 +1604,7 @@ where id in %s"""
|
|||||||
|
|
||||||
|
|
||||||
class ChangeModel(QDialog):
|
class ChangeModel(QDialog):
|
||||||
def __init__(self, browser: Browser, nids: List[int]) -> None:
|
def __init__(self, browser: Browser, nids: List[NoteID]) -> None:
|
||||||
QDialog.__init__(self, browser)
|
QDialog.__init__(self, browser)
|
||||||
self.browser = browser
|
self.browser = browser
|
||||||
self.nids = nids
|
self.nids = nids
|
||||||
|
@ -7,6 +7,7 @@ from typing import List, Optional, Sequence
|
|||||||
|
|
||||||
import aqt
|
import aqt
|
||||||
from anki.lang import TR
|
from anki.lang import TR
|
||||||
|
from anki.notes import NoteID
|
||||||
from aqt import AnkiQt, QWidget
|
from aqt import AnkiQt, QWidget
|
||||||
from aqt.qt import QDialog, Qt
|
from aqt.qt import QDialog, Qt
|
||||||
from aqt.utils import (
|
from aqt.utils import (
|
||||||
@ -31,7 +32,7 @@ def find_and_replace(
|
|||||||
*,
|
*,
|
||||||
mw: AnkiQt,
|
mw: AnkiQt,
|
||||||
parent: QWidget,
|
parent: QWidget,
|
||||||
note_ids: Sequence[int],
|
note_ids: Sequence[NoteID],
|
||||||
search: str,
|
search: str,
|
||||||
replacement: str,
|
replacement: str,
|
||||||
regex: bool,
|
regex: bool,
|
||||||
@ -82,7 +83,9 @@ def find_and_replace_tag(
|
|||||||
class FindAndReplaceDialog(QDialog):
|
class FindAndReplaceDialog(QDialog):
|
||||||
COMBO_NAME = "BrowserFindAndReplace"
|
COMBO_NAME = "BrowserFindAndReplace"
|
||||||
|
|
||||||
def __init__(self, parent: QWidget, *, mw: AnkiQt, note_ids: Sequence[int]) -> None:
|
def __init__(
|
||||||
|
self, parent: QWidget, *, mw: AnkiQt, note_ids: Sequence[NoteID]
|
||||||
|
) -> None:
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.mw = mw
|
self.mw = mw
|
||||||
self.note_ids = note_ids
|
self.note_ids = note_ids
|
||||||
|
@ -54,6 +54,7 @@ from anki.collection import (
|
|||||||
)
|
)
|
||||||
from anki.decks import DeckDict
|
from anki.decks import DeckDict
|
||||||
from anki.hooks import runHook
|
from anki.hooks import runHook
|
||||||
|
from anki.notes import NoteID
|
||||||
from anki.sound import AVTag, SoundOrVideoTag
|
from anki.sound import AVTag, SoundOrVideoTag
|
||||||
from anki.types import assert_exhaustive
|
from anki.types import assert_exhaustive
|
||||||
from anki.utils import devMode, ids2str, intTime, isMac, isWin, splitFields
|
from anki.utils import devMode, ids2str, intTime, isMac, isWin, splitFields
|
||||||
@ -1534,7 +1535,7 @@ title="%s" %s>%s</button>""" % (
|
|||||||
# Log note deletion
|
# Log note deletion
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def onRemNotes(self, col: Collection, nids: Sequence[int]) -> None:
|
def onRemNotes(self, col: Collection, nids: Sequence[NoteID]) -> None:
|
||||||
path = os.path.join(self.pm.profileFolder(), "deleted.txt")
|
path = os.path.join(self.pm.profileFolder(), "deleted.txt")
|
||||||
existed = os.path.exists(path)
|
existed = os.path.exists(path)
|
||||||
with open(path, "ab") as f:
|
with open(path, "ab") as f:
|
||||||
|
@ -5,7 +5,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import Callable, Sequence
|
from typing import Callable, Sequence
|
||||||
|
|
||||||
from anki.notes import Note
|
from anki.notes import Note, NoteID
|
||||||
from aqt import AnkiQt
|
from aqt import AnkiQt
|
||||||
from aqt.main import PerformOpOptionalSuccessCallback
|
from aqt.main import PerformOpOptionalSuccessCallback
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ def update_note(*, mw: AnkiQt, note: Note, after_hooks: Callable[[], None]) -> N
|
|||||||
def remove_notes(
|
def remove_notes(
|
||||||
*,
|
*,
|
||||||
mw: AnkiQt,
|
mw: AnkiQt,
|
||||||
note_ids: Sequence[int],
|
note_ids: Sequence[NoteID],
|
||||||
success: PerformOpOptionalSuccessCallback = None,
|
success: PerformOpOptionalSuccessCallback = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
mw.perform_op(lambda: mw.col.remove_notes(note_ids), success=success)
|
mw.perform_op(lambda: mw.col.remove_notes(note_ids), success=success)
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
# 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
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
|
from anki.notes import NoteID
|
||||||
from aqt import AnkiQt, gui_hooks
|
from aqt import AnkiQt, gui_hooks
|
||||||
from aqt.qt import *
|
from aqt.qt import *
|
||||||
from aqt.utils import TR, HelpPage, shortcut, tr
|
from aqt.utils import TR, HelpPage, shortcut, tr
|
||||||
@ -29,7 +30,7 @@ class NoteTypeChooser(QHBoxLayout):
|
|||||||
widget: QWidget,
|
widget: QWidget,
|
||||||
starting_notetype_id: int,
|
starting_notetype_id: int,
|
||||||
on_button_activated: Optional[Callable[[], None]] = None,
|
on_button_activated: Optional[Callable[[], None]] = None,
|
||||||
on_notetype_changed: Optional[Callable[[int], None]] = None,
|
on_notetype_changed: Optional[Callable[[NoteID], None]] = None,
|
||||||
show_prefix_label: bool = True,
|
show_prefix_label: bool = True,
|
||||||
) -> None:
|
) -> None:
|
||||||
QHBoxLayout.__init__(self)
|
QHBoxLayout.__init__(self)
|
||||||
|
@ -9,6 +9,7 @@ import aqt
|
|||||||
from anki.collection import CARD_TYPE_NEW, Config
|
from anki.collection import CARD_TYPE_NEW, Config
|
||||||
from anki.decks import DeckID
|
from anki.decks import DeckID
|
||||||
from anki.lang import TR
|
from anki.lang import TR
|
||||||
|
from anki.notes import NoteID
|
||||||
from anki.scheduler import FilteredDeckForUpdate
|
from anki.scheduler import FilteredDeckForUpdate
|
||||||
from aqt import AnkiQt
|
from aqt import AnkiQt
|
||||||
from aqt.main import PerformOpOptionalSuccessCallback
|
from aqt.main import PerformOpOptionalSuccessCallback
|
||||||
@ -143,7 +144,7 @@ def suspend_cards(
|
|||||||
def suspend_note(
|
def suspend_note(
|
||||||
*,
|
*,
|
||||||
mw: AnkiQt,
|
mw: AnkiQt,
|
||||||
note_id: int,
|
note_id: NoteID,
|
||||||
success: PerformOpOptionalSuccessCallback = None,
|
success: PerformOpOptionalSuccessCallback = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
mw.taskman.run_in_background(
|
mw.taskman.run_in_background(
|
||||||
@ -168,7 +169,7 @@ def bury_cards(
|
|||||||
def bury_note(
|
def bury_note(
|
||||||
*,
|
*,
|
||||||
mw: AnkiQt,
|
mw: AnkiQt,
|
||||||
note_id: int,
|
note_id: NoteID,
|
||||||
success: PerformOpOptionalSuccessCallback = None,
|
success: PerformOpOptionalSuccessCallback = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
mw.taskman.run_in_background(
|
mw.taskman.run_in_background(
|
||||||
|
@ -7,6 +7,7 @@ from typing import Callable, Sequence
|
|||||||
|
|
||||||
from anki.collection import OpChangesWithCount
|
from anki.collection import OpChangesWithCount
|
||||||
from anki.lang import TR
|
from anki.lang import TR
|
||||||
|
from anki.notes import NoteID
|
||||||
from aqt import AnkiQt, QWidget
|
from aqt import AnkiQt, QWidget
|
||||||
from aqt.main import PerformOpOptionalSuccessCallback
|
from aqt.main import PerformOpOptionalSuccessCallback
|
||||||
from aqt.utils import showInfo, tooltip, tr
|
from aqt.utils import showInfo, tooltip, tr
|
||||||
@ -15,7 +16,7 @@ from aqt.utils import showInfo, tooltip, tr
|
|||||||
def add_tags(
|
def add_tags(
|
||||||
*,
|
*,
|
||||||
mw: AnkiQt,
|
mw: AnkiQt,
|
||||||
note_ids: Sequence[int],
|
note_ids: Sequence[NoteID],
|
||||||
space_separated_tags: str,
|
space_separated_tags: str,
|
||||||
success: PerformOpOptionalSuccessCallback = None,
|
success: PerformOpOptionalSuccessCallback = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -27,7 +28,7 @@ def add_tags(
|
|||||||
def remove_tags_for_notes(
|
def remove_tags_for_notes(
|
||||||
*,
|
*,
|
||||||
mw: AnkiQt,
|
mw: AnkiQt,
|
||||||
note_ids: Sequence[int],
|
note_ids: Sequence[NoteID],
|
||||||
space_separated_tags: str,
|
space_separated_tags: str,
|
||||||
success: PerformOpOptionalSuccessCallback = None,
|
success: PerformOpOptionalSuccessCallback = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
Loading…
Reference in New Issue
Block a user