def setup_local(loadDecks=None): global deck1, deck2, client, server if loadDecks: deck1 = DeckStorage.Deck(loadDecks[0], backup=False) deck2 = DeckStorage.Deck(loadDecks[1], backup=False) else: deck1 = DeckStorage.Deck() deck1.addModel(BasicModel()) deck1.currentModel.cardModels[1].active = True deck1.newCardOrder = 1 f = deck1.newFact() f["Front"] = u"foo" f["Back"] = u"bar" f.tags = u"foo" deck1.addFact(f) deck2 = DeckStorage.Deck() deck2.addModel(BasicModel()) deck2.currentModel.cardModels[1].active = True f = deck2.newFact() f["Front"] = u"baz" f["Back"] = u"qux" f.tags = u"bar" deck2.addFact(f) deck2.newCardOrder = 1 client = SyncClient(deck1) server = SyncServer(deck2) client.setServer(server)
def exportInto(self, path): n = 3 if not self.includeSchedulingInfo: n += 1 try: os.unlink(path) except (IOError, OSError): pass self.newCol = DeckStorage.Deck(path) client = SyncClient(self.deck) server = SyncServer(self.newDeck) client.setServer(server) client.localTime = self.deck.modified client.remoteTime = 0 self.deck.db.flush() # set up a custom change list and sync lsum = self.localSummary() rsum = server.summary(0) payload = client.genPayload((lsum, rsum)) res = server.applyPayload(payload) if not self.includeSchedulingInfo: self.newDeck.resetCards() # media if self.includeMedia: server.deck.mediaPrefix = "" copyLocalMedia(client.deck, server.deck) # need to save manually self.newDeck.rebuildCounts() # FIXME #self.exportedCards = self.newDeck.cardCount self.newDeck.crt = 0 self.newDeck.db.commit() self.newDeck.close()
def exportInto(self, path): n = 3 if not self.includeSchedulingInfo: n += 1 try: os.unlink(path) except (IOError, OSError): pass self.newDeck = DeckStorage.Deck(path) client = SyncClient(self.deck) server = SyncServer(self.newDeck) client.setServer(server) client.localTime = self.deck.modified client.remoteTime = 0 self.deck.db.flush() # set up a custom change list and sync lsum = self.localSummary() rsum = server.summary(0) payload = client.genPayload((lsum, rsum)) res = server.applyPayload(payload) if not self.includeSchedulingInfo: self.newDeck.resetCards() # media if self.includeMedia: server.deck.mediaPrefix = "" copyLocalMedia(client.deck, server.deck) # need to save manually self.newDeck.rebuildCounts() # FIXME #self.exportedCards = self.newDeck.cardCount self.newDeck.crt = 0 self.newDeck.db.commit() self.newDeck.close()
def test_localsync_threeway(): # deck1 (client) <-> deck2 (server) <-> deck3 (client) deck3 = DeckStorage.Deck() client2 = SyncClient(deck3) server2 = SyncServer(deck2) client2.setServer(server2) client.sync() client2.sync() # add a new question f = deck1.newFact() f['Front'] = u"a" f['Back'] = u"b" f = deck1.addFact(f) card = f.cards[0] client.sync() assert deck1.cardCount == 6 assert deck2.cardCount == 6 # check it propagates from server to deck3 client2.sync() assert deck3.cardCount == 6 # delete a card on deck1 deck1.deleteCard(card.id) client.sync() assert deck1.cardCount == 5 assert deck2.cardCount == 5 # make sure the delete is now propagated from the server to deck3 client2.sync() assert deck3.cardCount == 5
def test_anki10_modtime(): deck1 = DeckStorage.Deck() deck2 = DeckStorage.Deck() client = SyncClient(deck1) server = SyncServer(deck2) client.setServer(server) deck1.addModel(BasicModel()) f = deck1.newFact() f['Front'] = u"foo"; f['Back'] = u"bar" deck1.addFact(f) assert deck1.cardCount == 1 assert deck2.cardCount == 0 client.sync() assert deck1.cardCount == 1 assert deck2.cardCount == 1 file_ = unicode(os.path.join(testDir, "importing/test10-3.anki")) file = "/tmp/test10-3.anki" shutil.copy(file_, file) i = anki10.Anki10Importer(deck1, file) i.doImport() client.sync() assert i.total == 1 assert deck2.s.scalar("select count(*) from cards") == 2 assert deck2.s.scalar("select count(*) from facts") == 2 assert deck2.s.scalar("select count(*) from models") == 2
def test_anki10_modtime(): deck1 = Deck() deck2 = Deck() client = SyncClient(deck1) server = SyncServer(deck2) client.setServer(server) deck1.addModel(BasicModel()) f = deck1.newFact() f['Front'] = u"foo" f['Back'] = u"bar" deck1.addFact(f) assert deck1.cardCount() == 1 assert deck2.cardCount() == 0 client.sync() assert deck1.cardCount() == 1 assert deck2.cardCount() == 1 file_ = unicode(os.path.join(testDir, "importing/test10-3.anki")) file = "/tmp/test10-3.anki" shutil.copy(file_, file) i = anki10.Anki10Importer(deck1, file) i.doImport() client.sync() assert i.total == 1 assert deck2.db.scalar("select count(*) from cards") == 2 assert deck2.db.scalar("select count(*) from facts") == 2 assert deck2.db.scalar("select count(*) from models") == 2
def test_localsync_threeway(): # deck1 (client) <-> deck2 (server) <-> deck3 (client) deck3 = DeckStorage.Deck() client2 = SyncClient(deck3) server2 = SyncServer(deck2) client2.setServer(server2) client.sync() client2.sync() # add a new question f = deck1.newFact() f['Front'] = u"a"; f['Back'] = u"b" f = deck1.addFact(f) card = f.cards[0] client.sync() assert deck1.cardCount == 6 assert deck2.cardCount == 6 # check it propagates from server to deck3 client2.sync() assert deck3.cardCount == 6 # delete a card on deck1 deck1.deleteCard(card.id) client.sync() assert deck1.cardCount == 5 assert deck2.cardCount == 5 # make sure the delete is now propagated from the server to deck3 client2.sync() assert deck3.cardCount == 5
def setup_local(loadDecks=None): global deck1, deck2, client, server if loadDecks: deck1 = DeckStorage.Deck(loadDecks[0], backup=False) deck2 = DeckStorage.Deck(loadDecks[1], backup=False) else: deck1 = DeckStorage.Deck() deck1.addModel(BasicModel()) deck1.currentModel.cardModels[1].active = True deck1.newCardOrder = 1 f = deck1.newFact() f['Front'] = u"foo" f['Back'] = u"bar" f.tags = u"foo" deck1.addFact(f) deck2 = DeckStorage.Deck() deck2.addModel(BasicModel()) deck2.currentModel.cardModels[1].active = True f = deck2.newFact() f['Front'] = u"baz" f['Back'] = u"qux" f.tags = u"bar" deck2.addFact(f) deck2.newCardOrder = 1 client = SyncClient(deck1) server = SyncServer(deck2) client.setServer(server)
def setup_local(loadDecks=None): global deck1, deck2, client, server if loadDecks: deck1 = Deck(loadDecks[0], backup=False) deck2 = Deck(loadDecks[1], backup=False) else: deck1 = Deck() deck1.addModel(BasicModel()) deck1.currentModel.cardModels[1].active = True deck1.newCardOrder = 1 f = deck1.newFact() f['Front'] = u"foo"; f['Back'] = u"bar"; f.tags = u"foo" deck1.addFact(f) deck2 = Deck() deck2.addModel(BasicModel()) deck2.currentModel.cardModels[1].active = True f = deck2.newFact() f['Front'] = u"baz"; f['Back'] = u"qux"; f.tags = u"bar" deck2.addFact(f) deck2.newCardOrder = 1 # normally such syncs would trigger a conflict, but we ignore it deck1.setVar("schemaMod", 0) deck2.setVar("schemaMod", 0) client = SyncClient(deck1) server = SyncServer(deck2) client.setServer(server)
def setup_local(loadDecks=None): global deck1, deck2, client, server if loadDecks: deck1 = Deck(loadDecks[0], backup=False) deck2 = Deck(loadDecks[1], backup=False) else: deck1 = Deck() deck1.addModel(BasicModel()) deck1.currentModel.cardModels[1].active = True deck1.newCardOrder = 1 f = deck1.newFact() f['Front'] = u"foo" f['Back'] = u"bar" f.tags = u"foo" deck1.addFact(f) deck2 = Deck() deck2.addModel(BasicModel()) deck2.currentModel.cardModels[1].active = True f = deck2.newFact() f['Front'] = u"baz" f['Back'] = u"qux" f.tags = u"bar" deck2.addFact(f) deck2.newCardOrder = 1 # normally such syncs would trigger a conflict, but we ignore it deck1.setVar("schemaMod", 0) deck2.setVar("schemaMod", 0) client = SyncClient(deck1) server = SyncServer(deck2) client.setServer(server)
def doImport(self): "Import." random = self.deck.newCardOrder == NEW_CARDS_RANDOM num = 4 if random: num += 1 src = DeckStorage.Deck(self.file, backup=False) client = SyncClient(self.deck) server = SyncServer(src) client.setServer(server) # if there is a conflict, sync local -> src client.localTime = self.deck.modified client.remoteTime = 0 src.s.execute("update facts set modified = 1") src.s.execute("update models set modified = 1") src.s.execute("update cards set modified = 1") src.s.execute("update media set created = 1") self.deck.db.flush() # set up a custom change list and sync lsum = client.summary(0) self._clearDeleted(lsum) rsum = server.summary(0) self._clearDeleted(rsum) payload = client.genPayload((lsum, rsum)) # no need to add anything to src payload['added-models'] = [] payload['added-cards'] = [] payload['added-facts'] = {'facts': [], 'fields': []} assert payload['deleted-facts'] == [] assert payload['deleted-cards'] == [] assert payload['deleted-models'] == [] res = server.applyPayload(payload) client.applyPayloadReply(res) copyLocalMedia(server.deck, client.deck) # add tags fids = [f[0] for f in res['added-facts']['facts']] self.deck.addTags(fids, self.tagsToAdd) # mark import material as newly added self.deck.db.execute("update cards set modified = :t where id in %s" % ids2str([x[0] for x in res['added-cards']]), t=time.time()) self.deck.db.execute( "update facts set modified = :t where id in %s" % ids2str([x[0] for x in res['added-facts']['facts']]), t=time.time()) self.deck.db.execute("update models set modified = :t where id in %s" % ids2str([x['id'] for x in res['added-models']]), t=time.time()) # update total and refresh self.total = len(res['added-facts']['facts']) src.s.rollback() src.engine.dispose() # randomize? if random: self.deck.randomizeNewCards([x[0] for x in res['added-cards']]) self.deck.flushMod()
def doImport(self): "Import." random = self.deck.newCardOrder == NEW_CARDS_RANDOM num = 4 if random: num += 1 src = DeckStorage.Deck(self.file, backup=False) client = SyncClient(self.deck) server = SyncServer(src) client.setServer(server) # if there is a conflict, sync local -> src client.localTime = self.deck.modified client.remoteTime = 0 src.s.execute("update facts set modified = 1") src.s.execute("update models set modified = 1") src.s.execute("update cards set modified = 1") src.s.execute("update media set created = 1") self.deck.db.flush() # set up a custom change list and sync lsum = client.summary(0) self._clearDeleted(lsum) rsum = server.summary(0) self._clearDeleted(rsum) payload = client.genPayload((lsum, rsum)) # no need to add anything to src payload['added-models'] = [] payload['added-cards'] = [] payload['added-facts'] = {'facts': [], 'fields': []} assert payload['deleted-facts'] == [] assert payload['deleted-cards'] == [] assert payload['deleted-models'] == [] res = server.applyPayload(payload) client.applyPayloadReply(res) copyLocalMedia(server.deck, client.deck) # add tags fids = [f[0] for f in res['added-facts']['facts']] self.deck.addTags(fids, self.tagsToAdd) # mark import material as newly added self.deck.db.execute( "update cards set modified = :t where id in %s" % ids2str([x[0] for x in res['added-cards']]), t=time.time()) self.deck.db.execute( "update facts set modified = :t where id in %s" % ids2str([x[0] for x in res['added-facts']['facts']]), t=time.time()) self.deck.db.execute( "update models set modified = :t where id in %s" % ids2str([x['id'] for x in res['added-models']]), t=time.time()) # update total and refresh self.total = len(res['added-facts']['facts']) src.s.rollback() src.engine.dispose() # randomize? if random: self.deck.randomizeNewCards([x[0] for x in res['added-cards']]) self.deck.flushMod()
def setup_remote(): setup_local() global client, server proxy = HttpSyncServerProxy("test", "foo") client = SyncClient(deck1) client.setServer(proxy) proxy.deckName = "test" proxy.runCmd = runCmd server = HttpSyncServer() server.deck = deck2 server.decks = {"test": (deck2.modified, 0)}
def setup_local(): global deck1, deck2, client, server deck1 = DeckStorage.Deck() deck1.addModel(BasicModel()) deck1.currentModel.cardModels[1].active = True deck1.newCardOrder = 1 f = deck1.newFact() f['Front'] = u"foo"; f['Back'] = u"bar" deck1.addFact(f) deck2 = DeckStorage.Deck() deck2.addModel(BasicModel()) deck2.currentModel.cardModels[1].active = True f = deck2.newFact() f['Front'] = u"baz"; f['Back'] = u"qux" deck2.addFact(f) # ensure a defined order in getCard() deck2.newCardOrder = 1 client = SyncClient(deck1) server = SyncServer(deck2) client.setServer(server)
def setup_local(loadDecks=None): global deck1, deck2, client, server if loadDecks: deck1 = Deck(loadDecks[0], backup=False) deck2 = Deck(loadDecks[1], backup=False) else: deck1 = getEmptyDeck() f = deck1.newFact() f['Front'] = u"foo"; f['Back'] = u"bar"; f.tags = [u"foo"] deck1.addFact(f) deck1.syncName = "abc" deck2 = getEmptyDeck() f = deck2.newFact() f['Front'] = u"foo"; f['Back'] = u"bar"; f.tags = [u"foo"] deck2.addFact(f) deck2.syncName = "abc" deck1.lastSync = deck2.lastSync = intTime() deck1.scm = deck2.scm = 0 time.sleep(1) # now add another fact to deck1 that hasn't been synced yet f = deck1.newFact() f['Front'] = u"bar"; f['Back'] = u"baz" deck1.addFact(f) # and another to deck2 f = deck2.newFact() f['Front'] = u"qux"; f['Back'] = u"baz" deck2.addFact(f) deck2.reset() c = deck2.sched.getCard() deck2.sched.answerCard(c, 3) # change deck1's model deck1.currentModel().flush() deck1.save(); deck2.save() client = SyncClient(deck1) server = SyncServer(deck2) print "deck1", client.deck.db.all("select * from facts") print "deck2", server.deck.db.all("select * from facts") client.setServer(server)
def exportInto(self, path): n = 3 if not self.includeSchedulingInfo: n += 1 self.deck.startProgress(n) self.deck.updateProgress(_("Exporting...")) try: os.unlink(path) except (IOError, OSError): pass self.newDeck = DeckStorage.Deck(path) client = SyncClient(self.deck) server = SyncServer(self.newDeck) client.setServer(server) client.localTime = self.deck.modified client.remoteTime = 0 self.deck.s.flush() # set up a custom change list and sync lsum = self.localSummary() rsum = server.summary(0) self.deck.updateProgress() payload = client.genPayload((lsum, rsum)) self.deck.updateProgress() res = server.applyPayload(payload) if not self.includeSchedulingInfo: self.deck.updateProgress() self.newDeck.s.statement(""" delete from reviewHistory""") self.newDeck.s.statement(""" update cards set interval = 0, lastInterval = 0, due = created, lastDue = 0, factor = 2.5, firstAnswered = 0, reps = 0, successive = 0, averageTime = 0, reviewTime = 0, youngEase0 = 0, youngEase1 = 0, youngEase2 = 0, youngEase3 = 0, youngEase4 = 0, matureEase0 = 0, matureEase1 = 0, matureEase2 = 0, matureEase3 = 0, matureEase4 = 0, yesCount = 0, noCount = 0, spaceUntil = 0, type = 2, relativeDelay = 2, combinedDue = created, modified = :now """, now=time.time()) self.newDeck.s.statement(""" delete from stats""") # media if self.includeMedia: server.deck.mediaPrefix = "" copyLocalMedia(client.deck, server.deck) # need to save manually self.newDeck.rebuildCounts() self.newDeck.updateAllPriorities() self.exportedCards = self.newDeck.cardCount self.newDeck.utcOffset = -1 self.newDeck.s.commit() self.newDeck.close() self.deck.finishProgress()
time.sleep(0.2) if self.conflictResolution == "cancel": # alert we're finished early self.emit(SIGNAL("syncFinished")) return -1 # reopen self.setStatus(_("Syncing <b>%s</b>...") % syncName, 0) self.deck = None try: self.deck = DeckStorage.Deck(path) disable = False if deck and not self.deck.syncName: # multi-mode sync and syncing has been disabled by upgrade disable = True client = SyncClient(self.deck) client.setServer(proxy) # need to do anything? start = time.time() if client.prepareSync(proxy.timediff) and not disable: if self.deck.lastSync <= 0: if client.remoteTime > client.localTime: self.conflictResolution = "keepRemote" else: self.conflictResolution = "keepLocal" changes = True # summary if not self.conflictResolution and not self.onlyMerge: self.setStatus(_("Fetching summary from server..."), 0) sums = client.summaries() if (self.conflictResolution or self.onlyMerge or client.needFullSync(sums)):
def exportInto(self, path): n = 3 if not self.includeSchedulingInfo: n += 1 self.deck.startProgress(n) self.deck.updateProgress(_("Exporting...")) self.newDeck = DeckStorage.Deck(path) client = SyncClient(self.deck) server = SyncServer(self.newDeck) server._mediaSupported = self.includeMedia client.setServer(server) client.localTime = self.deck.modified client.remoteTime = 0 self.deck.s.flush() # set up a custom change list and sync lsum = self.localSummary() rsum = server.summary(0) self.deck.updateProgress() payload = client.genPayload((lsum, rsum)) self.deck.updateProgress() res = server.applyPayload(payload) if not self.includeSchedulingInfo: self.deck.updateProgress() self.newDeck.s.statement(""" delete from reviewHistory""") self.newDeck.s.statement(""" update cards set interval = 0, lastInterval = 0, due = created, lastDue = 0, factor = 2.5, firstAnswered = 0, reps = 0, successive = 0, averageTime = 0, reviewTime = 0, youngEase0 = 0, youngEase1 = 0, youngEase2 = 0, youngEase3 = 0, youngEase4 = 0, matureEase0 = 0, matureEase1 = 0, matureEase2 = 0, matureEase3 = 0, matureEase4 = 0, yesCount = 0, noCount = 0, spaceUntil = 0, isDue = 1, type = 2, combinedDue = created, modified = :now """, now=time.time()) self.newDeck.s.statement(""" delete from stats""") # media if client.mediaSyncPending: bulkClient = BulkMediaSyncer(client.deck) bulkServer = BulkMediaSyncer(server.deck) bulkClient.server = bulkServer bulkClient.sync() # need to save manually self.newDeck.rebuildCounts() self.newDeck.updateAllPriorities() self.exportedCards = self.newDeck.cardCount self.newDeck.utcOffset = -1 self.newDeck.s.commit() self.newDeck.close() self.deck.finishProgress()
def do_sync(self): if self.SYNC_USERNAME == "" or self.SYNC_PASSWORD == "" and \ self.yesno_dlg(gtk.MESSAGE_QUESTION, "Do you want to set sync account?"): self.run_settings(None, None) self.deck_save() page = "<br/><br/>" self.deck.lastLoaded = time.time() #syncing while 1: proxy = HttpSyncServerProxy(self.SYNC_USERNAME, self.SYNC_PASSWORD) try: proxy.connect("ankimini") except: self.err_dlg("Cant connect - check connection and username/password") return if not proxy.hasDeck(self.deck.syncName): self.err_dlg("Cant sync, no deck on server") return if abs(proxy.timestamp - time.time()) > 60: self.err_dlg("Your clock is off by more than 60 seconds. Syncing will not work until you fix this.") return client = SyncClient(self.deck) client.setServer(proxy) # need to do anything? proxy.deckName = self.deck.syncName if not client.prepareSync(): return # summary page+=""" <html><head> <meta name="viewport" content="user-scalable=yes, width=device-width, maximum-scale=0.6667" /> </head><body>\n Fetching summary from server..<br> """ self.print_html_doc(page) sums = client.summaries() # diff page+="Determining differences.." self.print_html_doc(page) payload = client.genPayload(sums) # send payload pr = client.payloadChangeReport(payload) page+="<br>" + pr + "<br>" page+="Sending payload...<br>" self.print_html_doc(page) res = client.server.applyPayload(payload) # apply reply page+="Applying reply..<br>" self.print_html_doc(page) client.applyPayloadReply(res) # finished. save deck, preserving mod time page+="Sync complete." self.print_html_doc(page) self.deck.rebuildQueue() self.deck.lastLoaded = self.deck.modified self.deck.s.flush() self.deck.s.commit()
def syncDeck(self, deck): try: proxy = HttpSyncServerProxy(config.get('SYNC_USERNAME'), config.get('SYNC_PASSWORD')) proxy.connect("ankimini") except: raise Exception("Can't sync - check username/password") if not proxy.hasDeck(deck.syncName): raise Exception("Can't sync, no deck on server") if abs(proxy.timestamp - time.time()) > 60: raise Exception("Your clock is off by more than 60 seconds.<br>" \ "Syncing will not work until you fix this.") client = SyncClient(deck) client.setServer(proxy) # need to do anything? proxy.deckName = deck.syncName if not client.prepareSync(): raise Exception("Nothing to do") self.flushWrite("""<h1>Syncing deck</h1> <h2>%s</h2> <em>This could take a while with a big deck ... please be patient!</em> """ % (deck.path, )) # hack to get safari to render immediately! self.flushWrite("<!--" + " " * 1024 + "-->") # this can take a long time ... ensure the client doesn't timeout before we finish from threading import Event, Thread ping_event = Event() def ping_client(s=self.wfile, ev=ping_event): while 1: ev.wait(3) if ev.isSet(): return s.write(".<!--\n-->") s.flush() ping_thread = Thread(target=ping_client) ping_thread.start() # summary self.lineWrite("Fetching summary from server..") sums = client.summaries() needFull = client.needFullSync(sums) if needFull: self.lineWrite("Doing full sync..") client.fullSync() else: # diff self.lineWrite("Determining differences..") payload = client.genPayload(sums) # send payload pr = client.payloadChangeReport(payload) self.lineWrite("<br>" + pr + "<br>") self.lineWrite("Sending payload...") if needFull: deck = ds.Deck(deck.path, backup=False) # why is deck.syncName getting lost on a full sync??? if deck.syncName is None: deck.syncName = proxy.deckName print "syncName was lost on full sync, restored to", deck.syncName else: res = client.server.applyPayload(payload) # apply reply self.lineWrite("Applying reply..") client.applyPayloadReply(res) # finished. save deck, preserving mod time self.lineWrite("Sync complete.") deck.rebuildQueue() deck.lastLoaded = deck.modified deck.s.flush() deck.s.commit() # turn off client ping ping_event.set() ping_thread.join(5) return deck
def syncDeck(self, deck): try: proxy = HttpSyncServerProxy(config.get('SYNC_USERNAME'), config.get('SYNC_PASSWORD')) proxy.connect("ankimini") except: raise Exception("Can't sync: " + traceback.format_exc()) if not proxy.hasDeck(deck.name()): raise Exception("Can't sync, no deck on server") if abs(proxy.timestamp - time.time()) > 60: raise Exception("Your clock is off by more than 60 seconds.<br>" \ "Syncing will not work until you fix this.") client = SyncClient(deck) client.setServer(proxy) # need to do anything? proxy.deckName = deck.name() print proxy.deckName if not client.prepareSync(0): raise Exception("Nothing to do") self.flushWrite("""<h1>Syncing deck</h1> <h2>%s</h2> <em>This could take a while with a big deck ... please be patient!</em> """ % (deck.path,) ) # hack to get safari to render immediately! self.flushWrite("<!--" + " "*1024 + "-->") # this can take a long time ... ensure the client doesn't timeout before we finish from threading import Event, Thread ping_event = Event() def ping_client( s = self.wfile, ev=ping_event ): while 1: ev.wait(3) if ev.isSet(): return s.write(".<!--\n-->") s.flush() ping_thread = Thread(target=ping_client) ping_thread.start() # summary self.lineWrite("Fetching summary from server..") sums = client.summaries() needFull = client.needFullSync(sums) if needFull: self.lineWrite("Doing full sync..") client.fullSync() else: # diff self.lineWrite("Determining differences..") payload = client.genPayload(sums) # send payload pr = client.payloadChangeReport(payload) self.lineWrite("<br>" + pr + "<br>") self.lineWrite("Sending payload...") if needFull: deck = ds.Deck(deck.path, backup=False) else: res = client.server.applyPayload(payload) # apply reply self.lineWrite("Applying reply..") client.applyPayloadReply(res) try: client.server.finish() except: deck.s.rollback() # finished. save deck, preserving mod time self.lineWrite("Sync complete.") deck.reset() deck.lastLoaded = deck.modified deck.s.flush() deck.s.commit() # turn off client ping ping_event.set() ping_thread.join(5) return deck