mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -04:00
add a bunch of extra hints for mypy
This commit is contained in:
parent
d8d7e78b6b
commit
12093f2f2e
7 changed files with 46 additions and 51 deletions
|
@ -8,10 +8,9 @@ from anki.storage import Collection
|
||||||
from anki.utils import intTime, splitFields, joinFields
|
from anki.utils import intTime, splitFields, joinFields
|
||||||
from anki.importing.base import Importer
|
from anki.importing.base import Importer
|
||||||
from anki.lang import _
|
from anki.lang import _
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional, Dict, Tuple, List
|
||||||
|
|
||||||
from anki.collection import _Collection
|
from anki.collection import _Collection
|
||||||
from typing import List, Union
|
|
||||||
GUID = 1
|
GUID = 1
|
||||||
MID = 2
|
MID = 2
|
||||||
MOD = 3
|
MOD = 3
|
||||||
|
@ -19,15 +18,16 @@ MOD = 3
|
||||||
class Anki2Importer(Importer):
|
class Anki2Importer(Importer):
|
||||||
|
|
||||||
needMapper = False
|
needMapper = False
|
||||||
deckPrefix = None
|
deckPrefix: Optional[str] = None
|
||||||
allowUpdate = True
|
allowUpdate = True
|
||||||
|
src: _Collection
|
||||||
|
dst: _Collection
|
||||||
|
|
||||||
def __init__(self, col: _Collection, file: str) -> None:
|
def __init__(self, col: _Collection, file: str) -> None:
|
||||||
super().__init__(col, file)
|
super().__init__(col, file)
|
||||||
|
|
||||||
# set later, defined here for typechecking
|
# set later, defined here for typechecking
|
||||||
self.src = None
|
self._decks: Dict[int,int] = {}
|
||||||
self._decks = {}
|
|
||||||
self.mustResetLearning = False
|
self.mustResetLearning = False
|
||||||
|
|
||||||
def run(self, media: None = None) -> None:
|
def run(self, media: None = None) -> None:
|
||||||
|
@ -79,7 +79,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 = {}
|
self._notes: Dict[str, Tuple[int,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"):
|
||||||
|
@ -87,10 +87,10 @@ class Anki2Importer(Importer):
|
||||||
existing[id] = True
|
existing[id] = True
|
||||||
# we may need to rewrite the guid if the model schemas don't match,
|
# we may need to rewrite the guid if the model schemas don't match,
|
||||||
# so we need to keep track of the changes for the card import stage
|
# so we need to keep track of the changes for the card import stage
|
||||||
self._changedGuids = {}
|
self._changedGuids: Dict[str, bool] = {}
|
||||||
# we ignore updates to changed schemas. we need to note the ignored
|
# we ignore updates to changed schemas. we need to note the ignored
|
||||||
# guids, so we avoid importing invalid cards
|
# guids, so we avoid importing invalid cards
|
||||||
self._ignoredGuids = {}
|
self._ignoredGuids: Dict[str, bool] = {}
|
||||||
# iterate over source collection
|
# iterate over source collection
|
||||||
add = []
|
add = []
|
||||||
update = []
|
update = []
|
||||||
|
@ -188,7 +188,7 @@ class Anki2Importer(Importer):
|
||||||
|
|
||||||
# determine if note is a duplicate, and adjust mid and/or guid as required
|
# determine if note is a duplicate, and adjust mid and/or guid as required
|
||||||
# returns true if note should be added
|
# returns true if note should be added
|
||||||
def _uniquifyNote(self, note: List[Union[int, str]]) -> bool:
|
def _uniquifyNote(self, note: List[Any]) -> bool:
|
||||||
origGuid = note[GUID]
|
origGuid = note[GUID]
|
||||||
srcMid = note[MID]
|
srcMid = note[MID]
|
||||||
dstMid = self._mid(srcMid)
|
dstMid = self._mid(srcMid)
|
||||||
|
@ -212,7 +212,7 @@ class Anki2Importer(Importer):
|
||||||
|
|
||||||
def _prepareModels(self) -> None:
|
def _prepareModels(self) -> None:
|
||||||
"Prepare index of schema hashes."
|
"Prepare index of schema hashes."
|
||||||
self._modelMap = {}
|
self._modelMap: Dict[int, int] = {}
|
||||||
|
|
||||||
def _mid(self, srcMid: int) -> Any:
|
def _mid(self, srcMid: int) -> Any:
|
||||||
"Return local id for remote MID."
|
"Return local id for remote MID."
|
||||||
|
@ -302,7 +302,7 @@ class Anki2Importer(Importer):
|
||||||
if self.mustResetLearning:
|
if self.mustResetLearning:
|
||||||
self.src.changeSchedulerVer(2)
|
self.src.changeSchedulerVer(2)
|
||||||
# build map of (guid, ord) -> cid and used id cache
|
# build map of (guid, ord) -> cid and used id cache
|
||||||
self._cards = {}
|
self._cards: Dict[Tuple[str,int], int] = {}
|
||||||
existing = {}
|
existing = {}
|
||||||
for guid, ord, cid in self.dst.db.execute(
|
for guid, ord, cid in self.dst.db.execute(
|
||||||
"select f.guid, c.ord, c.id from cards c, notes f "
|
"select f.guid, c.ord, c.id from cards c, notes f "
|
||||||
|
@ -403,7 +403,7 @@ insert or ignore into revlog values (?,?,?,?,?,?,?,?,?)""", revlog)
|
||||||
with open(path, "rb") as f:
|
with open(path, "rb") as f:
|
||||||
return f.read()
|
return f.read()
|
||||||
except (IOError, OSError):
|
except (IOError, OSError):
|
||||||
return
|
return b''
|
||||||
|
|
||||||
def _srcMediaData(self, fname: str) -> bytes:
|
def _srcMediaData(self, fname: str) -> bytes:
|
||||||
"Data for FNAME in src collection."
|
"Data for FNAME in src collection."
|
||||||
|
@ -423,8 +423,8 @@ insert or ignore into revlog values (?,?,?,?,?,?,?,?,?)""", revlog)
|
||||||
# the user likely used subdirectories
|
# the user likely used subdirectories
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _mungeMedia(self, mid: int, fields: str) -> str:
|
def _mungeMedia(self, mid: int, fieldsStr: str) -> str:
|
||||||
fields = splitFields(fields)
|
fields = splitFields(fieldsStr)
|
||||||
def repl(match):
|
def repl(match):
|
||||||
fname = match.group("fname")
|
fname = match.group("fname")
|
||||||
srcData = self._srcMediaData(fname)
|
srcData = self._srcMediaData(fname)
|
||||||
|
|
|
@ -7,18 +7,13 @@ import unicodedata
|
||||||
import json
|
import json
|
||||||
from anki.utils import tmpfile
|
from anki.utils import tmpfile
|
||||||
from anki.importing.anki2 import Anki2Importer
|
from anki.importing.anki2 import Anki2Importer
|
||||||
from typing import Any
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
from anki.collection import _Collection
|
|
||||||
class AnkiPackageImporter(Anki2Importer):
|
class AnkiPackageImporter(Anki2Importer):
|
||||||
|
nameToNum: Dict[str, str]
|
||||||
|
zip: Optional[zipfile.ZipFile]
|
||||||
|
|
||||||
def __init__(self, col: _Collection, file: str) -> None:
|
def run(self) -> None: # type: ignore
|
||||||
super().__init__(col, file)
|
|
||||||
# set later; set here for typechecking
|
|
||||||
self.nameToNum = {}
|
|
||||||
self.zip = None
|
|
||||||
|
|
||||||
def run(self) -> None:
|
|
||||||
# extract the deck from the zip file
|
# extract the deck from the zip file
|
||||||
self.zip = z = zipfile.ZipFile(self.file)
|
self.zip = z = zipfile.ZipFile(self.file)
|
||||||
# v2 scheduler?
|
# v2 scheduler?
|
||||||
|
@ -56,5 +51,5 @@ class AnkiPackageImporter(Anki2Importer):
|
||||||
|
|
||||||
def _srcMediaData(self, fname: str) -> Any:
|
def _srcMediaData(self, fname: str) -> Any:
|
||||||
if fname in self.nameToNum:
|
if fname in self.nameToNum:
|
||||||
return self.zip.read(self.nameToNum[fname])
|
return self.zip.read(self.nameToNum[fname]) # pytype: disable=attribute-error
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -3,20 +3,21 @@
|
||||||
# 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 anki.utils import maxID
|
from anki.utils import maxID
|
||||||
from typing import Any
|
from typing import Any, Optional, List
|
||||||
|
from anki.collection import _Collection
|
||||||
|
|
||||||
# Base importer
|
# Base importer
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
from anki.collection import _Collection
|
|
||||||
class Importer:
|
class Importer:
|
||||||
|
|
||||||
needMapper = False
|
needMapper = False
|
||||||
needDelimiter = False
|
needDelimiter = False
|
||||||
|
dst: Optional[_Collection]
|
||||||
|
|
||||||
def __init__(self, col: _Collection, file: str) -> None:
|
def __init__(self, col: _Collection, file: str) -> None:
|
||||||
self.file = file
|
self.file = file
|
||||||
self.log = []
|
self.log: List[str] = []
|
||||||
self.col = col
|
self.col = col
|
||||||
self.total = 0
|
self.total = 0
|
||||||
self.dst = None
|
self.dst = None
|
||||||
|
|
|
@ -7,8 +7,7 @@ import re
|
||||||
|
|
||||||
from anki.importing.noteimp import NoteImporter, ForeignNote
|
from anki.importing.noteimp import NoteImporter, ForeignNote
|
||||||
from anki.lang import _
|
from anki.lang import _
|
||||||
from typing import List
|
from typing import List, Optional, Any, TextIO, Union
|
||||||
|
|
||||||
|
|
||||||
from anki.collection import _Collection
|
from anki.collection import _Collection
|
||||||
class TextImporter(NoteImporter):
|
class TextImporter(NoteImporter):
|
||||||
|
@ -19,10 +18,12 @@ class TextImporter(NoteImporter):
|
||||||
def __init__(self, col: _Collection, file: str) -> None:
|
def __init__(self, col: _Collection, file: str) -> None:
|
||||||
NoteImporter.__init__(self, col, file)
|
NoteImporter.__init__(self, col, file)
|
||||||
self.lines = None
|
self.lines = None
|
||||||
self.fileobj = None
|
self.fileobj: Optional[TextIO] = None
|
||||||
self.delimiter = None
|
self.delimiter: Optional[str] = None
|
||||||
self.tagsToAdd = []
|
self.tagsToAdd: List[str] = []
|
||||||
self.numFields = 0
|
self.numFields = 0
|
||||||
|
self.dialect: Optional[Any]
|
||||||
|
self.data: Optional[Union[str, List[str]]]
|
||||||
|
|
||||||
def foreignNotes(self) -> List[ForeignNote]:
|
def foreignNotes(self) -> List[ForeignNote]:
|
||||||
self.open()
|
self.open()
|
||||||
|
|
|
@ -12,20 +12,19 @@ from anki.utils import fieldChecksum, guid64, timestampID, \
|
||||||
joinFields, intTime, splitFields
|
joinFields, intTime, splitFields
|
||||||
from anki.importing.base import Importer
|
from anki.importing.base import Importer
|
||||||
from anki.lang import ngettext
|
from anki.lang import ngettext
|
||||||
from typing import Any, List, Optional
|
from typing import Any, List, Optional, Tuple, Dict, Union
|
||||||
|
from anki.collection import _Collection
|
||||||
|
|
||||||
# Stores a list of fields, tags and deck
|
# Stores a list of fields, tags and deck
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
from anki.collection import _Collection
|
|
||||||
from typing import List, Optional, Union
|
|
||||||
class ForeignNote:
|
class ForeignNote:
|
||||||
"An temporary object storing fields and attributes."
|
"An temporary object storing fields and attributes."
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.fields = []
|
self.fields: List[str] = []
|
||||||
self.tags = []
|
self.tags: List[str] = []
|
||||||
self.deck = None
|
self.deck = None
|
||||||
self.cards = {} # map of ord -> card
|
self.cards: Dict[int,ForeignCard] = {} # map of ord -> card
|
||||||
self.fieldsStr = ""
|
self.fieldsStr = ""
|
||||||
|
|
||||||
class ForeignCard:
|
class ForeignCard:
|
||||||
|
@ -56,12 +55,12 @@ class NoteImporter(Importer):
|
||||||
needDelimiter = False
|
needDelimiter = False
|
||||||
allowHTML = False
|
allowHTML = False
|
||||||
importMode = 0
|
importMode = 0
|
||||||
|
mapping: Optional[List[str]]
|
||||||
|
|
||||||
def __init__(self, col: _Collection, file: str) -> None:
|
def __init__(self, col: _Collection, file: str) -> None:
|
||||||
Importer.__init__(self, col, file)
|
Importer.__init__(self, col, file)
|
||||||
self.model = col.models.current()
|
self.model = col.models.current()
|
||||||
self.mapping = None
|
self.mapping = None
|
||||||
self._deckMap = {}
|
|
||||||
self._tagsMapped = False
|
self._tagsMapped = False
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
|
@ -105,14 +104,14 @@ 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 = {}
|
csums: Dict[str, List[int]] = {}
|
||||||
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']):
|
||||||
if csum in csums:
|
if csum in csums:
|
||||||
csums[csum].append(id)
|
csums[csum].append(id)
|
||||||
else:
|
else:
|
||||||
csums[csum] = [id]
|
csums[csum] = [id]
|
||||||
firsts = {}
|
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 = timestampID(self.col.db, "notes")
|
||||||
|
@ -122,11 +121,11 @@ class NoteImporter(Importer):
|
||||||
updateLogTxt = _("First field matched: %s")
|
updateLogTxt = _("First field matched: %s")
|
||||||
dupeLogTxt = _("Added duplicate with first field: %s")
|
dupeLogTxt = _("Added duplicate with first field: %s")
|
||||||
new = []
|
new = []
|
||||||
self._ids = []
|
self._ids: List[int] = []
|
||||||
self._cards = []
|
self._cards: List[Tuple] = []
|
||||||
self._emptyNotes = False
|
self._emptyNotes = False
|
||||||
dupeCount = 0
|
dupeCount = 0
|
||||||
dupes = []
|
dupes: List[str] = []
|
||||||
for n in notes:
|
for n in notes:
|
||||||
for c in range(len(n.fields)):
|
for c in range(len(n.fields)):
|
||||||
if not self.allowHTML:
|
if not self.allowHTML:
|
||||||
|
@ -228,7 +227,7 @@ content in the text file to the correct fields."""))
|
||||||
self._nextID += 1
|
self._nextID += 1
|
||||||
self._ids.append(id)
|
self._ids.append(id)
|
||||||
if not self.processFields(n):
|
if not self.processFields(n):
|
||||||
return
|
return None
|
||||||
# note id for card updates later
|
# note id for card updates later
|
||||||
for ord, c in list(n.cards.items()):
|
for ord, c in list(n.cards.items()):
|
||||||
self._cards.append((id, ord, c))
|
self._cards.append((id, ord, c))
|
||||||
|
@ -245,7 +244,7 @@ content in the text file to the correct fields."""))
|
||||||
def updateData(self, n: ForeignNote, id: int, sflds: List[str]) -> Optional[list]:
|
def updateData(self, n: ForeignNote, id: int, sflds: List[str]) -> Optional[list]:
|
||||||
self._ids.append(id)
|
self._ids.append(id)
|
||||||
if not self.processFields(n, sflds):
|
if not self.processFields(n, sflds):
|
||||||
return
|
return None
|
||||||
if self._tagsMapped:
|
if self._tagsMapped:
|
||||||
self.col.tags.register(n.tags)
|
self.col.tags.register(n.tags)
|
||||||
tags = self.col.tags.join(n.tags)
|
tags = self.col.tags.join(n.tags)
|
||||||
|
|
|
@ -6,7 +6,6 @@ import gzip, math, random, time, html
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from anki.importing.noteimp import NoteImporter, ForeignNote, ForeignCard
|
from anki.importing.noteimp import NoteImporter, ForeignNote, ForeignCard
|
||||||
from anki.stdmodels import addForwardReverse
|
from anki.stdmodels import addForwardReverse
|
||||||
from typing import List
|
|
||||||
|
|
||||||
ONE_DAY = 60*60*24
|
ONE_DAY = 60*60*24
|
||||||
|
|
||||||
|
@ -29,7 +28,7 @@ class PaukerImporter(NoteImporter):
|
||||||
'''Pauker is Front/Back'''
|
'''Pauker is Front/Back'''
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
def foreignNotes(self) -> List[ForeignNote]:
|
def foreignNotes(self):
|
||||||
'''Build and return a list of notes.'''
|
'''Build and return a list of notes.'''
|
||||||
notes = []
|
notes = []
|
||||||
|
|
||||||
|
@ -67,7 +66,7 @@ class PaukerImporter(NoteImporter):
|
||||||
|
|
||||||
return notes
|
return notes
|
||||||
|
|
||||||
def _learnedCard(self, batch, timestamp) -> ForeignCard:
|
def _learnedCard(self, batch, timestamp):
|
||||||
ivl = math.exp(batch)
|
ivl = math.exp(batch)
|
||||||
now = time.time()
|
now = time.time()
|
||||||
due = ivl - (now - timestamp/1000.0)/ONE_DAY
|
due = ivl - (now - timestamp/1000.0)/ONE_DAY
|
||||||
|
|
|
@ -276,7 +276,7 @@ def _incGuid(guid) -> str:
|
||||||
def joinFields(list: List[str]) -> str:
|
def joinFields(list: List[str]) -> str:
|
||||||
return "\x1f".join(list)
|
return "\x1f".join(list)
|
||||||
|
|
||||||
def splitFields(string: str) -> Any:
|
def splitFields(string: str) -> List[str]:
|
||||||
return string.split("\x1f")
|
return string.split("\x1f")
|
||||||
|
|
||||||
# Checksums
|
# Checksums
|
||||||
|
|
Loading…
Reference in a new issue