mirror of
https://github.com/ankitects/anki.git
synced 2025-09-25 01:06:35 -04:00
Merge pull request #467 from alanhdu/monkeytype
Add some type annotations [1/n]
This commit is contained in:
commit
244326c130
9 changed files with 82 additions and 54 deletions
|
@ -5,7 +5,7 @@ import os
|
||||||
import time
|
import time
|
||||||
from sqlite3 import Cursor
|
from sqlite3 import Cursor
|
||||||
from sqlite3 import dbapi2 as sqlite
|
from sqlite3 import dbapi2 as sqlite
|
||||||
from typing import Any, List
|
from typing import Any, List, Type
|
||||||
|
|
||||||
DBError = sqlite.Error
|
DBError = sqlite.Error
|
||||||
|
|
||||||
|
@ -110,5 +110,5 @@ class DB:
|
||||||
def _textFactory(self, data: bytes) -> str:
|
def _textFactory(self, data: bytes) -> str:
|
||||||
return str(data, errors="ignore")
|
return str(data, errors="ignore")
|
||||||
|
|
||||||
def cursor(self, factory=Cursor) -> Cursor:
|
def cursor(self, factory: Type[Cursor] = Cursor) -> Cursor:
|
||||||
return self._db.cursor(factory)
|
return self._db.cursor(factory)
|
||||||
|
|
|
@ -446,7 +446,7 @@ class DeckManager:
|
||||||
return deck["name"]
|
return deck["name"]
|
||||||
return _("[no deck]")
|
return _("[no deck]")
|
||||||
|
|
||||||
def nameOrNone(self, did) -> Any:
|
def nameOrNone(self, did: int) -> Any:
|
||||||
deck = self.get(did, default=False)
|
deck = self.get(did, default=False)
|
||||||
if deck:
|
if deck:
|
||||||
return deck["name"]
|
return deck["name"]
|
||||||
|
@ -530,7 +530,7 @@ class DeckManager:
|
||||||
self,
|
self,
|
||||||
force_default: bool = True,
|
force_default: bool = True,
|
||||||
assume_no_child: bool = False,
|
assume_no_child: bool = False,
|
||||||
default_deck=None,
|
default_deck: Optional[Dict[str, Any]] = None,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Whether the default deck should appear in main window, browser side list, filter, deck selection...
|
"""Whether the default deck should appear in main window, browser side list, filter, deck selection...
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ class AnkiError(Exception):
|
||||||
|
|
||||||
|
|
||||||
class DeckRenameError(Exception):
|
class DeckRenameError(Exception):
|
||||||
def __init__(self, description) -> None:
|
def __init__(self, description: str) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.description = description
|
self.description = description
|
||||||
|
|
||||||
|
|
|
@ -33,14 +33,16 @@ if isMac:
|
||||||
os.environ["PATH"] += ":/usr/texbin:/Library/TeX/texbin"
|
os.environ["PATH"] += ":/usr/texbin:/Library/TeX/texbin"
|
||||||
|
|
||||||
|
|
||||||
def on_card_did_render(output: TemplateRenderOutput, ctx: TemplateRenderContext):
|
def on_card_did_render(
|
||||||
|
output: TemplateRenderOutput, ctx: TemplateRenderContext
|
||||||
|
) -> None:
|
||||||
output.question_text = render_latex(
|
output.question_text = render_latex(
|
||||||
output.question_text, ctx.note_type(), ctx.col()
|
output.question_text, ctx.note_type(), ctx.col()
|
||||||
)
|
)
|
||||||
output.answer_text = render_latex(output.answer_text, ctx.note_type(), ctx.col())
|
output.answer_text = render_latex(output.answer_text, ctx.note_type(), ctx.col())
|
||||||
|
|
||||||
|
|
||||||
def render_latex(html: str, model: NoteType, col: anki.storage._Collection,) -> str:
|
def render_latex(html: str, model: NoteType, col: anki.storage._Collection) -> str:
|
||||||
"Convert embedded latex tags in text to image links."
|
"Convert embedded latex tags in text to image links."
|
||||||
html, err = render_latex_returning_errors(html, model, col)
|
html, err = render_latex_returning_errors(html, model, col)
|
||||||
if err:
|
if err:
|
||||||
|
@ -49,7 +51,10 @@ def render_latex(html: str, model: NoteType, col: anki.storage._Collection,) ->
|
||||||
|
|
||||||
|
|
||||||
def render_latex_returning_errors(
|
def render_latex_returning_errors(
|
||||||
html: str, model: NoteType, col: anki.storage._Collection, expand_clozes=False
|
html: str,
|
||||||
|
model: NoteType,
|
||||||
|
col: anki.storage._Collection,
|
||||||
|
expand_clozes: bool = False,
|
||||||
) -> Tuple[str, List[str]]:
|
) -> Tuple[str, List[str]]:
|
||||||
"""Returns (text, errors).
|
"""Returns (text, errors).
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ class Note:
|
||||||
self.mid,
|
self.mid,
|
||||||
self.mod,
|
self.mod,
|
||||||
self.usn,
|
self.usn,
|
||||||
self.tags,
|
tags,
|
||||||
fields,
|
fields,
|
||||||
self.flags,
|
self.flags,
|
||||||
self.data,
|
self.data,
|
||||||
|
@ -74,7 +74,7 @@ from notes where id = ?""",
|
||||||
self.id,
|
self.id,
|
||||||
)
|
)
|
||||||
self.fields = splitFields(fields)
|
self.fields = splitFields(fields)
|
||||||
self.tags = self.col.tags.split(self.tags)
|
self.tags = self.col.tags.split(tags)
|
||||||
self._model = self.col.models.get(self.mid)
|
self._model = self.col.models.get(self.mid)
|
||||||
self._fmap = self.col.models.fieldMap(self._model)
|
self._fmap = self.col.models.fieldMap(self._model)
|
||||||
self.scm = self.col.scm
|
self.scm = self.col.scm
|
||||||
|
|
|
@ -6,7 +6,7 @@ from __future__ import annotations
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
from typing import Any, Dict, List, Optional, Tuple
|
from typing import Any, Dict, List, Optional, Sequence, Tuple, Union
|
||||||
|
|
||||||
import anki
|
import anki
|
||||||
from anki.consts import *
|
from anki.consts import *
|
||||||
|
@ -17,6 +17,7 @@ from anki.utils import ids2str
|
||||||
# Card stats
|
# Card stats
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
|
||||||
PERIOD_MONTH = 0
|
PERIOD_MONTH = 0
|
||||||
PERIOD_YEAR = 1
|
PERIOD_YEAR = 1
|
||||||
PERIOD_LIFE = 2
|
PERIOD_LIFE = 2
|
||||||
|
@ -71,15 +72,15 @@ class CardStats:
|
||||||
self.txt += "</table>"
|
self.txt += "</table>"
|
||||||
return self.txt
|
return self.txt
|
||||||
|
|
||||||
def addLine(self, k, v) -> None:
|
def addLine(self, k: str, v: Union[int, str]) -> None:
|
||||||
self.txt += self.makeLine(k, v)
|
self.txt += self.makeLine(k, v)
|
||||||
|
|
||||||
def makeLine(self, k, v) -> str:
|
def makeLine(self, k: str, v: Union[str, int]) -> str:
|
||||||
txt = "<tr><td align=left style='padding-right: 3px;'>"
|
txt = "<tr><td align=left style='padding-right: 3px;'>"
|
||||||
txt += "<b>%s</b></td><td>%s</td></tr>" % (k, v)
|
txt += "<b>%s</b></td><td>%s</td></tr>" % (k, v)
|
||||||
return txt
|
return txt
|
||||||
|
|
||||||
def date(self, tm) -> str:
|
def date(self, tm: float) -> str:
|
||||||
return time.strftime("%Y-%m-%d", time.localtime(tm))
|
return time.strftime("%Y-%m-%d", time.localtime(tm))
|
||||||
|
|
||||||
def time(self, tm: float) -> str:
|
def time(self, tm: float) -> str:
|
||||||
|
@ -114,7 +115,7 @@ class CollectionStats:
|
||||||
self.wholeCollection = False
|
self.wholeCollection = False
|
||||||
|
|
||||||
# assumes jquery & plot are available in document
|
# assumes jquery & plot are available in document
|
||||||
def report(self, type=PERIOD_MONTH) -> str:
|
def report(self, type: int = PERIOD_MONTH) -> str:
|
||||||
# 0=month, 1=year, 2=deck life
|
# 0=month, 1=year, 2=deck life
|
||||||
self.type = type
|
self.type = type
|
||||||
from .statsbg import bg
|
from .statsbg import bg
|
||||||
|
@ -131,7 +132,7 @@ class CollectionStats:
|
||||||
txt += self._section(self.footer())
|
txt += self._section(self.footer())
|
||||||
return "<center>%s</center>" % txt
|
return "<center>%s</center>" % txt
|
||||||
|
|
||||||
def _section(self, txt) -> str:
|
def _section(self, txt: str) -> str:
|
||||||
return "<div class=section>%s</div>" % txt
|
return "<div class=section>%s</div>" % txt
|
||||||
|
|
||||||
css = """
|
css = """
|
||||||
|
@ -212,7 +213,7 @@ from revlog where id > ? """
|
||||||
# Due and cumulative due
|
# Due and cumulative due
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
def get_start_end_chunk(self, by="review") -> Tuple[int, Optional[int], int]:
|
def get_start_end_chunk(self, by: str = "review") -> Tuple[int, Optional[int], int]:
|
||||||
start = 0
|
start = 0
|
||||||
if self.type == PERIOD_MONTH:
|
if self.type == PERIOD_MONTH:
|
||||||
end, chunk = 31, 1
|
end, chunk = 31, 1
|
||||||
|
@ -273,7 +274,7 @@ from revlog where id > ? """
|
||||||
txt += self._dueInfo(tot, len(totd) * chunk)
|
txt += self._dueInfo(tot, len(totd) * chunk)
|
||||||
return txt
|
return txt
|
||||||
|
|
||||||
def _dueInfo(self, tot, num) -> str:
|
def _dueInfo(self, tot: int, num: int) -> str:
|
||||||
i: List[str] = []
|
i: List[str] = []
|
||||||
self._line(
|
self._line(
|
||||||
i, _("Total"), self.col.tr(TR.STATISTICS_REVIEWS, reviews=tot),
|
i, _("Total"), self.col.tr(TR.STATISTICS_REVIEWS, reviews=tot),
|
||||||
|
@ -290,7 +291,9 @@ and due = ?"""
|
||||||
self._line(i, _("Due tomorrow"), tomorrow)
|
self._line(i, _("Due tomorrow"), tomorrow)
|
||||||
return self._lineTbl(i)
|
return self._lineTbl(i)
|
||||||
|
|
||||||
def _due(self, start=None, end=None, chunk=1) -> Any:
|
def _due(
|
||||||
|
self, start: Optional[int] = None, end: Optional[int] = None, chunk: int = 1
|
||||||
|
) -> Any:
|
||||||
lim = ""
|
lim = ""
|
||||||
if start is not None:
|
if start is not None:
|
||||||
lim += " and due-:today >= %d" % start
|
lim += " and due-:today >= %d" % start
|
||||||
|
@ -414,7 +417,13 @@ group by day order by day"""
|
||||||
return self._section(txt1) + self._section(txt2)
|
return self._section(txt1) + self._section(txt2)
|
||||||
|
|
||||||
def _ansInfo(
|
def _ansInfo(
|
||||||
self, totd, studied, first, unit, convHours=False, total=None
|
self,
|
||||||
|
totd: List[Tuple[int, float]],
|
||||||
|
studied: int,
|
||||||
|
first: int,
|
||||||
|
unit: str,
|
||||||
|
convHours: bool = False,
|
||||||
|
total: Optional[int] = None,
|
||||||
) -> Tuple[str, int]:
|
) -> Tuple[str, int]:
|
||||||
assert totd
|
assert totd
|
||||||
tot = totd[-1][1]
|
tot = totd[-1][1]
|
||||||
|
@ -460,14 +469,16 @@ group by day order by day"""
|
||||||
)
|
)
|
||||||
return self._lineTbl(i), int(tot)
|
return self._lineTbl(i), int(tot)
|
||||||
|
|
||||||
def _splitRepData(self, data, spec) -> Tuple[List[dict], List[Tuple[Any, Any]]]:
|
def _splitRepData(
|
||||||
|
self, data: List[Tuple[Any, ...]], spec: Sequence[Tuple[int, str, str]],
|
||||||
|
) -> Tuple[List[Dict[str, Any]], List[Tuple[Any, Any]]]:
|
||||||
sep: Dict[int, Any] = {}
|
sep: Dict[int, Any] = {}
|
||||||
totcnt = {}
|
totcnt = {}
|
||||||
totd: Dict[int, Any] = {}
|
totd: Dict[int, Any] = {}
|
||||||
alltot = []
|
alltot = []
|
||||||
allcnt = 0
|
allcnt: float = 0
|
||||||
for (n, col, lab) in spec:
|
for (n, col, lab) in spec:
|
||||||
totcnt[n] = 0
|
totcnt[n] = 0.0
|
||||||
totd[n] = []
|
totd[n] = []
|
||||||
for row in data:
|
for row in data:
|
||||||
for (n, col, lab) in spec:
|
for (n, col, lab) in spec:
|
||||||
|
@ -497,7 +508,7 @@ group by day order by day"""
|
||||||
)
|
)
|
||||||
return (ret, alltot)
|
return (ret, alltot)
|
||||||
|
|
||||||
def _added(self, num=7, chunk=1) -> Any:
|
def _added(self, num: Optional[int] = 7, chunk: int = 1) -> Any:
|
||||||
lims = []
|
lims = []
|
||||||
if num is not None:
|
if num is not None:
|
||||||
lims.append(
|
lims.append(
|
||||||
|
@ -525,7 +536,7 @@ group by day order by day"""
|
||||||
chunk=chunk,
|
chunk=chunk,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _done(self, num=7, chunk=1) -> Any:
|
def _done(self, num: Optional[int] = 7, chunk: int = 1) -> Any:
|
||||||
lims = []
|
lims = []
|
||||||
if num is not None:
|
if num is not None:
|
||||||
lims.append(
|
lims.append(
|
||||||
|
@ -637,7 +648,7 @@ group by day order by day)"""
|
||||||
)
|
)
|
||||||
return txt + self._lineTbl(i)
|
return txt + self._lineTbl(i)
|
||||||
|
|
||||||
def _ivls(self) -> Tuple[list, int]:
|
def _ivls(self) -> Tuple[List[Any], int]:
|
||||||
start, end, chunk = self.get_start_end_chunk()
|
start, end, chunk = self.get_start_end_chunk()
|
||||||
lim = "and grp <= %d" % end if end else ""
|
lim = "and grp <= %d" % end if end else ""
|
||||||
data = [
|
data = [
|
||||||
|
@ -712,7 +723,7 @@ select count(), avg(ivl), max(ivl) from cards where did in %s and queue = {QUEUE
|
||||||
txt += self._easeInfo(eases)
|
txt += self._easeInfo(eases)
|
||||||
return txt
|
return txt
|
||||||
|
|
||||||
def _easeInfo(self, eases) -> str:
|
def _easeInfo(self, eases: List[Tuple[int, int, int]]) -> str:
|
||||||
types = {PERIOD_MONTH: [0, 0], PERIOD_YEAR: [0, 0], PERIOD_LIFE: [0, 0]}
|
types = {PERIOD_MONTH: [0, 0], PERIOD_YEAR: [0, 0], PERIOD_LIFE: [0, 0]}
|
||||||
for (type, ease, cnt) in eases:
|
for (type, ease, cnt) in eases:
|
||||||
if ease == 1:
|
if ease == 1:
|
||||||
|
@ -909,7 +920,9 @@ when you answer "good" on a review."""
|
||||||
)
|
)
|
||||||
return txt
|
return txt
|
||||||
|
|
||||||
def _line(self, i, a, b, bold=True) -> None:
|
def _line(
|
||||||
|
self, i: List[str], a: str, b: Union[int, str], bold: bool = True
|
||||||
|
) -> None:
|
||||||
# T: Symbols separating first and second column in a statistics table. Eg in "Total: 3 reviews".
|
# T: Symbols separating first and second column in a statistics table. Eg in "Total: 3 reviews".
|
||||||
colon = _(":")
|
colon = _(":")
|
||||||
if bold:
|
if bold:
|
||||||
|
@ -923,7 +936,7 @@ when you answer "good" on a review."""
|
||||||
% (a, colon, b)
|
% (a, colon, b)
|
||||||
)
|
)
|
||||||
|
|
||||||
def _lineTbl(self, i) -> str:
|
def _lineTbl(self, i: List[str]) -> str:
|
||||||
return "<table width=400>" + "".join(i) + "</table>"
|
return "<table width=400>" + "".join(i) + "</table>"
|
||||||
|
|
||||||
def _factors(self) -> Any:
|
def _factors(self) -> Any:
|
||||||
|
@ -969,7 +982,14 @@ from cards where did in %s"""
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
def _graph(
|
def _graph(
|
||||||
self, id, data, conf=None, type="bars", xunit=1, ylabel=_("Cards"), ylabel2=""
|
self,
|
||||||
|
id: str,
|
||||||
|
data: Any,
|
||||||
|
conf: Optional[Any] = None,
|
||||||
|
type: str = "bars",
|
||||||
|
xunit: int = 1,
|
||||||
|
ylabel: str = _("Cards"),
|
||||||
|
ylabel2: str = "",
|
||||||
) -> str:
|
) -> str:
|
||||||
if conf is None:
|
if conf is None:
|
||||||
conf = {}
|
conf = {}
|
||||||
|
@ -1088,10 +1108,10 @@ $(function () {
|
||||||
self.col.decks.active()
|
self.col.decks.active()
|
||||||
)
|
)
|
||||||
|
|
||||||
def _title(self, title, subtitle="") -> str:
|
def _title(self, title: str, subtitle: str = "") -> str:
|
||||||
return "<h1>%s</h1>%s" % (title, subtitle)
|
return "<h1>%s</h1>%s" % (title, subtitle)
|
||||||
|
|
||||||
def _deckAge(self, by) -> int:
|
def _deckAge(self, by: str) -> int:
|
||||||
lim = self._revlogLimit()
|
lim = self._revlogLimit()
|
||||||
if lim:
|
if lim:
|
||||||
lim = " where " + lim
|
lim = " where " + lim
|
||||||
|
@ -1112,7 +1132,7 @@ $(function () {
|
||||||
return None
|
return None
|
||||||
return end * chunk
|
return end * chunk
|
||||||
|
|
||||||
def _avgDay(self, tot, num, unit) -> str:
|
def _avgDay(self, tot: float, num: int, unit: str) -> str:
|
||||||
vals = []
|
vals = []
|
||||||
try:
|
try:
|
||||||
vals.append(_("%(a)0.1f %(b)s/day") % dict(a=tot / float(num), b=unit))
|
vals.append(_("%(a)0.1f %(b)s/day") % dict(a=tot / float(num), b=unit))
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
# Copyright: Ankitects Pty Ltd and contributors
|
# Copyright: Ankitects Pty Ltd and contributors
|
||||||
# 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 Any, Callable, List, Tuple, Union
|
from typing import Any, Callable, List, Optional, Tuple, Union
|
||||||
|
|
||||||
|
from anki.collection import _Collection
|
||||||
from anki.consts import MODEL_CLOZE
|
from anki.consts import MODEL_CLOZE
|
||||||
from anki.lang import _
|
from anki.lang import _
|
||||||
from anki.models import NoteType
|
from anki.models import NoteType
|
||||||
|
@ -13,7 +14,7 @@ models: List[Tuple[Union[Callable[[], str], str], Callable[[Any], NoteType]]] =
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
|
||||||
def _newBasicModel(col, name=None) -> NoteType:
|
def _newBasicModel(col: _Collection, name: Optional[str] = None) -> NoteType:
|
||||||
mm = col.models
|
mm = col.models
|
||||||
m = mm.new(name or _("Basic"))
|
m = mm.new(name or _("Basic"))
|
||||||
fm = mm.newField(_("Front"))
|
fm = mm.newField(_("Front"))
|
||||||
|
@ -27,7 +28,7 @@ def _newBasicModel(col, name=None) -> NoteType:
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
||||||
def addBasicModel(col) -> NoteType:
|
def addBasicModel(col: _Collection) -> NoteType:
|
||||||
m = _newBasicModel(col)
|
m = _newBasicModel(col)
|
||||||
col.models.add(m)
|
col.models.add(m)
|
||||||
return m
|
return m
|
||||||
|
@ -39,7 +40,7 @@ models.append((lambda: _("Basic"), addBasicModel))
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
|
||||||
def addBasicTypingModel(col) -> NoteType:
|
def addBasicTypingModel(col: _Collection) -> NoteType:
|
||||||
mm = col.models
|
mm = col.models
|
||||||
m = _newBasicModel(col, _("Basic (type in the answer)"))
|
m = _newBasicModel(col, _("Basic (type in the answer)"))
|
||||||
t = m["tmpls"][0]
|
t = m["tmpls"][0]
|
||||||
|
@ -55,7 +56,7 @@ models.append((lambda: _("Basic (type in the answer)"), addBasicTypingModel))
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
|
||||||
def _newForwardReverse(col, name=None) -> NoteType:
|
def _newForwardReverse(col: _Collection, name: Optional[str] = None) -> NoteType:
|
||||||
mm = col.models
|
mm = col.models
|
||||||
m = _newBasicModel(col, name or _("Basic (and reversed card)"))
|
m = _newBasicModel(col, name or _("Basic (and reversed card)"))
|
||||||
t = mm.newTemplate(_("Card 2"))
|
t = mm.newTemplate(_("Card 2"))
|
||||||
|
@ -65,7 +66,7 @@ def _newForwardReverse(col, name=None) -> NoteType:
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
||||||
def addForwardReverse(col) -> NoteType:
|
def addForwardReverse(col: _Collection) -> NoteType:
|
||||||
m = _newForwardReverse(col)
|
m = _newForwardReverse(col)
|
||||||
col.models.add(m)
|
col.models.add(m)
|
||||||
return m
|
return m
|
||||||
|
@ -77,7 +78,7 @@ models.append((lambda: _("Basic (and reversed card)"), addForwardReverse))
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
|
||||||
def addForwardOptionalReverse(col) -> NoteType:
|
def addForwardOptionalReverse(col: _Collection) -> NoteType:
|
||||||
mm = col.models
|
mm = col.models
|
||||||
m = _newForwardReverse(col, _("Basic (optional reversed card)"))
|
m = _newForwardReverse(col, _("Basic (optional reversed card)"))
|
||||||
av = _("Add Reverse")
|
av = _("Add Reverse")
|
||||||
|
@ -95,7 +96,7 @@ models.append((lambda: _("Basic (optional reversed card)"), addForwardOptionalRe
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
|
||||||
def addClozeModel(col) -> NoteType:
|
def addClozeModel(col: _Collection) -> NoteType:
|
||||||
mm = col.models
|
mm = col.models
|
||||||
m = mm.new(_("Cloze"))
|
m = mm.new(_("Cloze"))
|
||||||
m["type"] = MODEL_CLOZE
|
m["type"] = MODEL_CLOZE
|
||||||
|
|
|
@ -13,7 +13,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
from typing import Callable, Dict, List, Tuple
|
from typing import Callable, Collection, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
import anki # pylint: disable=unused-import
|
import anki # pylint: disable=unused-import
|
||||||
from anki import hooks
|
from anki import hooks
|
||||||
|
@ -29,7 +29,7 @@ class TagManager:
|
||||||
self.col = col
|
self.col = col
|
||||||
self.tags: Dict[str, int] = {}
|
self.tags: Dict[str, int] = {}
|
||||||
|
|
||||||
def load(self, json_) -> None:
|
def load(self, json_: str) -> None:
|
||||||
self.tags = json.loads(json_)
|
self.tags = json.loads(json_)
|
||||||
self.changed = False
|
self.changed = False
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ class TagManager:
|
||||||
# Registering and fetching tags
|
# Registering and fetching tags
|
||||||
#############################################################
|
#############################################################
|
||||||
|
|
||||||
def register(self, tags, usn=None) -> None:
|
def register(self, tags: Collection[str], usn: Optional[int] = None) -> None:
|
||||||
"Given a list of tags, add any missing ones to tag registry."
|
"Given a list of tags, add any missing ones to tag registry."
|
||||||
found = False
|
found = False
|
||||||
for t in tags:
|
for t in tags:
|
||||||
|
@ -55,7 +55,7 @@ class TagManager:
|
||||||
def all(self) -> List:
|
def all(self) -> List:
|
||||||
return list(self.tags.keys())
|
return list(self.tags.keys())
|
||||||
|
|
||||||
def registerNotes(self, nids=None) -> None:
|
def registerNotes(self, nids: Optional[List[int]] = None) -> None:
|
||||||
"Add any missing tags from notes to the tags list."
|
"Add any missing tags from notes to the tags list."
|
||||||
# when called without an argument, the old list is cleared first.
|
# when called without an argument, the old list is cleared first.
|
||||||
if nids:
|
if nids:
|
||||||
|
@ -94,7 +94,7 @@ class TagManager:
|
||||||
# Bulk addition/removal from notes
|
# Bulk addition/removal from notes
|
||||||
#############################################################
|
#############################################################
|
||||||
|
|
||||||
def bulkAdd(self, ids, tags, add=True) -> None:
|
def bulkAdd(self, ids: List[int], tags: str, add: bool = True) -> None:
|
||||||
"Add tags in bulk. TAGS is space-separated."
|
"Add tags in bulk. TAGS is space-separated."
|
||||||
newTags = self.split(tags)
|
newTags = self.split(tags)
|
||||||
if not newTags:
|
if not newTags:
|
||||||
|
@ -137,23 +137,23 @@ class TagManager:
|
||||||
[fix(row) for row in res],
|
[fix(row) for row in res],
|
||||||
)
|
)
|
||||||
|
|
||||||
def bulkRem(self, ids, tags) -> None:
|
def bulkRem(self, ids: List[int], tags: str) -> None:
|
||||||
self.bulkAdd(ids, tags, False)
|
self.bulkAdd(ids, tags, False)
|
||||||
|
|
||||||
# String-based utilities
|
# String-based utilities
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def split(self, tags) -> List[str]:
|
def split(self, tags: str) -> List[str]:
|
||||||
"Parse a string and return a list of tags."
|
"Parse a string and return a list of tags."
|
||||||
return [t for t in tags.replace("\u3000", " ").split(" ") if t]
|
return [t for t in tags.replace("\u3000", " ").split(" ") if t]
|
||||||
|
|
||||||
def join(self, tags) -> str:
|
def join(self, tags: List[str]) -> str:
|
||||||
"Join tags into a single string, with leading and trailing spaces."
|
"Join tags into a single string, with leading and trailing spaces."
|
||||||
if not tags:
|
if not tags:
|
||||||
return ""
|
return ""
|
||||||
return " %s " % " ".join(tags)
|
return " %s " % " ".join(tags)
|
||||||
|
|
||||||
def addToStr(self, addtags, tags) -> str:
|
def addToStr(self, addtags: str, tags: str) -> str:
|
||||||
"Add tags if they don't exist, and canonify."
|
"Add tags if they don't exist, and canonify."
|
||||||
currentTags = self.split(tags)
|
currentTags = self.split(tags)
|
||||||
for tag in self.split(addtags):
|
for tag in self.split(addtags):
|
||||||
|
@ -161,7 +161,7 @@ class TagManager:
|
||||||
currentTags.append(tag)
|
currentTags.append(tag)
|
||||||
return self.join(self.canonify(currentTags))
|
return self.join(self.canonify(currentTags))
|
||||||
|
|
||||||
def remFromStr(self, deltags, tags) -> str:
|
def remFromStr(self, deltags: str, tags: str) -> str:
|
||||||
"Delete tags if they exist."
|
"Delete tags if they exist."
|
||||||
|
|
||||||
def wildcard(pat, str):
|
def wildcard(pat, str):
|
||||||
|
@ -183,7 +183,7 @@ class TagManager:
|
||||||
# List-based utilities
|
# List-based utilities
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def canonify(self, tagList) -> List[str]:
|
def canonify(self, tagList: List[str]) -> List[str]:
|
||||||
"Strip duplicates, adjust case to match existing tags, and sort."
|
"Strip duplicates, adjust case to match existing tags, and sort."
|
||||||
strippedTags = []
|
strippedTags = []
|
||||||
for t in tagList:
|
for t in tagList:
|
||||||
|
@ -194,7 +194,7 @@ class TagManager:
|
||||||
strippedTags.append(s)
|
strippedTags.append(s)
|
||||||
return sorted(set(strippedTags))
|
return sorted(set(strippedTags))
|
||||||
|
|
||||||
def inList(self, tag, tags) -> bool:
|
def inList(self, tag: str, tags: List[str]) -> bool:
|
||||||
"True if TAG is in TAGS. Ignore case."
|
"True if TAG is in TAGS. Ignore case."
|
||||||
return tag.lower() in [t.lower() for t in tags]
|
return tag.lower() in [t.lower() for t in tags]
|
||||||
|
|
||||||
|
|
|
@ -143,7 +143,9 @@ def templates_for_card(card: Card, browser: bool) -> Tuple[str, str]:
|
||||||
return q, a # type: ignore
|
return q, a # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def fields_for_rendering(col: anki.storage._Collection, card: Card, note: Note):
|
def fields_for_rendering(
|
||||||
|
col: anki.storage._Collection, card: Card, note: Note
|
||||||
|
) -> Dict[str, str]:
|
||||||
# fields from note
|
# fields from note
|
||||||
fields = dict(note.items())
|
fields = dict(note.items())
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue