mirror of
https://github.com/ankitects/anki.git
synced 2025-09-19 22:42:25 -04:00
updateCache -> renderQA, move some functions around
This commit is contained in:
parent
0e084f8e3d
commit
ad68500494
8 changed files with 99 additions and 132 deletions
|
@ -110,10 +110,8 @@ streak=?, lapses=?, grade=?, cycles=? where id = ?""",
|
||||||
return self._getQA()['a']
|
return self._getQA()['a']
|
||||||
|
|
||||||
def _getQA(self, reload=False):
|
def _getQA(self, reload=False):
|
||||||
# this is a hack at the moment
|
|
||||||
if not self._qa or reload:
|
if not self._qa or reload:
|
||||||
self._qa = self.deck.updateCache(
|
self._qa = self.deck.renderQA([self.id], "card")[0]
|
||||||
[self.id], "card")[0]
|
|
||||||
return self._qa
|
return self._qa
|
||||||
|
|
||||||
def fact(self):
|
def fact(self):
|
||||||
|
|
208
anki/deck.py
208
anki/deck.py
|
@ -2,24 +2,18 @@
|
||||||
# Copyright: Damien Elmes <anki@ichi2.net>
|
# Copyright: Damien Elmes <anki@ichi2.net>
|
||||||
# License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html
|
# License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html
|
||||||
|
|
||||||
import tempfile, time, os, random, sys, re, stat, shutil
|
import time, os, random, re, stat, simplejson
|
||||||
import types, traceback, simplejson, datetime
|
|
||||||
from operator import itemgetter
|
|
||||||
from itertools import groupby
|
|
||||||
|
|
||||||
from anki.lang import _, ngettext
|
from anki.lang import _, ngettext
|
||||||
from anki.utils import parseTags, tidyHTML, ids2str, hexifyID, \
|
from anki.utils import parseTags, tidyHTML, ids2str, hexifyID, \
|
||||||
canonifyTags, joinTags, addTags, deleteTags, checksum, fieldChecksum, \
|
checksum, fieldChecksum, addTags, deleteTags, stripHTML, intTime, \
|
||||||
stripHTML, intTime, splitFields
|
splitFields
|
||||||
|
from anki.hooks import runHook, runFilter
|
||||||
from anki.hooks import runHook, hookEmpty, runFilter
|
|
||||||
|
|
||||||
from anki.sched import Scheduler
|
from anki.sched import Scheduler
|
||||||
from anki.media import MediaRegistry
|
from anki.media import MediaRegistry
|
||||||
|
|
||||||
from anki.consts import *
|
from anki.consts import *
|
||||||
import anki.latex # sets up hook
|
|
||||||
|
|
||||||
|
import anki.latex # sets up hook
|
||||||
import anki.cards, anki.facts, anki.models, anki.template
|
import anki.cards, anki.facts, anki.models, anki.template
|
||||||
|
|
||||||
# Settings related to queue building. These may be loaded without the rest of
|
# Settings related to queue building. These may be loaded without the rest of
|
||||||
|
@ -147,17 +141,9 @@ qconf=?, conf=?, data=?""",
|
||||||
"True if schema changed since last sync, or syncing off."
|
"True if schema changed since last sync, or syncing off."
|
||||||
return self.schema > self.lastSync
|
return self.schema > self.lastSync
|
||||||
|
|
||||||
# unsorted
|
# Object creation helpers
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def nextID(self, type):
|
|
||||||
id = self.conf.get(type, 1)
|
|
||||||
self.conf[type] = id+1
|
|
||||||
return id
|
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
self.sched.reset()
|
|
||||||
|
|
||||||
def getCard(self, id):
|
def getCard(self, id):
|
||||||
return anki.cards.Card(self, id)
|
return anki.cards.Card(self, id)
|
||||||
|
|
||||||
|
@ -168,84 +154,16 @@ qconf=?, conf=?, data=?""",
|
||||||
return anki.models.Template(self, self.deck.db.first(
|
return anki.models.Template(self, self.deck.db.first(
|
||||||
"select * from templates where id = ?", id))
|
"select * from templates where id = ?", id))
|
||||||
|
|
||||||
# if card:
|
# unsorted
|
||||||
# return card
|
##########################################################################
|
||||||
# if sched.name == "main":
|
|
||||||
# self.stopSession()
|
|
||||||
# else:
|
|
||||||
# # in a custom scheduler; return to normal
|
|
||||||
# print "fixme: this should be done in gui code"
|
|
||||||
# self.sched.cleanup()
|
|
||||||
# self.sched = AnkiScheduler(self)
|
|
||||||
# return self.getCard()
|
|
||||||
|
|
||||||
def resetCards(self, ids=None):
|
def nextID(self, type):
|
||||||
"Reset progress on cards in IDS."
|
id = self.conf.get(type, 1)
|
||||||
print "position in resetCards()"
|
self.conf[type] = id+1
|
||||||
sql = """
|
return id
|
||||||
update cards set mod=:now, position=0, type=2, queue=2, lastInterval=0,
|
|
||||||
interval=0, due=created, factor=2.5, reps=0, successive=0, lapses=0, flags=0"""
|
|
||||||
sql2 = "delete from revlog"
|
|
||||||
if ids is None:
|
|
||||||
lim = ""
|
|
||||||
else:
|
|
||||||
sids = ids2str(ids)
|
|
||||||
sql += " where id in "+sids
|
|
||||||
sql2 += " where cardId in "+sids
|
|
||||||
self.db.execute(sql, now=time.time())
|
|
||||||
self.db.execute(sql2)
|
|
||||||
if self.qconf['newCardOrder'] == NEW_CARDS_RANDOM:
|
|
||||||
# we need to re-randomize now
|
|
||||||
self.randomizeNewCards(ids)
|
|
||||||
|
|
||||||
def randomizeNewCards(self, cardIds=None):
|
def reset(self):
|
||||||
"Randomize 'due' on all new cards."
|
self.sched.reset()
|
||||||
now = time.time()
|
|
||||||
query = "select distinct fid from cards where reps = 0"
|
|
||||||
if cardIds:
|
|
||||||
query += " and id in %s" % ids2str(cardIds)
|
|
||||||
fids = self.db.list(query)
|
|
||||||
data = [{'fid': fid,
|
|
||||||
'rand': random.uniform(0, now),
|
|
||||||
'now': now} for fid in fids]
|
|
||||||
self.db.executemany("""
|
|
||||||
update cards
|
|
||||||
set due = :rand + ord,
|
|
||||||
mod = :now
|
|
||||||
where fid = :fid
|
|
||||||
and type = 2""", data)
|
|
||||||
|
|
||||||
def orderNewCards(self):
|
|
||||||
"Set 'due' to card creation time."
|
|
||||||
self.db.execute("""
|
|
||||||
update cards set
|
|
||||||
due = created,
|
|
||||||
mod = :now
|
|
||||||
where type = 2""", now=time.time())
|
|
||||||
|
|
||||||
def rescheduleCards(self, ids, min, max):
|
|
||||||
"Reset cards and schedule with new interval in days (min, max)."
|
|
||||||
self.resetCards(ids)
|
|
||||||
vals = []
|
|
||||||
for id in ids:
|
|
||||||
r = random.uniform(min*86400, max*86400)
|
|
||||||
vals.append({
|
|
||||||
'id': id,
|
|
||||||
'due': r + time.time(),
|
|
||||||
'int': r / 86400.0,
|
|
||||||
't': time.time(),
|
|
||||||
})
|
|
||||||
self.db.executemany("""
|
|
||||||
update cards set
|
|
||||||
interval = :int,
|
|
||||||
due = :due,
|
|
||||||
reps = 1,
|
|
||||||
successive = 1,
|
|
||||||
yesCount = 1,
|
|
||||||
firstAnswered = :t,
|
|
||||||
queue = 1,
|
|
||||||
type = 1,
|
|
||||||
where id = :id""", vals)
|
|
||||||
|
|
||||||
# Times
|
# Times
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
@ -508,9 +426,9 @@ due > :now and due < :now""", now=time.time())
|
||||||
# [cid, fid, mid, tid, gid, tags, flds, data]
|
# [cid, fid, mid, tid, gid, tags, flds, data]
|
||||||
data = [1, 1, fact.model.id, template.id, 1,
|
data = [1, 1, fact.model.id, template.id, 1,
|
||||||
"", fact.joinedFields(), ""]
|
"", fact.joinedFields(), ""]
|
||||||
now = self._formatQA(fact.model, template, "", data)
|
now = self._renderQA(fact.model, template, "", data)
|
||||||
data[6] = "\x1f".join([""]*len(fact._fields))
|
data[6] = "\x1f".join([""]*len(fact._fields))
|
||||||
empty = self._formatQA(fact.model, template, "", data)
|
empty = self._renderQA(fact.model, template, "", data)
|
||||||
if now['q'] == empty['q']:
|
if now['q'] == empty['q']:
|
||||||
continue
|
continue
|
||||||
if not template.conf['allowEmptyAns']:
|
if not template.conf['allowEmptyAns']:
|
||||||
|
@ -637,6 +555,74 @@ select id from facts where id not in (select distinct fid from cards)""")
|
||||||
delete from facts where id in (select fid from cards where queue = -4);
|
delete from facts where id in (select fid from cards where queue = -4);
|
||||||
delete from cards where queue = -4;""")
|
delete from cards where queue = -4;""")
|
||||||
|
|
||||||
|
def resetCards(self, ids=None):
|
||||||
|
"Reset progress on cards in IDS."
|
||||||
|
print "position in resetCards()"
|
||||||
|
sql = """
|
||||||
|
update cards set mod=:now, position=0, type=2, queue=2, lastInterval=0,
|
||||||
|
interval=0, due=created, factor=2.5, reps=0, successive=0, lapses=0, flags=0"""
|
||||||
|
sql2 = "delete from revlog"
|
||||||
|
if ids is None:
|
||||||
|
lim = ""
|
||||||
|
else:
|
||||||
|
sids = ids2str(ids)
|
||||||
|
sql += " where id in "+sids
|
||||||
|
sql2 += " where cardId in "+sids
|
||||||
|
self.db.execute(sql, now=time.time())
|
||||||
|
self.db.execute(sql2)
|
||||||
|
if self.qconf['newCardOrder'] == NEW_CARDS_RANDOM:
|
||||||
|
# we need to re-randomize now
|
||||||
|
self.randomizeNewCards(ids)
|
||||||
|
|
||||||
|
def randomizeNewCards(self, cardIds=None):
|
||||||
|
"Randomize 'due' on all new cards."
|
||||||
|
now = time.time()
|
||||||
|
query = "select distinct fid from cards where reps = 0"
|
||||||
|
if cardIds:
|
||||||
|
query += " and id in %s" % ids2str(cardIds)
|
||||||
|
fids = self.db.list(query)
|
||||||
|
data = [{'fid': fid,
|
||||||
|
'rand': random.uniform(0, now),
|
||||||
|
'now': now} for fid in fids]
|
||||||
|
self.db.executemany("""
|
||||||
|
update cards
|
||||||
|
set due = :rand + ord,
|
||||||
|
mod = :now
|
||||||
|
where fid = :fid
|
||||||
|
and type = 2""", data)
|
||||||
|
|
||||||
|
def orderNewCards(self):
|
||||||
|
"Set 'due' to card creation time."
|
||||||
|
self.db.execute("""
|
||||||
|
update cards set
|
||||||
|
due = created,
|
||||||
|
mod = :now
|
||||||
|
where type = 2""", now=time.time())
|
||||||
|
|
||||||
|
def rescheduleCards(self, ids, min, max):
|
||||||
|
"Reset cards and schedule with new interval in days (min, max)."
|
||||||
|
self.resetCards(ids)
|
||||||
|
vals = []
|
||||||
|
for id in ids:
|
||||||
|
r = random.uniform(min*86400, max*86400)
|
||||||
|
vals.append({
|
||||||
|
'id': id,
|
||||||
|
'due': r + time.time(),
|
||||||
|
'int': r / 86400.0,
|
||||||
|
't': time.time(),
|
||||||
|
})
|
||||||
|
self.db.executemany("""
|
||||||
|
update cards set
|
||||||
|
interval = :int,
|
||||||
|
due = :due,
|
||||||
|
reps = 1,
|
||||||
|
successive = 1,
|
||||||
|
yesCount = 1,
|
||||||
|
firstAnswered = :t,
|
||||||
|
queue = 1,
|
||||||
|
type = 1,
|
||||||
|
where id = :id""", vals)
|
||||||
|
|
||||||
# Models
|
# Models
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
@ -743,7 +729,6 @@ update cards set
|
||||||
tid = :new,
|
tid = :new,
|
||||||
ord = :ord
|
ord = :ord
|
||||||
where id in %s""" % ids2str(ids), new=new.id, ord=new.ord)
|
where id in %s""" % ids2str(ids), new=new.id, ord=new.ord)
|
||||||
self.updateCache(fids, type="fact")
|
|
||||||
cardIds = self.db.list(
|
cardIds = self.db.list(
|
||||||
"select id from cards where fid in %s" %
|
"select id from cards where fid in %s" %
|
||||||
ids2str(fids))
|
ids2str(fids))
|
||||||
|
@ -863,11 +848,10 @@ ord = (select ord from templates where id = tid),
|
||||||
mod = :now
|
mod = :now
|
||||||
where tid in %s""" % strids, now=time.time())
|
where tid in %s""" % strids, now=time.time())
|
||||||
|
|
||||||
# Caches: q/a, facts.cache and fdata.csum
|
# Q/A generation
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def updateCache(self, ids=None, type="card"):
|
def renderQA(self, ids=None, type="card"):
|
||||||
"Update cache after facts or models changed."
|
|
||||||
# gather metadata
|
# gather metadata
|
||||||
if type == "card":
|
if type == "card":
|
||||||
where = "and c.id in " + ids2str(ids)
|
where = "and c.id in " + ids2str(ids)
|
||||||
|
@ -886,10 +870,10 @@ where tid in %s""" % strids, now=time.time())
|
||||||
for t in m.templates:
|
for t in m.templates:
|
||||||
templs[t.id] = t
|
templs[t.id] = t
|
||||||
groups = dict(self.db.all("select id, name from groups"))
|
groups = dict(self.db.all("select id, name from groups"))
|
||||||
return [self._formatQA(mods[row[2]], templs[row[3]], groups[row[4]], row)
|
return [self._renderQA(mods[row[2]], templs[row[3]], groups[row[4]], row)
|
||||||
for row in self._qaData(where)]
|
for row in self._qaData(where)]
|
||||||
|
|
||||||
def _formatQA(self, model, template, gname, data, filters=True):
|
def _renderQA(self, model, template, gname, data, filters=True):
|
||||||
"Returns hash of id, question, answer."
|
"Returns hash of id, question, answer."
|
||||||
# data is [cid, fid, mid, tid, gid, tags, flds, data]
|
# data is [cid, fid, mid, tid, gid, tags, flds, data]
|
||||||
# unpack fields and create dict
|
# unpack fields and create dict
|
||||||
|
@ -913,10 +897,10 @@ where tid in %s""" % strids, now=time.time())
|
||||||
d = dict(id=data[0])
|
d = dict(id=data[0])
|
||||||
for (type, format) in (("q", template.qfmt), ("a", template.afmt)):
|
for (type, format) in (("q", template.qfmt), ("a", template.afmt)):
|
||||||
# if filters:
|
# if filters:
|
||||||
# fields = runFilter("formatQA.pre", fields, , self)
|
# fields = runFilter("renderQA.pre", fields, , self)
|
||||||
html = anki.template.render(format, fields)
|
html = anki.template.render(format, fields)
|
||||||
# if filters:
|
# if filters:
|
||||||
# d[type] = runFilter("formatQA.post", html, fields, meta, self)
|
# d[type] = runFilter("renderQA.post", html, fields, meta, self)
|
||||||
self.media.registerText(html)
|
self.media.registerText(html)
|
||||||
d[type] = html
|
d[type] = html
|
||||||
return d
|
return d
|
||||||
|
@ -1009,7 +993,7 @@ insert or ignore into tags (mod, name) values (%d, :t)""" % intTime(),
|
||||||
self.db.executemany("""
|
self.db.executemany("""
|
||||||
update facts set tags = :t, mod = :n where id = :id""", [fix(row) for row in res])
|
update facts set tags = :t, mod = :n where id = :id""", [fix(row) for row in res])
|
||||||
# update q/a cache
|
# update q/a cache
|
||||||
self.updateCache(fids, type="fact")
|
self.registerTags(parseTags(tags))
|
||||||
self.finishProgress()
|
self.finishProgress()
|
||||||
|
|
||||||
def deleteTags(self, ids, tags):
|
def deleteTags(self, ids, tags):
|
||||||
|
@ -1067,16 +1051,6 @@ update facts set tags = :t, mod = :n where id = :id""", [fix(row) for row in res
|
||||||
def disableProgressHandler(self):
|
def disableProgressHandler(self):
|
||||||
self.progressHandlerEnabled = False
|
self.progressHandlerEnabled = False
|
||||||
|
|
||||||
# Notifications
|
|
||||||
##########################################################################
|
|
||||||
|
|
||||||
def notify(self, msg):
|
|
||||||
"Send a notice to all listeners, or display on stdout."
|
|
||||||
if hookEmpty("notify"):
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
runHook("notify", msg)
|
|
||||||
|
|
||||||
# File-related
|
# File-related
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
import time
|
import time
|
||||||
from anki.errors import AnkiError
|
from anki.errors import AnkiError
|
||||||
from anki.utils import stripHTMLMedia, fieldChecksum, intTime, \
|
from anki.utils import stripHTMLMedia, fieldChecksum, intTime, \
|
||||||
addTags, deleteTags, joinFields, splitFields, ids2str
|
addTags, deleteTags, joinFields, splitFields, ids2str, parseTags
|
||||||
|
|
||||||
class Fact(object):
|
class Fact(object):
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@ insert or replace into facts values (?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||||
sfld, self.data)
|
sfld, self.data)
|
||||||
self.id = res.lastrowid
|
self.id = res.lastrowid
|
||||||
self.updateFieldChecksums()
|
self.updateFieldChecksums()
|
||||||
|
self.deck.registerTags(parseTags(self.tags))
|
||||||
|
|
||||||
def joinedFields(self):
|
def joinedFields(self):
|
||||||
return joinFields(self._fields)
|
return joinFields(self._fields)
|
||||||
|
|
|
@ -45,9 +45,6 @@ def removeHook(hook, func):
|
||||||
if func in hook:
|
if func in hook:
|
||||||
hook.remove(func)
|
hook.remove(func)
|
||||||
|
|
||||||
def hookEmpty(hook):
|
|
||||||
return not _hooks.get(hook)
|
|
||||||
|
|
||||||
# Instrumenting
|
# Instrumenting
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
|
|
|
@ -177,7 +177,7 @@ If a file with the same name exists, return a unique name."""
|
||||||
return unicodedata.normalize('NFD', s)
|
return unicodedata.normalize('NFD', s)
|
||||||
return s
|
return s
|
||||||
# generate q/a and look through all references
|
# generate q/a and look through all references
|
||||||
for p in self.deck.updateCache(type="all"):
|
for p in self.deck.renderQA(type="all"):
|
||||||
for type in ("q", "a"):
|
for type in ("q", "a"):
|
||||||
for f in self.mediaFiles(p[type]):
|
for f in self.mediaFiles(p[type]):
|
||||||
normrefs[norm(f)] = True
|
normrefs[norm(f)] = True
|
||||||
|
|
|
@ -49,9 +49,6 @@ insert or replace into models values (?, ?, ?, ?, ?, ?)""",
|
||||||
self.id = ret.lastrowid
|
self.id = ret.lastrowid
|
||||||
[t._flush() for t in self.templates]
|
[t._flush() for t in self.templates]
|
||||||
|
|
||||||
def updateCache(self):
|
|
||||||
self.deck.updateCache([self.id], "model")
|
|
||||||
|
|
||||||
def _getID(self):
|
def _getID(self):
|
||||||
if not self.id:
|
if not self.id:
|
||||||
# flush so we can get our DB id
|
# flush so we can get our DB id
|
||||||
|
|
|
@ -31,11 +31,11 @@ class Scheduler(object):
|
||||||
self.resetConf()
|
self.resetConf()
|
||||||
t = time.time()
|
t = time.time()
|
||||||
self.resetLearn()
|
self.resetLearn()
|
||||||
print "lrn %0.2fms" % ((time.time() - t)*1000); t = time.time()
|
#print "lrn %0.2fms" % ((time.time() - t)*1000); t = time.time()
|
||||||
self.resetReview()
|
self.resetReview()
|
||||||
print "rev %0.2fms" % ((time.time() - t)*1000); t = time.time()
|
#print "rev %0.2fms" % ((time.time() - t)*1000); t = time.time()
|
||||||
self.resetNew()
|
self.resetNew()
|
||||||
print "new %0.2fms" % ((time.time() - t)*1000); t = time.time()
|
#print "new %0.2fms" % ((time.time() - t)*1000); t = time.time()
|
||||||
|
|
||||||
def answerCard(self, card, ease):
|
def answerCard(self, card, ease):
|
||||||
if card.queue == 0:
|
if card.queue == 0:
|
||||||
|
|
|
@ -76,7 +76,7 @@ def test_db():
|
||||||
m = deck.currentModel()
|
m = deck.currentModel()
|
||||||
m.templates[0].afmt=u'<img src="{{{Back}}}">'
|
m.templates[0].afmt=u'<img src="{{{Back}}}">'
|
||||||
m.flush()
|
m.flush()
|
||||||
m.updateCache()
|
deck.renderQA(type="all")
|
||||||
assert deck.db.scalar("select count() from media") == 2
|
assert deck.db.scalar("select count() from media") == 2
|
||||||
|
|
||||||
def test_deckIntegration():
|
def test_deckIntegration():
|
||||||
|
|
Loading…
Reference in a new issue