From bd1c6d439578973a5bb5325ad0882c3f544f875c Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sat, 27 Oct 2012 20:55:08 +0900 Subject: [PATCH] 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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