Merge branch 'master' of github.com:dae/libanki into some-fixes

This commit is contained in:
ospalh 2012-10-29 09:31:43 +01:00
commit 0443d68eea
12 changed files with 82 additions and 34 deletions

View file

@ -642,6 +642,9 @@ select id from notes where mid not in """ + ids2str(self.models.ids()))
self.remNotes(ids)
# cards with invalid ordinal
for m in self.models.all():
# ignore clozes
if m['type'] != MODEL_STD:
continue
ids = self.db.list("""
select id from cards where ord not in %s and nid in (
select id from notes where mid = ?)""" %

View file

@ -4,11 +4,10 @@
import os
from anki import Collection
from anki.utils import intTime, splitFields, joinFields, checksum
from anki.utils import intTime, splitFields, joinFields, checksum, guid64
from anki.importing.base import Importer
from anki.lang import _
from anki.lang import ngettext
from anki.hooks import runFilter
#
# Import a .anki2 file into the current collection. Used for migration from
@ -43,7 +42,6 @@ class Anki2Importer(Importer):
def _import(self):
self._decks = {}
self._prepareDeckPrefix()
if self.deckPrefix:
id = self.dst.decks.id(self.deckPrefix)
self.dst.decks.select(id)
@ -56,24 +54,6 @@ class Anki2Importer(Importer):
self.dst.db.execute("vacuum")
self.dst.db.execute("analyze")
def _prepareDeckPrefix(self):
if self.deckPrefix:
return runFilter("prepareImportPrefix", self.deckPrefix)
prefix = None
for deck in self.src.decks.all():
if str(deck['id']) == "1":
# we can ignore the default deck if it's empty
if not self.src.db.scalar(
"select 1 from cards where did = ? limit 1", deck['id']):
continue
head = deck['name'].split("::")[0]
if not prefix:
prefix = head
else:
if prefix != head:
return
self.deckPrefix = runFilter("prepareImportPrefix", prefix)
# Notes
######################################################################
# - should note new for wizard
@ -86,6 +66,9 @@ class Anki2Importer(Importer):
"select id, guid, mod, mid from notes"):
self._notes[guid] = (id, mod, mid)
existing[id] = True
# we may need to rewrite the guid if the model schemas don't match,
# so we need to keep track of the changes for the card import stage
self._changedGuids = {}
# iterate over source collection
add = []
dirty = []
@ -96,8 +79,22 @@ class Anki2Importer(Importer):
# turn the db result into a mutable list
note = list(note)
guid, mid = note[1:3]
# missing from local col?
if guid not in self._notes:
canUseExisting = False
alreadyHaveGuid = False
# do we have the same guid?
if guid in self._notes:
alreadyHaveGuid = True
# and do they share the same model id?
if self._notes[guid][2] == mid:
# and do they share the same schema?
srcM = self.src.models.get(mid)
dstM = self.dst.models.get(self._notes[guid][2])
if (self.src.models.scmhash(srcM) ==
self.src.models.scmhash(dstM)):
# then it's safe to treat as an exact duplicate
canUseExisting = True
# if we can't reuse an existing one, we'll need to add new
if not canUseExisting:
# get corresponding local model
lmid = self._mid(mid)
# ensure id is unique
@ -111,11 +108,16 @@ class Anki2Importer(Importer):
note[6] = self._mungeMedia(mid, note[6])
add.append(note)
dirty.append(note[0])
# if it was originally the same as a note in this deck but the
# models have diverged, we need to change the guid
if alreadyHaveGuid:
guid = guid64()
self._changedGuids[note[1]] = guid
note[1] = guid
# note we have the added note
self._notes[guid] = (note[0], note[3], note[2])
else:
dupes += 1
pass
## update existing note - not yet tested; for post 2.0
# newer = note[3] > mod
# if self.allowUpdate and self._mid(mid) == mid and newer:
@ -245,6 +247,8 @@ class Anki2Importer(Importer):
"select f.guid, f.mid, c.* from cards c, notes f "
"where c.nid = f.id"):
guid = card[0]
if guid in self._changedGuids:
guid = self._changedGuids[guid]
# does the card's note exist in dst col?
if guid not in self._notes:
continue

View file

@ -64,7 +64,7 @@ def _latexFromHtml(col, latex):
# entitydefs defines nbsp as \xa0 instead of a standard space, so we
# replace it first
latex = latex.replace(" ", " ")
latex = re.sub("<br( /)?>|</div>", "\n", latex)
latex = re.sub("<br( /)?>|<div>", "\n", latex)
# replace <div> etc with spaces
latex = re.sub("<.+?>", " ", latex)
latex = stripHTML(latex)

View file

@ -452,6 +452,8 @@ select id from notes where mid = ?)""" % " ".join(map),
s = ""
for f in m['flds']:
s += f['name']
for t in m['tmpls']:
s += t['name']
return fieldChecksum(s)
# Required field/text cache

View file

@ -119,7 +119,7 @@ order by due""" % self._deckLimit(),
if card.odid and card.queue == 2:
return 4
conf = self._lapseConf(card)
if len(conf['delays']) > 1:
if card.type == 0 or len(conf['delays']) > 1:
return 3
return 2
elif card.queue == 2:

View file

@ -139,7 +139,7 @@ body {background-image: url(data:image/png;base64,%s); }
lim = " and " + lim
cards, thetime, failed, lrn, rev, relrn, filt = self.col.db.first("""
select count(), sum(time)/1000,
sum(case when ease = 0 then 1 else 0 end), /* failed */
sum(case when ease = 1 then 1 else 0 end), /* failed */
sum(case when type = 0 then 1 else 0 end), /* learning */
sum(case when type = 1 then 1 else 0 end), /* review */
sum(case when type = 2 then 1 else 0 end), /* relearn */
@ -154,7 +154,7 @@ from revlog where id > ? """+lim, (self.col.sched.dayCutoff-86400)*1000)
filt = filt or 0
# studied
def bold(s):
return "<b>"+str(s)+"</b>"
return "<b>"+unicode(s)+"</b>"
msgp1 = ngettext("%d card", "%d cards", cards) % cards
b += _("Studied %(a)s in %(b)s today.") % dict(
a=bold(msgp1), b=bold(fmtTimeSpan(thetime, unit=1)))
@ -828,7 +828,7 @@ $(function () {
def _avgDay(self, tot, num, unit):
vals = []
try:
vals.append(_("%(a)d %(b)s/day") % dict(a=tot/float(num), b=unit))
vals.append(_("%(a)0.1f %(b)s/day") % dict(a=tot/float(num), b=unit))
return ", ".join(vals)
except ZeroDivisionError:
return ""

View file

@ -341,11 +341,8 @@ insert or replace into col select id, cast(created as int), :t,
mods = {}
for row in db.all(
"select id, name from models"):
while 1:
t = intTime(1000)
if t not in times:
times[t] = True
break
# use only first 31 bits
t = abs(row[0]) >> 32
m = anki.models.defaultModel.copy()
m['id'] = t
m['name'] = row[1]

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -138,6 +138,48 @@ def test_anki1():
imp.run()
check()
def test_anki1_diffmodels():
# create a new empty deck
dst = getEmptyDeck()
# import the 1 card version of the model
tmp = getUpgradeDeckPath("diffmodels1.anki")
imp = Anki1Importer(dst, tmp)
imp.run()
before = dst.noteCount()
# repeating the process should do nothing
imp = Anki1Importer(dst, tmp)
imp.run()
assert before == dst.noteCount()
# then the 2 card version
tmp = getUpgradeDeckPath("diffmodels2.anki")
imp = Anki1Importer(dst, tmp)
imp.run()
after = dst.noteCount()
# as the model schemas differ, should have been imported as new model
assert after == before + 1
def test_anki2_diffmodels():
# create a new empty deck
dst = getEmptyDeck()
# import the 1 card version of the model
tmp = getUpgradeDeckPath("diffmodels2-1.apkg")
imp = AnkiPackageImporter(dst, tmp)
imp.run()
before = dst.noteCount()
# repeating the process should do nothing
imp = AnkiPackageImporter(dst, tmp)
imp.run()
assert before == dst.noteCount()
# then the 2 card version
tmp = getUpgradeDeckPath("diffmodels2-2.apkg")
imp = AnkiPackageImporter(dst, tmp)
imp.run()
after = dst.noteCount()
# as the model schemas differ, should have been imported as new model
assert after == before + 1
# and the new model should have both cards
assert dst.cardCount() == 3
def test_csv():
deck = getEmptyDeck()
file = unicode(os.path.join(testDir, "support/text-2fields.txt"))