mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -04:00
add genCards(), previewCards(), and more unit tests
This commit is contained in:
parent
53215230b4
commit
870c80e076
5 changed files with 163 additions and 80 deletions
|
@ -112,7 +112,7 @@ streak=?, lapses=?, grade=?, cycles=? where id = ?""",
|
||||||
return self.deck.getFact(self.fid)
|
return self.deck.getFact(self.fid)
|
||||||
|
|
||||||
def template(self):
|
def template(self):
|
||||||
return self.deck.getTemplate(self.tid)
|
return self.deck.getTemplate(self.fact().mid, self.ord)
|
||||||
|
|
||||||
def startTimer(self):
|
def startTimer(self):
|
||||||
self.timerStarted = time.time()
|
self.timerStarted = time.time()
|
||||||
|
|
149
anki/deck.py
149
anki/deck.py
|
@ -156,9 +156,8 @@ qconf=?, conf=?, data=?""",
|
||||||
def getFact(self, id):
|
def getFact(self, id):
|
||||||
return anki.facts.Fact(self, id=id)
|
return anki.facts.Fact(self, id=id)
|
||||||
|
|
||||||
def getTemplate(self, id):
|
def getTemplate(self, mid, ord):
|
||||||
return anki.models.Template(self, self.deck.db.first(
|
return self.getModel(mid).templates[ord]
|
||||||
"select * from templates where id = ?", id))
|
|
||||||
|
|
||||||
def getModel(self, mid):
|
def getModel(self, mid):
|
||||||
return anki.models.Model(self, mid)
|
return anki.models.Model(self, mid)
|
||||||
|
@ -198,27 +197,37 @@ qconf=?, conf=?, data=?""",
|
||||||
# notice any new tags
|
# notice any new tags
|
||||||
self.registerTags(fact.tags)
|
self.registerTags(fact.tags)
|
||||||
# if random mode, determine insertion point
|
# if random mode, determine insertion point
|
||||||
isRandom = self.qconf['newCardOrder'] == NEW_CARDS_RANDOM
|
if self.qconf['newCardOrder'] == NEW_CARDS_RANDOM:
|
||||||
if isRandom:
|
|
||||||
due = random.randrange(0, 1000000)
|
due = random.randrange(0, 1000000)
|
||||||
|
else:
|
||||||
|
due = fact.id
|
||||||
# add cards
|
# add cards
|
||||||
ncards = 0
|
ncards = 0
|
||||||
for template in cms:
|
for template in cms:
|
||||||
card = anki.cards.Card(self)
|
self._newCard(fact, template, due, gid)
|
||||||
card.id = self.nextID("cid")
|
|
||||||
card.fid = fact.id
|
|
||||||
card.ord = template['ord']
|
|
||||||
card.gid = template['gid'] or gid
|
|
||||||
if isRandom:
|
|
||||||
card.due = due
|
|
||||||
else:
|
|
||||||
card.due = fact.id
|
|
||||||
card.flush()
|
|
||||||
ncards += 1
|
ncards += 1
|
||||||
return ncards
|
return ncards
|
||||||
|
|
||||||
|
def _deleteFacts(self, ids):
|
||||||
|
"Bulk delete facts by ID. Don't call this directly."
|
||||||
|
if not ids:
|
||||||
|
return
|
||||||
|
strids = ids2str(ids)
|
||||||
|
self.db.execute("delete from facts where id in %s" % strids)
|
||||||
|
self.db.execute("delete from fsums where fid in %s" % strids)
|
||||||
|
|
||||||
|
def _deleteDanglingFacts(self):
|
||||||
|
"Delete any facts without cards. Don't call this directly."
|
||||||
|
ids = self.db.list("""
|
||||||
|
select id from facts where id not in (select distinct fid from cards)""")
|
||||||
|
self._deleteFacts(ids)
|
||||||
|
return ids
|
||||||
|
|
||||||
|
# Card creation
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
def findTemplates(self, fact, checkActive=True):
|
def findTemplates(self, fact, checkActive=True):
|
||||||
"Return active, non-empty templates."
|
"Return (active), non-empty templates."
|
||||||
ok = []
|
ok = []
|
||||||
for template in fact.model.templates:
|
for template in fact.model.templates:
|
||||||
if template['actv'] or not checkActive:
|
if template['actv'] or not checkActive:
|
||||||
|
@ -236,67 +245,57 @@ qconf=?, conf=?, data=?""",
|
||||||
ok.append(template)
|
ok.append(template)
|
||||||
return ok
|
return ok
|
||||||
|
|
||||||
def genCards(self, fact, templates):
|
def genCards(self, fact, templates, gid):
|
||||||
"Generate cards for templates if cards not empty."
|
"Generate cards for templates if cards not empty. Return cards."
|
||||||
# templates should have .ord set
|
cards = []
|
||||||
ids = []
|
# if random mode, determine insertion point
|
||||||
for template in self.findTemplates(fact, False):
|
if self.qconf['newCardOrder'] == NEW_CARDS_RANDOM:
|
||||||
|
# if this fact has existing new cards, use their due time
|
||||||
|
due = self.db.scalar(
|
||||||
|
"select due from cards where fid = ? and queue = 2", fact.id)
|
||||||
|
due = due or random.randrange(1, 1000000)
|
||||||
|
else:
|
||||||
|
due = fact.id
|
||||||
|
for template in self.findTemplates(fact, checkActive=False):
|
||||||
if template not in templates:
|
if template not in templates:
|
||||||
continue
|
continue
|
||||||
|
# if it doesn't already exist
|
||||||
if not self.db.scalar(
|
if not self.db.scalar(
|
||||||
"select 1 from cards where fid = ? and ord = ?",
|
"select 1 from cards where fid = ? and ord = ?",
|
||||||
fact.id, template.ord):
|
fact.id, template['ord']):
|
||||||
card = anki.cards.Card(
|
# create
|
||||||
fact, template,
|
cards.append(self._newCard(fact, template, due, gid))
|
||||||
fact.created+0.0001*template.ord)
|
|
||||||
raise Exception("incorrect; not checking selective study")
|
|
||||||
self.newAvail += 1
|
|
||||||
ids.append(card.id)
|
|
||||||
|
|
||||||
if ids:
|
|
||||||
fact.setMod(textChanged=True, deck=self)
|
|
||||||
self.setMod()
|
|
||||||
return ids
|
|
||||||
|
|
||||||
def _deleteFacts(self, ids):
|
|
||||||
"Bulk delete facts by ID. Don't call this directly."
|
|
||||||
if not ids:
|
|
||||||
return
|
|
||||||
strids = ids2str(ids)
|
|
||||||
self.db.execute("delete from facts where id in %s" % strids)
|
|
||||||
self.db.execute("delete from fsums where fid in %s" % strids)
|
|
||||||
|
|
||||||
def _deleteDanglingFacts(self):
|
|
||||||
"Delete any facts without cards. Don't call this directly."
|
|
||||||
ids = self.db.list("""
|
|
||||||
select id from facts where id not in (select distinct fid from cards)""")
|
|
||||||
self._deleteFacts(ids)
|
|
||||||
return ids
|
|
||||||
|
|
||||||
def previewFact(self, oldFact, cms=None):
|
|
||||||
"Duplicate fact and generate cards for preview. Don't add to deck."
|
|
||||||
# check we have card models available
|
|
||||||
if cms is None:
|
|
||||||
cms = self.findTemplates(oldFact, checkActive=True)
|
|
||||||
if not cms:
|
|
||||||
return []
|
|
||||||
fact = self.cloneFact(oldFact)
|
|
||||||
# proceed
|
|
||||||
cards = []
|
|
||||||
for template in cms:
|
|
||||||
card = anki.cards.Card(fact, template)
|
|
||||||
cards.append(card)
|
|
||||||
fact.setMod(textChanged=True, deck=self, media=False)
|
|
||||||
return cards
|
return cards
|
||||||
|
|
||||||
def cloneFact(self, oldFact):
|
# type 0 - when previewing in add dialog, only non-empty & active
|
||||||
"Copy fact into new session."
|
# type 1 - when previewing edit, only existing
|
||||||
model = self.db.query(Model).get(oldFact.model.id)
|
# type 2 - when previewing in models dialog, all
|
||||||
fact = self.newFact(model)
|
def previewCards(self, fact, type=0):
|
||||||
for field in fact.fdata:
|
"Return uncommited cards for preview."
|
||||||
fact[field.name] = oldFact[field.name]
|
if type == 0:
|
||||||
fact._tags = oldFact._tags
|
cms = self.findTemplates(fact, checkActive=True)
|
||||||
return fact
|
elif type == 1:
|
||||||
|
cms = [c.template() for c in fact.cards()]
|
||||||
|
else:
|
||||||
|
cms = fact.model.templates
|
||||||
|
if not cms:
|
||||||
|
return []
|
||||||
|
cards = []
|
||||||
|
for template in cms:
|
||||||
|
cards.append(self._newCard(fact, template, 1, 1, flush=False))
|
||||||
|
return cards
|
||||||
|
|
||||||
|
def _newCard(self, fact, template, due, gid, flush=True):
|
||||||
|
"Create a new card."
|
||||||
|
card = anki.cards.Card(self)
|
||||||
|
card.id = self.nextID("cid")
|
||||||
|
card.fid = fact.id
|
||||||
|
card.ord = template['ord']
|
||||||
|
card.gid = template['gid'] or gid
|
||||||
|
card.due = due
|
||||||
|
if flush:
|
||||||
|
card.flush()
|
||||||
|
return card
|
||||||
|
|
||||||
# Cards
|
# Cards
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
@ -325,7 +324,7 @@ select id from facts where id not in (select distinct fid from cards)""")
|
||||||
self.db.list("select fid from cards where id in "+sids))
|
self.db.list("select fid from cards where id in "+sids))
|
||||||
# need to handle delete of fsums/revlog remotely after sync
|
# need to handle delete of fsums/revlog remotely after sync
|
||||||
self.db.execute(
|
self.db.execute(
|
||||||
"update cards set crt = 0, mod = ? where id in "+sids,
|
"update cards set crt = 0, queue = -4, mod = ? where id in "+sids,
|
||||||
intTime())
|
intTime())
|
||||||
self.db.execute(
|
self.db.execute(
|
||||||
"update facts set crt = 0, mod = ? where id in "+sfids,
|
"update facts set crt = 0, mod = ? where id in "+sfids,
|
||||||
|
@ -440,12 +439,6 @@ select id from cards where fid in (select id from facts where mid = ?)""",
|
||||||
self.conf['currentModelId'] = self.db.scalar(
|
self.conf['currentModelId'] = self.db.scalar(
|
||||||
"select id from models limit 1")
|
"select id from models limit 1")
|
||||||
|
|
||||||
def modelUseCount(self, model):
|
|
||||||
"Return number of facts using model."
|
|
||||||
return self.db.scalar("select count() from facts "
|
|
||||||
"where facts.mid = :id",
|
|
||||||
id=model.id)
|
|
||||||
|
|
||||||
# Field checksums and sorting fields
|
# Field checksums and sorting fields
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,9 @@ insert or replace into facts values (?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||||
return [self.deck.getCard(id) for id in self.deck.db.list(
|
return [self.deck.getCard(id) for id in self.deck.db.list(
|
||||||
"select id from cards where fid = ? order by id", self.id)]
|
"select id from cards where fid = ? order by id", self.id)]
|
||||||
|
|
||||||
|
def model(self):
|
||||||
|
return self.deck.getModel(self.mid)
|
||||||
|
|
||||||
# Dict interface
|
# Dict interface
|
||||||
##################################################
|
##################################################
|
||||||
|
|
||||||
|
|
|
@ -80,7 +80,12 @@ insert or replace into models values (?, ?, ?, ?, ?, ?, ?)""",
|
||||||
self.id = ret.lastrowid
|
self.id = ret.lastrowid
|
||||||
|
|
||||||
def fids(self):
|
def fids(self):
|
||||||
return self.deck.db.list("select id from facts where mid = ?", self.id)
|
return self.deck.db.list(
|
||||||
|
"select id from facts where mid = ?", self.id)
|
||||||
|
|
||||||
|
def useCount(self):
|
||||||
|
return self.deck.db.scalar(
|
||||||
|
"select count() from facts where mid = ?", self.id)
|
||||||
|
|
||||||
# Copying
|
# Copying
|
||||||
##################################################
|
##################################################
|
||||||
|
|
82
tests/test_cards.py
Normal file
82
tests/test_cards.py
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
from anki.consts import *
|
||||||
|
from tests.shared import getEmptyDeck
|
||||||
|
|
||||||
|
def test_genCards():
|
||||||
|
deck = getEmptyDeck()
|
||||||
|
f = deck.newFact()
|
||||||
|
f['Front'] = u'1'
|
||||||
|
f['Back'] = u'2'
|
||||||
|
deck.addFact(f)
|
||||||
|
cards = deck.genCards(f, f.model.templates, 1)
|
||||||
|
assert len(cards) == 1
|
||||||
|
assert cards[0].ord == 1
|
||||||
|
assert deck.cardCount() == 2
|
||||||
|
assert cards[0].due == f.id
|
||||||
|
# should work on random mode too
|
||||||
|
deck.qconf['newCardOrder'] = NEW_CARDS_RANDOM
|
||||||
|
f = deck.newFact()
|
||||||
|
f['Front'] = u'1'
|
||||||
|
f['Back'] = u'2'
|
||||||
|
deck.addFact(f)
|
||||||
|
cards = deck.genCards(f, f.model.templates, 1)
|
||||||
|
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()
|
||||||
|
f = deck.newFact()
|
||||||
|
f['Front'] = u'1'
|
||||||
|
f['Back'] = u'2'
|
||||||
|
# non-empty and active
|
||||||
|
cards = deck.previewCards(f, 0)
|
||||||
|
assert len(cards) == 1
|
||||||
|
assert cards[0].ord == 0
|
||||||
|
# all templates
|
||||||
|
cards = deck.previewCards(f, 2)
|
||||||
|
assert len(cards) == 2
|
||||||
|
# add the fact, and test existing preview
|
||||||
|
deck.addFact(f)
|
||||||
|
cards = deck.previewCards(f, 1)
|
||||||
|
assert len(cards) == 1
|
||||||
|
assert cards[0].ord == 0
|
||||||
|
# make sure we haven't accidentally added cards to the db
|
||||||
|
assert deck.cardCount() == 1
|
||||||
|
|
||||||
|
def test_delete():
|
||||||
|
deck = getEmptyDeck()
|
||||||
|
f = deck.newFact()
|
||||||
|
f['Front'] = u'1'
|
||||||
|
f['Back'] = u'2'
|
||||||
|
deck.addFact(f)
|
||||||
|
cid = f.cards()[0].id
|
||||||
|
# when the schema is dirty, deletion should be immediate
|
||||||
|
assert deck.schemaDirty() == True
|
||||||
|
deck.deleteCard(cid)
|
||||||
|
assert deck.cardCount() == 0
|
||||||
|
assert deck.factCount() == 0
|
||||||
|
assert deck.db.scalar("select count() from facts") == 0
|
||||||
|
assert deck.db.scalar("select count() from cards") == 0
|
||||||
|
# add the fact back
|
||||||
|
deck.addFact(f)
|
||||||
|
assert deck.cardCount() == 1
|
||||||
|
# mark the schema as clean
|
||||||
|
deck.lastSync = deck.schema + 1
|
||||||
|
# cards/facts should go in the deletion log instead
|
||||||
|
cid = f.cards()[0].id
|
||||||
|
deck.deleteCard(cid)
|
||||||
|
assert deck.cardCount() == 0
|
||||||
|
assert deck.factCount() == 0
|
||||||
|
assert deck.db.scalar("select count() from facts") == 1
|
||||||
|
assert deck.db.scalar("select count() from cards") == 1
|
||||||
|
assert deck.db.scalar("select 1 from cards where crt = 0") == 1
|
||||||
|
assert deck.db.scalar("select 1 from facts where crt = 0") == 1
|
||||||
|
assert deck.db.scalar("select queue from cards") == -4
|
||||||
|
# modifying the schema should empty the trash
|
||||||
|
deck.modSchema()
|
||||||
|
assert deck.cardCount() == 0
|
||||||
|
assert deck.factCount() == 0
|
||||||
|
assert deck.db.scalar("select count() from facts") == 0
|
||||||
|
assert deck.db.scalar("select count() from cards") == 0
|
Loading…
Reference in a new issue