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