mirror of
https://github.com/ankitects/anki.git
synced 2025-11-11 15:17:12 -05:00
sync tweaks
This commit is contained in:
parent
d148a6cf1b
commit
6965a9c102
2 changed files with 45 additions and 38 deletions
67
anki/sync.py
67
anki/sync.py
|
|
@ -35,16 +35,14 @@ class Syncer(object):
|
|||
self.col = col
|
||||
self.server = server
|
||||
|
||||
def status(self, type):
|
||||
"Override to trace sync progress."
|
||||
#print "sync:", type
|
||||
pass
|
||||
|
||||
def sync(self):
|
||||
"Returns 'noChanges', 'fullSync', or 'success'."
|
||||
# step 1: login & metadata
|
||||
self.status("login")
|
||||
self.rmod, rscm, self.maxUsn, rts, self.mediaUsn = self.server.meta()
|
||||
runHook("sync", "login")
|
||||
ret = self.server.meta()
|
||||
if not ret:
|
||||
return "badAuth"
|
||||
self.rmod, rscm, self.maxUsn, rts, self.mediaUsn = ret
|
||||
self.lmod, lscm, self.minUsn, lts, dummy = self.meta()
|
||||
if abs(rts - lts) > 300:
|
||||
return "clockOff"
|
||||
|
|
@ -54,34 +52,34 @@ class Syncer(object):
|
|||
return "fullSync"
|
||||
self.lnewer = self.lmod > self.rmod
|
||||
# step 2: deletions and small objects
|
||||
self.status("meta")
|
||||
runHook("sync", "meta")
|
||||
lchg = self.changes()
|
||||
rchg = self.server.applyChanges(
|
||||
minUsn=self.minUsn, lnewer=self.lnewer, changes=lchg)
|
||||
self.mergeChanges(lchg, rchg)
|
||||
# step 3: stream large tables from server
|
||||
self.status("server")
|
||||
runHook("sync", "server")
|
||||
while 1:
|
||||
self.status("stream")
|
||||
runHook("sync", "stream")
|
||||
chunk = self.server.chunk()
|
||||
self.applyChunk(chunk=chunk)
|
||||
if chunk['done']:
|
||||
break
|
||||
# step 4: stream to server
|
||||
self.status("client")
|
||||
runHook("sync", "client")
|
||||
while 1:
|
||||
self.status("stream")
|
||||
runHook("sync", "stream")
|
||||
chunk = self.chunk()
|
||||
self.server.applyChunk(chunk=chunk)
|
||||
if chunk['done']:
|
||||
break
|
||||
# step 5: sanity check during beta testing
|
||||
self.status("sanity")
|
||||
runHook("sync", "sanity")
|
||||
c = self.sanityCheck()
|
||||
s = self.server.sanityCheck()
|
||||
assert c == s
|
||||
# finalize
|
||||
self.status("finalize")
|
||||
runHook("sync", "finalize")
|
||||
mod = self.server.finish()
|
||||
self.finish(mod)
|
||||
return "success"
|
||||
|
|
@ -381,16 +379,6 @@ class LocalServer(Syncer):
|
|||
|
||||
class HttpSyncer(object):
|
||||
|
||||
# retrieving a host key for future operations
|
||||
def hostKey(self, pw):
|
||||
h = httpCon()
|
||||
resp, cont = h.request(
|
||||
SYNC_URL+"hostKey?" + urllib.urlencode(dict(u=self.user,p=pw)))
|
||||
if resp['status'] != '200':
|
||||
raise Exception("Invalid response code: %s" % resp['status'])
|
||||
self.hkey = simplejson.loads(cont)['key']
|
||||
return self.hkey
|
||||
|
||||
def _vars(self):
|
||||
return dict(k=self.hkey)
|
||||
|
||||
|
|
@ -449,23 +437,38 @@ Content-Type: application/octet-stream\r\n\r\n""")
|
|||
|
||||
class RemoteServer(Syncer, HttpSyncer):
|
||||
|
||||
def __init__(self, user, hkey):
|
||||
self.user = user
|
||||
def __init__(self, hkey):
|
||||
self.hkey = hkey
|
||||
self.con = httpCon()
|
||||
|
||||
def hostKey(self, user, pw):
|
||||
"Returns hkey or none if user/pw incorrect."
|
||||
user = user.encode("utf-8")
|
||||
pw = pw.encode("utf-8")
|
||||
resp, cont = self.con.request(
|
||||
SYNC_URL+"hostKey?" + urllib.urlencode(dict(u=user,p=pw)))
|
||||
if resp['status'] == '200':
|
||||
self.hkey = simplejson.loads(cont)['key']
|
||||
return self.hkey
|
||||
elif resp['status'] == '403':
|
||||
# invalid auth
|
||||
return
|
||||
else:
|
||||
raise Exception("Unknown response code: %s" % resp['status'])
|
||||
return
|
||||
|
||||
def meta(self):
|
||||
resp, cont = self.con.request(
|
||||
SYNC_URL+"meta?" + urllib.urlencode(dict(u=self.user,v=SYNC_VER)))
|
||||
# fixme: convert these into easily-catchable errors
|
||||
if resp['status'] in ('503', '504'):
|
||||
SYNC_URL+"meta?" + urllib.urlencode(dict(k=self.hkey,v=SYNC_VER)))
|
||||
if resp['status'] == '403':
|
||||
# auth failure
|
||||
return
|
||||
elif resp['status'] in ('503', '504'):
|
||||
raise Exception("Server is too busy; please try again later.")
|
||||
elif resp['status'] == '501':
|
||||
raise Exception("Your client is out of date; please upgrade.")
|
||||
elif resp['status'] == '403':
|
||||
raise Exception("Invalid key; please authenticate.")
|
||||
elif resp['status'] != '200':
|
||||
raise Exception("Invalid response code: %s" % resp['status'])
|
||||
raise Exception("Unknown response code: %s" % resp['status'])
|
||||
return simplejson.loads(cont)
|
||||
|
||||
def applyChanges(self, **kw):
|
||||
|
|
|
|||
|
|
@ -34,19 +34,23 @@ def setup_remote():
|
|||
setup_basic()
|
||||
# mark deck1 as changed
|
||||
ts.deck1.save()
|
||||
ts.server = RemoteServer(TEST_USER, TEST_HKEY)
|
||||
ts.server = RemoteServer(TEST_HKEY)
|
||||
ts.client.server = ts.server
|
||||
|
||||
@nose.with_setup(setup_remote)
|
||||
def test_meta():
|
||||
global TEST_REMOTE
|
||||
try:
|
||||
(mod, scm, usn, tstamp, dummy) = ts.server.meta()
|
||||
# if the key is wrong, meta returns nothing
|
||||
ts.server.hkey = "abc"
|
||||
assert not ts.server.meta()
|
||||
except Exception, e:
|
||||
if e.errno == 61:
|
||||
TEST_REMOTE = False
|
||||
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
|
||||
|
|
@ -56,9 +60,9 @@ def test_meta():
|
|||
def test_hkey():
|
||||
if not TEST_REMOTE:
|
||||
return
|
||||
assertException(Exception, lambda: ts.server.hostKey("wrongpass"))
|
||||
ts.server.hkey = "abc"
|
||||
k = ts.server.hostKey(TEST_PASS)
|
||||
assert not ts.server.hostKey(TEST_USER, "wrongpass")
|
||||
ts.server.hkey = "willchange"
|
||||
k = ts.server.hostKey(TEST_USER, TEST_PASS)
|
||||
assert k == ts.server.hkey == TEST_HKEY
|
||||
|
||||
@nose.with_setup(setup_remote)
|
||||
|
|
@ -103,7 +107,7 @@ def setup_remoteMedia():
|
|||
setup_basic()
|
||||
con = httpCon()
|
||||
ts.server = RemoteMediaServer(TEST_HKEY, con)
|
||||
ts.server2 = RemoteServer(TEST_USER, TEST_HKEY)
|
||||
ts.server2 = RemoteServer(TEST_HKEY)
|
||||
ts.client = MediaSyncer(ts.deck1, ts.server)
|
||||
|
||||
@nose.with_setup(setup_remoteMedia)
|
||||
|
|
|
|||
Loading…
Reference in a new issue