diff --git a/anki/deck.py b/anki/deck.py index e9bb56622..f4590c610 100644 --- a/anki/deck.py +++ b/anki/deck.py @@ -5,7 +5,7 @@ import time, os, random, re, stat, simplejson, datetime, copy, shutil from anki.lang import _, ngettext from anki.utils import ids2str, hexifyID, checksum, fieldChecksum, stripHTML, \ - intTime, splitFields, joinFields + intTime, splitFields, joinFields, maxID from anki.hooks import runHook, runFilter from anki.sched import Scheduler from anki.models import ModelManager @@ -280,27 +280,36 @@ crt=?, mod=?, scm=?, dty=?, usn=?, ls=?, conf=?""", ok.append(t) return ok - def genCards(self, fact, templates): - "Generate cards for templates if cards not empty. Return cards." - cards = [] - # if random mode, determine insertion point - if self.models.randomNew(): - # if this fact has existing new cards, use their due time - due = self.db.scalar( - "select due from cards where fid = ? and queue = 0", fact.id) - due = due or self._randPos() - else: - due = fact.id - for template in self.findTemplates(fact, checkActive=False): - if template not in templates: - continue - # if it doesn't already exist - if not self.db.scalar( - "select 1 from cards where fid = ? and ord = ?", - fact.id, template['ord']): - # create - cards.append(self._newCard(fact, template, due)) - return cards + def genCards(self, fids, limit=None): + "Generate cards for active or limited, non-empty templates." + # build map of (fid,ord) so we don't create dupes + sfids = ids2str(fids) + have = {} + for fid, ord in self.db.execute( + "select fid, ord from cards where fid in "+sfids): + have[(fid,ord)] = True + # build cards for each fact + data = [] + ts = maxID(self.db) + for fid, mid, gid, flds in self.db.execute( + "select id, mid, gid, flds from facts where id in "+sfids): + model = self.models.get(mid) + avail = self.models.availOrds(model, flds) + ok = [] + for t in model['tmpls']: + if not limit and not t['actv']: + continue + elif limit and t not in limit: + continue + elif (fid,t['ord']) in have: + continue + if t['ord'] in avail: + data.append((ts, fid, t['gid'] or gid, t['ord'], + ts, fid)) + # bulk update + self.db.executemany(""" +insert into cards values (?,?,?,?,?,-1,0,0,?,0,0,0,0,0,0,0,"")""", + data) # type 0 - when previewing in add dialog, only non-empty & active # type 1 - when previewing edit, only existing diff --git a/anki/importing/base.py b/anki/importing/base.py index 83c6c8331..dcc6a7b99 100644 --- a/anki/importing/base.py +++ b/anki/importing/base.py @@ -2,7 +2,7 @@ # Copyright: Damien Elmes # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -from anki.utils import intTime +from anki.utils import intTime, maxID # Base importer ########################################################################## @@ -27,11 +27,7 @@ class Importer(object): # need to make sure our starting point is safe. def _prepareTS(self): - now = intTime(1000) - for tbl in "cards", "facts": - now = max(now, self.dst.db.scalar( - "select max(id) from %s" % tbl)) - self._ts = now + self._ts = maxID(self.dst.db) def ts(self): self._ts += 1 diff --git a/anki/utils.py b/anki/utils.py index 817bdbad3..976484185 100644 --- a/anki/utils.py +++ b/anki/utils.py @@ -182,6 +182,14 @@ def timestampID(db, table): t += 1 return t +def maxID(db): + "Return the first safe ID to use." + now = intTime(1000) + for tbl in "cards", "facts": + now = max(now, db.scalar( + "select max(id) from %s" % tbl)) + return now + 1 + def guid64(): return random.randint(-sys.maxint-1, sys.maxint) diff --git a/tests/test_cards.py b/tests/test_cards.py index 5bfd16208..7898525db 100644 --- a/tests/test_cards.py +++ b/tests/test_cards.py @@ -12,21 +12,8 @@ def test_genCards(): f['Front'] = u'1' f['Back'] = u'2' deck.addFact(f) - cards = deck.genCards(f, f.model()['tmpls']) - assert len(cards) == 1 - assert cards[0].ord == 1 + deck.genCards([f.id], f.model()['tmpls']) assert deck.cardCount() == 2 - assert cards[0].due == f.id - # should work on random mode too - deck.models.current()['newOrder'] = NEW_CARDS_RANDOM - f = deck.newFact() - f['Front'] = u'1' - f['Back'] = u'2' - deck.addFact(f) - cards = deck.genCards(f, f.model()['tmpls']) - assert deck.cardCount() == 4 - c = deck.db.list("select due from cards where fid = ?", f.id) - assert c[0] == c[1] def test_previewCards(): deck = getEmptyDeck()