diff --git a/anki/consts.py b/anki/consts.py index e07184caf..682314a23 100644 --- a/anki/consts.py +++ b/anki/consts.py @@ -47,7 +47,7 @@ SCHEMA_VERSION = 11 SYNC_ZIP_SIZE = int(2.5*1024*1024) SYNC_ZIP_COUNT = 100 SYNC_URL = os.environ.get("SYNC_URL") or "https://ankiweb.net/sync/" -SYNC_VER = 6 +SYNC_VER = 7 HELP_SITE="http://ankisrs.net/docs/manual.html" diff --git a/anki/sync.py b/anki/sync.py index 80e460295..8fe47471e 100644 --- a/anki/sync.py +++ b/anki/sync.py @@ -90,17 +90,39 @@ class Syncer(object): self.server = server def sync(self): - "Returns 'noChanges', 'fullSync', or 'success'." + "Returns 'noChanges', 'fullSync', 'success', etc" + self.syncMsg = "" + self.uname = "" # if the deck has any pending changes, flush them first and bump mod # time self.col.save() # step 1: login & metadata runHook("sync", "login") - ret = self.server.meta() - if not ret: + meta = self.server.meta() + if not meta: return "badAuth" - self.rmod, rscm, self.maxUsn, rts, self.mediaUsn = ret - self.lmod, lscm, self.minUsn, lts, dummy = self.meta() + rscm = meta['scm'] + rts = meta['ts'] + self.rmod = meta['mod'] + self.maxUsn = meta['usn'] + self.mediaUsn = meta['musn'] + self.syncMsg = meta['msg'] + # this is a temporary measure to address the problem of users + # forgetting which email address they've used - it will be removed + # when enough time has passed + self.uname = meta.get("uname", "") + # server requested abort? + if not meta['cont']: + return "serverAbort" + else: + # don't abort, but ui should show message after sync finishes + # and require confirmation if it's non-empty + pass + meta = self.meta() + self.lmod = meta['mod'] + self.minUsn = meta['usn'] + lscm = meta['scm'] + lts = meta['ts'] if abs(rts - lts) > 300: return "clockOff" if self.lmod == self.rmod: @@ -154,7 +176,15 @@ class Syncer(object): return "success" def meta(self): - return (self.col.mod, self.col.scm, self.col._usn, intTime(), None) + return dict( + mod=self.col.mod, + scm=self.col.scm, + usn=self.col._usn, + ts=intTime(), + musn=0, + msg="", + cont=True + ) def changes(self): "Bundle up small objects." diff --git a/aqt/sync.py b/aqt/sync.py index 6d4a321c1..b8a29a39a 100644 --- a/aqt/sync.py +++ b/aqt/sync.py @@ -26,6 +26,7 @@ class SyncManager(QObject): auth = self._getUserPass() if not auth: return + self.pm.profile['syncUser'] = auth[0] self._sync(auth) else: self._sync() @@ -50,6 +51,10 @@ class SyncManager(QObject): self.mw.app.processEvents() self.thread.wait(100) self.mw.progress.finish() + if self.thread.syncMsg: + showText(self.thread.syncMsg) + if self.thread.uname: + self.pm.profile['syncUser'] = self.thread.uname def delayedInfo(): if self._didFullUp and not self._didError: showInfo(_("""\ @@ -270,6 +275,8 @@ class SyncThread(QThread): return self.server = RemoteServer(self.hkey) self.client = Syncer(self.col, self.server) + self.syncMsg = "" + self.uname = "" self.sentTotal = 0 self.recvTotal = 0 # throttle updates; qt doesn't handle lots of posted events well @@ -349,8 +356,14 @@ class SyncThread(QThread): # save and note success state if ret == "noChanges": self.fireEvent("noChanges") - else: + elif ret == "success": self.fireEvent("success") + elif ret == "serverAbort": + self.fireEvent("error", self.client.syncMsg) + else: + self.fireEvent("error", "Unknown sync return code.") + self.syncMsg = self.client.syncMsg + self.uname = self.client.uname # then move on to media sync self._syncMedia() diff --git a/tests/test_remote_sync.py b/tests/test_remote_sync.py index c4d90caed..04feaf53d 100644 --- a/tests/test_remote_sync.py +++ b/tests/test_remote_sync.py @@ -45,11 +45,11 @@ def test_meta(): print "aborting; server offline" return ts.server.hkey = TEST_HKEY - (mod, scm, usn, tstamp, mediaUSN) = ts.server.meta() - assert mod - assert scm - assert mod != ts.client.col.mod - assert abs(tstamp - time.time()) < 3 + meta = ts.server.meta() + assert meta['mod'] + assert meta['scm'] + assert meta['mod'] != ts.client.col.mod + assert abs(meta['ts'] - time.time()) < 3 @nose.with_setup(setup_remote) def test_hkey():