facts -> notes

This commit is contained in:
Damien Elmes 2011-11-23 12:37:21 +09:00
parent 10e1c1b03e
commit 6e4e8249fb
33 changed files with 638 additions and 638 deletions

View file

@ -27,10 +27,10 @@ Refresh after a change:
Edit the card: Edit the card:
fact = card.fact() note = card.note()
for (name, value) in fact.items(): for (name, value) in note.items():
fact[name] = value + " new" note[name] = value + " new"
fact.flush() note.flush()
Save & close: Save & close:

View file

@ -12,7 +12,7 @@ from anki.utils import intTime, hexifyID, timestampID
# Queue: same as above, and: # Queue: same as above, and:
# -1=suspended, -2=user buried, -3=sched buried # -1=suspended, -2=user buried, -3=sched buried
# Due is used differently for different queues. # Due is used differently for different queues.
# - new queue: fact id or random int # - new queue: note id or random int
# - rev queue: integer day # - rev queue: integer day
# - lrn queue: integer timestamp # - lrn queue: integer timestamp
@ -27,7 +27,7 @@ class Card(object):
self.id = id self.id = id
self.load() self.load()
else: else:
# to flush, set fid, ord, and due # to flush, set nid, ord, and due
self.id = timestampID(deck.db, "cards") self.id = timestampID(deck.db, "cards")
self.gid = 1 self.gid = 1
self.crt = intTime() self.crt = intTime()
@ -44,7 +44,7 @@ class Card(object):
def load(self): def load(self):
(self.id, (self.id,
self.fid, self.nid,
self.gid, self.gid,
self.ord, self.ord,
self.mod, self.mod,
@ -72,7 +72,7 @@ class Card(object):
insert or replace into cards values insert or replace into cards values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""", (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
self.id, self.id,
self.fid, self.nid,
self.gid, self.gid,
self.ord, self.ord,
self.mod, self.mod,
@ -108,21 +108,21 @@ lapses=?, left=?, edue=? where id = ?""",
def _getQA(self, reload=False): def _getQA(self, reload=False):
if not self._qa or reload: if not self._qa or reload:
f = self.fact(); m = self.model() f = self.note(); m = self.model()
data = [self.id, f.id, m['id'], self.gid, self.ord, f.stringTags(), data = [self.id, f.id, m['id'], self.gid, self.ord, f.stringTags(),
f.joinedFields()] f.joinedFields()]
self._qa = self.deck._renderQA(data) self._qa = self.deck._renderQA(data)
return self._qa return self._qa
def _reviewData(self, reload=False): def _reviewData(self, reload=False):
"Fetch the model and fact." "Fetch the model and note."
if not self._rd or reload: if not self._rd or reload:
f = self.deck.getFact(self.fid) f = self.deck.getNote(self.nid)
m = self.deck.models.get(f.mid) m = self.deck.models.get(f.mid)
self._rd = [f, m] self._rd = [f, m]
return self._rd return self._rd
def fact(self): def note(self):
return self._reviewData()[0] return self._reviewData()[0]
def model(self, reload=False): def model(self, reload=False):

View file

@ -24,7 +24,7 @@ REV_CARDS_NEW_FIRST = 2
# removal types # removal types
REM_CARD = 0 REM_CARD = 0
REM_FACT = 1 REM_NOTE = 1
REM_GROUP = 2 REM_GROUP = 2
# count display # count display

View file

@ -16,7 +16,7 @@ from anki.consts import *
from anki.errors import AnkiError from anki.errors import AnkiError
import anki.latex # sets up hook import anki.latex # sets up hook
import anki.cards, anki.facts, anki.template, anki.cram, anki.find import anki.cards, anki.notes, anki.template, anki.cram, anki.find
defaultConf = { defaultConf = {
# scheduling options # scheduling options
@ -29,7 +29,7 @@ defaultConf = {
'fontFamilies': [ 'fontFamilies': [
[u' 明朝',u'ヒラギノ明朝 Pro W3',u'Kochi Mincho', u'東風明朝'] [u' 明朝',u'ヒラギノ明朝 Pro W3',u'Kochi Mincho', u'東風明朝']
], ],
'sortType': "factFld", 'sortType': "noteFld",
'sortBackwards': False, 'sortBackwards': False,
} }
@ -180,7 +180,7 @@ crt=?, mod=?, scm=?, dty=?, usn=?, ls=?, conf=?""",
def beforeUpload(self): def beforeUpload(self):
"Called before a full upload." "Called before a full upload."
tbls = "facts", "cards", "revlog", "graves" tbls = "notes", "cards", "revlog", "graves"
for t in tbls: for t in tbls:
self.db.execute("update %s set usn=0 where usn=-1" % t) self.db.execute("update %s set usn=0 where usn=-1" % t)
self._usn = 0 self._usn = 0
@ -194,8 +194,8 @@ crt=?, mod=?, scm=?, dty=?, usn=?, ls=?, conf=?""",
def getCard(self, id): def getCard(self, id):
return anki.cards.Card(self, id) return anki.cards.Card(self, id)
def getFact(self, id): def getNote(self, id):
return anki.facts.Fact(self, id=id) return anki.notes.Note(self, id=id)
# Utils # Utils
########################################################################## ##########################################################################
@ -218,23 +218,23 @@ crt=?, mod=?, scm=?, dty=?, usn=?, ls=?, conf=?""",
self.db.executemany("insert into graves values (%d, ?, %d)" % ( self.db.executemany("insert into graves values (%d, ?, %d)" % (
self.usn(), type), ([x] for x in ids)) self.usn(), type), ([x] for x in ids))
# Facts # Notes
########################################################################## ##########################################################################
def factCount(self): def noteCount(self):
return self.db.scalar("select count() from facts") return self.db.scalar("select count() from notes")
def newFact(self): def newNote(self):
"Return a new fact with the current model." "Return a new note with the current model."
return anki.facts.Fact(self, self.models.current()) return anki.notes.Note(self, self.models.current())
def addFact(self, fact): def addNote(self, note):
"Add a fact to the deck. Return number of new cards." "Add a note to the deck. Return number of new cards."
# check we have card models available, then save # check we have card models available, then save
cms = self.findTemplates(fact) cms = self.findTemplates(note)
if not cms: if not cms:
return 0 return 0
fact.flush() note.flush()
# randomize? # randomize?
if self.models.randomNew(): if self.models.randomNew():
due = self._randPos() due = self._randPos()
@ -243,65 +243,65 @@ crt=?, mod=?, scm=?, dty=?, usn=?, ls=?, conf=?""",
# add cards # add cards
ncards = 0 ncards = 0
for template in cms: for template in cms:
self._newCard(fact, template, due) self._newCard(note, template, due)
ncards += 1 ncards += 1
return ncards return ncards
def _randPos(self): def _randPos(self):
return random.randrange(1, sys.maxint) return random.randrange(1, sys.maxint)
def remFacts(self, ids): def remNotes(self, ids):
self.remCards(self.db.list("select id from cards where fid in "+ self.remCards(self.db.list("select id from cards where nid in "+
ids2str(ids))) ids2str(ids)))
def _remFacts(self, ids): def _remNotes(self, ids):
"Bulk delete facts by ID. Don't call this directly." "Bulk delete notes by ID. Don't call this directly."
if not ids: if not ids:
return return
strids = ids2str(ids) strids = ids2str(ids)
# we need to log these independently of cards, as one side may have # we need to log these independently of cards, as one side may have
# more card templates # more card templates
self._logRem(ids, REM_FACT) self._logRem(ids, REM_NOTE)
self.db.execute("delete from facts where id in %s" % strids) self.db.execute("delete from notes where id in %s" % strids)
self.db.execute("delete from fsums where fid in %s" % strids) self.db.execute("delete from nsums where nid in %s" % strids)
# Card creation # Card creation
########################################################################## ##########################################################################
def findTemplates(self, fact): def findTemplates(self, note):
"Return (active), non-empty templates." "Return (active), non-empty templates."
ok = [] ok = []
model = fact.model() model = note.model()
avail = self.models.availOrds(model, joinFields(fact.fields)) avail = self.models.availOrds(model, joinFields(note.fields))
ok = [] ok = []
for t in model['tmpls']: for t in model['tmpls']:
if t['ord'] in avail: if t['ord'] in avail:
ok.append(t) ok.append(t)
return ok return ok
def genCards(self, fids): def genCards(self, nids):
"Generate cards for non-empty templates." "Generate cards for non-empty templates."
# build map of (fid,ord) so we don't create dupes # build map of (nid,ord) so we don't create dupes
sfids = ids2str(fids) snids = ids2str(nids)
have = {} have = {}
for fid, ord in self.db.execute( for nid, ord in self.db.execute(
"select fid, ord from cards where fid in "+sfids): "select nid, ord from cards where nid in "+snids):
have[(fid,ord)] = True have[(nid,ord)] = True
# build cards for each fact # build cards for each note
data = [] data = []
ts = maxID(self.db) ts = maxID(self.db)
now = intTime() now = intTime()
for fid, mid, gid, flds in self.db.execute( for nid, mid, gid, flds in self.db.execute(
"select id, mid, gid, flds from facts where id in "+sfids): "select id, mid, gid, flds from notes where id in "+snids):
model = self.models.get(mid) model = self.models.get(mid)
avail = self.models.availOrds(model, flds) avail = self.models.availOrds(model, flds)
ok = [] ok = []
for t in model['tmpls']: for t in model['tmpls']:
if (fid,t['ord']) in have: if (nid,t['ord']) in have:
continue continue
if t['ord'] in avail: if t['ord'] in avail:
data.append((ts, fid, t['gid'] or gid, t['ord'], data.append((ts, nid, t['gid'] or gid, t['ord'],
now, fid)) now, nid))
ts += 1 ts += 1
# bulk update # bulk update
self.db.executemany(""" self.db.executemany("""
@ -311,26 +311,26 @@ insert into cards values (?,?,?,?,?,-1,0,0,?,0,0,0,0,0,0,0,"")""",
# type 0 - when previewing in add dialog, only non-empty # type 0 - when previewing in add dialog, only non-empty
# type 1 - when previewing edit, only existing # type 1 - when previewing edit, only existing
# type 2 - when previewing in models dialog, all templates # type 2 - when previewing in models dialog, all templates
def previewCards(self, fact, type=0): def previewCards(self, note, type=0):
if type == 0: if type == 0:
cms = self.findTemplates(fact) cms = self.findTemplates(note)
elif type == 1: elif type == 1:
cms = [c.template() for c in fact.cards()] cms = [c.template() for c in note.cards()]
else: else:
cms = fact.model()['tmpls'] cms = note.model()['tmpls']
if not cms: if not cms:
return [] return []
cards = [] cards = []
for template in cms: for template in cms:
cards.append(self._newCard(fact, template, 1, flush=False)) cards.append(self._newCard(note, template, 1, flush=False))
return cards return cards
def _newCard(self, fact, template, due, flush=True): def _newCard(self, note, template, due, flush=True):
"Create a new card." "Create a new card."
card = anki.cards.Card(self) card = anki.cards.Card(self)
card.fid = fact.id card.nid = note.id
card.ord = template['ord'] card.ord = template['ord']
card.gid = template['gid'] or fact.gid card.gid = template['gid'] or note.gid
card.due = due card.due = due
if flush: if flush:
card.flush() card.flush()
@ -350,42 +350,42 @@ insert into cards values (?,?,?,?,?,-1,0,0,?,0,0,0,0,0,0,0,"")""",
if not ids: if not ids:
return return
sids = ids2str(ids) sids = ids2str(ids)
fids = self.db.list("select fid from cards where id in "+sids) nids = self.db.list("select nid from cards where id in "+sids)
# remove cards # remove cards
self._logRem(ids, REM_CARD) self._logRem(ids, REM_CARD)
self.db.execute("delete from cards where id in "+sids) self.db.execute("delete from cards where id in "+sids)
self.db.execute("delete from revlog where cid in "+sids) self.db.execute("delete from revlog where cid in "+sids)
# then facts # then notes
fids = self.db.list(""" nids = self.db.list("""
select id from facts where id in %s and id not in (select fid from cards)""" % select id from notes where id in %s and id not in (select nid from cards)""" %
ids2str(fids)) ids2str(nids))
self._remFacts(fids) self._remNotes(nids)
# Field checksums and sorting fields # Field checksums and sorting fields
########################################################################## ##########################################################################
def _fieldData(self, sfids): def _fieldData(self, snids):
return self.db.execute( return self.db.execute(
"select id, mid, flds from facts where id in "+sfids) "select id, mid, flds from notes where id in "+snids)
def updateFieldCache(self, fids, csum=True): def updateFieldCache(self, nids, csum=True):
"Update field checksums and sort cache, after find&replace, etc." "Update field checksums and sort cache, after find&replace, etc."
sfids = ids2str(fids) snids = ids2str(nids)
r = [] r = []
r2 = [] r2 = []
for (fid, mid, flds) in self._fieldData(sfids): for (nid, mid, flds) in self._fieldData(snids):
fields = splitFields(flds) fields = splitFields(flds)
model = self.models.get(mid) model = self.models.get(mid)
if csum: if csum:
for f in model['flds']: for f in model['flds']:
if f['uniq'] and fields[f['ord']]: if f['uniq'] and fields[f['ord']]:
r.append((fid, mid, fieldChecksum(fields[f['ord']]))) r.append((nid, mid, fieldChecksum(fields[f['ord']])))
r2.append((stripHTML(fields[self.models.sortIdx(model)]), fid)) r2.append((stripHTML(fields[self.models.sortIdx(model)]), nid))
if csum: if csum:
self.db.execute("delete from fsums where fid in "+sfids) self.db.execute("delete from nsums where nid in "+snids)
self.db.executemany("insert into fsums values (?,?,?)", r) self.db.executemany("insert into nsums values (?,?,?)", r)
# rely on calling code to bump usn+mod # rely on calling code to bump usn+mod
self.db.executemany("update facts set sfld = ? where id = ?", r2) self.db.executemany("update notes set sfld = ? where id = ?", r2)
# Q/A generation # Q/A generation
########################################################################## ##########################################################################
@ -394,7 +394,7 @@ select id from facts where id in %s and id not in (select fid from cards)""" %
# gather metadata # gather metadata
if type == "card": if type == "card":
where = "and c.id in " + ids2str(ids) where = "and c.id in " + ids2str(ids)
elif type == "fact": elif type == "note":
where = "and f.id in " + ids2str(ids) where = "and f.id in " + ids2str(ids)
elif type == "model": elif type == "model":
where = "and m.id in " + ids2str(ids) where = "and m.id in " + ids2str(ids)
@ -407,7 +407,7 @@ select id from facts where id in %s and id not in (select fid from cards)""" %
def _renderQA(self, data): def _renderQA(self, data):
"Returns hash of id, question, answer." "Returns hash of id, question, answer."
# data is [cid, fid, mid, gid, ord, tags, flds] # data is [cid, nid, mid, gid, ord, tags, flds]
# unpack fields and create dict # unpack fields and create dict
flist = splitFields(data[6]) flist = splitFields(data[6])
fields = {} fields = {}
@ -437,11 +437,11 @@ select id from facts where id in %s and id not in (select fid from cards)""" %
return d return d
def _qaData(self, where=""): def _qaData(self, where=""):
"Return [cid, fid, mid, gid, ord, tags, flds] db query" "Return [cid, nid, mid, gid, ord, tags, flds] db query"
return self.db.execute(""" return self.db.execute("""
select c.id, f.id, f.mid, c.gid, c.ord, f.tags, f.flds select c.id, f.id, f.mid, c.gid, c.ord, f.tags, f.flds
from cards c, facts f from cards c, notes f
where c.fid == f.id where c.nid == f.id
%s""" % where) %s""" % where)
# Finding cards # Finding cards
@ -450,8 +450,8 @@ where c.fid == f.id
def findCards(self, query, full=False): def findCards(self, query, full=False):
return anki.find.Finder(self).findCards(query, full) return anki.find.Finder(self).findCards(query, full)
def findReplace(self, fids, src, dst, regex=None, field=None, fold=True): def findReplace(self, nids, src, dst, regex=None, field=None, fold=True):
return anki.find.findReplace(self, fids, src, dst, regex, field, fold) return anki.find.findReplace(self, nids, src, dst, regex, field, fold)
def findDuplicates(self, fmids): def findDuplicates(self, fmids):
return anki.find.findDuplicates(self, fmids) return anki.find.findDuplicates(self, fmids)
@ -570,15 +570,15 @@ where c.fid == f.id
problems = [] problems = []
self.save() self.save()
oldSize = os.stat(self.path)[stat.ST_SIZE] oldSize = os.stat(self.path)[stat.ST_SIZE]
# delete any facts with missing cards # delete any notes with missing cards
ids = self.db.list(""" ids = self.db.list("""
select id from facts where id not in (select distinct fid from cards)""") select id from notes where id not in (select distinct nid from cards)""")
self._remFacts(ids) self._remNotes(ids)
# tags # tags
self.tags.registerFacts() self.tags.registerNotes()
# field cache # field cache
for m in self.models.all(): for m in self.models.all():
self.updateFieldCache(self.models.fids(m)) self.updateFieldCache(self.models.nids(m))
# and finally, optimize # and finally, optimize
self.optimize() self.optimize()
newSize = os.stat(self.path)[stat.ST_SIZE] newSize = os.stat(self.path)[stat.ST_SIZE]

View file

@ -104,23 +104,23 @@ class AnkiExporter(Exporter):
cards = self.deck.db.all(""" cards = self.deck.db.all("""
select id, modified from cards select id, modified from cards
where id in %s""" % cStrIds) where id in %s""" % cStrIds)
facts = self.deck.db.all(""" notes = self.deck.db.all("""
select facts.id, facts.modified from cards, facts where select notes.id, notes.modified from cards, notes where
facts.id = cards.factId and notes.id = cards.noteId and
cards.id in %s""" % cStrIds) cards.id in %s""" % cStrIds)
models = self.deck.db.all(""" models = self.deck.db.all("""
select models.id, models.modified from models, facts where select models.id, models.modified from models, notes where
facts.modelId = models.id and notes.modelId = models.id and
facts.id in %s""" % ids2str([f[0] for f in facts])) notes.id in %s""" % ids2str([f[0] for f in notes]))
media = self.deck.db.all(""" media = self.deck.db.all("""
select id, modified from media""") select id, modified from media""")
return { return {
# cards # cards
"cards": cards, "cards": cards,
"delcards": [], "delcards": [],
# facts # notes
"facts": facts, "notes": notes,
"delfacts": [], "delnotes": [],
# models # models
"models": models, "models": models,
"delmodels": [], "delmodels": [],
@ -147,8 +147,8 @@ where cards.id in %s
order by cards.created""" % strids) order by cards.created""" % strids)
if self.includeTags: if self.includeTags:
self.cardTags = dict(self.deck.db.all(""" self.cardTags = dict(self.deck.db.all("""
select cards.id, facts.tags from cards, facts select cards.id, notes.tags from cards, notes
where cards.factId = facts.id where cards.noteId = notes.id
and cards.id in %s and cards.id in %s
order by cards.created""" % strids)) order by cards.created""" % strids))
out = u"\n".join(["%s\t%s%s" % ( out = u"\n".join(["%s\t%s%s" % (
@ -166,7 +166,7 @@ order by cards.created""" % strids))
return "\t" + ", ".join(parseTags(self.cardTags[id])) return "\t" + ", ".join(parseTags(self.cardTags[id]))
return "" return ""
class TextFactExporter(Exporter): class TextNoteExporter(Exporter):
key = _("Text files (*.txt)") key = _("Text files (*.txt)")
ext = ".txt" ext = ".txt"
@ -177,20 +177,20 @@ class TextFactExporter(Exporter):
def doExport(self, file): def doExport(self, file):
cardIds = self.cardIds() cardIds = self.cardIds()
facts = self.deck.db.all(""" notes = self.deck.db.all("""
select factId, value, facts.created from facts, fields select noteId, value, notes.created from notes, fields
where where
facts.id in notes.id in
(select distinct factId from cards (select distinct noteId from cards
where cards.id in %s) where cards.id in %s)
and facts.id = fields.factId and notes.id = fields.noteId
order by factId, ordinal""" % ids2str(cardIds)) order by noteId, ordinal""" % ids2str(cardIds))
txt = "" txt = ""
if self.includeTags: if self.includeTags:
self.factTags = dict(self.deck.db.all( self.noteTags = dict(self.deck.db.all(
"select id, tags from facts where id in %s" % "select id, tags from notes where id in %s" %
ids2str([fact[0] for fact in facts]))) ids2str([note[0] for note in notes])))
groups = itertools.groupby(facts, itemgetter(0)) groups = itertools.groupby(notes, itemgetter(0))
groups = [[x for x in y[1]] for y in groups] groups = [[x for x in y[1]] for y in groups]
groups = [(group[0][2], groups = [(group[0][2],
"\t".join([self.escapeText(x[1]) for x in group]) + "\t".join([self.escapeText(x[1]) for x in group]) +
@ -205,7 +205,7 @@ order by factId, ordinal""" % ids2str(cardIds))
def tags(self, id): def tags(self, id):
if self.includeTags: if self.includeTags:
return "\t" + self.factTags[id] return "\t" + self.noteTags[id]
return "" return ""
# Export modules # Export modules
@ -215,4 +215,4 @@ def exporters():
return ( return (
(_("Anki Deck (*.anki)"), AnkiExporter), (_("Anki Deck (*.anki)"), AnkiExporter),
(_("Cards in tab-separated text file (*.txt)"), TextCardExporter), (_("Cards in tab-separated text file (*.txt)"), TextCardExporter),
(_("Facts in tab-separated text file (*.txt)"), TextFactExporter)) (_("Notes in tab-separated text file (*.txt)"), TextNoteExporter))

View file

@ -9,7 +9,7 @@ from anki.utils import ids2str, splitFields, joinFields, stripHTML, intTime
SEARCH_TAG = 0 SEARCH_TAG = 0
SEARCH_TYPE = 1 SEARCH_TYPE = 1
SEARCH_PHRASE = 2 SEARCH_PHRASE = 2
SEARCH_FID = 3 SEARCH_NID = 3
SEARCH_TEMPLATE = 4 SEARCH_TEMPLATE = 4
SEARCH_FIELD = 5 SEARCH_FIELD = 5
SEARCH_MODEL = 6 SEARCH_MODEL = 6
@ -54,9 +54,9 @@ class Finder(object):
def _whereClause(self): def _whereClause(self):
x = [] x = []
if self.lims['fact']: if self.lims['note']:
x.append("fid in (select id from facts where %s)" % " and ".join( x.append("nid in (select id from notes where %s)" % " and ".join(
self.lims['fact'])) self.lims['note']))
if self.lims['card']: if self.lims['card']:
x.extend(self.lims['card']) x.extend(self.lims['card'])
q = " and ".join(x) q = " and ".join(x)
@ -68,17 +68,17 @@ class Finder(object):
type = self.deck.conf['sortType'] type = self.deck.conf['sortType']
if not type: if not type:
return "select id from cards c where " + lim return "select id from cards c where " + lim
elif type.startswith("fact"): elif type.startswith("note"):
if type == "factCrt": if type == "noteCrt":
sort = "f.id, c.ord" sort = "f.id, c.ord"
elif type == "factMod": elif type == "noteMod":
sort = "f.mod, c.ord" sort = "f.mod, c.ord"
elif type == "factFld": elif type == "noteFld":
sort = "f.sfld collate nocase, c.ord" sort = "f.sfld collate nocase, c.ord"
else: else:
raise Exception() raise Exception()
return """ return """
select c.id from cards c, facts f where %s and c.fid=f.id select c.id from cards c, notes f where %s and c.nid=f.id
order by %s""" % (lim, sort) order by %s""" % (lim, sort)
elif type.startswith("card"): elif type.startswith("card"):
if type == "cardMod": if type == "cardMod":
@ -101,9 +101,9 @@ order by %s""" % (lim, sort)
raise Exception() raise Exception()
def _findLimits(self): def _findLimits(self):
"Generate a list of fact/card limits for the query." "Generate a list of note/card limits for the query."
self.lims = { self.lims = {
'fact': [], 'note': [],
'card': [], 'card': [],
'args': {}, 'args': {},
'valid': True 'valid': True
@ -113,8 +113,8 @@ order by %s""" % (lim, sort)
self._findTag(token, isNeg, c) self._findTag(token, isNeg, c)
elif type == SEARCH_TYPE: elif type == SEARCH_TYPE:
self._findCardState(token, isNeg) self._findCardState(token, isNeg)
elif type == SEARCH_FID: elif type == SEARCH_NID:
self._findFids(token) self._findNids(token)
elif type == SEARCH_TEMPLATE: elif type == SEARCH_TEMPLATE:
self._findTemplate(token, isNeg) self._findTemplate(token, isNeg)
elif type == SEARCH_FIELD: elif type == SEARCH_FIELD:
@ -128,7 +128,7 @@ order by %s""" % (lim, sort)
def _findTag(self, val, neg, c): def _findTag(self, val, neg, c):
if val == "none": if val == "none":
self.lims['fact'].append("select id from facts where tags = ''") self.lims['note'].append("select id from notes where tags = ''")
return return
extra = "not" if neg else "" extra = "not" if neg else ""
val = val.replace("*", "%") val = val.replace("*", "%")
@ -137,7 +137,7 @@ order by %s""" % (lim, sort)
if not val.endswith("%"): if not val.endswith("%"):
val += " %" val += " %"
self.lims['args']["_tag_%d" % c] = val self.lims['args']["_tag_%d" % c] = val
self.lims['fact'].append( self.lims['note'].append(
"tags %s like :_tag_%d""" % (extra, c)) "tags %s like :_tag_%d""" % (extra, c))
def _findCardState(self, val, neg): def _findCardState(self, val, neg):
@ -168,20 +168,20 @@ order by %s""" % (lim, sort)
extra = "not" if neg else "" extra = "not" if neg else ""
if not self.full: if not self.full:
self.lims['args']["_text_%d"%c] = "%"+val+"%" self.lims['args']["_text_%d"%c] = "%"+val+"%"
self.lims['fact'].append("flds %s like :_text_%d escape '\\'" % ( self.lims['note'].append("flds %s like :_text_%d escape '\\'" % (
extra, c)) extra, c))
else: else:
# in the future we may want to apply this at the end to speed up # in the future we may want to apply this at the end to speed up
# the case where there are other limits # the case where there are other limits
fids = [] nids = []
for fid, flds in self.deck.db.execute( for nid, flds in self.deck.db.execute(
"select id, flds from facts"): "select id, flds from notes"):
if val in stripHTML(flds): if val in stripHTML(flds):
fids.append(fid) nids.append(nid)
self.lims['fact'].append("id in " + ids2str(fids)) self.lims['note'].append("id in " + ids2str(nids))
def _findFids(self, val): def _findNids(self, val):
self.lims['fact'].append("id in (%s)" % val) self.lims['note'].append("id in (%s)" % val)
def _findModel(self, val, isNeg): def _findModel(self, val, isNeg):
extra = "not" if isNeg else "" extra = "not" if isNeg else ""
@ -189,7 +189,7 @@ order by %s""" % (lim, sort)
for m in self.deck.models.all(): for m in self.deck.models.all():
if m['name'].lower() == val: if m['name'].lower() == val:
ids.append(m['id']) ids.append(m['id'])
self.lims['fact'].append("mid %s in %s" % (extra, ids2str(ids))) self.lims['note'].append("mid %s in %s" % (extra, ids2str(ids)))
def _findGroup(self, val, isNeg): def _findGroup(self, val, isNeg):
extra = "!" if isNeg else "" extra = "!" if isNeg else ""
@ -214,7 +214,7 @@ order by %s""" % (lim, sort)
# template name? # template name?
elif t['name'].lower() == val.lower(): elif t['name'].lower() == val.lower():
lims.append(( lims.append((
"(fid in (select id from facts where mid = %s) " "(nid in (select id from notes where mid = %s) "
"and ord %s %d)") % (m['id'], comp, t['ord'])) "and ord %s %d)") % (m['id'], comp, t['ord']))
found = True found = True
if lims: if lims:
@ -236,11 +236,11 @@ order by %s""" % (lim, sort)
# nothing has that field # nothing has that field
self.lims['valid'] = False self.lims['valid'] = False
return return
# gather fids # gather nids
regex = value.replace("%", ".*") regex = value.replace("%", ".*")
fids = [] nids = []
for (id,mid,flds) in self.deck.db.execute(""" for (id,mid,flds) in self.deck.db.execute("""
select id, mid, flds from facts select id, mid, flds from notes
where mid in %s and flds like ? escape '\\'""" % ( where mid in %s and flds like ? escape '\\'""" % (
ids2str(mods.keys())), ids2str(mods.keys())),
"%" if self.full else value): "%" if self.full else value):
@ -250,9 +250,9 @@ where mid in %s and flds like ? escape '\\'""" % (
if self.full: if self.full:
strg = stripHTML(strg) strg = stripHTML(strg)
if re.search(regex, strg): if re.search(regex, strg):
fids.append(id) nids.append(id)
extra = "not" if isNeg else "" extra = "not" if isNeg else ""
self.lims['fact'].append("id %s in %s" % (extra, ids2str(fids))) self.lims['note'].append("id %s in %s" % (extra, ids2str(nids)))
# Most of this function was written by Marcus # Most of this function was written by Marcus
def _parseQuery(self): def _parseQuery(self):
@ -332,7 +332,7 @@ where mid in %s and flds like ? escape '\\'""" % (
elif token['value'].startswith("group:"): elif token['value'].startswith("group:"):
token['value'] = token['value'][6:].lower() token['value'] = token['value'][6:].lower()
type = SEARCH_GROUP type = SEARCH_GROUP
elif token['value'].startswith("fid:") and len(token['value']) > 4: elif token['value'].startswith("nid:") and len(token['value']) > 4:
dec = token['value'][4:] dec = token['value'][4:]
try: try:
int(dec) int(dec)
@ -344,7 +344,7 @@ where mid in %s and flds like ? escape '\\'""" % (
token['value'] = token['value'][4:] token['value'] = token['value'][4:]
except: except:
token['value'] = "0" token['value'] = "0"
type = SEARCH_FID type = SEARCH_NID
elif token['value'].startswith("card:"): elif token['value'].startswith("card:"):
token['value'] = token['value'][5:] token['value'] = token['value'][5:]
type = SEARCH_TEMPLATE type = SEARCH_TEMPLATE
@ -370,8 +370,8 @@ where mid in %s and flds like ? escape '\\'""" % (
# Find and replace # Find and replace
########################################################################## ##########################################################################
def findReplace(deck, fids, src, dst, regex=False, field=None, fold=True): def findReplace(deck, nids, src, dst, regex=False, field=None, fold=True):
"Find and replace fields in a fact." "Find and replace fields in a note."
mmap = {} mmap = {}
if field: if field:
for m in deck.models.all(): for m in deck.models.all():
@ -389,8 +389,8 @@ def findReplace(deck, fids, src, dst, regex=False, field=None, fold=True):
def repl(str): def repl(str):
return re.sub(regex, dst, str) return re.sub(regex, dst, str)
d = [] d = []
for fid, mid, flds in deck.db.execute( for nid, mid, flds in deck.db.execute(
"select id, mid, flds from facts where id in "+ids2str(fids)): "select id, mid, flds from notes where id in "+ids2str(nids)):
origFlds = flds origFlds = flds
# does it match? # does it match?
sflds = splitFields(flds) sflds = splitFields(flds)
@ -402,12 +402,12 @@ def findReplace(deck, fids, src, dst, regex=False, field=None, fold=True):
sflds[c] = repl(sflds[c]) sflds[c] = repl(sflds[c])
flds = joinFields(sflds) flds = joinFields(sflds)
if flds != origFlds: if flds != origFlds:
d.append(dict(fid=fid,flds=flds,u=deck.usn(),m=intTime())) d.append(dict(nid=nid,flds=flds,u=deck.usn(),m=intTime()))
if not d: if not d:
return 0 return 0
# replace # replace
deck.db.executemany("update facts set flds=:flds,mod=:m,usn=:u where id=:fid", d) deck.db.executemany("update notes set flds=:flds,mod=:m,usn=:u where id=:nid", d)
deck.updateFieldCache(fids) deck.updateFieldCache(nids)
return len(d) return len(d)
# Find duplicates # Find duplicates
@ -415,14 +415,14 @@ def findReplace(deck, fids, src, dst, regex=False, field=None, fold=True):
def findDuplicates(deck, fmids): def findDuplicates(deck, fmids):
data = deck.db.all( data = deck.db.all(
"select fid, value from fdata where fmid in %s" % "select nid, value from fdata where fmid in %s" %
ids2str(fmids)) ids2str(fmids))
vals = {} vals = {}
for (fid, val) in data: for (nid, val) in data:
if not val.strip(): if not val.strip():
continue continue
if val not in vals: if val not in vals:
vals[val] = [fid] vals[val] = [nid]
else: else:
vals[val].append(fid) vals[val].append(nid)
return [(k,v) for (k,v) in vals.items() if len(v) > 1] return [(k,v) for (k,v) in vals.items() if len(v) > 1]

View file

@ -15,7 +15,7 @@ from anki.lang import _
# appropriate # appropriate
# notes: # notes:
# - it's difficult to enforce valid gids for models/facts/cards, as we # - it's difficult to enforce valid gids for models/notes/cards, as we
# may update the gid locally only to have it overwritten by a more recent # may update the gid locally only to have it overwritten by a more recent
# change from somewhere else. to avoid this, we allow invalid gid # change from somewhere else. to avoid this, we allow invalid gid
# references, and treat any invalid gids as the default group. # references, and treat any invalid gids as the default group.
@ -268,7 +268,7 @@ class GroupManager(object):
def sendHome(self, cids): def sendHome(self, cids):
self.deck.db.execute(""" self.deck.db.execute("""
update cards set gid=(select gid from facts f where f.id=fid), update cards set gid=(select gid from notes f where f.id=nid),
usn=?,mod=? where id in %s""" % ids2str(cids), usn=?,mod=? where id in %s""" % ids2str(cids),
self.deck.usn(), intTime(), gid) self.deck.usn(), intTime(), gid)

View file

@ -11,9 +11,9 @@ from anki.importing.base import Importer
# shared decks, and import from a packaged deck. # shared decks, and import from a packaged deck.
# #
# We can't rely on internal ids, so we: # We can't rely on internal ids, so we:
# - compare facts by guid # - compare notes by guid
# - compare models by schema signature # - compare models by schema signature
# - compare cards by fact guid + ordinal # - compare cards by note guid + ordinal
# - compare groups by name # - compare groups by name
# #
@ -44,53 +44,53 @@ class Anki2Importer(Importer):
self.dst.groups.select(id) self.dst.groups.select(id)
self._prepareTS() self._prepareTS()
self._prepareModels() self._prepareModels()
self._importFacts() self._importNotes()
self._importCards() self._importCards()
self._importMedia() self._importMedia()
self._postImport() self._postImport()
self.dst.db.execute("vacuum") self.dst.db.execute("vacuum")
self.dst.db.execute("analyze") self.dst.db.execute("analyze")
# Facts # Notes
###################################################################### ######################################################################
# - should note new for wizard # - should note new for wizard
def _importFacts(self): def _importNotes(self):
# build guid -> (id,mod,mid) hash # build guid -> (id,mod,mid) hash
self._facts = {} self._notes = {}
for id, guid, mod, mid in self.dst.db.execute( for id, guid, mod, mid in self.dst.db.execute(
"select id, guid, mod, mid from facts"): "select id, guid, mod, mid from notes"):
self._facts[guid] = (id, mod, mid) self._notes[guid] = (id, mod, mid)
# iterate over source deck # iterate over source deck
add = [] add = []
dirty = [] dirty = []
for fact in self.src.db.execute( for note in self.src.db.execute(
"select * from facts"): "select * from notes"):
# turn the db result into a mutable list # turn the db result into a mutable list
fact = list(fact) note = list(note)
guid, mid = fact[1:3] guid, mid = note[1:3]
# missing from local deck? # missing from local deck?
if guid not in self._facts: if guid not in self._notes:
# get corresponding local model # get corresponding local model
lmid = self._mid(mid) lmid = self._mid(mid)
# rewrite internal ids, models, etc # rewrite internal ids, models, etc
fact[0] = self.ts() note[0] = self.ts()
fact[2] = lmid note[2] = lmid
fact[3] = self._gid(fact[3]) note[3] = self._gid(note[3])
fact[4] = intTime() note[4] = intTime()
fact[5] = -1 # usn note[5] = -1 # usn
add.append(fact) add.append(note)
dirty.append(fact[0]) dirty.append(note[0])
# note we have the added fact # note we have the added note
self._facts[guid] = (fact[0], fact[4], fact[2]) self._notes[guid] = (note[0], note[4], note[2])
else: else:
continue #raise Exception("merging facts nyi") continue #raise Exception("merging notes nyi")
# add to deck # add to deck
self.dst.db.executemany( self.dst.db.executemany(
"insert or replace into facts values (?,?,?,?,?,?,?,?,?,?,?)", "insert or replace into notes values (?,?,?,?,?,?,?,?,?,?,?)",
add) add)
self.dst.updateFieldCache(dirty) self.dst.updateFieldCache(dirty)
self.dst.tags.registerFacts(dirty) self.dst.tags.registerNotes(dirty)
# Models # Models
###################################################################### ######################################################################
@ -168,24 +168,24 @@ class Anki2Importer(Importer):
# build map of (guid, ord) -> cid # build map of (guid, ord) -> cid
self._cards = {} self._cards = {}
for guid, ord, cid in self.dst.db.execute( for guid, ord, cid in self.dst.db.execute(
"select f.guid, c.ord, c.id from cards c, facts f " "select f.guid, c.ord, c.id from cards c, notes f "
"where c.fid = f.id"): "where c.nid = f.id"):
self._cards[(guid, ord)] = cid self._cards[(guid, ord)] = cid
# loop through src # loop through src
cards = [] cards = []
revlog = [] revlog = []
print "fixme: need to check schema issues in card import" print "fixme: need to check schema issues in card import"
for card in self.src.db.execute( for card in self.src.db.execute(
"select f.guid, f.mid, c.* from cards c, facts f " "select f.guid, f.mid, c.* from cards c, notes f "
"where c.fid = f.id"): "where c.nid = f.id"):
guid = card[0] guid = card[0]
# does the card's fact exist in dst deck? # does the card's note exist in dst deck?
if guid not in self._facts: if guid not in self._notes:
continue continue
dfid = self._facts[guid] dnid = self._notes[guid]
# does the fact share the same schema? # does the note share the same schema?
# shash = self._srcModels[card[1]] # shash = self._srcModels[card[1]]
# mid = self._facts[guid][2] # mid = self._notes[guid][2]
# if shash != self._dstModels[mid]: # if shash != self._dstModels[mid]:
# continue # continue
# does the card already exist in the dst deck? # does the card already exist in the dst deck?
@ -193,12 +193,12 @@ class Anki2Importer(Importer):
if (guid, ord) in self._cards: if (guid, ord) in self._cards:
# fixme: in future, could update if newer mod time # fixme: in future, could update if newer mod time
continue continue
# doesn't exist. strip off fact info, and save src id for later # doesn't exist. strip off note info, and save src id for later
card = list(card[2:]) card = list(card[2:])
scid = card[0] scid = card[0]
# update cid, fid, etc # update cid, nid, etc
card[0] = self.ts() card[0] = self.ts()
card[1] = self._facts[guid][0] card[1] = self._notes[guid][0]
card[2] = self._gid(card[2]) card[2] = self._gid(card[2])
card[4] = intTime() card[4] = intTime()
cards.append(card) cards.append(card)

View file

@ -57,15 +57,15 @@ class CardImporter(Importer):
cards = self.foreignCards() cards = self.foreignCards()
# grab data from db # grab data from db
fields = self.deck.db.all(""" fields = self.deck.db.all("""
select factId, value from fields where fieldModelId = :id select noteId, value from fields where fieldModelId = :id
and value != ''""", and value != ''""",
id=self.updateKey[1]) id=self.updateKey[1])
# hash it # hash it
vhash = {} vhash = {}
fids = [] nids = []
for (fid, val) in fields: for (nid, val) in fields:
fids.append(fid) nids.append(nid)
vhash[val] = fid vhash[val] = nid
# prepare tags # prepare tags
tagsIdx = None tagsIdx = None
try: try:
@ -82,7 +82,7 @@ and value != ''""",
if v in vhash: if v in vhash:
# ignore empty keys # ignore empty keys
if v: if v:
# fid, card # nid, card
upcards.append((vhash[v], c)) upcards.append((vhash[v], c))
else: else:
newcards.append(c) newcards.append(c)
@ -96,28 +96,28 @@ and value != ''""",
except ValueError: except ValueError:
# not mapped # not mapped
continue continue
data = [{'fid': fid, data = [{'nid': nid,
'fmid': fm.id, 'fmid': fm.id,
'v': c.fields[index], 'v': c.fields[index],
'chk': self.maybeChecksum(c.fields[index], fm.unique)} 'chk': self.maybeChecksum(c.fields[index], fm.unique)}
for (fid, c) in upcards] for (nid, c) in upcards]
self.deck.db.execute(""" self.deck.db.execute("""
update fields set value = :v, chksum = :chk where factId = :fid update fields set value = :v, chksum = :chk where noteId = :nid
and fieldModelId = :fmid""", data) and fieldModelId = :fmid""", data)
# update tags # update tags
if tagsIdx is not None: if tagsIdx is not None:
data = [{'fid': fid, data = [{'nid': nid,
't': c.fields[tagsIdx]} 't': c.fields[tagsIdx]}
for (fid, c) in upcards] for (nid, c) in upcards]
self.deck.db.execute( self.deck.db.execute(
"update facts set tags = :t where id = :fid", "update notes set tags = :t where id = :nid",
data) data)
# rebuild caches # rebuild caches
cids = self.deck.db.column0( cids = self.deck.db.column0(
"select id from cards where factId in %s" % "select id from cards where noteId in %s" %
ids2str(fids)) ids2str(nids))
self.deck.updateCardTags(cids) self.deck.updateCardTags(cids)
self.deck.updateCardsFromFactIds(fids) self.deck.updateCardsFromNoteIds(nids)
self.total = len(cards) self.total = len(cards)
self.deck.setModified() self.deck.setModified()
@ -166,7 +166,7 @@ and fieldModelId = :fmid""", data)
model = property(getModel, setModel) model = property(getModel, setModel)
def importCards(self, cards): def importCards(self, cards):
"Convert each card into a fact, apply attributes and add to deck." "Convert each card into a note, apply attributes and add to deck."
# ensure all unique and required fields are mapped # ensure all unique and required fields are mapped
for fm in self.model.fieldModels: for fm in self.model.fieldModels:
if fm.required or fm.unique: if fm.required or fm.unique:
@ -187,7 +187,7 @@ and fieldModelId = :fmid""", data)
return cards return cards
def addCards(self, cards): def addCards(self, cards):
"Add facts in bulk from foreign cards." "Add notes in bulk from foreign cards."
# map tags field to attr # map tags field to attr
try: try:
idx = self.mapping.index(0) idx = self.mapping.index(0)
@ -195,31 +195,31 @@ and fieldModelId = :fmid""", data)
c.tags += " " + c.fields[idx] c.tags += " " + c.fields[idx]
except ValueError: except ValueError:
pass pass
# add facts # add notes
factIds = [genID() for n in range(len(cards))] noteIds = [genID() for n in range(len(cards))]
factCreated = {} noteCreated = {}
def fudgeCreated(d, tmp=[]): def fudgeCreated(d, tmp=[]):
if not tmp: if not tmp:
tmp.append(time.time()) tmp.append(time.time())
else: else:
tmp[0] += 0.0001 tmp[0] += 0.0001
d['created'] = tmp[0] d['created'] = tmp[0]
factCreated[d['id']] = d['created'] noteCreated[d['id']] = d['created']
return d return d
self.deck.db.execute(factsTable.insert(), self.deck.db.execute(notesTable.insert(),
[fudgeCreated({'modelId': self.model.id, [fudgeCreated({'modelId': self.model.id,
'tags': canonifyTags(self.tagsToAdd + " " + cards[n].tags), 'tags': canonifyTags(self.tagsToAdd + " " + cards[n].tags),
'id': factIds[n]}) for n in range(len(cards))]) 'id': noteIds[n]}) for n in range(len(cards))])
self.deck.db.execute(""" self.deck.db.execute("""
delete from factsDeleted delete from notesDeleted
where factId in (%s)""" % ",".join([str(s) for s in factIds])) where noteId in (%s)""" % ",".join([str(s) for s in noteIds]))
# add all the fields # add all the fields
for fm in self.model.fieldModels: for fm in self.model.fieldModels:
try: try:
index = self.mapping.index(fm) index = self.mapping.index(fm)
except ValueError: except ValueError:
index = None index = None
data = [{'factId': factIds[m], data = [{'noteId': noteIds[m],
'fieldModelId': fm.id, 'fieldModelId': fm.id,
'ordinal': fm.ordinal, 'ordinal': fm.ordinal,
'id': genID(), 'id': genID(),
@ -239,8 +239,8 @@ where factId in (%s)""" % ",".join([str(s) for s in factIds]))
active += 1 active += 1
data = [self.addMeta({ data = [self.addMeta({
'id': genID(), 'id': genID(),
'factId': factIds[m], 'noteId': noteIds[m],
'factCreated': factCreated[factIds[m]], 'noteCreated': noteCreated[noteIds[m]],
'cardModelId': cm.id, 'cardModelId': cm.id,
'ordinal': cm.ordinal, 'ordinal': cm.ordinal,
'question': u"", 'question': u"",
@ -248,14 +248,14 @@ where factId in (%s)""" % ",".join([str(s) for s in factIds]))
},cards[m]) for m in range(len(cards))] },cards[m]) for m in range(len(cards))]
self.deck.db.execute(cardsTable.insert(), self.deck.db.execute(cardsTable.insert(),
data) data)
self.deck.updateCardsFromFactIds(factIds) self.deck.updateCardsFromNoteIds(noteIds)
self.total = len(factIds) self.total = len(noteIds)
def addMeta(self, data, card): def addMeta(self, data, card):
"Add any scheduling metadata to cards" "Add any scheduling metadata to cards"
if 'fields' in card.__dict__: if 'fields' in card.__dict__:
del card.fields del card.fields
t = data['factCreated'] + data['ordinal'] * 0.00001 t = data['noteCreated'] + data['ordinal'] * 0.00001
data['created'] = t data['created'] = t
data['modified'] = t data['modified'] = t
data['due'] = t data['due'] = t
@ -281,7 +281,7 @@ where factId in (%s)""" % ",".join([str(s) for s in factIds]))
for n in range(len(self.mapping)): for n in range(len(self.mapping)):
if self.mapping[n] and self.mapping[n].required: if self.mapping[n] and self.mapping[n].required:
if fieldNum <= n or not card.fields[n].strip(): if fieldNum <= n or not card.fields[n].strip():
self.log.append("Fact is missing field '%s': %s" % self.log.append("Note is missing field '%s': %s" %
(self.mapping[n].name, (self.mapping[n].name,
", ".join(card.fields))) ", ".join(card.fields)))
return False return False
@ -307,7 +307,7 @@ where factId in (%s)""" % ",".join([str(s) for s in factIds]))
if self.mapping[n] and self.mapping[n].unique: if self.mapping[n] and self.mapping[n].unique:
if card.fields[n] in self.uniqueCache[self.mapping[n].id]: if card.fields[n] in self.uniqueCache[self.mapping[n].id]:
if not self.tagDuplicates: if not self.tagDuplicates:
self.log.append("Fact has duplicate '%s': %s" % self.log.append("Note has duplicate '%s': %s" %
(self.mapping[n].name, (self.mapping[n].name,
", ".join(card.fields))) ", ".join(card.fields)))
return False return False

View file

@ -161,7 +161,7 @@ If the same name exists, compare checksums."""
def allMedia(self): def allMedia(self):
"Return a set of all referenced filenames." "Return a set of all referenced filenames."
files = set() files = set()
for mid, flds in self.deck.db.execute("select mid, flds from facts"): for mid, flds in self.deck.db.execute("select mid, flds from notes"):
for f in self.filesInStr(mid, flds): for f in self.filesInStr(mid, flds):
files.add(f) files.add(f)
return files return files

View file

@ -75,7 +75,7 @@ class ModelManager(object):
m['usn'] = self.deck.usn() m['usn'] = self.deck.usn()
self._updateRequired(m) self._updateRequired(m)
if gencards: if gencards:
self.deck.genCards(self.fids(m)) self.deck.genCards(self.nids(m))
self.changed = True self.changed = True
def flush(self): def flush(self):
@ -129,12 +129,12 @@ class ModelManager(object):
return self._add(m) return self._add(m)
def rem(self, m): def rem(self, m):
"Delete model, and all its cards/facts." "Delete model, and all its cards/notes."
self.deck.modSchema() self.deck.modSchema()
current = self.current()['id'] == m['id'] current = self.current()['id'] == m['id']
# delete facts/cards # delete notes/cards
self.deck.remCards(self.deck.db.list(""" self.deck.remCards(self.deck.db.list("""
select id from cards where fid in (select id from facts where mid = ?)""", select id from cards where nid in (select id from notes where mid = ?)""",
m['id'])) m['id']))
# then the model # then the model
del self.models[str(m['id'])] del self.models[str(m['id'])]
@ -168,15 +168,15 @@ select id from cards where fid in (select id from facts where mid = ?)""",
# Tools # Tools
################################################## ##################################################
def fids(self, m): def nids(self, m):
"Fact ids for M." "Note ids for M."
return self.deck.db.list( return self.deck.db.list(
"select id from facts where mid = ?", m['id']) "select id from notes where mid = ?", m['id'])
def useCount(self, m): def useCount(self, m):
"Number of fact using M." "Number of note using M."
return self.deck.db.scalar( return self.deck.db.scalar(
"select count() from facts where mid = ?", m['id']) "select count() from notes where mid = ?", m['id'])
def randomNew(self): def randomNew(self):
return self.current()['newOrder'] == NEW_CARDS_RANDOM return self.current()['newOrder'] == NEW_CARDS_RANDOM
@ -212,7 +212,7 @@ select id from cards where fid in (select id from facts where mid = ?)""",
assert idx >= 0 and idx < len(m['flds']) assert idx >= 0 and idx < len(m['flds'])
self.deck.modSchema() self.deck.modSchema()
m['sortf'] = idx m['sortf'] = idx
self.deck.updateFieldCache(self.fids(m), csum=False) self.deck.updateFieldCache(self.nids(m), csum=False)
self.save(m) self.save(m)
def addField(self, m, field): def addField(self, m, field):
@ -234,7 +234,7 @@ select id from cards where fid in (select id from facts where mid = ?)""",
self._transformFields(m, delete) self._transformFields(m, delete)
if idx == self.sortIdx(m): if idx == self.sortIdx(m):
# need to rebuild # need to rebuild
self.deck.updateFieldCache(self.fids(m), csum=False) self.deck.updateFieldCache(self.nids(m), csum=False)
# saves # saves
self.renameField(m, field, None) self.renameField(m, field, None)
@ -276,11 +276,11 @@ select id from cards where fid in (select id from facts where mid = ?)""",
self.deck.modSchema() self.deck.modSchema()
r = [] r = []
for (id, flds) in self.deck.db.execute( for (id, flds) in self.deck.db.execute(
"select id, flds from facts where mid = ?", m['id']): "select id, flds from notes where mid = ?", m['id']):
r.append((joinFields(fn(splitFields(flds))), r.append((joinFields(fn(splitFields(flds))),
intTime(), self.deck.usn(), id)) intTime(), self.deck.usn(), id))
self.deck.db.executemany( self.deck.db.executemany(
"update facts set flds=?,mod=?,usn=? where id = ?", r) "update notes set flds=?,mod=?,usn=? where id = ?", r)
# Templates # Templates
################################################## ##################################################
@ -298,18 +298,18 @@ select id from cards where fid in (select id from facts where mid = ?)""",
self.save(m) self.save(m)
def remTemplate(self, m, template): def remTemplate(self, m, template):
"False if removing template would leave orphan facts." "False if removing template would leave orphan notes."
# find cards using this template # find cards using this template
ord = m['tmpls'].index(template) ord = m['tmpls'].index(template)
cids = self.deck.db.list(""" cids = self.deck.db.list("""
select c.id from cards c, facts f where c.fid=f.id and mid = ? and ord = ?""", select c.id from cards c, notes f where c.nid=f.id and mid = ? and ord = ?""",
m['id'], ord) m['id'], ord)
# all facts with this template must have at least two cards, or we # all notes with this template must have at least two cards, or we
# could end up creating orphaned facts # could end up creating orphaned notes
if self.deck.db.scalar(""" if self.deck.db.scalar("""
select fid, count() from cards where select nid, count() from cards where
fid in (select fid from cards where id in %s) nid in (select nid from cards where id in %s)
group by fid group by nid
having count() < 2 having count() < 2
limit 1""" % ids2str(cids)): limit 1""" % ids2str(cids)):
return False return False
@ -319,7 +319,7 @@ limit 1""" % ids2str(cids)):
# shift ordinals # shift ordinals
self.deck.db.execute(""" self.deck.db.execute("""
update cards set ord = ord - 1, usn = ?, mod = ? update cards set ord = ord - 1, usn = ?, mod = ?
where fid in (select id from facts where mid = ?) and ord > ?""", where nid in (select id from notes where mid = ?) and ord > ?""",
self.deck.usn(), intTime(), m['id'], ord) self.deck.usn(), intTime(), m['id'], ord)
m['tmpls'].remove(template) m['tmpls'].remove(template)
self._updateTemplOrds(m) self._updateTemplOrds(m)
@ -345,8 +345,8 @@ update cards set ord = ord - 1, usn = ?, mod = ?
# apply # apply
self.save(m) self.save(m)
self.deck.db.execute(""" self.deck.db.execute("""
update cards set ord = (case %s end),usn=?,mod=? where fid in ( update cards set ord = (case %s end),usn=?,mod=? where nid in (
select id from facts where mid = ?)""" % " ".join(map), select id from notes where mid = ?)""" % " ".join(map),
self.deck.usn(), intTime(), m['id']) self.deck.usn(), intTime(), m['id'])
# Model changing # Model changing
@ -354,19 +354,19 @@ select id from facts where mid = ?)""" % " ".join(map),
# - maps are ord->ord, and there should not be duplicate targets # - maps are ord->ord, and there should not be duplicate targets
# - newModel should be self if model is not changing # - newModel should be self if model is not changing
def change(self, m, fids, newModel, fmap, cmap): def change(self, m, nids, newModel, fmap, cmap):
self.deck.modSchema() self.deck.modSchema()
assert newModel['id'] == m['id'] or (fmap and cmap) assert newModel['id'] == m['id'] or (fmap and cmap)
if fmap: if fmap:
self._changeFacts(fids, newModel, fmap) self._changeNotes(nids, newModel, fmap)
if cmap: if cmap:
self._changeCards(fids, newModel, cmap) self._changeCards(nids, newModel, cmap)
def _changeFacts(self, fids, newModel, map): def _changeNotes(self, nids, newModel, map):
d = [] d = []
nfields = len(newModel['flds']) nfields = len(newModel['flds'])
for (fid, flds) in self.deck.db.execute( for (nid, flds) in self.deck.db.execute(
"select id, flds from facts where id in "+ids2str(fids)): "select id, flds from notes where id in "+ids2str(nids)):
newflds = {} newflds = {}
flds = splitFields(flds) flds = splitFields(flds)
for old, new in map.items(): for old, new in map.items():
@ -375,17 +375,17 @@ select id from facts where mid = ?)""" % " ".join(map),
for c in range(nfields): for c in range(nfields):
flds.append(newflds.get(c, "")) flds.append(newflds.get(c, ""))
flds = joinFields(flds) flds = joinFields(flds)
d.append(dict(fid=fid, flds=flds, mid=newModel['id'], d.append(dict(nid=nid, flds=flds, mid=newModel['id'],
m=intTime(),u=self.deck.usn())) m=intTime(),u=self.deck.usn()))
self.deck.db.executemany( self.deck.db.executemany(
"update facts set flds=:flds,mid=:mid,mod=:m,usn=:u where id = :fid", d) "update notes set flds=:flds,mid=:mid,mod=:m,usn=:u where id = :nid", d)
self.deck.updateFieldCache(fids) self.deck.updateFieldCache(nids)
def _changeCards(self, fids, newModel, map): def _changeCards(self, nids, newModel, map):
d = [] d = []
deleted = [] deleted = []
for (cid, ord) in self.deck.db.execute( for (cid, ord) in self.deck.db.execute(
"select id, ord from cards where fid in "+ids2str(fids)): "select id, ord from cards where nid in "+ids2str(nids)):
if map[ord] is not None: if map[ord] is not None:
d.append(dict( d.append(dict(
cid=cid,new=map[ord],u=self.deck.usn(),m=intTime())) cid=cid,new=map[ord],u=self.deck.usn(),m=intTime()))

View file

@ -7,7 +7,7 @@ from anki.errors import AnkiError
from anki.utils import fieldChecksum, intTime, \ from anki.utils import fieldChecksum, intTime, \
joinFields, splitFields, ids2str, stripHTML, timestampID, guid64 joinFields, splitFields, ids2str, stripHTML, timestampID, guid64
class Fact(object): class Note(object):
def __init__(self, deck, model=None, id=None): def __init__(self, deck, model=None, id=None):
assert not (model and id) assert not (model and id)
@ -16,7 +16,7 @@ class Fact(object):
self.id = id self.id = id
self.load() self.load()
else: else:
self.id = timestampID(deck.db, "facts") self.id = timestampID(deck.db, "notes")
self.guid = guid64() self.guid = guid64()
self._model = model self._model = model
self.gid = model['gid'] self.gid = model['gid']
@ -38,7 +38,7 @@ class Fact(object):
self.flags, self.flags,
self.data) = self.deck.db.first(""" self.data) = self.deck.db.first("""
select guid, mid, gid, mod, usn, tags, flds, flags, data select guid, mid, gid, mod, usn, tags, flds, flags, data
from facts where id = ?""", self.id) from notes where id = ?""", self.id)
self.fields = splitFields(self.fields) self.fields = splitFields(self.fields)
self.tags = self.deck.tags.split(self.tags) self.tags = self.deck.tags.split(self.tags)
self._model = self.deck.models.get(self.mid) self._model = self.deck.models.get(self.mid)
@ -52,7 +52,7 @@ from facts where id = ?""", self.id)
sfld = stripHTML(self.fields[self.deck.models.sortIdx(self._model)]) sfld = stripHTML(self.fields[self.deck.models.sortIdx(self._model)])
tags = self.stringTags() tags = self.stringTags()
res = self.deck.db.execute(""" res = self.deck.db.execute("""
insert or replace into facts values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""", insert or replace into notes values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
self.id, self.guid, self.mid, self.gid, self.id, self.guid, self.mid, self.gid,
self.mod, self.usn, tags, self.mod, self.usn, tags,
self.joinedFields(), sfld, self.flags, self.data) self.joinedFields(), sfld, self.flags, self.data)
@ -66,7 +66,7 @@ insert or replace into facts values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
return joinFields(self.fields) return joinFields(self.fields)
def updateFieldChecksums(self): def updateFieldChecksums(self):
self.deck.db.execute("delete from fsums where fid = ?", self.id) self.deck.db.execute("delete from nsums where nid = ?", self.id)
d = [] d = []
for (ord, conf) in self._fmap.values(): for (ord, conf) in self._fmap.values():
if not conf['uniq']: if not conf['uniq']:
@ -75,11 +75,11 @@ insert or replace into facts values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
if not val: if not val:
continue continue
d.append((self.id, self.mid, fieldChecksum(val))) d.append((self.id, self.mid, fieldChecksum(val)))
self.deck.db.executemany("insert into fsums values (?, ?, ?)", d) self.deck.db.executemany("insert into nsums values (?, ?, ?)", d)
def cards(self): def cards(self):
return [self.deck.getCard(id) for id in self.deck.db.list( return [self.deck.getCard(id) for id in self.deck.db.list(
"select id from cards where fid = ? order by ord", self.id)] "select id from cards where nid = ? order by ord", self.id)]
def model(self): def model(self):
return self._model return self._model
@ -151,18 +151,18 @@ insert or replace into facts values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
return True return True
csum = fieldChecksum(val) csum = fieldChecksum(val)
if self.id: if self.id:
lim = "and fid != :fid" lim = "and nid != :nid"
else: else:
lim = "" lim = ""
fids = self.deck.db.list( nids = self.deck.db.list(
"select fid from fsums where csum = ? and fid != ? and mid = ?", "select nid from nsums where csum = ? and nid != ? and mid = ?",
csum, self.id or 0, self.mid) csum, self.id or 0, self.mid)
if not fids: if not nids:
return True return True
# grab facts with the same checksums, and see if they're actually # grab notes with the same checksums, and see if they're actually
# duplicates # duplicates
for flds in self.deck.db.list("select flds from facts where id in "+ for flds in self.deck.db.list("select flds from notes where id in "+
ids2str(fids)): ids2str(nids)):
fields = splitFields(flds) fields = splitFields(flds)
if fields[ord] == val: if fields[ord] == val:
return False return False
@ -185,19 +185,19 @@ insert or replace into facts values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
d.append((ord, None)) d.append((ord, None))
return [x[1] for x in sorted(d)] return [x[1] for x in sorted(d)]
# Flushing cloze facts # Flushing cloze notes
################################################## ##################################################
def _clozePreFlush(self): def _clozePreFlush(self):
self.newlyAdded = not self.deck.db.scalar( self.newlyAdded = not self.deck.db.scalar(
"select 1 from cards where fid = ?", self.id) "select 1 from cards where nid = ?", self.id)
tmpls = self.deck.findTemplates(self) tmpls = self.deck.findTemplates(self)
ok = [] ok = []
for t in tmpls: for t in tmpls:
ok.append(t['ord']) ok.append(t['ord'])
# check if there are cards referencing a deleted cloze # check if there are cards referencing a deleted cloze
if self.deck.db.scalar( if self.deck.db.scalar(
"select 1 from cards where fid = ? and ord not in %s" % "select 1 from cards where nid = ? and ord not in %s" %
ids2str(ok), self.id): ids2str(ok), self.id):
# there are; abort, as the UI should have handled this # there are; abort, as the UI should have handled this
raise Exception("UI should have deleted cloze") raise Exception("UI should have deleted cloze")

View file

@ -269,7 +269,7 @@ select id, due from cards where gid = ? and queue = 0 limit ?""", gid, lim)
self._newQueue.insert(0, self._newQueue.pop()) self._newQueue.insert(0, self._newQueue.pop())
n -= 1 n -= 1
if not n: if not n:
# we only have one fact in the queue; stop rotating # we only have one note in the queue; stop rotating
break break
self.newCount -= 1 self.newCount -= 1
return id return id
@ -605,8 +605,8 @@ gid in %s and queue = 2 and due <= :lim %s limit %d""" % (
conf = self._cardConf(card)['rev'] conf = self._cardConf(card)['rev']
# find sibling positions # find sibling positions
dues = self.deck.db.list( dues = self.deck.db.list(
"select due from cards where fid = ? and queue = 2" "select due from cards where nid = ? and queue = 2"
" and id != ?", card.fid, card.id) " and id != ?", card.nid, card.id)
if not dues or idealDue not in dues: if not dues or idealDue not in dues:
return idealIvl return idealIvl
else: else:
@ -637,7 +637,7 @@ gid in %s and queue = 2 and due <= :lim %s limit %d""" % (
if (lf >= card.lapses and if (lf >= card.lapses and
(card.lapses-lf) % (max(lf/2, 1)) == 0): (card.lapses-lf) % (max(lf/2, 1)) == 0):
# add a leech tag # add a leech tag
f = card.fact() f = card.note()
f.addTag("leech") f.addTag("leech")
f.flush() f.flush()
# handle # handle
@ -782,12 +782,12 @@ your short-term review workload will become."""))
"where queue = -1 and id in "+ ids2str(ids), "where queue = -1 and id in "+ ids2str(ids),
intTime(), self.deck.usn()) intTime(), self.deck.usn())
def buryFact(self, fid): def buryNote(self, nid):
"Bury all cards for fact until next session." "Bury all cards for note until next session."
self.deck.setDirty() self.deck.setDirty()
self.removeFailed( self.removeFailed(
self.deck.db.list("select id from cards where fid = ?", fid)) self.deck.db.list("select id from cards where nid = ?", nid))
self.deck.db.execute("update cards set queue = -2 where fid = ?", fid) self.deck.db.execute("update cards set queue = -2 where nid = ?", nid)
# Resetting # Resetting
########################################################################## ##########################################################################
@ -818,18 +818,18 @@ your short-term review workload will become."""))
def sortCards(self, cids, start=1, step=1, shuffle=False, shift=False): def sortCards(self, cids, start=1, step=1, shuffle=False, shift=False):
scids = ids2str(cids) scids = ids2str(cids)
now = intTime() now = intTime()
fids = self.deck.db.list( nids = self.deck.db.list(
("select distinct fid from cards where type = 0 and id in %s " ("select distinct nid from cards where type = 0 and id in %s "
"order by fid") % scids) "order by nid") % scids)
if not fids: if not nids:
# no new cards # no new cards
return return
# determine fid ordering # determine nid ordering
due = {} due = {}
if shuffle: if shuffle:
random.shuffle(fids) random.shuffle(nids)
for c, fid in enumerate(fids): for c, nid in enumerate(nids):
due[fid] = start+c*step due[nid] = start+c*step
high = start+c*step high = start+c*step
# shift? # shift?
if shift: if shift:
@ -844,9 +844,9 @@ update cards set mod=?, usn=?, due=due+? where id not in %s
and due >= ?""" % scids, now, self.deck.usn(), shiftby, low) and due >= ?""" % scids, now, self.deck.usn(), shiftby, low)
# reorder cards # reorder cards
d = [] d = []
for id, fid in self.deck.db.execute( for id, nid in self.deck.db.execute(
"select id, fid from cards where type = 0 and id in "+scids): "select id, nid from cards where type = 0 and id in "+scids):
d.append(dict(now=now, due=due[fid], usn=self.deck.usn(), cid=id)) d.append(dict(now=now, due=due[nid], usn=self.deck.usn(), cid=id))
self.deck.db.executemany( self.deck.db.executemany(
"update cards set due=:due,mod=:now,usn=:usn where id = :cid""", d) "update cards set due=:due,mod=:now,usn=:usn where id = :cid""", d)

View file

@ -50,7 +50,7 @@ class CardStats(object):
self.addLine(_("Model"), c.model()['name']) self.addLine(_("Model"), c.model()['name'])
self.addLine(_("Template"), c.template()['name']) self.addLine(_("Template"), c.template()['name'])
self.addLine(_("Current Group"), self.deck.groups.name(c.gid)) self.addLine(_("Current Group"), self.deck.groups.name(c.gid))
self.addLine(_("Home Group"), self.deck.groups.name(c.fact().gid)) self.addLine(_("Home Group"), self.deck.groups.name(c.note().gid))
self.txt += "</table>" self.txt += "</table>"
return self.txt return self.txt
@ -538,10 +538,10 @@ group by hour having count() > 30 order by hour""" % lim,
# text data # text data
i = [] i = []
(c, f) = self.deck.db.first(""" (c, f) = self.deck.db.first("""
select count(id), count(distinct fid) from cards select count(id), count(distinct nid) from cards
where gid in %s """ % self._limit()) where gid in %s """ % self._limit())
self._line(i, _("Total cards"), c) self._line(i, _("Total cards"), c)
self._line(i, _("Total facts"), f) self._line(i, _("Total notes"), f)
(low, avg, high) = self._factors() (low, avg, high) = self._factors()
if low: if low:
self._line(i, _("Lowest ease factor"), "%d%%" % low) self._line(i, _("Lowest ease factor"), "%d%%" % low)

View file

@ -80,7 +80,7 @@ create table if not exists deck (
tags text not null tags text not null
); );
create table if not exists facts ( create table if not exists notes (
id integer primary key, id integer primary key,
guid integer not null, guid integer not null,
mid integer not null, mid integer not null,
@ -94,14 +94,14 @@ create table if not exists facts (
data text not null data text not null
); );
create table if not exists fsums ( create table if not exists nsums (
fid integer not null, nid integer not null,
mid integer not null, mid integer not null,
csum integer not null csum integer not null
); );
create table if not exists cards ( create table if not exists cards (
id integer primary key, id integer primary key,
fid integer not null, nid integer not null,
gid integer not null, gid integer not null,
ord integer not null, ord integer not null,
mod integer not null, mod integer not null,
@ -168,16 +168,16 @@ def _updateIndices(db):
"Add indices to the DB." "Add indices to the DB."
db.executescript(""" db.executescript("""
-- syncing -- syncing
create index if not exists ix_facts_usn on facts (usn); create index if not exists ix_notes_usn on notes (usn);
create index if not exists ix_cards_usn on cards (usn); create index if not exists ix_cards_usn on cards (usn);
create index if not exists ix_revlog_usn on revlog (usn); create index if not exists ix_revlog_usn on revlog (usn);
-- card spacing, etc -- card spacing, etc
create index if not exists ix_cards_fid on cards (fid); create index if not exists ix_cards_nid on cards (nid);
-- scheduling and group limiting -- scheduling and group limiting
create index if not exists ix_cards_sched on cards (gid, queue, due); create index if not exists ix_cards_sched on cards (gid, queue, due);
-- revlog by card -- revlog by card
create index if not exists ix_revlog_cid on revlog (cid); create index if not exists ix_revlog_cid on revlog (cid);
-- field uniqueness check -- field uniqueness check
create index if not exists ix_fsums_fid on fsums (fid); create index if not exists ix_nsums_nid on nsums (nid);
create index if not exists ix_fsums_csum on fsums (csum); create index if not exists ix_nsums_csum on nsums (csum);
""") """)

View file

@ -128,10 +128,10 @@ class Syncer(object):
# some basic checks to ensure the sync went ok. this is slow, so will # some basic checks to ensure the sync went ok. this is slow, so will
# be removed before official release # be removed before official release
assert not self.deck.db.scalar(""" assert not self.deck.db.scalar("""
select count() from cards where fid not in (select id from facts)""") select count() from cards where nid not in (select id from notes)""")
assert not self.deck.db.scalar(""" assert not self.deck.db.scalar("""
select count() from facts where id not in (select distinct fid from cards)""") select count() from notes where id not in (select distinct nid from cards)""")
for t in "cards", "facts", "revlog", "graves": for t in "cards", "notes", "revlog", "graves":
assert not self.deck.db.scalar( assert not self.deck.db.scalar(
"select count() from %s where usn = -1" % t) "select count() from %s where usn = -1" % t)
for g in self.deck.groups.all(): for g in self.deck.groups.all():
@ -142,9 +142,9 @@ select count() from facts where id not in (select distinct fid from cards)""")
assert m['usn'] != -1 assert m['usn'] != -1
return [ return [
self.deck.db.scalar("select count() from cards"), self.deck.db.scalar("select count() from cards"),
self.deck.db.scalar("select count() from facts"), self.deck.db.scalar("select count() from notes"),
self.deck.db.scalar("select count() from revlog"), self.deck.db.scalar("select count() from revlog"),
self.deck.db.scalar("select count() from fsums"), self.deck.db.scalar("select count() from nsums"),
self.deck.db.scalar("select count() from graves"), self.deck.db.scalar("select count() from graves"),
len(self.deck.models.all()), len(self.deck.models.all()),
len(self.deck.tags.all()), len(self.deck.tags.all()),
@ -171,7 +171,7 @@ select count() from facts where id not in (select distinct fid from cards)""")
########################################################################## ##########################################################################
def prepareToChunk(self): def prepareToChunk(self):
self.tablesLeft = ["revlog", "cards", "facts"] self.tablesLeft = ["revlog", "cards", "notes"]
self.cursor = None self.cursor = None
def cursorForTable(self, table): def cursorForTable(self, table):
@ -184,12 +184,12 @@ select id, cid, %d, ease, ivl, lastIvl, factor, time, type
from revlog where %s""" % d) from revlog where %s""" % d)
elif table == "cards": elif table == "cards":
return x(""" return x("""
select id, fid, gid, ord, mod, %d, type, queue, due, ivl, factor, reps, select id, nid, gid, ord, mod, %d, type, queue, due, ivl, factor, reps,
lapses, left, edue, flags, data from cards where %s""" % d) lapses, left, edue, flags, data from cards where %s""" % d)
else: else:
return x(""" return x("""
select id, guid, mid, gid, mod, %d, tags, flds, '', flags, data select id, guid, mid, gid, mod, %d, tags, flds, '', flags, data
from facts where %s""" % d) from notes where %s""" % d)
def chunk(self): def chunk(self):
buf = dict(done=False) buf = dict(done=False)
@ -221,15 +221,15 @@ from facts where %s""" % d)
self.mergeRevlog(chunk['revlog']) self.mergeRevlog(chunk['revlog'])
if "cards" in chunk: if "cards" in chunk:
self.mergeCards(chunk['cards']) self.mergeCards(chunk['cards'])
if "facts" in chunk: if "notes" in chunk:
self.mergeFacts(chunk['facts']) self.mergeNotes(chunk['notes'])
# Deletions # Deletions
########################################################################## ##########################################################################
def getGraves(self): def getGraves(self):
cards = [] cards = []
facts = [] notes = []
groups = [] groups = []
if self.deck.server: if self.deck.server:
curs = self.deck.db.execute( curs = self.deck.db.execute(
@ -240,18 +240,18 @@ from facts where %s""" % d)
for oid, type in curs: for oid, type in curs:
if type == REM_CARD: if type == REM_CARD:
cards.append(oid) cards.append(oid)
elif type == REM_FACT: elif type == REM_NOTE:
facts.append(oid) notes.append(oid)
else: else:
groups.append(oid) groups.append(oid)
if not self.deck.server: if not self.deck.server:
self.deck.db.execute("update graves set usn=? where usn=-1", self.deck.db.execute("update graves set usn=? where usn=-1",
self.maxUsn) self.maxUsn)
return dict(cards=cards, facts=facts, groups=groups) return dict(cards=cards, notes=notes, groups=groups)
def mergeGraves(self, graves): def mergeGraves(self, graves):
# facts first, so we don't end up with duplicate graves # notes first, so we don't end up with duplicate graves
self.deck._remFacts(graves['facts']) self.deck._remNotes(graves['notes'])
self.deck.remCards(graves['cards']) self.deck.remCards(graves['cards'])
for oid in graves['groups']: for oid in graves['groups']:
self.deck.groups.rem(oid) self.deck.groups.rem(oid)
@ -326,7 +326,7 @@ from facts where %s""" % d)
def mergeTags(self, tags): def mergeTags(self, tags):
self.deck.tags.register(tags, usn=self.maxUsn) self.deck.tags.register(tags, usn=self.maxUsn)
# Cards/facts/revlog # Cards/notes/revlog
########################################################################## ##########################################################################
def mergeRevlog(self, logs): def mergeRevlog(self, logs):
@ -353,10 +353,10 @@ from facts where %s""" % d)
"(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
self.newerRows(cards, "cards", 4)) self.newerRows(cards, "cards", 4))
def mergeFacts(self, facts): def mergeNotes(self, notes):
rows = self.newerRows(facts, "facts", 4) rows = self.newerRows(notes, "notes", 4)
self.deck.db.executemany( self.deck.db.executemany(
"insert or replace into facts values (?,?,?,?,?,?,?,?,?,?,?)", "insert or replace into notes values (?,?,?,?,?,?,?,?,?,?,?)",
rows) rows)
self.deck.updateFieldCache([f[0] for f in rows]) self.deck.updateFieldCache([f[0] for f in rows])

View file

@ -10,7 +10,7 @@ Anki maintains a cache of used tags so it can quickly present a list of tags
for autocomplete and in the browser. For efficiency, deletions are not for autocomplete and in the browser. For efficiency, deletions are not
tracked, so unused tags can only be removed from the list with a DB check. tracked, so unused tags can only be removed from the list with a DB check.
This module manages the tag cache and tags for facts. This module manages the tag cache and tags for notes.
""" """
class TagManager(object): class TagManager(object):
@ -45,17 +45,17 @@ class TagManager(object):
def all(self): def all(self):
return self.tags.keys() return self.tags.keys()
def registerFacts(self, fids=None): def registerNotes(self, nids=None):
"Add any missing tags from facts to the tags list." "Add any missing tags from notes to the tags list."
# when called without an argument, the old list is cleared first. # when called without an argument, the old list is cleared first.
if fids: if nids:
lim = " where id in " + ids2str(fids) lim = " where id in " + ids2str(nids)
else: else:
lim = "" lim = ""
self.tags = {} self.tags = {}
self.changed = True self.changed = True
self.register(set(self.split( self.register(set(self.split(
" ".join(self.deck.db.list("select distinct tags from facts"+lim))))) " ".join(self.deck.db.list("select distinct tags from notes"+lim)))))
def allItems(self): def allItems(self):
return self.tags.items() return self.tags.items()
@ -63,7 +63,7 @@ class TagManager(object):
def save(self): def save(self):
self.changed = True self.changed = True
# Bulk addition/removal from facts # Bulk addition/removal from notes
############################################################# #############################################################
def bulkAdd(self, ids, tags, add=True): def bulkAdd(self, ids, tags, add=True):
@ -73,7 +73,7 @@ class TagManager(object):
return return
# cache tag names # cache tag names
self.register(newTags) self.register(newTags)
# find facts missing the tags # find notes missing the tags
if add: if add:
l = "tags not " l = "tags not "
fn = self.addToStr fn = self.addToStr
@ -83,18 +83,18 @@ class TagManager(object):
lim = " or ".join( lim = " or ".join(
[l+"like :_%d" % c for c, t in enumerate(newTags)]) [l+"like :_%d" % c for c, t in enumerate(newTags)])
res = self.deck.db.all( res = self.deck.db.all(
"select id, tags from facts where id in %s and %s" % ( "select id, tags from notes where id in %s and %s" % (
ids2str(ids), lim), ids2str(ids), lim),
**dict([("_%d" % x, '%% %s %%' % y) **dict([("_%d" % x, '%% %s %%' % y)
for x, y in enumerate(newTags)])) for x, y in enumerate(newTags)]))
# update tags # update tags
fids = [] nids = []
def fix(row): def fix(row):
fids.append(row[0]) nids.append(row[0])
return {'id': row[0], 't': fn(tags, row[1]), 'n':intTime(), return {'id': row[0], 't': fn(tags, row[1]), 'n':intTime(),
'u':self.deck.usn()} 'u':self.deck.usn()}
self.deck.db.executemany( self.deck.db.executemany(
"update facts set tags=:t,mod=:n,usn=:u where id = :id", "update notes set tags=:t,mod=:n,usn=:u where id = :id",
[fix(row) for row in res]) [fix(row) for row in res])
def bulkRem(self, ids, tags): def bulkRem(self, ids, tags):
@ -149,12 +149,12 @@ class TagManager(object):
# Tag-based selective study # Tag-based selective study
########################################################################## ##########################################################################
def selTagFids(self, yes, no): def selTagNids(self, yes, no):
l = [] l = []
# find facts that match yes # find notes that match yes
lim = "" lim = ""
args = [] args = []
query = "select id from facts" query = "select id from notes"
if not yes and not no: if not yes and not no:
pass pass
else: else:
@ -172,7 +172,7 @@ class TagManager(object):
return self.deck.db.list(query, *args) return self.deck.db.list(query, *args)
def setGroupForTags(self, yes, no, gid): def setGroupForTags(self, yes, no, gid):
fids = self.selTagFids(yes, no) nids = self.selTagNids(yes, no)
self.deck.db.execute( self.deck.db.execute(
"update cards set gid=?,mod=?,usn=? where fid in "+ids2str(fids), "update cards set gid=?,mod=?,usn=? where nid in "+ids2str(nids),
gid, intTime(), self.deck.usn()) gid, intTime(), self.deck.usn())

View file

@ -133,7 +133,7 @@ analyze;""")
db.execute("pragma page_size = 4096") db.execute("pragma page_size = 4096")
db.execute("pragma legacy_file_format = 0") db.execute("pragma legacy_file_format = 0")
# facts # notes
########### ###########
# tags should have a leading and trailing space if not empty, and not # tags should have a leading and trailing space if not empty, and not
# use commas # use commas
@ -177,7 +177,7 @@ select id, id, modelId, 1, cast(created*1000 as int), cast(modified as int),
# and put the facts into the new table # and put the facts into the new table
db.execute("drop table facts") db.execute("drop table facts")
_addSchema(db, False) _addSchema(db, False)
db.executemany("insert into facts values (?,?,?,?,?,?,?,?,'',0,'')", data) db.executemany("insert into notes values (?,?,?,?,?,?,?,?,'',0,'')", data)
db.execute("drop table fields") db.execute("drop table fields")
# cards # cards
@ -336,7 +336,7 @@ insert or replace into deck select id, cast(created as int), :t,
m['flds'] = self._fieldsForModel(row[0]) m['flds'] = self._fieldsForModel(row[0])
m['tmpls'] = self._templatesForModel(row[0], m['flds']) m['tmpls'] = self._templatesForModel(row[0], m['flds'])
mods[m['id']] = m mods[m['id']] = m
db.execute("update facts set mid = ? where mid = ?", t, row[0]) db.execute("update notes set mid = ? where mid = ?", t, row[0])
# save and clean up # save and clean up
db.execute("update deck set models = ?", simplejson.dumps(mods)) db.execute("update deck set models = ?", simplejson.dumps(mods))
db.execute("drop table fieldModels") db.execute("drop table fieldModels")
@ -465,7 +465,7 @@ order by ordinal""", mid)):
# Media references # Media references
###################################################################### ######################################################################
# In 2.0 we drop support for media and latex references in the template, # In 2.0 we drop support for media and latex references in the template,
# since they require generating card templates to see what media a fact # since they require generating card templates to see what media a note
# uses, and are confusing for shared deck users. To ease the upgrade # uses, and are confusing for shared deck users. To ease the upgrade
# process, we automatically convert the references to new fields. # process, we automatically convert the references to new fields.
@ -500,16 +500,16 @@ order by ordinal""", mid)):
# add the new field # add the new field
f = deck.models.newField(fld) f = deck.models.newField(fld)
deck.models.addField(m, f) deck.models.addField(m, f)
# loop through facts and write reference into new field # loop through notes and write reference into new field
data = [] data = []
for id, flds in self.deck.db.execute( for id, flds in self.deck.db.execute(
"select id, flds from facts where id in "+ "select id, flds from notes where id in "+
ids2str(deck.models.fids(m))): ids2str(deck.models.nids(m))):
sflds = splitFields(flds) sflds = splitFields(flds)
ref = all.replace(fname, pre+sflds[idx]+suf) ref = all.replace(fname, pre+sflds[idx]+suf)
data.append((flds+ref, id)) data.append((flds+ref, id))
# update facts # update notes
deck.db.executemany("update facts set flds=? where id=?", deck.db.executemany("update notes set flds=? where id=?",
data) data)
# note field for future # note field for future
state['mflds'][fname] = fld state['mflds'][fname] = fld
@ -544,7 +544,7 @@ order by ordinal""", mid)):
for t in m['tmpls']: for t in m['tmpls']:
if not t['actv']: if not t['actv']:
if not d.db.scalar(""" if not d.db.scalar("""
select 1 from cards where fid in (select id from facts where mid = ?) select 1 from cards where nid in (select id from notes where mid = ?)
and ord = ? limit 1""", m['id'], t['ord']): and ord = ? limit 1""", m['id'], t['ord']):
remove.append(t) remove.append(t)
del t['actv'] del t['actv']
@ -582,7 +582,7 @@ and ord = ? limit 1""", m['id'], t['ord']):
deck.crt = int(time.mktime(d.timetuple())) deck.crt = int(time.mktime(d.timetuple()))
deck.sched._updateCutoff() deck.sched._updateCutoff()
# update uniq cache # update uniq cache
deck.updateFieldCache(deck.db.list("select id from facts")) deck.updateFieldCache(deck.db.list("select id from notes"))
# remove old views # remove old views
for v in ("failedCards", "revCardsOld", "revCardsNew", for v in ("failedCards", "revCardsOld", "revCardsNew",
"revCardsDue", "revCardsRandom", "acqCardsRandom", "revCardsDue", "revCardsRandom", "acqCardsRandom",
@ -595,11 +595,11 @@ and ord = ? limit 1""", m['id'], t['ord']):
deck.db.execute("update cards set queue=-2 where queue between 3 and 5") deck.db.execute("update cards set queue=-2 where queue between 3 and 5")
deck.db.execute("update cards set queue=-3 where queue between 6 and 8") deck.db.execute("update cards set queue=-3 where queue between 6 and 8")
# remove old deleted tables # remove old deleted tables
for t in ("cards", "facts", "models", "media"): for t in ("cards", "notes", "models", "media"):
deck.db.execute("drop table if exists %sDeleted" % t) deck.db.execute("drop table if exists %sDeleted" % t)
# rewrite due times for new cards # rewrite due times for new cards
deck.db.execute(""" deck.db.execute("""
update cards set due = fid where type=0""") update cards set due = nid where type=0""")
# and failed cards # and failed cards
left = len(deck.groups.conf(1)['new']['delays']) left = len(deck.groups.conf(1)['new']['delays'])
deck.db.execute("update cards set edue = ?, left=? where type = 1", deck.db.execute("update cards set edue = ?, left=? where type = 1",
@ -614,7 +614,7 @@ update cards set due = cast(
if deck.models.randomNew(): if deck.models.randomNew():
deck.sched.randomizeCards() deck.sched.randomizeCards()
# update insertion id # update insertion id
deck.conf['nextPos'] = deck.db.scalar("select max(id) from facts")+1 deck.conf['nextPos'] = deck.db.scalar("select max(id) from notes")+1
deck.save() deck.save()
# optimize and finish # optimize and finish

View file

@ -185,7 +185,7 @@ def timestampID(db, table):
def maxID(db): def maxID(db):
"Return the first safe ID to use." "Return the first safe ID to use."
now = intTime(1000) now = intTime(1000)
for tbl in "cards", "facts": for tbl in "cards", "notes":
now = max(now, db.scalar( now = max(now, db.scalar(
"select max(id) from %s" % tbl)) "select max(id) from %s" % tbl))
return now + 1 return now + 1

View file

@ -15,12 +15,12 @@ def setup1():
deck = Deck() deck = Deck()
deck.addModel(BasicModel()) deck.addModel(BasicModel())
deck.currentModel.cardModels[1].active = True deck.currentModel.cardModels[1].active = True
f = deck.newFact() f = deck.newNote()
f['Front'] = u"foo"; f['Back'] = u"bar"; f.tags = u"tag, tag2" f['Front'] = u"foo"; f['Back'] = u"bar"; f.tags = u"tag, tag2"
deck.addFact(f) deck.addNote(f)
f = deck.newFact() f = deck.newNote()
f['Front'] = u"baz"; f['Back'] = u"qux" f['Front'] = u"baz"; f['Back'] = u"qux"
deck.addFact(f) deck.addNote(f)
########################################################################## ##########################################################################
@ -53,8 +53,8 @@ def test_export_textcard():
e.exportInto(f) e.exportInto(f)
@nose.with_setup(setup1) @nose.with_setup(setup1)
def test_export_textfact(): def test_export_textnote():
e = TextFactExporter(deck) e = TextNoteExporter(deck)
f = unicode(tempfile.mkstemp(prefix="ankitest")[1]) f = unicode(tempfile.mkstemp(prefix="ankitest")[1])
os.unlink(f) os.unlink(f)
e.exportInto(f) e.exportInto(f)

View file

@ -8,7 +8,7 @@ from tests.shared import getEmptyDeck
def test_previewCards(): def test_previewCards():
deck = getEmptyDeck() deck = getEmptyDeck()
f = deck.newFact() f = deck.newNote()
f['Front'] = u'1' f['Front'] = u'1'
f['Back'] = u'2' f['Back'] = u'2'
# non-empty and active # non-empty and active
@ -18,8 +18,8 @@ def test_previewCards():
# all templates # all templates
cards = deck.previewCards(f, 2) cards = deck.previewCards(f, 2)
assert len(cards) == 1 assert len(cards) == 1
# add the fact, and test existing preview # add the note, and test existing preview
deck.addFact(f) deck.addNote(f)
cards = deck.previewCards(f, 1) cards = deck.previewCards(f, 1)
assert len(cards) == 1 assert len(cards) == 1
assert cards[0].ord == 0 assert cards[0].ord == 0
@ -28,29 +28,29 @@ def test_previewCards():
def test_delete(): def test_delete():
deck = getEmptyDeck() deck = getEmptyDeck()
f = deck.newFact() f = deck.newNote()
f['Front'] = u'1' f['Front'] = u'1'
f['Back'] = u'2' f['Back'] = u'2'
deck.addFact(f) deck.addNote(f)
cid = f.cards()[0].id cid = f.cards()[0].id
deck.reset() deck.reset()
deck.sched.answerCard(deck.sched.getCard(), 2) deck.sched.answerCard(deck.sched.getCard(), 2)
assert deck.db.scalar("select count() from revlog") == 1 assert deck.db.scalar("select count() from revlog") == 1
deck.remCards([cid]) deck.remCards([cid])
assert deck.cardCount() == 0 assert deck.cardCount() == 0
assert deck.factCount() == 0 assert deck.noteCount() == 0
assert deck.db.scalar("select count() from facts") == 0 assert deck.db.scalar("select count() from notes") == 0
assert deck.db.scalar("select count() from cards") == 0 assert deck.db.scalar("select count() from cards") == 0
assert deck.db.scalar("select count() from fsums") == 0 assert deck.db.scalar("select count() from nsums") == 0
assert deck.db.scalar("select count() from revlog") == 0 assert deck.db.scalar("select count() from revlog") == 0
assert deck.db.scalar("select count() from graves") == 2 assert deck.db.scalar("select count() from graves") == 2
def test_misc(): def test_misc():
d = getEmptyDeck() d = getEmptyDeck()
f = d.newFact() f = d.newNote()
f['Front'] = u'1' f['Front'] = u'1'
f['Back'] = u'2' f['Back'] = u'2'
d.addFact(f) d.addNote(f)
c = f.cards()[0] c = f.cards()[0]
id = d.models.current()['id'] id = d.models.current()['id']
assert c.template()['ord'] == 0 assert c.template()['ord'] == 0

View file

@ -41,12 +41,12 @@ def test_openReadOnly():
os.chmod(newPath, 0666) os.chmod(newPath, 0666)
os.unlink(newPath) os.unlink(newPath)
def test_factAddDelete(): def test_noteAddDelete():
deck = getEmptyDeck() deck = getEmptyDeck()
# add a fact # add a note
f = deck.newFact() f = deck.newNote()
f['Front'] = u"one"; f['Back'] = u"two" f['Front'] = u"one"; f['Back'] = u"two"
n = deck.addFact(f) n = deck.addNote(f)
assert n == 1 assert n == 1
# test multiple cards - add another template # test multiple cards - add another template
m = deck.models.current(); mm = deck.models m = deck.models.current(); mm = deck.models
@ -61,10 +61,10 @@ def test_factAddDelete():
# should generate cards on close # should generate cards on close
mm.save(m, gencards=True) mm.save(m, gencards=True)
assert deck.cardCount() == 2 assert deck.cardCount() == 2
# creating new facts should use both cards # creating new notes should use both cards
f = deck.newFact() f = deck.newNote()
f['Front'] = u"three"; f['Back'] = u"four" f['Front'] = u"three"; f['Back'] = u"four"
n = deck.addFact(f) n = deck.addNote(f)
assert n == 2 assert n == 2
assert deck.cardCount() == 4 assert deck.cardCount() == 4
# check q/a generation # check q/a generation
@ -74,7 +74,7 @@ def test_factAddDelete():
for p in f.problems(): for p in f.problems():
assert not p assert not p
# now let's make a duplicate and test uniqueness # now let's make a duplicate and test uniqueness
f2 = deck.newFact() f2 = deck.newNote()
f2.model()['flds'][1]['req'] = True f2.model()['flds'][1]['req'] = True
f2['Front'] = u"one"; f2['Back'] = u"" f2['Front'] = u"one"; f2['Back'] = u""
p = f2.problems() p = f2.problems()
@ -84,66 +84,66 @@ def test_factAddDelete():
cards = f.cards() cards = f.cards()
id1 = cards[0].id; id2 = cards[1].id id1 = cards[0].id; id2 = cards[1].id
assert deck.cardCount() == 4 assert deck.cardCount() == 4
assert deck.factCount() == 2 assert deck.noteCount() == 2
deck.remCards([id1]) deck.remCards([id1])
assert deck.cardCount() == 3 assert deck.cardCount() == 3
assert deck.factCount() == 2 assert deck.noteCount() == 2
# and the second should clear the fact # and the second should clear the note
deck.remCards([id2]) deck.remCards([id2])
assert deck.cardCount() == 2 assert deck.cardCount() == 2
assert deck.factCount() == 1 assert deck.noteCount() == 1
def test_fieldChecksum(): def test_fieldChecksum():
deck = getEmptyDeck() deck = getEmptyDeck()
f = deck.newFact() f = deck.newNote()
f['Front'] = u"new"; f['Back'] = u"new2" f['Front'] = u"new"; f['Back'] = u"new2"
deck.addFact(f) deck.addNote(f)
assert deck.db.scalar( assert deck.db.scalar(
"select csum from fsums") == int("c2a6b03f", 16) "select csum from nsums") == int("c2a6b03f", 16)
# empty field should have no checksum # empty field should have no checksum
f['Front'] = u"" f['Front'] = u""
f.flush() f.flush()
assert deck.db.scalar( assert deck.db.scalar(
"select count() from fsums") == 0 "select count() from nsums") == 0
# changing the val should change the checksum # changing the val should change the checksum
f['Front'] = u"newx" f['Front'] = u"newx"
f.flush() f.flush()
assert deck.db.scalar( assert deck.db.scalar(
"select csum from fsums") == int("302811ae", 16) "select csum from nsums") == int("302811ae", 16)
# turning off unique and modifying the fact should delete the sum # turning off unique and modifying the note should delete the sum
m = f.model() m = f.model()
m['flds'][0]['uniq'] = False m['flds'][0]['uniq'] = False
deck.models.save(m) deck.models.save(m)
f.flush() f.flush()
assert deck.db.scalar( assert deck.db.scalar(
"select count() from fsums") == 0 "select count() from nsums") == 0
# and turning on both should ensure two checksums generated # and turning on both should ensure two checksums generated
m['flds'][0]['uniq'] = True m['flds'][0]['uniq'] = True
m['flds'][1]['uniq'] = True m['flds'][1]['uniq'] = True
deck.models.save(m) deck.models.save(m)
f.flush() f.flush()
assert deck.db.scalar( assert deck.db.scalar(
"select count() from fsums") == 2 "select count() from nsums") == 2
def test_selective(): def test_selective():
deck = getEmptyDeck() deck = getEmptyDeck()
f = deck.newFact() f = deck.newNote()
f['Front'] = u"1"; f.tags = ["one", "three"] f['Front'] = u"1"; f.tags = ["one", "three"]
deck.addFact(f) deck.addNote(f)
f = deck.newFact() f = deck.newNote()
f['Front'] = u"2"; f.tags = ["two", "three", "four"] f['Front'] = u"2"; f.tags = ["two", "three", "four"]
deck.addFact(f) deck.addNote(f)
f = deck.newFact() f = deck.newNote()
f['Front'] = u"3"; f.tags = ["one", "two", "three", "four"] f['Front'] = u"3"; f.tags = ["one", "two", "three", "four"]
deck.addFact(f) deck.addNote(f)
assert len(deck.tags.selTagFids(["one"], [])) == 2 assert len(deck.tags.selTagNids(["one"], [])) == 2
assert len(deck.tags.selTagFids(["three"], [])) == 3 assert len(deck.tags.selTagNids(["three"], [])) == 3
assert len(deck.tags.selTagFids([], ["three"])) == 0 assert len(deck.tags.selTagNids([], ["three"])) == 0
assert len(deck.tags.selTagFids(["one"], ["three"])) == 0 assert len(deck.tags.selTagNids(["one"], ["three"])) == 0
assert len(deck.tags.selTagFids(["one"], ["two"])) == 1 assert len(deck.tags.selTagNids(["one"], ["two"])) == 1
assert len(deck.tags.selTagFids(["two", "three"], [])) == 3 assert len(deck.tags.selTagNids(["two", "three"], [])) == 3
assert len(deck.tags.selTagFids(["two", "three"], ["one"])) == 1 assert len(deck.tags.selTagNids(["two", "three"], ["one"])) == 1
assert len(deck.tags.selTagFids(["one", "three"], ["two", "four"])) == 1 assert len(deck.tags.selTagNids(["one", "three"], ["two", "four"])) == 1
deck.tags.setGroupForTags(["three"], [], 3) deck.tags.setGroupForTags(["three"], [], 3)
assert deck.db.scalar("select count() from cards where gid = 3") == 3 assert deck.db.scalar("select count() from cards where gid = 3") == 3
deck.tags.setGroupForTags(["one"], [], 2) deck.tags.setGroupForTags(["one"], [], 2)
@ -151,12 +151,12 @@ def test_selective():
def test_addDelTags(): def test_addDelTags():
deck = getEmptyDeck() deck = getEmptyDeck()
f = deck.newFact() f = deck.newNote()
f['Front'] = u"1" f['Front'] = u"1"
deck.addFact(f) deck.addNote(f)
f2 = deck.newFact() f2 = deck.newNote()
f2['Front'] = u"2" f2['Front'] = u"2"
deck.addFact(f2) deck.addNote(f2)
# adding for a given id # adding for a given id
deck.tags.bulkAdd([f.id], "foo") deck.tags.bulkAdd([f.id], "foo")
f.load(); f2.load() f.load(); f2.load()

View file

@ -4,25 +4,25 @@ from tests.shared import getEmptyDeck
def test_findCards(): def test_findCards():
deck = getEmptyDeck() deck = getEmptyDeck()
f = deck.newFact() f = deck.newNote()
f['Front'] = u'dog' f['Front'] = u'dog'
f['Back'] = u'cat' f['Back'] = u'cat'
f.tags.append(u"monkey") f.tags.append(u"monkey")
f1id = f.id f1id = f.id
deck.addFact(f) deck.addNote(f)
firstCardId = f.cards()[0].id firstCardId = f.cards()[0].id
f = deck.newFact() f = deck.newNote()
f['Front'] = u'goats are fun' f['Front'] = u'goats are fun'
f['Back'] = u'sheep' f['Back'] = u'sheep'
f.tags.append(u"sheep goat horse") f.tags.append(u"sheep goat horse")
deck.addFact(f) deck.addNote(f)
f2id = f.id f2id = f.id
f = deck.newFact() f = deck.newNote()
f['Front'] = u'cat' f['Front'] = u'cat'
f['Back'] = u'sheep' f['Back'] = u'sheep'
deck.addFact(f) deck.addNote(f)
catCard = f.cards()[0] catCard = f.cards()[0]
f = deck.newFact() f = deck.newNote()
f['Front'] = u'template test' f['Front'] = u'template test'
f['Back'] = u'foo bar' f['Back'] = u'foo bar'
m = deck.models.current(); mm = deck.models m = deck.models.current(); mm = deck.models
@ -31,7 +31,7 @@ def test_findCards():
t['afmt'] = "{{Front}}" t['afmt'] = "{{Front}}"
mm.addTemplate(m, t) mm.addTemplate(m, t)
mm.save(m) mm.save(m)
deck.addFact(f) deck.addNote(f)
latestCardIds = [c.id for c in f.cards()] latestCardIds = [c.id for c in f.cards()]
# tag searches # tag searches
assert not deck.findCards("tag:donkey") assert not deck.findCards("tag:donkey")
@ -41,11 +41,11 @@ def test_findCards():
assert len(deck.findCards("tag:monkey")) == 1 assert len(deck.findCards("tag:monkey")) == 1
assert len(deck.findCards("tag:sheep -tag:monkey")) == 1 assert len(deck.findCards("tag:sheep -tag:monkey")) == 1
assert len(deck.findCards("-tag:sheep")) == 4 assert len(deck.findCards("-tag:sheep")) == 4
deck.tags.bulkAdd(deck.db.list("select id from facts"), "foo bar") deck.tags.bulkAdd(deck.db.list("select id from notes"), "foo bar")
assert (len(deck.findCards("tag:foo")) == assert (len(deck.findCards("tag:foo")) ==
len(deck.findCards("tag:bar")) == len(deck.findCards("tag:bar")) ==
5) 5)
deck.tags.bulkRem(deck.db.list("select id from facts"), "foo") deck.tags.bulkRem(deck.db.list("select id from notes"), "foo")
assert len(deck.findCards("tag:foo")) == 0 assert len(deck.findCards("tag:foo")) == 0
assert len(deck.findCards("tag:bar")) == 5 assert len(deck.findCards("tag:bar")) == 5
# text searches # text searches
@ -71,10 +71,10 @@ def test_findCards():
import time; time.sleep(1) import time; time.sleep(1)
c.flush() c.flush()
assert deck.findCards("is:suspended") == [c.id] assert deck.findCards("is:suspended") == [c.id]
# fids # nids
assert deck.findCards("fid:54321") == [] assert deck.findCards("nid:54321") == []
assert len(deck.findCards("fid:%d"%f.id)) == 2 assert len(deck.findCards("nid:%d"%f.id)) == 2
assert len(deck.findCards("fid:%d,%d" % (f1id, f2id))) == 2 assert len(deck.findCards("nid:%d,%d" % (f1id, f2id))) == 2
# templates # templates
assert len(deck.findCards("card:foo")) == 0 assert len(deck.findCards("card:foo")) == 0
assert len(deck.findCards("card:forward")) == 4 assert len(deck.findCards("card:forward")) == 4
@ -89,10 +89,10 @@ def test_findCards():
assert len(deck.findCards("-back:sheep")) == 3 assert len(deck.findCards("-back:sheep")) == 3
assert len(deck.findCards("front:")) == 5 assert len(deck.findCards("front:")) == 5
# ordering # ordering
deck.conf['sortType'] = "factCrt" deck.conf['sortType'] = "noteCrt"
assert deck.findCards("front:")[-1] in latestCardIds assert deck.findCards("front:")[-1] in latestCardIds
assert deck.findCards("")[-1] in latestCardIds assert deck.findCards("")[-1] in latestCardIds
deck.conf['sortType'] = "factFld" deck.conf['sortType'] = "noteFld"
assert deck.findCards("")[0] == catCard.id assert deck.findCards("")[0] == catCard.id
assert deck.findCards("")[-1] in latestCardIds assert deck.findCards("")[-1] in latestCardIds
deck.conf['sortType'] = "cardMod" deck.conf['sortType'] = "cardMod"
@ -109,10 +109,10 @@ def test_findCards():
assert len(deck.findCards("-group:default")) == 0 assert len(deck.findCards("-group:default")) == 0
assert len(deck.findCards("-group:foo")) == 5 assert len(deck.findCards("-group:foo")) == 5
# full search # full search
f = deck.newFact() f = deck.newNote()
f['Front'] = u'hello<b>world</b>' f['Front'] = u'hello<b>world</b>'
f['Back'] = u'' f['Back'] = u''
deck.addFact(f) deck.addNote(f)
assert len(deck.findCards("helloworld")) == 0 assert len(deck.findCards("helloworld")) == 0
assert len(deck.findCards("helloworld", full=True)) == 1 assert len(deck.findCards("helloworld", full=True)) == 1
assert len(deck.findCards("front:helloworld")) == 0 assert len(deck.findCards("front:helloworld")) == 0
@ -122,27 +122,27 @@ def test_findCards():
def test_findReplace(): def test_findReplace():
deck = getEmptyDeck() deck = getEmptyDeck()
f = deck.newFact() f = deck.newNote()
f['Front'] = u'foo' f['Front'] = u'foo'
f['Back'] = u'bar' f['Back'] = u'bar'
deck.addFact(f) deck.addNote(f)
f2 = deck.newFact() f2 = deck.newNote()
f2['Front'] = u'baz' f2['Front'] = u'baz'
f2['Back'] = u'foo' f2['Back'] = u'foo'
deck.addFact(f2) deck.addNote(f2)
fids = [f.id, f2.id] nids = [f.id, f2.id]
# should do nothing # should do nothing
assert deck.findReplace(fids, "abc", "123") == 0 assert deck.findReplace(nids, "abc", "123") == 0
# global replace # global replace
assert deck.findReplace(fids, "foo", "qux") == 2 assert deck.findReplace(nids, "foo", "qux") == 2
f.load(); assert f['Front'] == "qux" f.load(); assert f['Front'] == "qux"
f2.load(); assert f2['Back'] == "qux" f2.load(); assert f2['Back'] == "qux"
# single field replace # single field replace
assert deck.findReplace(fids, "qux", "foo", field="Front") == 1 assert deck.findReplace(nids, "qux", "foo", field="Front") == 1
f.load(); assert f['Front'] == "foo" f.load(); assert f['Front'] == "foo"
f2.load(); assert f2['Back'] == "qux" f2.load(); assert f2['Back'] == "qux"
# regex replace # regex replace
assert deck.findReplace(fids, "B.r", "reg") == 0 assert deck.findReplace(nids, "B.r", "reg") == 0
f.load(); assert f['Back'] != "reg" f.load(); assert f['Back'] != "reg"
assert deck.findReplace(fids, "B.r", "reg", regex=True) == 1 assert deck.findReplace(nids, "B.r", "reg", regex=True) == 1
f.load(); assert f['Back'] == "reg" f.load(); assert f['Back'] == "reg"

View file

@ -39,12 +39,12 @@ def test_remove():
deck = getEmptyDeck() deck = getEmptyDeck()
# can't remove the default group # can't remove the default group
assertException(AssertionError, lambda: deck.groups.rem(1)) assertException(AssertionError, lambda: deck.groups.rem(1))
# create a new group, and add a fact/card to it # create a new group, and add a note/card to it
g1 = deck.groups.id("g1") g1 = deck.groups.id("g1")
f = deck.newFact() f = deck.newNote()
f['Front'] = u"1" f['Front'] = u"1"
f.gid = g1 f.gid = g1
deck.addFact(f) deck.addNote(f)
c = f.cards()[0] c = f.cards()[0]
assert c.gid == g1 assert c.gid == g1
# by default deleting the group leaves the cards with an invalid gid # by default deleting the group leaves the cards with an invalid gid
@ -58,10 +58,10 @@ def test_remove():
# let's create another group and explicitly set the card to it # let's create another group and explicitly set the card to it
g2 = deck.groups.id("g2") g2 = deck.groups.id("g2")
c.gid = g2; c.flush() c.gid = g2; c.flush()
# this time we'll delete the card/fact too # this time we'll delete the card/note too
deck.groups.rem(g2, cardsToo=True) deck.groups.rem(g2, cardsToo=True)
assert deck.cardCount() == 0 assert deck.cardCount() == 0
assert deck.factCount() == 0 assert deck.noteCount() == 0
def test_rename(): def test_rename():
d = getEmptyDeck() d = getEmptyDeck()

View file

@ -8,23 +8,23 @@ from anki.errors import *
from anki import Deck from anki import Deck
from anki.importing import Anki1Importer, Anki2Importer, TextImporter, \ from anki.importing import Anki1Importer, Anki2Importer, TextImporter, \
SupermemoXmlImporter SupermemoXmlImporter
from anki.facts import Fact from anki.notes import Note
from anki.db import * from anki.db import *
testDir = os.path.dirname(__file__) testDir = os.path.dirname(__file__)
srcFacts=None srcNotes=None
srcCards=None srcCards=None
def test_anki2(): def test_anki2():
global srcFacts, srcCards global srcNotes, srcCards
# get the deck to import # get the deck to import
tmp = getUpgradeDeckPath() tmp = getUpgradeDeckPath()
u = Upgrader() u = Upgrader()
src = u.upgrade(tmp) src = u.upgrade(tmp)
srcpath = src.path srcpath = src.path
srcFacts = src.factCount() srcNotes = src.noteCount()
srcCards = src.cardCount() srcCards = src.cardCount()
srcRev = src.db.scalar("select count() from revlog") srcRev = src.db.scalar("select count() from revlog")
# add a media file for testing # add a media file for testing
@ -36,14 +36,14 @@ def test_anki2():
imp = Anki2Importer(dst, srcpath) imp = Anki2Importer(dst, srcpath)
imp.run() imp.run()
def check(): def check():
assert dst.factCount() == srcFacts assert dst.noteCount() == srcNotes
assert dst.cardCount() == srcCards assert dst.cardCount() == srcCards
assert srcRev == dst.db.scalar("select count() from revlog") assert srcRev == dst.db.scalar("select count() from revlog")
mids = [int(x) for x in dst.models.models.keys()] mids = [int(x) for x in dst.models.models.keys()]
assert not dst.db.scalar( assert not dst.db.scalar(
"select count() from facts where mid not in "+ids2str(mids)) "select count() from notes where mid not in "+ids2str(mids))
assert not dst.db.scalar( assert not dst.db.scalar(
"select count() from cards where fid not in (select id from facts)") "select count() from cards where nid not in (select id from notes)")
assert not dst.db.scalar( assert not dst.db.scalar(
"select count() from revlog where cid not in (select id from cards)") "select count() from revlog where cid not in (select id from cards)")
assert dst.fixIntegrity().startswith("Database rebuilt") assert dst.fixIntegrity().startswith("Database rebuilt")
@ -68,7 +68,7 @@ def test_anki1():
imp = Anki1Importer(dst, tmp) imp = Anki1Importer(dst, tmp)
imp.run() imp.run()
def check(): def check():
assert dst.factCount() == srcFacts assert dst.noteCount() == srcNotes
assert dst.cardCount() == srcCards assert dst.cardCount() == srcCards
assert len(os.listdir(dst.media.dir())) == 1 assert len(os.listdir(dst.media.dir())) == 1
check() check()
@ -96,9 +96,9 @@ def test_csv_tags():
file = unicode(os.path.join(testDir, "importing/text-tags.txt")) file = unicode(os.path.join(testDir, "importing/text-tags.txt"))
i = csvfile.TextImporter(deck, file) i = csvfile.TextImporter(deck, file)
i.run() i.run()
facts = deck.db.query(Fact).all() notes = deck.db.query(Note).all()
assert len(facts) == 2 assert len(notes) == 2
assert facts[0].tags == "baz qux" or facts[1].tags == "baz qux" assert notes[0].tags == "baz qux" or notes[1].tags == "baz qux"
deck.close() deck.close()
def test_supermemo_xml_01_unicode(): def test_supermemo_xml_01_unicode():

View file

@ -10,10 +10,10 @@ def test_latex():
# change latex cmd to simulate broken build # change latex cmd to simulate broken build
import anki.latex import anki.latex
anki.latex.latexCmd[0] = "nolatex" anki.latex.latexCmd[0] = "nolatex"
# add a fact with latex # add a note with latex
f = d.newFact() f = d.newNote()
f['Front'] = u"[latex]hello[/latex]" f['Front'] = u"[latex]hello[/latex]"
d.addFact(f) d.addNote(f)
# but since latex couldn't run, there's nothing there # but since latex couldn't run, there's nothing there
assert len(os.listdir(d.media.dir())) == 0 assert len(os.listdir(d.media.dir())) == 0
# check the error message # check the error message
@ -31,25 +31,25 @@ def test_latex():
d.media.check() d.media.check()
assert len(os.listdir(d.media.dir())) == 1 assert len(os.listdir(d.media.dir())) == 1
assert ".png" in f.cards()[0].q() assert ".png" in f.cards()[0].q()
# adding new facts should cause generation on question display # adding new notes should cause generation on question display
f = d.newFact() f = d.newNote()
f['Front'] = u"[latex]world[/latex]" f['Front'] = u"[latex]world[/latex]"
d.addFact(f) d.addNote(f)
f.cards()[0].q() f.cards()[0].q()
assert len(os.listdir(d.media.dir())) == 2 assert len(os.listdir(d.media.dir())) == 2
# another fact with the same media should reuse # another note with the same media should reuse
f = d.newFact() f = d.newNote()
f['Front'] = u" [latex]world[/latex]" f['Front'] = u" [latex]world[/latex]"
d.addFact(f) d.addNote(f)
assert len(os.listdir(d.media.dir())) == 2 assert len(os.listdir(d.media.dir())) == 2
oldcard = f.cards()[0] oldcard = f.cards()[0]
assert ".png" in oldcard.q() assert ".png" in oldcard.q()
# if we turn off building, then previous cards should work, but cards with # if we turn off building, then previous cards should work, but cards with
# missing media will show the latex # missing media will show the latex
anki.latex.build = False anki.latex.build = False
f = d.newFact() f = d.newNote()
f['Front'] = u"[latex]foo[/latex]" f['Front'] = u"[latex]foo[/latex]"
d.addFact(f) d.addNote(f)
assert len(os.listdir(d.media.dir())) == 2 assert len(os.listdir(d.media.dir())) == 2
assert stripHTML(f.cards()[0].q()) == "[latex]foo[/latex]" assert stripHTML(f.cards()[0].q()) == "[latex]foo[/latex]"
assert ".png" in oldcard.q() assert ".png" in oldcard.q()

View file

@ -46,14 +46,14 @@ def test_deckIntegration():
# put a file into it # put a file into it
file = unicode(os.path.join(testDir, "support/fake.png")) file = unicode(os.path.join(testDir, "support/fake.png"))
d.media.addFile(file) d.media.addFile(file)
# add a fact which references it # add a note which references it
f = d.newFact() f = d.newNote()
f['Front'] = u"one"; f['Back'] = u"<img src='fake.png'>" f['Front'] = u"one"; f['Back'] = u"<img src='fake.png'>"
d.addFact(f) d.addNote(f)
# and one which references a non-existent file # and one which references a non-existent file
f = d.newFact() f = d.newNote()
f['Front'] = u"one"; f['Back'] = u"<img src='fake2.png'>" f['Front'] = u"one"; f['Back'] = u"<img src='fake2.png'>"
d.addFact(f) d.addNote(f)
# and add another file which isn't used # and add another file which isn't used
open(os.path.join(d.media.dir(), "foo.jpg"), "wb").write("test") open(os.path.join(d.media.dir(), "foo.jpg"), "wb").write("test")
# check media # check media

View file

@ -5,10 +5,10 @@ from anki.utils import stripHTML
def test_modelDelete(): def test_modelDelete():
deck = getEmptyDeck() deck = getEmptyDeck()
f = deck.newFact() f = deck.newNote()
f['Front'] = u'1' f['Front'] = u'1'
f['Back'] = u'2' f['Back'] = u'2'
deck.addFact(f) deck.addNote(f)
assert deck.cardCount() == 1 assert deck.cardCount() == 1
deck.models.rem(deck.models.current()) deck.models.rem(deck.models.current())
assert deck.cardCount() == 0 assert deck.cardCount() == 0
@ -28,10 +28,10 @@ def test_modelCopy():
def test_fields(): def test_fields():
d = getEmptyDeck() d = getEmptyDeck()
f = d.newFact() f = d.newNote()
f['Front'] = u'1' f['Front'] = u'1'
f['Back'] = u'2' f['Back'] = u'2'
d.addFact(f) d.addNote(f)
m = d.models.current() m = d.models.current()
# make sure renaming a field updates the templates # make sure renaming a field updates the templates
d.models.renameField(m, m['flds'][0], "NewFront") d.models.renameField(m, m['flds'][0], "NewFront")
@ -41,37 +41,37 @@ def test_fields():
f = d.models.newField(m) f = d.models.newField(m)
f['name'] = "foo" f['name'] = "foo"
d.models.addField(m, f) d.models.addField(m, f)
assert d.getFact(d.models.fids(m)[0]).fields == ["1", "2", ""] assert d.getNote(d.models.nids(m)[0]).fields == ["1", "2", ""]
assert d.models.scmhash(m) != h assert d.models.scmhash(m) != h
# rename it # rename it
d.models.renameField(m, f, "bar") d.models.renameField(m, f, "bar")
assert d.getFact(d.models.fids(m)[0])['bar'] == '' assert d.getNote(d.models.nids(m)[0])['bar'] == ''
# delete back # delete back
d.models.remField(m, m['flds'][1]) d.models.remField(m, m['flds'][1])
assert d.getFact(d.models.fids(m)[0]).fields == ["1", ""] assert d.getNote(d.models.nids(m)[0]).fields == ["1", ""]
# move 0 -> 1 # move 0 -> 1
d.models.moveField(m, m['flds'][0], 1) d.models.moveField(m, m['flds'][0], 1)
assert d.getFact(d.models.fids(m)[0]).fields == ["", "1"] assert d.getNote(d.models.nids(m)[0]).fields == ["", "1"]
# move 1 -> 0 # move 1 -> 0
d.models.moveField(m, m['flds'][1], 0) d.models.moveField(m, m['flds'][1], 0)
assert d.getFact(d.models.fids(m)[0]).fields == ["1", ""] assert d.getNote(d.models.nids(m)[0]).fields == ["1", ""]
# add another and put in middle # add another and put in middle
f = d.models.newField(m) f = d.models.newField(m)
f['name'] = "baz" f['name'] = "baz"
d.models.addField(m, f) d.models.addField(m, f)
f = d.getFact(d.models.fids(m)[0]) f = d.getNote(d.models.nids(m)[0])
f['baz'] = "2" f['baz'] = "2"
f.flush() f.flush()
assert d.getFact(d.models.fids(m)[0]).fields == ["1", "", "2"] assert d.getNote(d.models.nids(m)[0]).fields == ["1", "", "2"]
# move 2 -> 1 # move 2 -> 1
d.models.moveField(m, m['flds'][2], 1) d.models.moveField(m, m['flds'][2], 1)
assert d.getFact(d.models.fids(m)[0]).fields == ["1", "2", ""] assert d.getNote(d.models.nids(m)[0]).fields == ["1", "2", ""]
# move 0 -> 2 # move 0 -> 2
d.models.moveField(m, m['flds'][0], 2) d.models.moveField(m, m['flds'][0], 2)
assert d.getFact(d.models.fids(m)[0]).fields == ["2", "", "1"] assert d.getNote(d.models.nids(m)[0]).fields == ["2", "", "1"]
# move 0 -> 1 # move 0 -> 1
d.models.moveField(m, m['flds'][0], 1) d.models.moveField(m, m['flds'][0], 1)
assert d.getFact(d.models.fids(m)[0]).fields == ["", "2", "1"] assert d.getNote(d.models.nids(m)[0]).fields == ["", "2", "1"]
def test_templates(): def test_templates():
d = getEmptyDeck() d = getEmptyDeck()
@ -81,10 +81,10 @@ def test_templates():
t['afmt'] = "{{Front}}" t['afmt'] = "{{Front}}"
mm.addTemplate(m, t) mm.addTemplate(m, t)
mm.save(m) mm.save(m)
f = d.newFact() f = d.newNote()
f['Front'] = u'1' f['Front'] = u'1'
f['Back'] = u'2' f['Back'] = u'2'
d.addFact(f) d.addNote(f)
assert d.cardCount() == 2 assert d.cardCount() == 2
(c, c2) = f.cards() (c, c2) = f.cards()
# first card should have first ord # first card should have first ord
@ -102,7 +102,7 @@ def test_templates():
c = f.cards()[0] c = f.cards()[0]
assert c.ord == 0 assert c.ord == 0
stripHTML(c.q()) == "2" stripHTML(c.q()) == "2"
# it shouldn't be possible to orphan facts by removing templates # it shouldn't be possible to orphan notes by removing templates
assert not d.models.remTemplate(m, m['tmpls'][0]) assert not d.models.remTemplate(m, m['tmpls'][0])
def test_text(): def test_text():
@ -110,22 +110,22 @@ def test_text():
m = d.models.current() m = d.models.current()
m['tmpls'][0]['qfmt'] = "{{text:Front}}" m['tmpls'][0]['qfmt'] = "{{text:Front}}"
d.models.save(m) d.models.save(m)
f = d.newFact() f = d.newNote()
f['Front'] = u'hello<b>world' f['Front'] = u'hello<b>world'
d.addFact(f) d.addNote(f)
assert "helloworld" in f.cards()[0].q() assert "helloworld" in f.cards()[0].q()
def test_cloze(): def test_cloze():
d = getEmptyDeck() d = getEmptyDeck()
d.models.setCurrent(d.models.byName("Cloze")) d.models.setCurrent(d.models.byName("Cloze"))
f = d.newFact() f = d.newNote()
assert f.model()['name'] == "Cloze" assert f.model()['name'] == "Cloze"
# a cloze model with no clozes is empty # a cloze model with no clozes is empty
f['Text'] = u'nothing' f['Text'] = u'nothing'
assert d.addFact(f) == 0 assert d.addNote(f) == 0
# try with one cloze # try with one cloze
f['Text'] = "hello {{c1::world}}" f['Text'] = "hello {{c1::world}}"
assert d.addFact(f) == 1 assert d.addNote(f) == 1
assert "hello <span class=cloze>[...]</span>" in f.cards()[0].q() assert "hello <span class=cloze>[...]</span>" in f.cards()[0].q()
# the default is no context # the default is no context
assert "<span class=cloze>world</span>" in f.cards()[0].a() assert "<span class=cloze>world</span>" in f.cards()[0].a()
@ -134,15 +134,15 @@ def test_cloze():
f.model()['clozectx'] = True f.model()['clozectx'] = True
assert "hello <span class=cloze>world</span>" in f.cards()[0].a() assert "hello <span class=cloze>world</span>" in f.cards()[0].a()
# and with a comment # and with a comment
f = d.newFact() f = d.newNote()
f['Text'] = "hello {{c1::world::typical}}" f['Text'] = "hello {{c1::world::typical}}"
assert d.addFact(f) == 1 assert d.addNote(f) == 1
assert "<span class=cloze>[...(typical)]</span>" in f.cards()[0].q() assert "<span class=cloze>[...(typical)]</span>" in f.cards()[0].q()
assert "<span class=cloze>world</span>" in f.cards()[0].a() assert "<span class=cloze>world</span>" in f.cards()[0].a()
# and with 2 clozes # and with 2 clozes
f = d.newFact() f = d.newNote()
f['Text'] = "hello {{c1::world}} {{c2::bar}}" f['Text'] = "hello {{c1::world}} {{c2::bar}}"
assert d.addFact(f) == 2 assert d.addNote(f) == 2
(c1, c2) = f.cards() (c1, c2) = f.cards()
assert "<span class=cloze>[...]</span> bar" in c1.q() assert "<span class=cloze>[...]</span> bar" in c1.q()
assert "<span class=cloze>world</span> bar" in c1.a() assert "<span class=cloze>world</span> bar" in c1.a()
@ -151,21 +151,21 @@ def test_cloze():
# if there are multiple answers for a single cloze, they are given in a # if there are multiple answers for a single cloze, they are given in a
# list # list
f.model()['clozectx'] = False f.model()['clozectx'] = False
f = d.newFact() f = d.newNote()
f['Text'] = "a {{c1::b}} {{c1::c}}" f['Text'] = "a {{c1::b}} {{c1::c}}"
assert d.addFact(f) == 1 assert d.addNote(f) == 1
assert "<span class=cloze>b</span>, <span class=cloze>c</span>" in ( assert "<span class=cloze>b</span>, <span class=cloze>c</span>" in (
f.cards()[0].a()) f.cards()[0].a())
# clozes should be supported in sections too # clozes should be supported in sections too
m = d.models.current() m = d.models.current()
m['tmpls'][0]['qfmt'] = "{{#cloze:1:Text}}{{Notes}}{{/cloze:1:Text}}" m['tmpls'][0]['qfmt'] = "{{#cloze:1:Text}}{{Notes}}{{/cloze:1:Text}}"
d.models.save(m) d.models.save(m)
f = d.newFact() f = d.newNote()
f['Text'] = "hello" f['Text'] = "hello"
f['Notes'] = "world" f['Notes'] = "world"
assert d.addFact(f) == 0 assert d.addNote(f) == 0
f['Text'] = "hello {{c1::foo}}" f['Text'] = "hello {{c1::foo}}"
assert d.addFact(f) == 1 assert d.addNote(f) == 1
# deleting a cloze should fail; the ui should clean up invalid cards # deleting a cloze should fail; the ui should clean up invalid cards
cnt = d.cardCount() cnt = d.cardCount()
f['Text'] = "hello" f['Text'] = "hello"
@ -181,17 +181,17 @@ def test_modelChange():
deck = getEmptyDeck() deck = getEmptyDeck()
basic = deck.models.byName("Basic") basic = deck.models.byName("Basic")
cloze = deck.models.byName("Cloze") cloze = deck.models.byName("Cloze")
# enable second template and add a fact # enable second template and add a note
m = deck.models.current(); mm = deck.models m = deck.models.current(); mm = deck.models
t = mm.newTemplate("Reverse") t = mm.newTemplate("Reverse")
t['qfmt'] = "{{Back}}" t['qfmt'] = "{{Back}}"
t['afmt'] = "{{Front}}" t['afmt'] = "{{Front}}"
mm.addTemplate(m, t) mm.addTemplate(m, t)
mm.save(m) mm.save(m)
f = deck.newFact() f = deck.newNote()
f['Front'] = u'f' f['Front'] = u'f'
f['Back'] = u'b' f['Back'] = u'b'
deck.addFact(f) deck.addNote(f)
# switch fields # switch fields
map = {0: 1, 1: 0} map = {0: 1, 1: 0}
deck.models.change(basic, [f.id], basic, map, None) deck.models.change(basic, [f.id], basic, map, None)
@ -231,11 +231,11 @@ def test_modelChange():
f.load() f.load()
assert f['Front'] == '' assert f['Front'] == ''
assert f['Back'] == 'f' assert f['Back'] == 'f'
# another fact to try model conversion # another note to try model conversion
f = deck.newFact() f = deck.newNote()
f['Front'] = u'f2' f['Front'] = u'f2'
f['Back'] = u'b2' f['Back'] = u'b2'
deck.addFact(f) deck.addNote(f)
assert deck.models.useCount(basic) == 2 assert deck.models.useCount(basic) == 2
assert deck.models.useCount(cloze) == 0 assert deck.models.useCount(cloze) == 0
map = {0: 0, 1: 1} map = {0: 0, 1: 1}

View file

@ -8,7 +8,7 @@ from anki import Deck
from anki.utils import intTime from anki.utils import intTime
from anki.sync import Syncer, FullSyncer, LocalServer, RemoteServer, \ from anki.sync import Syncer, FullSyncer, LocalServer, RemoteServer, \
MediaSyncer, RemoteMediaServer MediaSyncer, RemoteMediaServer
from anki.facts import Fact from anki.notes import Note
from anki.cards import Card from anki.cards import Card
from tests.shared import getEmptyDeck from tests.shared import getEmptyDeck

View file

@ -13,10 +13,10 @@ def test_basics():
def test_new(): def test_new():
d = getEmptyDeck() d = getEmptyDeck()
assert d.sched.newCount == 0 assert d.sched.newCount == 0
# add a fact # add a note
f = d.newFact() f = d.newNote()
f['Front'] = u"one"; f['Back'] = u"two" f['Front'] = u"one"; f['Back'] = u"two"
d.addFact(f) d.addNote(f)
d.reset() d.reset()
assert d.sched.newCount == 1 assert d.sched.newCount == 1
# fetch it # fetch it
@ -38,12 +38,12 @@ def test_new():
t['afmt'] = "{{Front}}" t['afmt'] = "{{Front}}"
mm.addTemplate(m, t) mm.addTemplate(m, t)
mm.save(m) mm.save(m)
f = d.newFact() f = d.newNote()
f['Front'] = u"2"; f['Back'] = u"2" f['Front'] = u"2"; f['Back'] = u"2"
d.addFact(f) d.addNote(f)
f = d.newFact() f = d.newNote()
f['Front'] = u"3"; f['Back'] = u"3" f['Front'] = u"3"; f['Back'] = u"3"
d.addFact(f) d.addNote(f)
d.reset() d.reset()
qs = ("2", "3", "2", "3") qs = ("2", "3", "2", "3")
for n in range(4): for n in range(4):
@ -53,14 +53,14 @@ def test_new():
def test_newLimits(): def test_newLimits():
d = getEmptyDeck() d = getEmptyDeck()
# add some facts # add some notes
g2 = d.groups.id("Default::foo") g2 = d.groups.id("Default::foo")
for i in range(30): for i in range(30):
f = d.newFact() f = d.newNote()
f['Front'] = str(i) f['Front'] = str(i)
if i > 4: if i > 4:
f.gid = g2 f.gid = g2
d.addFact(f) d.addNote(f)
# give the child group a different configuration # give the child group a different configuration
c2 = d.groups.confId("new conf") c2 = d.groups.confId("new conf")
d.groups.setConf(d.groups.get(g2), c2) d.groups.setConf(d.groups.get(g2), c2)
@ -92,11 +92,11 @@ def test_newOrder():
t['actv'] = i > 25 t['actv'] = i > 25
d.models.addTemplate(m, t) d.models.addTemplate(m, t)
d.models.save(m) d.models.save(m)
f = d.newFact() f = d.newNote()
f['Front'] = u'1' f['Front'] = u'1'
f['Back'] = u'2' f['Back'] = u'2'
# add first half # add first half
d.addFact(f) d.addNote(f)
# generate second half # generate second half
d.db.execute("update cards set gid = random()") d.db.execute("update cards set gid = random()")
d.conf['newPerDay'] = 100 d.conf['newPerDay'] = 100
@ -106,9 +106,9 @@ def test_newOrder():
def test_newBoxes(): def test_newBoxes():
d = getEmptyDeck() d = getEmptyDeck()
f = d.newFact() f = d.newNote()
f['Front'] = u"one" f['Front'] = u"one"
d.addFact(f) d.addNote(f)
d.reset() d.reset()
c = d.sched.getCard() c = d.sched.getCard()
d.sched._cardConf(c)['new']['delays'] = [1,2,3,4,5] d.sched._cardConf(c)['new']['delays'] = [1,2,3,4,5]
@ -119,10 +119,10 @@ def test_newBoxes():
def test_learn(): def test_learn():
d = getEmptyDeck() d = getEmptyDeck()
# add a fact # add a note
f = d.newFact() f = d.newNote()
f['Front'] = u"one"; f['Back'] = u"two" f['Front'] = u"one"; f['Back'] = u"two"
f = d.addFact(f) f = d.addNote(f)
# set as a learn card and rebuild queues # set as a learn card and rebuild queues
d.db.execute("update cards set queue=0, type=0") d.db.execute("update cards set queue=0, type=0")
d.reset() d.reset()
@ -191,10 +191,10 @@ def test_learn():
def test_reviews(): def test_reviews():
d = getEmptyDeck() d = getEmptyDeck()
# add a fact # add a note
f = d.newFact() f = d.newNote()
f['Front'] = u"one"; f['Back'] = u"two" f['Front'] = u"one"; f['Back'] = u"two"
d.addFact(f) d.addNote(f)
# set the card up as a review card, due 8 days ago # set the card up as a review card, due 8 days ago
c = f.cards()[0] c = f.cards()[0]
c.type = 2 c.type = 2
@ -286,9 +286,9 @@ def test_finished():
# nothing due # nothing due
assert "Congratulations" in d.sched.finishedMsg() assert "Congratulations" in d.sched.finishedMsg()
assert "limit" not in d.sched.finishedMsg() assert "limit" not in d.sched.finishedMsg()
f = d.newFact() f = d.newNote()
f['Front'] = u"one"; f['Back'] = u"two" f['Front'] = u"one"; f['Back'] = u"two"
d.addFact(f) d.addNote(f)
# have a new card # have a new card
assert "new cards available" in d.sched.finishedMsg() assert "new cards available" in d.sched.finishedMsg()
# turn it into a review # turn it into a review
@ -301,9 +301,9 @@ def test_finished():
def test_nextIvl(): def test_nextIvl():
d = getEmptyDeck() d = getEmptyDeck()
f = d.newFact() f = d.newNote()
f['Front'] = u"one"; f['Back'] = u"two" f['Front'] = u"one"; f['Back'] = u"two"
d.addFact(f) d.addNote(f)
d.reset() d.reset()
c = d.sched.getCard() c = d.sched.getCard()
d.sched._cardConf(c)['new']['delays'] = [0.5, 3, 10] d.sched._cardConf(c)['new']['delays'] = [0.5, 3, 10]
@ -356,12 +356,12 @@ def test_nextIvl():
def test_misc(): def test_misc():
d = getEmptyDeck() d = getEmptyDeck()
f = d.newFact() f = d.newNote()
f['Front'] = u"one" f['Front'] = u"one"
d.addFact(f) d.addNote(f)
c = f.cards()[0] c = f.cards()[0]
# burying # burying
d.sched.buryFact(c.fid) d.sched.buryNote(c.nid)
d.reset() d.reset()
assert not d.sched.getCard() assert not d.sched.getCard()
d.sched.onClose() d.sched.onClose()
@ -370,9 +370,9 @@ def test_misc():
def test_suspend(): def test_suspend():
d = getEmptyDeck() d = getEmptyDeck()
f = d.newFact() f = d.newNote()
f['Front'] = u"one" f['Front'] = u"one"
d.addFact(f) d.addNote(f)
c = f.cards()[0] c = f.cards()[0]
# suspending # suspending
d.reset() d.reset()
@ -403,9 +403,9 @@ def test_cram():
print "disabled for now" print "disabled for now"
return return
d = getEmptyDeck() d = getEmptyDeck()
f = d.newFact() f = d.newNote()
f['Front'] = u"one" f['Front'] = u"one"
d.addFact(f) d.addNote(f)
c = f.cards()[0] c = f.cards()[0]
c.ivl = 100 c.ivl = 100
c.type = c.queue = 2 c.type = c.queue = 2
@ -485,9 +485,9 @@ def test_cramLimits():
d = getEmptyDeck() d = getEmptyDeck()
# create three cards, due tomorrow, the next, etc # create three cards, due tomorrow, the next, etc
for i in range(3): for i in range(3):
f = d.newFact() f = d.newNote()
f['Front'] = str(i) f['Front'] = str(i)
d.addFact(f) d.addNote(f)
c = f.cards()[0] c = f.cards()[0]
c.type = c.queue = 2 c.type = c.queue = 2
c.due = d.sched.today + 1 + i c.due = d.sched.today + 1 + i
@ -539,10 +539,10 @@ def test_adjIvl():
t['afmt'] = "{{Back}}" t['afmt'] = "{{Back}}"
d.models.addTemplate(m, t) d.models.addTemplate(m, t)
d.models.save(m) d.models.save(m)
# create a new fact; it should have 4 cards # create a new note; it should have 4 cards
f = d.newFact() f = d.newNote()
f['Front'] = "1"; f['Back'] = "1" f['Front'] = "1"; f['Back'] = "1"
d.addFact(f) d.addNote(f)
assert d.cardCount() == 4 assert d.cardCount() == 4
d.reset() d.reset()
# immediately remove first; it should get ideal ivl # immediately remove first; it should get ideal ivl
@ -561,10 +561,10 @@ def test_adjIvl():
c = d.sched.getCard() c = d.sched.getCard()
d.sched.answerCard(c, 3) d.sched.answerCard(c, 3)
assert c.ivl == 4 assert c.ivl == 4
# try again with another fact # try again with another note
f = d.newFact() f = d.newNote()
f['Front'] = "2"; f['Back'] = "2" f['Front'] = "2"; f['Back'] = "2"
d.addFact(f) d.addNote(f)
d.reset() d.reset()
# set a minSpacing of 0 # set a minSpacing of 0
conf = d.sched._cardConf(c) conf = d.sched._cardConf(c)
@ -601,10 +601,10 @@ def test_ordcycle():
t['afmt'] = "{{Back}}" t['afmt'] = "{{Back}}"
mm.addTemplate(m, t) mm.addTemplate(m, t)
mm.save(m) mm.save(m)
# create a new fact; it should have 3 cards # create a new note; it should have 3 cards
f = d.newFact() f = d.newNote()
f['Front'] = "1"; f['Back'] = "1" f['Front'] = "1"; f['Back'] = "1"
d.addFact(f) d.addNote(f)
assert d.cardCount() == 3 assert d.cardCount() == 3
d.reset() d.reset()
# ordinals should arrive in order # ordinals should arrive in order
@ -620,10 +620,10 @@ def test_cardcounts():
for type in range(3): for type in range(3):
# and each of the groups # and each of the groups
for gid in (1,grp): for gid in (1,grp):
# create a new fact # create a new note
f = d.newFact() f = d.newNote()
f['Front'] = u"one" f['Front'] = u"one"
d.addFact(f) d.addNote(f)
c = f.cards()[0] c = f.cards()[0]
# set type/gid # set type/gid
c.type = type c.type = type
@ -641,9 +641,9 @@ def test_cardcounts():
def test_counts_idx(): def test_counts_idx():
d = getEmptyDeck() d = getEmptyDeck()
f = d.newFact() f = d.newNote()
f['Front'] = u"one"; f['Back'] = u"two" f['Front'] = u"one"; f['Back'] = u"two"
d.addFact(f) d.addNote(f)
d.reset() d.reset()
assert d.sched.cardCounts() == (1, 0, 0) assert d.sched.cardCounts() == (1, 0, 0)
c = d.sched.getCard() c = d.sched.getCard()
@ -663,9 +663,9 @@ def test_counts_idx():
def test_repCounts(): def test_repCounts():
d = getEmptyDeck() d = getEmptyDeck()
f = d.newFact() f = d.newNote()
f['Front'] = u"one" f['Front'] = u"one"
d.addFact(f) d.addNote(f)
d.reset() d.reset()
# lrnReps should be accurate on pass/fail # lrnReps should be accurate on pass/fail
assert d.sched.repCounts() == (1, 0, 0) assert d.sched.repCounts() == (1, 0, 0)
@ -681,9 +681,9 @@ def test_repCounts():
assert d.sched.repCounts() == (0, 1, 0) assert d.sched.repCounts() == (0, 1, 0)
d.sched.answerCard(d.sched.getCard(), 2) d.sched.answerCard(d.sched.getCard(), 2)
assert d.sched.repCounts() == (0, 0, 0) assert d.sched.repCounts() == (0, 0, 0)
f = d.newFact() f = d.newNote()
f['Front'] = u"two" f['Front'] = u"two"
d.addFact(f) d.addNote(f)
d.reset() d.reset()
# initial pass should be correct too # initial pass should be correct too
d.sched.answerCard(d.sched.getCard(), 2) d.sched.answerCard(d.sched.getCard(), 2)
@ -693,16 +693,16 @@ def test_repCounts():
d.sched.answerCard(d.sched.getCard(), 3) d.sched.answerCard(d.sched.getCard(), 3)
assert d.sched.repCounts() == (0, 0, 0) assert d.sched.repCounts() == (0, 0, 0)
# immediate graduate should work # immediate graduate should work
f = d.newFact() f = d.newNote()
f['Front'] = u"three" f['Front'] = u"three"
d.addFact(f) d.addNote(f)
d.reset() d.reset()
d.sched.answerCard(d.sched.getCard(), 3) d.sched.answerCard(d.sched.getCard(), 3)
assert d.sched.repCounts() == (0, 0, 0) assert d.sched.repCounts() == (0, 0, 0)
# and failing a review should too # and failing a review should too
f = d.newFact() f = d.newNote()
f['Front'] = u"three" f['Front'] = u"three"
d.addFact(f) d.addNote(f)
c = f.cards()[0] c = f.cards()[0]
c.type = 2 c.type = 2
c.queue = 2 c.queue = 2
@ -717,9 +717,9 @@ def test_timing():
d = getEmptyDeck() d = getEmptyDeck()
# add a few review cards, due today # add a few review cards, due today
for i in range(5): for i in range(5):
f = d.newFact() f = d.newNote()
f['Front'] = "num"+str(i) f['Front'] = "num"+str(i)
d.addFact(f) d.addNote(f)
c = f.cards()[0] c = f.cards()[0]
c.type = 2 c.type = 2
c.queue = 2 c.queue = 2
@ -741,10 +741,10 @@ def test_timing():
def test_collapse(): def test_collapse():
d = getEmptyDeck() d = getEmptyDeck()
# add a fact # add a note
f = d.newFact() f = d.newNote()
f['Front'] = u"one" f['Front'] = u"one"
d.addFact(f) d.addNote(f)
d.reset() d.reset()
# test collapsing # test collapsing
c = d.sched.getCard() c = d.sched.getCard()
@ -755,30 +755,30 @@ def test_collapse():
def test_groupCounts(): def test_groupCounts():
d = getEmptyDeck() d = getEmptyDeck()
# add a fact with default group # add a note with default group
f = d.newFact() f = d.newNote()
f['Front'] = u"one" f['Front'] = u"one"
d.addFact(f) d.addNote(f)
# and one that's a child # and one that's a child
f = d.newFact() f = d.newNote()
f['Front'] = u"two" f['Front'] = u"two"
default1 = f.gid = d.groups.id("Default::1") default1 = f.gid = d.groups.id("Default::1")
d.addFact(f) d.addNote(f)
# make it a review card # make it a review card
c = f.cards()[0] c = f.cards()[0]
c.queue = 2 c.queue = 2
c.due = 0 c.due = 0
c.flush() c.flush()
# add one more with a new group # add one more with a new group
f = d.newFact() f = d.newNote()
f['Front'] = u"two" f['Front'] = u"two"
foobar = f.gid = d.groups.id("foo::bar") foobar = f.gid = d.groups.id("foo::bar")
d.addFact(f) d.addNote(f)
# and one that's a sibling # and one that's a sibling
f = d.newFact() f = d.newNote()
f['Front'] = u"three" f['Front'] = u"three"
foobaz = f.gid = d.groups.id("foo::baz") foobaz = f.gid = d.groups.id("foo::baz")
d.addFact(f) d.addNote(f)
d.reset() d.reset()
assert len(d.groups.groups) == 5 assert len(d.groups.groups) == 5
cnts = d.sched.groupCounts() cnts = d.sched.groupCounts()
@ -815,37 +815,37 @@ def test_groupTree():
def test_groupFlow(): def test_groupFlow():
d = getEmptyDeck() d = getEmptyDeck()
# add a fact with default group # add a note with default group
f = d.newFact() f = d.newNote()
f['Front'] = u"one" f['Front'] = u"one"
d.addFact(f) d.addNote(f)
# and one that's a child # and one that's a child
f = d.newFact() f = d.newNote()
f['Front'] = u"two" f['Front'] = u"two"
default1 = f.gid = d.groups.id("Default::2") default1 = f.gid = d.groups.id("Default::2")
d.addFact(f) d.addNote(f)
# and another that's higher up # and another that's higher up
f = d.newFact() f = d.newNote()
f['Front'] = u"three" f['Front'] = u"three"
default1 = f.gid = d.groups.id("Default::1") default1 = f.gid = d.groups.id("Default::1")
d.addFact(f) d.addNote(f)
# should get top level one first, then ::1, then ::2 # should get top level one first, then ::1, then ::2
d.reset() d.reset()
assert d.sched.cardCounts() == (3,0,0) assert d.sched.cardCounts() == (3,0,0)
for i in "one", "three", "two": for i in "one", "three", "two":
c = d.sched.getCard() c = d.sched.getCard()
assert c.fact()['Front'] == i assert c.note()['Front'] == i
d.sched.answerCard(c, 2) d.sched.answerCard(c, 2)
def test_reorder(): def test_reorder():
d = getEmptyDeck() d = getEmptyDeck()
# add a fact with default group # add a note with default group
f = d.newFact() f = d.newNote()
f['Front'] = u"one" f['Front'] = u"one"
d.addFact(f) d.addNote(f)
f2 = d.newFact() f2 = d.newNote()
f2['Front'] = u"two" f2['Front'] = u"two"
d.addFact(f2) d.addNote(f2)
assert f2.cards()[0].due == 2 assert f2.cards()[0].due == 2
found=False found=False
# 50/50 chance of being reordered # 50/50 chance of being reordered
@ -858,12 +858,12 @@ def test_reorder():
d.sched.orderCards() d.sched.orderCards()
assert f.cards()[0].due == 1 assert f.cards()[0].due == 1
# shifting # shifting
f3 = d.newFact() f3 = d.newNote()
f3['Front'] = u"three" f3['Front'] = u"three"
d.addFact(f3) d.addNote(f3)
f4 = d.newFact() f4 = d.newNote()
f4['Front'] = u"four" f4['Front'] = u"four"
d.addFact(f4) d.addNote(f4)
assert f.cards()[0].due == 1 assert f.cards()[0].due == 1
assert f2.cards()[0].due == 2 assert f2.cards()[0].due == 2
assert f3.cards()[0].due == 3 assert f3.cards()[0].due == 3
@ -877,9 +877,9 @@ def test_reorder():
def test_forget(): def test_forget():
d = getEmptyDeck() d = getEmptyDeck()
f = d.newFact() f = d.newNote()
f['Front'] = u"one" f['Front'] = u"one"
d.addFact(f) d.addNote(f)
c = f.cards()[0] c = f.cards()[0]
c.queue = 2; c.type = 2; c.ivl = 100; c.due = 0 c.queue = 2; c.type = 2; c.ivl = 100; c.due = 0
c.flush() c.flush()
@ -891,9 +891,9 @@ def test_forget():
def test_resched(): def test_resched():
d = getEmptyDeck() d = getEmptyDeck()
f = d.newFact() f = d.newNote()
f['Front'] = u"one" f['Front'] = u"one"
d.addFact(f) d.addNote(f)
c = f.cards()[0] c = f.cards()[0]
d.sched.reschedCards([c.id], 0, 0) d.sched.reschedCards([c.id], 0, 0)
c.load() c.load()
@ -908,9 +908,9 @@ def test_resched():
def test_revlim(): def test_revlim():
d = getEmptyDeck() d = getEmptyDeck()
for i in range(20): for i in range(20):
f = d.newFact() f = d.newNote()
f['Front'] = str(i) f['Front'] = str(i)
d.addFact(f) d.addNote(f)
d.db.execute("update cards set due = 0, queue = 2, type = 2") d.db.execute("update cards set due = 0, queue = 2, type = 2")
d.reset() d.reset()
assert d.sched.repCounts()[2] == 20 assert d.sched.repCounts()[2] == 20

View file

@ -7,9 +7,9 @@ from anki.hooks import addHook
def test_stats(): def test_stats():
d = getEmptyDeck() d = getEmptyDeck()
f = d.newFact() f = d.newNote()
f['Front'] = "foo" f['Front'] = "foo"
d.addFact(f) d.addNote(f)
c = f.cards()[0] c = f.cards()[0]
# card stats # card stats
assert d.cardStats(c) assert d.cardStats(c)

View file

@ -8,7 +8,7 @@ from anki import Deck
from anki.utils import intTime from anki.utils import intTime
from anki.sync import Syncer, FullSyncer, LocalServer, RemoteServer, \ from anki.sync import Syncer, FullSyncer, LocalServer, RemoteServer, \
MediaSyncer, RemoteMediaServer MediaSyncer, RemoteMediaServer
from anki.facts import Fact from anki.notes import Note
from anki.cards import Card from anki.cards import Card
from tests.shared import getEmptyDeck from tests.shared import getEmptyDeck
@ -24,17 +24,17 @@ server2=None
def setup_basic(): def setup_basic():
global deck1, deck2, client, server global deck1, deck2, client, server
deck1 = getEmptyDeck() deck1 = getEmptyDeck()
# add a fact to deck 1 # add a note to deck 1
f = deck1.newFact() f = deck1.newNote()
f['Front'] = u"foo"; f['Back'] = u"bar"; f.tags = [u"foo"] f['Front'] = u"foo"; f['Back'] = u"bar"; f.tags = [u"foo"]
deck1.addFact(f) deck1.addNote(f)
# answer it # answer it
deck1.reset(); deck1.sched.answerCard(deck1.sched.getCard(), 4) deck1.reset(); deck1.sched.answerCard(deck1.sched.getCard(), 4)
# repeat for deck2 # repeat for deck2
deck2 = getEmptyDeck(server=True) deck2 = getEmptyDeck(server=True)
f = deck2.newFact() f = deck2.newNote()
f['Front'] = u"bar"; f['Back'] = u"bar"; f.tags = [u"bar"] f['Front'] = u"bar"; f['Back'] = u"bar"; f.tags = [u"bar"]
deck2.addFact(f) deck2.addNote(f)
deck2.reset(); deck2.sched.answerCard(deck2.sched.getCard(), 4) deck2.reset(); deck2.sched.answerCard(deck2.sched.getCard(), 4)
# start with same schema and sync time # start with same schema and sync time
deck1.scm = deck2.scm = 0 deck1.scm = deck2.scm = 0
@ -62,7 +62,7 @@ def test_changedSchema():
def test_sync(): def test_sync():
def check(num): def check(num):
for d in deck1, deck2: for d in deck1, deck2:
for t in ("revlog", "facts", "cards", "fsums"): for t in ("revlog", "notes", "cards", "nsums"):
assert d.db.scalar("select count() from %s" % t) == num assert d.db.scalar("select count() from %s" % t) == num
assert len(d.models.all()) == num*2 assert len(d.models.all()) == num*2
# the default group and config have an id of 1, so always 1 # the default group and config have an id of 1, so always 1
@ -107,31 +107,31 @@ def test_models():
assert client.sync() == "fullSync" assert client.sync() == "fullSync"
@nose.with_setup(setup_modified) @nose.with_setup(setup_modified)
def test_facts(): def test_notes():
test_sync() test_sync()
# modifications should be synced # modifications should be synced
fid = deck1.db.scalar("select id from facts") nid = deck1.db.scalar("select id from notes")
fact = deck1.getFact(fid) note = deck1.getNote(nid)
assert fact['Front'] != "abc" assert note['Front'] != "abc"
fact['Front'] = "abc" note['Front'] = "abc"
fact.flush() note.flush()
deck1.save() deck1.save()
assert client.sync() == "success" assert client.sync() == "success"
assert deck2.getFact(fid)['Front'] == "abc" assert deck2.getNote(nid)['Front'] == "abc"
# deletions too # deletions too
assert deck1.db.scalar("select 1 from facts where id = ?", fid) assert deck1.db.scalar("select 1 from notes where id = ?", nid)
deck1.remFacts([fid]) deck1.remNotes([nid])
deck1.save() deck1.save()
assert client.sync() == "success" assert client.sync() == "success"
assert not deck1.db.scalar("select 1 from facts where id = ?", fid) assert not deck1.db.scalar("select 1 from notes where id = ?", nid)
assert not deck2.db.scalar("select 1 from facts where id = ?", fid) assert not deck2.db.scalar("select 1 from notes where id = ?", nid)
@nose.with_setup(setup_modified) @nose.with_setup(setup_modified)
def test_cards(): def test_cards():
test_sync() test_sync()
fid = deck1.db.scalar("select id from facts") nid = deck1.db.scalar("select id from notes")
fact = deck1.getFact(fid) note = deck1.getNote(nid)
card = fact.cards()[0] card = note.cards()[0]
# answer the card locally # answer the card locally
card.startTimer() card.startTimer()
deck1.sched.answerCard(card, 4) deck1.sched.answerCard(card, 4)
@ -215,26 +215,26 @@ def test_threeway():
assert client2.sync() == "noChanges" assert client2.sync() == "noChanges"
# client 1 adds a card at time 1 # client 1 adds a card at time 1
time.sleep(1) time.sleep(1)
f = deck1.newFact() f = deck1.newNote()
f['Front'] = u"1"; f['Front'] = u"1";
deck1.addFact(f) deck1.addNote(f)
deck1.save() deck1.save()
# at time 2, client 2 syncs to server # at time 2, client 2 syncs to server
time.sleep(1) time.sleep(1)
deck3.save() deck3.save()
assert client2.sync() == "success" assert client2.sync() == "success"
# at time 3, client 1 syncs, adding the older fact # at time 3, client 1 syncs, adding the older note
time.sleep(1) time.sleep(1)
assert client.sync() == "success" assert client.sync() == "success"
assert deck1.factCount() == deck2.factCount() assert deck1.noteCount() == deck2.noteCount()
# syncing client2 should pick it up # syncing client2 should pick it up
assert client2.sync() == "success" assert client2.sync() == "success"
assert deck1.factCount() == deck2.factCount() == deck3.factCount() assert deck1.noteCount() == deck2.noteCount() == deck3.noteCount()
def _test_speed(): def _test_speed():
t = time.time() t = time.time()
deck1 = Deck(os.path.expanduser("~/rapid.anki")) deck1 = Deck(os.path.expanduser("~/rapid.anki"))
for tbl in "revlog", "cards", "facts", "graves": for tbl in "revlog", "cards", "notes", "graves":
deck1.db.execute("update %s set usn = -1 where usn != -1"%tbl) deck1.db.execute("update %s set usn = -1 where usn != -1"%tbl)
for m in deck1.models.all(): for m in deck1.models.all():
m['usn'] = -1 m['usn'] = -1

View file

@ -26,9 +26,9 @@ def test_op():
assert not d.undoName() assert not d.undoName()
# and a review will, too # and a review will, too
d.save("add") d.save("add")
f = d.newFact() f = d.newNote()
f['Front'] = u"one" f['Front'] = u"one"
d.addFact(f) d.addNote(f)
d.reset() d.reset()
assert d.undoName() == "add" assert d.undoName() == "add"
c = d.sched.getCard() c = d.sched.getCard()
@ -38,9 +38,9 @@ def test_op():
def test_review(): def test_review():
d = getEmptyDeck() d = getEmptyDeck()
d.conf['counts'] = COUNT_REMAINING d.conf['counts'] = COUNT_REMAINING
f = d.newFact() f = d.newNote()
f['Front'] = u"one" f['Front'] = u"one"
d.addFact(f) d.addNote(f)
d.reset() d.reset()
assert not d.undoName() assert not d.undoName()
# answer # answer
@ -62,7 +62,7 @@ def test_review():
assert not d.undoName() assert not d.undoName()
# we should be able to undo multiple answers too # we should be able to undo multiple answers too
f['Front'] = u"two" f['Front'] = u"two"
d.addFact(f) d.addNote(f)
d.reset() d.reset()
assert d.sched.cardCounts() == (2, 0, 0) assert d.sched.cardCounts() == (2, 0, 0)
c = d.sched.getCard() c = d.sched.getCard()