mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
Merge branch 'master' of github.com:dae/libanki into some-fixes
This commit is contained in:
commit
0443d68eea
12 changed files with 82 additions and 34 deletions
|
@ -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 = ?)""" %
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 ""
|
||||
|
|
|
@ -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]
|
||||
|
|
BIN
tests/support/diffmodels1.anki
Normal file
BIN
tests/support/diffmodels1.anki
Normal file
Binary file not shown.
BIN
tests/support/diffmodels2-1.apkg
Normal file
BIN
tests/support/diffmodels2-1.apkg
Normal file
Binary file not shown.
BIN
tests/support/diffmodels2-2.apkg
Normal file
BIN
tests/support/diffmodels2-2.apkg
Normal file
Binary file not shown.
BIN
tests/support/diffmodels2.anki
Normal file
BIN
tests/support/diffmodels2.anki
Normal file
Binary file not shown.
|
@ -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"))
|
||||
|
|
Loading…
Reference in a new issue