From 7491cfc521f9b2a987d69bd4892fdd67ce0484f6 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Tue, 23 Nov 2010 23:12:34 +0900 Subject: [PATCH] new syncName handling - the local deck name must now match the online deck - syncName is a hash of the current deck's path or None - the hash is checked on deck load, and if it is different (because the deck was copied or moved), syncing is disabled. This should prevent people from accidentally clobbering their online decks --- anki/deck.py | 71 ++++++++++++++++++++++++++++++++++++++++++++++++--- anki/hooks.py | 3 +++ 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/anki/deck.py b/anki/deck.py index 8ed63da5c..288c7c1df 100644 --- a/anki/deck.py +++ b/anki/deck.py @@ -16,7 +16,7 @@ from anki.lang import _, ngettext from anki.errors import DeckAccessError from anki.stdmodels import BasicModel from anki.utils import parseTags, tidyHTML, genID, ids2str, hexifyID, \ - canonifyTags, joinTags, addTags + canonifyTags, joinTags, addTags, checksum from anki.history import CardHistoryEntry from anki.models import Model, CardModel, formatQA from anki.stats import dailyStats, globalStats, genToday @@ -24,7 +24,7 @@ from anki.fonts import toPlatformFont from anki.tags import initTagTables, tagIds from operator import itemgetter from itertools import groupby -from anki.hooks import runHook +from anki.hooks import runHook, hookEmpty # ensure all the metadata in other files is loaded before proceeding import anki.models, anki.facts, anki.cards, anki.stats @@ -68,7 +68,7 @@ SEARCH_FIELD = 6 SEARCH_FIELD_EXISTS = 7 SEARCH_QA = 8 SEARCH_PHRASE_WB = 9 -DECK_VERSION = 50 +DECK_VERSION = 52 deckVarsTable = Table( 'deckVars', metadata, @@ -84,7 +84,9 @@ decksTable = Table( Column('description', UnicodeText, nullable=False, default=u""), Column('version', Integer, nullable=False, default=DECK_VERSION), Column('currentModelId', Integer, ForeignKey("models.id")), - # syncing + # syncName stores an md5sum of the deck path when syncing is enabled. If + # it doesn't match the current deck path, the deck has been moved, + # and syncing is disabled on load. Column('syncName', UnicodeText), Column('lastSync', Float, nullable=False, default=0), # scheduling @@ -2800,6 +2802,16 @@ select id from facts where spaceUntil like :_ff_%d escape '\\'""" % c def disableProgressHandler(self): self.progressHandlerEnabled = False + # Notifications + ########################################################################## + + def notify(self, msg): + "Send a notice to all listeners, or display on stdout." + if hookEmpty("notify"): + sys.stderr.write(msg + "\n") + else: + runHook("notify", msg) + # File-related ########################################################################## @@ -3066,6 +3078,33 @@ Return new path, relative to media dir.""" self.finishProgress() return newDeck + # Syncing + ########################################################################## + # toggling does not bump deck mod time, since it may happen on upgrade, + # and the variable is not synced + + def enableSyncing(self): + self.syncName = unicode(checksum(self.path)) + self.lastSync = 0 + self.s.commit() + + def disableSyncing(self): + self.syncName = None + self.lastSync = 0 + self.s.commit() + + def syncingEnabled(self): + return self.syncName + + def checkSyncHash(self): + if self.syncName and self.syncName != checksum(self.path): + self.notify(_("""\ +Because '%s' has been moved or copied, automatic synchronisation \ +has been disabled. (ERR-0100)""") % self.name()) + self.disableSyncing() + self.syncName = None + self.lastSync = 0 + # DB maintenance ########################################################################## @@ -3592,6 +3631,8 @@ update cards set type = type - 3 where type between 0 and 2 and priority = -3""" deck.s.statement( "update cards set type = relativeDelay where type > 2") deck.s.commit() + # check if deck has been moved, and disable syncing + deck.checkSyncHash() # determine starting factor for new cards deck.averageFactor = (deck.s.scalar( "select avg(factor) from cards where type = 1") @@ -4150,6 +4191,28 @@ nextFactor, reps, thinkingTime, yesCount, noCount from reviewHistory""") DeckStorage._addIndices(deck) deck.version = 50 deck.s.commit() + # skip 51 + if deck.version < 52: + dname = deck.name() + sname = deck.syncName + if sname and dname != sname: + deck.notify(_("""\ +When syncing, Anki now uses the same deck name on the server as the deck \ +name on your computer. Because you had '%(dname)s' set to sync to \ +'%(sname)s' on the server, syncing has been temporarily disabled. + +If you want to keep your changes to the online version, please use \ +File>Download>Personal Deck to download the online version. + +If you want to keep the version on your computer, please enable \ +syncing again via Settings>Deck Properties>Synchronsiation. \ +(ERR-0101)""") % { + 'sname':sname, 'dname':dname}) + deck.disableSyncing() + elif sname: + deck.enableSyncing() + deck.version = 52 + deck.s.commit() # executing a pragma here is very slow on large decks, so we store # our own record if not deck.getInt("pageSize") == 4096: diff --git a/anki/hooks.py b/anki/hooks.py index c43e104bf..fc91c85ad 100644 --- a/anki/hooks.py +++ b/anki/hooks.py @@ -45,6 +45,9 @@ def removeHook(hook, func): if func in hook: hook.remove(func) +def hookEmpty(hook): + return not _hooks.get(hook) + # Instrumenting ##############################################################################