mirror of
https://github.com/ankitects/anki.git
synced 2025-09-25 01:06:35 -04:00
remove typings from some other files
not used frequently enough to deal with the mypy errors they're causing at the moment
This commit is contained in:
parent
b6b8df2dcf
commit
d8d7e78b6b
4 changed files with 167 additions and 173 deletions
|
@ -101,7 +101,7 @@ acq_reps+ret_reps, lapses, card_type_id from cards"""):
|
||||||
def fields(self):
|
def fields(self):
|
||||||
return self._fields
|
return self._fields
|
||||||
|
|
||||||
def _mungeField(self, fld) -> str:
|
def _mungeField(self, fld):
|
||||||
# \n -> br
|
# \n -> br
|
||||||
fld = re.sub("\r?\n", "<br>", fld)
|
fld = re.sub("\r?\n", "<br>", fld)
|
||||||
# latex differences
|
# latex differences
|
||||||
|
@ -110,7 +110,7 @@ acq_reps+ret_reps, lapses, card_type_id from cards"""):
|
||||||
fld = re.sub("<audio src=\"(.+?)\">(</audio>)?", "[sound:\\1]", fld)
|
fld = re.sub("<audio src=\"(.+?)\">(</audio>)?", "[sound:\\1]", fld)
|
||||||
return fld
|
return fld
|
||||||
|
|
||||||
def _addFronts(self, notes, model=None, fields=("f", "b")) -> None:
|
def _addFronts(self, notes, model=None, fields=("f", "b")):
|
||||||
data = []
|
data = []
|
||||||
for orig in notes:
|
for orig in notes:
|
||||||
# create a foreign note object
|
# create a foreign note object
|
||||||
|
@ -135,7 +135,7 @@ acq_reps+ret_reps, lapses, card_type_id from cards"""):
|
||||||
# import
|
# import
|
||||||
self.importNotes(data)
|
self.importNotes(data)
|
||||||
|
|
||||||
def _addFrontBacks(self, notes) -> None:
|
def _addFrontBacks(self, notes):
|
||||||
m = addBasicModel(self.col)
|
m = addBasicModel(self.col)
|
||||||
m['name'] = "Mnemosyne-FrontBack"
|
m['name'] = "Mnemosyne-FrontBack"
|
||||||
mm = self.col.models
|
mm = self.col.models
|
||||||
|
@ -145,7 +145,7 @@ acq_reps+ret_reps, lapses, card_type_id from cards"""):
|
||||||
mm.addTemplate(m, t)
|
mm.addTemplate(m, t)
|
||||||
self._addFronts(notes, m)
|
self._addFronts(notes, m)
|
||||||
|
|
||||||
def _addVocabulary(self, notes) -> None:
|
def _addVocabulary(self, notes):
|
||||||
mm = self.col.models
|
mm = self.col.models
|
||||||
m = mm.new("Mnemosyne-Vocabulary")
|
m = mm.new("Mnemosyne-Vocabulary")
|
||||||
for f in "Expression", "Pronunciation", "Meaning", "Notes":
|
for f in "Expression", "Pronunciation", "Meaning", "Notes":
|
||||||
|
@ -164,7 +164,7 @@ acq_reps+ret_reps, lapses, card_type_id from cards"""):
|
||||||
mm.add(m)
|
mm.add(m)
|
||||||
self._addFronts(notes, m, fields=("f", "p_1", "m_1", "n"))
|
self._addFronts(notes, m, fields=("f", "p_1", "m_1", "n"))
|
||||||
|
|
||||||
def _addCloze(self, notes) -> None:
|
def _addCloze(self, notes):
|
||||||
data = []
|
data = []
|
||||||
notes = list(notes.values())
|
notes = list(notes.values())
|
||||||
for orig in notes:
|
for orig in notes:
|
||||||
|
|
|
@ -13,10 +13,7 @@ from anki.lang import ngettext
|
||||||
from xml.dom import minidom
|
from xml.dom import minidom
|
||||||
from string import capwords
|
from string import capwords
|
||||||
import re, unicodedata, time
|
import re, unicodedata, time
|
||||||
from typing import Any, List, Optional
|
|
||||||
|
|
||||||
from anki.collection import _Collection
|
|
||||||
from xml.dom.minidom import Element, Text
|
|
||||||
class SmartDict(dict):
|
class SmartDict(dict):
|
||||||
"""
|
"""
|
||||||
See http://www.peterbe.com/plog/SmartDict
|
See http://www.peterbe.com/plog/SmartDict
|
||||||
|
@ -28,7 +25,7 @@ class SmartDict(dict):
|
||||||
x.get('first_name').
|
x.get('first_name').
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *a, **kw) -> None:
|
def __init__(self, *a, **kw):
|
||||||
if a:
|
if a:
|
||||||
if isinstance(type(a[0]), dict):
|
if isinstance(type(a[0]), dict):
|
||||||
kw.update(a[0])
|
kw.update(a[0])
|
||||||
|
@ -43,7 +40,7 @@ class SmartDict(dict):
|
||||||
class SuperMemoElement(SmartDict):
|
class SuperMemoElement(SmartDict):
|
||||||
"SmartDict wrapper to store SM Element data"
|
"SmartDict wrapper to store SM Element data"
|
||||||
|
|
||||||
def __init__(self, *a, **kw) -> None:
|
def __init__(self, *a, **kw):
|
||||||
SmartDict.__init__(self, *a, **kw)
|
SmartDict.__init__(self, *a, **kw)
|
||||||
#default content
|
#default content
|
||||||
self.__dict__['lTitle'] = None
|
self.__dict__['lTitle'] = None
|
||||||
|
@ -82,7 +79,7 @@ class SupermemoXmlImporter(NoteImporter):
|
||||||
Code should be upgrade to support importing of SM2006 exports.
|
Code should be upgrade to support importing of SM2006 exports.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, col: _Collection, file: str) -> None:
|
def __init__(self, col, file):
|
||||||
"""Initialize internal varables.
|
"""Initialize internal varables.
|
||||||
Pameters to be exposed to GUI are stored in self.META"""
|
Pameters to be exposed to GUI are stored in self.META"""
|
||||||
NoteImporter.__init__(self, col, file)
|
NoteImporter.__init__(self, col, file)
|
||||||
|
@ -122,17 +119,17 @@ class SupermemoXmlImporter(NoteImporter):
|
||||||
|
|
||||||
## TOOLS
|
## TOOLS
|
||||||
|
|
||||||
def _fudgeText(self, text: str) -> Any:
|
def _fudgeText(self, text):
|
||||||
"Replace sm syntax to Anki syntax"
|
"Replace sm syntax to Anki syntax"
|
||||||
text = text.replace("\n\r", "<br>")
|
text = text.replace("\n\r", "<br>")
|
||||||
text = text.replace("\n", "<br>")
|
text = text.replace("\n", "<br>")
|
||||||
return text
|
return text
|
||||||
|
|
||||||
def _unicode2ascii(self,str: str) -> str:
|
def _unicode2ascii(self,str):
|
||||||
"Remove diacritic punctuation from strings (titles)"
|
"Remove diacritic punctuation from strings (titles)"
|
||||||
return "".join([ c for c in unicodedata.normalize('NFKD', str) if not unicodedata.combining(c)])
|
return "".join([ c for c in unicodedata.normalize('NFKD', str) if not unicodedata.combining(c)])
|
||||||
|
|
||||||
def _decode_htmlescapes(self,s: str) -> str:
|
def _decode_htmlescapes(self,s):
|
||||||
"""Unescape HTML code."""
|
"""Unescape HTML code."""
|
||||||
#In case of bad formated html you can import MinimalSoup etc.. see btflsoup source code
|
#In case of bad formated html you can import MinimalSoup etc.. see btflsoup source code
|
||||||
from bs4 import BeautifulSoup as btflsoup
|
from bs4 import BeautifulSoup as btflsoup
|
||||||
|
@ -145,7 +142,7 @@ class SupermemoXmlImporter(NoteImporter):
|
||||||
|
|
||||||
return str(btflsoup(s, "html.parser"))
|
return str(btflsoup(s, "html.parser"))
|
||||||
|
|
||||||
def _afactor2efactor(self, af: float) -> Any:
|
def _afactor2efactor(self, af):
|
||||||
# Adapted from <http://www.supermemo.com/beta/xml/xml-core.htm>
|
# Adapted from <http://www.supermemo.com/beta/xml/xml-core.htm>
|
||||||
|
|
||||||
# Ranges for A-factors and E-factors
|
# Ranges for A-factors and E-factors
|
||||||
|
@ -169,7 +166,7 @@ class SupermemoXmlImporter(NoteImporter):
|
||||||
|
|
||||||
## DEFAULT IMPORTER METHODS
|
## DEFAULT IMPORTER METHODS
|
||||||
|
|
||||||
def foreignNotes(self) -> List[ForeignNote]:
|
def foreignNotes(self):
|
||||||
|
|
||||||
# Load file and parse it by minidom
|
# Load file and parse it by minidom
|
||||||
self.loadSource(self.file)
|
self.loadSource(self.file)
|
||||||
|
@ -185,12 +182,12 @@ class SupermemoXmlImporter(NoteImporter):
|
||||||
self.log.append(ngettext("%d card imported.", "%d cards imported.", self.total) % self.total)
|
self.log.append(ngettext("%d card imported.", "%d cards imported.", self.total) % self.total)
|
||||||
return self.notes
|
return self.notes
|
||||||
|
|
||||||
def fields(self) -> int:
|
def fields(self):
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
## PARSER METHODS
|
## PARSER METHODS
|
||||||
|
|
||||||
def addItemToCards(self,item: SuperMemoElement) -> None:
|
def addItemToCards(self,item):
|
||||||
"This method actually do conversion"
|
"This method actually do conversion"
|
||||||
|
|
||||||
# new anki card
|
# new anki card
|
||||||
|
@ -250,7 +247,7 @@ class SupermemoXmlImporter(NoteImporter):
|
||||||
|
|
||||||
self.notes.append(note)
|
self.notes.append(note)
|
||||||
|
|
||||||
def logger(self,text: str,level: int = 1) -> None:
|
def logger(self,text,level=1):
|
||||||
"Wrapper for Anki logger"
|
"Wrapper for Anki logger"
|
||||||
|
|
||||||
dLevels={0:'',1:'Info',2:'Verbose',3:'Debug'}
|
dLevels={0:'',1:'Info',2:'Verbose',3:'Debug'}
|
||||||
|
@ -262,7 +259,7 @@ class SupermemoXmlImporter(NoteImporter):
|
||||||
|
|
||||||
|
|
||||||
# OPEN AND LOAD
|
# OPEN AND LOAD
|
||||||
def openAnything(self,source) -> Any:
|
def openAnything(self,source):
|
||||||
"Open any source / actually only openig of files is used"
|
"Open any source / actually only openig of files is used"
|
||||||
|
|
||||||
if source == "-":
|
if source == "-":
|
||||||
|
@ -285,7 +282,7 @@ class SupermemoXmlImporter(NoteImporter):
|
||||||
import io
|
import io
|
||||||
return io.StringIO(str(source))
|
return io.StringIO(str(source))
|
||||||
|
|
||||||
def loadSource(self, source: str) -> None:
|
def loadSource(self, source):
|
||||||
"""Load source file and parse with xml.dom.minidom"""
|
"""Load source file and parse with xml.dom.minidom"""
|
||||||
self.source = source
|
self.source = source
|
||||||
self.logger('Load started...')
|
self.logger('Load started...')
|
||||||
|
@ -296,7 +293,7 @@ class SupermemoXmlImporter(NoteImporter):
|
||||||
|
|
||||||
|
|
||||||
# PARSE
|
# PARSE
|
||||||
def parse(self, node: Optional[Any] = None) -> None:
|
def parse(self, node=None):
|
||||||
"Parse method - parses document elements"
|
"Parse method - parses document elements"
|
||||||
|
|
||||||
if node is None and self.xmldoc is not None:
|
if node is None and self.xmldoc is not None:
|
||||||
|
@ -309,12 +306,12 @@ class SupermemoXmlImporter(NoteImporter):
|
||||||
else:
|
else:
|
||||||
self.logger('No handler for method %s' % _method, level=3)
|
self.logger('No handler for method %s' % _method, level=3)
|
||||||
|
|
||||||
def parse_Document(self, node) -> None:
|
def parse_Document(self, node):
|
||||||
"Parse XML document"
|
"Parse XML document"
|
||||||
|
|
||||||
self.parse(node.documentElement)
|
self.parse(node.documentElement)
|
||||||
|
|
||||||
def parse_Element(self, node: Element) -> None:
|
def parse_Element(self, node):
|
||||||
"Parse XML element"
|
"Parse XML element"
|
||||||
|
|
||||||
_method = "do_%s" % node.tagName
|
_method = "do_%s" % node.tagName
|
||||||
|
@ -325,7 +322,7 @@ class SupermemoXmlImporter(NoteImporter):
|
||||||
self.logger('No handler for method %s' % _method, level=3)
|
self.logger('No handler for method %s' % _method, level=3)
|
||||||
#print traceback.print_exc()
|
#print traceback.print_exc()
|
||||||
|
|
||||||
def parse_Text(self, node: Text) -> None:
|
def parse_Text(self, node):
|
||||||
"Parse text inside elements. Text is stored into local buffer."
|
"Parse text inside elements. Text is stored into local buffer."
|
||||||
|
|
||||||
text = node.data
|
text = node.data
|
||||||
|
@ -339,12 +336,12 @@ class SupermemoXmlImporter(NoteImporter):
|
||||||
|
|
||||||
|
|
||||||
# DO
|
# DO
|
||||||
def do_SuperMemoCollection(self, node: Element) -> None:
|
def do_SuperMemoCollection(self, node):
|
||||||
"Process SM Collection"
|
"Process SM Collection"
|
||||||
|
|
||||||
for child in node.childNodes: self.parse(child)
|
for child in node.childNodes: self.parse(child)
|
||||||
|
|
||||||
def do_SuperMemoElement(self, node: Element) -> None:
|
def do_SuperMemoElement(self, node):
|
||||||
"Process SM Element (Type - Title,Topics)"
|
"Process SM Element (Type - Title,Topics)"
|
||||||
|
|
||||||
self.logger('='*45, level=3)
|
self.logger('='*45, level=3)
|
||||||
|
@ -394,14 +391,14 @@ class SupermemoXmlImporter(NoteImporter):
|
||||||
t = self.cntMeta['title'].pop()
|
t = self.cntMeta['title'].pop()
|
||||||
self.logger('End of topic \t- %s' % (t), level=2)
|
self.logger('End of topic \t- %s' % (t), level=2)
|
||||||
|
|
||||||
def do_Content(self, node: Element) -> None:
|
def do_Content(self, node):
|
||||||
"Process SM element Content"
|
"Process SM element Content"
|
||||||
|
|
||||||
for child in node.childNodes:
|
for child in node.childNodes:
|
||||||
if hasattr(child,'tagName') and child.firstChild is not None:
|
if hasattr(child,'tagName') and child.firstChild is not None:
|
||||||
self.cntElm[-1][child.tagName]=child.firstChild.data
|
self.cntElm[-1][child.tagName]=child.firstChild.data
|
||||||
|
|
||||||
def do_LearningData(self, node: Element) -> None:
|
def do_LearningData(self, node):
|
||||||
"Process SM element LearningData"
|
"Process SM element LearningData"
|
||||||
|
|
||||||
for child in node.childNodes:
|
for child in node.childNodes:
|
||||||
|
@ -418,7 +415,7 @@ class SupermemoXmlImporter(NoteImporter):
|
||||||
# for child in node.childNodes: self.parse(child)
|
# for child in node.childNodes: self.parse(child)
|
||||||
# self.cntElm[-1][node.tagName]=self.cntBuf.pop()
|
# self.cntElm[-1][node.tagName]=self.cntBuf.pop()
|
||||||
|
|
||||||
def do_Title(self, node: Element) -> None:
|
def do_Title(self, node):
|
||||||
"Process SM element Title"
|
"Process SM element Title"
|
||||||
|
|
||||||
t = self._decode_htmlescapes(node.firstChild.data)
|
t = self._decode_htmlescapes(node.firstChild.data)
|
||||||
|
@ -428,7 +425,7 @@ class SupermemoXmlImporter(NoteImporter):
|
||||||
self.logger('Start of topic \t- ' + " / ".join(self.cntMeta['title']), level=2)
|
self.logger('Start of topic \t- ' + " / ".join(self.cntMeta['title']), level=2)
|
||||||
|
|
||||||
|
|
||||||
def do_Type(self, node: Element) -> None:
|
def do_Type(self, node):
|
||||||
"Process SM element Type"
|
"Process SM element Type"
|
||||||
|
|
||||||
if len(self.cntBuf) >=1 :
|
if len(self.cntBuf) >=1 :
|
||||||
|
|
71
anki/mpv.py
71
anki/mpv.py
|
@ -39,6 +39,8 @@ import inspect
|
||||||
from distutils.spawn import find_executable # pylint: disable=import-error,no-name-in-module
|
from distutils.spawn import find_executable # pylint: disable=import-error,no-name-in-module
|
||||||
from queue import Queue, Empty, Full
|
from queue import Queue, Empty, Full
|
||||||
|
|
||||||
|
from typing import Dict, Optional
|
||||||
|
|
||||||
|
|
||||||
class MPVError(Exception):
|
class MPVError(Exception):
|
||||||
pass
|
pass
|
||||||
|
@ -56,7 +58,6 @@ class MPVTimeoutError(MPVError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
from anki.utils import isWin
|
from anki.utils import isWin
|
||||||
from typing import Any
|
|
||||||
if isWin:
|
if isWin:
|
||||||
# pylint: disable=import-error
|
# pylint: disable=import-error
|
||||||
import win32file, win32pipe, pywintypes, winerror # pytype: disable=import-error
|
import win32file, win32pipe, pywintypes, winerror # pytype: disable=import-error
|
||||||
|
@ -66,7 +67,7 @@ class MPVBase:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
executable = find_executable("mpv")
|
executable = find_executable("mpv")
|
||||||
popenEnv = None
|
popenEnv: Optional[Dict[str,str]] = None
|
||||||
|
|
||||||
default_argv = [
|
default_argv = [
|
||||||
"--idle",
|
"--idle",
|
||||||
|
@ -77,7 +78,7 @@ class MPVBase:
|
||||||
"--keep-open=no",
|
"--keep-open=no",
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, window_id=None, debug=False) -> None:
|
def __init__(self, window_id=None, debug=False):
|
||||||
self.window_id = window_id
|
self.window_id = window_id
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
|
|
||||||
|
@ -88,18 +89,18 @@ class MPVBase:
|
||||||
self._prepare_thread()
|
self._prepare_thread()
|
||||||
self._start_thread()
|
self._start_thread()
|
||||||
|
|
||||||
def __del__(self) -> None:
|
def __del__(self):
|
||||||
self._stop_thread()
|
self._stop_thread()
|
||||||
self._stop_process()
|
self._stop_process()
|
||||||
self._stop_socket()
|
self._stop_socket()
|
||||||
|
|
||||||
def _thread_id(self) -> int:
|
def _thread_id(self):
|
||||||
return threading.get_ident()
|
return threading.get_ident()
|
||||||
|
|
||||||
#
|
#
|
||||||
# Process
|
# Process
|
||||||
#
|
#
|
||||||
def _prepare_process(self) -> None:
|
def _prepare_process(self):
|
||||||
"""Prepare the argument list for the mpv process.
|
"""Prepare the argument list for the mpv process.
|
||||||
"""
|
"""
|
||||||
self.argv = [self.executable]
|
self.argv = [self.executable]
|
||||||
|
@ -108,12 +109,12 @@ class MPVBase:
|
||||||
if self.window_id is not None:
|
if self.window_id is not None:
|
||||||
self.argv += ["--wid", str(self.window_id)]
|
self.argv += ["--wid", str(self.window_id)]
|
||||||
|
|
||||||
def _start_process(self) -> None:
|
def _start_process(self):
|
||||||
"""Start the mpv process.
|
"""Start the mpv process.
|
||||||
"""
|
"""
|
||||||
self._proc = subprocess.Popen(self.argv, env=self.popenEnv)
|
self._proc = subprocess.Popen(self.argv, env=self.popenEnv)
|
||||||
|
|
||||||
def _stop_process(self) -> None:
|
def _stop_process(self):
|
||||||
"""Stop the mpv process.
|
"""Stop the mpv process.
|
||||||
"""
|
"""
|
||||||
if hasattr(self, "_proc"):
|
if hasattr(self, "_proc"):
|
||||||
|
@ -126,7 +127,7 @@ class MPVBase:
|
||||||
#
|
#
|
||||||
# Socket communication
|
# Socket communication
|
||||||
#
|
#
|
||||||
def _prepare_socket(self) -> None:
|
def _prepare_socket(self):
|
||||||
"""Create a random socket filename which we pass to mpv with the
|
"""Create a random socket filename which we pass to mpv with the
|
||||||
--input-unix-socket option.
|
--input-unix-socket option.
|
||||||
"""
|
"""
|
||||||
|
@ -137,7 +138,7 @@ class MPVBase:
|
||||||
os.close(fd)
|
os.close(fd)
|
||||||
os.remove(self._sock_filename)
|
os.remove(self._sock_filename)
|
||||||
|
|
||||||
def _start_socket(self) -> None:
|
def _start_socket(self):
|
||||||
"""Wait for the mpv process to create the unix socket and finish
|
"""Wait for the mpv process to create the unix socket and finish
|
||||||
startup.
|
startup.
|
||||||
"""
|
"""
|
||||||
|
@ -174,7 +175,7 @@ class MPVBase:
|
||||||
else:
|
else:
|
||||||
raise MPVProcessError("unable to start process")
|
raise MPVProcessError("unable to start process")
|
||||||
|
|
||||||
def _stop_socket(self) -> None:
|
def _stop_socket(self):
|
||||||
"""Clean up the socket.
|
"""Clean up the socket.
|
||||||
"""
|
"""
|
||||||
if hasattr(self, "_sock"):
|
if hasattr(self, "_sock"):
|
||||||
|
@ -185,7 +186,7 @@ class MPVBase:
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _prepare_thread(self) -> None:
|
def _prepare_thread(self):
|
||||||
"""Set up the queues for the communication threads.
|
"""Set up the queues for the communication threads.
|
||||||
"""
|
"""
|
||||||
self._request_queue = Queue(1)
|
self._request_queue = Queue(1)
|
||||||
|
@ -193,14 +194,14 @@ class MPVBase:
|
||||||
self._event_queue = Queue()
|
self._event_queue = Queue()
|
||||||
self._stop_event = threading.Event()
|
self._stop_event = threading.Event()
|
||||||
|
|
||||||
def _start_thread(self) -> None:
|
def _start_thread(self):
|
||||||
"""Start up the communication threads.
|
"""Start up the communication threads.
|
||||||
"""
|
"""
|
||||||
self._thread = threading.Thread(target=self._reader)
|
self._thread = threading.Thread(target=self._reader)
|
||||||
self._thread.daemon = True
|
self._thread.daemon = True
|
||||||
self._thread.start()
|
self._thread.start()
|
||||||
|
|
||||||
def _stop_thread(self) -> None:
|
def _stop_thread(self):
|
||||||
"""Stop the communication threads.
|
"""Stop the communication threads.
|
||||||
"""
|
"""
|
||||||
if hasattr(self, "_stop_event"):
|
if hasattr(self, "_stop_event"):
|
||||||
|
@ -208,7 +209,7 @@ class MPVBase:
|
||||||
if hasattr(self, "_thread"):
|
if hasattr(self, "_thread"):
|
||||||
self._thread.join()
|
self._thread.join()
|
||||||
|
|
||||||
def _reader(self) -> None:
|
def _reader(self):
|
||||||
"""Read the incoming json messages from the unix socket that is
|
"""Read the incoming json messages from the unix socket that is
|
||||||
connected to the mpv process. Pass them on to the message handler.
|
connected to the mpv process. Pass them on to the message handler.
|
||||||
"""
|
"""
|
||||||
|
@ -250,21 +251,21 @@ class MPVBase:
|
||||||
#
|
#
|
||||||
# Message handling
|
# Message handling
|
||||||
#
|
#
|
||||||
def _compose_message(self, message) -> bytes:
|
def _compose_message(self, message):
|
||||||
"""Return a json representation from a message dictionary.
|
"""Return a json representation from a message dictionary.
|
||||||
"""
|
"""
|
||||||
# XXX may be strict is too strict ;-)
|
# XXX may be strict is too strict ;-)
|
||||||
data = json.dumps(message)
|
data = json.dumps(message)
|
||||||
return data.encode("utf8", "strict") + b"\n"
|
return data.encode("utf8", "strict") + b"\n"
|
||||||
|
|
||||||
def _parse_message(self, data) -> Any:
|
def _parse_message(self, data):
|
||||||
"""Return a message dictionary from a json representation.
|
"""Return a message dictionary from a json representation.
|
||||||
"""
|
"""
|
||||||
# XXX may be strict is too strict ;-)
|
# XXX may be strict is too strict ;-)
|
||||||
data = data.decode("utf8", "strict")
|
data = data.decode("utf8", "strict")
|
||||||
return json.loads(data)
|
return json.loads(data)
|
||||||
|
|
||||||
def _handle_message(self, message) -> None:
|
def _handle_message(self, message):
|
||||||
"""Handle different types of incoming messages, i.e. responses to
|
"""Handle different types of incoming messages, i.e. responses to
|
||||||
commands or asynchronous events.
|
commands or asynchronous events.
|
||||||
"""
|
"""
|
||||||
|
@ -284,7 +285,7 @@ class MPVBase:
|
||||||
else:
|
else:
|
||||||
raise MPVCommunicationError("invalid message %r" % message)
|
raise MPVCommunicationError("invalid message %r" % message)
|
||||||
|
|
||||||
def _send_message(self, message, timeout=None) -> None:
|
def _send_message(self, message, timeout=None):
|
||||||
"""Send a message/command to the mpv process, message must be a
|
"""Send a message/command to the mpv process, message must be a
|
||||||
dictionary of the form {"command": ["arg1", "arg2", ...]}. Responses
|
dictionary of the form {"command": ["arg1", "arg2", ...]}. Responses
|
||||||
from the mpv process must be collected using _get_response().
|
from the mpv process must be collected using _get_response().
|
||||||
|
@ -321,7 +322,7 @@ class MPVBase:
|
||||||
raise MPVCommunicationError("broken sender socket")
|
raise MPVCommunicationError("broken sender socket")
|
||||||
data = data[size:]
|
data = data[size:]
|
||||||
|
|
||||||
def _get_response(self, timeout=None) -> Any:
|
def _get_response(self, timeout=None):
|
||||||
"""Collect the response message to a previous request. If there was an
|
"""Collect the response message to a previous request. If there was an
|
||||||
error a MPVCommandError exception is raised, otherwise the command
|
error a MPVCommandError exception is raised, otherwise the command
|
||||||
specific data is returned.
|
specific data is returned.
|
||||||
|
@ -336,7 +337,7 @@ class MPVBase:
|
||||||
else:
|
else:
|
||||||
return message.get("data")
|
return message.get("data")
|
||||||
|
|
||||||
def _get_event(self, timeout=None) -> Any:
|
def _get_event(self, timeout=None):
|
||||||
"""Collect a single event message that has been received out-of-band
|
"""Collect a single event message that has been received out-of-band
|
||||||
from the mpv process. If a timeout is specified and there have not
|
from the mpv process. If a timeout is specified and there have not
|
||||||
been any events during that period, None is returned.
|
been any events during that period, None is returned.
|
||||||
|
@ -346,7 +347,7 @@ class MPVBase:
|
||||||
except Empty:
|
except Empty:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _send_request(self, message, timeout=None, _retry=1) -> Any:
|
def _send_request(self, message, timeout=None, _retry=1):
|
||||||
"""Send a command to the mpv process and collect the result.
|
"""Send a command to the mpv process and collect the result.
|
||||||
"""
|
"""
|
||||||
self.ensure_running()
|
self.ensure_running()
|
||||||
|
@ -366,12 +367,12 @@ class MPVBase:
|
||||||
#
|
#
|
||||||
# Public API
|
# Public API
|
||||||
#
|
#
|
||||||
def is_running(self) -> bool:
|
def is_running(self):
|
||||||
"""Return True if the mpv process is still active.
|
"""Return True if the mpv process is still active.
|
||||||
"""
|
"""
|
||||||
return self._proc.poll() is None
|
return self._proc.poll() is None
|
||||||
|
|
||||||
def ensure_running(self) -> None:
|
def ensure_running(self):
|
||||||
if not self.is_running():
|
if not self.is_running():
|
||||||
self._stop_thread()
|
self._stop_thread()
|
||||||
self._stop_process()
|
self._stop_process()
|
||||||
|
@ -383,7 +384,7 @@ class MPVBase:
|
||||||
self._prepare_thread()
|
self._prepare_thread()
|
||||||
self._start_thread()
|
self._start_thread()
|
||||||
|
|
||||||
def close(self) -> None:
|
def close(self):
|
||||||
"""Shutdown the mpv process and our communication setup.
|
"""Shutdown the mpv process and our communication setup.
|
||||||
"""
|
"""
|
||||||
if self.is_running():
|
if self.is_running():
|
||||||
|
@ -414,7 +415,7 @@ class MPV(MPVBase):
|
||||||
threads to the same MPV instance are synchronized.
|
threads to the same MPV instance are synchronized.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs) -> None:
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self._callbacks = {}
|
self._callbacks = {}
|
||||||
|
@ -464,7 +465,7 @@ class MPV(MPVBase):
|
||||||
#
|
#
|
||||||
# Event/callback API
|
# Event/callback API
|
||||||
#
|
#
|
||||||
def _event_reader(self) -> None:
|
def _event_reader(self):
|
||||||
"""Collect incoming event messages and call the event handler.
|
"""Collect incoming event messages and call the event handler.
|
||||||
"""
|
"""
|
||||||
while not self._stop_event.is_set():
|
while not self._stop_event.is_set():
|
||||||
|
@ -474,7 +475,7 @@ class MPV(MPVBase):
|
||||||
|
|
||||||
self._handle_event(message)
|
self._handle_event(message)
|
||||||
|
|
||||||
def _handle_event(self, message) -> None:
|
def _handle_event(self, message):
|
||||||
"""Lookup and call the callbacks for a particular event message.
|
"""Lookup and call the callbacks for a particular event message.
|
||||||
"""
|
"""
|
||||||
if message["event"] == "property-change":
|
if message["event"] == "property-change":
|
||||||
|
@ -488,7 +489,7 @@ class MPV(MPVBase):
|
||||||
else:
|
else:
|
||||||
callback()
|
callback()
|
||||||
|
|
||||||
def register_callback(self, name, callback) -> None:
|
def register_callback(self, name, callback):
|
||||||
"""Register a function `callback` for the event `name`.
|
"""Register a function `callback` for the event `name`.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
@ -498,7 +499,7 @@ class MPV(MPVBase):
|
||||||
|
|
||||||
self._callbacks.setdefault(name, []).append(callback)
|
self._callbacks.setdefault(name, []).append(callback)
|
||||||
|
|
||||||
def unregister_callback(self, name, callback) -> None:
|
def unregister_callback(self, name, callback):
|
||||||
"""Unregister a previously registered function `callback` for the event
|
"""Unregister a previously registered function `callback` for the event
|
||||||
`name`.
|
`name`.
|
||||||
"""
|
"""
|
||||||
|
@ -512,7 +513,7 @@ class MPV(MPVBase):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise MPVError("callback %r not registered for event %r" % (callback, name))
|
raise MPVError("callback %r not registered for event %r" % (callback, name))
|
||||||
|
|
||||||
def register_property_callback(self, name, callback) -> int:
|
def register_property_callback(self, name, callback):
|
||||||
"""Register a function `callback` for the property-change event on
|
"""Register a function `callback` for the property-change event on
|
||||||
property `name`.
|
property `name`.
|
||||||
"""
|
"""
|
||||||
|
@ -534,7 +535,7 @@ class MPV(MPVBase):
|
||||||
self._property_serials[(name, callback)] = serial
|
self._property_serials[(name, callback)] = serial
|
||||||
return serial
|
return serial
|
||||||
|
|
||||||
def unregister_property_callback(self, name, callback) -> None:
|
def unregister_property_callback(self, name, callback):
|
||||||
"""Unregister a previously registered function `callback` for the
|
"""Unregister a previously registered function `callback` for the
|
||||||
property-change event on property `name`.
|
property-change event on property `name`.
|
||||||
"""
|
"""
|
||||||
|
@ -554,17 +555,17 @@ class MPV(MPVBase):
|
||||||
#
|
#
|
||||||
# Public API
|
# Public API
|
||||||
#
|
#
|
||||||
def command(self, *args, timeout=1) -> Any:
|
def command(self, *args, timeout=1):
|
||||||
"""Execute a single command on the mpv process and return the result.
|
"""Execute a single command on the mpv process and return the result.
|
||||||
"""
|
"""
|
||||||
return self._send_request({"command": list(args)}, timeout=timeout)
|
return self._send_request({"command": list(args)}, timeout=timeout)
|
||||||
|
|
||||||
def get_property(self, name) -> Any:
|
def get_property(self, name):
|
||||||
"""Return the value of property `name`.
|
"""Return the value of property `name`.
|
||||||
"""
|
"""
|
||||||
return self.command("get_property", name)
|
return self.command("get_property", name)
|
||||||
|
|
||||||
def set_property(self, name, value) -> Any:
|
def set_property(self, name, value):
|
||||||
"""Set the value of property `name`.
|
"""Set the value of property `name`.
|
||||||
"""
|
"""
|
||||||
return self.command("set_property", name, value)
|
return self.command("set_property", name, value)
|
||||||
|
|
210
anki/sched.py
210
anki/sched.py
|
@ -14,10 +14,6 @@ from anki.lang import _
|
||||||
from anki.consts import *
|
from anki.consts import *
|
||||||
from anki.hooks import runHook
|
from anki.hooks import runHook
|
||||||
|
|
||||||
from anki.cards import Card
|
|
||||||
#from anki.collection import _Collection
|
|
||||||
from typing import Any, Callable, Dict, List, Optional, Union, Tuple
|
|
||||||
|
|
||||||
# queue types: 0=new/cram, 1=lrn, 2=rev, 3=day lrn, -1=suspended, -2=buried
|
# queue types: 0=new/cram, 1=lrn, 2=rev, 3=day lrn, -1=suspended, -2=buried
|
||||||
# revlog types: 0=lrn, 1=rev, 2=relrn, 3=cram
|
# revlog types: 0=lrn, 1=rev, 2=relrn, 3=cram
|
||||||
# positive revlog intervals are in days (rev), negative in seconds (lrn)
|
# positive revlog intervals are in days (rev), negative in seconds (lrn)
|
||||||
|
@ -28,7 +24,7 @@ class Scheduler:
|
||||||
_spreadRev = True
|
_spreadRev = True
|
||||||
_burySiblingsOnAnswer = True
|
_burySiblingsOnAnswer = True
|
||||||
|
|
||||||
def __init__(self, col) -> None:
|
def __init__(self, col):
|
||||||
self.col = col
|
self.col = col
|
||||||
self.queueLimit = 50
|
self.queueLimit = 50
|
||||||
self.reportLimit = 1000
|
self.reportLimit = 1000
|
||||||
|
@ -37,7 +33,7 @@ class Scheduler:
|
||||||
self._haveQueues = False
|
self._haveQueues = False
|
||||||
self._updateCutoff()
|
self._updateCutoff()
|
||||||
|
|
||||||
def getCard(self) -> Any:
|
def getCard(self):
|
||||||
"Pop the next card from the queue. None if finished."
|
"Pop the next card from the queue. None if finished."
|
||||||
self._checkDay()
|
self._checkDay()
|
||||||
if not self._haveQueues:
|
if not self._haveQueues:
|
||||||
|
@ -51,14 +47,14 @@ class Scheduler:
|
||||||
card.startTimer()
|
card.startTimer()
|
||||||
return card
|
return card
|
||||||
|
|
||||||
def reset(self) -> None:
|
def reset(self):
|
||||||
self._updateCutoff()
|
self._updateCutoff()
|
||||||
self._resetLrn()
|
self._resetLrn()
|
||||||
self._resetRev()
|
self._resetRev()
|
||||||
self._resetNew()
|
self._resetNew()
|
||||||
self._haveQueues = True
|
self._haveQueues = True
|
||||||
|
|
||||||
def answerCard(self, card: Card, ease: int) -> None:
|
def answerCard(self, card, ease):
|
||||||
self.col.log()
|
self.col.log()
|
||||||
assert 1 <= ease <= 4
|
assert 1 <= ease <= 4
|
||||||
self.col.markReview(card)
|
self.col.markReview(card)
|
||||||
|
@ -97,7 +93,7 @@ class Scheduler:
|
||||||
card.usn = self.col.usn()
|
card.usn = self.col.usn()
|
||||||
card.flushSched()
|
card.flushSched()
|
||||||
|
|
||||||
def counts(self, card: None = None) -> tuple:
|
def counts(self, card=None):
|
||||||
counts = [self.newCount, self.lrnCount, self.revCount]
|
counts = [self.newCount, self.lrnCount, self.revCount]
|
||||||
if card:
|
if card:
|
||||||
idx = self.countIdx(card)
|
idx = self.countIdx(card)
|
||||||
|
@ -107,7 +103,7 @@ class Scheduler:
|
||||||
counts[idx] += 1
|
counts[idx] += 1
|
||||||
return tuple(counts)
|
return tuple(counts)
|
||||||
|
|
||||||
def dueForecast(self, days=7) -> List:
|
def dueForecast(self, days=7):
|
||||||
"Return counts over next DAYS. Includes today."
|
"Return counts over next DAYS. Includes today."
|
||||||
daysd = dict(self.col.db.all("""
|
daysd = dict(self.col.db.all("""
|
||||||
select due, count() from cards
|
select due, count() from cards
|
||||||
|
@ -125,12 +121,12 @@ order by due""" % self._deckLimit(),
|
||||||
ret = [x[1] for x in sorted(daysd.items())]
|
ret = [x[1] for x in sorted(daysd.items())]
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def countIdx(self, card: Card) -> Any:
|
def countIdx(self, card):
|
||||||
if card.queue == 3:
|
if card.queue == 3:
|
||||||
return 1
|
return 1
|
||||||
return card.queue
|
return card.queue
|
||||||
|
|
||||||
def answerButtons(self, card: Card) -> int:
|
def answerButtons(self, card):
|
||||||
if card.odue:
|
if card.odue:
|
||||||
# normal review in dyn deck?
|
# normal review in dyn deck?
|
||||||
if card.odid and card.queue == 2:
|
if card.odid and card.queue == 2:
|
||||||
|
@ -144,7 +140,7 @@ order by due""" % self._deckLimit(),
|
||||||
else:
|
else:
|
||||||
return 3
|
return 3
|
||||||
|
|
||||||
def unburyCards(self) -> None:
|
def unburyCards(self):
|
||||||
"Unbury cards."
|
"Unbury cards."
|
||||||
self.col.conf['lastUnburied'] = self.today
|
self.col.conf['lastUnburied'] = self.today
|
||||||
self.col.log(
|
self.col.log(
|
||||||
|
@ -152,7 +148,7 @@ order by due""" % self._deckLimit(),
|
||||||
self.col.db.execute(
|
self.col.db.execute(
|
||||||
"update cards set queue=type where queue = -2")
|
"update cards set queue=type where queue = -2")
|
||||||
|
|
||||||
def unburyCardsForDeck(self) -> None:
|
def unburyCardsForDeck(self):
|
||||||
sids = ids2str(self.col.decks.active())
|
sids = ids2str(self.col.decks.active())
|
||||||
self.col.log(
|
self.col.log(
|
||||||
self.col.db.list("select id from cards where queue = -2 and did in %s"
|
self.col.db.list("select id from cards where queue = -2 and did in %s"
|
||||||
|
@ -164,7 +160,7 @@ order by due""" % self._deckLimit(),
|
||||||
# Rev/lrn/time daily stats
|
# Rev/lrn/time daily stats
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _updateStats(self, card: Card, type: str, cnt: int = 1) -> None:
|
def _updateStats(self, card, type, cnt=1):
|
||||||
key = type+"Today"
|
key = type+"Today"
|
||||||
for g in ([self.col.decks.get(card.did)] +
|
for g in ([self.col.decks.get(card.did)] +
|
||||||
self.col.decks.parents(card.did)):
|
self.col.decks.parents(card.did)):
|
||||||
|
@ -172,7 +168,7 @@ order by due""" % self._deckLimit(),
|
||||||
g[key][1] += cnt
|
g[key][1] += cnt
|
||||||
self.col.decks.save(g)
|
self.col.decks.save(g)
|
||||||
|
|
||||||
def extendLimits(self, new, rev) -> None:
|
def extendLimits(self, new, rev):
|
||||||
cur = self.col.decks.current()
|
cur = self.col.decks.current()
|
||||||
parents = self.col.decks.parents(cur['id'])
|
parents = self.col.decks.parents(cur['id'])
|
||||||
children = [self.col.decks.get(did) for (name, did) in
|
children = [self.col.decks.get(did) for (name, did) in
|
||||||
|
@ -183,7 +179,7 @@ order by due""" % self._deckLimit(),
|
||||||
g['revToday'][1] -= rev
|
g['revToday'][1] -= rev
|
||||||
self.col.decks.save(g)
|
self.col.decks.save(g)
|
||||||
|
|
||||||
def _walkingCount(self, limFn: Optional[Callable] = None, cntFn: Optional[Callable] = None) -> Any:
|
def _walkingCount(self, limFn=None, cntFn=None):
|
||||||
tot = 0
|
tot = 0
|
||||||
pcounts = {}
|
pcounts = {}
|
||||||
# for each of the active decks
|
# for each of the active decks
|
||||||
|
@ -217,7 +213,7 @@ order by due""" % self._deckLimit(),
|
||||||
# Deck list
|
# Deck list
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def deckDueList(self) -> List[list]:
|
def deckDueList(self):
|
||||||
"Returns [deckname, did, rev, lrn, new]"
|
"Returns [deckname, did, rev, lrn, new]"
|
||||||
self._checkDay()
|
self._checkDay()
|
||||||
self.col.decks.checkIntegrity()
|
self.col.decks.checkIntegrity()
|
||||||
|
@ -251,10 +247,10 @@ order by due""" % self._deckLimit(),
|
||||||
lims[deck['name']] = [nlim, rlim]
|
lims[deck['name']] = [nlim, rlim]
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def deckDueTree(self) -> Any:
|
def deckDueTree(self):
|
||||||
return self._groupChildren(self.deckDueList())
|
return self._groupChildren(self.deckDueList())
|
||||||
|
|
||||||
def _groupChildren(self, grps: List[List]) -> Tuple[Tuple[Any, Any, Any, Any, Any, Any], ...]:
|
def _groupChildren(self, grps):
|
||||||
# first, split the group names into components
|
# first, split the group names into components
|
||||||
for g in grps:
|
for g in grps:
|
||||||
g[0] = g[0].split("::")
|
g[0] = g[0].split("::")
|
||||||
|
@ -263,7 +259,7 @@ order by due""" % self._deckLimit(),
|
||||||
# then run main function
|
# then run main function
|
||||||
return self._groupChildrenMain(grps)
|
return self._groupChildrenMain(grps)
|
||||||
|
|
||||||
def _groupChildrenMain(self, grps: List[List[Union[List[str], int]]]) -> Tuple[Tuple[Any, Any, Any, Any, Any, Any], ...]:
|
def _groupChildrenMain(self, grps):
|
||||||
tree = []
|
tree = []
|
||||||
# group and recurse
|
# group and recurse
|
||||||
def key(grp):
|
def key(grp):
|
||||||
|
@ -304,7 +300,7 @@ order by due""" % self._deckLimit(),
|
||||||
# Getting the next card
|
# Getting the next card
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _getCard(self) -> Any:
|
def _getCard(self):
|
||||||
"Return the next due card id, or None."
|
"Return the next due card id, or None."
|
||||||
# learning card due?
|
# learning card due?
|
||||||
c = self._getLrnCard()
|
c = self._getLrnCard()
|
||||||
|
@ -333,19 +329,19 @@ order by due""" % self._deckLimit(),
|
||||||
# New cards
|
# New cards
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _resetNewCount(self) -> None:
|
def _resetNewCount(self):
|
||||||
cntFn = lambda did, lim: self.col.db.scalar("""
|
cntFn = lambda did, lim: self.col.db.scalar("""
|
||||||
select count() from (select 1 from cards where
|
select count() from (select 1 from cards where
|
||||||
did = ? and queue = 0 limit ?)""", did, lim)
|
did = ? and queue = 0 limit ?)""", did, lim)
|
||||||
self.newCount = self._walkingCount(self._deckNewLimitSingle, cntFn)
|
self.newCount = self._walkingCount(self._deckNewLimitSingle, cntFn)
|
||||||
|
|
||||||
def _resetNew(self) -> None:
|
def _resetNew(self):
|
||||||
self._resetNewCount()
|
self._resetNewCount()
|
||||||
self._newDids = self.col.decks.active()[:]
|
self._newDids = self.col.decks.active()[:]
|
||||||
self._newQueue = []
|
self._newQueue = []
|
||||||
self._updateNewCardRatio()
|
self._updateNewCardRatio()
|
||||||
|
|
||||||
def _fillNew(self) -> Any:
|
def _fillNew(self):
|
||||||
if self._newQueue:
|
if self._newQueue:
|
||||||
return True
|
return True
|
||||||
if not self.newCount:
|
if not self.newCount:
|
||||||
|
@ -369,12 +365,12 @@ did = ? and queue = 0 limit ?)""", did, lim)
|
||||||
self._resetNew()
|
self._resetNew()
|
||||||
return self._fillNew()
|
return self._fillNew()
|
||||||
|
|
||||||
def _getNewCard(self) -> Any:
|
def _getNewCard(self):
|
||||||
if self._fillNew():
|
if self._fillNew():
|
||||||
self.newCount -= 1
|
self.newCount -= 1
|
||||||
return self.col.getCard(self._newQueue.pop())
|
return self.col.getCard(self._newQueue.pop())
|
||||||
|
|
||||||
def _updateNewCardRatio(self) -> None:
|
def _updateNewCardRatio(self):
|
||||||
if self.col.conf['newSpread'] == NEW_CARDS_DISTRIBUTE:
|
if self.col.conf['newSpread'] == NEW_CARDS_DISTRIBUTE:
|
||||||
if self.newCount:
|
if self.newCount:
|
||||||
self.newCardModulus = (
|
self.newCardModulus = (
|
||||||
|
@ -385,7 +381,7 @@ did = ? and queue = 0 limit ?)""", did, lim)
|
||||||
return
|
return
|
||||||
self.newCardModulus = 0
|
self.newCardModulus = 0
|
||||||
|
|
||||||
def _timeForNewCard(self) -> Optional[int]:
|
def _timeForNewCard(self):
|
||||||
"True if it's time to display a new card when distributing."
|
"True if it's time to display a new card when distributing."
|
||||||
if not self.newCount:
|
if not self.newCount:
|
||||||
return False
|
return False
|
||||||
|
@ -396,7 +392,7 @@ did = ? and queue = 0 limit ?)""", did, lim)
|
||||||
elif self.newCardModulus:
|
elif self.newCardModulus:
|
||||||
return self.reps and self.reps % self.newCardModulus == 0
|
return self.reps and self.reps % self.newCardModulus == 0
|
||||||
|
|
||||||
def _deckNewLimit(self, did: int, fn: Optional[Callable] = None) -> Any:
|
def _deckNewLimit(self, did, fn=None):
|
||||||
if not fn:
|
if not fn:
|
||||||
fn = self._deckNewLimitSingle
|
fn = self._deckNewLimitSingle
|
||||||
sel = self.col.decks.get(did)
|
sel = self.col.decks.get(did)
|
||||||
|
@ -410,7 +406,7 @@ did = ? and queue = 0 limit ?)""", did, lim)
|
||||||
lim = min(rem, lim)
|
lim = min(rem, lim)
|
||||||
return lim
|
return lim
|
||||||
|
|
||||||
def _newForDeck(self, did: int, lim: int) -> Any:
|
def _newForDeck(self, did, lim):
|
||||||
"New count for a single deck."
|
"New count for a single deck."
|
||||||
if not lim:
|
if not lim:
|
||||||
return 0
|
return 0
|
||||||
|
@ -419,14 +415,14 @@ did = ? and queue = 0 limit ?)""", did, lim)
|
||||||
select count() from
|
select count() from
|
||||||
(select 1 from cards where did = ? and queue = 0 limit ?)""", did, lim)
|
(select 1 from cards where did = ? and queue = 0 limit ?)""", did, lim)
|
||||||
|
|
||||||
def _deckNewLimitSingle(self, g: Dict[str, Any]) -> Any:
|
def _deckNewLimitSingle(self, g):
|
||||||
"Limit for deck without parent limits."
|
"Limit for deck without parent limits."
|
||||||
if g['dyn']:
|
if g['dyn']:
|
||||||
return self.reportLimit
|
return self.reportLimit
|
||||||
c = self.col.decks.confForDid(g['id'])
|
c = self.col.decks.confForDid(g['id'])
|
||||||
return max(0, c['new']['perDay'] - g['newToday'][1])
|
return max(0, c['new']['perDay'] - g['newToday'][1])
|
||||||
|
|
||||||
def totalNewForCurrentDeck(self) -> Any:
|
def totalNewForCurrentDeck(self):
|
||||||
return self.col.db.scalar(
|
return self.col.db.scalar(
|
||||||
"""
|
"""
|
||||||
select count() from cards where id in (
|
select count() from cards where id in (
|
||||||
|
@ -436,7 +432,7 @@ select id from cards where did in %s and queue = 0 limit ?)"""
|
||||||
# Learning queues
|
# Learning queues
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _resetLrnCount(self) -> None:
|
def _resetLrnCount(self):
|
||||||
# sub-day
|
# sub-day
|
||||||
self.lrnCount = self.col.db.scalar("""
|
self.lrnCount = self.col.db.scalar("""
|
||||||
select sum(left/1000) from (select left from cards where
|
select sum(left/1000) from (select left from cards where
|
||||||
|
@ -449,14 +445,14 @@ select count() from cards where did in %s and queue = 3
|
||||||
and due <= ? limit %d""" % (self._deckLimit(), self.reportLimit),
|
and due <= ? limit %d""" % (self._deckLimit(), self.reportLimit),
|
||||||
self.today)
|
self.today)
|
||||||
|
|
||||||
def _resetLrn(self) -> None:
|
def _resetLrn(self):
|
||||||
self._resetLrnCount()
|
self._resetLrnCount()
|
||||||
self._lrnQueue = []
|
self._lrnQueue = []
|
||||||
self._lrnDayQueue = []
|
self._lrnDayQueue = []
|
||||||
self._lrnDids = self.col.decks.active()[:]
|
self._lrnDids = self.col.decks.active()[:]
|
||||||
|
|
||||||
# sub-day learning
|
# sub-day learning
|
||||||
def _fillLrn(self) -> Any:
|
def _fillLrn(self):
|
||||||
if not self.lrnCount:
|
if not self.lrnCount:
|
||||||
return False
|
return False
|
||||||
if self._lrnQueue:
|
if self._lrnQueue:
|
||||||
|
@ -469,7 +465,7 @@ limit %d""" % (self._deckLimit(), self.reportLimit), lim=self.dayCutoff)
|
||||||
self._lrnQueue.sort()
|
self._lrnQueue.sort()
|
||||||
return self._lrnQueue
|
return self._lrnQueue
|
||||||
|
|
||||||
def _getLrnCard(self, collapse: bool = False) -> Any:
|
def _getLrnCard(self, collapse=False):
|
||||||
if self._fillLrn():
|
if self._fillLrn():
|
||||||
cutoff = time.time()
|
cutoff = time.time()
|
||||||
if collapse:
|
if collapse:
|
||||||
|
@ -481,7 +477,7 @@ limit %d""" % (self._deckLimit(), self.reportLimit), lim=self.dayCutoff)
|
||||||
return card
|
return card
|
||||||
|
|
||||||
# daily learning
|
# daily learning
|
||||||
def _fillLrnDay(self) -> Optional[bool]:
|
def _fillLrnDay(self):
|
||||||
if not self.lrnCount:
|
if not self.lrnCount:
|
||||||
return False
|
return False
|
||||||
if self._lrnDayQueue:
|
if self._lrnDayQueue:
|
||||||
|
@ -505,15 +501,15 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
# nothing left in the deck; move to next
|
# nothing left in the deck; move to next
|
||||||
self._lrnDids.pop(0)
|
self._lrnDids.pop(0)
|
||||||
|
|
||||||
def _getLrnDayCard(self) -> Any:
|
def _getLrnDayCard(self):
|
||||||
if self._fillLrnDay():
|
if self._fillLrnDay():
|
||||||
self.lrnCount -= 1
|
self.lrnCount -= 1
|
||||||
return self.col.getCard(self._lrnDayQueue.pop())
|
return self.col.getCard(self._lrnDayQueue.pop())
|
||||||
|
|
||||||
def _answerLrnCard(self, card: Card, ease: int) -> None:
|
def _answerLrnCard(self, card, ease):
|
||||||
# ease 1=no, 2=yes, 3=remove
|
# ease 1=no, 2=yes, 3=remove
|
||||||
conf = self._lrnConf(card)
|
conf = self._lrnConf(card)
|
||||||
if card.odid and not card.wasNew: # type: ignore
|
if card.odid and not card.wasNew:
|
||||||
type = 3
|
type = 3
|
||||||
elif card.type == 2:
|
elif card.type == 2:
|
||||||
type = 2
|
type = 2
|
||||||
|
@ -572,7 +568,7 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
card.queue = 3
|
card.queue = 3
|
||||||
self._logLrn(card, ease, conf, leaving, type, lastLeft)
|
self._logLrn(card, ease, conf, leaving, type, lastLeft)
|
||||||
|
|
||||||
def _delayForGrade(self, conf: Dict[str, Any], left: int) -> Any:
|
def _delayForGrade(self, conf, left):
|
||||||
left = left % 1000
|
left = left % 1000
|
||||||
try:
|
try:
|
||||||
delay = conf['delays'][-left]
|
delay = conf['delays'][-left]
|
||||||
|
@ -584,13 +580,13 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
delay = 1
|
delay = 1
|
||||||
return delay*60
|
return delay*60
|
||||||
|
|
||||||
def _lrnConf(self, card: Card) -> Any:
|
def _lrnConf(self, card):
|
||||||
if card.type == 2:
|
if card.type == 2:
|
||||||
return self._lapseConf(card)
|
return self._lapseConf(card)
|
||||||
else:
|
else:
|
||||||
return self._newConf(card)
|
return self._newConf(card)
|
||||||
|
|
||||||
def _rescheduleAsRev(self, card: Card, conf: Dict[str, Any], early: bool) -> None:
|
def _rescheduleAsRev(self, card, conf, early):
|
||||||
lapse = card.type == 2
|
lapse = card.type == 2
|
||||||
if lapse:
|
if lapse:
|
||||||
if self._resched(card):
|
if self._resched(card):
|
||||||
|
@ -613,7 +609,7 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
card.queue = card.type = 0
|
card.queue = card.type = 0
|
||||||
card.due = self.col.nextID("pos")
|
card.due = self.col.nextID("pos")
|
||||||
|
|
||||||
def _startingLeft(self, card: Card) -> int:
|
def _startingLeft(self, card):
|
||||||
if card.type == 2:
|
if card.type == 2:
|
||||||
conf = self._lapseConf(card)
|
conf = self._lapseConf(card)
|
||||||
else:
|
else:
|
||||||
|
@ -622,7 +618,7 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
tod = self._leftToday(conf['delays'], tot)
|
tod = self._leftToday(conf['delays'], tot)
|
||||||
return tot + tod*1000
|
return tot + tod*1000
|
||||||
|
|
||||||
def _leftToday(self, delays: Any, left: int, now: None = None) -> int:
|
def _leftToday(self, delays, left, now=None):
|
||||||
"The number of steps that can be completed by the day cutoff."
|
"The number of steps that can be completed by the day cutoff."
|
||||||
if not now:
|
if not now:
|
||||||
now = intTime()
|
now = intTime()
|
||||||
|
@ -635,7 +631,7 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
ok = i
|
ok = i
|
||||||
return ok+1
|
return ok+1
|
||||||
|
|
||||||
def _graduatingIvl(self, card: Card, conf: Dict[str, Any], early: bool, adj: bool = True) -> Any:
|
def _graduatingIvl(self, card, conf, early, adj=True):
|
||||||
if card.type == 2:
|
if card.type == 2:
|
||||||
# lapsed card being relearnt
|
# lapsed card being relearnt
|
||||||
if card.odid:
|
if card.odid:
|
||||||
|
@ -653,13 +649,13 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
else:
|
else:
|
||||||
return ideal
|
return ideal
|
||||||
|
|
||||||
def _rescheduleNew(self, card: Card, conf: Dict[str, Any], early: bool) -> None:
|
def _rescheduleNew(self, card, conf, early):
|
||||||
"Reschedule a new card that's graduated for the first time."
|
"Reschedule a new card that's graduated for the first time."
|
||||||
card.ivl = self._graduatingIvl(card, conf, early)
|
card.ivl = self._graduatingIvl(card, conf, early)
|
||||||
card.due = self.today+card.ivl
|
card.due = self.today+card.ivl
|
||||||
card.factor = conf['initialFactor']
|
card.factor = conf['initialFactor']
|
||||||
|
|
||||||
def _logLrn(self, card: Card, ease: int, conf: Dict[str, Any], leaving: bool, type: int, lastLeft: int) -> None:
|
def _logLrn(self, card, ease, conf, leaving, type, lastLeft):
|
||||||
lastIvl = -(self._delayForGrade(conf, lastLeft))
|
lastIvl = -(self._delayForGrade(conf, lastLeft))
|
||||||
ivl = card.ivl if leaving else -(self._delayForGrade(conf, card.left))
|
ivl = card.ivl if leaving else -(self._delayForGrade(conf, card.left))
|
||||||
def log():
|
def log():
|
||||||
|
@ -674,7 +670,7 @@ did = ? and queue = 3 and due <= ? limit ?""",
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
log()
|
log()
|
||||||
|
|
||||||
def removeLrn(self, ids: Optional[List[int]] = None) -> None:
|
def removeLrn(self, ids=None):
|
||||||
"Remove cards from the learning queues."
|
"Remove cards from the learning queues."
|
||||||
if ids:
|
if ids:
|
||||||
extra = " and id in "+ids2str(ids)
|
extra = " and id in "+ids2str(ids)
|
||||||
|
@ -693,7 +689,7 @@ where queue in (1,3) and type = 2
|
||||||
self.forgetCards(self.col.db.list(
|
self.forgetCards(self.col.db.list(
|
||||||
"select id from cards where queue in (1,3) %s" % extra))
|
"select id from cards where queue in (1,3) %s" % extra))
|
||||||
|
|
||||||
def _lrnForDeck(self, did: int) -> Any:
|
def _lrnForDeck(self, did):
|
||||||
cnt = self.col.db.scalar(
|
cnt = self.col.db.scalar(
|
||||||
"""
|
"""
|
||||||
select sum(left/1000) from
|
select sum(left/1000) from
|
||||||
|
@ -709,16 +705,16 @@ and due <= ? limit ?)""",
|
||||||
# Reviews
|
# Reviews
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _deckRevLimit(self, did: int) -> Any:
|
def _deckRevLimit(self, did):
|
||||||
return self._deckNewLimit(did, self._deckRevLimitSingle)
|
return self._deckNewLimit(did, self._deckRevLimitSingle)
|
||||||
|
|
||||||
def _deckRevLimitSingle(self, d: Dict[str, Any]) -> Any:
|
def _deckRevLimitSingle(self, d):
|
||||||
if d['dyn']:
|
if d['dyn']:
|
||||||
return self.reportLimit
|
return self.reportLimit
|
||||||
c = self.col.decks.confForDid(d['id'])
|
c = self.col.decks.confForDid(d['id'])
|
||||||
return max(0, c['rev']['perDay'] - d['revToday'][1])
|
return max(0, c['rev']['perDay'] - d['revToday'][1])
|
||||||
|
|
||||||
def _revForDeck(self, did: int, lim: int) -> Any:
|
def _revForDeck(self, did, lim):
|
||||||
lim = min(lim, self.reportLimit)
|
lim = min(lim, self.reportLimit)
|
||||||
return self.col.db.scalar(
|
return self.col.db.scalar(
|
||||||
"""
|
"""
|
||||||
|
@ -727,7 +723,7 @@ select count() from
|
||||||
and due <= ? limit ?)""",
|
and due <= ? limit ?)""",
|
||||||
did, self.today, lim)
|
did, self.today, lim)
|
||||||
|
|
||||||
def _resetRevCount(self) -> None:
|
def _resetRevCount(self):
|
||||||
def cntFn(did, lim):
|
def cntFn(did, lim):
|
||||||
return self.col.db.scalar("""
|
return self.col.db.scalar("""
|
||||||
select count() from (select id from cards where
|
select count() from (select id from cards where
|
||||||
|
@ -736,12 +732,12 @@ did = ? and queue = 2 and due <= ? limit %d)""" % lim,
|
||||||
self.revCount = self._walkingCount(
|
self.revCount = self._walkingCount(
|
||||||
self._deckRevLimitSingle, cntFn)
|
self._deckRevLimitSingle, cntFn)
|
||||||
|
|
||||||
def _resetRev(self) -> None:
|
def _resetRev(self):
|
||||||
self._resetRevCount()
|
self._resetRevCount()
|
||||||
self._revQueue = []
|
self._revQueue = []
|
||||||
self._revDids = self.col.decks.active()[:]
|
self._revDids = self.col.decks.active()[:]
|
||||||
|
|
||||||
def _fillRev(self) -> Any:
|
def _fillRev(self):
|
||||||
if self._revQueue:
|
if self._revQueue:
|
||||||
return True
|
return True
|
||||||
if not self.revCount:
|
if not self.revCount:
|
||||||
|
@ -778,12 +774,12 @@ did = ? and queue = 2 and due <= ? limit ?""",
|
||||||
self._resetRev()
|
self._resetRev()
|
||||||
return self._fillRev()
|
return self._fillRev()
|
||||||
|
|
||||||
def _getRevCard(self) -> Any:
|
def _getRevCard(self):
|
||||||
if self._fillRev():
|
if self._fillRev():
|
||||||
self.revCount -= 1
|
self.revCount -= 1
|
||||||
return self.col.getCard(self._revQueue.pop())
|
return self.col.getCard(self._revQueue.pop())
|
||||||
|
|
||||||
def totalRevForCurrentDeck(self) -> Any:
|
def totalRevForCurrentDeck(self):
|
||||||
return self.col.db.scalar(
|
return self.col.db.scalar(
|
||||||
"""
|
"""
|
||||||
select count() from cards where id in (
|
select count() from cards where id in (
|
||||||
|
@ -793,7 +789,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
# Answering a review card
|
# Answering a review card
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _answerRevCard(self, card: Card, ease: int) -> None:
|
def _answerRevCard(self, card, ease):
|
||||||
delay = 0
|
delay = 0
|
||||||
if ease == 1:
|
if ease == 1:
|
||||||
delay = self._rescheduleLapse(card)
|
delay = self._rescheduleLapse(card)
|
||||||
|
@ -801,7 +797,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
self._rescheduleRev(card, ease)
|
self._rescheduleRev(card, ease)
|
||||||
self._logRev(card, ease, delay)
|
self._logRev(card, ease, delay)
|
||||||
|
|
||||||
def _rescheduleLapse(self, card: Card) -> Any:
|
def _rescheduleLapse(self, card):
|
||||||
conf = self._lapseConf(card)
|
conf = self._lapseConf(card)
|
||||||
card.lastIvl = card.ivl
|
card.lastIvl = card.ivl
|
||||||
if self._resched(card):
|
if self._resched(card):
|
||||||
|
@ -837,10 +833,10 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
card.queue = 3
|
card.queue = 3
|
||||||
return delay
|
return delay
|
||||||
|
|
||||||
def _nextLapseIvl(self, card: Card, conf: Dict[str, Any]) -> Any:
|
def _nextLapseIvl(self, card, conf):
|
||||||
return max(conf['minInt'], int(card.ivl*conf['mult']))
|
return max(conf['minInt'], int(card.ivl*conf['mult']))
|
||||||
|
|
||||||
def _rescheduleRev(self, card: Card, ease: int) -> None:
|
def _rescheduleRev(self, card, ease):
|
||||||
# update interval
|
# update interval
|
||||||
card.lastIvl = card.ivl
|
card.lastIvl = card.ivl
|
||||||
if self._resched(card):
|
if self._resched(card):
|
||||||
|
@ -855,7 +851,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
card.odid = 0
|
card.odid = 0
|
||||||
card.odue = 0
|
card.odue = 0
|
||||||
|
|
||||||
def _logRev(self, card: Card, ease: int, delay: Union[int, float]) -> None:
|
def _logRev(self, card, ease, delay):
|
||||||
def log():
|
def log():
|
||||||
self.col.db.execute(
|
self.col.db.execute(
|
||||||
"insert into revlog values (?,?,?,?,?,?,?,?,?)",
|
"insert into revlog values (?,?,?,?,?,?,?,?,?)",
|
||||||
|
@ -872,7 +868,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
# Interval management
|
# Interval management
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _nextRevIvl(self, card: Card, ease: int) -> Any:
|
def _nextRevIvl(self, card, ease):
|
||||||
"Ideal next interval for CARD, given EASE."
|
"Ideal next interval for CARD, given EASE."
|
||||||
delay = self._daysLate(card)
|
delay = self._daysLate(card)
|
||||||
conf = self._revConf(card)
|
conf = self._revConf(card)
|
||||||
|
@ -890,11 +886,11 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
# interval capped?
|
# interval capped?
|
||||||
return min(interval, conf['maxIvl'])
|
return min(interval, conf['maxIvl'])
|
||||||
|
|
||||||
def _fuzzedIvl(self, ivl: int) -> int:
|
def _fuzzedIvl(self, ivl):
|
||||||
min, max = self._fuzzIvlRange(ivl)
|
min, max = self._fuzzIvlRange(ivl)
|
||||||
return random.randint(min, max)
|
return random.randint(min, max)
|
||||||
|
|
||||||
def _fuzzIvlRange(self, ivl: int) -> List:
|
def _fuzzIvlRange(self, ivl):
|
||||||
if ivl < 2:
|
if ivl < 2:
|
||||||
return [1, 1]
|
return [1, 1]
|
||||||
elif ivl == 2:
|
elif ivl == 2:
|
||||||
|
@ -909,22 +905,22 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
fuzz = max(fuzz, 1)
|
fuzz = max(fuzz, 1)
|
||||||
return [ivl-fuzz, ivl+fuzz]
|
return [ivl-fuzz, ivl+fuzz]
|
||||||
|
|
||||||
def _constrainedIvl(self, ivl: float, conf: Dict[str, Any], prev: int) -> int:
|
def _constrainedIvl(self, ivl, conf, prev):
|
||||||
"Integer interval after interval factor and prev+1 constraints applied."
|
"Integer interval after interval factor and prev+1 constraints applied."
|
||||||
new = ivl * conf.get('ivlFct', 1)
|
new = ivl * conf.get('ivlFct', 1)
|
||||||
return int(max(new, prev+1))
|
return int(max(new, prev+1))
|
||||||
|
|
||||||
def _daysLate(self, card: Card) -> Any:
|
def _daysLate(self, card):
|
||||||
"Number of days later than scheduled."
|
"Number of days later than scheduled."
|
||||||
due = card.odue if card.odid else card.due
|
due = card.odue if card.odid else card.due
|
||||||
return max(0, self.today - due)
|
return max(0, self.today - due)
|
||||||
|
|
||||||
def _updateRevIvl(self, card: Card, ease: int) -> None:
|
def _updateRevIvl(self, card, ease):
|
||||||
idealIvl = self._nextRevIvl(card, ease)
|
idealIvl = self._nextRevIvl(card, ease)
|
||||||
card.ivl = min(max(self._adjRevIvl(card, idealIvl), card.ivl+1),
|
card.ivl = min(max(self._adjRevIvl(card, idealIvl), card.ivl+1),
|
||||||
self._revConf(card)['maxIvl'])
|
self._revConf(card)['maxIvl'])
|
||||||
|
|
||||||
def _adjRevIvl(self, card: Card, idealIvl: int) -> int:
|
def _adjRevIvl(self, card, idealIvl):
|
||||||
if self._spreadRev:
|
if self._spreadRev:
|
||||||
idealIvl = self._fuzzedIvl(idealIvl)
|
idealIvl = self._fuzzedIvl(idealIvl)
|
||||||
return idealIvl
|
return idealIvl
|
||||||
|
@ -932,7 +928,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
# Dynamic deck handling
|
# Dynamic deck handling
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def rebuildDyn(self, did: Optional[int] = None) -> Any:
|
def rebuildDyn(self, did=None):
|
||||||
"Rebuild a dynamic deck."
|
"Rebuild a dynamic deck."
|
||||||
did = did or self.col.decks.selected()
|
did = did or self.col.decks.selected()
|
||||||
deck = self.col.decks.get(did)
|
deck = self.col.decks.get(did)
|
||||||
|
@ -946,7 +942,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
self.col.decks.select(did)
|
self.col.decks.select(did)
|
||||||
return ids
|
return ids
|
||||||
|
|
||||||
def _fillDyn(self, deck: Dict[str, Any]) -> Any:
|
def _fillDyn(self, deck):
|
||||||
search, limit, order = deck['terms'][0]
|
search, limit, order = deck['terms'][0]
|
||||||
orderlimit = self._dynOrder(order, limit)
|
orderlimit = self._dynOrder(order, limit)
|
||||||
if search.strip():
|
if search.strip():
|
||||||
|
@ -962,7 +958,7 @@ select id from cards where did in %s and queue = 2 and due <= ? limit ?)"""
|
||||||
self._moveToDyn(deck['id'], ids)
|
self._moveToDyn(deck['id'], ids)
|
||||||
return ids
|
return ids
|
||||||
|
|
||||||
def emptyDyn(self, did: Optional[int], lim: Optional[str] = None) -> None:
|
def emptyDyn(self, did, lim=None):
|
||||||
if not lim:
|
if not lim:
|
||||||
lim = "did = %s" % did
|
lim = "did = %s" % did
|
||||||
self.col.log(self.col.db.list("select id from cards where %s" % lim))
|
self.col.log(self.col.db.list("select id from cards where %s" % lim))
|
||||||
|
@ -973,10 +969,10 @@ else type end), type = (case when type = 1 then 0 else type end),
|
||||||
due = odue, odue = 0, odid = 0, usn = ? where %s""" % lim,
|
due = odue, odue = 0, odid = 0, usn = ? where %s""" % lim,
|
||||||
self.col.usn())
|
self.col.usn())
|
||||||
|
|
||||||
def remFromDyn(self, cids: List[int]) -> None:
|
def remFromDyn(self, cids):
|
||||||
self.emptyDyn(None, "id in %s and odid" % ids2str(cids))
|
self.emptyDyn(None, "id in %s and odid" % ids2str(cids))
|
||||||
|
|
||||||
def _dynOrder(self, o: int, l: int) -> str:
|
def _dynOrder(self, o, l):
|
||||||
if o == DYN_OLDEST:
|
if o == DYN_OLDEST:
|
||||||
t = "(select max(id) from revlog where cid=c.id)"
|
t = "(select max(id) from revlog where cid=c.id)"
|
||||||
elif o == DYN_RANDOM:
|
elif o == DYN_RANDOM:
|
||||||
|
@ -1001,7 +997,7 @@ due = odue, odue = 0, odid = 0, usn = ? where %s""" % lim,
|
||||||
t = "c.due"
|
t = "c.due"
|
||||||
return t + " limit %d" % l
|
return t + " limit %d" % l
|
||||||
|
|
||||||
def _moveToDyn(self, did: int, ids: List[int]) -> None:
|
def _moveToDyn(self, did, ids):
|
||||||
deck = self.col.decks.get(did)
|
deck = self.col.decks.get(did)
|
||||||
data = []
|
data = []
|
||||||
t = intTime(); u = self.col.usn()
|
t = intTime(); u = self.col.usn()
|
||||||
|
@ -1020,7 +1016,7 @@ odid = (case when odid then odid else did end),
|
||||||
odue = (case when odue then odue else due end),
|
odue = (case when odue then odue else due end),
|
||||||
did = ?, queue = %s, due = ?, usn = ? where id = ?""" % queue, data)
|
did = ?, queue = %s, due = ?, usn = ? where id = ?""" % queue, data)
|
||||||
|
|
||||||
def _dynIvlBoost(self, card: Card) -> Any:
|
def _dynIvlBoost(self, card):
|
||||||
assert card.odid and card.type == 2
|
assert card.odid and card.type == 2
|
||||||
assert card.factor
|
assert card.factor
|
||||||
elapsed = card.ivl - (card.odue - self.today)
|
elapsed = card.ivl - (card.odue - self.today)
|
||||||
|
@ -1032,7 +1028,7 @@ did = ?, queue = %s, due = ?, usn = ? where id = ?""" % queue, data)
|
||||||
# Leeches
|
# Leeches
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _checkLeech(self, card: Card, conf: Dict[str, Any]) -> Optional[bool]:
|
def _checkLeech(self, card, conf):
|
||||||
"Leech handler. True if card was a leech."
|
"Leech handler. True if card was a leech."
|
||||||
lf = conf['leechFails']
|
lf = conf['leechFails']
|
||||||
if not lf:
|
if not lf:
|
||||||
|
@ -1061,10 +1057,10 @@ did = ?, queue = %s, due = ?, usn = ? where id = ?""" % queue, data)
|
||||||
# Tools
|
# Tools
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _cardConf(self, card: Card) -> Any:
|
def _cardConf(self, card):
|
||||||
return self.col.decks.confForDid(card.did)
|
return self.col.decks.confForDid(card.did)
|
||||||
|
|
||||||
def _newConf(self, card: Card) -> Any:
|
def _newConf(self, card):
|
||||||
conf = self._cardConf(card)
|
conf = self._cardConf(card)
|
||||||
# normal deck
|
# normal deck
|
||||||
if not card.odid:
|
if not card.odid:
|
||||||
|
@ -1084,7 +1080,7 @@ did = ?, queue = %s, due = ?, usn = ? where id = ?""" % queue, data)
|
||||||
perDay=self.reportLimit
|
perDay=self.reportLimit
|
||||||
)
|
)
|
||||||
|
|
||||||
def _lapseConf(self, card: Card) -> Any:
|
def _lapseConf(self, card):
|
||||||
conf = self._cardConf(card)
|
conf = self._cardConf(card)
|
||||||
# normal deck
|
# normal deck
|
||||||
if not card.odid:
|
if not card.odid:
|
||||||
|
@ -1103,7 +1099,7 @@ did = ?, queue = %s, due = ?, usn = ? where id = ?""" % queue, data)
|
||||||
resched=conf['resched'],
|
resched=conf['resched'],
|
||||||
)
|
)
|
||||||
|
|
||||||
def _revConf(self, card: Card) -> Any:
|
def _revConf(self, card):
|
||||||
conf = self._cardConf(card)
|
conf = self._cardConf(card)
|
||||||
# normal deck
|
# normal deck
|
||||||
if not card.odid:
|
if not card.odid:
|
||||||
|
@ -1111,10 +1107,10 @@ did = ?, queue = %s, due = ?, usn = ? where id = ?""" % queue, data)
|
||||||
# dynamic deck
|
# dynamic deck
|
||||||
return self.col.decks.confForDid(card.odid)['rev']
|
return self.col.decks.confForDid(card.odid)['rev']
|
||||||
|
|
||||||
def _deckLimit(self) -> str:
|
def _deckLimit(self):
|
||||||
return ids2str(self.col.decks.active())
|
return ids2str(self.col.decks.active())
|
||||||
|
|
||||||
def _resched(self, card: Card) -> Any:
|
def _resched(self, card):
|
||||||
conf = self._cardConf(card)
|
conf = self._cardConf(card)
|
||||||
if not conf['dyn']:
|
if not conf['dyn']:
|
||||||
return True
|
return True
|
||||||
|
@ -1123,7 +1119,7 @@ did = ?, queue = %s, due = ?, usn = ? where id = ?""" % queue, data)
|
||||||
# Daily cutoff
|
# Daily cutoff
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _updateCutoff(self) -> None:
|
def _updateCutoff(self):
|
||||||
oldToday = self.today
|
oldToday = self.today
|
||||||
# days since col created
|
# days since col created
|
||||||
self.today = int((time.time() - self.col.crt) // 86400)
|
self.today = int((time.time() - self.col.crt) // 86400)
|
||||||
|
@ -1145,7 +1141,7 @@ did = ?, queue = %s, due = ?, usn = ? where id = ?""" % queue, data)
|
||||||
if unburied < self.today:
|
if unburied < self.today:
|
||||||
self.unburyCards()
|
self.unburyCards()
|
||||||
|
|
||||||
def _checkDay(self) -> None:
|
def _checkDay(self):
|
||||||
# check if the day has rolled over
|
# check if the day has rolled over
|
||||||
if time.time() > self.dayCutoff:
|
if time.time() > self.dayCutoff:
|
||||||
self.reset()
|
self.reset()
|
||||||
|
@ -1153,12 +1149,12 @@ did = ?, queue = %s, due = ?, usn = ? where id = ?""" % queue, data)
|
||||||
# Deck finished state
|
# Deck finished state
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def finishedMsg(self) -> str:
|
def finishedMsg(self):
|
||||||
return ("<b>"+_(
|
return ("<b>"+_(
|
||||||
"Congratulations! You have finished this deck for now.")+
|
"Congratulations! You have finished this deck for now.")+
|
||||||
"</b><br><br>" + self._nextDueMsg())
|
"</b><br><br>" + self._nextDueMsg())
|
||||||
|
|
||||||
def _nextDueMsg(self) -> str:
|
def _nextDueMsg(self):
|
||||||
line = []
|
line = []
|
||||||
# the new line replacements are so we don't break translations
|
# the new line replacements are so we don't break translations
|
||||||
# in a point release
|
# in a point release
|
||||||
|
@ -1185,20 +1181,20 @@ Some related or buried cards were delayed until a later session.""")+now)
|
||||||
To study outside of the normal schedule, click the Custom Study button below."""))
|
To study outside of the normal schedule, click the Custom Study button below."""))
|
||||||
return "<p>".join(line)
|
return "<p>".join(line)
|
||||||
|
|
||||||
def revDue(self) -> Any:
|
def revDue(self):
|
||||||
"True if there are any rev cards due."
|
"True if there are any rev cards due."
|
||||||
return self.col.db.scalar(
|
return self.col.db.scalar(
|
||||||
("select 1 from cards where did in %s and queue = 2 "
|
("select 1 from cards where did in %s and queue = 2 "
|
||||||
"and due <= ? limit 1") % self._deckLimit(),
|
"and due <= ? limit 1") % self._deckLimit(),
|
||||||
self.today)
|
self.today)
|
||||||
|
|
||||||
def newDue(self) -> Any:
|
def newDue(self):
|
||||||
"True if there are any new cards due."
|
"True if there are any new cards due."
|
||||||
return self.col.db.scalar(
|
return self.col.db.scalar(
|
||||||
("select 1 from cards where did in %s and queue = 0 "
|
("select 1 from cards where did in %s and queue = 0 "
|
||||||
"limit 1") % self._deckLimit())
|
"limit 1") % self._deckLimit())
|
||||||
|
|
||||||
def haveBuried(self) -> bool:
|
def haveBuried(self):
|
||||||
sdids = ids2str(self.col.decks.active())
|
sdids = ids2str(self.col.decks.active())
|
||||||
cnt = self.col.db.scalar(
|
cnt = self.col.db.scalar(
|
||||||
"select 1 from cards where queue = -2 and did in %s limit 1" % sdids)
|
"select 1 from cards where queue = -2 and did in %s limit 1" % sdids)
|
||||||
|
@ -1207,7 +1203,7 @@ To study outside of the normal schedule, click the Custom Study button below."""
|
||||||
# Next time reports
|
# Next time reports
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def nextIvlStr(self, card: Card, ease: int, short: bool = False) -> Any:
|
def nextIvlStr(self, card, ease, short=False):
|
||||||
"Return the next interval for CARD as a string."
|
"Return the next interval for CARD as a string."
|
||||||
ivl = self.nextIvl(card, ease)
|
ivl = self.nextIvl(card, ease)
|
||||||
if not ivl:
|
if not ivl:
|
||||||
|
@ -1217,7 +1213,7 @@ To study outside of the normal schedule, click the Custom Study button below."""
|
||||||
s = "<"+s
|
s = "<"+s
|
||||||
return s
|
return s
|
||||||
|
|
||||||
def nextIvl(self, card: Card, ease: int) -> Any:
|
def nextIvl(self, card, ease):
|
||||||
"Return the next interval for CARD, in seconds."
|
"Return the next interval for CARD, in seconds."
|
||||||
if card.queue in (0,1,3):
|
if card.queue in (0,1,3):
|
||||||
return self._nextLrnIvl(card, ease)
|
return self._nextLrnIvl(card, ease)
|
||||||
|
@ -1232,7 +1228,7 @@ To study outside of the normal schedule, click the Custom Study button below."""
|
||||||
return self._nextRevIvl(card, ease)*86400
|
return self._nextRevIvl(card, ease)*86400
|
||||||
|
|
||||||
# this isn't easily extracted from the learn code
|
# this isn't easily extracted from the learn code
|
||||||
def _nextLrnIvl(self, card: Card, ease: int) -> Any:
|
def _nextLrnIvl(self, card, ease):
|
||||||
if card.queue == 0:
|
if card.queue == 0:
|
||||||
card.left = self._startingLeft(card)
|
card.left = self._startingLeft(card)
|
||||||
conf = self._lrnConf(card)
|
conf = self._lrnConf(card)
|
||||||
|
@ -1257,7 +1253,7 @@ To study outside of the normal schedule, click the Custom Study button below."""
|
||||||
# Suspending
|
# Suspending
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def suspendCards(self, ids: List[int]) -> None:
|
def suspendCards(self, ids):
|
||||||
"Suspend cards."
|
"Suspend cards."
|
||||||
self.col.log(ids)
|
self.col.log(ids)
|
||||||
self.remFromDyn(ids)
|
self.remFromDyn(ids)
|
||||||
|
@ -1266,7 +1262,7 @@ To study outside of the normal schedule, click the Custom Study button below."""
|
||||||
"update cards set queue=-1,mod=?,usn=? where id in "+
|
"update cards set queue=-1,mod=?,usn=? where id in "+
|
||||||
ids2str(ids), intTime(), self.col.usn())
|
ids2str(ids), intTime(), self.col.usn())
|
||||||
|
|
||||||
def unsuspendCards(self, ids: List[int]) -> None:
|
def unsuspendCards(self, ids):
|
||||||
"Unsuspend cards."
|
"Unsuspend cards."
|
||||||
self.col.log(ids)
|
self.col.log(ids)
|
||||||
self.col.db.execute(
|
self.col.db.execute(
|
||||||
|
@ -1274,7 +1270,7 @@ To study outside of the normal schedule, click the Custom Study button below."""
|
||||||
"where queue = -1 and id in "+ ids2str(ids),
|
"where queue = -1 and id in "+ ids2str(ids),
|
||||||
intTime(), self.col.usn())
|
intTime(), self.col.usn())
|
||||||
|
|
||||||
def buryCards(self, cids: List[int]) -> None:
|
def buryCards(self, cids):
|
||||||
self.col.log(cids)
|
self.col.log(cids)
|
||||||
self.remFromDyn(cids)
|
self.remFromDyn(cids)
|
||||||
self.removeLrn(cids)
|
self.removeLrn(cids)
|
||||||
|
@ -1282,7 +1278,7 @@ To study outside of the normal schedule, click the Custom Study button below."""
|
||||||
update cards set queue=-2,mod=?,usn=? where id in """+ids2str(cids),
|
update cards set queue=-2,mod=?,usn=? where id in """+ids2str(cids),
|
||||||
intTime(), self.col.usn())
|
intTime(), self.col.usn())
|
||||||
|
|
||||||
def buryNote(self, nid: int) -> None:
|
def buryNote(self, nid):
|
||||||
"Bury all cards for note until next session."
|
"Bury all cards for note until next session."
|
||||||
cids = self.col.db.list(
|
cids = self.col.db.list(
|
||||||
"select id from cards where nid = ? and queue >= 0", nid)
|
"select id from cards where nid = ? and queue >= 0", nid)
|
||||||
|
@ -1291,7 +1287,7 @@ update cards set queue=-2,mod=?,usn=? where id in """+ids2str(cids),
|
||||||
# Sibling spacing
|
# Sibling spacing
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _burySiblings(self, card: Card) -> None:
|
def _burySiblings(self, card):
|
||||||
toBury = []
|
toBury = []
|
||||||
nconf = self._newConf(card)
|
nconf = self._newConf(card)
|
||||||
buryNew = nconf.get("bury", True)
|
buryNew = nconf.get("bury", True)
|
||||||
|
@ -1328,7 +1324,7 @@ and (queue=0 or (queue=2 and due<=?))""",
|
||||||
# Resetting
|
# Resetting
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def forgetCards(self, ids: List[int]) -> None:
|
def forgetCards(self, ids):
|
||||||
"Put cards at the end of the new queue."
|
"Put cards at the end of the new queue."
|
||||||
self.remFromDyn(ids)
|
self.remFromDyn(ids)
|
||||||
self.col.db.execute(
|
self.col.db.execute(
|
||||||
|
@ -1340,7 +1336,7 @@ and (queue=0 or (queue=2 and due<=?))""",
|
||||||
self.sortCards(ids, start=pmax+1)
|
self.sortCards(ids, start=pmax+1)
|
||||||
self.col.log(ids)
|
self.col.log(ids)
|
||||||
|
|
||||||
def reschedCards(self, ids: List[int], imin: int, imax: int) -> None:
|
def reschedCards(self, ids, imin, imax):
|
||||||
"Put cards in review queue with a new interval in days (min, max)."
|
"Put cards in review queue with a new interval in days (min, max)."
|
||||||
d = []
|
d = []
|
||||||
t = self.today
|
t = self.today
|
||||||
|
@ -1356,7 +1352,7 @@ usn=:usn,mod=:mod,factor=:fact where id=:id""",
|
||||||
d)
|
d)
|
||||||
self.col.log(ids)
|
self.col.log(ids)
|
||||||
|
|
||||||
def resetCards(self, ids) -> None:
|
def resetCards(self, ids):
|
||||||
"Completely reset cards for export."
|
"Completely reset cards for export."
|
||||||
sids = ids2str(ids)
|
sids = ids2str(ids)
|
||||||
# we want to avoid resetting due number of existing new cards on export
|
# we want to avoid resetting due number of existing new cards on export
|
||||||
|
@ -1375,7 +1371,7 @@ usn=:usn,mod=:mod,factor=:fact where id=:id""",
|
||||||
# Repositioning new cards
|
# Repositioning new cards
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def sortCards(self, cids: List[int], start: int = 1, step: int = 1, shuffle: bool = False, shift: bool = False) -> None:
|
def sortCards(self, cids, start=1, step=1, shuffle=False, shift=False):
|
||||||
scids = ids2str(cids)
|
scids = ids2str(cids)
|
||||||
now = intTime()
|
now = intTime()
|
||||||
nids = []
|
nids = []
|
||||||
|
@ -1415,15 +1411,15 @@ and due >= ? and queue = 0""" % scids, now, self.col.usn(), shiftby, low)
|
||||||
self.col.db.executemany(
|
self.col.db.executemany(
|
||||||
"update cards set due=:due,mod=:now,usn=:usn where id = :cid", d)
|
"update cards set due=:due,mod=:now,usn=:usn where id = :cid", d)
|
||||||
|
|
||||||
def randomizeCards(self, did: int) -> None:
|
def randomizeCards(self, did):
|
||||||
cids = self.col.db.list("select id from cards where did = ?", did)
|
cids = self.col.db.list("select id from cards where did = ?", did)
|
||||||
self.sortCards(cids, shuffle=True)
|
self.sortCards(cids, shuffle=True)
|
||||||
|
|
||||||
def orderCards(self, did: int) -> None:
|
def orderCards(self, did):
|
||||||
cids = self.col.db.list("select id from cards where did = ? order by id", did)
|
cids = self.col.db.list("select id from cards where did = ? order by id", did)
|
||||||
self.sortCards(cids)
|
self.sortCards(cids)
|
||||||
|
|
||||||
def resortConf(self, conf) -> None:
|
def resortConf(self, conf):
|
||||||
for did in self.col.decks.didsForConf(conf):
|
for did in self.col.decks.didsForConf(conf):
|
||||||
if conf['new']['order'] == 0:
|
if conf['new']['order'] == 0:
|
||||||
self.randomizeCards(did)
|
self.randomizeCards(did)
|
||||||
|
@ -1431,7 +1427,7 @@ and due >= ? and queue = 0""" % scids, now, self.col.usn(), shiftby, low)
|
||||||
self.orderCards(did)
|
self.orderCards(did)
|
||||||
|
|
||||||
# for post-import
|
# for post-import
|
||||||
def maybeRandomizeDeck(self, did=None) -> None:
|
def maybeRandomizeDeck(self, did=None):
|
||||||
if not did:
|
if not did:
|
||||||
did = self.col.decks.selected()
|
did = self.col.decks.selected()
|
||||||
conf = self.col.decks.confForDid(did)
|
conf = self.col.decks.confForDid(did)
|
||||||
|
|
Loading…
Reference in a new issue