updateCache -> renderQA, move some functions around

This commit is contained in:
Damien Elmes 2011-03-11 04:02:27 +09:00
parent 0e084f8e3d
commit ad68500494
8 changed files with 99 additions and 132 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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