mirror of
https://github.com/ankitects/anki.git
synced 2025-09-21 07:22:23 -04:00
restore the deletion log
the initial plan was to zero the creation time and leave the cards/facts there until we have a chance to garbage collect them on a schema change, but such an approach won't work with deck subscriptions
This commit is contained in:
parent
f2604b7805
commit
3d370f675b
6 changed files with 39 additions and 62 deletions
|
@ -10,7 +10,7 @@ from anki.utils import intTime, hexifyID
|
||||||
|
|
||||||
# Type: 0=new, 1=learning, 2=due
|
# Type: 0=new, 1=learning, 2=due
|
||||||
# Queue: same as above, and:
|
# Queue: same as above, and:
|
||||||
# -1=suspended, -2=user buried, -3=sched buried, -4=deleted
|
# -1=suspended, -2=user buried, -3=sched buried
|
||||||
# Due is used differently for different queues.
|
# Due is used differently for different queues.
|
||||||
# - new queue: fact id or random int
|
# - new queue: fact id or random int
|
||||||
# - rev queue: integer day
|
# - rev queue: integer day
|
||||||
|
|
|
@ -20,6 +20,10 @@ REV_CARDS_OLD_FIRST = 0
|
||||||
REV_CARDS_NEW_FIRST = 1
|
REV_CARDS_NEW_FIRST = 1
|
||||||
REV_CARDS_RANDOM = 2
|
REV_CARDS_RANDOM = 2
|
||||||
|
|
||||||
|
# deletion types
|
||||||
|
DEL_CARD = 0
|
||||||
|
DEL_FACT = 1
|
||||||
|
|
||||||
# Labels
|
# Labels
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
|
63
anki/deck.py
63
anki/deck.py
|
@ -152,8 +152,6 @@ qconf=?, conf=?, data=?""",
|
||||||
if not self.schemaChanged():
|
if not self.schemaChanged():
|
||||||
if check and not runFilter("modSchema", True):
|
if check and not runFilter("modSchema", True):
|
||||||
raise AnkiError("abortSchemaMod")
|
raise AnkiError("abortSchemaMod")
|
||||||
# next sync will be full
|
|
||||||
self.emptyTrash()
|
|
||||||
self.scm = intTime()
|
self.scm = intTime()
|
||||||
|
|
||||||
def schemaChanged(self):
|
def schemaChanged(self):
|
||||||
|
@ -213,11 +211,18 @@ qconf=?, conf=?, data=?""",
|
||||||
self.modelCache = {}
|
self.modelCache = {}
|
||||||
self.sched.reset()
|
self.sched.reset()
|
||||||
|
|
||||||
|
# Deletion logging
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
def _logDels(self, ids, type):
|
||||||
|
self.db.executemany("insert into graves values (%d, ?, %d)" % (
|
||||||
|
intTime(), type), ([x] for x in ids))
|
||||||
|
|
||||||
# Facts
|
# Facts
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def factCount(self):
|
def factCount(self):
|
||||||
return self.db.scalar("select count() from facts where crt != 0")
|
return self.db.scalar("select count() from facts")
|
||||||
|
|
||||||
def newFact(self):
|
def newFact(self):
|
||||||
"Return a new fact with the current model."
|
"Return a new fact with the current model."
|
||||||
|
@ -251,13 +256,7 @@ qconf=?, conf=?, data=?""",
|
||||||
strids = ids2str(ids)
|
strids = ids2str(ids)
|
||||||
self.db.execute("delete from facts where id in %s" % strids)
|
self.db.execute("delete from facts where id in %s" % strids)
|
||||||
self.db.execute("delete from fsums where fid in %s" % strids)
|
self.db.execute("delete from fsums where fid in %s" % strids)
|
||||||
|
self._logDels(ids, DEL_FACT)
|
||||||
def _delDanglingFacts(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._delFacts(ids)
|
|
||||||
return ids
|
|
||||||
|
|
||||||
# Card creation
|
# Card creation
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
@ -341,41 +340,23 @@ select id from facts where id not in (select distinct fid from cards)""")
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def cardCount(self):
|
def cardCount(self):
|
||||||
return self.db.scalar("select count() from cards where crt != 0")
|
return self.db.scalar("select count() from cards")
|
||||||
|
|
||||||
def delCard(self, id):
|
|
||||||
"Delete a card given its id. Delete any unused facts."
|
|
||||||
self.delCards([id])
|
|
||||||
|
|
||||||
def delCards(self, ids):
|
def delCards(self, ids):
|
||||||
"Bulk delete cards by ID."
|
"Bulk delete cards by ID."
|
||||||
if not ids:
|
if not ids:
|
||||||
return
|
return
|
||||||
sids = ids2str(ids)
|
sids = ids2str(ids)
|
||||||
if self.schemaChanged():
|
fids = self.db.list("select fid from cards where id in "+sids)
|
||||||
# immediate delete?
|
# remove cards
|
||||||
self.db.execute("delete from cards where id in %s" % sids)
|
self.db.execute("delete from cards where id in "+sids)
|
||||||
self.db.execute("delete from revlog where cid in %s" % sids)
|
self.db.execute("delete from revlog where cid in "+sids)
|
||||||
# remove any dangling facts
|
self._logDels(ids, DEL_CARD)
|
||||||
self._delDanglingFacts()
|
# then facts
|
||||||
else:
|
fids = self.db.list("""
|
||||||
# trash
|
select id from facts where id in %s and id not in (select fid from cards)""" %
|
||||||
sfids = ids2str(
|
ids2str(fids))
|
||||||
self.db.list("select fid from cards where id in "+sids))
|
self._delFacts(fids)
|
||||||
# need to handle delete of fsums/revlog remotely after sync
|
|
||||||
self.db.execute(
|
|
||||||
"update cards set crt = 0, queue = -4, mod = ? where id in "+sids,
|
|
||||||
intTime())
|
|
||||||
self.db.execute(
|
|
||||||
"update facts set crt = 0, mod = ? where id in "+sfids,
|
|
||||||
intTime())
|
|
||||||
self.db.execute("delete from fsums where fid in "+sfids)
|
|
||||||
self.db.execute("delete from revlog where cid in "+sids)
|
|
||||||
|
|
||||||
def emptyTrash(self):
|
|
||||||
self.db.executescript("""
|
|
||||||
delete from facts where id in (select fid from cards where queue = -4);
|
|
||||||
delete from cards where queue = -4;""")
|
|
||||||
|
|
||||||
# Models
|
# Models
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
@ -783,6 +764,10 @@ update facts set tags = :t, mod = :n where id = :id""", [fix(row) for row in res
|
||||||
problems = []
|
problems = []
|
||||||
self.save()
|
self.save()
|
||||||
oldSize = os.stat(self.path)[stat.ST_SIZE]
|
oldSize = os.stat(self.path)[stat.ST_SIZE]
|
||||||
|
# delete any facts with missing cards
|
||||||
|
ids = self.db.list("""
|
||||||
|
select id from facts where id not in (select distinct fid from cards)""")
|
||||||
|
self._delFacts(ids)
|
||||||
# tags
|
# tags
|
||||||
self.db.execute("delete from tags")
|
self.db.execute("delete from tags")
|
||||||
self.updateFactTags()
|
self.updateFactTags()
|
||||||
|
|
|
@ -135,6 +135,12 @@ create table if not exists gconf (
|
||||||
conf text not null
|
conf text not null
|
||||||
);
|
);
|
||||||
|
|
||||||
|
create table if not exists graves (
|
||||||
|
time integer not null,
|
||||||
|
oid integer not null,
|
||||||
|
type integer not null
|
||||||
|
);
|
||||||
|
|
||||||
create table if not exists revlog (
|
create table if not exists revlog (
|
||||||
time integer primary key,
|
time integer primary key,
|
||||||
cid integer not null,
|
cid integer not null,
|
||||||
|
|
|
@ -53,12 +53,10 @@ def test_delete():
|
||||||
f['Back'] = u'2'
|
f['Back'] = u'2'
|
||||||
deck.addFact(f)
|
deck.addFact(f)
|
||||||
cid = f.cards()[0].id
|
cid = f.cards()[0].id
|
||||||
# when the schema is dirty, deletion should be immediate
|
|
||||||
assert deck.schemaChanged() == True
|
|
||||||
deck.reset()
|
deck.reset()
|
||||||
deck.sched.answerCard(deck.sched.getCard(), 2)
|
deck.sched.answerCard(deck.sched.getCard(), 2)
|
||||||
assert deck.db.scalar("select count() from revlog") == 1
|
assert deck.db.scalar("select count() from revlog") == 1
|
||||||
deck.delCard(cid)
|
deck.delCards([cid])
|
||||||
assert deck.cardCount() == 0
|
assert deck.cardCount() == 0
|
||||||
assert deck.factCount() == 0
|
assert deck.factCount() == 0
|
||||||
assert deck.db.scalar("select count() from facts") == 0
|
assert deck.db.scalar("select count() from facts") == 0
|
||||||
|
@ -68,26 +66,10 @@ def test_delete():
|
||||||
# add the fact back
|
# add the fact back
|
||||||
deck.addFact(f)
|
deck.addFact(f)
|
||||||
assert deck.cardCount() == 1
|
assert deck.cardCount() == 1
|
||||||
# mark the schema as clean
|
|
||||||
deck.lastSync = deck.scm + 1
|
|
||||||
# and deck as syncable
|
|
||||||
deck.syncName = "abc"
|
|
||||||
# cards/facts should go in the deletion log instead
|
|
||||||
cid = f.cards()[0].id
|
cid = f.cards()[0].id
|
||||||
deck.delCard(cid)
|
deck.delCards([cid])
|
||||||
assert deck.cardCount() == 0
|
assert deck.cardCount() == 0
|
||||||
assert deck.factCount() == 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
|
|
||||||
|
|
||||||
def test_misc():
|
def test_misc():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
|
|
|
@ -74,11 +74,11 @@ def test_factAddDelete():
|
||||||
id1 = cards[0].id; id2 = cards[1].id
|
id1 = cards[0].id; id2 = cards[1].id
|
||||||
assert deck.cardCount() == 2
|
assert deck.cardCount() == 2
|
||||||
assert deck.factCount() == 1
|
assert deck.factCount() == 1
|
||||||
deck.delCard(id1)
|
deck.delCards([id1])
|
||||||
assert deck.cardCount() == 1
|
assert deck.cardCount() == 1
|
||||||
assert deck.factCount() == 1
|
assert deck.factCount() == 1
|
||||||
# and the second should clear the fact
|
# and the second should clear the fact
|
||||||
deck.delCard(id2)
|
deck.delCards([id2])
|
||||||
assert deck.cardCount() == 0
|
assert deck.cardCount() == 0
|
||||||
assert deck.factCount() == 0
|
assert deck.factCount() == 0
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue