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']
def _getQA(self, reload=False):
# this is a hack at the moment
if not self._qa or reload:
self._qa = self.deck.updateCache(
[self.id], "card")[0]
self._qa = self.deck.renderQA([self.id], "card")[0]
return self._qa
def fact(self):

View file

@ -2,24 +2,18 @@
# Copyright: Damien Elmes <anki@ichi2.net>
# License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html
import tempfile, time, os, random, sys, re, stat, shutil
import types, traceback, simplejson, datetime
from operator import itemgetter
from itertools import groupby
import time, os, random, re, stat, simplejson
from anki.lang import _, ngettext
from anki.utils import parseTags, tidyHTML, ids2str, hexifyID, \
canonifyTags, joinTags, addTags, deleteTags, checksum, fieldChecksum, \
stripHTML, intTime, splitFields
from anki.hooks import runHook, hookEmpty, runFilter
checksum, fieldChecksum, addTags, deleteTags, stripHTML, intTime, \
splitFields
from anki.hooks import runHook, runFilter
from anki.sched import Scheduler
from anki.media import MediaRegistry
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
# 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."
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):
return anki.cards.Card(self, id)
@ -168,84 +154,16 @@ qconf=?, conf=?, data=?""",
return anki.models.Template(self, self.deck.db.first(
"select * from templates where id = ?", id))
# if card:
# 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()
# unsorted
##########################################################################
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 nextID(self, type):
id = self.conf.get(type, 1)
self.conf[type] = id+1
return id
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)
def reset(self):
self.sched.reset()
# Times
##########################################################################
@ -508,9 +426,9 @@ due > :now and due < :now""", now=time.time())
# [cid, fid, mid, tid, gid, tags, flds, data]
data = [1, 1, fact.model.id, template.id, 1,
"", fact.joinedFields(), ""]
now = self._formatQA(fact.model, template, "", data)
now = self._renderQA(fact.model, template, "", data)
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']:
continue
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 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
##########################################################################
@ -743,7 +729,6 @@ update cards set
tid = :new,
ord = :ord
where id in %s""" % ids2str(ids), new=new.id, ord=new.ord)
self.updateCache(fids, type="fact")
cardIds = self.db.list(
"select id from cards where fid in %s" %
ids2str(fids))
@ -863,11 +848,10 @@ ord = (select ord from templates where id = tid),
mod = :now
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"):
"Update cache after facts or models changed."
def renderQA(self, ids=None, type="card"):
# gather metadata
if type == "card":
where = "and c.id in " + ids2str(ids)
@ -886,10 +870,10 @@ where tid in %s""" % strids, now=time.time())
for t in m.templates:
templs[t.id] = t
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)]
def _formatQA(self, model, template, gname, data, filters=True):
def _renderQA(self, model, template, gname, data, filters=True):
"Returns hash of id, question, answer."
# data is [cid, fid, mid, tid, gid, tags, flds, data]
# unpack fields and create dict
@ -913,10 +897,10 @@ where tid in %s""" % strids, now=time.time())
d = dict(id=data[0])
for (type, format) in (("q", template.qfmt), ("a", template.afmt)):
# if filters:
# fields = runFilter("formatQA.pre", fields, , self)
# fields = runFilter("renderQA.pre", fields, , self)
html = anki.template.render(format, fields)
# if filters:
# d[type] = runFilter("formatQA.post", html, fields, meta, self)
# d[type] = runFilter("renderQA.post", html, fields, meta, self)
self.media.registerText(html)
d[type] = html
return d
@ -1009,7 +993,7 @@ insert or ignore into tags (mod, name) values (%d, :t)""" % intTime(),
self.db.executemany("""
update facts set tags = :t, mod = :n where id = :id""", [fix(row) for row in res])
# update q/a cache
self.updateCache(fids, type="fact")
self.registerTags(parseTags(tags))
self.finishProgress()
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):
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
##########################################################################

View file

@ -5,7 +5,7 @@
import time
from anki.errors import AnkiError
from anki.utils import stripHTMLMedia, fieldChecksum, intTime, \
addTags, deleteTags, joinFields, splitFields, ids2str
addTags, deleteTags, joinFields, splitFields, ids2str, parseTags
class Fact(object):
@ -48,6 +48,7 @@ insert or replace into facts values (?, ?, ?, ?, ?, ?, ?, ?)""",
sfld, self.data)
self.id = res.lastrowid
self.updateFieldChecksums()
self.deck.registerTags(parseTags(self.tags))
def joinedFields(self):
return joinFields(self._fields)

View file

@ -45,9 +45,6 @@ def removeHook(hook, func):
if func in hook:
hook.remove(func)
def hookEmpty(hook):
return not _hooks.get(hook)
# 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 s
# 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 f in self.mediaFiles(p[type]):
normrefs[norm(f)] = True

View file

@ -49,9 +49,6 @@ insert or replace into models values (?, ?, ?, ?, ?, ?)""",
self.id = ret.lastrowid
[t._flush() for t in self.templates]
def updateCache(self):
self.deck.updateCache([self.id], "model")
def _getID(self):
if not self.id:
# flush so we can get our DB id

View file

@ -31,11 +31,11 @@ class Scheduler(object):
self.resetConf()
t = time.time()
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()
print "rev %0.2fms" % ((time.time() - t)*1000); t = time.time()
#print "rev %0.2fms" % ((time.time() - t)*1000); t = time.time()
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):
if card.queue == 0:

View file

@ -76,7 +76,7 @@ def test_db():
m = deck.currentModel()
m.templates[0].afmt=u'<img src="{{{Back}}}">'
m.flush()
m.updateCache()
deck.renderQA(type="all")
assert deck.db.scalar("select count() from media") == 2
def test_deckIntegration():