NF: NoteID type

This commit is contained in:
Arthur Milchior 2021-03-25 13:50:31 +01:00 committed by Damien Elmes
parent 23083c3eb4
commit 6ac540927a
18 changed files with 82 additions and 59 deletions

View File

@ -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

View File

@ -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,

View File

@ -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))

View File

@ -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"

View File

@ -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)

View File

@ -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]],

View File

@ -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

View File

@ -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())

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.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)

View File

@ -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"]),

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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)

View File

@ -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)

View File

@ -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(

View File

@ -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: