update auto-sync code

This commit is contained in:
Damien Elmes 2020-05-31 10:53:54 +10:00
parent 058ff1b71a
commit 7e221f0acf
5 changed files with 90 additions and 83 deletions

View File

@ -42,6 +42,7 @@ from aqt.mediasync import MediaSyncer
from aqt.profiles import ProfileManager as ProfileManagerType
from aqt.qt import *
from aqt.qt import sip
from aqt.sync import sync_collection, sync_login
from aqt.taskman import TaskManager
from aqt.theme import theme_manager
from aqt.utils import (
@ -380,13 +381,9 @@ close the profile or restart Anki."""
self.taskman.run_in_background(downgrade, on_done)
def loadProfile(self, onsuccess: Optional[Callable] = None) -> None:
self.maybeAutoSync()
if not self.loadCollection():
return
self.maybe_auto_sync_media()
self.pm.apply_profile_options()
# show main window
@ -408,17 +405,16 @@ close the profile or restart Anki."""
self.handleImport(self.pendingImport)
self.pendingImport = None
gui_hooks.profile_did_open()
if onsuccess:
onsuccess()
if onsuccess is None:
onsuccess = lambda: None
self.maybe_auto_sync_on_open_close(onsuccess)
def unloadProfile(self, onsuccess: Callable) -> None:
def callback():
self._unloadProfile()
onsuccess()
# start media sync if not already running
self.maybe_auto_sync_media()
gui_hooks.profile_will_close()
self.unloadCollection(callback)
@ -433,8 +429,6 @@ close the profile or restart Anki."""
# at this point there should be no windows left
self._checkForUnclosedWidgets()
self.maybeAutoSync()
def _checkForUnclosedWidgets(self) -> None:
for w in self.app.topLevelWidgets():
if w.isVisible():
@ -520,13 +514,16 @@ close the profile or restart Anki."""
self.col.reopen()
def unloadCollection(self, onsuccess: Callable) -> None:
def callback():
self.setEnabled(False)
def after_sync():
self.media_syncer.show_diag_until_finished()
self._unloadCollection()
onsuccess()
self.closeAllWindows(callback)
def before_sync():
self.setEnabled(False)
self.maybe_auto_sync_on_open_close(after_sync)
self.closeAllWindows(before_sync)
def _unloadCollection(self) -> None:
if not self.col:
@ -869,52 +866,51 @@ title="%s" %s>%s</button>""" % (
# Syncing
##########################################################################
# expects a current profile and a loaded collection; reloads
# collection after sync completes
def onSync(self):
def on_sync_button_clicked(self):
if self.media_syncer.is_syncing():
self.media_syncer.show_sync_log()
else:
self.temp_sync()
# self.unloadCollection(self._onSync)
auth = self.pm.sync_auth()
if not auth:
sync_login(self, self._sync_collection_and_media)
else:
self._sync_collection_and_media(lambda: None)
def _onSync(self):
self._sync()
if not self.loadCollection():
return
self.media_syncer.start()
def _sync_collection_and_media(self, after_sync: Callable[[], None]):
"Caller should ensure auth available."
# start media sync if not already running
if not self.media_syncer.is_syncing():
self.media_syncer.start()
# expects a current profile, but no collection loaded
def maybeAutoSync(self) -> None:
if (
not self.pm.profile["syncKey"]
or not self.pm.profile["autoSync"]
or self.safeMode
or self.restoringBackup
):
return
def on_collection_sync_finished():
self.reset()
after_sync()
# ok to sync
self._sync()
sync_collection(self, on_done=on_collection_sync_finished)
def maybe_auto_sync_on_open_close(self, after_sync: Callable[[], None]) -> None:
"If disabled, after_sync() is called immediately."
if self.can_auto_sync():
self._sync_collection_and_media(after_sync)
else:
after_sync()
def maybe_auto_sync_media(self) -> None:
if not self.pm.profile["autoSync"] or self.safeMode or self.restoringBackup:
if self.can_auto_sync():
return
# media_syncer takes care of media syncing preference check
self.media_syncer.start()
def can_auto_sync(self) -> bool:
return (self.pm.auto_syncing_enabled()
and self.pm.sync_auth()
and not self.safeMode
and not self.restoringBackup)
# legacy
def _sync(self):
from aqt.sync import SyncManager
self.state = "sync"
self.app.setQuitOnLastWindowClosed(False)
self.syncer = SyncManager(self, self.pm)
self.syncer.sync()
self.app.setQuitOnLastWindowClosed(True)
def temp_sync(self):
from aqt.sync import sync
sync(self)
pass
onSync = on_sync_button_clicked
# Tools
##########################################################################
@ -940,7 +936,7 @@ title="%s" %s>%s</button>""" % (
("a", self.onAddCard),
("b", self.onBrowse),
("t", self.onStats),
("y", self.onSync),
("y", self.on_sync_button_clicked),
]
self.applyShortcuts(globalShortcuts)

View File

@ -62,7 +62,7 @@ class MediaSyncer:
self._log_and_notify(tr(TR.SYNC_MEDIA_STARTING))
self._syncing = True
self._progress_timer = self.mw.progress.timer(1000, self._on_progress, True)
self._progress_timer = self.mw.progress.timer(1000, self._on_progress, False)
gui_hooks.media_sync_did_start_or_stop(True)
def run() -> None:

View File

@ -618,6 +618,9 @@ create table if not exists profiles
def media_syncing_enabled(self) -> bool:
return self.profile["syncMedia"]
def auto_syncing_enabled(self) -> bool:
return self.profile["autoSync"]
def sync_auth(self) -> Optional[SyncAuth]:
hkey = self.profile.get("syncKey")
if not hkey:

View File

@ -28,6 +28,10 @@ from aqt.qt import (
)
from aqt.utils import askUser, askUserDialog, showText, showWarning, tr
# fixme: catch auth error in other routines, clear sync auth
# fixme: sync progress
# fixme: curDeck marking collection modified
# fixme: show progress immediately
class FullSyncChoice(enum.Enum):
CANCEL = 0
@ -40,69 +44,73 @@ def get_sync_status(mw: aqt.main.AnkiQt, callback: Callable[[SyncOutput], None])
if not auth:
return
def on_done(fut):
def on_future_done(fut):
callback(fut.result())
mw.taskman.run_in_background(lambda: mw.col.backend.sync_status(auth), on_done)
mw.taskman.run_in_background(lambda: mw.col.backend.sync_status(auth), on_future_done)
def sync(mw: aqt.main.AnkiQt) -> None:
def sync_collection(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None:
auth = mw.pm.sync_auth()
if not auth:
login(mw, on_success=lambda: sync(mw))
sync_login(mw, on_success=lambda: sync_collection(mw))
return
def on_done(fut):
def on_future_done(fut):
mw.col.db.begin()
try:
out: SyncOutput = fut.result()
except InterruptedError:
return
return on_done()
except Exception as e:
showWarning(str(e))
return
return on_done()
mw.pm.set_host_number(out.host_number)
if out.server_message:
showText(out.server_message)
if out.required == out.NO_CHANGES:
# all done
return
return on_done()
else:
full_sync(mw, out)
full_sync(mw, out, on_done)
if not mw.col.basicCheck():
showWarning("Please use Tools>Check Database")
return
return on_done()
mw.col.save(trx=False)
mw.taskman.with_progress(
lambda: mw.col.backend.sync_collection(auth),
on_done,
on_future_done,
label=tr(TR.SYNC_CHECKING),
)
def full_sync(mw: aqt.main.AnkiQt, out: SyncOutput) -> None:
def full_sync(
mw: aqt.main.AnkiQt, out: SyncOutput, on_done: Callable[[], None]
) -> None:
if out.required == out.FULL_DOWNLOAD:
confirm_full_download(mw)
confirm_full_download(mw, on_done)
elif out.required == out.FULL_UPLOAD:
full_upload(mw)
full_upload(mw, on_done)
else:
choice = ask_user_to_decide_direction()
if choice == FullSyncChoice.UPLOAD:
full_upload(mw)
full_upload(mw, on_done)
elif choice == FullSyncChoice.DOWNLOAD:
full_download(mw)
full_download(mw, on_done)
else:
on_done()
def confirm_full_download(mw: aqt.main.AnkiQt) -> None:
def confirm_full_download(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None:
# confirmation step required, as some users customize their notetypes
# in an empty collection, then want to upload them
if not askUser(tr(TR.SYNC_CONFIRM_EMPTY_DOWNLOAD)):
return
return on_done()
else:
mw.closeAllWindows(lambda: full_download(mw))
mw.closeAllWindows(lambda: full_download(mw, on_done))
def on_full_sync_timer(mw: aqt.main.AnkiQt) -> None:
@ -119,7 +127,7 @@ def on_full_sync_timer(mw: aqt.main.AnkiQt) -> None:
mw.col.backend.abort_sync()
def full_download(mw: aqt.main.AnkiQt) -> None:
def full_download(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None:
mw.col.close_for_full_sync()
def on_timer():
@ -129,7 +137,7 @@ def full_download(mw: aqt.main.AnkiQt) -> None:
qconnect(timer.timeout, on_timer)
timer.start(150)
def on_done(fut):
def on_future_done(fut):
timer.stop()
mw.col.reopen(after_full_sync=True)
mw.reset()
@ -137,16 +145,16 @@ def full_download(mw: aqt.main.AnkiQt) -> None:
fut.result()
except Exception as e:
showWarning(str(e))
return
return on_done()
mw.taskman.with_progress(
lambda: mw.col.backend.full_download(mw.pm.sync_auth()),
on_done,
on_future_done,
label=tr(TR.SYNC_DOWNLOADING_FROM_ANKIWEB),
)
def full_upload(mw: aqt.main.AnkiQt) -> None:
def full_upload(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None:
mw.col.close_for_full_sync()
def on_timer():
@ -156,7 +164,7 @@ def full_upload(mw: aqt.main.AnkiQt) -> None:
qconnect(timer.timeout, on_timer)
timer.start(150)
def on_done(fut):
def on_future_done(fut):
timer.stop()
mw.col.reopen(after_full_sync=True)
mw.reset()
@ -164,16 +172,15 @@ def full_upload(mw: aqt.main.AnkiQt) -> None:
fut.result()
except Exception as e:
showWarning(str(e))
return
return on_done()
mw.taskman.with_progress(
lambda: mw.col.backend.full_upload(mw.pm.sync_auth()),
on_done,
on_future_done,
label=tr(TR.SYNC_UPLOADING_TO_ANKIWEB),
)
def login(
def sync_login(
mw: aqt.main.AnkiQt, on_success: Callable[[], None], username="", password=""
) -> None:
while True:
@ -183,13 +190,13 @@ def login(
if username and password:
break
def on_done(fut):
def on_future_done(fut):
try:
auth = fut.result()
except SyncError as e:
if e.kind() == SyncErrorKind.AUTH_FAILED:
showWarning(str(e))
login(mw, on_success, username, password)
sync_login(mw, on_success, username, password)
return
except Exception as e:
showWarning(str(e))
@ -202,7 +209,8 @@ def login(
on_success()
mw.taskman.with_progress(
lambda: mw.col.backend.sync_login(username=username, password=password), on_done
lambda: mw.col.backend.sync_login(username=username, password=password),
on_future_done,
)

View File

@ -175,7 +175,7 @@ class Toolbar:
self.mw.onStats()
def _syncLinkHandler(self) -> None:
self.mw.onSync()
self.mw.on_sync_button_clicked()
# HTML & CSS
######################################################################