From 729862e45ec21e0a8296a320cb5416f66dcc51a7 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 26 Oct 2012 14:48:01 +0900 Subject: [PATCH 01/10] show 3 buttons for new cards --- anki/sched.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/anki/sched.py b/anki/sched.py index 6054b6f9b..e3a8df4d0 100644 --- a/anki/sched.py +++ b/anki/sched.py @@ -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: From 9c43e5928c18d908e0e6bf57784d81d7f222bb25 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 26 Oct 2012 16:59:18 +0900 Subject: [PATCH 02/10] fix unicode error in stats --- anki/stats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/anki/stats.py b/anki/stats.py index e8cac5061..8b579c1bd 100644 --- a/anki/stats.py +++ b/anki/stats.py @@ -154,7 +154,7 @@ from revlog where id > ? """+lim, (self.col.sched.dayCutoff-86400)*1000) filt = filt or 0 # studied def bold(s): - return ""+str(s)+"" + return ""+unicode(s)+"" 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))) From 8a06ab7bfe1045fa892984fddfdb498f4e48820c Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 26 Oct 2012 19:30:01 +0900 Subject: [PATCH 03/10] break on div start, not end --- anki/latex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/anki/latex.py b/anki/latex.py index 90cfd5e78..58a017dff 100644 --- a/anki/latex.py +++ b/anki/latex.py @@ -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("|", "\n", latex) + latex = re.sub("|
", "\n", latex) # replace
etc with spaces latex = re.sub("<.+?>", " ", latex) latex = stripHTML(latex) From 76926c953aeadd88ee12245c764e250ea2fee19d Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 26 Oct 2012 19:43:34 +0900 Subject: [PATCH 04/10] make sure we don't delete cloze notes in db check --- anki/collection.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/anki/collection.py b/anki/collection.py index 7ca632f92..61710b6a8 100644 --- a/anki/collection.py +++ b/anki/collection.py @@ -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 = ?)""" % From 6830ca99d9331e2e4105fe0853ad758c8f7b77bc Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 26 Oct 2012 23:35:16 +0900 Subject: [PATCH 05/10] fix again count --- anki/stats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/anki/stats.py b/anki/stats.py index 8b579c1bd..7cc4c9ba5 100644 --- a/anki/stats.py +++ b/anki/stats.py @@ -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 */ From bd1c6d439578973a5bb5325ad0882c3f544f875c Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sat, 27 Oct 2012 20:55:08 +0900 Subject: [PATCH 06/10] more robust anki1 upgrading & anki2 importing - base the 2.0 model id on the 1.2 one so we don't get new models each time we reimport the file - when determining if we can reuse an existing note, it must have the same model id, and the model must have the same schema - make sure we check templates when determining schema - if schema has diverged, note needs new guid --- anki/importing/anki2.py | 27 +++++++++++++++++++++++---- anki/models.py | 2 ++ anki/upgrade.py | 7 ++----- tests/support/diffmodels1.anki | Bin 0 -> 39424 bytes tests/support/diffmodels2.anki | Bin 0 -> 37888 bytes tests/test_importing.py | 20 ++++++++++++++++++++ 6 files changed, 47 insertions(+), 9 deletions(-) create mode 100644 tests/support/diffmodels1.anki create mode 100644 tests/support/diffmodels2.anki diff --git a/anki/importing/anki2.py b/anki/importing/anki2.py index 0b41ad804..7ccd8835b 100644 --- a/anki/importing/anki2.py +++ b/anki/importing/anki2.py @@ -4,7 +4,7 @@ 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 @@ -96,8 +96,24 @@ 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: + duplicate = False + guidChange = False + # do we have the same guid? + if guid in self._notes: + # 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 a duplicate + duplicate = True + if not duplicate: + # not identical models, so we need to change guid + guidChange = True + # missing from local col or divergent model? + if not duplicate: # get corresponding local model lmid = self._mid(mid) # ensure id is unique @@ -111,11 +127,14 @@ 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 guidChange: + guid = guid64() # 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: diff --git a/anki/models.py b/anki/models.py index e6ca38c67..e4044875d 100644 --- a/anki/models.py +++ b/anki/models.py @@ -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 diff --git a/anki/upgrade.py b/anki/upgrade.py index 259720586..4434ec541 100644 --- a/anki/upgrade.py +++ b/anki/upgrade.py @@ -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] diff --git a/tests/support/diffmodels1.anki b/tests/support/diffmodels1.anki new file mode 100644 index 0000000000000000000000000000000000000000..aa2103b05a340e839959c5ddbbb6299b045f2cb2 GIT binary patch literal 39424 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 literal 0 HcmV?d00001 diff --git a/tests/support/diffmodels2.anki b/tests/support/diffmodels2.anki new file mode 100644 index 0000000000000000000000000000000000000000..0f921c90a884c6cbf8e735324c84982793953055 GIT binary patch literal 37888 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 Date: Sat, 27 Oct 2012 21:11:16 +0900 Subject: [PATCH 07/10] make sure we update guid in note object too --- anki/importing/anki2.py | 9 ++++----- tests/support/diffmodels2-1.apkg | Bin 0 -> 3070 bytes tests/support/diffmodels2-2.apkg | Bin 0 -> 3186 bytes tests/test_importing.py | 20 ++++++++++++++++++++ 4 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 tests/support/diffmodels2-1.apkg create mode 100644 tests/support/diffmodels2-2.apkg diff --git a/anki/importing/anki2.py b/anki/importing/anki2.py index 7ccd8835b..18c6fad6d 100644 --- a/anki/importing/anki2.py +++ b/anki/importing/anki2.py @@ -97,9 +97,10 @@ class Anki2Importer(Importer): note = list(note) guid, mid = note[1:3] duplicate = False - guidChange = False + first = True # do we have the same guid? if guid in self._notes: + first = False # and do they share the same model id? if self._notes[guid][2] == mid: # and do they share the same schema? @@ -109,9 +110,6 @@ class Anki2Importer(Importer): self.src.models.scmhash(dstM)): # then it's safe to treat as a duplicate duplicate = True - if not duplicate: - # not identical models, so we need to change guid - guidChange = True # missing from local col or divergent model? if not duplicate: # get corresponding local model @@ -129,8 +127,9 @@ class Anki2Importer(Importer): 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 guidChange: + if not first: guid = guid64() + note[1] = guid # note we have the added note self._notes[guid] = (note[0], note[3], note[2]) else: diff --git a/tests/support/diffmodels2-1.apkg b/tests/support/diffmodels2-1.apkg new file mode 100644 index 0000000000000000000000000000000000000000..e3c85af8114518bded85823215f9f1fd21b475bd GIT binary patch literal 3070 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 I|L83K1!G+XApigX literal 0 HcmV?d00001 diff --git a/tests/test_importing.py b/tests/test_importing.py index 023996b3f..d06dc5aad 100644 --- a/tests/test_importing.py +++ b/tests/test_importing.py @@ -158,6 +158,26 @@ def test_anki1_diffmodels(): # 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 + def test_csv(): deck = getEmptyDeck() file = unicode(os.path.join(testDir, "support/text-2fields.txt")) From 1403d360424c7e1dfda88548aa0d20fef5fb4d7a Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sat, 27 Oct 2012 21:59:31 +0900 Subject: [PATCH 08/10] need to account for guid changes when importing cards --- anki/importing/anki2.py | 22 ++++++++++++++-------- tests/test_importing.py | 2 ++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/anki/importing/anki2.py b/anki/importing/anki2.py index 18c6fad6d..5ff01b489 100644 --- a/anki/importing/anki2.py +++ b/anki/importing/anki2.py @@ -86,6 +86,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,11 +99,11 @@ class Anki2Importer(Importer): # turn the db result into a mutable list note = list(note) guid, mid = note[1:3] - duplicate = False - first = True + canUseExisting = False + alreadyHaveGuid = False # do we have the same guid? if guid in self._notes: - first = False + alreadyHaveGuid = True # and do they share the same model id? if self._notes[guid][2] == mid: # and do they share the same schema? @@ -108,10 +111,10 @@ class Anki2Importer(Importer): 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 a duplicate - duplicate = True - # missing from local col or divergent model? - if not duplicate: + # 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 @@ -127,8 +130,9 @@ class Anki2Importer(Importer): 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 not first: + 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]) @@ -263,6 +267,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 diff --git a/tests/test_importing.py b/tests/test_importing.py index d06dc5aad..8f5134b13 100644 --- a/tests/test_importing.py +++ b/tests/test_importing.py @@ -177,6 +177,8 @@ def test_anki2_diffmodels(): 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() From 29b0628141e435900173c62d74e5bf4a89e6707d Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sun, 28 Oct 2012 00:04:36 +0900 Subject: [PATCH 09/10] revert import deck name selection --- anki/importing/anki2.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/anki/importing/anki2.py b/anki/importing/anki2.py index 5ff01b489..4a95440c0 100644 --- a/anki/importing/anki2.py +++ b/anki/importing/anki2.py @@ -8,7 +8,6 @@ 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 From 4011fa30843690697bca76136ebcab44ce51a104 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sun, 28 Oct 2012 00:57:28 +0900 Subject: [PATCH 10/10] add decimal point to reviews/day stat --- anki/stats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/anki/stats.py b/anki/stats.py index 7cc4c9ba5..ada18bbd8 100644 --- a/anki/stats.py +++ b/anki/stats.py @@ -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 ""