mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 14:02:21 -04:00
facts -> notes
This commit is contained in:
parent
10e1c1b03e
commit
6e4e8249fb
33 changed files with 638 additions and 638 deletions
|
@ -27,10 +27,10 @@ Refresh after a change:
|
||||||
|
|
||||||
Edit the card:
|
Edit the card:
|
||||||
|
|
||||||
fact = card.fact()
|
note = card.note()
|
||||||
for (name, value) in fact.items():
|
for (name, value) in note.items():
|
||||||
fact[name] = value + " new"
|
note[name] = value + " new"
|
||||||
fact.flush()
|
note.flush()
|
||||||
|
|
||||||
Save & close:
|
Save & close:
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ from anki.utils import intTime, hexifyID, timestampID
|
||||||
# Queue: same as above, and:
|
# Queue: same as above, and:
|
||||||
# -1=suspended, -2=user buried, -3=sched buried
|
# -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: note id or random int
|
||||||
# - rev queue: integer day
|
# - rev queue: integer day
|
||||||
# - lrn queue: integer timestamp
|
# - lrn queue: integer timestamp
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ class Card(object):
|
||||||
self.id = id
|
self.id = id
|
||||||
self.load()
|
self.load()
|
||||||
else:
|
else:
|
||||||
# to flush, set fid, ord, and due
|
# to flush, set nid, ord, and due
|
||||||
self.id = timestampID(deck.db, "cards")
|
self.id = timestampID(deck.db, "cards")
|
||||||
self.gid = 1
|
self.gid = 1
|
||||||
self.crt = intTime()
|
self.crt = intTime()
|
||||||
|
@ -44,7 +44,7 @@ class Card(object):
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
(self.id,
|
(self.id,
|
||||||
self.fid,
|
self.nid,
|
||||||
self.gid,
|
self.gid,
|
||||||
self.ord,
|
self.ord,
|
||||||
self.mod,
|
self.mod,
|
||||||
|
@ -72,7 +72,7 @@ class Card(object):
|
||||||
insert or replace into cards values
|
insert or replace into cards values
|
||||||
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||||
self.id,
|
self.id,
|
||||||
self.fid,
|
self.nid,
|
||||||
self.gid,
|
self.gid,
|
||||||
self.ord,
|
self.ord,
|
||||||
self.mod,
|
self.mod,
|
||||||
|
@ -108,21 +108,21 @@ lapses=?, left=?, edue=? where id = ?""",
|
||||||
|
|
||||||
def _getQA(self, reload=False):
|
def _getQA(self, reload=False):
|
||||||
if not self._qa or reload:
|
if not self._qa or reload:
|
||||||
f = self.fact(); m = self.model()
|
f = self.note(); m = self.model()
|
||||||
data = [self.id, f.id, m['id'], self.gid, self.ord, f.stringTags(),
|
data = [self.id, f.id, m['id'], self.gid, self.ord, f.stringTags(),
|
||||||
f.joinedFields()]
|
f.joinedFields()]
|
||||||
self._qa = self.deck._renderQA(data)
|
self._qa = self.deck._renderQA(data)
|
||||||
return self._qa
|
return self._qa
|
||||||
|
|
||||||
def _reviewData(self, reload=False):
|
def _reviewData(self, reload=False):
|
||||||
"Fetch the model and fact."
|
"Fetch the model and note."
|
||||||
if not self._rd or reload:
|
if not self._rd or reload:
|
||||||
f = self.deck.getFact(self.fid)
|
f = self.deck.getNote(self.nid)
|
||||||
m = self.deck.models.get(f.mid)
|
m = self.deck.models.get(f.mid)
|
||||||
self._rd = [f, m]
|
self._rd = [f, m]
|
||||||
return self._rd
|
return self._rd
|
||||||
|
|
||||||
def fact(self):
|
def note(self):
|
||||||
return self._reviewData()[0]
|
return self._reviewData()[0]
|
||||||
|
|
||||||
def model(self, reload=False):
|
def model(self, reload=False):
|
||||||
|
|
|
@ -24,7 +24,7 @@ REV_CARDS_NEW_FIRST = 2
|
||||||
|
|
||||||
# removal types
|
# removal types
|
||||||
REM_CARD = 0
|
REM_CARD = 0
|
||||||
REM_FACT = 1
|
REM_NOTE = 1
|
||||||
REM_GROUP = 2
|
REM_GROUP = 2
|
||||||
|
|
||||||
# count display
|
# count display
|
||||||
|
|
148
anki/deck.py
148
anki/deck.py
|
@ -16,7 +16,7 @@ from anki.consts import *
|
||||||
from anki.errors import AnkiError
|
from anki.errors import AnkiError
|
||||||
|
|
||||||
import anki.latex # sets up hook
|
import anki.latex # sets up hook
|
||||||
import anki.cards, anki.facts, anki.template, anki.cram, anki.find
|
import anki.cards, anki.notes, anki.template, anki.cram, anki.find
|
||||||
|
|
||||||
defaultConf = {
|
defaultConf = {
|
||||||
# scheduling options
|
# scheduling options
|
||||||
|
@ -29,7 +29,7 @@ defaultConf = {
|
||||||
'fontFamilies': [
|
'fontFamilies': [
|
||||||
[u'MS 明朝',u'ヒラギノ明朝 Pro W3',u'Kochi Mincho', u'東風明朝']
|
[u'MS 明朝',u'ヒラギノ明朝 Pro W3',u'Kochi Mincho', u'東風明朝']
|
||||||
],
|
],
|
||||||
'sortType': "factFld",
|
'sortType': "noteFld",
|
||||||
'sortBackwards': False,
|
'sortBackwards': False,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,7 +180,7 @@ crt=?, mod=?, scm=?, dty=?, usn=?, ls=?, conf=?""",
|
||||||
|
|
||||||
def beforeUpload(self):
|
def beforeUpload(self):
|
||||||
"Called before a full upload."
|
"Called before a full upload."
|
||||||
tbls = "facts", "cards", "revlog", "graves"
|
tbls = "notes", "cards", "revlog", "graves"
|
||||||
for t in tbls:
|
for t in tbls:
|
||||||
self.db.execute("update %s set usn=0 where usn=-1" % t)
|
self.db.execute("update %s set usn=0 where usn=-1" % t)
|
||||||
self._usn = 0
|
self._usn = 0
|
||||||
|
@ -194,8 +194,8 @@ crt=?, mod=?, scm=?, dty=?, usn=?, ls=?, conf=?""",
|
||||||
def getCard(self, id):
|
def getCard(self, id):
|
||||||
return anki.cards.Card(self, id)
|
return anki.cards.Card(self, id)
|
||||||
|
|
||||||
def getFact(self, id):
|
def getNote(self, id):
|
||||||
return anki.facts.Fact(self, id=id)
|
return anki.notes.Note(self, id=id)
|
||||||
|
|
||||||
# Utils
|
# Utils
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
@ -218,23 +218,23 @@ crt=?, mod=?, scm=?, dty=?, usn=?, ls=?, conf=?""",
|
||||||
self.db.executemany("insert into graves values (%d, ?, %d)" % (
|
self.db.executemany("insert into graves values (%d, ?, %d)" % (
|
||||||
self.usn(), type), ([x] for x in ids))
|
self.usn(), type), ([x] for x in ids))
|
||||||
|
|
||||||
# Facts
|
# Notes
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def factCount(self):
|
def noteCount(self):
|
||||||
return self.db.scalar("select count() from facts")
|
return self.db.scalar("select count() from notes")
|
||||||
|
|
||||||
def newFact(self):
|
def newNote(self):
|
||||||
"Return a new fact with the current model."
|
"Return a new note with the current model."
|
||||||
return anki.facts.Fact(self, self.models.current())
|
return anki.notes.Note(self, self.models.current())
|
||||||
|
|
||||||
def addFact(self, fact):
|
def addNote(self, note):
|
||||||
"Add a fact to the deck. Return number of new cards."
|
"Add a note to the deck. Return number of new cards."
|
||||||
# check we have card models available, then save
|
# check we have card models available, then save
|
||||||
cms = self.findTemplates(fact)
|
cms = self.findTemplates(note)
|
||||||
if not cms:
|
if not cms:
|
||||||
return 0
|
return 0
|
||||||
fact.flush()
|
note.flush()
|
||||||
# randomize?
|
# randomize?
|
||||||
if self.models.randomNew():
|
if self.models.randomNew():
|
||||||
due = self._randPos()
|
due = self._randPos()
|
||||||
|
@ -243,65 +243,65 @@ crt=?, mod=?, scm=?, dty=?, usn=?, ls=?, conf=?""",
|
||||||
# add cards
|
# add cards
|
||||||
ncards = 0
|
ncards = 0
|
||||||
for template in cms:
|
for template in cms:
|
||||||
self._newCard(fact, template, due)
|
self._newCard(note, template, due)
|
||||||
ncards += 1
|
ncards += 1
|
||||||
return ncards
|
return ncards
|
||||||
|
|
||||||
def _randPos(self):
|
def _randPos(self):
|
||||||
return random.randrange(1, sys.maxint)
|
return random.randrange(1, sys.maxint)
|
||||||
|
|
||||||
def remFacts(self, ids):
|
def remNotes(self, ids):
|
||||||
self.remCards(self.db.list("select id from cards where fid in "+
|
self.remCards(self.db.list("select id from cards where nid in "+
|
||||||
ids2str(ids)))
|
ids2str(ids)))
|
||||||
|
|
||||||
def _remFacts(self, ids):
|
def _remNotes(self, ids):
|
||||||
"Bulk delete facts by ID. Don't call this directly."
|
"Bulk delete notes by ID. Don't call this directly."
|
||||||
if not ids:
|
if not ids:
|
||||||
return
|
return
|
||||||
strids = ids2str(ids)
|
strids = ids2str(ids)
|
||||||
# we need to log these independently of cards, as one side may have
|
# we need to log these independently of cards, as one side may have
|
||||||
# more card templates
|
# more card templates
|
||||||
self._logRem(ids, REM_FACT)
|
self._logRem(ids, REM_NOTE)
|
||||||
self.db.execute("delete from facts where id in %s" % strids)
|
self.db.execute("delete from notes where id in %s" % strids)
|
||||||
self.db.execute("delete from fsums where fid in %s" % strids)
|
self.db.execute("delete from nsums where nid in %s" % strids)
|
||||||
|
|
||||||
# Card creation
|
# Card creation
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def findTemplates(self, fact):
|
def findTemplates(self, note):
|
||||||
"Return (active), non-empty templates."
|
"Return (active), non-empty templates."
|
||||||
ok = []
|
ok = []
|
||||||
model = fact.model()
|
model = note.model()
|
||||||
avail = self.models.availOrds(model, joinFields(fact.fields))
|
avail = self.models.availOrds(model, joinFields(note.fields))
|
||||||
ok = []
|
ok = []
|
||||||
for t in model['tmpls']:
|
for t in model['tmpls']:
|
||||||
if t['ord'] in avail:
|
if t['ord'] in avail:
|
||||||
ok.append(t)
|
ok.append(t)
|
||||||
return ok
|
return ok
|
||||||
|
|
||||||
def genCards(self, fids):
|
def genCards(self, nids):
|
||||||
"Generate cards for non-empty templates."
|
"Generate cards for non-empty templates."
|
||||||
# build map of (fid,ord) so we don't create dupes
|
# build map of (nid,ord) so we don't create dupes
|
||||||
sfids = ids2str(fids)
|
snids = ids2str(nids)
|
||||||
have = {}
|
have = {}
|
||||||
for fid, ord in self.db.execute(
|
for nid, ord in self.db.execute(
|
||||||
"select fid, ord from cards where fid in "+sfids):
|
"select nid, ord from cards where nid in "+snids):
|
||||||
have[(fid,ord)] = True
|
have[(nid,ord)] = True
|
||||||
# build cards for each fact
|
# build cards for each note
|
||||||
data = []
|
data = []
|
||||||
ts = maxID(self.db)
|
ts = maxID(self.db)
|
||||||
now = intTime()
|
now = intTime()
|
||||||
for fid, mid, gid, flds in self.db.execute(
|
for nid, mid, gid, flds in self.db.execute(
|
||||||
"select id, mid, gid, flds from facts where id in "+sfids):
|
"select id, mid, gid, flds from notes where id in "+snids):
|
||||||
model = self.models.get(mid)
|
model = self.models.get(mid)
|
||||||
avail = self.models.availOrds(model, flds)
|
avail = self.models.availOrds(model, flds)
|
||||||
ok = []
|
ok = []
|
||||||
for t in model['tmpls']:
|
for t in model['tmpls']:
|
||||||
if (fid,t['ord']) in have:
|
if (nid,t['ord']) in have:
|
||||||
continue
|
continue
|
||||||
if t['ord'] in avail:
|
if t['ord'] in avail:
|
||||||
data.append((ts, fid, t['gid'] or gid, t['ord'],
|
data.append((ts, nid, t['gid'] or gid, t['ord'],
|
||||||
now, fid))
|
now, nid))
|
||||||
ts += 1
|
ts += 1
|
||||||
# bulk update
|
# bulk update
|
||||||
self.db.executemany("""
|
self.db.executemany("""
|
||||||
|
@ -311,26 +311,26 @@ insert into cards values (?,?,?,?,?,-1,0,0,?,0,0,0,0,0,0,0,"")""",
|
||||||
# type 0 - when previewing in add dialog, only non-empty
|
# type 0 - when previewing in add dialog, only non-empty
|
||||||
# type 1 - when previewing edit, only existing
|
# type 1 - when previewing edit, only existing
|
||||||
# type 2 - when previewing in models dialog, all templates
|
# type 2 - when previewing in models dialog, all templates
|
||||||
def previewCards(self, fact, type=0):
|
def previewCards(self, note, type=0):
|
||||||
if type == 0:
|
if type == 0:
|
||||||
cms = self.findTemplates(fact)
|
cms = self.findTemplates(note)
|
||||||
elif type == 1:
|
elif type == 1:
|
||||||
cms = [c.template() for c in fact.cards()]
|
cms = [c.template() for c in note.cards()]
|
||||||
else:
|
else:
|
||||||
cms = fact.model()['tmpls']
|
cms = note.model()['tmpls']
|
||||||
if not cms:
|
if not cms:
|
||||||
return []
|
return []
|
||||||
cards = []
|
cards = []
|
||||||
for template in cms:
|
for template in cms:
|
||||||
cards.append(self._newCard(fact, template, 1, flush=False))
|
cards.append(self._newCard(note, template, 1, flush=False))
|
||||||
return cards
|
return cards
|
||||||
|
|
||||||
def _newCard(self, fact, template, due, flush=True):
|
def _newCard(self, note, template, due, flush=True):
|
||||||
"Create a new card."
|
"Create a new card."
|
||||||
card = anki.cards.Card(self)
|
card = anki.cards.Card(self)
|
||||||
card.fid = fact.id
|
card.nid = note.id
|
||||||
card.ord = template['ord']
|
card.ord = template['ord']
|
||||||
card.gid = template['gid'] or fact.gid
|
card.gid = template['gid'] or note.gid
|
||||||
card.due = due
|
card.due = due
|
||||||
if flush:
|
if flush:
|
||||||
card.flush()
|
card.flush()
|
||||||
|
@ -350,42 +350,42 @@ insert into cards values (?,?,?,?,?,-1,0,0,?,0,0,0,0,0,0,0,"")""",
|
||||||
if not ids:
|
if not ids:
|
||||||
return
|
return
|
||||||
sids = ids2str(ids)
|
sids = ids2str(ids)
|
||||||
fids = self.db.list("select fid from cards where id in "+sids)
|
nids = self.db.list("select nid from cards where id in "+sids)
|
||||||
# remove cards
|
# remove cards
|
||||||
self._logRem(ids, REM_CARD)
|
self._logRem(ids, REM_CARD)
|
||||||
self.db.execute("delete from cards where id in "+sids)
|
self.db.execute("delete from cards where id in "+sids)
|
||||||
self.db.execute("delete from revlog where cid in "+sids)
|
self.db.execute("delete from revlog where cid in "+sids)
|
||||||
# then facts
|
# then notes
|
||||||
fids = self.db.list("""
|
nids = self.db.list("""
|
||||||
select id from facts where id in %s and id not in (select fid from cards)""" %
|
select id from notes where id in %s and id not in (select nid from cards)""" %
|
||||||
ids2str(fids))
|
ids2str(nids))
|
||||||
self._remFacts(fids)
|
self._remNotes(nids)
|
||||||
|
|
||||||
# Field checksums and sorting fields
|
# Field checksums and sorting fields
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _fieldData(self, sfids):
|
def _fieldData(self, snids):
|
||||||
return self.db.execute(
|
return self.db.execute(
|
||||||
"select id, mid, flds from facts where id in "+sfids)
|
"select id, mid, flds from notes where id in "+snids)
|
||||||
|
|
||||||
def updateFieldCache(self, fids, csum=True):
|
def updateFieldCache(self, nids, csum=True):
|
||||||
"Update field checksums and sort cache, after find&replace, etc."
|
"Update field checksums and sort cache, after find&replace, etc."
|
||||||
sfids = ids2str(fids)
|
snids = ids2str(nids)
|
||||||
r = []
|
r = []
|
||||||
r2 = []
|
r2 = []
|
||||||
for (fid, mid, flds) in self._fieldData(sfids):
|
for (nid, mid, flds) in self._fieldData(snids):
|
||||||
fields = splitFields(flds)
|
fields = splitFields(flds)
|
||||||
model = self.models.get(mid)
|
model = self.models.get(mid)
|
||||||
if csum:
|
if csum:
|
||||||
for f in model['flds']:
|
for f in model['flds']:
|
||||||
if f['uniq'] and fields[f['ord']]:
|
if f['uniq'] and fields[f['ord']]:
|
||||||
r.append((fid, mid, fieldChecksum(fields[f['ord']])))
|
r.append((nid, mid, fieldChecksum(fields[f['ord']])))
|
||||||
r2.append((stripHTML(fields[self.models.sortIdx(model)]), fid))
|
r2.append((stripHTML(fields[self.models.sortIdx(model)]), nid))
|
||||||
if csum:
|
if csum:
|
||||||
self.db.execute("delete from fsums where fid in "+sfids)
|
self.db.execute("delete from nsums where nid in "+snids)
|
||||||
self.db.executemany("insert into fsums values (?,?,?)", r)
|
self.db.executemany("insert into nsums values (?,?,?)", r)
|
||||||
# rely on calling code to bump usn+mod
|
# rely on calling code to bump usn+mod
|
||||||
self.db.executemany("update facts set sfld = ? where id = ?", r2)
|
self.db.executemany("update notes set sfld = ? where id = ?", r2)
|
||||||
|
|
||||||
# Q/A generation
|
# Q/A generation
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
@ -394,7 +394,7 @@ select id from facts where id in %s and id not in (select fid from cards)""" %
|
||||||
# gather metadata
|
# gather metadata
|
||||||
if type == "card":
|
if type == "card":
|
||||||
where = "and c.id in " + ids2str(ids)
|
where = "and c.id in " + ids2str(ids)
|
||||||
elif type == "fact":
|
elif type == "note":
|
||||||
where = "and f.id in " + ids2str(ids)
|
where = "and f.id in " + ids2str(ids)
|
||||||
elif type == "model":
|
elif type == "model":
|
||||||
where = "and m.id in " + ids2str(ids)
|
where = "and m.id in " + ids2str(ids)
|
||||||
|
@ -407,7 +407,7 @@ select id from facts where id in %s and id not in (select fid from cards)""" %
|
||||||
|
|
||||||
def _renderQA(self, data):
|
def _renderQA(self, data):
|
||||||
"Returns hash of id, question, answer."
|
"Returns hash of id, question, answer."
|
||||||
# data is [cid, fid, mid, gid, ord, tags, flds]
|
# data is [cid, nid, mid, gid, ord, tags, flds]
|
||||||
# unpack fields and create dict
|
# unpack fields and create dict
|
||||||
flist = splitFields(data[6])
|
flist = splitFields(data[6])
|
||||||
fields = {}
|
fields = {}
|
||||||
|
@ -437,11 +437,11 @@ select id from facts where id in %s and id not in (select fid from cards)""" %
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def _qaData(self, where=""):
|
def _qaData(self, where=""):
|
||||||
"Return [cid, fid, mid, gid, ord, tags, flds] db query"
|
"Return [cid, nid, mid, gid, ord, tags, flds] db query"
|
||||||
return self.db.execute("""
|
return self.db.execute("""
|
||||||
select c.id, f.id, f.mid, c.gid, c.ord, f.tags, f.flds
|
select c.id, f.id, f.mid, c.gid, c.ord, f.tags, f.flds
|
||||||
from cards c, facts f
|
from cards c, notes f
|
||||||
where c.fid == f.id
|
where c.nid == f.id
|
||||||
%s""" % where)
|
%s""" % where)
|
||||||
|
|
||||||
# Finding cards
|
# Finding cards
|
||||||
|
@ -450,8 +450,8 @@ where c.fid == f.id
|
||||||
def findCards(self, query, full=False):
|
def findCards(self, query, full=False):
|
||||||
return anki.find.Finder(self).findCards(query, full)
|
return anki.find.Finder(self).findCards(query, full)
|
||||||
|
|
||||||
def findReplace(self, fids, src, dst, regex=None, field=None, fold=True):
|
def findReplace(self, nids, src, dst, regex=None, field=None, fold=True):
|
||||||
return anki.find.findReplace(self, fids, src, dst, regex, field, fold)
|
return anki.find.findReplace(self, nids, src, dst, regex, field, fold)
|
||||||
|
|
||||||
def findDuplicates(self, fmids):
|
def findDuplicates(self, fmids):
|
||||||
return anki.find.findDuplicates(self, fmids)
|
return anki.find.findDuplicates(self, fmids)
|
||||||
|
@ -570,15 +570,15 @@ where c.fid == f.id
|
||||||
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
|
# delete any notes with missing cards
|
||||||
ids = self.db.list("""
|
ids = self.db.list("""
|
||||||
select id from facts where id not in (select distinct fid from cards)""")
|
select id from notes where id not in (select distinct nid from cards)""")
|
||||||
self._remFacts(ids)
|
self._remNotes(ids)
|
||||||
# tags
|
# tags
|
||||||
self.tags.registerFacts()
|
self.tags.registerNotes()
|
||||||
# field cache
|
# field cache
|
||||||
for m in self.models.all():
|
for m in self.models.all():
|
||||||
self.updateFieldCache(self.models.fids(m))
|
self.updateFieldCache(self.models.nids(m))
|
||||||
# and finally, optimize
|
# and finally, optimize
|
||||||
self.optimize()
|
self.optimize()
|
||||||
newSize = os.stat(self.path)[stat.ST_SIZE]
|
newSize = os.stat(self.path)[stat.ST_SIZE]
|
||||||
|
|
|
@ -104,23 +104,23 @@ class AnkiExporter(Exporter):
|
||||||
cards = self.deck.db.all("""
|
cards = self.deck.db.all("""
|
||||||
select id, modified from cards
|
select id, modified from cards
|
||||||
where id in %s""" % cStrIds)
|
where id in %s""" % cStrIds)
|
||||||
facts = self.deck.db.all("""
|
notes = self.deck.db.all("""
|
||||||
select facts.id, facts.modified from cards, facts where
|
select notes.id, notes.modified from cards, notes where
|
||||||
facts.id = cards.factId and
|
notes.id = cards.noteId and
|
||||||
cards.id in %s""" % cStrIds)
|
cards.id in %s""" % cStrIds)
|
||||||
models = self.deck.db.all("""
|
models = self.deck.db.all("""
|
||||||
select models.id, models.modified from models, facts where
|
select models.id, models.modified from models, notes where
|
||||||
facts.modelId = models.id and
|
notes.modelId = models.id and
|
||||||
facts.id in %s""" % ids2str([f[0] for f in facts]))
|
notes.id in %s""" % ids2str([f[0] for f in notes]))
|
||||||
media = self.deck.db.all("""
|
media = self.deck.db.all("""
|
||||||
select id, modified from media""")
|
select id, modified from media""")
|
||||||
return {
|
return {
|
||||||
# cards
|
# cards
|
||||||
"cards": cards,
|
"cards": cards,
|
||||||
"delcards": [],
|
"delcards": [],
|
||||||
# facts
|
# notes
|
||||||
"facts": facts,
|
"notes": notes,
|
||||||
"delfacts": [],
|
"delnotes": [],
|
||||||
# models
|
# models
|
||||||
"models": models,
|
"models": models,
|
||||||
"delmodels": [],
|
"delmodels": [],
|
||||||
|
@ -147,8 +147,8 @@ where cards.id in %s
|
||||||
order by cards.created""" % strids)
|
order by cards.created""" % strids)
|
||||||
if self.includeTags:
|
if self.includeTags:
|
||||||
self.cardTags = dict(self.deck.db.all("""
|
self.cardTags = dict(self.deck.db.all("""
|
||||||
select cards.id, facts.tags from cards, facts
|
select cards.id, notes.tags from cards, notes
|
||||||
where cards.factId = facts.id
|
where cards.noteId = notes.id
|
||||||
and cards.id in %s
|
and cards.id in %s
|
||||||
order by cards.created""" % strids))
|
order by cards.created""" % strids))
|
||||||
out = u"\n".join(["%s\t%s%s" % (
|
out = u"\n".join(["%s\t%s%s" % (
|
||||||
|
@ -166,7 +166,7 @@ order by cards.created""" % strids))
|
||||||
return "\t" + ", ".join(parseTags(self.cardTags[id]))
|
return "\t" + ", ".join(parseTags(self.cardTags[id]))
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
class TextFactExporter(Exporter):
|
class TextNoteExporter(Exporter):
|
||||||
|
|
||||||
key = _("Text files (*.txt)")
|
key = _("Text files (*.txt)")
|
||||||
ext = ".txt"
|
ext = ".txt"
|
||||||
|
@ -177,20 +177,20 @@ class TextFactExporter(Exporter):
|
||||||
|
|
||||||
def doExport(self, file):
|
def doExport(self, file):
|
||||||
cardIds = self.cardIds()
|
cardIds = self.cardIds()
|
||||||
facts = self.deck.db.all("""
|
notes = self.deck.db.all("""
|
||||||
select factId, value, facts.created from facts, fields
|
select noteId, value, notes.created from notes, fields
|
||||||
where
|
where
|
||||||
facts.id in
|
notes.id in
|
||||||
(select distinct factId from cards
|
(select distinct noteId from cards
|
||||||
where cards.id in %s)
|
where cards.id in %s)
|
||||||
and facts.id = fields.factId
|
and notes.id = fields.noteId
|
||||||
order by factId, ordinal""" % ids2str(cardIds))
|
order by noteId, ordinal""" % ids2str(cardIds))
|
||||||
txt = ""
|
txt = ""
|
||||||
if self.includeTags:
|
if self.includeTags:
|
||||||
self.factTags = dict(self.deck.db.all(
|
self.noteTags = dict(self.deck.db.all(
|
||||||
"select id, tags from facts where id in %s" %
|
"select id, tags from notes where id in %s" %
|
||||||
ids2str([fact[0] for fact in facts])))
|
ids2str([note[0] for note in notes])))
|
||||||
groups = itertools.groupby(facts, itemgetter(0))
|
groups = itertools.groupby(notes, itemgetter(0))
|
||||||
groups = [[x for x in y[1]] for y in groups]
|
groups = [[x for x in y[1]] for y in groups]
|
||||||
groups = [(group[0][2],
|
groups = [(group[0][2],
|
||||||
"\t".join([self.escapeText(x[1]) for x in group]) +
|
"\t".join([self.escapeText(x[1]) for x in group]) +
|
||||||
|
@ -205,7 +205,7 @@ order by factId, ordinal""" % ids2str(cardIds))
|
||||||
|
|
||||||
def tags(self, id):
|
def tags(self, id):
|
||||||
if self.includeTags:
|
if self.includeTags:
|
||||||
return "\t" + self.factTags[id]
|
return "\t" + self.noteTags[id]
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
# Export modules
|
# Export modules
|
||||||
|
@ -215,4 +215,4 @@ def exporters():
|
||||||
return (
|
return (
|
||||||
(_("Anki Deck (*.anki)"), AnkiExporter),
|
(_("Anki Deck (*.anki)"), AnkiExporter),
|
||||||
(_("Cards in tab-separated text file (*.txt)"), TextCardExporter),
|
(_("Cards in tab-separated text file (*.txt)"), TextCardExporter),
|
||||||
(_("Facts in tab-separated text file (*.txt)"), TextFactExporter))
|
(_("Notes in tab-separated text file (*.txt)"), TextNoteExporter))
|
||||||
|
|
86
anki/find.py
86
anki/find.py
|
@ -9,7 +9,7 @@ from anki.utils import ids2str, splitFields, joinFields, stripHTML, intTime
|
||||||
SEARCH_TAG = 0
|
SEARCH_TAG = 0
|
||||||
SEARCH_TYPE = 1
|
SEARCH_TYPE = 1
|
||||||
SEARCH_PHRASE = 2
|
SEARCH_PHRASE = 2
|
||||||
SEARCH_FID = 3
|
SEARCH_NID = 3
|
||||||
SEARCH_TEMPLATE = 4
|
SEARCH_TEMPLATE = 4
|
||||||
SEARCH_FIELD = 5
|
SEARCH_FIELD = 5
|
||||||
SEARCH_MODEL = 6
|
SEARCH_MODEL = 6
|
||||||
|
@ -54,9 +54,9 @@ class Finder(object):
|
||||||
|
|
||||||
def _whereClause(self):
|
def _whereClause(self):
|
||||||
x = []
|
x = []
|
||||||
if self.lims['fact']:
|
if self.lims['note']:
|
||||||
x.append("fid in (select id from facts where %s)" % " and ".join(
|
x.append("nid in (select id from notes where %s)" % " and ".join(
|
||||||
self.lims['fact']))
|
self.lims['note']))
|
||||||
if self.lims['card']:
|
if self.lims['card']:
|
||||||
x.extend(self.lims['card'])
|
x.extend(self.lims['card'])
|
||||||
q = " and ".join(x)
|
q = " and ".join(x)
|
||||||
|
@ -68,17 +68,17 @@ class Finder(object):
|
||||||
type = self.deck.conf['sortType']
|
type = self.deck.conf['sortType']
|
||||||
if not type:
|
if not type:
|
||||||
return "select id from cards c where " + lim
|
return "select id from cards c where " + lim
|
||||||
elif type.startswith("fact"):
|
elif type.startswith("note"):
|
||||||
if type == "factCrt":
|
if type == "noteCrt":
|
||||||
sort = "f.id, c.ord"
|
sort = "f.id, c.ord"
|
||||||
elif type == "factMod":
|
elif type == "noteMod":
|
||||||
sort = "f.mod, c.ord"
|
sort = "f.mod, c.ord"
|
||||||
elif type == "factFld":
|
elif type == "noteFld":
|
||||||
sort = "f.sfld collate nocase, c.ord"
|
sort = "f.sfld collate nocase, c.ord"
|
||||||
else:
|
else:
|
||||||
raise Exception()
|
raise Exception()
|
||||||
return """
|
return """
|
||||||
select c.id from cards c, facts f where %s and c.fid=f.id
|
select c.id from cards c, notes f where %s and c.nid=f.id
|
||||||
order by %s""" % (lim, sort)
|
order by %s""" % (lim, sort)
|
||||||
elif type.startswith("card"):
|
elif type.startswith("card"):
|
||||||
if type == "cardMod":
|
if type == "cardMod":
|
||||||
|
@ -101,9 +101,9 @@ order by %s""" % (lim, sort)
|
||||||
raise Exception()
|
raise Exception()
|
||||||
|
|
||||||
def _findLimits(self):
|
def _findLimits(self):
|
||||||
"Generate a list of fact/card limits for the query."
|
"Generate a list of note/card limits for the query."
|
||||||
self.lims = {
|
self.lims = {
|
||||||
'fact': [],
|
'note': [],
|
||||||
'card': [],
|
'card': [],
|
||||||
'args': {},
|
'args': {},
|
||||||
'valid': True
|
'valid': True
|
||||||
|
@ -113,8 +113,8 @@ order by %s""" % (lim, sort)
|
||||||
self._findTag(token, isNeg, c)
|
self._findTag(token, isNeg, c)
|
||||||
elif type == SEARCH_TYPE:
|
elif type == SEARCH_TYPE:
|
||||||
self._findCardState(token, isNeg)
|
self._findCardState(token, isNeg)
|
||||||
elif type == SEARCH_FID:
|
elif type == SEARCH_NID:
|
||||||
self._findFids(token)
|
self._findNids(token)
|
||||||
elif type == SEARCH_TEMPLATE:
|
elif type == SEARCH_TEMPLATE:
|
||||||
self._findTemplate(token, isNeg)
|
self._findTemplate(token, isNeg)
|
||||||
elif type == SEARCH_FIELD:
|
elif type == SEARCH_FIELD:
|
||||||
|
@ -128,7 +128,7 @@ order by %s""" % (lim, sort)
|
||||||
|
|
||||||
def _findTag(self, val, neg, c):
|
def _findTag(self, val, neg, c):
|
||||||
if val == "none":
|
if val == "none":
|
||||||
self.lims['fact'].append("select id from facts where tags = ''")
|
self.lims['note'].append("select id from notes where tags = ''")
|
||||||
return
|
return
|
||||||
extra = "not" if neg else ""
|
extra = "not" if neg else ""
|
||||||
val = val.replace("*", "%")
|
val = val.replace("*", "%")
|
||||||
|
@ -137,7 +137,7 @@ order by %s""" % (lim, sort)
|
||||||
if not val.endswith("%"):
|
if not val.endswith("%"):
|
||||||
val += " %"
|
val += " %"
|
||||||
self.lims['args']["_tag_%d" % c] = val
|
self.lims['args']["_tag_%d" % c] = val
|
||||||
self.lims['fact'].append(
|
self.lims['note'].append(
|
||||||
"tags %s like :_tag_%d""" % (extra, c))
|
"tags %s like :_tag_%d""" % (extra, c))
|
||||||
|
|
||||||
def _findCardState(self, val, neg):
|
def _findCardState(self, val, neg):
|
||||||
|
@ -168,20 +168,20 @@ order by %s""" % (lim, sort)
|
||||||
extra = "not" if neg else ""
|
extra = "not" if neg else ""
|
||||||
if not self.full:
|
if not self.full:
|
||||||
self.lims['args']["_text_%d"%c] = "%"+val+"%"
|
self.lims['args']["_text_%d"%c] = "%"+val+"%"
|
||||||
self.lims['fact'].append("flds %s like :_text_%d escape '\\'" % (
|
self.lims['note'].append("flds %s like :_text_%d escape '\\'" % (
|
||||||
extra, c))
|
extra, c))
|
||||||
else:
|
else:
|
||||||
# in the future we may want to apply this at the end to speed up
|
# in the future we may want to apply this at the end to speed up
|
||||||
# the case where there are other limits
|
# the case where there are other limits
|
||||||
fids = []
|
nids = []
|
||||||
for fid, flds in self.deck.db.execute(
|
for nid, flds in self.deck.db.execute(
|
||||||
"select id, flds from facts"):
|
"select id, flds from notes"):
|
||||||
if val in stripHTML(flds):
|
if val in stripHTML(flds):
|
||||||
fids.append(fid)
|
nids.append(nid)
|
||||||
self.lims['fact'].append("id in " + ids2str(fids))
|
self.lims['note'].append("id in " + ids2str(nids))
|
||||||
|
|
||||||
def _findFids(self, val):
|
def _findNids(self, val):
|
||||||
self.lims['fact'].append("id in (%s)" % val)
|
self.lims['note'].append("id in (%s)" % val)
|
||||||
|
|
||||||
def _findModel(self, val, isNeg):
|
def _findModel(self, val, isNeg):
|
||||||
extra = "not" if isNeg else ""
|
extra = "not" if isNeg else ""
|
||||||
|
@ -189,7 +189,7 @@ order by %s""" % (lim, sort)
|
||||||
for m in self.deck.models.all():
|
for m in self.deck.models.all():
|
||||||
if m['name'].lower() == val:
|
if m['name'].lower() == val:
|
||||||
ids.append(m['id'])
|
ids.append(m['id'])
|
||||||
self.lims['fact'].append("mid %s in %s" % (extra, ids2str(ids)))
|
self.lims['note'].append("mid %s in %s" % (extra, ids2str(ids)))
|
||||||
|
|
||||||
def _findGroup(self, val, isNeg):
|
def _findGroup(self, val, isNeg):
|
||||||
extra = "!" if isNeg else ""
|
extra = "!" if isNeg else ""
|
||||||
|
@ -214,7 +214,7 @@ order by %s""" % (lim, sort)
|
||||||
# template name?
|
# template name?
|
||||||
elif t['name'].lower() == val.lower():
|
elif t['name'].lower() == val.lower():
|
||||||
lims.append((
|
lims.append((
|
||||||
"(fid in (select id from facts where mid = %s) "
|
"(nid in (select id from notes where mid = %s) "
|
||||||
"and ord %s %d)") % (m['id'], comp, t['ord']))
|
"and ord %s %d)") % (m['id'], comp, t['ord']))
|
||||||
found = True
|
found = True
|
||||||
if lims:
|
if lims:
|
||||||
|
@ -236,11 +236,11 @@ order by %s""" % (lim, sort)
|
||||||
# nothing has that field
|
# nothing has that field
|
||||||
self.lims['valid'] = False
|
self.lims['valid'] = False
|
||||||
return
|
return
|
||||||
# gather fids
|
# gather nids
|
||||||
regex = value.replace("%", ".*")
|
regex = value.replace("%", ".*")
|
||||||
fids = []
|
nids = []
|
||||||
for (id,mid,flds) in self.deck.db.execute("""
|
for (id,mid,flds) in self.deck.db.execute("""
|
||||||
select id, mid, flds from facts
|
select id, mid, flds from notes
|
||||||
where mid in %s and flds like ? escape '\\'""" % (
|
where mid in %s and flds like ? escape '\\'""" % (
|
||||||
ids2str(mods.keys())),
|
ids2str(mods.keys())),
|
||||||
"%" if self.full else value):
|
"%" if self.full else value):
|
||||||
|
@ -250,9 +250,9 @@ where mid in %s and flds like ? escape '\\'""" % (
|
||||||
if self.full:
|
if self.full:
|
||||||
strg = stripHTML(strg)
|
strg = stripHTML(strg)
|
||||||
if re.search(regex, strg):
|
if re.search(regex, strg):
|
||||||
fids.append(id)
|
nids.append(id)
|
||||||
extra = "not" if isNeg else ""
|
extra = "not" if isNeg else ""
|
||||||
self.lims['fact'].append("id %s in %s" % (extra, ids2str(fids)))
|
self.lims['note'].append("id %s in %s" % (extra, ids2str(nids)))
|
||||||
|
|
||||||
# Most of this function was written by Marcus
|
# Most of this function was written by Marcus
|
||||||
def _parseQuery(self):
|
def _parseQuery(self):
|
||||||
|
@ -332,7 +332,7 @@ where mid in %s and flds like ? escape '\\'""" % (
|
||||||
elif token['value'].startswith("group:"):
|
elif token['value'].startswith("group:"):
|
||||||
token['value'] = token['value'][6:].lower()
|
token['value'] = token['value'][6:].lower()
|
||||||
type = SEARCH_GROUP
|
type = SEARCH_GROUP
|
||||||
elif token['value'].startswith("fid:") and len(token['value']) > 4:
|
elif token['value'].startswith("nid:") and len(token['value']) > 4:
|
||||||
dec = token['value'][4:]
|
dec = token['value'][4:]
|
||||||
try:
|
try:
|
||||||
int(dec)
|
int(dec)
|
||||||
|
@ -344,7 +344,7 @@ where mid in %s and flds like ? escape '\\'""" % (
|
||||||
token['value'] = token['value'][4:]
|
token['value'] = token['value'][4:]
|
||||||
except:
|
except:
|
||||||
token['value'] = "0"
|
token['value'] = "0"
|
||||||
type = SEARCH_FID
|
type = SEARCH_NID
|
||||||
elif token['value'].startswith("card:"):
|
elif token['value'].startswith("card:"):
|
||||||
token['value'] = token['value'][5:]
|
token['value'] = token['value'][5:]
|
||||||
type = SEARCH_TEMPLATE
|
type = SEARCH_TEMPLATE
|
||||||
|
@ -370,8 +370,8 @@ where mid in %s and flds like ? escape '\\'""" % (
|
||||||
# Find and replace
|
# Find and replace
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def findReplace(deck, fids, src, dst, regex=False, field=None, fold=True):
|
def findReplace(deck, nids, src, dst, regex=False, field=None, fold=True):
|
||||||
"Find and replace fields in a fact."
|
"Find and replace fields in a note."
|
||||||
mmap = {}
|
mmap = {}
|
||||||
if field:
|
if field:
|
||||||
for m in deck.models.all():
|
for m in deck.models.all():
|
||||||
|
@ -389,8 +389,8 @@ def findReplace(deck, fids, src, dst, regex=False, field=None, fold=True):
|
||||||
def repl(str):
|
def repl(str):
|
||||||
return re.sub(regex, dst, str)
|
return re.sub(regex, dst, str)
|
||||||
d = []
|
d = []
|
||||||
for fid, mid, flds in deck.db.execute(
|
for nid, mid, flds in deck.db.execute(
|
||||||
"select id, mid, flds from facts where id in "+ids2str(fids)):
|
"select id, mid, flds from notes where id in "+ids2str(nids)):
|
||||||
origFlds = flds
|
origFlds = flds
|
||||||
# does it match?
|
# does it match?
|
||||||
sflds = splitFields(flds)
|
sflds = splitFields(flds)
|
||||||
|
@ -402,12 +402,12 @@ def findReplace(deck, fids, src, dst, regex=False, field=None, fold=True):
|
||||||
sflds[c] = repl(sflds[c])
|
sflds[c] = repl(sflds[c])
|
||||||
flds = joinFields(sflds)
|
flds = joinFields(sflds)
|
||||||
if flds != origFlds:
|
if flds != origFlds:
|
||||||
d.append(dict(fid=fid,flds=flds,u=deck.usn(),m=intTime()))
|
d.append(dict(nid=nid,flds=flds,u=deck.usn(),m=intTime()))
|
||||||
if not d:
|
if not d:
|
||||||
return 0
|
return 0
|
||||||
# replace
|
# replace
|
||||||
deck.db.executemany("update facts set flds=:flds,mod=:m,usn=:u where id=:fid", d)
|
deck.db.executemany("update notes set flds=:flds,mod=:m,usn=:u where id=:nid", d)
|
||||||
deck.updateFieldCache(fids)
|
deck.updateFieldCache(nids)
|
||||||
return len(d)
|
return len(d)
|
||||||
|
|
||||||
# Find duplicates
|
# Find duplicates
|
||||||
|
@ -415,14 +415,14 @@ def findReplace(deck, fids, src, dst, regex=False, field=None, fold=True):
|
||||||
|
|
||||||
def findDuplicates(deck, fmids):
|
def findDuplicates(deck, fmids):
|
||||||
data = deck.db.all(
|
data = deck.db.all(
|
||||||
"select fid, value from fdata where fmid in %s" %
|
"select nid, value from fdata where fmid in %s" %
|
||||||
ids2str(fmids))
|
ids2str(fmids))
|
||||||
vals = {}
|
vals = {}
|
||||||
for (fid, val) in data:
|
for (nid, val) in data:
|
||||||
if not val.strip():
|
if not val.strip():
|
||||||
continue
|
continue
|
||||||
if val not in vals:
|
if val not in vals:
|
||||||
vals[val] = [fid]
|
vals[val] = [nid]
|
||||||
else:
|
else:
|
||||||
vals[val].append(fid)
|
vals[val].append(nid)
|
||||||
return [(k,v) for (k,v) in vals.items() if len(v) > 1]
|
return [(k,v) for (k,v) in vals.items() if len(v) > 1]
|
||||||
|
|
|
@ -15,7 +15,7 @@ from anki.lang import _
|
||||||
# appropriate
|
# appropriate
|
||||||
|
|
||||||
# notes:
|
# notes:
|
||||||
# - it's difficult to enforce valid gids for models/facts/cards, as we
|
# - it's difficult to enforce valid gids for models/notes/cards, as we
|
||||||
# may update the gid locally only to have it overwritten by a more recent
|
# may update the gid locally only to have it overwritten by a more recent
|
||||||
# change from somewhere else. to avoid this, we allow invalid gid
|
# change from somewhere else. to avoid this, we allow invalid gid
|
||||||
# references, and treat any invalid gids as the default group.
|
# references, and treat any invalid gids as the default group.
|
||||||
|
@ -268,7 +268,7 @@ class GroupManager(object):
|
||||||
|
|
||||||
def sendHome(self, cids):
|
def sendHome(self, cids):
|
||||||
self.deck.db.execute("""
|
self.deck.db.execute("""
|
||||||
update cards set gid=(select gid from facts f where f.id=fid),
|
update cards set gid=(select gid from notes f where f.id=nid),
|
||||||
usn=?,mod=? where id in %s""" % ids2str(cids),
|
usn=?,mod=? where id in %s""" % ids2str(cids),
|
||||||
self.deck.usn(), intTime(), gid)
|
self.deck.usn(), intTime(), gid)
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,9 @@ from anki.importing.base import Importer
|
||||||
# shared decks, and import from a packaged deck.
|
# shared decks, and import from a packaged deck.
|
||||||
#
|
#
|
||||||
# We can't rely on internal ids, so we:
|
# We can't rely on internal ids, so we:
|
||||||
# - compare facts by guid
|
# - compare notes by guid
|
||||||
# - compare models by schema signature
|
# - compare models by schema signature
|
||||||
# - compare cards by fact guid + ordinal
|
# - compare cards by note guid + ordinal
|
||||||
# - compare groups by name
|
# - compare groups by name
|
||||||
#
|
#
|
||||||
|
|
||||||
|
@ -44,53 +44,53 @@ class Anki2Importer(Importer):
|
||||||
self.dst.groups.select(id)
|
self.dst.groups.select(id)
|
||||||
self._prepareTS()
|
self._prepareTS()
|
||||||
self._prepareModels()
|
self._prepareModels()
|
||||||
self._importFacts()
|
self._importNotes()
|
||||||
self._importCards()
|
self._importCards()
|
||||||
self._importMedia()
|
self._importMedia()
|
||||||
self._postImport()
|
self._postImport()
|
||||||
self.dst.db.execute("vacuum")
|
self.dst.db.execute("vacuum")
|
||||||
self.dst.db.execute("analyze")
|
self.dst.db.execute("analyze")
|
||||||
|
|
||||||
# Facts
|
# Notes
|
||||||
######################################################################
|
######################################################################
|
||||||
# - should note new for wizard
|
# - should note new for wizard
|
||||||
|
|
||||||
def _importFacts(self):
|
def _importNotes(self):
|
||||||
# build guid -> (id,mod,mid) hash
|
# build guid -> (id,mod,mid) hash
|
||||||
self._facts = {}
|
self._notes = {}
|
||||||
for id, guid, mod, mid in self.dst.db.execute(
|
for id, guid, mod, mid in self.dst.db.execute(
|
||||||
"select id, guid, mod, mid from facts"):
|
"select id, guid, mod, mid from notes"):
|
||||||
self._facts[guid] = (id, mod, mid)
|
self._notes[guid] = (id, mod, mid)
|
||||||
# iterate over source deck
|
# iterate over source deck
|
||||||
add = []
|
add = []
|
||||||
dirty = []
|
dirty = []
|
||||||
for fact in self.src.db.execute(
|
for note in self.src.db.execute(
|
||||||
"select * from facts"):
|
"select * from notes"):
|
||||||
# turn the db result into a mutable list
|
# turn the db result into a mutable list
|
||||||
fact = list(fact)
|
note = list(note)
|
||||||
guid, mid = fact[1:3]
|
guid, mid = note[1:3]
|
||||||
# missing from local deck?
|
# missing from local deck?
|
||||||
if guid not in self._facts:
|
if guid not in self._notes:
|
||||||
# get corresponding local model
|
# get corresponding local model
|
||||||
lmid = self._mid(mid)
|
lmid = self._mid(mid)
|
||||||
# rewrite internal ids, models, etc
|
# rewrite internal ids, models, etc
|
||||||
fact[0] = self.ts()
|
note[0] = self.ts()
|
||||||
fact[2] = lmid
|
note[2] = lmid
|
||||||
fact[3] = self._gid(fact[3])
|
note[3] = self._gid(note[3])
|
||||||
fact[4] = intTime()
|
note[4] = intTime()
|
||||||
fact[5] = -1 # usn
|
note[5] = -1 # usn
|
||||||
add.append(fact)
|
add.append(note)
|
||||||
dirty.append(fact[0])
|
dirty.append(note[0])
|
||||||
# note we have the added fact
|
# note we have the added note
|
||||||
self._facts[guid] = (fact[0], fact[4], fact[2])
|
self._notes[guid] = (note[0], note[4], note[2])
|
||||||
else:
|
else:
|
||||||
continue #raise Exception("merging facts nyi")
|
continue #raise Exception("merging notes nyi")
|
||||||
# add to deck
|
# add to deck
|
||||||
self.dst.db.executemany(
|
self.dst.db.executemany(
|
||||||
"insert or replace into facts values (?,?,?,?,?,?,?,?,?,?,?)",
|
"insert or replace into notes values (?,?,?,?,?,?,?,?,?,?,?)",
|
||||||
add)
|
add)
|
||||||
self.dst.updateFieldCache(dirty)
|
self.dst.updateFieldCache(dirty)
|
||||||
self.dst.tags.registerFacts(dirty)
|
self.dst.tags.registerNotes(dirty)
|
||||||
|
|
||||||
# Models
|
# Models
|
||||||
######################################################################
|
######################################################################
|
||||||
|
@ -168,24 +168,24 @@ class Anki2Importer(Importer):
|
||||||
# build map of (guid, ord) -> cid
|
# build map of (guid, ord) -> cid
|
||||||
self._cards = {}
|
self._cards = {}
|
||||||
for guid, ord, cid in self.dst.db.execute(
|
for guid, ord, cid in self.dst.db.execute(
|
||||||
"select f.guid, c.ord, c.id from cards c, facts f "
|
"select f.guid, c.ord, c.id from cards c, notes f "
|
||||||
"where c.fid = f.id"):
|
"where c.nid = f.id"):
|
||||||
self._cards[(guid, ord)] = cid
|
self._cards[(guid, ord)] = cid
|
||||||
# loop through src
|
# loop through src
|
||||||
cards = []
|
cards = []
|
||||||
revlog = []
|
revlog = []
|
||||||
print "fixme: need to check schema issues in card import"
|
print "fixme: need to check schema issues in card import"
|
||||||
for card in self.src.db.execute(
|
for card in self.src.db.execute(
|
||||||
"select f.guid, f.mid, c.* from cards c, facts f "
|
"select f.guid, f.mid, c.* from cards c, notes f "
|
||||||
"where c.fid = f.id"):
|
"where c.nid = f.id"):
|
||||||
guid = card[0]
|
guid = card[0]
|
||||||
# does the card's fact exist in dst deck?
|
# does the card's note exist in dst deck?
|
||||||
if guid not in self._facts:
|
if guid not in self._notes:
|
||||||
continue
|
continue
|
||||||
dfid = self._facts[guid]
|
dnid = self._notes[guid]
|
||||||
# does the fact share the same schema?
|
# does the note share the same schema?
|
||||||
# shash = self._srcModels[card[1]]
|
# shash = self._srcModels[card[1]]
|
||||||
# mid = self._facts[guid][2]
|
# mid = self._notes[guid][2]
|
||||||
# if shash != self._dstModels[mid]:
|
# if shash != self._dstModels[mid]:
|
||||||
# continue
|
# continue
|
||||||
# does the card already exist in the dst deck?
|
# does the card already exist in the dst deck?
|
||||||
|
@ -193,12 +193,12 @@ class Anki2Importer(Importer):
|
||||||
if (guid, ord) in self._cards:
|
if (guid, ord) in self._cards:
|
||||||
# fixme: in future, could update if newer mod time
|
# fixme: in future, could update if newer mod time
|
||||||
continue
|
continue
|
||||||
# doesn't exist. strip off fact info, and save src id for later
|
# doesn't exist. strip off note info, and save src id for later
|
||||||
card = list(card[2:])
|
card = list(card[2:])
|
||||||
scid = card[0]
|
scid = card[0]
|
||||||
# update cid, fid, etc
|
# update cid, nid, etc
|
||||||
card[0] = self.ts()
|
card[0] = self.ts()
|
||||||
card[1] = self._facts[guid][0]
|
card[1] = self._notes[guid][0]
|
||||||
card[2] = self._gid(card[2])
|
card[2] = self._gid(card[2])
|
||||||
card[4] = intTime()
|
card[4] = intTime()
|
||||||
cards.append(card)
|
cards.append(card)
|
||||||
|
|
|
@ -57,15 +57,15 @@ class CardImporter(Importer):
|
||||||
cards = self.foreignCards()
|
cards = self.foreignCards()
|
||||||
# grab data from db
|
# grab data from db
|
||||||
fields = self.deck.db.all("""
|
fields = self.deck.db.all("""
|
||||||
select factId, value from fields where fieldModelId = :id
|
select noteId, value from fields where fieldModelId = :id
|
||||||
and value != ''""",
|
and value != ''""",
|
||||||
id=self.updateKey[1])
|
id=self.updateKey[1])
|
||||||
# hash it
|
# hash it
|
||||||
vhash = {}
|
vhash = {}
|
||||||
fids = []
|
nids = []
|
||||||
for (fid, val) in fields:
|
for (nid, val) in fields:
|
||||||
fids.append(fid)
|
nids.append(nid)
|
||||||
vhash[val] = fid
|
vhash[val] = nid
|
||||||
# prepare tags
|
# prepare tags
|
||||||
tagsIdx = None
|
tagsIdx = None
|
||||||
try:
|
try:
|
||||||
|
@ -82,7 +82,7 @@ and value != ''""",
|
||||||
if v in vhash:
|
if v in vhash:
|
||||||
# ignore empty keys
|
# ignore empty keys
|
||||||
if v:
|
if v:
|
||||||
# fid, card
|
# nid, card
|
||||||
upcards.append((vhash[v], c))
|
upcards.append((vhash[v], c))
|
||||||
else:
|
else:
|
||||||
newcards.append(c)
|
newcards.append(c)
|
||||||
|
@ -96,28 +96,28 @@ and value != ''""",
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# not mapped
|
# not mapped
|
||||||
continue
|
continue
|
||||||
data = [{'fid': fid,
|
data = [{'nid': nid,
|
||||||
'fmid': fm.id,
|
'fmid': fm.id,
|
||||||
'v': c.fields[index],
|
'v': c.fields[index],
|
||||||
'chk': self.maybeChecksum(c.fields[index], fm.unique)}
|
'chk': self.maybeChecksum(c.fields[index], fm.unique)}
|
||||||
for (fid, c) in upcards]
|
for (nid, c) in upcards]
|
||||||
self.deck.db.execute("""
|
self.deck.db.execute("""
|
||||||
update fields set value = :v, chksum = :chk where factId = :fid
|
update fields set value = :v, chksum = :chk where noteId = :nid
|
||||||
and fieldModelId = :fmid""", data)
|
and fieldModelId = :fmid""", data)
|
||||||
# update tags
|
# update tags
|
||||||
if tagsIdx is not None:
|
if tagsIdx is not None:
|
||||||
data = [{'fid': fid,
|
data = [{'nid': nid,
|
||||||
't': c.fields[tagsIdx]}
|
't': c.fields[tagsIdx]}
|
||||||
for (fid, c) in upcards]
|
for (nid, c) in upcards]
|
||||||
self.deck.db.execute(
|
self.deck.db.execute(
|
||||||
"update facts set tags = :t where id = :fid",
|
"update notes set tags = :t where id = :nid",
|
||||||
data)
|
data)
|
||||||
# rebuild caches
|
# rebuild caches
|
||||||
cids = self.deck.db.column0(
|
cids = self.deck.db.column0(
|
||||||
"select id from cards where factId in %s" %
|
"select id from cards where noteId in %s" %
|
||||||
ids2str(fids))
|
ids2str(nids))
|
||||||
self.deck.updateCardTags(cids)
|
self.deck.updateCardTags(cids)
|
||||||
self.deck.updateCardsFromFactIds(fids)
|
self.deck.updateCardsFromNoteIds(nids)
|
||||||
self.total = len(cards)
|
self.total = len(cards)
|
||||||
self.deck.setModified()
|
self.deck.setModified()
|
||||||
|
|
||||||
|
@ -166,7 +166,7 @@ and fieldModelId = :fmid""", data)
|
||||||
model = property(getModel, setModel)
|
model = property(getModel, setModel)
|
||||||
|
|
||||||
def importCards(self, cards):
|
def importCards(self, cards):
|
||||||
"Convert each card into a fact, apply attributes and add to deck."
|
"Convert each card into a note, apply attributes and add to deck."
|
||||||
# ensure all unique and required fields are mapped
|
# ensure all unique and required fields are mapped
|
||||||
for fm in self.model.fieldModels:
|
for fm in self.model.fieldModels:
|
||||||
if fm.required or fm.unique:
|
if fm.required or fm.unique:
|
||||||
|
@ -187,7 +187,7 @@ and fieldModelId = :fmid""", data)
|
||||||
return cards
|
return cards
|
||||||
|
|
||||||
def addCards(self, cards):
|
def addCards(self, cards):
|
||||||
"Add facts in bulk from foreign cards."
|
"Add notes in bulk from foreign cards."
|
||||||
# map tags field to attr
|
# map tags field to attr
|
||||||
try:
|
try:
|
||||||
idx = self.mapping.index(0)
|
idx = self.mapping.index(0)
|
||||||
|
@ -195,31 +195,31 @@ and fieldModelId = :fmid""", data)
|
||||||
c.tags += " " + c.fields[idx]
|
c.tags += " " + c.fields[idx]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
# add facts
|
# add notes
|
||||||
factIds = [genID() for n in range(len(cards))]
|
noteIds = [genID() for n in range(len(cards))]
|
||||||
factCreated = {}
|
noteCreated = {}
|
||||||
def fudgeCreated(d, tmp=[]):
|
def fudgeCreated(d, tmp=[]):
|
||||||
if not tmp:
|
if not tmp:
|
||||||
tmp.append(time.time())
|
tmp.append(time.time())
|
||||||
else:
|
else:
|
||||||
tmp[0] += 0.0001
|
tmp[0] += 0.0001
|
||||||
d['created'] = tmp[0]
|
d['created'] = tmp[0]
|
||||||
factCreated[d['id']] = d['created']
|
noteCreated[d['id']] = d['created']
|
||||||
return d
|
return d
|
||||||
self.deck.db.execute(factsTable.insert(),
|
self.deck.db.execute(notesTable.insert(),
|
||||||
[fudgeCreated({'modelId': self.model.id,
|
[fudgeCreated({'modelId': self.model.id,
|
||||||
'tags': canonifyTags(self.tagsToAdd + " " + cards[n].tags),
|
'tags': canonifyTags(self.tagsToAdd + " " + cards[n].tags),
|
||||||
'id': factIds[n]}) for n in range(len(cards))])
|
'id': noteIds[n]}) for n in range(len(cards))])
|
||||||
self.deck.db.execute("""
|
self.deck.db.execute("""
|
||||||
delete from factsDeleted
|
delete from notesDeleted
|
||||||
where factId in (%s)""" % ",".join([str(s) for s in factIds]))
|
where noteId in (%s)""" % ",".join([str(s) for s in noteIds]))
|
||||||
# add all the fields
|
# add all the fields
|
||||||
for fm in self.model.fieldModels:
|
for fm in self.model.fieldModels:
|
||||||
try:
|
try:
|
||||||
index = self.mapping.index(fm)
|
index = self.mapping.index(fm)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
index = None
|
index = None
|
||||||
data = [{'factId': factIds[m],
|
data = [{'noteId': noteIds[m],
|
||||||
'fieldModelId': fm.id,
|
'fieldModelId': fm.id,
|
||||||
'ordinal': fm.ordinal,
|
'ordinal': fm.ordinal,
|
||||||
'id': genID(),
|
'id': genID(),
|
||||||
|
@ -239,8 +239,8 @@ where factId in (%s)""" % ",".join([str(s) for s in factIds]))
|
||||||
active += 1
|
active += 1
|
||||||
data = [self.addMeta({
|
data = [self.addMeta({
|
||||||
'id': genID(),
|
'id': genID(),
|
||||||
'factId': factIds[m],
|
'noteId': noteIds[m],
|
||||||
'factCreated': factCreated[factIds[m]],
|
'noteCreated': noteCreated[noteIds[m]],
|
||||||
'cardModelId': cm.id,
|
'cardModelId': cm.id,
|
||||||
'ordinal': cm.ordinal,
|
'ordinal': cm.ordinal,
|
||||||
'question': u"",
|
'question': u"",
|
||||||
|
@ -248,14 +248,14 @@ where factId in (%s)""" % ",".join([str(s) for s in factIds]))
|
||||||
},cards[m]) for m in range(len(cards))]
|
},cards[m]) for m in range(len(cards))]
|
||||||
self.deck.db.execute(cardsTable.insert(),
|
self.deck.db.execute(cardsTable.insert(),
|
||||||
data)
|
data)
|
||||||
self.deck.updateCardsFromFactIds(factIds)
|
self.deck.updateCardsFromNoteIds(noteIds)
|
||||||
self.total = len(factIds)
|
self.total = len(noteIds)
|
||||||
|
|
||||||
def addMeta(self, data, card):
|
def addMeta(self, data, card):
|
||||||
"Add any scheduling metadata to cards"
|
"Add any scheduling metadata to cards"
|
||||||
if 'fields' in card.__dict__:
|
if 'fields' in card.__dict__:
|
||||||
del card.fields
|
del card.fields
|
||||||
t = data['factCreated'] + data['ordinal'] * 0.00001
|
t = data['noteCreated'] + data['ordinal'] * 0.00001
|
||||||
data['created'] = t
|
data['created'] = t
|
||||||
data['modified'] = t
|
data['modified'] = t
|
||||||
data['due'] = t
|
data['due'] = t
|
||||||
|
@ -281,7 +281,7 @@ where factId in (%s)""" % ",".join([str(s) for s in factIds]))
|
||||||
for n in range(len(self.mapping)):
|
for n in range(len(self.mapping)):
|
||||||
if self.mapping[n] and self.mapping[n].required:
|
if self.mapping[n] and self.mapping[n].required:
|
||||||
if fieldNum <= n or not card.fields[n].strip():
|
if fieldNum <= n or not card.fields[n].strip():
|
||||||
self.log.append("Fact is missing field '%s': %s" %
|
self.log.append("Note is missing field '%s': %s" %
|
||||||
(self.mapping[n].name,
|
(self.mapping[n].name,
|
||||||
", ".join(card.fields)))
|
", ".join(card.fields)))
|
||||||
return False
|
return False
|
||||||
|
@ -307,7 +307,7 @@ where factId in (%s)""" % ",".join([str(s) for s in factIds]))
|
||||||
if self.mapping[n] and self.mapping[n].unique:
|
if self.mapping[n] and self.mapping[n].unique:
|
||||||
if card.fields[n] in self.uniqueCache[self.mapping[n].id]:
|
if card.fields[n] in self.uniqueCache[self.mapping[n].id]:
|
||||||
if not self.tagDuplicates:
|
if not self.tagDuplicates:
|
||||||
self.log.append("Fact has duplicate '%s': %s" %
|
self.log.append("Note has duplicate '%s': %s" %
|
||||||
(self.mapping[n].name,
|
(self.mapping[n].name,
|
||||||
", ".join(card.fields)))
|
", ".join(card.fields)))
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -161,7 +161,7 @@ If the same name exists, compare checksums."""
|
||||||
def allMedia(self):
|
def allMedia(self):
|
||||||
"Return a set of all referenced filenames."
|
"Return a set of all referenced filenames."
|
||||||
files = set()
|
files = set()
|
||||||
for mid, flds in self.deck.db.execute("select mid, flds from facts"):
|
for mid, flds in self.deck.db.execute("select mid, flds from notes"):
|
||||||
for f in self.filesInStr(mid, flds):
|
for f in self.filesInStr(mid, flds):
|
||||||
files.add(f)
|
files.add(f)
|
||||||
return files
|
return files
|
||||||
|
|
|
@ -75,7 +75,7 @@ class ModelManager(object):
|
||||||
m['usn'] = self.deck.usn()
|
m['usn'] = self.deck.usn()
|
||||||
self._updateRequired(m)
|
self._updateRequired(m)
|
||||||
if gencards:
|
if gencards:
|
||||||
self.deck.genCards(self.fids(m))
|
self.deck.genCards(self.nids(m))
|
||||||
self.changed = True
|
self.changed = True
|
||||||
|
|
||||||
def flush(self):
|
def flush(self):
|
||||||
|
@ -129,12 +129,12 @@ class ModelManager(object):
|
||||||
return self._add(m)
|
return self._add(m)
|
||||||
|
|
||||||
def rem(self, m):
|
def rem(self, m):
|
||||||
"Delete model, and all its cards/facts."
|
"Delete model, and all its cards/notes."
|
||||||
self.deck.modSchema()
|
self.deck.modSchema()
|
||||||
current = self.current()['id'] == m['id']
|
current = self.current()['id'] == m['id']
|
||||||
# delete facts/cards
|
# delete notes/cards
|
||||||
self.deck.remCards(self.deck.db.list("""
|
self.deck.remCards(self.deck.db.list("""
|
||||||
select id from cards where fid in (select id from facts where mid = ?)""",
|
select id from cards where nid in (select id from notes where mid = ?)""",
|
||||||
m['id']))
|
m['id']))
|
||||||
# then the model
|
# then the model
|
||||||
del self.models[str(m['id'])]
|
del self.models[str(m['id'])]
|
||||||
|
@ -168,15 +168,15 @@ select id from cards where fid in (select id from facts where mid = ?)""",
|
||||||
# Tools
|
# Tools
|
||||||
##################################################
|
##################################################
|
||||||
|
|
||||||
def fids(self, m):
|
def nids(self, m):
|
||||||
"Fact ids for M."
|
"Note ids for M."
|
||||||
return self.deck.db.list(
|
return self.deck.db.list(
|
||||||
"select id from facts where mid = ?", m['id'])
|
"select id from notes where mid = ?", m['id'])
|
||||||
|
|
||||||
def useCount(self, m):
|
def useCount(self, m):
|
||||||
"Number of fact using M."
|
"Number of note using M."
|
||||||
return self.deck.db.scalar(
|
return self.deck.db.scalar(
|
||||||
"select count() from facts where mid = ?", m['id'])
|
"select count() from notes where mid = ?", m['id'])
|
||||||
|
|
||||||
def randomNew(self):
|
def randomNew(self):
|
||||||
return self.current()['newOrder'] == NEW_CARDS_RANDOM
|
return self.current()['newOrder'] == NEW_CARDS_RANDOM
|
||||||
|
@ -212,7 +212,7 @@ select id from cards where fid in (select id from facts where mid = ?)""",
|
||||||
assert idx >= 0 and idx < len(m['flds'])
|
assert idx >= 0 and idx < len(m['flds'])
|
||||||
self.deck.modSchema()
|
self.deck.modSchema()
|
||||||
m['sortf'] = idx
|
m['sortf'] = idx
|
||||||
self.deck.updateFieldCache(self.fids(m), csum=False)
|
self.deck.updateFieldCache(self.nids(m), csum=False)
|
||||||
self.save(m)
|
self.save(m)
|
||||||
|
|
||||||
def addField(self, m, field):
|
def addField(self, m, field):
|
||||||
|
@ -234,7 +234,7 @@ select id from cards where fid in (select id from facts where mid = ?)""",
|
||||||
self._transformFields(m, delete)
|
self._transformFields(m, delete)
|
||||||
if idx == self.sortIdx(m):
|
if idx == self.sortIdx(m):
|
||||||
# need to rebuild
|
# need to rebuild
|
||||||
self.deck.updateFieldCache(self.fids(m), csum=False)
|
self.deck.updateFieldCache(self.nids(m), csum=False)
|
||||||
# saves
|
# saves
|
||||||
self.renameField(m, field, None)
|
self.renameField(m, field, None)
|
||||||
|
|
||||||
|
@ -276,11 +276,11 @@ select id from cards where fid in (select id from facts where mid = ?)""",
|
||||||
self.deck.modSchema()
|
self.deck.modSchema()
|
||||||
r = []
|
r = []
|
||||||
for (id, flds) in self.deck.db.execute(
|
for (id, flds) in self.deck.db.execute(
|
||||||
"select id, flds from facts where mid = ?", m['id']):
|
"select id, flds from notes where mid = ?", m['id']):
|
||||||
r.append((joinFields(fn(splitFields(flds))),
|
r.append((joinFields(fn(splitFields(flds))),
|
||||||
intTime(), self.deck.usn(), id))
|
intTime(), self.deck.usn(), id))
|
||||||
self.deck.db.executemany(
|
self.deck.db.executemany(
|
||||||
"update facts set flds=?,mod=?,usn=? where id = ?", r)
|
"update notes set flds=?,mod=?,usn=? where id = ?", r)
|
||||||
|
|
||||||
# Templates
|
# Templates
|
||||||
##################################################
|
##################################################
|
||||||
|
@ -298,18 +298,18 @@ select id from cards where fid in (select id from facts where mid = ?)""",
|
||||||
self.save(m)
|
self.save(m)
|
||||||
|
|
||||||
def remTemplate(self, m, template):
|
def remTemplate(self, m, template):
|
||||||
"False if removing template would leave orphan facts."
|
"False if removing template would leave orphan notes."
|
||||||
# find cards using this template
|
# find cards using this template
|
||||||
ord = m['tmpls'].index(template)
|
ord = m['tmpls'].index(template)
|
||||||
cids = self.deck.db.list("""
|
cids = self.deck.db.list("""
|
||||||
select c.id from cards c, facts f where c.fid=f.id and mid = ? and ord = ?""",
|
select c.id from cards c, notes f where c.nid=f.id and mid = ? and ord = ?""",
|
||||||
m['id'], ord)
|
m['id'], ord)
|
||||||
# all facts with this template must have at least two cards, or we
|
# all notes with this template must have at least two cards, or we
|
||||||
# could end up creating orphaned facts
|
# could end up creating orphaned notes
|
||||||
if self.deck.db.scalar("""
|
if self.deck.db.scalar("""
|
||||||
select fid, count() from cards where
|
select nid, count() from cards where
|
||||||
fid in (select fid from cards where id in %s)
|
nid in (select nid from cards where id in %s)
|
||||||
group by fid
|
group by nid
|
||||||
having count() < 2
|
having count() < 2
|
||||||
limit 1""" % ids2str(cids)):
|
limit 1""" % ids2str(cids)):
|
||||||
return False
|
return False
|
||||||
|
@ -319,7 +319,7 @@ limit 1""" % ids2str(cids)):
|
||||||
# shift ordinals
|
# shift ordinals
|
||||||
self.deck.db.execute("""
|
self.deck.db.execute("""
|
||||||
update cards set ord = ord - 1, usn = ?, mod = ?
|
update cards set ord = ord - 1, usn = ?, mod = ?
|
||||||
where fid in (select id from facts where mid = ?) and ord > ?""",
|
where nid in (select id from notes where mid = ?) and ord > ?""",
|
||||||
self.deck.usn(), intTime(), m['id'], ord)
|
self.deck.usn(), intTime(), m['id'], ord)
|
||||||
m['tmpls'].remove(template)
|
m['tmpls'].remove(template)
|
||||||
self._updateTemplOrds(m)
|
self._updateTemplOrds(m)
|
||||||
|
@ -345,8 +345,8 @@ update cards set ord = ord - 1, usn = ?, mod = ?
|
||||||
# apply
|
# apply
|
||||||
self.save(m)
|
self.save(m)
|
||||||
self.deck.db.execute("""
|
self.deck.db.execute("""
|
||||||
update cards set ord = (case %s end),usn=?,mod=? where fid in (
|
update cards set ord = (case %s end),usn=?,mod=? where nid in (
|
||||||
select id from facts where mid = ?)""" % " ".join(map),
|
select id from notes where mid = ?)""" % " ".join(map),
|
||||||
self.deck.usn(), intTime(), m['id'])
|
self.deck.usn(), intTime(), m['id'])
|
||||||
|
|
||||||
# Model changing
|
# Model changing
|
||||||
|
@ -354,19 +354,19 @@ select id from facts where mid = ?)""" % " ".join(map),
|
||||||
# - maps are ord->ord, and there should not be duplicate targets
|
# - maps are ord->ord, and there should not be duplicate targets
|
||||||
# - newModel should be self if model is not changing
|
# - newModel should be self if model is not changing
|
||||||
|
|
||||||
def change(self, m, fids, newModel, fmap, cmap):
|
def change(self, m, nids, newModel, fmap, cmap):
|
||||||
self.deck.modSchema()
|
self.deck.modSchema()
|
||||||
assert newModel['id'] == m['id'] or (fmap and cmap)
|
assert newModel['id'] == m['id'] or (fmap and cmap)
|
||||||
if fmap:
|
if fmap:
|
||||||
self._changeFacts(fids, newModel, fmap)
|
self._changeNotes(nids, newModel, fmap)
|
||||||
if cmap:
|
if cmap:
|
||||||
self._changeCards(fids, newModel, cmap)
|
self._changeCards(nids, newModel, cmap)
|
||||||
|
|
||||||
def _changeFacts(self, fids, newModel, map):
|
def _changeNotes(self, nids, newModel, map):
|
||||||
d = []
|
d = []
|
||||||
nfields = len(newModel['flds'])
|
nfields = len(newModel['flds'])
|
||||||
for (fid, flds) in self.deck.db.execute(
|
for (nid, flds) in self.deck.db.execute(
|
||||||
"select id, flds from facts where id in "+ids2str(fids)):
|
"select id, flds from notes where id in "+ids2str(nids)):
|
||||||
newflds = {}
|
newflds = {}
|
||||||
flds = splitFields(flds)
|
flds = splitFields(flds)
|
||||||
for old, new in map.items():
|
for old, new in map.items():
|
||||||
|
@ -375,17 +375,17 @@ select id from facts where mid = ?)""" % " ".join(map),
|
||||||
for c in range(nfields):
|
for c in range(nfields):
|
||||||
flds.append(newflds.get(c, ""))
|
flds.append(newflds.get(c, ""))
|
||||||
flds = joinFields(flds)
|
flds = joinFields(flds)
|
||||||
d.append(dict(fid=fid, flds=flds, mid=newModel['id'],
|
d.append(dict(nid=nid, flds=flds, mid=newModel['id'],
|
||||||
m=intTime(),u=self.deck.usn()))
|
m=intTime(),u=self.deck.usn()))
|
||||||
self.deck.db.executemany(
|
self.deck.db.executemany(
|
||||||
"update facts set flds=:flds,mid=:mid,mod=:m,usn=:u where id = :fid", d)
|
"update notes set flds=:flds,mid=:mid,mod=:m,usn=:u where id = :nid", d)
|
||||||
self.deck.updateFieldCache(fids)
|
self.deck.updateFieldCache(nids)
|
||||||
|
|
||||||
def _changeCards(self, fids, newModel, map):
|
def _changeCards(self, nids, newModel, map):
|
||||||
d = []
|
d = []
|
||||||
deleted = []
|
deleted = []
|
||||||
for (cid, ord) in self.deck.db.execute(
|
for (cid, ord) in self.deck.db.execute(
|
||||||
"select id, ord from cards where fid in "+ids2str(fids)):
|
"select id, ord from cards where nid in "+ids2str(nids)):
|
||||||
if map[ord] is not None:
|
if map[ord] is not None:
|
||||||
d.append(dict(
|
d.append(dict(
|
||||||
cid=cid,new=map[ord],u=self.deck.usn(),m=intTime()))
|
cid=cid,new=map[ord],u=self.deck.usn(),m=intTime()))
|
||||||
|
|
|
@ -7,7 +7,7 @@ from anki.errors import AnkiError
|
||||||
from anki.utils import fieldChecksum, intTime, \
|
from anki.utils import fieldChecksum, intTime, \
|
||||||
joinFields, splitFields, ids2str, stripHTML, timestampID, guid64
|
joinFields, splitFields, ids2str, stripHTML, timestampID, guid64
|
||||||
|
|
||||||
class Fact(object):
|
class Note(object):
|
||||||
|
|
||||||
def __init__(self, deck, model=None, id=None):
|
def __init__(self, deck, model=None, id=None):
|
||||||
assert not (model and id)
|
assert not (model and id)
|
||||||
|
@ -16,7 +16,7 @@ class Fact(object):
|
||||||
self.id = id
|
self.id = id
|
||||||
self.load()
|
self.load()
|
||||||
else:
|
else:
|
||||||
self.id = timestampID(deck.db, "facts")
|
self.id = timestampID(deck.db, "notes")
|
||||||
self.guid = guid64()
|
self.guid = guid64()
|
||||||
self._model = model
|
self._model = model
|
||||||
self.gid = model['gid']
|
self.gid = model['gid']
|
||||||
|
@ -38,7 +38,7 @@ class Fact(object):
|
||||||
self.flags,
|
self.flags,
|
||||||
self.data) = self.deck.db.first("""
|
self.data) = self.deck.db.first("""
|
||||||
select guid, mid, gid, mod, usn, tags, flds, flags, data
|
select guid, mid, gid, mod, usn, tags, flds, flags, data
|
||||||
from facts where id = ?""", self.id)
|
from notes where id = ?""", self.id)
|
||||||
self.fields = splitFields(self.fields)
|
self.fields = splitFields(self.fields)
|
||||||
self.tags = self.deck.tags.split(self.tags)
|
self.tags = self.deck.tags.split(self.tags)
|
||||||
self._model = self.deck.models.get(self.mid)
|
self._model = self.deck.models.get(self.mid)
|
||||||
|
@ -52,7 +52,7 @@ from facts where id = ?""", self.id)
|
||||||
sfld = stripHTML(self.fields[self.deck.models.sortIdx(self._model)])
|
sfld = stripHTML(self.fields[self.deck.models.sortIdx(self._model)])
|
||||||
tags = self.stringTags()
|
tags = self.stringTags()
|
||||||
res = self.deck.db.execute("""
|
res = self.deck.db.execute("""
|
||||||
insert or replace into facts values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
insert or replace into notes values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||||
self.id, self.guid, self.mid, self.gid,
|
self.id, self.guid, self.mid, self.gid,
|
||||||
self.mod, self.usn, tags,
|
self.mod, self.usn, tags,
|
||||||
self.joinedFields(), sfld, self.flags, self.data)
|
self.joinedFields(), sfld, self.flags, self.data)
|
||||||
|
@ -66,7 +66,7 @@ insert or replace into facts values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||||
return joinFields(self.fields)
|
return joinFields(self.fields)
|
||||||
|
|
||||||
def updateFieldChecksums(self):
|
def updateFieldChecksums(self):
|
||||||
self.deck.db.execute("delete from fsums where fid = ?", self.id)
|
self.deck.db.execute("delete from nsums where nid = ?", self.id)
|
||||||
d = []
|
d = []
|
||||||
for (ord, conf) in self._fmap.values():
|
for (ord, conf) in self._fmap.values():
|
||||||
if not conf['uniq']:
|
if not conf['uniq']:
|
||||||
|
@ -75,11 +75,11 @@ insert or replace into facts values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||||
if not val:
|
if not val:
|
||||||
continue
|
continue
|
||||||
d.append((self.id, self.mid, fieldChecksum(val)))
|
d.append((self.id, self.mid, fieldChecksum(val)))
|
||||||
self.deck.db.executemany("insert into fsums values (?, ?, ?)", d)
|
self.deck.db.executemany("insert into nsums values (?, ?, ?)", d)
|
||||||
|
|
||||||
def cards(self):
|
def cards(self):
|
||||||
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 ord", self.id)]
|
"select id from cards where nid = ? order by ord", self.id)]
|
||||||
|
|
||||||
def model(self):
|
def model(self):
|
||||||
return self._model
|
return self._model
|
||||||
|
@ -151,18 +151,18 @@ insert or replace into facts values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||||
return True
|
return True
|
||||||
csum = fieldChecksum(val)
|
csum = fieldChecksum(val)
|
||||||
if self.id:
|
if self.id:
|
||||||
lim = "and fid != :fid"
|
lim = "and nid != :nid"
|
||||||
else:
|
else:
|
||||||
lim = ""
|
lim = ""
|
||||||
fids = self.deck.db.list(
|
nids = self.deck.db.list(
|
||||||
"select fid from fsums where csum = ? and fid != ? and mid = ?",
|
"select nid from nsums where csum = ? and nid != ? and mid = ?",
|
||||||
csum, self.id or 0, self.mid)
|
csum, self.id or 0, self.mid)
|
||||||
if not fids:
|
if not nids:
|
||||||
return True
|
return True
|
||||||
# grab facts with the same checksums, and see if they're actually
|
# grab notes with the same checksums, and see if they're actually
|
||||||
# duplicates
|
# duplicates
|
||||||
for flds in self.deck.db.list("select flds from facts where id in "+
|
for flds in self.deck.db.list("select flds from notes where id in "+
|
||||||
ids2str(fids)):
|
ids2str(nids)):
|
||||||
fields = splitFields(flds)
|
fields = splitFields(flds)
|
||||||
if fields[ord] == val:
|
if fields[ord] == val:
|
||||||
return False
|
return False
|
||||||
|
@ -185,19 +185,19 @@ insert or replace into facts values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||||
d.append((ord, None))
|
d.append((ord, None))
|
||||||
return [x[1] for x in sorted(d)]
|
return [x[1] for x in sorted(d)]
|
||||||
|
|
||||||
# Flushing cloze facts
|
# Flushing cloze notes
|
||||||
##################################################
|
##################################################
|
||||||
|
|
||||||
def _clozePreFlush(self):
|
def _clozePreFlush(self):
|
||||||
self.newlyAdded = not self.deck.db.scalar(
|
self.newlyAdded = not self.deck.db.scalar(
|
||||||
"select 1 from cards where fid = ?", self.id)
|
"select 1 from cards where nid = ?", self.id)
|
||||||
tmpls = self.deck.findTemplates(self)
|
tmpls = self.deck.findTemplates(self)
|
||||||
ok = []
|
ok = []
|
||||||
for t in tmpls:
|
for t in tmpls:
|
||||||
ok.append(t['ord'])
|
ok.append(t['ord'])
|
||||||
# check if there are cards referencing a deleted cloze
|
# check if there are cards referencing a deleted cloze
|
||||||
if self.deck.db.scalar(
|
if self.deck.db.scalar(
|
||||||
"select 1 from cards where fid = ? and ord not in %s" %
|
"select 1 from cards where nid = ? and ord not in %s" %
|
||||||
ids2str(ok), self.id):
|
ids2str(ok), self.id):
|
||||||
# there are; abort, as the UI should have handled this
|
# there are; abort, as the UI should have handled this
|
||||||
raise Exception("UI should have deleted cloze")
|
raise Exception("UI should have deleted cloze")
|
|
@ -269,7 +269,7 @@ select id, due from cards where gid = ? and queue = 0 limit ?""", gid, lim)
|
||||||
self._newQueue.insert(0, self._newQueue.pop())
|
self._newQueue.insert(0, self._newQueue.pop())
|
||||||
n -= 1
|
n -= 1
|
||||||
if not n:
|
if not n:
|
||||||
# we only have one fact in the queue; stop rotating
|
# we only have one note in the queue; stop rotating
|
||||||
break
|
break
|
||||||
self.newCount -= 1
|
self.newCount -= 1
|
||||||
return id
|
return id
|
||||||
|
@ -605,8 +605,8 @@ gid in %s and queue = 2 and due <= :lim %s limit %d""" % (
|
||||||
conf = self._cardConf(card)['rev']
|
conf = self._cardConf(card)['rev']
|
||||||
# find sibling positions
|
# find sibling positions
|
||||||
dues = self.deck.db.list(
|
dues = self.deck.db.list(
|
||||||
"select due from cards where fid = ? and queue = 2"
|
"select due from cards where nid = ? and queue = 2"
|
||||||
" and id != ?", card.fid, card.id)
|
" and id != ?", card.nid, card.id)
|
||||||
if not dues or idealDue not in dues:
|
if not dues or idealDue not in dues:
|
||||||
return idealIvl
|
return idealIvl
|
||||||
else:
|
else:
|
||||||
|
@ -637,7 +637,7 @@ gid in %s and queue = 2 and due <= :lim %s limit %d""" % (
|
||||||
if (lf >= card.lapses and
|
if (lf >= card.lapses and
|
||||||
(card.lapses-lf) % (max(lf/2, 1)) == 0):
|
(card.lapses-lf) % (max(lf/2, 1)) == 0):
|
||||||
# add a leech tag
|
# add a leech tag
|
||||||
f = card.fact()
|
f = card.note()
|
||||||
f.addTag("leech")
|
f.addTag("leech")
|
||||||
f.flush()
|
f.flush()
|
||||||
# handle
|
# handle
|
||||||
|
@ -782,12 +782,12 @@ your short-term review workload will become."""))
|
||||||
"where queue = -1 and id in "+ ids2str(ids),
|
"where queue = -1 and id in "+ ids2str(ids),
|
||||||
intTime(), self.deck.usn())
|
intTime(), self.deck.usn())
|
||||||
|
|
||||||
def buryFact(self, fid):
|
def buryNote(self, nid):
|
||||||
"Bury all cards for fact until next session."
|
"Bury all cards for note until next session."
|
||||||
self.deck.setDirty()
|
self.deck.setDirty()
|
||||||
self.removeFailed(
|
self.removeFailed(
|
||||||
self.deck.db.list("select id from cards where fid = ?", fid))
|
self.deck.db.list("select id from cards where nid = ?", nid))
|
||||||
self.deck.db.execute("update cards set queue = -2 where fid = ?", fid)
|
self.deck.db.execute("update cards set queue = -2 where nid = ?", nid)
|
||||||
|
|
||||||
# Resetting
|
# Resetting
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
@ -818,18 +818,18 @@ your short-term review workload will become."""))
|
||||||
def sortCards(self, cids, start=1, step=1, shuffle=False, shift=False):
|
def sortCards(self, cids, start=1, step=1, shuffle=False, shift=False):
|
||||||
scids = ids2str(cids)
|
scids = ids2str(cids)
|
||||||
now = intTime()
|
now = intTime()
|
||||||
fids = self.deck.db.list(
|
nids = self.deck.db.list(
|
||||||
("select distinct fid from cards where type = 0 and id in %s "
|
("select distinct nid from cards where type = 0 and id in %s "
|
||||||
"order by fid") % scids)
|
"order by nid") % scids)
|
||||||
if not fids:
|
if not nids:
|
||||||
# no new cards
|
# no new cards
|
||||||
return
|
return
|
||||||
# determine fid ordering
|
# determine nid ordering
|
||||||
due = {}
|
due = {}
|
||||||
if shuffle:
|
if shuffle:
|
||||||
random.shuffle(fids)
|
random.shuffle(nids)
|
||||||
for c, fid in enumerate(fids):
|
for c, nid in enumerate(nids):
|
||||||
due[fid] = start+c*step
|
due[nid] = start+c*step
|
||||||
high = start+c*step
|
high = start+c*step
|
||||||
# shift?
|
# shift?
|
||||||
if shift:
|
if shift:
|
||||||
|
@ -844,9 +844,9 @@ update cards set mod=?, usn=?, due=due+? where id not in %s
|
||||||
and due >= ?""" % scids, now, self.deck.usn(), shiftby, low)
|
and due >= ?""" % scids, now, self.deck.usn(), shiftby, low)
|
||||||
# reorder cards
|
# reorder cards
|
||||||
d = []
|
d = []
|
||||||
for id, fid in self.deck.db.execute(
|
for id, nid in self.deck.db.execute(
|
||||||
"select id, fid from cards where type = 0 and id in "+scids):
|
"select id, nid from cards where type = 0 and id in "+scids):
|
||||||
d.append(dict(now=now, due=due[fid], usn=self.deck.usn(), cid=id))
|
d.append(dict(now=now, due=due[nid], usn=self.deck.usn(), cid=id))
|
||||||
self.deck.db.executemany(
|
self.deck.db.executemany(
|
||||||
"update cards set due=:due,mod=:now,usn=:usn where id = :cid""", d)
|
"update cards set due=:due,mod=:now,usn=:usn where id = :cid""", d)
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ class CardStats(object):
|
||||||
self.addLine(_("Model"), c.model()['name'])
|
self.addLine(_("Model"), c.model()['name'])
|
||||||
self.addLine(_("Template"), c.template()['name'])
|
self.addLine(_("Template"), c.template()['name'])
|
||||||
self.addLine(_("Current Group"), self.deck.groups.name(c.gid))
|
self.addLine(_("Current Group"), self.deck.groups.name(c.gid))
|
||||||
self.addLine(_("Home Group"), self.deck.groups.name(c.fact().gid))
|
self.addLine(_("Home Group"), self.deck.groups.name(c.note().gid))
|
||||||
self.txt += "</table>"
|
self.txt += "</table>"
|
||||||
return self.txt
|
return self.txt
|
||||||
|
|
||||||
|
@ -538,10 +538,10 @@ group by hour having count() > 30 order by hour""" % lim,
|
||||||
# text data
|
# text data
|
||||||
i = []
|
i = []
|
||||||
(c, f) = self.deck.db.first("""
|
(c, f) = self.deck.db.first("""
|
||||||
select count(id), count(distinct fid) from cards
|
select count(id), count(distinct nid) from cards
|
||||||
where gid in %s """ % self._limit())
|
where gid in %s """ % self._limit())
|
||||||
self._line(i, _("Total cards"), c)
|
self._line(i, _("Total cards"), c)
|
||||||
self._line(i, _("Total facts"), f)
|
self._line(i, _("Total notes"), f)
|
||||||
(low, avg, high) = self._factors()
|
(low, avg, high) = self._factors()
|
||||||
if low:
|
if low:
|
||||||
self._line(i, _("Lowest ease factor"), "%d%%" % low)
|
self._line(i, _("Lowest ease factor"), "%d%%" % low)
|
||||||
|
|
|
@ -80,7 +80,7 @@ create table if not exists deck (
|
||||||
tags text not null
|
tags text not null
|
||||||
);
|
);
|
||||||
|
|
||||||
create table if not exists facts (
|
create table if not exists notes (
|
||||||
id integer primary key,
|
id integer primary key,
|
||||||
guid integer not null,
|
guid integer not null,
|
||||||
mid integer not null,
|
mid integer not null,
|
||||||
|
@ -94,14 +94,14 @@ create table if not exists facts (
|
||||||
data text not null
|
data text not null
|
||||||
);
|
);
|
||||||
|
|
||||||
create table if not exists fsums (
|
create table if not exists nsums (
|
||||||
fid integer not null,
|
nid integer not null,
|
||||||
mid integer not null,
|
mid integer not null,
|
||||||
csum integer not null
|
csum integer not null
|
||||||
);
|
);
|
||||||
create table if not exists cards (
|
create table if not exists cards (
|
||||||
id integer primary key,
|
id integer primary key,
|
||||||
fid integer not null,
|
nid integer not null,
|
||||||
gid integer not null,
|
gid integer not null,
|
||||||
ord integer not null,
|
ord integer not null,
|
||||||
mod integer not null,
|
mod integer not null,
|
||||||
|
@ -168,16 +168,16 @@ def _updateIndices(db):
|
||||||
"Add indices to the DB."
|
"Add indices to the DB."
|
||||||
db.executescript("""
|
db.executescript("""
|
||||||
-- syncing
|
-- syncing
|
||||||
create index if not exists ix_facts_usn on facts (usn);
|
create index if not exists ix_notes_usn on notes (usn);
|
||||||
create index if not exists ix_cards_usn on cards (usn);
|
create index if not exists ix_cards_usn on cards (usn);
|
||||||
create index if not exists ix_revlog_usn on revlog (usn);
|
create index if not exists ix_revlog_usn on revlog (usn);
|
||||||
-- card spacing, etc
|
-- card spacing, etc
|
||||||
create index if not exists ix_cards_fid on cards (fid);
|
create index if not exists ix_cards_nid on cards (nid);
|
||||||
-- scheduling and group limiting
|
-- scheduling and group limiting
|
||||||
create index if not exists ix_cards_sched on cards (gid, queue, due);
|
create index if not exists ix_cards_sched on cards (gid, queue, due);
|
||||||
-- revlog by card
|
-- revlog by card
|
||||||
create index if not exists ix_revlog_cid on revlog (cid);
|
create index if not exists ix_revlog_cid on revlog (cid);
|
||||||
-- field uniqueness check
|
-- field uniqueness check
|
||||||
create index if not exists ix_fsums_fid on fsums (fid);
|
create index if not exists ix_nsums_nid on nsums (nid);
|
||||||
create index if not exists ix_fsums_csum on fsums (csum);
|
create index if not exists ix_nsums_csum on nsums (csum);
|
||||||
""")
|
""")
|
||||||
|
|
40
anki/sync.py
40
anki/sync.py
|
@ -128,10 +128,10 @@ class Syncer(object):
|
||||||
# some basic checks to ensure the sync went ok. this is slow, so will
|
# some basic checks to ensure the sync went ok. this is slow, so will
|
||||||
# be removed before official release
|
# be removed before official release
|
||||||
assert not self.deck.db.scalar("""
|
assert not self.deck.db.scalar("""
|
||||||
select count() from cards where fid not in (select id from facts)""")
|
select count() from cards where nid not in (select id from notes)""")
|
||||||
assert not self.deck.db.scalar("""
|
assert not self.deck.db.scalar("""
|
||||||
select count() from facts where id not in (select distinct fid from cards)""")
|
select count() from notes where id not in (select distinct nid from cards)""")
|
||||||
for t in "cards", "facts", "revlog", "graves":
|
for t in "cards", "notes", "revlog", "graves":
|
||||||
assert not self.deck.db.scalar(
|
assert not self.deck.db.scalar(
|
||||||
"select count() from %s where usn = -1" % t)
|
"select count() from %s where usn = -1" % t)
|
||||||
for g in self.deck.groups.all():
|
for g in self.deck.groups.all():
|
||||||
|
@ -142,9 +142,9 @@ select count() from facts where id not in (select distinct fid from cards)""")
|
||||||
assert m['usn'] != -1
|
assert m['usn'] != -1
|
||||||
return [
|
return [
|
||||||
self.deck.db.scalar("select count() from cards"),
|
self.deck.db.scalar("select count() from cards"),
|
||||||
self.deck.db.scalar("select count() from facts"),
|
self.deck.db.scalar("select count() from notes"),
|
||||||
self.deck.db.scalar("select count() from revlog"),
|
self.deck.db.scalar("select count() from revlog"),
|
||||||
self.deck.db.scalar("select count() from fsums"),
|
self.deck.db.scalar("select count() from nsums"),
|
||||||
self.deck.db.scalar("select count() from graves"),
|
self.deck.db.scalar("select count() from graves"),
|
||||||
len(self.deck.models.all()),
|
len(self.deck.models.all()),
|
||||||
len(self.deck.tags.all()),
|
len(self.deck.tags.all()),
|
||||||
|
@ -171,7 +171,7 @@ select count() from facts where id not in (select distinct fid from cards)""")
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def prepareToChunk(self):
|
def prepareToChunk(self):
|
||||||
self.tablesLeft = ["revlog", "cards", "facts"]
|
self.tablesLeft = ["revlog", "cards", "notes"]
|
||||||
self.cursor = None
|
self.cursor = None
|
||||||
|
|
||||||
def cursorForTable(self, table):
|
def cursorForTable(self, table):
|
||||||
|
@ -184,12 +184,12 @@ select id, cid, %d, ease, ivl, lastIvl, factor, time, type
|
||||||
from revlog where %s""" % d)
|
from revlog where %s""" % d)
|
||||||
elif table == "cards":
|
elif table == "cards":
|
||||||
return x("""
|
return x("""
|
||||||
select id, fid, gid, ord, mod, %d, type, queue, due, ivl, factor, reps,
|
select id, nid, gid, ord, mod, %d, type, queue, due, ivl, factor, reps,
|
||||||
lapses, left, edue, flags, data from cards where %s""" % d)
|
lapses, left, edue, flags, data from cards where %s""" % d)
|
||||||
else:
|
else:
|
||||||
return x("""
|
return x("""
|
||||||
select id, guid, mid, gid, mod, %d, tags, flds, '', flags, data
|
select id, guid, mid, gid, mod, %d, tags, flds, '', flags, data
|
||||||
from facts where %s""" % d)
|
from notes where %s""" % d)
|
||||||
|
|
||||||
def chunk(self):
|
def chunk(self):
|
||||||
buf = dict(done=False)
|
buf = dict(done=False)
|
||||||
|
@ -221,15 +221,15 @@ from facts where %s""" % d)
|
||||||
self.mergeRevlog(chunk['revlog'])
|
self.mergeRevlog(chunk['revlog'])
|
||||||
if "cards" in chunk:
|
if "cards" in chunk:
|
||||||
self.mergeCards(chunk['cards'])
|
self.mergeCards(chunk['cards'])
|
||||||
if "facts" in chunk:
|
if "notes" in chunk:
|
||||||
self.mergeFacts(chunk['facts'])
|
self.mergeNotes(chunk['notes'])
|
||||||
|
|
||||||
# Deletions
|
# Deletions
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def getGraves(self):
|
def getGraves(self):
|
||||||
cards = []
|
cards = []
|
||||||
facts = []
|
notes = []
|
||||||
groups = []
|
groups = []
|
||||||
if self.deck.server:
|
if self.deck.server:
|
||||||
curs = self.deck.db.execute(
|
curs = self.deck.db.execute(
|
||||||
|
@ -240,18 +240,18 @@ from facts where %s""" % d)
|
||||||
for oid, type in curs:
|
for oid, type in curs:
|
||||||
if type == REM_CARD:
|
if type == REM_CARD:
|
||||||
cards.append(oid)
|
cards.append(oid)
|
||||||
elif type == REM_FACT:
|
elif type == REM_NOTE:
|
||||||
facts.append(oid)
|
notes.append(oid)
|
||||||
else:
|
else:
|
||||||
groups.append(oid)
|
groups.append(oid)
|
||||||
if not self.deck.server:
|
if not self.deck.server:
|
||||||
self.deck.db.execute("update graves set usn=? where usn=-1",
|
self.deck.db.execute("update graves set usn=? where usn=-1",
|
||||||
self.maxUsn)
|
self.maxUsn)
|
||||||
return dict(cards=cards, facts=facts, groups=groups)
|
return dict(cards=cards, notes=notes, groups=groups)
|
||||||
|
|
||||||
def mergeGraves(self, graves):
|
def mergeGraves(self, graves):
|
||||||
# facts first, so we don't end up with duplicate graves
|
# notes first, so we don't end up with duplicate graves
|
||||||
self.deck._remFacts(graves['facts'])
|
self.deck._remNotes(graves['notes'])
|
||||||
self.deck.remCards(graves['cards'])
|
self.deck.remCards(graves['cards'])
|
||||||
for oid in graves['groups']:
|
for oid in graves['groups']:
|
||||||
self.deck.groups.rem(oid)
|
self.deck.groups.rem(oid)
|
||||||
|
@ -326,7 +326,7 @@ from facts where %s""" % d)
|
||||||
def mergeTags(self, tags):
|
def mergeTags(self, tags):
|
||||||
self.deck.tags.register(tags, usn=self.maxUsn)
|
self.deck.tags.register(tags, usn=self.maxUsn)
|
||||||
|
|
||||||
# Cards/facts/revlog
|
# Cards/notes/revlog
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def mergeRevlog(self, logs):
|
def mergeRevlog(self, logs):
|
||||||
|
@ -353,10 +353,10 @@ from facts where %s""" % d)
|
||||||
"(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
"(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
||||||
self.newerRows(cards, "cards", 4))
|
self.newerRows(cards, "cards", 4))
|
||||||
|
|
||||||
def mergeFacts(self, facts):
|
def mergeNotes(self, notes):
|
||||||
rows = self.newerRows(facts, "facts", 4)
|
rows = self.newerRows(notes, "notes", 4)
|
||||||
self.deck.db.executemany(
|
self.deck.db.executemany(
|
||||||
"insert or replace into facts values (?,?,?,?,?,?,?,?,?,?,?)",
|
"insert or replace into notes values (?,?,?,?,?,?,?,?,?,?,?)",
|
||||||
rows)
|
rows)
|
||||||
self.deck.updateFieldCache([f[0] for f in rows])
|
self.deck.updateFieldCache([f[0] for f in rows])
|
||||||
|
|
||||||
|
|
34
anki/tags.py
34
anki/tags.py
|
@ -10,7 +10,7 @@ Anki maintains a cache of used tags so it can quickly present a list of tags
|
||||||
for autocomplete and in the browser. For efficiency, deletions are not
|
for autocomplete and in the browser. For efficiency, deletions are not
|
||||||
tracked, so unused tags can only be removed from the list with a DB check.
|
tracked, so unused tags can only be removed from the list with a DB check.
|
||||||
|
|
||||||
This module manages the tag cache and tags for facts.
|
This module manages the tag cache and tags for notes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class TagManager(object):
|
class TagManager(object):
|
||||||
|
@ -45,17 +45,17 @@ class TagManager(object):
|
||||||
def all(self):
|
def all(self):
|
||||||
return self.tags.keys()
|
return self.tags.keys()
|
||||||
|
|
||||||
def registerFacts(self, fids=None):
|
def registerNotes(self, nids=None):
|
||||||
"Add any missing tags from facts to the tags list."
|
"Add any missing tags from notes to the tags list."
|
||||||
# when called without an argument, the old list is cleared first.
|
# when called without an argument, the old list is cleared first.
|
||||||
if fids:
|
if nids:
|
||||||
lim = " where id in " + ids2str(fids)
|
lim = " where id in " + ids2str(nids)
|
||||||
else:
|
else:
|
||||||
lim = ""
|
lim = ""
|
||||||
self.tags = {}
|
self.tags = {}
|
||||||
self.changed = True
|
self.changed = True
|
||||||
self.register(set(self.split(
|
self.register(set(self.split(
|
||||||
" ".join(self.deck.db.list("select distinct tags from facts"+lim)))))
|
" ".join(self.deck.db.list("select distinct tags from notes"+lim)))))
|
||||||
|
|
||||||
def allItems(self):
|
def allItems(self):
|
||||||
return self.tags.items()
|
return self.tags.items()
|
||||||
|
@ -63,7 +63,7 @@ class TagManager(object):
|
||||||
def save(self):
|
def save(self):
|
||||||
self.changed = True
|
self.changed = True
|
||||||
|
|
||||||
# Bulk addition/removal from facts
|
# Bulk addition/removal from notes
|
||||||
#############################################################
|
#############################################################
|
||||||
|
|
||||||
def bulkAdd(self, ids, tags, add=True):
|
def bulkAdd(self, ids, tags, add=True):
|
||||||
|
@ -73,7 +73,7 @@ class TagManager(object):
|
||||||
return
|
return
|
||||||
# cache tag names
|
# cache tag names
|
||||||
self.register(newTags)
|
self.register(newTags)
|
||||||
# find facts missing the tags
|
# find notes missing the tags
|
||||||
if add:
|
if add:
|
||||||
l = "tags not "
|
l = "tags not "
|
||||||
fn = self.addToStr
|
fn = self.addToStr
|
||||||
|
@ -83,18 +83,18 @@ class TagManager(object):
|
||||||
lim = " or ".join(
|
lim = " or ".join(
|
||||||
[l+"like :_%d" % c for c, t in enumerate(newTags)])
|
[l+"like :_%d" % c for c, t in enumerate(newTags)])
|
||||||
res = self.deck.db.all(
|
res = self.deck.db.all(
|
||||||
"select id, tags from facts where id in %s and %s" % (
|
"select id, tags from notes where id in %s and %s" % (
|
||||||
ids2str(ids), lim),
|
ids2str(ids), lim),
|
||||||
**dict([("_%d" % x, '%% %s %%' % y)
|
**dict([("_%d" % x, '%% %s %%' % y)
|
||||||
for x, y in enumerate(newTags)]))
|
for x, y in enumerate(newTags)]))
|
||||||
# update tags
|
# update tags
|
||||||
fids = []
|
nids = []
|
||||||
def fix(row):
|
def fix(row):
|
||||||
fids.append(row[0])
|
nids.append(row[0])
|
||||||
return {'id': row[0], 't': fn(tags, row[1]), 'n':intTime(),
|
return {'id': row[0], 't': fn(tags, row[1]), 'n':intTime(),
|
||||||
'u':self.deck.usn()}
|
'u':self.deck.usn()}
|
||||||
self.deck.db.executemany(
|
self.deck.db.executemany(
|
||||||
"update facts set tags=:t,mod=:n,usn=:u where id = :id",
|
"update notes set tags=:t,mod=:n,usn=:u where id = :id",
|
||||||
[fix(row) for row in res])
|
[fix(row) for row in res])
|
||||||
|
|
||||||
def bulkRem(self, ids, tags):
|
def bulkRem(self, ids, tags):
|
||||||
|
@ -149,12 +149,12 @@ class TagManager(object):
|
||||||
# Tag-based selective study
|
# Tag-based selective study
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def selTagFids(self, yes, no):
|
def selTagNids(self, yes, no):
|
||||||
l = []
|
l = []
|
||||||
# find facts that match yes
|
# find notes that match yes
|
||||||
lim = ""
|
lim = ""
|
||||||
args = []
|
args = []
|
||||||
query = "select id from facts"
|
query = "select id from notes"
|
||||||
if not yes and not no:
|
if not yes and not no:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
|
@ -172,7 +172,7 @@ class TagManager(object):
|
||||||
return self.deck.db.list(query, *args)
|
return self.deck.db.list(query, *args)
|
||||||
|
|
||||||
def setGroupForTags(self, yes, no, gid):
|
def setGroupForTags(self, yes, no, gid):
|
||||||
fids = self.selTagFids(yes, no)
|
nids = self.selTagNids(yes, no)
|
||||||
self.deck.db.execute(
|
self.deck.db.execute(
|
||||||
"update cards set gid=?,mod=?,usn=? where fid in "+ids2str(fids),
|
"update cards set gid=?,mod=?,usn=? where nid in "+ids2str(nids),
|
||||||
gid, intTime(), self.deck.usn())
|
gid, intTime(), self.deck.usn())
|
||||||
|
|
|
@ -133,7 +133,7 @@ analyze;""")
|
||||||
db.execute("pragma page_size = 4096")
|
db.execute("pragma page_size = 4096")
|
||||||
db.execute("pragma legacy_file_format = 0")
|
db.execute("pragma legacy_file_format = 0")
|
||||||
|
|
||||||
# facts
|
# notes
|
||||||
###########
|
###########
|
||||||
# tags should have a leading and trailing space if not empty, and not
|
# tags should have a leading and trailing space if not empty, and not
|
||||||
# use commas
|
# use commas
|
||||||
|
@ -177,7 +177,7 @@ select id, id, modelId, 1, cast(created*1000 as int), cast(modified as int),
|
||||||
# and put the facts into the new table
|
# and put the facts into the new table
|
||||||
db.execute("drop table facts")
|
db.execute("drop table facts")
|
||||||
_addSchema(db, False)
|
_addSchema(db, False)
|
||||||
db.executemany("insert into facts values (?,?,?,?,?,?,?,?,'',0,'')", data)
|
db.executemany("insert into notes values (?,?,?,?,?,?,?,?,'',0,'')", data)
|
||||||
db.execute("drop table fields")
|
db.execute("drop table fields")
|
||||||
|
|
||||||
# cards
|
# cards
|
||||||
|
@ -336,7 +336,7 @@ insert or replace into deck select id, cast(created as int), :t,
|
||||||
m['flds'] = self._fieldsForModel(row[0])
|
m['flds'] = self._fieldsForModel(row[0])
|
||||||
m['tmpls'] = self._templatesForModel(row[0], m['flds'])
|
m['tmpls'] = self._templatesForModel(row[0], m['flds'])
|
||||||
mods[m['id']] = m
|
mods[m['id']] = m
|
||||||
db.execute("update facts set mid = ? where mid = ?", t, row[0])
|
db.execute("update notes set mid = ? where mid = ?", t, row[0])
|
||||||
# save and clean up
|
# save and clean up
|
||||||
db.execute("update deck set models = ?", simplejson.dumps(mods))
|
db.execute("update deck set models = ?", simplejson.dumps(mods))
|
||||||
db.execute("drop table fieldModels")
|
db.execute("drop table fieldModels")
|
||||||
|
@ -465,7 +465,7 @@ order by ordinal""", mid)):
|
||||||
# Media references
|
# Media references
|
||||||
######################################################################
|
######################################################################
|
||||||
# In 2.0 we drop support for media and latex references in the template,
|
# In 2.0 we drop support for media and latex references in the template,
|
||||||
# since they require generating card templates to see what media a fact
|
# since they require generating card templates to see what media a note
|
||||||
# uses, and are confusing for shared deck users. To ease the upgrade
|
# uses, and are confusing for shared deck users. To ease the upgrade
|
||||||
# process, we automatically convert the references to new fields.
|
# process, we automatically convert the references to new fields.
|
||||||
|
|
||||||
|
@ -500,16 +500,16 @@ order by ordinal""", mid)):
|
||||||
# add the new field
|
# add the new field
|
||||||
f = deck.models.newField(fld)
|
f = deck.models.newField(fld)
|
||||||
deck.models.addField(m, f)
|
deck.models.addField(m, f)
|
||||||
# loop through facts and write reference into new field
|
# loop through notes and write reference into new field
|
||||||
data = []
|
data = []
|
||||||
for id, flds in self.deck.db.execute(
|
for id, flds in self.deck.db.execute(
|
||||||
"select id, flds from facts where id in "+
|
"select id, flds from notes where id in "+
|
||||||
ids2str(deck.models.fids(m))):
|
ids2str(deck.models.nids(m))):
|
||||||
sflds = splitFields(flds)
|
sflds = splitFields(flds)
|
||||||
ref = all.replace(fname, pre+sflds[idx]+suf)
|
ref = all.replace(fname, pre+sflds[idx]+suf)
|
||||||
data.append((flds+ref, id))
|
data.append((flds+ref, id))
|
||||||
# update facts
|
# update notes
|
||||||
deck.db.executemany("update facts set flds=? where id=?",
|
deck.db.executemany("update notes set flds=? where id=?",
|
||||||
data)
|
data)
|
||||||
# note field for future
|
# note field for future
|
||||||
state['mflds'][fname] = fld
|
state['mflds'][fname] = fld
|
||||||
|
@ -544,7 +544,7 @@ order by ordinal""", mid)):
|
||||||
for t in m['tmpls']:
|
for t in m['tmpls']:
|
||||||
if not t['actv']:
|
if not t['actv']:
|
||||||
if not d.db.scalar("""
|
if not d.db.scalar("""
|
||||||
select 1 from cards where fid in (select id from facts where mid = ?)
|
select 1 from cards where nid in (select id from notes where mid = ?)
|
||||||
and ord = ? limit 1""", m['id'], t['ord']):
|
and ord = ? limit 1""", m['id'], t['ord']):
|
||||||
remove.append(t)
|
remove.append(t)
|
||||||
del t['actv']
|
del t['actv']
|
||||||
|
@ -582,7 +582,7 @@ and ord = ? limit 1""", m['id'], t['ord']):
|
||||||
deck.crt = int(time.mktime(d.timetuple()))
|
deck.crt = int(time.mktime(d.timetuple()))
|
||||||
deck.sched._updateCutoff()
|
deck.sched._updateCutoff()
|
||||||
# update uniq cache
|
# update uniq cache
|
||||||
deck.updateFieldCache(deck.db.list("select id from facts"))
|
deck.updateFieldCache(deck.db.list("select id from notes"))
|
||||||
# remove old views
|
# remove old views
|
||||||
for v in ("failedCards", "revCardsOld", "revCardsNew",
|
for v in ("failedCards", "revCardsOld", "revCardsNew",
|
||||||
"revCardsDue", "revCardsRandom", "acqCardsRandom",
|
"revCardsDue", "revCardsRandom", "acqCardsRandom",
|
||||||
|
@ -595,11 +595,11 @@ and ord = ? limit 1""", m['id'], t['ord']):
|
||||||
deck.db.execute("update cards set queue=-2 where queue between 3 and 5")
|
deck.db.execute("update cards set queue=-2 where queue between 3 and 5")
|
||||||
deck.db.execute("update cards set queue=-3 where queue between 6 and 8")
|
deck.db.execute("update cards set queue=-3 where queue between 6 and 8")
|
||||||
# remove old deleted tables
|
# remove old deleted tables
|
||||||
for t in ("cards", "facts", "models", "media"):
|
for t in ("cards", "notes", "models", "media"):
|
||||||
deck.db.execute("drop table if exists %sDeleted" % t)
|
deck.db.execute("drop table if exists %sDeleted" % t)
|
||||||
# rewrite due times for new cards
|
# rewrite due times for new cards
|
||||||
deck.db.execute("""
|
deck.db.execute("""
|
||||||
update cards set due = fid where type=0""")
|
update cards set due = nid where type=0""")
|
||||||
# and failed cards
|
# and failed cards
|
||||||
left = len(deck.groups.conf(1)['new']['delays'])
|
left = len(deck.groups.conf(1)['new']['delays'])
|
||||||
deck.db.execute("update cards set edue = ?, left=? where type = 1",
|
deck.db.execute("update cards set edue = ?, left=? where type = 1",
|
||||||
|
@ -614,7 +614,7 @@ update cards set due = cast(
|
||||||
if deck.models.randomNew():
|
if deck.models.randomNew():
|
||||||
deck.sched.randomizeCards()
|
deck.sched.randomizeCards()
|
||||||
# update insertion id
|
# update insertion id
|
||||||
deck.conf['nextPos'] = deck.db.scalar("select max(id) from facts")+1
|
deck.conf['nextPos'] = deck.db.scalar("select max(id) from notes")+1
|
||||||
deck.save()
|
deck.save()
|
||||||
|
|
||||||
# optimize and finish
|
# optimize and finish
|
||||||
|
|
|
@ -185,7 +185,7 @@ def timestampID(db, table):
|
||||||
def maxID(db):
|
def maxID(db):
|
||||||
"Return the first safe ID to use."
|
"Return the first safe ID to use."
|
||||||
now = intTime(1000)
|
now = intTime(1000)
|
||||||
for tbl in "cards", "facts":
|
for tbl in "cards", "notes":
|
||||||
now = max(now, db.scalar(
|
now = max(now, db.scalar(
|
||||||
"select max(id) from %s" % tbl))
|
"select max(id) from %s" % tbl))
|
||||||
return now + 1
|
return now + 1
|
||||||
|
|
|
@ -15,12 +15,12 @@ def setup1():
|
||||||
deck = Deck()
|
deck = Deck()
|
||||||
deck.addModel(BasicModel())
|
deck.addModel(BasicModel())
|
||||||
deck.currentModel.cardModels[1].active = True
|
deck.currentModel.cardModels[1].active = True
|
||||||
f = deck.newFact()
|
f = deck.newNote()
|
||||||
f['Front'] = u"foo"; f['Back'] = u"bar"; f.tags = u"tag, tag2"
|
f['Front'] = u"foo"; f['Back'] = u"bar"; f.tags = u"tag, tag2"
|
||||||
deck.addFact(f)
|
deck.addNote(f)
|
||||||
f = deck.newFact()
|
f = deck.newNote()
|
||||||
f['Front'] = u"baz"; f['Back'] = u"qux"
|
f['Front'] = u"baz"; f['Back'] = u"qux"
|
||||||
deck.addFact(f)
|
deck.addNote(f)
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
@ -53,8 +53,8 @@ def test_export_textcard():
|
||||||
e.exportInto(f)
|
e.exportInto(f)
|
||||||
|
|
||||||
@nose.with_setup(setup1)
|
@nose.with_setup(setup1)
|
||||||
def test_export_textfact():
|
def test_export_textnote():
|
||||||
e = TextFactExporter(deck)
|
e = TextNoteExporter(deck)
|
||||||
f = unicode(tempfile.mkstemp(prefix="ankitest")[1])
|
f = unicode(tempfile.mkstemp(prefix="ankitest")[1])
|
||||||
os.unlink(f)
|
os.unlink(f)
|
||||||
e.exportInto(f)
|
e.exportInto(f)
|
||||||
|
|
|
@ -8,7 +8,7 @@ from tests.shared import getEmptyDeck
|
||||||
|
|
||||||
def test_previewCards():
|
def test_previewCards():
|
||||||
deck = getEmptyDeck()
|
deck = getEmptyDeck()
|
||||||
f = deck.newFact()
|
f = deck.newNote()
|
||||||
f['Front'] = u'1'
|
f['Front'] = u'1'
|
||||||
f['Back'] = u'2'
|
f['Back'] = u'2'
|
||||||
# non-empty and active
|
# non-empty and active
|
||||||
|
@ -18,8 +18,8 @@ def test_previewCards():
|
||||||
# all templates
|
# all templates
|
||||||
cards = deck.previewCards(f, 2)
|
cards = deck.previewCards(f, 2)
|
||||||
assert len(cards) == 1
|
assert len(cards) == 1
|
||||||
# add the fact, and test existing preview
|
# add the note, and test existing preview
|
||||||
deck.addFact(f)
|
deck.addNote(f)
|
||||||
cards = deck.previewCards(f, 1)
|
cards = deck.previewCards(f, 1)
|
||||||
assert len(cards) == 1
|
assert len(cards) == 1
|
||||||
assert cards[0].ord == 0
|
assert cards[0].ord == 0
|
||||||
|
@ -28,29 +28,29 @@ def test_previewCards():
|
||||||
|
|
||||||
def test_delete():
|
def test_delete():
|
||||||
deck = getEmptyDeck()
|
deck = getEmptyDeck()
|
||||||
f = deck.newFact()
|
f = deck.newNote()
|
||||||
f['Front'] = u'1'
|
f['Front'] = u'1'
|
||||||
f['Back'] = u'2'
|
f['Back'] = u'2'
|
||||||
deck.addFact(f)
|
deck.addNote(f)
|
||||||
cid = f.cards()[0].id
|
cid = f.cards()[0].id
|
||||||
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.remCards([cid])
|
deck.remCards([cid])
|
||||||
assert deck.cardCount() == 0
|
assert deck.cardCount() == 0
|
||||||
assert deck.factCount() == 0
|
assert deck.noteCount() == 0
|
||||||
assert deck.db.scalar("select count() from facts") == 0
|
assert deck.db.scalar("select count() from notes") == 0
|
||||||
assert deck.db.scalar("select count() from cards") == 0
|
assert deck.db.scalar("select count() from cards") == 0
|
||||||
assert deck.db.scalar("select count() from fsums") == 0
|
assert deck.db.scalar("select count() from nsums") == 0
|
||||||
assert deck.db.scalar("select count() from revlog") == 0
|
assert deck.db.scalar("select count() from revlog") == 0
|
||||||
assert deck.db.scalar("select count() from graves") == 2
|
assert deck.db.scalar("select count() from graves") == 2
|
||||||
|
|
||||||
def test_misc():
|
def test_misc():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u'1'
|
f['Front'] = u'1'
|
||||||
f['Back'] = u'2'
|
f['Back'] = u'2'
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
c = f.cards()[0]
|
c = f.cards()[0]
|
||||||
id = d.models.current()['id']
|
id = d.models.current()['id']
|
||||||
assert c.template()['ord'] == 0
|
assert c.template()['ord'] == 0
|
||||||
|
|
|
@ -41,12 +41,12 @@ def test_openReadOnly():
|
||||||
os.chmod(newPath, 0666)
|
os.chmod(newPath, 0666)
|
||||||
os.unlink(newPath)
|
os.unlink(newPath)
|
||||||
|
|
||||||
def test_factAddDelete():
|
def test_noteAddDelete():
|
||||||
deck = getEmptyDeck()
|
deck = getEmptyDeck()
|
||||||
# add a fact
|
# add a note
|
||||||
f = deck.newFact()
|
f = deck.newNote()
|
||||||
f['Front'] = u"one"; f['Back'] = u"two"
|
f['Front'] = u"one"; f['Back'] = u"two"
|
||||||
n = deck.addFact(f)
|
n = deck.addNote(f)
|
||||||
assert n == 1
|
assert n == 1
|
||||||
# test multiple cards - add another template
|
# test multiple cards - add another template
|
||||||
m = deck.models.current(); mm = deck.models
|
m = deck.models.current(); mm = deck.models
|
||||||
|
@ -61,10 +61,10 @@ def test_factAddDelete():
|
||||||
# should generate cards on close
|
# should generate cards on close
|
||||||
mm.save(m, gencards=True)
|
mm.save(m, gencards=True)
|
||||||
assert deck.cardCount() == 2
|
assert deck.cardCount() == 2
|
||||||
# creating new facts should use both cards
|
# creating new notes should use both cards
|
||||||
f = deck.newFact()
|
f = deck.newNote()
|
||||||
f['Front'] = u"three"; f['Back'] = u"four"
|
f['Front'] = u"three"; f['Back'] = u"four"
|
||||||
n = deck.addFact(f)
|
n = deck.addNote(f)
|
||||||
assert n == 2
|
assert n == 2
|
||||||
assert deck.cardCount() == 4
|
assert deck.cardCount() == 4
|
||||||
# check q/a generation
|
# check q/a generation
|
||||||
|
@ -74,7 +74,7 @@ def test_factAddDelete():
|
||||||
for p in f.problems():
|
for p in f.problems():
|
||||||
assert not p
|
assert not p
|
||||||
# now let's make a duplicate and test uniqueness
|
# now let's make a duplicate and test uniqueness
|
||||||
f2 = deck.newFact()
|
f2 = deck.newNote()
|
||||||
f2.model()['flds'][1]['req'] = True
|
f2.model()['flds'][1]['req'] = True
|
||||||
f2['Front'] = u"one"; f2['Back'] = u""
|
f2['Front'] = u"one"; f2['Back'] = u""
|
||||||
p = f2.problems()
|
p = f2.problems()
|
||||||
|
@ -84,66 +84,66 @@ def test_factAddDelete():
|
||||||
cards = f.cards()
|
cards = f.cards()
|
||||||
id1 = cards[0].id; id2 = cards[1].id
|
id1 = cards[0].id; id2 = cards[1].id
|
||||||
assert deck.cardCount() == 4
|
assert deck.cardCount() == 4
|
||||||
assert deck.factCount() == 2
|
assert deck.noteCount() == 2
|
||||||
deck.remCards([id1])
|
deck.remCards([id1])
|
||||||
assert deck.cardCount() == 3
|
assert deck.cardCount() == 3
|
||||||
assert deck.factCount() == 2
|
assert deck.noteCount() == 2
|
||||||
# and the second should clear the fact
|
# and the second should clear the note
|
||||||
deck.remCards([id2])
|
deck.remCards([id2])
|
||||||
assert deck.cardCount() == 2
|
assert deck.cardCount() == 2
|
||||||
assert deck.factCount() == 1
|
assert deck.noteCount() == 1
|
||||||
|
|
||||||
def test_fieldChecksum():
|
def test_fieldChecksum():
|
||||||
deck = getEmptyDeck()
|
deck = getEmptyDeck()
|
||||||
f = deck.newFact()
|
f = deck.newNote()
|
||||||
f['Front'] = u"new"; f['Back'] = u"new2"
|
f['Front'] = u"new"; f['Back'] = u"new2"
|
||||||
deck.addFact(f)
|
deck.addNote(f)
|
||||||
assert deck.db.scalar(
|
assert deck.db.scalar(
|
||||||
"select csum from fsums") == int("c2a6b03f", 16)
|
"select csum from nsums") == int("c2a6b03f", 16)
|
||||||
# empty field should have no checksum
|
# empty field should have no checksum
|
||||||
f['Front'] = u""
|
f['Front'] = u""
|
||||||
f.flush()
|
f.flush()
|
||||||
assert deck.db.scalar(
|
assert deck.db.scalar(
|
||||||
"select count() from fsums") == 0
|
"select count() from nsums") == 0
|
||||||
# changing the val should change the checksum
|
# changing the val should change the checksum
|
||||||
f['Front'] = u"newx"
|
f['Front'] = u"newx"
|
||||||
f.flush()
|
f.flush()
|
||||||
assert deck.db.scalar(
|
assert deck.db.scalar(
|
||||||
"select csum from fsums") == int("302811ae", 16)
|
"select csum from nsums") == int("302811ae", 16)
|
||||||
# turning off unique and modifying the fact should delete the sum
|
# turning off unique and modifying the note should delete the sum
|
||||||
m = f.model()
|
m = f.model()
|
||||||
m['flds'][0]['uniq'] = False
|
m['flds'][0]['uniq'] = False
|
||||||
deck.models.save(m)
|
deck.models.save(m)
|
||||||
f.flush()
|
f.flush()
|
||||||
assert deck.db.scalar(
|
assert deck.db.scalar(
|
||||||
"select count() from fsums") == 0
|
"select count() from nsums") == 0
|
||||||
# and turning on both should ensure two checksums generated
|
# and turning on both should ensure two checksums generated
|
||||||
m['flds'][0]['uniq'] = True
|
m['flds'][0]['uniq'] = True
|
||||||
m['flds'][1]['uniq'] = True
|
m['flds'][1]['uniq'] = True
|
||||||
deck.models.save(m)
|
deck.models.save(m)
|
||||||
f.flush()
|
f.flush()
|
||||||
assert deck.db.scalar(
|
assert deck.db.scalar(
|
||||||
"select count() from fsums") == 2
|
"select count() from nsums") == 2
|
||||||
|
|
||||||
def test_selective():
|
def test_selective():
|
||||||
deck = getEmptyDeck()
|
deck = getEmptyDeck()
|
||||||
f = deck.newFact()
|
f = deck.newNote()
|
||||||
f['Front'] = u"1"; f.tags = ["one", "three"]
|
f['Front'] = u"1"; f.tags = ["one", "three"]
|
||||||
deck.addFact(f)
|
deck.addNote(f)
|
||||||
f = deck.newFact()
|
f = deck.newNote()
|
||||||
f['Front'] = u"2"; f.tags = ["two", "three", "four"]
|
f['Front'] = u"2"; f.tags = ["two", "three", "four"]
|
||||||
deck.addFact(f)
|
deck.addNote(f)
|
||||||
f = deck.newFact()
|
f = deck.newNote()
|
||||||
f['Front'] = u"3"; f.tags = ["one", "two", "three", "four"]
|
f['Front'] = u"3"; f.tags = ["one", "two", "three", "four"]
|
||||||
deck.addFact(f)
|
deck.addNote(f)
|
||||||
assert len(deck.tags.selTagFids(["one"], [])) == 2
|
assert len(deck.tags.selTagNids(["one"], [])) == 2
|
||||||
assert len(deck.tags.selTagFids(["three"], [])) == 3
|
assert len(deck.tags.selTagNids(["three"], [])) == 3
|
||||||
assert len(deck.tags.selTagFids([], ["three"])) == 0
|
assert len(deck.tags.selTagNids([], ["three"])) == 0
|
||||||
assert len(deck.tags.selTagFids(["one"], ["three"])) == 0
|
assert len(deck.tags.selTagNids(["one"], ["three"])) == 0
|
||||||
assert len(deck.tags.selTagFids(["one"], ["two"])) == 1
|
assert len(deck.tags.selTagNids(["one"], ["two"])) == 1
|
||||||
assert len(deck.tags.selTagFids(["two", "three"], [])) == 3
|
assert len(deck.tags.selTagNids(["two", "three"], [])) == 3
|
||||||
assert len(deck.tags.selTagFids(["two", "three"], ["one"])) == 1
|
assert len(deck.tags.selTagNids(["two", "three"], ["one"])) == 1
|
||||||
assert len(deck.tags.selTagFids(["one", "three"], ["two", "four"])) == 1
|
assert len(deck.tags.selTagNids(["one", "three"], ["two", "four"])) == 1
|
||||||
deck.tags.setGroupForTags(["three"], [], 3)
|
deck.tags.setGroupForTags(["three"], [], 3)
|
||||||
assert deck.db.scalar("select count() from cards where gid = 3") == 3
|
assert deck.db.scalar("select count() from cards where gid = 3") == 3
|
||||||
deck.tags.setGroupForTags(["one"], [], 2)
|
deck.tags.setGroupForTags(["one"], [], 2)
|
||||||
|
@ -151,12 +151,12 @@ def test_selective():
|
||||||
|
|
||||||
def test_addDelTags():
|
def test_addDelTags():
|
||||||
deck = getEmptyDeck()
|
deck = getEmptyDeck()
|
||||||
f = deck.newFact()
|
f = deck.newNote()
|
||||||
f['Front'] = u"1"
|
f['Front'] = u"1"
|
||||||
deck.addFact(f)
|
deck.addNote(f)
|
||||||
f2 = deck.newFact()
|
f2 = deck.newNote()
|
||||||
f2['Front'] = u"2"
|
f2['Front'] = u"2"
|
||||||
deck.addFact(f2)
|
deck.addNote(f2)
|
||||||
# adding for a given id
|
# adding for a given id
|
||||||
deck.tags.bulkAdd([f.id], "foo")
|
deck.tags.bulkAdd([f.id], "foo")
|
||||||
f.load(); f2.load()
|
f.load(); f2.load()
|
||||||
|
|
|
@ -4,25 +4,25 @@ from tests.shared import getEmptyDeck
|
||||||
|
|
||||||
def test_findCards():
|
def test_findCards():
|
||||||
deck = getEmptyDeck()
|
deck = getEmptyDeck()
|
||||||
f = deck.newFact()
|
f = deck.newNote()
|
||||||
f['Front'] = u'dog'
|
f['Front'] = u'dog'
|
||||||
f['Back'] = u'cat'
|
f['Back'] = u'cat'
|
||||||
f.tags.append(u"monkey")
|
f.tags.append(u"monkey")
|
||||||
f1id = f.id
|
f1id = f.id
|
||||||
deck.addFact(f)
|
deck.addNote(f)
|
||||||
firstCardId = f.cards()[0].id
|
firstCardId = f.cards()[0].id
|
||||||
f = deck.newFact()
|
f = deck.newNote()
|
||||||
f['Front'] = u'goats are fun'
|
f['Front'] = u'goats are fun'
|
||||||
f['Back'] = u'sheep'
|
f['Back'] = u'sheep'
|
||||||
f.tags.append(u"sheep goat horse")
|
f.tags.append(u"sheep goat horse")
|
||||||
deck.addFact(f)
|
deck.addNote(f)
|
||||||
f2id = f.id
|
f2id = f.id
|
||||||
f = deck.newFact()
|
f = deck.newNote()
|
||||||
f['Front'] = u'cat'
|
f['Front'] = u'cat'
|
||||||
f['Back'] = u'sheep'
|
f['Back'] = u'sheep'
|
||||||
deck.addFact(f)
|
deck.addNote(f)
|
||||||
catCard = f.cards()[0]
|
catCard = f.cards()[0]
|
||||||
f = deck.newFact()
|
f = deck.newNote()
|
||||||
f['Front'] = u'template test'
|
f['Front'] = u'template test'
|
||||||
f['Back'] = u'foo bar'
|
f['Back'] = u'foo bar'
|
||||||
m = deck.models.current(); mm = deck.models
|
m = deck.models.current(); mm = deck.models
|
||||||
|
@ -31,7 +31,7 @@ def test_findCards():
|
||||||
t['afmt'] = "{{Front}}"
|
t['afmt'] = "{{Front}}"
|
||||||
mm.addTemplate(m, t)
|
mm.addTemplate(m, t)
|
||||||
mm.save(m)
|
mm.save(m)
|
||||||
deck.addFact(f)
|
deck.addNote(f)
|
||||||
latestCardIds = [c.id for c in f.cards()]
|
latestCardIds = [c.id for c in f.cards()]
|
||||||
# tag searches
|
# tag searches
|
||||||
assert not deck.findCards("tag:donkey")
|
assert not deck.findCards("tag:donkey")
|
||||||
|
@ -41,11 +41,11 @@ def test_findCards():
|
||||||
assert len(deck.findCards("tag:monkey")) == 1
|
assert len(deck.findCards("tag:monkey")) == 1
|
||||||
assert len(deck.findCards("tag:sheep -tag:monkey")) == 1
|
assert len(deck.findCards("tag:sheep -tag:monkey")) == 1
|
||||||
assert len(deck.findCards("-tag:sheep")) == 4
|
assert len(deck.findCards("-tag:sheep")) == 4
|
||||||
deck.tags.bulkAdd(deck.db.list("select id from facts"), "foo bar")
|
deck.tags.bulkAdd(deck.db.list("select id from notes"), "foo bar")
|
||||||
assert (len(deck.findCards("tag:foo")) ==
|
assert (len(deck.findCards("tag:foo")) ==
|
||||||
len(deck.findCards("tag:bar")) ==
|
len(deck.findCards("tag:bar")) ==
|
||||||
5)
|
5)
|
||||||
deck.tags.bulkRem(deck.db.list("select id from facts"), "foo")
|
deck.tags.bulkRem(deck.db.list("select id from notes"), "foo")
|
||||||
assert len(deck.findCards("tag:foo")) == 0
|
assert len(deck.findCards("tag:foo")) == 0
|
||||||
assert len(deck.findCards("tag:bar")) == 5
|
assert len(deck.findCards("tag:bar")) == 5
|
||||||
# text searches
|
# text searches
|
||||||
|
@ -71,10 +71,10 @@ def test_findCards():
|
||||||
import time; time.sleep(1)
|
import time; time.sleep(1)
|
||||||
c.flush()
|
c.flush()
|
||||||
assert deck.findCards("is:suspended") == [c.id]
|
assert deck.findCards("is:suspended") == [c.id]
|
||||||
# fids
|
# nids
|
||||||
assert deck.findCards("fid:54321") == []
|
assert deck.findCards("nid:54321") == []
|
||||||
assert len(deck.findCards("fid:%d"%f.id)) == 2
|
assert len(deck.findCards("nid:%d"%f.id)) == 2
|
||||||
assert len(deck.findCards("fid:%d,%d" % (f1id, f2id))) == 2
|
assert len(deck.findCards("nid:%d,%d" % (f1id, f2id))) == 2
|
||||||
# templates
|
# templates
|
||||||
assert len(deck.findCards("card:foo")) == 0
|
assert len(deck.findCards("card:foo")) == 0
|
||||||
assert len(deck.findCards("card:forward")) == 4
|
assert len(deck.findCards("card:forward")) == 4
|
||||||
|
@ -89,10 +89,10 @@ def test_findCards():
|
||||||
assert len(deck.findCards("-back:sheep")) == 3
|
assert len(deck.findCards("-back:sheep")) == 3
|
||||||
assert len(deck.findCards("front:")) == 5
|
assert len(deck.findCards("front:")) == 5
|
||||||
# ordering
|
# ordering
|
||||||
deck.conf['sortType'] = "factCrt"
|
deck.conf['sortType'] = "noteCrt"
|
||||||
assert deck.findCards("front:")[-1] in latestCardIds
|
assert deck.findCards("front:")[-1] in latestCardIds
|
||||||
assert deck.findCards("")[-1] in latestCardIds
|
assert deck.findCards("")[-1] in latestCardIds
|
||||||
deck.conf['sortType'] = "factFld"
|
deck.conf['sortType'] = "noteFld"
|
||||||
assert deck.findCards("")[0] == catCard.id
|
assert deck.findCards("")[0] == catCard.id
|
||||||
assert deck.findCards("")[-1] in latestCardIds
|
assert deck.findCards("")[-1] in latestCardIds
|
||||||
deck.conf['sortType'] = "cardMod"
|
deck.conf['sortType'] = "cardMod"
|
||||||
|
@ -109,10 +109,10 @@ def test_findCards():
|
||||||
assert len(deck.findCards("-group:default")) == 0
|
assert len(deck.findCards("-group:default")) == 0
|
||||||
assert len(deck.findCards("-group:foo")) == 5
|
assert len(deck.findCards("-group:foo")) == 5
|
||||||
# full search
|
# full search
|
||||||
f = deck.newFact()
|
f = deck.newNote()
|
||||||
f['Front'] = u'hello<b>world</b>'
|
f['Front'] = u'hello<b>world</b>'
|
||||||
f['Back'] = u''
|
f['Back'] = u''
|
||||||
deck.addFact(f)
|
deck.addNote(f)
|
||||||
assert len(deck.findCards("helloworld")) == 0
|
assert len(deck.findCards("helloworld")) == 0
|
||||||
assert len(deck.findCards("helloworld", full=True)) == 1
|
assert len(deck.findCards("helloworld", full=True)) == 1
|
||||||
assert len(deck.findCards("front:helloworld")) == 0
|
assert len(deck.findCards("front:helloworld")) == 0
|
||||||
|
@ -122,27 +122,27 @@ def test_findCards():
|
||||||
|
|
||||||
def test_findReplace():
|
def test_findReplace():
|
||||||
deck = getEmptyDeck()
|
deck = getEmptyDeck()
|
||||||
f = deck.newFact()
|
f = deck.newNote()
|
||||||
f['Front'] = u'foo'
|
f['Front'] = u'foo'
|
||||||
f['Back'] = u'bar'
|
f['Back'] = u'bar'
|
||||||
deck.addFact(f)
|
deck.addNote(f)
|
||||||
f2 = deck.newFact()
|
f2 = deck.newNote()
|
||||||
f2['Front'] = u'baz'
|
f2['Front'] = u'baz'
|
||||||
f2['Back'] = u'foo'
|
f2['Back'] = u'foo'
|
||||||
deck.addFact(f2)
|
deck.addNote(f2)
|
||||||
fids = [f.id, f2.id]
|
nids = [f.id, f2.id]
|
||||||
# should do nothing
|
# should do nothing
|
||||||
assert deck.findReplace(fids, "abc", "123") == 0
|
assert deck.findReplace(nids, "abc", "123") == 0
|
||||||
# global replace
|
# global replace
|
||||||
assert deck.findReplace(fids, "foo", "qux") == 2
|
assert deck.findReplace(nids, "foo", "qux") == 2
|
||||||
f.load(); assert f['Front'] == "qux"
|
f.load(); assert f['Front'] == "qux"
|
||||||
f2.load(); assert f2['Back'] == "qux"
|
f2.load(); assert f2['Back'] == "qux"
|
||||||
# single field replace
|
# single field replace
|
||||||
assert deck.findReplace(fids, "qux", "foo", field="Front") == 1
|
assert deck.findReplace(nids, "qux", "foo", field="Front") == 1
|
||||||
f.load(); assert f['Front'] == "foo"
|
f.load(); assert f['Front'] == "foo"
|
||||||
f2.load(); assert f2['Back'] == "qux"
|
f2.load(); assert f2['Back'] == "qux"
|
||||||
# regex replace
|
# regex replace
|
||||||
assert deck.findReplace(fids, "B.r", "reg") == 0
|
assert deck.findReplace(nids, "B.r", "reg") == 0
|
||||||
f.load(); assert f['Back'] != "reg"
|
f.load(); assert f['Back'] != "reg"
|
||||||
assert deck.findReplace(fids, "B.r", "reg", regex=True) == 1
|
assert deck.findReplace(nids, "B.r", "reg", regex=True) == 1
|
||||||
f.load(); assert f['Back'] == "reg"
|
f.load(); assert f['Back'] == "reg"
|
||||||
|
|
|
@ -39,12 +39,12 @@ def test_remove():
|
||||||
deck = getEmptyDeck()
|
deck = getEmptyDeck()
|
||||||
# can't remove the default group
|
# can't remove the default group
|
||||||
assertException(AssertionError, lambda: deck.groups.rem(1))
|
assertException(AssertionError, lambda: deck.groups.rem(1))
|
||||||
# create a new group, and add a fact/card to it
|
# create a new group, and add a note/card to it
|
||||||
g1 = deck.groups.id("g1")
|
g1 = deck.groups.id("g1")
|
||||||
f = deck.newFact()
|
f = deck.newNote()
|
||||||
f['Front'] = u"1"
|
f['Front'] = u"1"
|
||||||
f.gid = g1
|
f.gid = g1
|
||||||
deck.addFact(f)
|
deck.addNote(f)
|
||||||
c = f.cards()[0]
|
c = f.cards()[0]
|
||||||
assert c.gid == g1
|
assert c.gid == g1
|
||||||
# by default deleting the group leaves the cards with an invalid gid
|
# by default deleting the group leaves the cards with an invalid gid
|
||||||
|
@ -58,10 +58,10 @@ def test_remove():
|
||||||
# let's create another group and explicitly set the card to it
|
# let's create another group and explicitly set the card to it
|
||||||
g2 = deck.groups.id("g2")
|
g2 = deck.groups.id("g2")
|
||||||
c.gid = g2; c.flush()
|
c.gid = g2; c.flush()
|
||||||
# this time we'll delete the card/fact too
|
# this time we'll delete the card/note too
|
||||||
deck.groups.rem(g2, cardsToo=True)
|
deck.groups.rem(g2, cardsToo=True)
|
||||||
assert deck.cardCount() == 0
|
assert deck.cardCount() == 0
|
||||||
assert deck.factCount() == 0
|
assert deck.noteCount() == 0
|
||||||
|
|
||||||
def test_rename():
|
def test_rename():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
|
|
|
@ -8,23 +8,23 @@ from anki.errors import *
|
||||||
from anki import Deck
|
from anki import Deck
|
||||||
from anki.importing import Anki1Importer, Anki2Importer, TextImporter, \
|
from anki.importing import Anki1Importer, Anki2Importer, TextImporter, \
|
||||||
SupermemoXmlImporter
|
SupermemoXmlImporter
|
||||||
from anki.facts import Fact
|
from anki.notes import Note
|
||||||
|
|
||||||
from anki.db import *
|
from anki.db import *
|
||||||
|
|
||||||
testDir = os.path.dirname(__file__)
|
testDir = os.path.dirname(__file__)
|
||||||
|
|
||||||
srcFacts=None
|
srcNotes=None
|
||||||
srcCards=None
|
srcCards=None
|
||||||
|
|
||||||
def test_anki2():
|
def test_anki2():
|
||||||
global srcFacts, srcCards
|
global srcNotes, srcCards
|
||||||
# get the deck to import
|
# get the deck to import
|
||||||
tmp = getUpgradeDeckPath()
|
tmp = getUpgradeDeckPath()
|
||||||
u = Upgrader()
|
u = Upgrader()
|
||||||
src = u.upgrade(tmp)
|
src = u.upgrade(tmp)
|
||||||
srcpath = src.path
|
srcpath = src.path
|
||||||
srcFacts = src.factCount()
|
srcNotes = src.noteCount()
|
||||||
srcCards = src.cardCount()
|
srcCards = src.cardCount()
|
||||||
srcRev = src.db.scalar("select count() from revlog")
|
srcRev = src.db.scalar("select count() from revlog")
|
||||||
# add a media file for testing
|
# add a media file for testing
|
||||||
|
@ -36,14 +36,14 @@ def test_anki2():
|
||||||
imp = Anki2Importer(dst, srcpath)
|
imp = Anki2Importer(dst, srcpath)
|
||||||
imp.run()
|
imp.run()
|
||||||
def check():
|
def check():
|
||||||
assert dst.factCount() == srcFacts
|
assert dst.noteCount() == srcNotes
|
||||||
assert dst.cardCount() == srcCards
|
assert dst.cardCount() == srcCards
|
||||||
assert srcRev == dst.db.scalar("select count() from revlog")
|
assert srcRev == dst.db.scalar("select count() from revlog")
|
||||||
mids = [int(x) for x in dst.models.models.keys()]
|
mids = [int(x) for x in dst.models.models.keys()]
|
||||||
assert not dst.db.scalar(
|
assert not dst.db.scalar(
|
||||||
"select count() from facts where mid not in "+ids2str(mids))
|
"select count() from notes where mid not in "+ids2str(mids))
|
||||||
assert not dst.db.scalar(
|
assert not dst.db.scalar(
|
||||||
"select count() from cards where fid not in (select id from facts)")
|
"select count() from cards where nid not in (select id from notes)")
|
||||||
assert not dst.db.scalar(
|
assert not dst.db.scalar(
|
||||||
"select count() from revlog where cid not in (select id from cards)")
|
"select count() from revlog where cid not in (select id from cards)")
|
||||||
assert dst.fixIntegrity().startswith("Database rebuilt")
|
assert dst.fixIntegrity().startswith("Database rebuilt")
|
||||||
|
@ -68,7 +68,7 @@ def test_anki1():
|
||||||
imp = Anki1Importer(dst, tmp)
|
imp = Anki1Importer(dst, tmp)
|
||||||
imp.run()
|
imp.run()
|
||||||
def check():
|
def check():
|
||||||
assert dst.factCount() == srcFacts
|
assert dst.noteCount() == srcNotes
|
||||||
assert dst.cardCount() == srcCards
|
assert dst.cardCount() == srcCards
|
||||||
assert len(os.listdir(dst.media.dir())) == 1
|
assert len(os.listdir(dst.media.dir())) == 1
|
||||||
check()
|
check()
|
||||||
|
@ -96,9 +96,9 @@ def test_csv_tags():
|
||||||
file = unicode(os.path.join(testDir, "importing/text-tags.txt"))
|
file = unicode(os.path.join(testDir, "importing/text-tags.txt"))
|
||||||
i = csvfile.TextImporter(deck, file)
|
i = csvfile.TextImporter(deck, file)
|
||||||
i.run()
|
i.run()
|
||||||
facts = deck.db.query(Fact).all()
|
notes = deck.db.query(Note).all()
|
||||||
assert len(facts) == 2
|
assert len(notes) == 2
|
||||||
assert facts[0].tags == "baz qux" or facts[1].tags == "baz qux"
|
assert notes[0].tags == "baz qux" or notes[1].tags == "baz qux"
|
||||||
deck.close()
|
deck.close()
|
||||||
|
|
||||||
def test_supermemo_xml_01_unicode():
|
def test_supermemo_xml_01_unicode():
|
||||||
|
|
|
@ -10,10 +10,10 @@ def test_latex():
|
||||||
# change latex cmd to simulate broken build
|
# change latex cmd to simulate broken build
|
||||||
import anki.latex
|
import anki.latex
|
||||||
anki.latex.latexCmd[0] = "nolatex"
|
anki.latex.latexCmd[0] = "nolatex"
|
||||||
# add a fact with latex
|
# add a note with latex
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"[latex]hello[/latex]"
|
f['Front'] = u"[latex]hello[/latex]"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
# but since latex couldn't run, there's nothing there
|
# but since latex couldn't run, there's nothing there
|
||||||
assert len(os.listdir(d.media.dir())) == 0
|
assert len(os.listdir(d.media.dir())) == 0
|
||||||
# check the error message
|
# check the error message
|
||||||
|
@ -31,25 +31,25 @@ def test_latex():
|
||||||
d.media.check()
|
d.media.check()
|
||||||
assert len(os.listdir(d.media.dir())) == 1
|
assert len(os.listdir(d.media.dir())) == 1
|
||||||
assert ".png" in f.cards()[0].q()
|
assert ".png" in f.cards()[0].q()
|
||||||
# adding new facts should cause generation on question display
|
# adding new notes should cause generation on question display
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"[latex]world[/latex]"
|
f['Front'] = u"[latex]world[/latex]"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
f.cards()[0].q()
|
f.cards()[0].q()
|
||||||
assert len(os.listdir(d.media.dir())) == 2
|
assert len(os.listdir(d.media.dir())) == 2
|
||||||
# another fact with the same media should reuse
|
# another note with the same media should reuse
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u" [latex]world[/latex]"
|
f['Front'] = u" [latex]world[/latex]"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
assert len(os.listdir(d.media.dir())) == 2
|
assert len(os.listdir(d.media.dir())) == 2
|
||||||
oldcard = f.cards()[0]
|
oldcard = f.cards()[0]
|
||||||
assert ".png" in oldcard.q()
|
assert ".png" in oldcard.q()
|
||||||
# if we turn off building, then previous cards should work, but cards with
|
# if we turn off building, then previous cards should work, but cards with
|
||||||
# missing media will show the latex
|
# missing media will show the latex
|
||||||
anki.latex.build = False
|
anki.latex.build = False
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"[latex]foo[/latex]"
|
f['Front'] = u"[latex]foo[/latex]"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
assert len(os.listdir(d.media.dir())) == 2
|
assert len(os.listdir(d.media.dir())) == 2
|
||||||
assert stripHTML(f.cards()[0].q()) == "[latex]foo[/latex]"
|
assert stripHTML(f.cards()[0].q()) == "[latex]foo[/latex]"
|
||||||
assert ".png" in oldcard.q()
|
assert ".png" in oldcard.q()
|
||||||
|
|
|
@ -46,14 +46,14 @@ def test_deckIntegration():
|
||||||
# put a file into it
|
# put a file into it
|
||||||
file = unicode(os.path.join(testDir, "support/fake.png"))
|
file = unicode(os.path.join(testDir, "support/fake.png"))
|
||||||
d.media.addFile(file)
|
d.media.addFile(file)
|
||||||
# add a fact which references it
|
# add a note which references it
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"one"; f['Back'] = u"<img src='fake.png'>"
|
f['Front'] = u"one"; f['Back'] = u"<img src='fake.png'>"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
# and one which references a non-existent file
|
# and one which references a non-existent file
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"one"; f['Back'] = u"<img src='fake2.png'>"
|
f['Front'] = u"one"; f['Back'] = u"<img src='fake2.png'>"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
# and add another file which isn't used
|
# and add another file which isn't used
|
||||||
open(os.path.join(d.media.dir(), "foo.jpg"), "wb").write("test")
|
open(os.path.join(d.media.dir(), "foo.jpg"), "wb").write("test")
|
||||||
# check media
|
# check media
|
||||||
|
|
|
@ -5,10 +5,10 @@ from anki.utils import stripHTML
|
||||||
|
|
||||||
def test_modelDelete():
|
def test_modelDelete():
|
||||||
deck = getEmptyDeck()
|
deck = getEmptyDeck()
|
||||||
f = deck.newFact()
|
f = deck.newNote()
|
||||||
f['Front'] = u'1'
|
f['Front'] = u'1'
|
||||||
f['Back'] = u'2'
|
f['Back'] = u'2'
|
||||||
deck.addFact(f)
|
deck.addNote(f)
|
||||||
assert deck.cardCount() == 1
|
assert deck.cardCount() == 1
|
||||||
deck.models.rem(deck.models.current())
|
deck.models.rem(deck.models.current())
|
||||||
assert deck.cardCount() == 0
|
assert deck.cardCount() == 0
|
||||||
|
@ -28,10 +28,10 @@ def test_modelCopy():
|
||||||
|
|
||||||
def test_fields():
|
def test_fields():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u'1'
|
f['Front'] = u'1'
|
||||||
f['Back'] = u'2'
|
f['Back'] = u'2'
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
m = d.models.current()
|
m = d.models.current()
|
||||||
# make sure renaming a field updates the templates
|
# make sure renaming a field updates the templates
|
||||||
d.models.renameField(m, m['flds'][0], "NewFront")
|
d.models.renameField(m, m['flds'][0], "NewFront")
|
||||||
|
@ -41,37 +41,37 @@ def test_fields():
|
||||||
f = d.models.newField(m)
|
f = d.models.newField(m)
|
||||||
f['name'] = "foo"
|
f['name'] = "foo"
|
||||||
d.models.addField(m, f)
|
d.models.addField(m, f)
|
||||||
assert d.getFact(d.models.fids(m)[0]).fields == ["1", "2", ""]
|
assert d.getNote(d.models.nids(m)[0]).fields == ["1", "2", ""]
|
||||||
assert d.models.scmhash(m) != h
|
assert d.models.scmhash(m) != h
|
||||||
# rename it
|
# rename it
|
||||||
d.models.renameField(m, f, "bar")
|
d.models.renameField(m, f, "bar")
|
||||||
assert d.getFact(d.models.fids(m)[0])['bar'] == ''
|
assert d.getNote(d.models.nids(m)[0])['bar'] == ''
|
||||||
# delete back
|
# delete back
|
||||||
d.models.remField(m, m['flds'][1])
|
d.models.remField(m, m['flds'][1])
|
||||||
assert d.getFact(d.models.fids(m)[0]).fields == ["1", ""]
|
assert d.getNote(d.models.nids(m)[0]).fields == ["1", ""]
|
||||||
# move 0 -> 1
|
# move 0 -> 1
|
||||||
d.models.moveField(m, m['flds'][0], 1)
|
d.models.moveField(m, m['flds'][0], 1)
|
||||||
assert d.getFact(d.models.fids(m)[0]).fields == ["", "1"]
|
assert d.getNote(d.models.nids(m)[0]).fields == ["", "1"]
|
||||||
# move 1 -> 0
|
# move 1 -> 0
|
||||||
d.models.moveField(m, m['flds'][1], 0)
|
d.models.moveField(m, m['flds'][1], 0)
|
||||||
assert d.getFact(d.models.fids(m)[0]).fields == ["1", ""]
|
assert d.getNote(d.models.nids(m)[0]).fields == ["1", ""]
|
||||||
# add another and put in middle
|
# add another and put in middle
|
||||||
f = d.models.newField(m)
|
f = d.models.newField(m)
|
||||||
f['name'] = "baz"
|
f['name'] = "baz"
|
||||||
d.models.addField(m, f)
|
d.models.addField(m, f)
|
||||||
f = d.getFact(d.models.fids(m)[0])
|
f = d.getNote(d.models.nids(m)[0])
|
||||||
f['baz'] = "2"
|
f['baz'] = "2"
|
||||||
f.flush()
|
f.flush()
|
||||||
assert d.getFact(d.models.fids(m)[0]).fields == ["1", "", "2"]
|
assert d.getNote(d.models.nids(m)[0]).fields == ["1", "", "2"]
|
||||||
# move 2 -> 1
|
# move 2 -> 1
|
||||||
d.models.moveField(m, m['flds'][2], 1)
|
d.models.moveField(m, m['flds'][2], 1)
|
||||||
assert d.getFact(d.models.fids(m)[0]).fields == ["1", "2", ""]
|
assert d.getNote(d.models.nids(m)[0]).fields == ["1", "2", ""]
|
||||||
# move 0 -> 2
|
# move 0 -> 2
|
||||||
d.models.moveField(m, m['flds'][0], 2)
|
d.models.moveField(m, m['flds'][0], 2)
|
||||||
assert d.getFact(d.models.fids(m)[0]).fields == ["2", "", "1"]
|
assert d.getNote(d.models.nids(m)[0]).fields == ["2", "", "1"]
|
||||||
# move 0 -> 1
|
# move 0 -> 1
|
||||||
d.models.moveField(m, m['flds'][0], 1)
|
d.models.moveField(m, m['flds'][0], 1)
|
||||||
assert d.getFact(d.models.fids(m)[0]).fields == ["", "2", "1"]
|
assert d.getNote(d.models.nids(m)[0]).fields == ["", "2", "1"]
|
||||||
|
|
||||||
def test_templates():
|
def test_templates():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
|
@ -81,10 +81,10 @@ def test_templates():
|
||||||
t['afmt'] = "{{Front}}"
|
t['afmt'] = "{{Front}}"
|
||||||
mm.addTemplate(m, t)
|
mm.addTemplate(m, t)
|
||||||
mm.save(m)
|
mm.save(m)
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u'1'
|
f['Front'] = u'1'
|
||||||
f['Back'] = u'2'
|
f['Back'] = u'2'
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
assert d.cardCount() == 2
|
assert d.cardCount() == 2
|
||||||
(c, c2) = f.cards()
|
(c, c2) = f.cards()
|
||||||
# first card should have first ord
|
# first card should have first ord
|
||||||
|
@ -102,7 +102,7 @@ def test_templates():
|
||||||
c = f.cards()[0]
|
c = f.cards()[0]
|
||||||
assert c.ord == 0
|
assert c.ord == 0
|
||||||
stripHTML(c.q()) == "2"
|
stripHTML(c.q()) == "2"
|
||||||
# it shouldn't be possible to orphan facts by removing templates
|
# it shouldn't be possible to orphan notes by removing templates
|
||||||
assert not d.models.remTemplate(m, m['tmpls'][0])
|
assert not d.models.remTemplate(m, m['tmpls'][0])
|
||||||
|
|
||||||
def test_text():
|
def test_text():
|
||||||
|
@ -110,22 +110,22 @@ def test_text():
|
||||||
m = d.models.current()
|
m = d.models.current()
|
||||||
m['tmpls'][0]['qfmt'] = "{{text:Front}}"
|
m['tmpls'][0]['qfmt'] = "{{text:Front}}"
|
||||||
d.models.save(m)
|
d.models.save(m)
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u'hello<b>world'
|
f['Front'] = u'hello<b>world'
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
assert "helloworld" in f.cards()[0].q()
|
assert "helloworld" in f.cards()[0].q()
|
||||||
|
|
||||||
def test_cloze():
|
def test_cloze():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
d.models.setCurrent(d.models.byName("Cloze"))
|
d.models.setCurrent(d.models.byName("Cloze"))
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
assert f.model()['name'] == "Cloze"
|
assert f.model()['name'] == "Cloze"
|
||||||
# a cloze model with no clozes is empty
|
# a cloze model with no clozes is empty
|
||||||
f['Text'] = u'nothing'
|
f['Text'] = u'nothing'
|
||||||
assert d.addFact(f) == 0
|
assert d.addNote(f) == 0
|
||||||
# try with one cloze
|
# try with one cloze
|
||||||
f['Text'] = "hello {{c1::world}}"
|
f['Text'] = "hello {{c1::world}}"
|
||||||
assert d.addFact(f) == 1
|
assert d.addNote(f) == 1
|
||||||
assert "hello <span class=cloze>[...]</span>" in f.cards()[0].q()
|
assert "hello <span class=cloze>[...]</span>" in f.cards()[0].q()
|
||||||
# the default is no context
|
# the default is no context
|
||||||
assert "<span class=cloze>world</span>" in f.cards()[0].a()
|
assert "<span class=cloze>world</span>" in f.cards()[0].a()
|
||||||
|
@ -134,15 +134,15 @@ def test_cloze():
|
||||||
f.model()['clozectx'] = True
|
f.model()['clozectx'] = True
|
||||||
assert "hello <span class=cloze>world</span>" in f.cards()[0].a()
|
assert "hello <span class=cloze>world</span>" in f.cards()[0].a()
|
||||||
# and with a comment
|
# and with a comment
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Text'] = "hello {{c1::world::typical}}"
|
f['Text'] = "hello {{c1::world::typical}}"
|
||||||
assert d.addFact(f) == 1
|
assert d.addNote(f) == 1
|
||||||
assert "<span class=cloze>[...(typical)]</span>" in f.cards()[0].q()
|
assert "<span class=cloze>[...(typical)]</span>" in f.cards()[0].q()
|
||||||
assert "<span class=cloze>world</span>" in f.cards()[0].a()
|
assert "<span class=cloze>world</span>" in f.cards()[0].a()
|
||||||
# and with 2 clozes
|
# and with 2 clozes
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Text'] = "hello {{c1::world}} {{c2::bar}}"
|
f['Text'] = "hello {{c1::world}} {{c2::bar}}"
|
||||||
assert d.addFact(f) == 2
|
assert d.addNote(f) == 2
|
||||||
(c1, c2) = f.cards()
|
(c1, c2) = f.cards()
|
||||||
assert "<span class=cloze>[...]</span> bar" in c1.q()
|
assert "<span class=cloze>[...]</span> bar" in c1.q()
|
||||||
assert "<span class=cloze>world</span> bar" in c1.a()
|
assert "<span class=cloze>world</span> bar" in c1.a()
|
||||||
|
@ -151,21 +151,21 @@ def test_cloze():
|
||||||
# if there are multiple answers for a single cloze, they are given in a
|
# if there are multiple answers for a single cloze, they are given in a
|
||||||
# list
|
# list
|
||||||
f.model()['clozectx'] = False
|
f.model()['clozectx'] = False
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Text'] = "a {{c1::b}} {{c1::c}}"
|
f['Text'] = "a {{c1::b}} {{c1::c}}"
|
||||||
assert d.addFact(f) == 1
|
assert d.addNote(f) == 1
|
||||||
assert "<span class=cloze>b</span>, <span class=cloze>c</span>" in (
|
assert "<span class=cloze>b</span>, <span class=cloze>c</span>" in (
|
||||||
f.cards()[0].a())
|
f.cards()[0].a())
|
||||||
# clozes should be supported in sections too
|
# clozes should be supported in sections too
|
||||||
m = d.models.current()
|
m = d.models.current()
|
||||||
m['tmpls'][0]['qfmt'] = "{{#cloze:1:Text}}{{Notes}}{{/cloze:1:Text}}"
|
m['tmpls'][0]['qfmt'] = "{{#cloze:1:Text}}{{Notes}}{{/cloze:1:Text}}"
|
||||||
d.models.save(m)
|
d.models.save(m)
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Text'] = "hello"
|
f['Text'] = "hello"
|
||||||
f['Notes'] = "world"
|
f['Notes'] = "world"
|
||||||
assert d.addFact(f) == 0
|
assert d.addNote(f) == 0
|
||||||
f['Text'] = "hello {{c1::foo}}"
|
f['Text'] = "hello {{c1::foo}}"
|
||||||
assert d.addFact(f) == 1
|
assert d.addNote(f) == 1
|
||||||
# deleting a cloze should fail; the ui should clean up invalid cards
|
# deleting a cloze should fail; the ui should clean up invalid cards
|
||||||
cnt = d.cardCount()
|
cnt = d.cardCount()
|
||||||
f['Text'] = "hello"
|
f['Text'] = "hello"
|
||||||
|
@ -181,17 +181,17 @@ def test_modelChange():
|
||||||
deck = getEmptyDeck()
|
deck = getEmptyDeck()
|
||||||
basic = deck.models.byName("Basic")
|
basic = deck.models.byName("Basic")
|
||||||
cloze = deck.models.byName("Cloze")
|
cloze = deck.models.byName("Cloze")
|
||||||
# enable second template and add a fact
|
# enable second template and add a note
|
||||||
m = deck.models.current(); mm = deck.models
|
m = deck.models.current(); mm = deck.models
|
||||||
t = mm.newTemplate("Reverse")
|
t = mm.newTemplate("Reverse")
|
||||||
t['qfmt'] = "{{Back}}"
|
t['qfmt'] = "{{Back}}"
|
||||||
t['afmt'] = "{{Front}}"
|
t['afmt'] = "{{Front}}"
|
||||||
mm.addTemplate(m, t)
|
mm.addTemplate(m, t)
|
||||||
mm.save(m)
|
mm.save(m)
|
||||||
f = deck.newFact()
|
f = deck.newNote()
|
||||||
f['Front'] = u'f'
|
f['Front'] = u'f'
|
||||||
f['Back'] = u'b'
|
f['Back'] = u'b'
|
||||||
deck.addFact(f)
|
deck.addNote(f)
|
||||||
# switch fields
|
# switch fields
|
||||||
map = {0: 1, 1: 0}
|
map = {0: 1, 1: 0}
|
||||||
deck.models.change(basic, [f.id], basic, map, None)
|
deck.models.change(basic, [f.id], basic, map, None)
|
||||||
|
@ -231,11 +231,11 @@ def test_modelChange():
|
||||||
f.load()
|
f.load()
|
||||||
assert f['Front'] == ''
|
assert f['Front'] == ''
|
||||||
assert f['Back'] == 'f'
|
assert f['Back'] == 'f'
|
||||||
# another fact to try model conversion
|
# another note to try model conversion
|
||||||
f = deck.newFact()
|
f = deck.newNote()
|
||||||
f['Front'] = u'f2'
|
f['Front'] = u'f2'
|
||||||
f['Back'] = u'b2'
|
f['Back'] = u'b2'
|
||||||
deck.addFact(f)
|
deck.addNote(f)
|
||||||
assert deck.models.useCount(basic) == 2
|
assert deck.models.useCount(basic) == 2
|
||||||
assert deck.models.useCount(cloze) == 0
|
assert deck.models.useCount(cloze) == 0
|
||||||
map = {0: 0, 1: 1}
|
map = {0: 0, 1: 1}
|
||||||
|
|
|
@ -8,7 +8,7 @@ from anki import Deck
|
||||||
from anki.utils import intTime
|
from anki.utils import intTime
|
||||||
from anki.sync import Syncer, FullSyncer, LocalServer, RemoteServer, \
|
from anki.sync import Syncer, FullSyncer, LocalServer, RemoteServer, \
|
||||||
MediaSyncer, RemoteMediaServer
|
MediaSyncer, RemoteMediaServer
|
||||||
from anki.facts import Fact
|
from anki.notes import Note
|
||||||
from anki.cards import Card
|
from anki.cards import Card
|
||||||
from tests.shared import getEmptyDeck
|
from tests.shared import getEmptyDeck
|
||||||
|
|
||||||
|
|
|
@ -13,10 +13,10 @@ def test_basics():
|
||||||
def test_new():
|
def test_new():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
assert d.sched.newCount == 0
|
assert d.sched.newCount == 0
|
||||||
# add a fact
|
# add a note
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"one"; f['Back'] = u"two"
|
f['Front'] = u"one"; f['Back'] = u"two"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
d.reset()
|
d.reset()
|
||||||
assert d.sched.newCount == 1
|
assert d.sched.newCount == 1
|
||||||
# fetch it
|
# fetch it
|
||||||
|
@ -38,12 +38,12 @@ def test_new():
|
||||||
t['afmt'] = "{{Front}}"
|
t['afmt'] = "{{Front}}"
|
||||||
mm.addTemplate(m, t)
|
mm.addTemplate(m, t)
|
||||||
mm.save(m)
|
mm.save(m)
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"2"; f['Back'] = u"2"
|
f['Front'] = u"2"; f['Back'] = u"2"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"3"; f['Back'] = u"3"
|
f['Front'] = u"3"; f['Back'] = u"3"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
d.reset()
|
d.reset()
|
||||||
qs = ("2", "3", "2", "3")
|
qs = ("2", "3", "2", "3")
|
||||||
for n in range(4):
|
for n in range(4):
|
||||||
|
@ -53,14 +53,14 @@ def test_new():
|
||||||
|
|
||||||
def test_newLimits():
|
def test_newLimits():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
# add some facts
|
# add some notes
|
||||||
g2 = d.groups.id("Default::foo")
|
g2 = d.groups.id("Default::foo")
|
||||||
for i in range(30):
|
for i in range(30):
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = str(i)
|
f['Front'] = str(i)
|
||||||
if i > 4:
|
if i > 4:
|
||||||
f.gid = g2
|
f.gid = g2
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
# give the child group a different configuration
|
# give the child group a different configuration
|
||||||
c2 = d.groups.confId("new conf")
|
c2 = d.groups.confId("new conf")
|
||||||
d.groups.setConf(d.groups.get(g2), c2)
|
d.groups.setConf(d.groups.get(g2), c2)
|
||||||
|
@ -92,11 +92,11 @@ def test_newOrder():
|
||||||
t['actv'] = i > 25
|
t['actv'] = i > 25
|
||||||
d.models.addTemplate(m, t)
|
d.models.addTemplate(m, t)
|
||||||
d.models.save(m)
|
d.models.save(m)
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u'1'
|
f['Front'] = u'1'
|
||||||
f['Back'] = u'2'
|
f['Back'] = u'2'
|
||||||
# add first half
|
# add first half
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
# generate second half
|
# generate second half
|
||||||
d.db.execute("update cards set gid = random()")
|
d.db.execute("update cards set gid = random()")
|
||||||
d.conf['newPerDay'] = 100
|
d.conf['newPerDay'] = 100
|
||||||
|
@ -106,9 +106,9 @@ def test_newOrder():
|
||||||
|
|
||||||
def test_newBoxes():
|
def test_newBoxes():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"one"
|
f['Front'] = u"one"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
d.reset()
|
d.reset()
|
||||||
c = d.sched.getCard()
|
c = d.sched.getCard()
|
||||||
d.sched._cardConf(c)['new']['delays'] = [1,2,3,4,5]
|
d.sched._cardConf(c)['new']['delays'] = [1,2,3,4,5]
|
||||||
|
@ -119,10 +119,10 @@ def test_newBoxes():
|
||||||
|
|
||||||
def test_learn():
|
def test_learn():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
# add a fact
|
# add a note
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"one"; f['Back'] = u"two"
|
f['Front'] = u"one"; f['Back'] = u"two"
|
||||||
f = d.addFact(f)
|
f = d.addNote(f)
|
||||||
# set as a learn card and rebuild queues
|
# set as a learn card and rebuild queues
|
||||||
d.db.execute("update cards set queue=0, type=0")
|
d.db.execute("update cards set queue=0, type=0")
|
||||||
d.reset()
|
d.reset()
|
||||||
|
@ -191,10 +191,10 @@ def test_learn():
|
||||||
|
|
||||||
def test_reviews():
|
def test_reviews():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
# add a fact
|
# add a note
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"one"; f['Back'] = u"two"
|
f['Front'] = u"one"; f['Back'] = u"two"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
# set the card up as a review card, due 8 days ago
|
# set the card up as a review card, due 8 days ago
|
||||||
c = f.cards()[0]
|
c = f.cards()[0]
|
||||||
c.type = 2
|
c.type = 2
|
||||||
|
@ -286,9 +286,9 @@ def test_finished():
|
||||||
# nothing due
|
# nothing due
|
||||||
assert "Congratulations" in d.sched.finishedMsg()
|
assert "Congratulations" in d.sched.finishedMsg()
|
||||||
assert "limit" not in d.sched.finishedMsg()
|
assert "limit" not in d.sched.finishedMsg()
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"one"; f['Back'] = u"two"
|
f['Front'] = u"one"; f['Back'] = u"two"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
# have a new card
|
# have a new card
|
||||||
assert "new cards available" in d.sched.finishedMsg()
|
assert "new cards available" in d.sched.finishedMsg()
|
||||||
# turn it into a review
|
# turn it into a review
|
||||||
|
@ -301,9 +301,9 @@ def test_finished():
|
||||||
|
|
||||||
def test_nextIvl():
|
def test_nextIvl():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"one"; f['Back'] = u"two"
|
f['Front'] = u"one"; f['Back'] = u"two"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
d.reset()
|
d.reset()
|
||||||
c = d.sched.getCard()
|
c = d.sched.getCard()
|
||||||
d.sched._cardConf(c)['new']['delays'] = [0.5, 3, 10]
|
d.sched._cardConf(c)['new']['delays'] = [0.5, 3, 10]
|
||||||
|
@ -356,12 +356,12 @@ def test_nextIvl():
|
||||||
|
|
||||||
def test_misc():
|
def test_misc():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"one"
|
f['Front'] = u"one"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
c = f.cards()[0]
|
c = f.cards()[0]
|
||||||
# burying
|
# burying
|
||||||
d.sched.buryFact(c.fid)
|
d.sched.buryNote(c.nid)
|
||||||
d.reset()
|
d.reset()
|
||||||
assert not d.sched.getCard()
|
assert not d.sched.getCard()
|
||||||
d.sched.onClose()
|
d.sched.onClose()
|
||||||
|
@ -370,9 +370,9 @@ def test_misc():
|
||||||
|
|
||||||
def test_suspend():
|
def test_suspend():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"one"
|
f['Front'] = u"one"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
c = f.cards()[0]
|
c = f.cards()[0]
|
||||||
# suspending
|
# suspending
|
||||||
d.reset()
|
d.reset()
|
||||||
|
@ -403,9 +403,9 @@ def test_cram():
|
||||||
print "disabled for now"
|
print "disabled for now"
|
||||||
return
|
return
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"one"
|
f['Front'] = u"one"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
c = f.cards()[0]
|
c = f.cards()[0]
|
||||||
c.ivl = 100
|
c.ivl = 100
|
||||||
c.type = c.queue = 2
|
c.type = c.queue = 2
|
||||||
|
@ -485,9 +485,9 @@ def test_cramLimits():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
# create three cards, due tomorrow, the next, etc
|
# create three cards, due tomorrow, the next, etc
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = str(i)
|
f['Front'] = str(i)
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
c = f.cards()[0]
|
c = f.cards()[0]
|
||||||
c.type = c.queue = 2
|
c.type = c.queue = 2
|
||||||
c.due = d.sched.today + 1 + i
|
c.due = d.sched.today + 1 + i
|
||||||
|
@ -539,10 +539,10 @@ def test_adjIvl():
|
||||||
t['afmt'] = "{{Back}}"
|
t['afmt'] = "{{Back}}"
|
||||||
d.models.addTemplate(m, t)
|
d.models.addTemplate(m, t)
|
||||||
d.models.save(m)
|
d.models.save(m)
|
||||||
# create a new fact; it should have 4 cards
|
# create a new note; it should have 4 cards
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = "1"; f['Back'] = "1"
|
f['Front'] = "1"; f['Back'] = "1"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
assert d.cardCount() == 4
|
assert d.cardCount() == 4
|
||||||
d.reset()
|
d.reset()
|
||||||
# immediately remove first; it should get ideal ivl
|
# immediately remove first; it should get ideal ivl
|
||||||
|
@ -561,10 +561,10 @@ def test_adjIvl():
|
||||||
c = d.sched.getCard()
|
c = d.sched.getCard()
|
||||||
d.sched.answerCard(c, 3)
|
d.sched.answerCard(c, 3)
|
||||||
assert c.ivl == 4
|
assert c.ivl == 4
|
||||||
# try again with another fact
|
# try again with another note
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = "2"; f['Back'] = "2"
|
f['Front'] = "2"; f['Back'] = "2"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
d.reset()
|
d.reset()
|
||||||
# set a minSpacing of 0
|
# set a minSpacing of 0
|
||||||
conf = d.sched._cardConf(c)
|
conf = d.sched._cardConf(c)
|
||||||
|
@ -601,10 +601,10 @@ def test_ordcycle():
|
||||||
t['afmt'] = "{{Back}}"
|
t['afmt'] = "{{Back}}"
|
||||||
mm.addTemplate(m, t)
|
mm.addTemplate(m, t)
|
||||||
mm.save(m)
|
mm.save(m)
|
||||||
# create a new fact; it should have 3 cards
|
# create a new note; it should have 3 cards
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = "1"; f['Back'] = "1"
|
f['Front'] = "1"; f['Back'] = "1"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
assert d.cardCount() == 3
|
assert d.cardCount() == 3
|
||||||
d.reset()
|
d.reset()
|
||||||
# ordinals should arrive in order
|
# ordinals should arrive in order
|
||||||
|
@ -620,10 +620,10 @@ def test_cardcounts():
|
||||||
for type in range(3):
|
for type in range(3):
|
||||||
# and each of the groups
|
# and each of the groups
|
||||||
for gid in (1,grp):
|
for gid in (1,grp):
|
||||||
# create a new fact
|
# create a new note
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"one"
|
f['Front'] = u"one"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
c = f.cards()[0]
|
c = f.cards()[0]
|
||||||
# set type/gid
|
# set type/gid
|
||||||
c.type = type
|
c.type = type
|
||||||
|
@ -641,9 +641,9 @@ def test_cardcounts():
|
||||||
|
|
||||||
def test_counts_idx():
|
def test_counts_idx():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"one"; f['Back'] = u"two"
|
f['Front'] = u"one"; f['Back'] = u"two"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
d.reset()
|
d.reset()
|
||||||
assert d.sched.cardCounts() == (1, 0, 0)
|
assert d.sched.cardCounts() == (1, 0, 0)
|
||||||
c = d.sched.getCard()
|
c = d.sched.getCard()
|
||||||
|
@ -663,9 +663,9 @@ def test_counts_idx():
|
||||||
|
|
||||||
def test_repCounts():
|
def test_repCounts():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"one"
|
f['Front'] = u"one"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
d.reset()
|
d.reset()
|
||||||
# lrnReps should be accurate on pass/fail
|
# lrnReps should be accurate on pass/fail
|
||||||
assert d.sched.repCounts() == (1, 0, 0)
|
assert d.sched.repCounts() == (1, 0, 0)
|
||||||
|
@ -681,9 +681,9 @@ def test_repCounts():
|
||||||
assert d.sched.repCounts() == (0, 1, 0)
|
assert d.sched.repCounts() == (0, 1, 0)
|
||||||
d.sched.answerCard(d.sched.getCard(), 2)
|
d.sched.answerCard(d.sched.getCard(), 2)
|
||||||
assert d.sched.repCounts() == (0, 0, 0)
|
assert d.sched.repCounts() == (0, 0, 0)
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"two"
|
f['Front'] = u"two"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
d.reset()
|
d.reset()
|
||||||
# initial pass should be correct too
|
# initial pass should be correct too
|
||||||
d.sched.answerCard(d.sched.getCard(), 2)
|
d.sched.answerCard(d.sched.getCard(), 2)
|
||||||
|
@ -693,16 +693,16 @@ def test_repCounts():
|
||||||
d.sched.answerCard(d.sched.getCard(), 3)
|
d.sched.answerCard(d.sched.getCard(), 3)
|
||||||
assert d.sched.repCounts() == (0, 0, 0)
|
assert d.sched.repCounts() == (0, 0, 0)
|
||||||
# immediate graduate should work
|
# immediate graduate should work
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"three"
|
f['Front'] = u"three"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
d.reset()
|
d.reset()
|
||||||
d.sched.answerCard(d.sched.getCard(), 3)
|
d.sched.answerCard(d.sched.getCard(), 3)
|
||||||
assert d.sched.repCounts() == (0, 0, 0)
|
assert d.sched.repCounts() == (0, 0, 0)
|
||||||
# and failing a review should too
|
# and failing a review should too
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"three"
|
f['Front'] = u"three"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
c = f.cards()[0]
|
c = f.cards()[0]
|
||||||
c.type = 2
|
c.type = 2
|
||||||
c.queue = 2
|
c.queue = 2
|
||||||
|
@ -717,9 +717,9 @@ def test_timing():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
# add a few review cards, due today
|
# add a few review cards, due today
|
||||||
for i in range(5):
|
for i in range(5):
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = "num"+str(i)
|
f['Front'] = "num"+str(i)
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
c = f.cards()[0]
|
c = f.cards()[0]
|
||||||
c.type = 2
|
c.type = 2
|
||||||
c.queue = 2
|
c.queue = 2
|
||||||
|
@ -741,10 +741,10 @@ def test_timing():
|
||||||
|
|
||||||
def test_collapse():
|
def test_collapse():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
# add a fact
|
# add a note
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"one"
|
f['Front'] = u"one"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
d.reset()
|
d.reset()
|
||||||
# test collapsing
|
# test collapsing
|
||||||
c = d.sched.getCard()
|
c = d.sched.getCard()
|
||||||
|
@ -755,30 +755,30 @@ def test_collapse():
|
||||||
|
|
||||||
def test_groupCounts():
|
def test_groupCounts():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
# add a fact with default group
|
# add a note with default group
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"one"
|
f['Front'] = u"one"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
# and one that's a child
|
# and one that's a child
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"two"
|
f['Front'] = u"two"
|
||||||
default1 = f.gid = d.groups.id("Default::1")
|
default1 = f.gid = d.groups.id("Default::1")
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
# make it a review card
|
# make it a review card
|
||||||
c = f.cards()[0]
|
c = f.cards()[0]
|
||||||
c.queue = 2
|
c.queue = 2
|
||||||
c.due = 0
|
c.due = 0
|
||||||
c.flush()
|
c.flush()
|
||||||
# add one more with a new group
|
# add one more with a new group
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"two"
|
f['Front'] = u"two"
|
||||||
foobar = f.gid = d.groups.id("foo::bar")
|
foobar = f.gid = d.groups.id("foo::bar")
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
# and one that's a sibling
|
# and one that's a sibling
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"three"
|
f['Front'] = u"three"
|
||||||
foobaz = f.gid = d.groups.id("foo::baz")
|
foobaz = f.gid = d.groups.id("foo::baz")
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
d.reset()
|
d.reset()
|
||||||
assert len(d.groups.groups) == 5
|
assert len(d.groups.groups) == 5
|
||||||
cnts = d.sched.groupCounts()
|
cnts = d.sched.groupCounts()
|
||||||
|
@ -815,37 +815,37 @@ def test_groupTree():
|
||||||
|
|
||||||
def test_groupFlow():
|
def test_groupFlow():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
# add a fact with default group
|
# add a note with default group
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"one"
|
f['Front'] = u"one"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
# and one that's a child
|
# and one that's a child
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"two"
|
f['Front'] = u"two"
|
||||||
default1 = f.gid = d.groups.id("Default::2")
|
default1 = f.gid = d.groups.id("Default::2")
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
# and another that's higher up
|
# and another that's higher up
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"three"
|
f['Front'] = u"three"
|
||||||
default1 = f.gid = d.groups.id("Default::1")
|
default1 = f.gid = d.groups.id("Default::1")
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
# should get top level one first, then ::1, then ::2
|
# should get top level one first, then ::1, then ::2
|
||||||
d.reset()
|
d.reset()
|
||||||
assert d.sched.cardCounts() == (3,0,0)
|
assert d.sched.cardCounts() == (3,0,0)
|
||||||
for i in "one", "three", "two":
|
for i in "one", "three", "two":
|
||||||
c = d.sched.getCard()
|
c = d.sched.getCard()
|
||||||
assert c.fact()['Front'] == i
|
assert c.note()['Front'] == i
|
||||||
d.sched.answerCard(c, 2)
|
d.sched.answerCard(c, 2)
|
||||||
|
|
||||||
def test_reorder():
|
def test_reorder():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
# add a fact with default group
|
# add a note with default group
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"one"
|
f['Front'] = u"one"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
f2 = d.newFact()
|
f2 = d.newNote()
|
||||||
f2['Front'] = u"two"
|
f2['Front'] = u"two"
|
||||||
d.addFact(f2)
|
d.addNote(f2)
|
||||||
assert f2.cards()[0].due == 2
|
assert f2.cards()[0].due == 2
|
||||||
found=False
|
found=False
|
||||||
# 50/50 chance of being reordered
|
# 50/50 chance of being reordered
|
||||||
|
@ -858,12 +858,12 @@ def test_reorder():
|
||||||
d.sched.orderCards()
|
d.sched.orderCards()
|
||||||
assert f.cards()[0].due == 1
|
assert f.cards()[0].due == 1
|
||||||
# shifting
|
# shifting
|
||||||
f3 = d.newFact()
|
f3 = d.newNote()
|
||||||
f3['Front'] = u"three"
|
f3['Front'] = u"three"
|
||||||
d.addFact(f3)
|
d.addNote(f3)
|
||||||
f4 = d.newFact()
|
f4 = d.newNote()
|
||||||
f4['Front'] = u"four"
|
f4['Front'] = u"four"
|
||||||
d.addFact(f4)
|
d.addNote(f4)
|
||||||
assert f.cards()[0].due == 1
|
assert f.cards()[0].due == 1
|
||||||
assert f2.cards()[0].due == 2
|
assert f2.cards()[0].due == 2
|
||||||
assert f3.cards()[0].due == 3
|
assert f3.cards()[0].due == 3
|
||||||
|
@ -877,9 +877,9 @@ def test_reorder():
|
||||||
|
|
||||||
def test_forget():
|
def test_forget():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"one"
|
f['Front'] = u"one"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
c = f.cards()[0]
|
c = f.cards()[0]
|
||||||
c.queue = 2; c.type = 2; c.ivl = 100; c.due = 0
|
c.queue = 2; c.type = 2; c.ivl = 100; c.due = 0
|
||||||
c.flush()
|
c.flush()
|
||||||
|
@ -891,9 +891,9 @@ def test_forget():
|
||||||
|
|
||||||
def test_resched():
|
def test_resched():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"one"
|
f['Front'] = u"one"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
c = f.cards()[0]
|
c = f.cards()[0]
|
||||||
d.sched.reschedCards([c.id], 0, 0)
|
d.sched.reschedCards([c.id], 0, 0)
|
||||||
c.load()
|
c.load()
|
||||||
|
@ -908,9 +908,9 @@ def test_resched():
|
||||||
def test_revlim():
|
def test_revlim():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
for i in range(20):
|
for i in range(20):
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = str(i)
|
f['Front'] = str(i)
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
d.db.execute("update cards set due = 0, queue = 2, type = 2")
|
d.db.execute("update cards set due = 0, queue = 2, type = 2")
|
||||||
d.reset()
|
d.reset()
|
||||||
assert d.sched.repCounts()[2] == 20
|
assert d.sched.repCounts()[2] == 20
|
||||||
|
|
|
@ -7,9 +7,9 @@ from anki.hooks import addHook
|
||||||
|
|
||||||
def test_stats():
|
def test_stats():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = "foo"
|
f['Front'] = "foo"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
c = f.cards()[0]
|
c = f.cards()[0]
|
||||||
# card stats
|
# card stats
|
||||||
assert d.cardStats(c)
|
assert d.cardStats(c)
|
||||||
|
|
|
@ -8,7 +8,7 @@ from anki import Deck
|
||||||
from anki.utils import intTime
|
from anki.utils import intTime
|
||||||
from anki.sync import Syncer, FullSyncer, LocalServer, RemoteServer, \
|
from anki.sync import Syncer, FullSyncer, LocalServer, RemoteServer, \
|
||||||
MediaSyncer, RemoteMediaServer
|
MediaSyncer, RemoteMediaServer
|
||||||
from anki.facts import Fact
|
from anki.notes import Note
|
||||||
from anki.cards import Card
|
from anki.cards import Card
|
||||||
from tests.shared import getEmptyDeck
|
from tests.shared import getEmptyDeck
|
||||||
|
|
||||||
|
@ -24,17 +24,17 @@ server2=None
|
||||||
def setup_basic():
|
def setup_basic():
|
||||||
global deck1, deck2, client, server
|
global deck1, deck2, client, server
|
||||||
deck1 = getEmptyDeck()
|
deck1 = getEmptyDeck()
|
||||||
# add a fact to deck 1
|
# add a note to deck 1
|
||||||
f = deck1.newFact()
|
f = deck1.newNote()
|
||||||
f['Front'] = u"foo"; f['Back'] = u"bar"; f.tags = [u"foo"]
|
f['Front'] = u"foo"; f['Back'] = u"bar"; f.tags = [u"foo"]
|
||||||
deck1.addFact(f)
|
deck1.addNote(f)
|
||||||
# answer it
|
# answer it
|
||||||
deck1.reset(); deck1.sched.answerCard(deck1.sched.getCard(), 4)
|
deck1.reset(); deck1.sched.answerCard(deck1.sched.getCard(), 4)
|
||||||
# repeat for deck2
|
# repeat for deck2
|
||||||
deck2 = getEmptyDeck(server=True)
|
deck2 = getEmptyDeck(server=True)
|
||||||
f = deck2.newFact()
|
f = deck2.newNote()
|
||||||
f['Front'] = u"bar"; f['Back'] = u"bar"; f.tags = [u"bar"]
|
f['Front'] = u"bar"; f['Back'] = u"bar"; f.tags = [u"bar"]
|
||||||
deck2.addFact(f)
|
deck2.addNote(f)
|
||||||
deck2.reset(); deck2.sched.answerCard(deck2.sched.getCard(), 4)
|
deck2.reset(); deck2.sched.answerCard(deck2.sched.getCard(), 4)
|
||||||
# start with same schema and sync time
|
# start with same schema and sync time
|
||||||
deck1.scm = deck2.scm = 0
|
deck1.scm = deck2.scm = 0
|
||||||
|
@ -62,7 +62,7 @@ def test_changedSchema():
|
||||||
def test_sync():
|
def test_sync():
|
||||||
def check(num):
|
def check(num):
|
||||||
for d in deck1, deck2:
|
for d in deck1, deck2:
|
||||||
for t in ("revlog", "facts", "cards", "fsums"):
|
for t in ("revlog", "notes", "cards", "nsums"):
|
||||||
assert d.db.scalar("select count() from %s" % t) == num
|
assert d.db.scalar("select count() from %s" % t) == num
|
||||||
assert len(d.models.all()) == num*2
|
assert len(d.models.all()) == num*2
|
||||||
# the default group and config have an id of 1, so always 1
|
# the default group and config have an id of 1, so always 1
|
||||||
|
@ -107,31 +107,31 @@ def test_models():
|
||||||
assert client.sync() == "fullSync"
|
assert client.sync() == "fullSync"
|
||||||
|
|
||||||
@nose.with_setup(setup_modified)
|
@nose.with_setup(setup_modified)
|
||||||
def test_facts():
|
def test_notes():
|
||||||
test_sync()
|
test_sync()
|
||||||
# modifications should be synced
|
# modifications should be synced
|
||||||
fid = deck1.db.scalar("select id from facts")
|
nid = deck1.db.scalar("select id from notes")
|
||||||
fact = deck1.getFact(fid)
|
note = deck1.getNote(nid)
|
||||||
assert fact['Front'] != "abc"
|
assert note['Front'] != "abc"
|
||||||
fact['Front'] = "abc"
|
note['Front'] = "abc"
|
||||||
fact.flush()
|
note.flush()
|
||||||
deck1.save()
|
deck1.save()
|
||||||
assert client.sync() == "success"
|
assert client.sync() == "success"
|
||||||
assert deck2.getFact(fid)['Front'] == "abc"
|
assert deck2.getNote(nid)['Front'] == "abc"
|
||||||
# deletions too
|
# deletions too
|
||||||
assert deck1.db.scalar("select 1 from facts where id = ?", fid)
|
assert deck1.db.scalar("select 1 from notes where id = ?", nid)
|
||||||
deck1.remFacts([fid])
|
deck1.remNotes([nid])
|
||||||
deck1.save()
|
deck1.save()
|
||||||
assert client.sync() == "success"
|
assert client.sync() == "success"
|
||||||
assert not deck1.db.scalar("select 1 from facts where id = ?", fid)
|
assert not deck1.db.scalar("select 1 from notes where id = ?", nid)
|
||||||
assert not deck2.db.scalar("select 1 from facts where id = ?", fid)
|
assert not deck2.db.scalar("select 1 from notes where id = ?", nid)
|
||||||
|
|
||||||
@nose.with_setup(setup_modified)
|
@nose.with_setup(setup_modified)
|
||||||
def test_cards():
|
def test_cards():
|
||||||
test_sync()
|
test_sync()
|
||||||
fid = deck1.db.scalar("select id from facts")
|
nid = deck1.db.scalar("select id from notes")
|
||||||
fact = deck1.getFact(fid)
|
note = deck1.getNote(nid)
|
||||||
card = fact.cards()[0]
|
card = note.cards()[0]
|
||||||
# answer the card locally
|
# answer the card locally
|
||||||
card.startTimer()
|
card.startTimer()
|
||||||
deck1.sched.answerCard(card, 4)
|
deck1.sched.answerCard(card, 4)
|
||||||
|
@ -215,26 +215,26 @@ def test_threeway():
|
||||||
assert client2.sync() == "noChanges"
|
assert client2.sync() == "noChanges"
|
||||||
# client 1 adds a card at time 1
|
# client 1 adds a card at time 1
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
f = deck1.newFact()
|
f = deck1.newNote()
|
||||||
f['Front'] = u"1";
|
f['Front'] = u"1";
|
||||||
deck1.addFact(f)
|
deck1.addNote(f)
|
||||||
deck1.save()
|
deck1.save()
|
||||||
# at time 2, client 2 syncs to server
|
# at time 2, client 2 syncs to server
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
deck3.save()
|
deck3.save()
|
||||||
assert client2.sync() == "success"
|
assert client2.sync() == "success"
|
||||||
# at time 3, client 1 syncs, adding the older fact
|
# at time 3, client 1 syncs, adding the older note
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
assert client.sync() == "success"
|
assert client.sync() == "success"
|
||||||
assert deck1.factCount() == deck2.factCount()
|
assert deck1.noteCount() == deck2.noteCount()
|
||||||
# syncing client2 should pick it up
|
# syncing client2 should pick it up
|
||||||
assert client2.sync() == "success"
|
assert client2.sync() == "success"
|
||||||
assert deck1.factCount() == deck2.factCount() == deck3.factCount()
|
assert deck1.noteCount() == deck2.noteCount() == deck3.noteCount()
|
||||||
|
|
||||||
def _test_speed():
|
def _test_speed():
|
||||||
t = time.time()
|
t = time.time()
|
||||||
deck1 = Deck(os.path.expanduser("~/rapid.anki"))
|
deck1 = Deck(os.path.expanduser("~/rapid.anki"))
|
||||||
for tbl in "revlog", "cards", "facts", "graves":
|
for tbl in "revlog", "cards", "notes", "graves":
|
||||||
deck1.db.execute("update %s set usn = -1 where usn != -1"%tbl)
|
deck1.db.execute("update %s set usn = -1 where usn != -1"%tbl)
|
||||||
for m in deck1.models.all():
|
for m in deck1.models.all():
|
||||||
m['usn'] = -1
|
m['usn'] = -1
|
||||||
|
|
|
@ -26,9 +26,9 @@ def test_op():
|
||||||
assert not d.undoName()
|
assert not d.undoName()
|
||||||
# and a review will, too
|
# and a review will, too
|
||||||
d.save("add")
|
d.save("add")
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"one"
|
f['Front'] = u"one"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
d.reset()
|
d.reset()
|
||||||
assert d.undoName() == "add"
|
assert d.undoName() == "add"
|
||||||
c = d.sched.getCard()
|
c = d.sched.getCard()
|
||||||
|
@ -38,9 +38,9 @@ def test_op():
|
||||||
def test_review():
|
def test_review():
|
||||||
d = getEmptyDeck()
|
d = getEmptyDeck()
|
||||||
d.conf['counts'] = COUNT_REMAINING
|
d.conf['counts'] = COUNT_REMAINING
|
||||||
f = d.newFact()
|
f = d.newNote()
|
||||||
f['Front'] = u"one"
|
f['Front'] = u"one"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
d.reset()
|
d.reset()
|
||||||
assert not d.undoName()
|
assert not d.undoName()
|
||||||
# answer
|
# answer
|
||||||
|
@ -62,7 +62,7 @@ def test_review():
|
||||||
assert not d.undoName()
|
assert not d.undoName()
|
||||||
# we should be able to undo multiple answers too
|
# we should be able to undo multiple answers too
|
||||||
f['Front'] = u"two"
|
f['Front'] = u"two"
|
||||||
d.addFact(f)
|
d.addNote(f)
|
||||||
d.reset()
|
d.reset()
|
||||||
assert d.sched.cardCounts() == (2, 0, 0)
|
assert d.sched.cardCounts() == (2, 0, 0)
|
||||||
c = d.sched.getCard()
|
c = d.sched.getCard()
|
||||||
|
|
Loading…
Reference in a new issue