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) self.remNotes(ids)
# cards with invalid ordinal # cards with invalid ordinal
for m in self.models.all(): for m in self.models.all():
# ignore clozes
if m['type'] != MODEL_STD:
continue
ids = self.db.list(""" ids = self.db.list("""
select id from cards where ord not in %s and nid in ( select id from cards where ord not in %s and nid in (
select id from notes where mid = ?)""" % select id from notes where mid = ?)""" %

View file

@ -4,11 +4,10 @@
import os import os
from anki import Collection 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.importing.base import Importer
from anki.lang import _ from anki.lang import _
from anki.lang import ngettext from anki.lang import ngettext
from anki.hooks import runFilter
# #
# Import a .anki2 file into the current collection. Used for migration from # Import a .anki2 file into the current collection. Used for migration from
@ -43,7 +42,6 @@ class Anki2Importer(Importer):
def _import(self): def _import(self):
self._decks = {} self._decks = {}
self._prepareDeckPrefix()
if self.deckPrefix: if self.deckPrefix:
id = self.dst.decks.id(self.deckPrefix) id = self.dst.decks.id(self.deckPrefix)
self.dst.decks.select(id) self.dst.decks.select(id)
@ -56,24 +54,6 @@ class Anki2Importer(Importer):
self.dst.db.execute("vacuum") self.dst.db.execute("vacuum")
self.dst.db.execute("analyze") 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 # Notes
###################################################################### ######################################################################
# - should note new for wizard # - should note new for wizard
@ -86,6 +66,9 @@ class Anki2Importer(Importer):
"select id, guid, mod, mid from notes"): "select id, guid, mod, mid from notes"):
self._notes[guid] = (id, mod, mid) self._notes[guid] = (id, mod, mid)
existing[id] = True 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 # iterate over source collection
add = [] add = []
dirty = [] dirty = []
@ -96,8 +79,22 @@ class Anki2Importer(Importer):
# turn the db result into a mutable list # turn the db result into a mutable list
note = list(note) note = list(note)
guid, mid = note[1:3] guid, mid = note[1:3]
# missing from local col? canUseExisting = False
if guid not in self._notes: 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 # get corresponding local model
lmid = self._mid(mid) lmid = self._mid(mid)
# ensure id is unique # ensure id is unique
@ -111,11 +108,16 @@ class Anki2Importer(Importer):
note[6] = self._mungeMedia(mid, note[6]) note[6] = self._mungeMedia(mid, note[6])
add.append(note) add.append(note)
dirty.append(note[0]) 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 # note we have the added note
self._notes[guid] = (note[0], note[3], note[2]) self._notes[guid] = (note[0], note[3], note[2])
else: else:
dupes += 1 dupes += 1
pass
## update existing note - not yet tested; for post 2.0 ## update existing note - not yet tested; for post 2.0
# newer = note[3] > mod # newer = note[3] > mod
# if self.allowUpdate and self._mid(mid) == mid and newer: # 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 " "select f.guid, f.mid, c.* from cards c, notes f "
"where c.nid = f.id"): "where c.nid = f.id"):
guid = card[0] guid = card[0]
if guid in self._changedGuids:
guid = self._changedGuids[guid]
# does the card's note exist in dst col? # does the card's note exist in dst col?
if guid not in self._notes: if guid not in self._notes:
continue continue

View file

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

View file

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

View file

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

View file

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

View file

@ -341,11 +341,8 @@ insert or replace into col select id, cast(created as int), :t,
mods = {} mods = {}
for row in db.all( for row in db.all(
"select id, name from models"): "select id, name from models"):
while 1: # use only first 31 bits
t = intTime(1000) t = abs(row[0]) >> 32
if t not in times:
times[t] = True
break
m = anki.models.defaultModel.copy() m = anki.models.defaultModel.copy()
m['id'] = t m['id'] = t
m['name'] = row[1] 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() imp.run()
check() 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(): def test_csv():
deck = getEmptyDeck() deck = getEmptyDeck()
file = unicode(os.path.join(testDir, "support/text-2fields.txt")) file = unicode(os.path.join(testDir, "support/text-2fields.txt"))